From fd42d3722d11ae4980bddd718e94105e42b5ce4f Mon Sep 17 00:00:00 2001 From: IanShaw027 Date: Tue, 18 Aug 2026 14:55:08 +0800 Subject: [PATCH] fix: hide Grok prepaid and used/limit when they are empty Skip the prepaid chip without a positive balance, and skip used/limit when monthly limit is 0. --- .../components/account/AccountUsageCell.vue | 35 +++++--- .../__tests__/AccountUsageCell.spec.ts | 88 +++++++++++++++++++ 2 files changed, 109 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue index 3291b9b30d..966b0a5e08 100644 --- a/frontend/src/components/account/AccountUsageCell.vue +++ b/frontend/src/components/account/AccountUsageCell.vue @@ -397,12 +397,16 @@ class="flex flex-wrap items-center gap-1 text-[10px] text-gray-500 dark:text-gray-400" > {{ t('admin.accounts.usageWindow.grokPrepaid') }} ${{ grokPrepaidMoneyLine.prepaid }} - + {{ t('admin.accounts.usageWindow.grokUsed') }} {{ grokPrepaidMoneyLine.used }}/{{ grokPrepaidMoneyLine.limit }} @@ -1164,30 +1168,33 @@ const formatGrokMoney = (value?: number | null) => { if (value >= 10) return value.toFixed(1) return value.toFixed(2) } -// Prepaid money line for paid Grok: show when prepaid_balance is present. -// Monthly used/limit numbers are optional context; primary progress is the 30d bar. +// Prepaid chip only when there is a positive prepaid balance. +// Used/limit only when monthly limit is a positive number (0 means unlimited / unset). const grokPrepaidMoneyLine = computed(() => { const billing = grokBilling.value if (!billing) return null const prepaid = billing.prepaid_balance - // "只针对预付": only render when prepaid field exists (including $0.00). - if (prepaid == null || !Number.isFinite(prepaid)) return null + const showPrepaid = prepaid != null && Number.isFinite(prepaid) && prepaid > 0 + const limitRaw = + billing.monthly_limit != null + ? billing.monthly_limit + : billing.monthly_limit_cents != null + ? billing.monthly_limit_cents / 100 + : null + const showUsedLimit = limitRaw != null && Number.isFinite(limitRaw) && limitRaw > 0 + if (!showPrepaid && !showUsedLimit) return null const used = billing.monthly_used != null ? billing.monthly_used : billing.used_cents != null ? billing.used_cents / 100 : 0 - const limit = - billing.monthly_limit != null - ? billing.monthly_limit - : billing.monthly_limit_cents != null - ? billing.monthly_limit_cents / 100 - : 0 return { - prepaid: formatGrokMoney(prepaid), - used: formatGrokMoney(used), - limit: formatGrokMoney(limit) + showPrepaid, + showUsedLimit, + prepaid: showPrepaid ? formatGrokMoney(prepaid) : null, + used: showUsedLimit ? formatGrokMoney(used) : null, + limit: showUsedLimit ? formatGrokMoney(limitRaw) : null } }) const grokPlanLabelIsFree = (value: string) => value.includes('free') || value.includes('basic') diff --git a/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts b/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts index 3e048a5346..1ad6866dd7 100644 --- a/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts +++ b/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts @@ -1168,6 +1168,94 @@ describe('AccountUsageCell', () => { expect(wrapper.text()).toContain('30d|8|4400000') }) + it('Grok paid hides zero prepaid and hides used/limit when monthly limit is 0', async () => { + getUsage.mockResolvedValue({ + subscription_tier: 'SuperGrok', + grok_billing: { + period_type: 'weekly', + usage_percent: 20, + prepaid_balance: 0, + monthly_limit: 0, + monthly_used: 3.5, + plan: 'SuperGrok' + } + }) + + const wrapper = mount(AccountUsageCell, { + props: { + account: makeAccount({ id: 4413, platform: 'grok', type: 'oauth', extra: {} }) + }, + global: { + stubs: { + UsageProgressBar: true, + AccountQuotaInfo: true + } + } + }) + + await flushPromises() + expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokPrepaid') + expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokUsed') + expect(wrapper.text()).not.toContain('3.5/0') + }) + + it('Grok paid shows used/limit without prepaid, and prepaid without a zero monthly limit', async () => { + getUsage.mockResolvedValueOnce({ + subscription_tier: 'SuperGrok', + grok_billing: { + period_type: 'weekly', + usage_percent: 20, + monthly_limit: 25, + monthly_used: 3.5, + plan: 'SuperGrok' + } + }) + + const usedOnly = mount(AccountUsageCell, { + props: { + account: makeAccount({ id: 4414, platform: 'grok', type: 'oauth', extra: {} }) + }, + global: { + stubs: { + UsageProgressBar: true, + AccountQuotaInfo: true + } + } + }) + await flushPromises() + expect(usedOnly.text()).not.toContain('admin.accounts.usageWindow.grokPrepaid') + expect(usedOnly.text()).toContain('admin.accounts.usageWindow.grokUsed') + expect(usedOnly.text()).toContain('3.50/25.0') + + getUsage.mockResolvedValueOnce({ + subscription_tier: 'SuperGrok Heavy', + grok_billing: { + period_type: 'weekly', + usage_percent: 20, + prepaid_balance: 12.5, + monthly_limit: 0, + monthly_used: 8, + plan: 'SuperGrok Heavy' + } + }) + const prepaidOnly = mount(AccountUsageCell, { + props: { + account: makeAccount({ id: 4415, platform: 'grok', type: 'oauth', extra: {} }) + }, + global: { + stubs: { + UsageProgressBar: true, + AccountQuotaInfo: true + } + } + }) + await flushPromises() + expect(prepaidOnly.text()).toContain('admin.accounts.usageWindow.grokPrepaid') + expect(prepaidOnly.text()).toContain('$12.5') + expect(prepaidOnly.text()).not.toContain('admin.accounts.usageWindow.grokUsed') + expect(prepaidOnly.text()).not.toContain('8.00/0') + }) + it('Key 账号在 today stats loading 时显示骨架屏', async () => { const wrapper = mount(AccountUsageCell, { props: {