mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 04:14:57 +00:00
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:
1 parent
1f323f082c
commit
ce16517144
3 files changed
+64
-9
No files matched your search
@@ -8,6 +8,7 @@ import {
|
|||||||
formatResetTime,
|
formatResetTime,
|
||||||
getClaudeResetTime,
|
getClaudeResetTime,
|
||||||
getMinClaudeQuota,
|
getMinClaudeQuota,
|
||||||
|
getModelsWithExhaustedIndicator,
|
||||||
} from '@/lib/utils';
|
} from '@/lib/utils';
|
||||||
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
|
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
|
||||||
import { GripVertical, Loader2, Clock, Pause, Play } from 'lucide-react';
|
import { GripVertical, Loader2, Clock, Pause, Play } from 'lucide-react';
|
||||||
@@ -219,12 +220,28 @@ export function AccountCard({
|
|||||||
<TooltipContent side="top" className="max-w-xs">
|
<TooltipContent side="top" className="max-w-xs">
|
||||||
<div className="text-xs space-y-1">
|
<div className="text-xs space-y-1">
|
||||||
<p className="font-medium">Model Quotas:</p>
|
<p className="font-medium">Model Quotas:</p>
|
||||||
{sortModelsByPriority(quota?.models || []).map((m) => (
|
{sortModelsByPriority(getModelsWithExhaustedIndicator(quota?.models || [])).map(
|
||||||
<div key={m.name} className="flex justify-between gap-4">
|
(m) => (
|
||||||
<span className="truncate">{m.displayName || m.name}</span>
|
<div key={m.name} className="flex justify-between gap-4">
|
||||||
<span className="font-mono">{m.percentage}%</span>
|
<span
|
||||||
</div>
|
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 || []);
|
const resetTime = getClaudeResetTime(quota?.models || []);
|
||||||
return resetTime ? (
|
return resetTime ? (
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
formatResetTime,
|
formatResetTime,
|
||||||
getClaudeResetTime,
|
getClaudeResetTime,
|
||||||
getMinClaudeQuota,
|
getMinClaudeQuota,
|
||||||
|
getModelsWithExhaustedIndicator,
|
||||||
} from '@/lib/utils';
|
} from '@/lib/utils';
|
||||||
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
|
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
|
||||||
import { useAccountQuota, useCliproxyStats } from '@/hooks/use-cliproxy-stats';
|
import { useAccountQuota, useCliproxyStats } from '@/hooks/use-cliproxy-stats';
|
||||||
@@ -349,10 +350,26 @@ export function AccountItem({
|
|||||||
<TooltipContent side="bottom" className="max-w-xs">
|
<TooltipContent side="bottom" className="max-w-xs">
|
||||||
<div className="text-xs space-y-1">
|
<div className="text-xs space-y-1">
|
||||||
<p className="font-medium">Model Quotas:</p>
|
<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">
|
<div key={m.name} className="flex justify-between gap-4">
|
||||||
<span className="truncate">{m.displayName || m.name}</span>
|
<span
|
||||||
<span className="font-mono">{m.percentage}%</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>
|
</div>
|
||||||
))}
|
))}
|
||||||
{nextReset && (
|
{nextReset && (
|
||||||
|
|||||||
@@ -184,3 +184,24 @@ export function getClaudeResetTime<
|
|||||||
null as string | null
|
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,
|
||||||
|
];
|
||||||
|
}
|
||||||
Reference in new issue
Block a user