@@ -3,11 +3,53 @@ import { Button } from "zudoku/components";
33import { Link } from "zudoku/router" ;
44import { FeatureItem } from "../../components/FeatureItem" ;
55import { QuotaItem } from "../../components/QuotaItem" ;
6- import type { Plan } from "../../types/PlanType" ;
6+ import type { Plan , PlanPhase } from "../../types/PlanType" ;
77import { categorizeRateCards } from "../../utils/categorizeRateCards" ;
8+ import { formatDuration } from "../../utils/formatDuration" ;
89import { formatPrice } from "../../utils/formatPrice" ;
910import { getPriceFromPlan } from "../../utils/getPriceFromPlan" ;
1011
12+ const PhaseSection = ( {
13+ phase,
14+ currency,
15+ showName,
16+ excludeKeys,
17+ } : {
18+ phase : PlanPhase ;
19+ currency ?: string ;
20+ showName : boolean ;
21+ excludeKeys : Set < string > ;
22+ } ) => {
23+ const { quotas, features } = categorizeRateCards ( phase . rateCards , currency ) ;
24+
25+ const filteredQuotas = quotas . filter ( ( q ) => ! excludeKeys . has ( q . key ) ) ;
26+ const filteredFeatures = features . filter ( ( f ) => ! excludeKeys . has ( f . key ) ) ;
27+
28+ if ( filteredQuotas . length === 0 && filteredFeatures . length === 0 ) return null ;
29+
30+ return (
31+ < div className = "space-y-2" >
32+ { showName && (
33+ < div className = "text-sm font-medium text-card-foreground" >
34+ { phase . name }
35+ { phase . duration && (
36+ < span className = "text-muted-foreground font-normal" >
37+ { " " }
38+ — { formatDuration ( phase . duration ) }
39+ </ span >
40+ ) }
41+ </ div >
42+ ) }
43+ { filteredQuotas . map ( ( quota ) => (
44+ < QuotaItem key = { quota . key } quota = { quota } />
45+ ) ) }
46+ { filteredFeatures . map ( ( feature ) => (
47+ < FeatureItem key = { feature . key } feature = { feature } />
48+ ) ) }
49+ </ div >
50+ ) ;
51+ } ;
52+
1153export const PricingCard = ( {
1254 plan,
1355 isPopular = false ,
@@ -17,17 +59,13 @@ export const PricingCard = ({
1759 isPopular ?: boolean ;
1860 isSubscribed ?: boolean ;
1961} ) => {
20- const defaultPhase = plan . phases . at ( - 1 ) ;
21- if ( ! defaultPhase ) return null ;
62+ if ( plan . phases . length === 0 ) return null ;
2263
23- const { quotas, features } = categorizeRateCards (
24- defaultPhase . rateCards ,
25- plan . currency ,
26- ) ;
2764 const price = getPriceFromPlan ( plan ) ;
2865 const isFree = price . monthly === 0 ;
2966
3067 const isCustom = plan . metadata ?. isCustom === true ;
68+ const hasMultiplePhases = plan . phases . length > 1 ;
3169
3270 return (
3371 < div
@@ -81,22 +119,23 @@ export const PricingCard = ({
81119 ) }
82120 </ div >
83121
84- < div className = "space-y-4 mb-6 grow" >
85- { quotas . length > 0 && (
86- < div className = "space-y-2" >
87- { quotas . map ( ( quota ) => (
88- < QuotaItem key = { quota . key } quota = { quota } />
89- ) ) }
90- </ div >
91- ) }
92-
93- { features . length > 0 && (
94- < div className = "space-y-2" >
95- { features . map ( ( feature ) => (
96- < FeatureItem key = { feature . key } feature = { feature } />
97- ) ) }
98- </ div >
99- ) }
122+ < div className = "space-y-4 mb-6 grow" >
123+ { plan . phases . map ( ( phase , index ) => {
124+ const laterKeys = new Set (
125+ plan . phases
126+ . slice ( index + 1 )
127+ . flatMap ( ( p ) => p . rateCards . map ( ( rc ) => rc . featureKey ?? rc . key ) ) ,
128+ ) ;
129+ return (
130+ < PhaseSection
131+ key = { phase . key }
132+ phase = { phase }
133+ currency = { plan . currency }
134+ showName = { hasMultiplePhases }
135+ excludeKeys = { laterKeys }
136+ />
137+ ) ;
138+ } ) }
100139 </ div >
101140
102141 { isSubscribed ? (
0 commit comments