From 7cd661d726b8bda3e3f4259de3e5be4361359e1c Mon Sep 17 00:00:00 2001 From: Ramnique Singh <30795890+ramnique@users.noreply.github.com> Date: Sun, 24 May 2026 12:46:54 +0530 Subject: [PATCH] Show split monthly and daily credits - update billing contract to consume split monthly/daily usage - show monthly and daily credit percentage bars in account settings - keep sidebar plan labels normalized - update out-of-credits copy for daily credits --- .../components/settings/account-settings.tsx | 46 +++++++++++++++++-- .../src/components/sidebar-content.tsx | 7 ++- apps/x/apps/renderer/src/lib/billing-error.ts | 2 +- apps/x/packages/core/src/billing/billing.ts | 17 +++++-- apps/x/packages/shared/src/billing.ts | 13 +++++- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/apps/x/apps/renderer/src/components/settings/account-settings.tsx b/apps/x/apps/renderer/src/components/settings/account-settings.tsx index feee291b..c40f411e 100644 --- a/apps/x/apps/renderer/src/components/settings/account-settings.tsx +++ b/apps/x/apps/renderer/src/components/settings/account-settings.tsx @@ -17,11 +17,44 @@ import { import { Separator } from "@/components/ui/separator" import { useBilling } from "@/hooks/useBilling" import { toast } from "sonner" +import type { BillingUsageBucket } from "@x/shared/dist/billing.js" interface AccountSettingsProps { dialogOpen: boolean } +function formatPlanName(plan: string | null | undefined) { + if (!plan) return 'No Plan' + return `${plan.charAt(0).toUpperCase()}${plan.slice(1)} Plan` +} + +function CreditUsageBar({ label, bucket, helper }: { + label: string + bucket: BillingUsageBucket + helper?: string +}) { + const pct = bucket.sanctionedCredits > 0 + ? Math.min(100, Math.max(0, Math.round((bucket.usedCredits / bucket.sanctionedCredits) * 100))) + : 0 + + return ( +
{label}
+ {helper ?{helper}
: null} ++ {pct}% +
+- {billing.subscriptionPlan ? `${billing.subscriptionPlan} Plan` : 'No Plan'} + {formatPlanName(billing.subscriptionPlan)}
{billing.subscriptionStatus === 'trialing' && billing.trialExpiresAt ? (() => { const days = Math.max(0, Math.ceil((new Date(billing.trialExpiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))) @@ -179,14 +212,19 @@ export function AccountSettings({ dialogOpen }: AccountSettingsProps) { {!billing.subscriptionPlan && (Subscribe to access AI features
)} - {billing.subscriptionPlan === 'free' && ( -Free usage resets daily at 00:00 UTC.
- )}Unable to load plan details
diff --git a/apps/x/apps/renderer/src/components/sidebar-content.tsx b/apps/x/apps/renderer/src/components/sidebar-content.tsx index a679c4fd..dbb5edb5 100644 --- a/apps/x/apps/renderer/src/components/sidebar-content.tsx +++ b/apps/x/apps/renderer/src/components/sidebar-content.tsx @@ -96,6 +96,11 @@ function displayNoteName(node: TreeNode): string { return node.name } +function formatBillingPlanName(plan: string | null | undefined) { + if (!plan) return 'No plan' + return `${plan.charAt(0).toUpperCase()}${plan.slice(1)} plan` +} + function formatAgo(ms: number): string { const diffMs = Math.max(0, Date.now() - ms) const min = Math.floor(diffMs / 60000) @@ -921,7 +926,7 @@ export function SidebarContentPanel({