feat(accounts): preserve codex variant detail in dashboard cards

This commit is contained in:
Tam Nhu Tran
2026-04-14 18:09:23 -04:00
parent 7279d4889a
commit 32c16a3739
4 changed files with 164 additions and 39 deletions
@@ -13,6 +13,7 @@ import {
getProviderResetTime,
getQuotaFailureInfo,
} from '@/lib/utils';
import { formatAccountVariantPart } from '@/lib/account-identity';
import { GripVertical, Loader2, Pause, Play } from 'lucide-react';
import {
useAccountQuota,
@@ -87,29 +88,110 @@ function getCompactQuotaColor(percentage: number) {
return 'bg-red-500';
}
function getVariantMarkerLabel(audience: string, fallbackLabel?: string | null) {
if (audience === 'business') return 'Biz';
if (audience === 'personal') return 'Pers';
function getCodexPlanDetailLabel(quota: unknown): string | null {
if (!quota || typeof quota !== 'object' || !('planType' in quota)) {
return null;
}
const normalizedFallback = fallbackLabel?.trim();
const planType = (quota as { planType?: unknown }).planType;
if (typeof planType !== 'string' || planType.trim().length === 0) {
return null;
}
return formatAccountVariantPart(planType);
}
function getVariantDetailLabel(
variant: {
audience: string;
detailLabel?: string | null;
compactDetailLabel?: string | null;
},
quota?: unknown
) {
const codexPlanDetail = getCodexPlanDetailLabel(quota);
return variant.detailLabel ?? variant.compactDetailLabel ?? codexPlanDetail;
}
function getVariantCompactDetailLabel(
variant: {
audience: string;
compactDetailLabel?: string | null;
detailLabel?: string | null;
},
quota?: unknown
) {
const codexPlanDetail = getCodexPlanDetailLabel(quota);
return variant.compactDetailLabel ?? variant.detailLabel ?? codexPlanDetail;
}
function getVariantInlineLabel(
variant: {
audience: string;
audienceLabel?: string | null;
detailLabel?: string | null;
compactDetailLabel?: string | null;
inlineLabel?: string | null;
},
quota?: unknown
) {
const detailLabel = getVariantDetailLabel(variant, quota);
const composedLabel = [variant.audienceLabel, detailLabel].filter(Boolean).join(' · ');
return composedLabel || variant.inlineLabel || null;
}
function getVariantMarkerLabel(
variant: {
audience: string;
audienceLabel?: string | null;
detailLabel?: string | null;
compactDetailLabel?: string | null;
},
audienceCounts: Map<string, number>,
quota?: unknown
) {
const compactDetailLabel = getVariantCompactDetailLabel(variant, quota);
if (variant.audience === 'business') {
const businessVariantCount = audienceCounts.get('business') ?? 0;
return businessVariantCount > 1 && compactDetailLabel ? compactDetailLabel : 'Biz';
}
if (variant.audience === 'personal') {
return compactDetailLabel ?? 'Pers';
}
const normalizedFallback =
compactDetailLabel?.trim() || variant.audienceLabel?.trim() || variant.detailLabel?.trim();
return normalizedFallback?.[0]?.toUpperCase() ?? '?';
}
function getGroupedVariantSummaryLabel(
variants: Array<{ audience: string; audienceLabel?: string | null; detailLabel?: string | null }>
variants: Array<{
audience: string;
audienceLabel?: string | null;
detailLabel?: string | null;
compactDetailLabel?: string | null;
}>,
quotas: Array<unknown>,
audienceCounts: Map<string, number>
) {
const audiences = new Set(variants.map((variant) => variant.audience));
const hasDistinctDetails = variants.some((variant, index) =>
Boolean(getVariantCompactDetailLabel(variant, quotas[index]))
);
if (audiences.size === 2 && audiences.has('business') && audiences.has('personal')) {
if (
!hasDistinctDetails &&
variants.length === 2 &&
audiences.size === 2 &&
audiences.has('business') &&
audiences.has('personal')
) {
return 'B|P';
}
if (variants.length === 1) {
const [variant] = variants;
return getVariantMarkerLabel(
variant.audience,
variant.audienceLabel ?? variant.detailLabel ?? null
);
return getVariantMarkerLabel(variant, audienceCounts, quotas[0]);
}
return null;
@@ -153,20 +235,19 @@ export function AccountCard({
})),
showQuota && hasGroupedVariants
);
const groupedHeaderVariants = hasGroupedVariants
? Array.from(
new Map(
(account.variants ?? [])
.slice()
.sort((left, right) => {
const order = { business: 0, personal: 1, unknown: 2 } as const;
return order[left.audience] - order[right.audience];
})
.map((variant) => [variant.audienceLabel ?? variant.detailLabel ?? variant.id, variant])
).values()
)
: [];
const groupedVariantSummaryLabel = getGroupedVariantSummaryLabel(groupedHeaderVariants);
const groupedHeaderVariants = hasGroupedVariants ? (account.variants ?? []) : [];
const groupedVariantQuotas = groupedHeaderVariants.map(
(_, index) => variantQuotaQueries[index]?.data
);
const groupedVariantAudienceCounts = groupedHeaderVariants.reduce((counts, variant) => {
counts.set(variant.audience, (counts.get(variant.audience) ?? 0) + 1);
return counts;
}, new Map<string, number>());
const groupedVariantSummaryLabel = getGroupedVariantSummaryLabel(
groupedHeaderVariants,
groupedVariantQuotas,
groupedVariantAudienceCounts
);
const compactMetaBadges = hasGroupedVariants ? (
<>
@@ -174,8 +255,9 @@ export function AccountCard({
className="inline-flex shrink-0 items-center overflow-hidden rounded-md border border-border/60 bg-muted/60 shadow-sm shadow-black/5 dark:bg-zinc-900/80"
title={groupedHeaderVariants
.map(
(variant) =>
variant.audienceLabel ?? variant.detailLabel ?? t('accountSurfaceCard.variant')
(variant, index) =>
getVariantInlineLabel(variant, groupedVariantQuotas[index]) ??
t('accountSurfaceCard.variant')
)
.join(' • ')}
>
@@ -198,8 +280,9 @@ export function AccountCard({
)}
>
{getVariantMarkerLabel(
variant.audience,
variant.audienceLabel ?? variant.detailLabel ?? null
variant,
groupedVariantAudienceCounts,
groupedVariantQuotas[index]
)}
</span>
))
@@ -224,7 +307,7 @@ export function AccountCard({
const quotaLabel = minQuota !== null ? formatQuotaPercent(minQuota) : null;
const quotaValue = quotaLabel !== null ? Number(quotaLabel) : null;
const failureInfo = getQuotaFailureInfo(quota);
const label = variant.audienceLabel ?? variant.detailLabel ?? cleanEmail(variant.email);
const label = getVariantInlineLabel(variant, quota) ?? cleanEmail(variant.email);
return (
<Tooltip key={variant.id}>
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
import { Badge } from '@/components/ui/badge';
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
import type { UnifiedQuotaResult } from '@/hooks/use-cliproxy-stats';
import { getAccountIdentityPresentation } from '@/lib/account-identity';
import { formatAccountVariantPart, getAccountIdentityPresentation } from '@/lib/account-identity';
import { cn } from '@/lib/utils';
import { Pause, Star, User } from 'lucide-react';
import { useTranslation } from 'react-i18next';
@@ -63,6 +63,18 @@ function getCompactAudienceBadgeLabel(
return '?';
}
function getCompactDetailBadgeClass(audience: 'business' | 'personal' | 'unknown') {
if (audience === 'business') {
return 'border-sky-500/30 bg-sky-500/10 text-sky-700 dark:border-sky-400/30 dark:bg-sky-500/15 dark:text-sky-200';
}
if (audience === 'personal') {
return 'border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:border-emerald-400/30 dark:bg-emerald-500/15 dark:text-emerald-200';
}
return 'border-border/60 bg-muted/60 text-muted-foreground';
}
function resolveEffectiveTier(
tier: AccountTier | undefined,
quota: UnifiedQuotaResult | undefined
@@ -76,6 +88,14 @@ function resolveEffectiveTier(
return tier;
}
function getCodexPlanDetailLabel(quota: UnifiedQuotaResult | undefined): string | null {
if (!quota || !('planType' in quota) || !quota.planType) {
return null;
}
return formatAccountVariantPart(quota.planType);
}
export function AccountSurfaceCard({
mode,
provider,
@@ -104,6 +124,10 @@ export function AccountSurfaceCard({
const title = displayEmail || identity.email || accountId;
const normalizedProvider = provider.toLowerCase();
const effectiveTier = resolveEffectiveTier(tier, quota);
const codexPlanDetailLabel =
normalizedProvider === 'codex' ? getCodexPlanDetailLabel(quota) : null;
const resolvedDetailLabel = identity.detailLabel ?? codexPlanDetailLabel;
const resolvedCompactDetailLabel = identity.compactDetailLabel ?? codexPlanDetailLabel;
const showTierBadge =
(normalizedProvider === 'agy' ||
normalizedProvider === 'antigravity' ||
@@ -137,6 +161,17 @@ export function AccountSurfaceCard({
{getCompactAudienceBadgeLabel(identity.audience, t)}
</span>
)}
{resolvedCompactDetailLabel && (
<span
title={resolvedDetailLabel ?? resolvedCompactDetailLabel}
className={cn(
'text-[8px] font-semibold px-1.5 py-0.5 rounded-md border shrink-0',
getCompactDetailBadgeClass(identity.audience)
)}
>
{resolvedCompactDetailLabel}
</span>
)}
{paused && (
<span className="text-[8px] font-semibold px-1.5 py-0.5 rounded-md shrink-0 bg-amber-500/15 text-amber-700 dark:bg-amber-500/25 dark:text-amber-300">
{/* TODO i18n: missing key for compact "Paused" badge */}
@@ -203,9 +238,9 @@ export function AccountSurfaceCard({
{identity.audienceLabel}
</Badge>
)}
{!isCompact && identity.detailLabel && (
{!isCompact && resolvedDetailLabel && (
<Badge variant="outline" className="text-[10px] h-4 px-1.5">
{identity.detailLabel}
{resolvedDetailLabel}
</Badge>
)}
{!isCompact && isDefault && (
+8 -5
View File
@@ -24,7 +24,7 @@ function normalizeVariantTokenPart(value: string): string {
.toLowerCase();
}
function formatVariantPart(part: string): string {
export function formatAccountVariantPart(part: string): string {
const normalized = part.trim().toLowerCase();
if (!normalized) {
return '';
@@ -103,7 +103,7 @@ function formatWorkspaceLabel(parts: string[]): {
};
}
const extraLabel = parts.map(formatVariantPart).filter(Boolean).join(' · ');
const extraLabel = parts.map(formatAccountVariantPart).filter(Boolean).join(' · ');
return {
detailLabel: extraLabel || 'Team', // TODO i18n: missing key for team fallback
compactDetailLabel: extraLabel || 'Team',
@@ -167,10 +167,13 @@ export function getAccountIdentityPresentation(
}
if (suffix && PERSONAL_PLAN_PARTS.has(suffix)) {
const detailParts = [formatVariantPart(suffix), ...parts.slice(0, -1).map(formatVariantPart)]
const detailParts = [
formatAccountVariantPart(suffix),
...parts.slice(0, -1).map(formatAccountVariantPart),
]
.filter(Boolean)
.join(' · ');
const detailLabel = detailParts || formatVariantPart(suffix);
const detailLabel = detailParts || formatAccountVariantPart(suffix);
const inlineLabel = ['Personal', detailLabel].filter(Boolean).join(' · '); // TODO i18n: missing key for Personal
return {
email: resolvedEmail,
@@ -182,7 +185,7 @@ export function getAccountIdentityPresentation(
};
}
const fallbackLabel = parts.map(formatVariantPart).filter(Boolean).join(' · ');
const fallbackLabel = parts.map(formatAccountVariantPart).filter(Boolean).join(' · ');
return {
email: resolvedEmail,
audience: 'unknown',
+6 -2
View File
@@ -16,6 +16,8 @@ export interface AccountVisualVariant {
audience: AccountAudience;
audienceLabel: string | null;
detailLabel: string | null;
compactDetailLabel: string | null;
inlineLabel: string | null;
}
export interface AccountVisualGroup {
@@ -67,6 +69,8 @@ function buildAccountVariant(
audience: identity.audience,
audienceLabel: identity.audienceLabel,
detailLabel: identity.detailLabel,
compactDetailLabel: identity.compactDetailLabel,
inlineLabel: identity.inlineLabel,
};
}
@@ -77,8 +81,8 @@ function sortAccountVariants(variants: AccountVisualVariant[]): AccountVisualVar
return audienceDelta;
}
const leftLabel = left.audienceLabel ?? left.detailLabel ?? left.id;
const rightLabel = right.audienceLabel ?? right.detailLabel ?? right.id;
const leftLabel = left.inlineLabel ?? left.audienceLabel ?? left.detailLabel ?? left.id;
const rightLabel = right.inlineLabel ?? right.audienceLabel ?? right.detailLabel ?? right.id;
return leftLabel.localeCompare(rightLabel);
});