payemnt billing and credits refactoring

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-20 07:39:51 +00:00
parent a97c72640a
commit bc50b022f1
34 changed files with 3028 additions and 307 deletions

View File

@@ -368,7 +368,7 @@ export default function SiteDashboard() {
/>
<CreditAvailabilityWidget
availableCredits={balance?.credits_remaining ?? 0}
availableCredits={(balance as any)?.total_credits ?? balance?.credits_remaining ?? 0}
totalCredits={balance?.plan_credits_per_month ?? 0}
usedCredits={balance?.credits_used_this_month ?? 0}
loading={loading}

View File

@@ -921,16 +921,26 @@ export default function PlansAndBillingPage() {
</div>
{/* Quick Stats */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div className="p-4 bg-white/80 dark:bg-gray-800/60 rounded-xl shadow-sm">
<div className="flex items-center gap-2 text-sm text-brand-700 dark:text-brand-300 mb-1">
<ZapIcon className="w-4 h-4 text-brand-600" />
Credits
Plan Credits
</div>
<div className="text-2xl font-bold text-brand-900 dark:text-white">
{creditBalance?.credits?.toLocaleString() || 0}
</div>
<div className="text-xs text-brand-600 dark:text-brand-400 mt-1">Available now</div>
<div className="text-xs text-brand-600 dark:text-brand-400 mt-1">Reset on renewal</div>
</div>
<div className="p-4 bg-white/80 dark:bg-gray-800/60 rounded-xl shadow-sm border-l-4 border-l-success-500">
<div className="flex items-center gap-2 text-sm text-success-700 dark:text-success-300 mb-1">
<ZapIcon className="w-4 h-4 text-success-600" />
Bonus Credits
</div>
<div className="text-2xl font-bold text-success-600 dark:text-success-400">
{((creditBalance as any)?.bonus_credits || 0).toLocaleString()}
</div>
<div className="text-xs text-success-600 dark:text-success-400 mt-1">Never expire</div>
</div>
<div className="p-4 bg-white/80 dark:bg-gray-800/60 rounded-xl shadow-sm">
<div className="flex items-center gap-2 text-sm text-purple-700 dark:text-purple-300 mb-1">
@@ -972,6 +982,21 @@ export default function PlansAndBillingPage() {
</div>
</div>
{/* Total Credits Info Box */}
{((creditBalance as any)?.bonus_credits || 0) > 0 && (
<div className="mt-4 p-3 bg-white/60 dark:bg-gray-800/60 rounded-lg border border-success-200 dark:border-success-800">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Total Available:</span>
<span className="text-lg font-bold text-brand-700 dark:text-brand-400">
{((creditBalance?.credits || 0) + ((creditBalance as any)?.bonus_credits || 0)).toLocaleString()} credits
</span>
</div>
<span className="text-xs text-gray-500 dark:text-gray-400">Plan + Bonus</span>
</div>
</div>
)}
{/* Credit Usage Bar */}
<div className="mt-6 pt-6 border-t border-brand-200/50 dark:border-brand-700/30">
<div className="flex justify-between text-sm mb-2">
@@ -1319,17 +1344,22 @@ export default function PlansAndBillingPage() {
)}
</td>
<td className="px-6 py-3 text-center">
<Badge variant="soft" tone={invoice.status === 'paid' ? 'success' : 'warning'}>
<Badge variant="soft" tone={
invoice.status === 'paid' ? 'success' :
invoice.status === 'failed' ? 'error' :
invoice.status === 'overdue' ? 'error' :
'warning'
}>
{invoice.status}
</Badge>
</td>
<td className="px-6 py-3 text-end">
<div className="flex items-center justify-end gap-2">
{invoice.status === 'pending' && (
{['pending', 'overdue', 'failed', 'sent'].includes(invoice.status) && (
<Button
size="sm"
variant="primary"
tone="brand"
tone={invoice.status === 'overdue' || invoice.status === 'failed' ? 'error' : 'brand'}
startIcon={<DollarLineIcon className="w-4 h-4" />}
onClick={() => {
setSelectedInvoice(invoice);

View File

@@ -469,27 +469,49 @@ export default function UsageDashboardPage() {
</Link>
</div>
<div className="grid grid-cols-3 gap-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<div className="text-4xl font-bold text-brand-700 dark:text-brand-400 mb-1">
<div className="text-3xl font-bold text-brand-700 dark:text-brand-400 mb-1">
{creditBalance?.credits.toLocaleString() || 0}
</div>
<div className="text-sm text-brand-600 dark:text-brand-300">Available Now</div>
<div className="text-sm text-brand-600 dark:text-brand-300">Plan Credits</div>
</div>
<div>
<div className="text-4xl font-bold text-purple-700 dark:text-purple-400 mb-1">
<div className="text-3xl font-bold text-success-600 dark:text-success-400 mb-1">
{((creditBalance as any)?.bonus_credits || 0).toLocaleString()}
</div>
<div className="text-sm text-success-600 dark:text-success-300">Bonus Credits</div>
<div className="text-xs text-gray-500 dark:text-gray-400">Never expire</div>
</div>
<div>
<div className="text-3xl font-bold text-purple-700 dark:text-purple-400 mb-1">
{creditBalance?.credits_used_this_month.toLocaleString() || 0}
</div>
<div className="text-sm text-purple-600 dark:text-purple-300">Used This Month</div>
</div>
<div>
<div className="text-4xl font-bold text-indigo-800 dark:text-white mb-1">
<div className="text-3xl font-bold text-indigo-800 dark:text-white mb-1">
{creditBalance?.plan_credits_per_month.toLocaleString() || 0}
</div>
<div className="text-sm text-indigo-600 dark:text-indigo-300">Monthly Allowance</div>
</div>
</div>
{/* Total Available Credits */}
{((creditBalance as any)?.bonus_credits || 0) > 0 && (
<div className="mt-4 p-3 bg-white/60 dark:bg-gray-800/60 rounded-lg border border-brand-200 dark:border-brand-700">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Total Available</span>
<span className="text-lg font-bold text-brand-700 dark:text-brand-400">
{((creditBalance?.credits || 0) + ((creditBalance as any)?.bonus_credits || 0)).toLocaleString()}
</span>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
Plan credits are used first, then bonus credits
</p>
</div>
)}
{/* Credit Usage Bar */}
<div className="mt-6">
<div className="flex justify-between text-sm mb-2">