Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | /** * PricingPage Stories — Family Coverage States * * Demonstrates how the pricing page adapts when a free-tier parent's * children are covered by another parent's family subscription. */ import type { Meta, StoryObj } from '@storybook/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { billingKeys } from '@/lib/queryKeys' import { TIER_LIMITS, type TierName } from '@/lib/tier-limits' import type { TierResponse, FamilyCoverageResponse } from '@/hooks/useTier' import PricingPage from './page' function tierData(tier: TierName): TierResponse { const limits = TIER_LIMITS[tier] return { tier, limits: { maxPracticeStudents: limits.maxPracticeStudents === Infinity ? null : limits.maxPracticeStudents, maxSessionMinutes: limits.maxSessionMinutes, maxSessionsPerWeek: limits.maxSessionsPerWeek === Infinity ? null : limits.maxSessionsPerWeek, maxOfflineParsingPerMonth: limits.maxOfflineParsingPerMonth, }, } } const DEFAULT_PRICES = { family: { monthly: { amount: 600, display: 6 }, annual: { amount: 3768, display: 37.68, monthlyEquivalent: 3.14 }, }, } function createQC(tier: TierName, coverage: FamilyCoverageResponse) { const qc = new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: Infinity } }, }) qc.setQueryData(billingKeys.tier(), tierData(tier)) qc.setQueryData(billingKeys.coverage(), coverage) qc.setQueryData(billingKeys.prices(), DEFAULT_PRICES) return qc } /** * Provides a seeded QueryClient that overrides the global one from preview.tsx. * All other providers (Fullscreen, AudioManager, etc.) come from the global decorator. */ function Wrapper({ children, tier, coverage, }: { children: React.ReactNode tier: TierName coverage: FamilyCoverageResponse }) { const qc = createQC(tier, coverage) return <QueryClientProvider client={qc}>{children}</QueryClientProvider> } const meta: Meta<typeof PricingPage> = { title: 'Pages/Pricing/Family Coverage', component: PricingPage, parameters: { layout: 'fullscreen', nextjs: { appDirectory: true }, }, tags: ['autodocs'], } export default meta type Story = StoryObj<typeof PricingPage> /** * Free-tier parent with NO coverage — default upgrade CTA. */ export const FreeNoCoverage: Story = { name: 'Free — No Coverage', render: () => ( <Wrapper tier="free" coverage={{ isCovered: false, coveredBy: null, coveredChildCount: 0, totalChildCount: 2 }} > <PricingPage /> </Wrapper> ), } /** * Free-tier parent where ALL children are covered by co-parent's family plan. * Shows blue banner + dimmed "Already Covered" CTA + "Subscribe anyway" link. */ export const FreeFullyCovered: Story = { name: 'Free — Fully Covered (2/2)', render: () => ( <Wrapper tier="free" coverage={{ isCovered: true, coveredBy: { userId: 'mom-id', name: 'Sarah' }, coveredChildCount: 2, totalChildCount: 2, }} > <PricingPage /> </Wrapper> ), } /** * Free-tier parent where only SOME children are covered. * Still shows coverage banner since at least one is covered. */ export const FreePartiallyCovered: Story = { name: 'Free — Partially Covered (1/3)', render: () => ( <Wrapper tier="free" coverage={{ isCovered: true, coveredBy: { userId: 'mom-id', name: 'Sarah' }, coveredChildCount: 1, totalChildCount: 3, }} > <PricingPage /> </Wrapper> ), } /** * Family-tier parent — no coverage banner, shows "Manage Subscription". * Coverage data is irrelevant when user already has their own subscription. */ export const FamilyTier: Story = { name: 'Family — Own Subscription', render: () => ( <Wrapper tier="family" coverage={{ isCovered: false, coveredBy: null, coveredChildCount: 0, totalChildCount: 2 }} > <PricingPage /> </Wrapper> ), } /** * Guest user — no coverage, shows "Get Started Free" CTA. */ export const Guest: Story = { name: 'Guest — No Coverage', render: () => ( <Wrapper tier="guest" coverage={{ isCovered: false, coveredBy: null, coveredChildCount: 0, totalChildCount: 0 }} > <PricingPage /> </Wrapper> ), } |