'use client' import { useState } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { updateBillingConfig } from '@/app/actions/admin-billing' import { toast } from 'sonner' import { useLanguage } from '@/lib/i18n' import { CreditCard, Gauge, Coins, Package } from 'lucide-react' type BillingAdminData = { billingConfig: Record usageOverview: { period: string lastSyncedAt: string | null byFeature: Array<{ feature: string; requests: number; tokens: number; users: number }> topUsers: Array<{ userId: string; email: string; name: string | null; requests: number }> } tiers: string[] /** Allocations mensuelles du solde unique (source de vérité débit) */ creditAllocations?: Array<{ tier: string monthlyCredits: number | null unlimited: boolean }> creditCosts?: Record creditPacks?: Array<{ id: string credits: number defaultDisplay: string }> } const SUBSCRIPTION_PRICE_KEYS = [ 'STRIPE_PRICE_PRO_MONTHLY', 'STRIPE_PRICE_PRO_ANNUAL', 'STRIPE_PRICE_BUSINESS_MONTHLY', 'STRIPE_PRICE_BUSINESS_ANNUAL', ] as const const PACK_PRICE_KEYS = [ 'STRIPE_PRICE_CREDITS_S', 'STRIPE_PRICE_CREDITS_M', 'STRIPE_PRICE_CREDITS_L', ] as const /** Date stable SSR/client (pas de toLocaleString — mismatch locale + fuseau). */ function formatStableUtc(iso: string): string { const d = new Date(iso) if (Number.isNaN(d.getTime())) return iso const pad = (n: number) => String(n).padStart(2, '0') return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC` } export function BillingAdminClient({ initialData }: { initialData: BillingAdminData }) { const { t } = useLanguage() const [billingEnabled, setBillingEnabled] = useState(initialData.billingConfig.BILLING_ENABLED === 'true') const [isSavingBilling, setIsSavingBilling] = useState(false) const handleSaveBilling = async (formData: FormData) => { setIsSavingBilling(true) try { const data: Record = { BILLING_ENABLED: billingEnabled ? 'true' : 'false', } for (const key of [...SUBSCRIPTION_PRICE_KEYS, ...PACK_PRICE_KEYS]) { data[key] = String(formData.get(key) ?? '') } await updateBillingConfig(data) toast.success(t('admin.billing.configSaved')) } catch (e: unknown) { toast.error(e instanceof Error ? e.message : t('admin.billing.configFailed')) } finally { setIsSavingBilling(false) } } return (

{t('admin.billing.title')}

{t('admin.billing.description')}

{t('admin.billing.stripeConfigTitle')}

{t('admin.billing.stripeConfigDescription')}

{ e.preventDefault(); handleSaveBilling(new FormData(e.currentTarget)) }} className="p-6 space-y-6">
setBillingEnabled(!!c)} />

{t('admin.billing.subscriptionPricesTitle')}

{SUBSCRIPTION_PRICE_KEYS.map((key) => (
))}

{t('admin.billing.packPricesTitle')}

{t('admin.billing.packPricesDescription')}

{PACK_PRICE_KEYS.map((key) => (
))}

{t('admin.billing.secretsNote')}

{/* Solde unique — source de vérité du débit */}

{t('admin.billing.creditsTitle')}

{t('admin.billing.creditsDescription')}

{(initialData.creditAllocations ?? []).map((row) => (

{row.tier}

{row.unlimited ? t('admin.billing.modeUnlimited') : row.monthlyCredits?.toLocaleString('fr-FR')}

{!row.unlimited && (

{t('admin.billing.creditsPerMonth')}

)}
))}
{(initialData.creditPacks?.length ?? 0) > 0 && (

{t('admin.billing.packsCatalogTitle')}

{(initialData.creditPacks ?? []).map((pack) => ( {pack.id} {pack.credits.toLocaleString('fr-FR')} cr. · {pack.defaultDisplay} ))}
)}

{t('admin.billing.creditsCostsNote')}

{t('admin.billing.creditsResetHint')}

{t('admin.billing.usageTitle')}

{t('admin.billing.usagePeriod', { period: initialData.usageOverview.period })} {initialData.usageOverview.lastSyncedAt ? ` · ${t('admin.billing.lastSync', { // Format fixe UTC (évite mismatch SSR locale/fuseau vs client) date: formatStableUtc(initialData.usageOverview.lastSyncedAt), })}` : ` · ${t('admin.billing.notSynced')}`}

{t('admin.billing.byFeature')}

{initialData.usageOverview.byFeature.length === 0 ? (

{t('admin.billing.noUsageData')}

) : (
    {initialData.usageOverview.byFeature.map((row) => (
  • {row.feature} {row.requests} req · {row.tokens} tok
  • ))}
)}

{t('admin.billing.topUsers')}

{initialData.usageOverview.topUsers.length === 0 ? (

{t('admin.billing.noUsageData')}

) : (
    {initialData.usageOverview.topUsers.map((row) => (
  • {row.email} {row.requests} req
  • ))}
)}
) }