From 530515b6efe2e801e8118c6f8e9cee74fa1d93cc Mon Sep 17 00:00:00 2001 From: yyh Date: Thu, 5 Mar 2026 10:35:03 +0800 Subject: [PATCH 1/2] fix(web): prevent model list from expanding on priority switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST event emission from changePriority onSuccess. This event was designed for custom model add/edit/delete scenarios where the card should expand, but firing it on priority switch caused ProviderAddedCard to unexpectedly expand via refreshModelList → setCollapsed(false). --- .../provider-added-card/credential-panel.spec.tsx | 2 +- .../provider-added-card/credential-panel.tsx | 7 ------- 2 files changed, 1 insertion(+), 8 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 9e29313f32..1b50c30348 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 @@ -321,7 +321,7 @@ describe('CredentialPanel', () => { ) expect(mockUpdateModelProviders).toHaveBeenCalled() expect(mockUpdateModelList).toHaveBeenCalledWith('llm') - expect(mockEventEmitter.emit).toHaveBeenCalled() + expect(mockEventEmitter.emit).not.toHaveBeenCalled() }) }) }) 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 3b48be856f..401d07b684 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 @@ -7,7 +7,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import Indicator from '@/app/components/header/indicator' -import { useEventEmitterContextContext } from '@/context/event-emitter' import { consoleQuery } from '@/service/client' import { ConfigurationMethodEnum, @@ -16,7 +15,6 @@ import { useUpdateModelList, useUpdateModelProviders, } from '../hooks' -import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './index' import ModelAuthDropdown from './model-auth-dropdown' import SystemQuotaCard from './system-quota-card' import { isDestructiveVariant, useCredentialPanelState } from './use-credential-panel-state' @@ -37,7 +35,6 @@ const CredentialPanel = ({ provider, }: CredentialPanelProps) => { const { t } = useTranslation() - const { eventEmitter } = useEventEmitterContextContext() const queryClient = useQueryClient() const updateModelList = useUpdateModelList() const updateModelProviders = useUpdateModelProviders() @@ -56,10 +53,6 @@ const CredentialPanel = ({ if (method === ConfigurationMethodEnum.predefinedModel) provider.supported_model_types.forEach(modelType => updateModelList(modelType)) }) - eventEmitter?.emit({ - type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST, - payload: provider.provider, - } as { type: string, payload: string }) }, onError: () => { Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) }) From d45edffaa34c1311354392c458922dbac3168a9a Mon Sep 17 00:00:00 2001 From: yyh Date: Thu, 5 Mar 2026 10:39:31 +0800 Subject: [PATCH 2/2] fix(web): wire upgrade link to pricing modal and add credits-coin icon Replace broken HTML string interpolation with Trans component and useModalContextSelector so "upgrade your plan" opens the pricing modal. Add custom credits-coin SVG icon to replace the generic ri-coin-line. --- .../line/financeAndECommerce/credits-coin.svg | 4 +++ .../line/financeAndECommerce/CreditsCoin.json | 35 +++++++++++++++++++ .../line/financeAndECommerce/CreditsCoin.tsx | 20 +++++++++++ .../vender/line/financeAndECommerce/index.ts | 1 + .../credits-exhausted-alert.tsx | 19 ++++++---- web/i18n/en-US/common.json | 4 +-- web/i18n/zh-Hans/common.json | 4 +-- 7 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/line/financeAndECommerce/credits-coin.svg create mode 100644 web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.json create mode 100644 web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.tsx diff --git a/web/app/components/base/icons/assets/vender/line/financeAndECommerce/credits-coin.svg b/web/app/components/base/icons/assets/vender/line/financeAndECommerce/credits-coin.svg new file mode 100644 index 0000000000..bee018ba2f --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/financeAndECommerce/credits-coin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.json b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.json new file mode 100644 index 0000000000..a199f15d36 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.json @@ -0,0 +1,35 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "10", + "height": "10", + "viewBox": "0 0 10 10", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M2 5C2 3.44487 2.58482 1.98537 3.54004 1.04932C2.17681 1.34034 1 2.90001 1 5C1 7.09996 2.17685 8.65912 3.54004 8.9502C2.58496 8.01413 2 6.55501 2 5ZM3 5C3 7.33338 4.4528 9 6 9C7.5472 9 9 7.33338 9 5C9 2.66664 7.5472 1 6 1C4.4528 1 3 2.66664 3 5ZM10 5C10 7.63722 8.3188 10 6 10H4C1.6812 10 0 7.63722 0 5C0 2.3628 1.6812 0 4 0H6C8.3188 0 10 2.3628 10 5Z", + "fill": "currentColor" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M6.71519 4.09259L6.45385 3.18667C6.42141 3.07421 6.34037 3 6.25 3C6.15963 3 6.07859 3.07421 6.04615 3.18667L5.78481 4.09259C5.74675 4.22464 5.66849 4.32899 5.56945 4.37978L4.88999 4.7282C4.80565 4.77146 4.75 4.87951 4.75 5C4.75 5.12049 4.80565 5.22854 4.88999 5.2718L5.56945 5.62022C5.66849 5.67101 5.74675 5.77536 5.78481 5.90741L6.04615 6.81333C6.07859 6.92579 6.15963 7 6.25 7C6.34037 7 6.42141 6.92579 6.45385 6.81333L6.71519 5.90741C6.75325 5.77536 6.83151 5.67101 6.93055 5.62022L7.61001 5.2718C7.69435 5.22854 7.75 5.12049 7.75 5C7.75 4.87951 7.69435 4.77146 7.61001 4.7282L6.93055 4.37978C6.83151 4.32899 6.75325 4.22464 6.71519 4.09259Z", + "fill": "currentColor" + }, + "children": [] + } + ] + }, + "name": "CreditsCoin" +} diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.tsx b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.tsx new file mode 100644 index 0000000000..77b44e7830 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CreditsCoin.tsx @@ -0,0 +1,20 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import type { IconData } from '@/app/components/base/icons/IconBase' +import * as React from 'react' +import IconBase from '@/app/components/base/icons/IconBase' +import data from './CreditsCoin.json' + +const Icon = ( + { + ref, + ...props + }: React.SVGProps & { + ref?: React.RefObject> + }, +) => + +Icon.displayName = 'CreditsCoin' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts b/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts index 2223daa1d5..8a98a4612c 100644 --- a/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts @@ -1,5 +1,6 @@ export { default as Balance } from './Balance' export { default as CoinsStacked01 } from './CoinsStacked01' +export { default as CreditsCoin } from './CreditsCoin' export { default as GoldCoin } from './GoldCoin' export { default as ReceiptList } from './ReceiptList' export { default as Tag01 } from './Tag01' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx index 899706f39f..e642df09ed 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx @@ -1,4 +1,6 @@ -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' +import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' +import { useModalContextSelector } from '@/context/modal-context' import { formatNumber } from '@/utils/format' import { useTrialCredits } from '../use-trial-credits' @@ -8,6 +10,7 @@ type CreditsExhaustedAlertProps = { export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExhaustedAlertProps) { const { t } = useTranslation() + const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal) const { credits, totalCredits } = useTrialCredits() const titleKey = hasApiKeyFallback @@ -27,11 +30,13 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha {t(titleKey, { ns: 'common' })}
- {t(descriptionKey, { - ns: 'common', - upgradeLink: `${t('modelProvider.card.upgradePlan', { ns: 'common' })}`, - interpolation: { escapeValue: false }, - })} + , + }} + />
@@ -40,7 +45,7 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha {t('modelProvider.card.usageLabel', { ns: 'common' })}
- + {formatNumber(usedCredits)} / diff --git a/web/i18n/en-US/common.json b/web/i18n/en-US/common.json index 4f590688cf..135646d82d 100644 --- a/web/i18n/en-US/common.json +++ b/web/i18n/en-US/common.json @@ -348,9 +348,9 @@ "modelProvider.card.apiKeyUnavailableFallbackDescription": "Check your API key configuration to switch back", "modelProvider.card.buyQuota": "Buy Quota", "modelProvider.card.callTimes": "Call times", - "modelProvider.card.creditsExhaustedDescription": "Please {{upgradeLink}} or configure an API key", + "modelProvider.card.creditsExhaustedDescription": "Please upgrade your plan or configure an API key", "modelProvider.card.creditsExhaustedFallback": "AI credits exhausted, now using API key", - "modelProvider.card.creditsExhaustedFallbackDescription": "{{upgradeLink}} to resume AI credit priority.", + "modelProvider.card.creditsExhaustedFallbackDescription": "Upgrade your plan to resume AI credit priority.", "modelProvider.card.creditsExhaustedMessage": "AI credits have been exhausted", "modelProvider.card.modelAPI": "{{modelName}} models are using the API Key.", "modelProvider.card.modelNotSupported": "{{modelName}} not installed", diff --git a/web/i18n/zh-Hans/common.json b/web/i18n/zh-Hans/common.json index 80b7d922cd..9466a56f0d 100644 --- a/web/i18n/zh-Hans/common.json +++ b/web/i18n/zh-Hans/common.json @@ -348,9 +348,9 @@ "modelProvider.card.apiKeyUnavailableFallbackDescription": "检查你的 API Key 配置以切换回来", "modelProvider.card.buyQuota": "购买额度", "modelProvider.card.callTimes": "调用次数", - "modelProvider.card.creditsExhaustedDescription": "请{{upgradeLink}}或配置 API Key", + "modelProvider.card.creditsExhaustedDescription": "请升级套餐或配置 API Key", "modelProvider.card.creditsExhaustedFallback": "AI 额度已用尽,正在使用 API Key", - "modelProvider.card.creditsExhaustedFallbackDescription": "{{upgradeLink}}以恢复 AI 额度优先使用。", + "modelProvider.card.creditsExhaustedFallbackDescription": "升级套餐以恢复 AI 额度优先使用。", "modelProvider.card.creditsExhaustedMessage": "AI 额度已用尽", "modelProvider.card.modelAPI": "{{modelName}} 模型正在使用 API Key。", "modelProvider.card.modelNotSupported": "{{modelName}} 未安装",