@@ -20,28 +20,27 @@ import { Badge } from '@/components/ui/badge';
2020import { PlanDetails } from '@/lib/api' ;
2121import { LocalizedPrice } from 'react-currency-localizer' ;
2222
23- type FREQUENCY = 'monthly' | 'yearly' ;
24-
25- interface BillingPlan {
23+ interface RawPlan {
2624 name : string ;
2725 info : string ;
28- price : {
29- monthly : number ;
30- yearly : number ;
31- } ;
26+ price : number | { monthly : number } ;
3227 features : {
3328 text : string ;
3429 tooltip ?: string ;
3530 } [ ] ;
31+ credits : number ;
32+ planId : string ;
33+ highlighted ?: boolean ;
34+ }
35+
36+ interface BillingPlan extends RawPlan {
3637 btn : {
3738 text : string ;
3839 href ?: string ;
3940 onClick ?: ( ) => void ;
4041 } ;
41- highlighted ?: boolean ;
4242 isCurrentPlan ?: boolean ;
4343 isUpgrading ?: boolean ;
44- credits : number ;
4544}
4645
4746interface BillingPricingSectionProps extends React . ComponentProps < 'div' > {
@@ -62,7 +61,7 @@ export function BillingPricingSection({
6261 description,
6362 ...props
6463} : BillingPricingSectionProps ) {
65- const [ frequency , setFrequency ] = React . useState < 'monthly' | 'yearly' > ( 'monthly' ) ;
64+
6665 const [ upgradingPlan , setUpgradingPlan ] = useState < string | null > ( null ) ;
6766
6867 const { getToken, isSignedIn } = useAuth ( ) ;
@@ -115,26 +114,26 @@ export function BillingPricingSection({
115114 const getStaticPricingPlans = ( ) : BillingPlan [ ] => {
116115 const currentPlan = planName ?. toLowerCase ( ) ;
117116
118- const plans = [
117+ const plans : RawPlan [ ] = [
119118 {
120119 name : 'Free' ,
121120 info : 'Perfect for getting started' ,
122- price : { monthly : 0 , yearly : 0 } ,
121+ price : 0 ,
123122 features : [
124- { text : '5 credits daily (up to 20 credits /month) ' , tooltip : 'Daily credit allocation with monthly cap ' } ,
123+ { text : '5 credits/month' , tooltip : 'Monthly credit allocation - use anytime ' } ,
125124 { text : '1 deployed website' , tooltip : 'Host one website for free' } ,
126125 { text : 'Community support' , tooltip : 'Access to community forums and documentation' } ,
127126 ] ,
128- credits : 20 ,
127+ credits : 5 ,
129128 planId : 'free'
130129 } ,
131130 {
132131 name : 'Pro' ,
133132 info : 'Best for growing developers' ,
134- price : { monthly : 18 , yearly : 180 } , // USD prices
133+ price : 18 , // USD price - one-time purchase
135134 features : [
136135 { text : 'Everything in Free' } ,
137- { text : '150 credits/month ' , tooltip : 'Monthly credit allocation for AI operations ' } ,
136+ { text : '150 credits included ' , tooltip : 'One-time credit purchase - credits never expire ' } ,
138137 { text : '10 deployed websites' , tooltip : 'Host up to 10 websites' } ,
139138 { text : 'Custom domains' , tooltip : 'Use your own domain names' } ,
140139 { text : 'Codebase download' , tooltip : 'Download your generated code' } ,
@@ -146,10 +145,10 @@ export function BillingPricingSection({
146145 {
147146 name : 'Premium' ,
148147 info : 'For professional developers' ,
149- price : { monthly : 30 , yearly : 300 } , // USD prices
148+ price : 30 , // USD price - one-time purchase
150149 features : [
151150 { text : 'Everything in Pro' } ,
152- { text : '250 credits/month ' , tooltip : 'Monthly credit allocation for AI operations ' } ,
151+ { text : '250 credits included ' , tooltip : 'One-time credit purchase - credits never expire ' } ,
153152 { text : '25 deployed websites' , tooltip : 'Host up to 25 websites' } ,
154153 { text : 'Priority support' , tooltip : '24/7 email and chat support' } ,
155154 ] ,
@@ -159,7 +158,7 @@ export function BillingPricingSection({
159158 {
160159 name : 'Bring Your Own Key (BYOK)' ,
161160 info : 'Ultimate flexibility' ,
162- price : { monthly : 9 , yearly : 108 } , // USD prices
161+ price : { monthly : 9 } , // USD price - monthly subscription only
163162 features : [
164163 { text : 'API cost paid directly to LLM provider' , tooltip : 'Pay OpenAI/Anthropic directly, no markup' } ,
165164 { text : '100 deployed websites' , tooltip : 'Host up to 100 websites' } ,
@@ -182,8 +181,9 @@ export function BillingPricingSection({
182181 const getButtonText = ( ) => {
183182 if ( isCurrentPlan ) return 'Current Plan' ;
184183 if ( isUpgrading ) return 'Processing...' ;
185- if ( plan . price . monthly === 0 ) return 'Get Started' ;
186- return 'Upgrade' ;
184+ if ( typeof plan . price === 'number' && plan . price === 0 ) return 'Get Started' ;
185+ if ( plan . planId === 'byok' ) return 'Subscribe' ;
186+ return 'Buy Credits' ;
187187 } ;
188188
189189 return {
@@ -199,6 +199,7 @@ export function BillingPricingSection({
199199 isCurrentPlan,
200200 isUpgrading,
201201 credits : plan . credits ,
202+ planId : plan . planId ,
202203 } ;
203204 } ) ;
204205 } ;
@@ -237,7 +238,6 @@ export function BillingPricingSection({
237238 { plans . filter ( plan => ! plan . name . includes ( 'BYOK' ) ) . map ( ( plan ) => (
238239 < BillingPricingCard
239240 plan = { plan }
240- frequency = { frequency }
241241 insideDialog = { insideDialog }
242242 isByokPlan = { false }
243243 key = { plan . name }
@@ -249,7 +249,6 @@ export function BillingPricingSection({
249249 < div key = { plan . name } className = "mx-auto w-full max-w-6xl mt-4 md:mt-6 grid grid-cols-1 lg:grid-cols-2 gap-4" >
250250 < BillingPricingCard
251251 plan = { plan }
252- frequency = { frequency }
253252 insideDialog = { insideDialog }
254253 isByokPlan = { true }
255254 />
@@ -282,7 +281,6 @@ export function BillingPricingSection({
282281 { plans . filter ( plan => ! plan . name . includes ( 'BYOK' ) ) . map ( ( plan ) => (
283282 < BillingPricingCard
284283 plan = { plan }
285- frequency = { frequency }
286284 insideDialog = { insideDialog }
287285 isByokPlan = { false }
288286 key = { plan . name }
@@ -294,7 +292,6 @@ export function BillingPricingSection({
294292 { plans . filter ( plan => plan . name . includes ( 'BYOK' ) ) . map ( ( plan ) => (
295293 < BillingPricingCard
296294 plan = { plan }
297- frequency = { frequency }
298295 insideDialog = { insideDialog }
299296 isByokPlan = { true }
300297 key = { plan . name }
@@ -310,7 +307,6 @@ export function BillingPricingSection({
310307
311308type BillingPricingCardProps = React . ComponentProps < 'div' > & {
312309 plan : BillingPlan ;
313- frequency ?: FREQUENCY ;
314310 insideDialog ?: boolean ;
315311 isByokPlan ?: boolean ;
316312} ;
@@ -330,7 +326,6 @@ const Step = ({ number, children }: { number: number; children: React.ReactNode
330326export function BillingPricingCard ( {
331327 plan,
332328 className,
333- frequency = 'monthly' ,
334329 insideDialog = false ,
335330 isByokPlan = false ,
336331 ...props
@@ -378,12 +373,12 @@ export function BillingPricingCard({
378373 < p className = "text-muted-foreground text-sm font-normal" > { plan . info } </ p >
379374
380375 < div className = "mt-2 flex items-end gap-1" >
381- { plan . price [ frequency ] === 0 ? (
376+ { ( typeof plan . price === 'number' && plan . price === 0 ) ? (
382377 < span className = "text-3xl font-bold" > Free</ span >
383378 ) : (
384379 < >
385380 < LocalizedPrice
386- basePrice = { isByokPlan ? plan . price . monthly : plan . price [ frequency ] }
381+ basePrice = { typeof plan . price === 'number' ? plan . price : plan . price . monthly }
387382 baseCurrency = "USD"
388383 apiKey = { process . env . NEXT_PUBLIC_EXCHANGE_API_KEY || '' }
389384 formatPrice = { ( price , currency ) => (
@@ -395,31 +390,30 @@ export function BillingPricingCard({
395390 </ span >
396391 ) }
397392 />
398- < span className = { cn ( 'text-muted-foreground' , insideDialog && 'text-sm' ) } >
399- { isByokPlan
400- ? '/month'
401- : `/${ frequency === 'monthly' ? 'month' : 'year' } `
402- }
403- </ span >
393+ { isByokPlan && (
394+ < span className = { cn ( 'text-muted-foreground' , insideDialog && 'text-sm' ) } >
395+ /month
396+ </ span >
397+ ) }
404398 </ >
405399 ) }
406400 </ div >
407401
408402 { isByokPlan && (
409403 < div className = "mt-1" >
410404 < Badge variant = "secondary" className = "text-xs" >
411- Paid Annually
405+ Monthly Subscription
412406 </ Badge >
413407 </ div >
414408 ) }
415409
416410 { plan . credits !== 0 && (
417411 < div className = "mt-2 flex items-center gap-2 text-sm text-muted-foreground" >
418412 < Zap className = "h-4 w-4 text-blue-500" />
419- < span >
420- { plan . credits === - 1 ? 'Unlimited' : plan . credits }
421- { plan . credits === - 1 ? ' credits' : ' credits/month ' }
422- </ span >
413+ < span >
414+ { plan . credits === - 1 ? 'Unlimited' : plan . credits }
415+ { plan . planId === 'free' ? ' credits/month' : plan . credits === - 1 ? ' credits' : ' credits' }
416+ </ span >
423417 </ div >
424418 ) }
425419 </ div >
@@ -474,7 +468,7 @@ export function BillingPricingCard({
474468 >
475469 < span className = "flex items-center justify-center space-x-2" >
476470 < span > { plan . btn . text } </ span >
477- { ! plan . isCurrentPlan && ! plan . isUpgrading && plan . price . monthly > 0 && (
471+ { ! plan . isCurrentPlan && ! plan . isUpgrading && ( ( typeof plan . price === 'number' && plan . price > 0 ) || ( typeof plan . price === 'object' && plan . price . monthly > 0 ) ) && (
478472 < ArrowRight className = "h-4 w-4" />
479473 ) }
480474 </ span >
0 commit comments