diff --git a/ui/src/components/account/flow-viz/account-card.tsx b/ui/src/components/account/flow-viz/account-card.tsx index 6e71c344..4fdf42a8 100644 --- a/ui/src/components/account/flow-viz/account-card.tsx +++ b/ui/src/components/account/flow-viz/account-card.tsx @@ -4,6 +4,7 @@ import { cn, + formatQuotaPercent, getCodexQuotaBreakdown, getProviderMinQuota, getProviderResetTime, @@ -110,6 +111,7 @@ export function AccountCard({ { label: '5h', value: codexBreakdown?.fiveHourWindow?.remainingPercent ?? null }, { label: 'Wk', value: codexBreakdown?.weeklyWindow?.remainingPercent ?? null }, ].filter((row): row is { label: string; value: number } => row.value !== null); + const minQuotaLabel = minQuota !== null ? formatQuotaPercent(minQuota) : null; // Tier badge (AGY only) - show P for Pro, U for Ultra const showTierBadge = @@ -240,7 +242,7 @@ export function AccountCard({ : 'text-red-500' )} > - {minQuota}% + {minQuotaLabel}% {account.provider === 'codex' && codexQuotaRows.length > 0 && ( diff --git a/ui/src/components/cliproxy/provider-editor/account-item.tsx b/ui/src/components/cliproxy/provider-editor/account-item.tsx index f258c0c1..17dafc52 100644 --- a/ui/src/components/cliproxy/provider-editor/account-item.tsx +++ b/ui/src/components/cliproxy/provider-editor/account-item.tsx @@ -32,6 +32,7 @@ import { } from 'lucide-react'; import { cn, + formatQuotaPercent, getCodexQuotaBreakdown, getProviderMinQuota, getProviderResetTime, @@ -129,6 +130,7 @@ export function AccountItem({ { label: '5h', value: codexBreakdown?.fiveHourWindow?.remainingPercent ?? null }, { label: 'Weekly', value: codexBreakdown?.weeklyWindow?.remainingPercent ?? null }, ].filter((row): row is { label: string; value: number } => row.value !== null); + const minQuotaLabel = minQuota !== null ? formatQuotaPercent(minQuota) : null; return (
- {minQuota}% + + {minQuotaLabel}% +
)} diff --git a/ui/src/components/shared/quota-tooltip-content.tsx b/ui/src/components/shared/quota-tooltip-content.tsx index 4e3406d6..8030c16a 100644 --- a/ui/src/components/shared/quota-tooltip-content.tsx +++ b/ui/src/components/shared/quota-tooltip-content.tsx @@ -6,6 +6,7 @@ import { Clock } from 'lucide-react'; import { cn, + formatQuotaPercent, formatResetTime, getCodexQuotaBreakdown, getCodexWindowDisplayLabel, @@ -24,6 +25,16 @@ interface QuotaTooltipContentProps { resetTime: string | null; } +function formatPlanLabel(planType: string | null | undefined): string | null { + if (!planType) return null; + const normalized = planType + .split(/[\s_-]+/g) + .map((part) => part.trim()) + .filter((part) => part.length > 0) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)); + return normalized.length > 0 ? normalized.join(' ') : planType; +} + /** * Renders provider-specific quota tooltip content * Uses type guards for proper TypeScript narrowing @@ -131,24 +142,32 @@ export function QuotaTooltipContent({ quota, resetTime }: QuotaTooltipContentPro { label: 'Completions', snapshot: quota.snapshots.completions }, ]; const effectiveResetTime = quota.quotaResetDate ?? resetTime; + const planLabel = formatPlanLabel(quota.planType); return (

Quota Snapshots:

- {quota.planType &&

Plan: {quota.planType}

} - {snapshotRows.map(({ label, snapshot }) => ( -
- - {label} - {snapshot.unlimited ? ' (Unlimited)' : ''} - - - {snapshot.unlimited - ? 'inf' - : `${snapshot.percentRemaining}% (${snapshot.remaining}/${snapshot.entitlement})`} - -
- ))} + {planLabel &&

Plan: {planLabel}

} + {snapshotRows.map(({ label, snapshot }) => { + const isLow = snapshot.percentRemaining < 20; + return ( +
+
+ {label} + + {snapshot.unlimited + ? 'Unlimited' + : `${formatQuotaPercent(snapshot.percentRemaining)}%`} + +
+ {!snapshot.unlimited && ( +
+ {snapshot.remaining}/{snapshot.entitlement} remaining +
+ )} +
+ ); + })}
); diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index e4743f68..faca5f72 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -13,6 +13,16 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } +/** + * Format quota percentage for UI display. + * Uses rounded whole numbers to keep quota labels compact and consistent. + */ +export function formatQuotaPercent(value: number): string { + if (!Number.isFinite(value)) return '0'; + const clamped = Math.max(0, Math.min(100, value)); + return `${Math.round(clamped)}`; +} + // Vibrant Tones Palette const VIBRANT_TONES = [ '#f94144', // Strawberry Red