fix(ui): replace misleading 'Expires' with 'Last used' in credential health

- credential-health-list.tsx showed 'Expires: Expired' based on stale
  file state (CLIProxyAPIPlus intentionally doesn't persist refreshed
  token expiry to disk)
- Now shows runtime-based 'Last used: Xm/h/d ago' from useCliproxyStats
- Consistent with account-item.tsx changes from previous commit
This commit is contained in:
kaitranntt committed 2025-12-29 13:15:51 -05:00
1 parent 6ccf6c5e13
commit 4233415095
1 file changed
+38 -24
@@ -6,8 +6,9 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { CheckCircle2, AlertCircle, XCircle, MinusCircle, RefreshCw } from 'lucide-react'; import { CheckCircle2, AlertCircle, XCircle, MinusCircle, RefreshCw, Clock } from 'lucide-react';
import { useCliproxyAuth } from '@/hooks/use-cliproxy'; import { useCliproxyAuth } from '@/hooks/use-cliproxy';
import { useCliproxyStats } from '@/hooks/use-cliproxy-stats';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { usePrivacy, PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context'; import { usePrivacy, PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
@@ -19,7 +20,7 @@ interface CredentialRowProps {
status: CredentialStatus; status: CredentialStatus;
statusMessage: string; statusMessage: string;
email?: string; email?: string;
expiresAt?: string; lastUsedAt?: string;
onRefresh?: () => void; onRefresh?: () => void;
privacyMode?: boolean; privacyMode?: boolean;
} }
@@ -30,7 +31,7 @@ function CredentialRow({
status, status,
statusMessage, statusMessage,
email, email,
expiresAt, lastUsedAt,
onRefresh, onRefresh,
privacyMode, privacyMode,
}: CredentialRowProps) { }: CredentialRowProps) {
@@ -60,16 +61,23 @@ function CredentialRow({
const config = statusConfig[status]; const config = statusConfig[status];
const Icon = config.icon; const Icon = config.icon;
const formatExpiry = (date?: string) => { const formatLastUsed = (date?: string) => {
if (!date) return 'Never'; if (!date) return 'Never used';
const expiry = new Date(date); try {
const now = new Date(); const lastUsed = new Date(date);
const diff = expiry.getTime() - now.getTime(); const now = new Date();
if (diff < 0) return 'Expired'; const diff = now.getTime() - lastUsed.getTime();
const hours = Math.floor(diff / 3600000); if (diff < 0) return 'Just now';
if (hours < 1) return 'Soon'; const minutes = Math.floor(diff / 60000);
if (hours < 24) return `${hours}h`; const hours = Math.floor(diff / 3600000);
return `${Math.floor(hours / 24)}d`; const days = Math.floor(diff / 86400000);
if (days > 0) return `${days}d ago`;
if (hours > 0) return `${hours}h ago`;
if (minutes > 0) return `${minutes}m ago`;
return 'Just now';
} catch {
return 'Unknown';
}
}; };
return ( return (
@@ -96,8 +104,9 @@ function CredentialRow({
> >
{statusMessage} {statusMessage}
</Badge> </Badge>
<div className="text-xs text-muted-foreground mt-0.5"> <div className="flex items-center gap-1 text-xs text-muted-foreground mt-0.5 justify-end">
Expires: {formatExpiry(expiresAt)} <Clock className="w-3 h-3" />
{formatLastUsed(lastUsedAt)}
</div> </div>
</div> </div>
{status === 'warning' && onRefresh && ( {status === 'warning' && onRefresh && (
@@ -129,23 +138,28 @@ function CredentialHealthSkeleton() {
export function CredentialHealthList() { export function CredentialHealthList() {
const { data: authData, isLoading } = useCliproxyAuth(); const { data: authData, isLoading } = useCliproxyAuth();
const { data: stats } = useCliproxyStats(true);
const { privacyMode } = usePrivacy(); const { privacyMode } = usePrivacy();
if (isLoading) { if (isLoading) {
return <CredentialHealthSkeleton />; return <CredentialHealthSkeleton />;
} }
// Flatten accounts from all providers // Flatten accounts from all providers with runtime lastUsedAt
const credentials = const credentials =
authData?.authStatus.flatMap((status) => authData?.authStatus.flatMap((status) =>
(status.accounts ?? []).map((account) => ({ (status.accounts ?? []).map((account) => {
name: account.id, const accountKey = account.email || account.id;
provider: status.provider, const runtimeLastUsed = stats?.accountStats?.[accountKey]?.lastUsedAt;
status: (account as { status?: CredentialStatus }).status ?? 'ready', return {
statusMessage: (account as { statusMessage?: string }).statusMessage ?? 'Ready', name: account.id,
email: account.email, provider: status.provider,
expiresAt: (account as { expiresAt?: string }).expiresAt, status: (account as { status?: CredentialStatus }).status ?? 'ready',
})) statusMessage: (account as { statusMessage?: string }).statusMessage ?? 'Ready',
email: account.email,
lastUsedAt: runtimeLastUsed || account.lastUsedAt,
};
})
) ?? []; ) ?? [];
if (credentials.length === 0) { if (credentials.length === 0) {