@@ -37,11 +37,15 @@ export function ProvidersTab() {
3737 providerType : string ;
3838 isEnabled : boolean ;
3939 notes : string ;
40+ billingType ?: 'pay-per-use' | 'subscription' | 'free' ;
41+ subscriptionCostUsd ?: number ;
42+ subscriptionPlan ?: string ;
4043 } > ( {
4144 baseUrl : '' ,
4245 providerType : '' ,
4346 isEnabled : true ,
4447 notes : '' ,
48+ billingType : 'pay-per-use' ,
4549 } ) ;
4650 const [ saving , setSaving ] = useState ( false ) ;
4751
@@ -85,6 +89,9 @@ export function ProvidersTab() {
8589 providerType : override . providerType || ( baseConfig as Record < string , string > ) . type || '' ,
8690 isEnabled : override . isEnabled !== false ,
8791 notes : override . notes || '' ,
92+ billingType : override . billingType ?? 'pay-per-use' ,
93+ subscriptionCostUsd : override . subscriptionCostUsd ,
94+ subscriptionPlan : override . subscriptionPlan ,
8895 } ) ;
8996 setEditingProvider ( providerId ) ;
9097 } catch {
@@ -101,6 +108,11 @@ export function ProvidersTab() {
101108 providerType : editForm . providerType || undefined ,
102109 isEnabled : editForm . isEnabled ,
103110 notes : editForm . notes || undefined ,
111+ billingType : editForm . billingType ,
112+ subscriptionCostUsd :
113+ editForm . billingType === 'subscription' ? editForm . subscriptionCostUsd : undefined ,
114+ subscriptionPlan :
115+ editForm . billingType === 'subscription' ? editForm . subscriptionPlan : undefined ,
104116 } ) ;
105117 // Refresh providers
106118 await fetchProviders ( ) ;
@@ -179,6 +191,17 @@ export function ProvidersTab() {
179191 Override
180192 </ span >
181193 ) }
194+ { provider . billingType === 'subscription' && (
195+ < span className = "text-xs px-1.5 py-0.5 bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300 rounded" >
196+ Subscription
197+ { provider . subscriptionCostUsd ? ` $${ provider . subscriptionCostUsd } /mo` : '' }
198+ </ span >
199+ ) }
200+ { provider . billingType === 'free' && (
201+ < span className = "text-xs px-1.5 py-0.5 bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 rounded" >
202+ Free
203+ </ span >
204+ ) }
182205 </ div >
183206
184207 < div className = "text-xs text-gray-500 dark:text-gray-400 space-y-1" >
@@ -433,6 +456,72 @@ export function ProvidersTab() {
433456 className = "w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
434457 />
435458 </ div >
459+
460+ { /* Billing Type */ }
461+ < div >
462+ < label className = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" >
463+ Billing Type
464+ </ label >
465+ < select
466+ value = { editForm . billingType ?? 'pay-per-use' }
467+ onChange = { ( e ) =>
468+ setEditForm ( ( f ) => ( { ...f , billingType : e . target . value as never } ) )
469+ }
470+ className = "w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
471+ >
472+ < option value = "pay-per-use" > Pay-per-use (API)</ option >
473+ < option value = "subscription" > Subscription (flat monthly fee)</ option >
474+ < option value = "free" > Free (local / free tier)</ option >
475+ </ select >
476+ < p className = "mt-1 text-xs text-gray-500" >
477+ Pay-per-use: billed per token. Subscription: fixed monthly fee, no per-token
478+ cost. Free: no cost.
479+ </ p >
480+ </ div >
481+
482+ { /* Subscription details (only if subscription) */ }
483+ { editForm . billingType === 'subscription' && (
484+ < div className = "grid grid-cols-2 gap-3" >
485+ < div >
486+ < label className = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" >
487+ Monthly Cost (USD)
488+ </ label >
489+ < input
490+ type = "number"
491+ step = "0.01"
492+ min = "0"
493+ value = { editForm . subscriptionCostUsd ?? '' }
494+ onChange = { ( e ) =>
495+ setEditForm ( ( f ) => ( {
496+ ...f ,
497+ subscriptionCostUsd : e . target . value
498+ ? parseFloat ( e . target . value )
499+ : undefined ,
500+ } ) )
501+ }
502+ placeholder = "e.g. 20.00"
503+ className = "w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
504+ />
505+ </ div >
506+ < div >
507+ < label className = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" >
508+ Plan Name
509+ </ label >
510+ < input
511+ type = "text"
512+ value = { editForm . subscriptionPlan ?? '' }
513+ onChange = { ( e ) =>
514+ setEditForm ( ( f ) => ( {
515+ ...f ,
516+ subscriptionPlan : e . target . value || undefined ,
517+ } ) )
518+ }
519+ placeholder = "e.g. ChatGPT Plus"
520+ className = "w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
521+ />
522+ </ div >
523+ </ div >
524+ ) }
436525 </ div >
437526
438527 { /* Actions */ }
0 commit comments