fix(ui): display exhausted Claude/GPT models in quota tooltip

When Claude/GPT models are exhausted (0%), API removes them from response.
Added getModelsWithExhaustedIndicator() to prepend synthetic "Claude/GPT
(Exhausted)" entry with 0% when primary models missing. Styled in red for
visibility. Applied to both account-item and flow-viz account-card tooltips.
This commit is contained in:
kaitranntt
2026-01-25 16:51:49 -05:00
parent 1f323f082c
commit ce16517144
3 changed files with 64 additions and 9 deletions
@@ -8,6 +8,7 @@ import {
formatResetTime,
getClaudeResetTime,
getMinClaudeQuota,
getModelsWithExhaustedIndicator,
} from '@/lib/utils';
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
import { GripVertical, Loader2, Clock, Pause, Play } from 'lucide-react';
@@ -219,12 +220,28 @@ export function AccountCard({
<TooltipContent side="top" className="max-w-xs">
<div className="text-xs space-y-1">
<p className="font-medium">Model Quotas:</p>
{sortModelsByPriority(quota?.models || []).map((m) => (
<div key={m.name} className="flex justify-between gap-4">
<span className="truncate">{m.displayName || m.name}</span>
<span className="font-mono">{m.percentage}%</span>
</div>
))}
{sortModelsByPriority(getModelsWithExhaustedIndicator(quota?.models || [])).map(
(m) => (
<div key={m.name} className="flex justify-between gap-4">
<span
className={cn(
'truncate',
m.name === 'claude-exhausted' && 'text-red-500'
)}
>
{m.displayName || m.name}
</span>
<span
className={cn(
'font-mono',
m.name === 'claude-exhausted' && 'text-red-500'
)}
>
{m.percentage}%
</span>
</div>
)
)}
{(() => {
const resetTime = getClaudeResetTime(quota?.models || []);
return resetTime ? (
@@ -35,6 +35,7 @@ import {
formatResetTime,
getClaudeResetTime,
getMinClaudeQuota,
getModelsWithExhaustedIndicator,
} from '@/lib/utils';
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
import { useAccountQuota, useCliproxyStats } from '@/hooks/use-cliproxy-stats';
@@ -349,10 +350,26 @@ export function AccountItem({
<TooltipContent side="bottom" className="max-w-xs">
<div className="text-xs space-y-1">
<p className="font-medium">Model Quotas:</p>
{sortModelsByPriority(quota?.models || []).map((m) => (
{sortModelsByPriority(
getModelsWithExhaustedIndicator(quota?.models || [])
).map((m) => (
<div key={m.name} className="flex justify-between gap-4">
<span className="truncate">{m.displayName || m.name}</span>
<span className="font-mono">{m.percentage}%</span>
<span
className={cn(
'truncate',
m.name === 'claude-exhausted' && 'text-red-500'
)}
>
{m.displayName || m.name}
</span>
<span
className={cn(
'font-mono',
m.name === 'claude-exhausted' && 'text-red-500'
)}
>
{m.percentage}%
</span>
</div>
))}
{nextReset && (
+21
View File
@@ -184,3 +184,24 @@ export function getClaudeResetTime<
null as string | null
);
}
/**
* Augment models list with synthetic "Exhausted" entry when Claude/GPT models are missing.
* Used for tooltip display to show user that primary models are exhausted (0%).
*/
export function getModelsWithExhaustedIndicator<
T extends { name: string; displayName?: string; percentage: number },
>(models: T[]): (T | { name: string; displayName: string; percentage: number })[] {
if (models.length === 0) return [];
const primaryModels = filterPrimaryModels(models);
// If primary models exist, return as-is
if (primaryModels.length > 0) return models;
// Primary models exhausted - prepend synthetic entry
return [
{ name: 'claude-exhausted', displayName: 'Claude/GPT (Exhausted)', percentage: 0 },
...models,
];
}