trial choice
This commit is contained in:
parent
c7d6f049b0
commit
aa683cc574
@ -6,6 +6,8 @@ import {
|
|||||||
PaymentPlacementProvider,
|
PaymentPlacementProvider,
|
||||||
TrialVariantSelectionProvider,
|
TrialVariantSelectionProvider,
|
||||||
} from "../src/entities/session/payment";
|
} from "../src/entities/session/payment";
|
||||||
|
import type { IFunnelPaymentPlacement } from "../src/entities/session/funnel/types";
|
||||||
|
import { Currency } from "../src/shared/types";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@ -35,6 +37,24 @@ const poppins = Poppins({
|
|||||||
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
|
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Storybook mock placement to avoid network calls
|
||||||
|
const storybookPlacement: IFunnelPaymentPlacement = {
|
||||||
|
currency: Currency.USD,
|
||||||
|
billingPeriod: "WEEK",
|
||||||
|
billingInterval: 1,
|
||||||
|
trialPeriod: "DAY",
|
||||||
|
trialInterval: 7,
|
||||||
|
placementId: "plc_story",
|
||||||
|
paywallId: "pw_story",
|
||||||
|
paymentUrl: "https://example.com/pay",
|
||||||
|
variants: [
|
||||||
|
{ id: "v1", key: "basic", type: "subscription", price: 1499, trialPrice: 100, title: "Basic" },
|
||||||
|
{ id: "v2", key: "standard", type: "subscription", price: 1499, trialPrice: 499, title: "Standard" },
|
||||||
|
{ id: "v3", key: "popular", type: "subscription", price: 1499, trialPrice: 899, title: "Popular", accent: true },
|
||||||
|
{ id: "v4", key: "premium", type: "subscription", price: 1499, trialPrice: 1367, title: "Premium" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
const preview: Preview = {
|
const preview: Preview = {
|
||||||
parameters: {
|
parameters: {
|
||||||
controls: {
|
controls: {
|
||||||
@ -62,7 +82,11 @@ const preview: Preview = {
|
|||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story) => (
|
||||||
<PaymentPlacementProvider>
|
<PaymentPlacementProvider
|
||||||
|
initialCache={[
|
||||||
|
{ funnelKey: "storybook-funnel", paymentId: "main", placement: storybookPlacement },
|
||||||
|
]}
|
||||||
|
>
|
||||||
<TrialVariantSelectionProvider>
|
<TrialVariantSelectionProvider>
|
||||||
<div
|
<div
|
||||||
className={`${geistSans.variable} ${geistMono.variable} ${manrope.variable} ${inter.variable} ${poppins.variable} flex items-center justify-center size-full max-w-[560px] min-w-xs mx-auto antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} ${manrope.variable} ${inter.variable} ${poppins.variable} flex items-center justify-center size-full max-w-[560px] min-w-xs mx-auto antialiased`}
|
||||||
|
|||||||
@ -22,15 +22,28 @@ const PaymentPlacementContext = createContext<PaymentPlacementContextValue | nul
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
type PreloadedPlacement = {
|
||||||
|
funnelKey: string;
|
||||||
|
paymentId: string;
|
||||||
|
placement: IFunnelPaymentPlacement;
|
||||||
|
};
|
||||||
|
|
||||||
export function PaymentPlacementProvider({
|
export function PaymentPlacementProvider({
|
||||||
children,
|
children,
|
||||||
|
initialCache,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
initialCache?: PreloadedPlacement[];
|
||||||
}) {
|
}) {
|
||||||
// Cache: Map<"funnelKey:paymentId", PlacementCacheEntry>
|
// Cache: Map<"funnelKey:paymentId", PlacementCacheEntry>
|
||||||
const [cache, setCache] = useState<Map<string, PlacementCacheEntry>>(
|
const [cache, setCache] = useState<Map<string, PlacementCacheEntry>>(() => {
|
||||||
new Map()
|
const map = new Map<string, PlacementCacheEntry>();
|
||||||
);
|
initialCache?.forEach(({ funnelKey, paymentId, placement }) => {
|
||||||
|
const key = `${funnelKey}:${paymentId}`;
|
||||||
|
map.set(key, { placement, isLoading: false, error: null });
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
const getCacheKey = (funnelKey: string, paymentId: string) =>
|
const getCacheKey = (funnelKey: string, paymentId: string) =>
|
||||||
`${funnelKey}:${paymentId}`;
|
`${funnelKey}:${paymentId}`;
|
||||||
|
|||||||
@ -8,8 +8,25 @@ export const useClientToken = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const token = await getAuthTokenFromCookie();
|
try {
|
||||||
setToken(token);
|
const token = await getAuthTokenFromCookie();
|
||||||
|
// If server returned undefined, fall back to a stable mock in Storybook
|
||||||
|
if (!token && typeof window !== "undefined") {
|
||||||
|
const w = window as Window & { __STORYBOOK_ADDONS?: unknown };
|
||||||
|
const isStorybook = Boolean(w.__STORYBOOK_ADDONS) ||
|
||||||
|
/:\/\/.*(:6006|storybook)/i.test(w.location.href);
|
||||||
|
setToken(isStorybook ? "storybook-token" : undefined);
|
||||||
|
} else {
|
||||||
|
setToken(token);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// In Storybook or non-Next runtime, server action may fail; use mock token
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
setToken("storybook-token");
|
||||||
|
} else {
|
||||||
|
setToken(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user