From f13f0d1f9a84ad5caa7b5d62391026a59f6271e0 Mon Sep 17 00:00:00 2001 From: yyh Date: Thu, 5 Mar 2026 10:09:51 +0800 Subject: [PATCH] fix(web): align dropdown alerts with Figma design and fix hardcoded credits total - Expose totalCredits from useTrialCredits hook instead of hardcoding 10,000 - Align CreditsExhaustedAlert with Figma: dynamic progress bar, correct design tokens (components-progress-error-bg/progress), sm-medium/xs-regular typography - Align CreditsFallbackAlert typography to sm-medium/xs-regular - Fix ApiKeySection empty state: horizontal gradient, sm-medium title, Figma-aligned padding (pl-7 for API KEYS label) - Hoist empty credentials array constant to stabilize memo (rerender-memo-with-default-value) - Remove redundant useCallback wrapper in ApiKeySection - Replace nested ternary with Record lookup in TextLabel - Remove dead || 0 guard in useTrialCredits - Update all test mocks with totalCredits field --- .../credential-panel.spec.tsx | 4 +- .../provider-added-card/credential-panel.tsx | 16 ++--- .../model-auth-dropdown/api-key-section.tsx | 58 +++++++++--------- .../credits-exhausted-alert.spec.tsx | 2 +- .../credits-exhausted-alert.tsx | 61 +++++++++++-------- .../credits-fallback-alert.tsx | 18 +++--- .../dropdown-content.spec.tsx | 2 +- .../model-auth-dropdown/dropdown-content.tsx | 4 +- .../model-auth-dropdown/index.spec.tsx | 2 +- .../use-credential-panel-state.spec.ts | 4 +- .../provider-added-card/use-trial-credits.ts | 4 +- 11 files changed, 96 insertions(+), 79 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.spec.tsx index 5e01edd407..9e29313f32 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.spec.tsx @@ -21,7 +21,7 @@ const { mockToastNotify: vi.fn(), mockUpdateModelList: vi.fn(), mockUpdateModelProviders: vi.fn(), - mockTrialCredits: { credits: 100, isExhausted: false, isLoading: false, nextCreditResetDate: undefined }, + mockTrialCredits: { credits: 100, totalCredits: 10_000, isExhausted: false, isLoading: false, nextCreditResetDate: undefined }, mockChangePriorityFn: vi.fn().mockResolvedValue({ result: 'success' }), })) @@ -113,7 +113,7 @@ const renderWithQueryClient = (provider: ModelProvider) => { describe('CredentialPanel', () => { beforeEach(() => { vi.clearAllMocks() - Object.assign(mockTrialCredits, { credits: 100, isExhausted: false, isLoading: false }) + Object.assign(mockTrialCredits, { credits: 100, totalCredits: 10_000, isExhausted: false, isLoading: false }) }) describe('Text label variants', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 269553a2f2..3b48be856f 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -97,16 +97,18 @@ const CredentialPanel = ({ ) } +const TEXT_LABEL_KEYS = { + 'credits-active': 'modelProvider.card.aiCreditsInUse', + 'credits-exhausted': 'modelProvider.card.quotaExhausted', + 'no-usage': 'modelProvider.card.noAvailableUsage', + 'api-required-add': 'modelProvider.card.apiKeyRequired', + 'api-required-configure': 'modelProvider.card.apiKeyRequired', +} as const satisfies Partial> + function TextLabel({ variant }: { variant: CardVariant }) { const { t } = useTranslation() const isDestructive = isDestructiveVariant(variant) - const labelKey = variant === 'credits-active' - ? 'modelProvider.card.aiCreditsInUse' - : variant === 'credits-exhausted' - ? 'modelProvider.card.quotaExhausted' - : variant === 'no-usage' - ? 'modelProvider.card.noAvailableUsage' - : 'modelProvider.card.apiKeyRequired' + const labelKey = TEXT_LABEL_KEYS[variant as keyof typeof TEXT_LABEL_KEYS] return ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx index 74a9229704..3bd34c2cfc 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx @@ -1,5 +1,5 @@ import type { Credential, CustomModel, ModelProvider } from '../../declarations' -import { memo, useCallback } from 'react' +import { memo } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import CredentialItem from '../../model-auth/authorized/credential-item' @@ -26,25 +26,23 @@ function ApiKeySection({ const { t } = useTranslation() const notAllowCustomCredential = provider.allow_custom_token === false - const handleItemClick = useCallback((credential: Credential) => { - onItemClick(credential) - }, [onItemClick]) - if (!credentials.length) { return ( -
-
-
- {t('modelProvider.card.noApiKeysTitle', { ns: 'common' })} -
-
- {t('modelProvider.card.noApiKeysDescription', { ns: 'common' })} +
+
+
+
+ {t('modelProvider.card.noApiKeysTitle', { ns: 'common' })} +
+
+ {t('modelProvider.card.noApiKeysDescription', { ns: 'common' })} +
{!notAllowCustomCredential && ( @@ -55,24 +53,26 @@ function ApiKeySection({ return (
-
- {t('modelProvider.auth.apiKeys', { ns: 'common' })} -
-
- {credentials.map(credential => ( - - ))} +
+
+ {t('modelProvider.auth.apiKeys', { ns: 'common' })} +
+
+ {credentials.map(credential => ( + + ))} +
{!notAllowCustomCredential && ( -
+