From 51e8779725b49fc701e205aa05a24b693b1db531 Mon Sep 17 00:00:00 2001 From: NieiR Date: Wed, 7 Jan 2026 20:19:24 +0800 Subject: [PATCH 1/2] fix: enable provider group editing in edit user dialog - Always show providerGroup field in edit mode (was hidden when user had no providerGroup) - Replace read-only Badge display with editable ProviderGroupSelect component - Move modelSuggestions hook after form declaration to support dynamic updates Regression from #539 --- .../_components/user/edit-user-dialog.tsx | 11 ++++---- .../user/forms/user-edit-section.tsx | 25 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx b/src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx index cc21562b3..107648249 100644 --- a/src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx +++ b/src/app/[locale]/dashboard/_components/user/edit-user-dialog.tsx @@ -72,10 +72,8 @@ function EditUserDialogInner({ onOpenChange, user, onSuccess }: EditUserDialogPr const tCommon = useTranslations("common"); const [isPending, startTransition] = useTransition(); - // Use shared hooks - const modelSuggestions = useModelSuggestions(user.providerGroup); - const showUserProviderGroup = Boolean(user.providerGroup?.trim()); - const userEditTranslations = useUserTranslations({ showProviderGroup: showUserProviderGroup }); + // Always show providerGroup field in edit mode + const userEditTranslations = useUserTranslations({ showProviderGroup: true }); const defaultValues = useMemo(() => buildDefaultValues(user), [user]); @@ -125,6 +123,9 @@ function EditUserDialogInner({ onOpenChange, user, onSuccess }: EditUserDialogPr const currentUserDraft = form.values || defaultValues; + // Model suggestions based on current providerGroup value + const modelSuggestions = useModelSuggestions(currentUserDraft.providerGroup); + const handleUserChange = (field: string | Record, value?: any) => { const prev = form.values || defaultValues; const next = { ...prev } as EditUserValues; @@ -224,7 +225,7 @@ function EditUserDialogInner({ onOpenChange, user, onSuccess }: EditUserDialogPr await handleEnableUser(); } }} - showProviderGroup={showUserProviderGroup} + showProviderGroup onChange={handleUserChange} translations={userEditTranslations} modelSuggestions={modelSuggestions} diff --git a/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx b/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx index e8a691d60..b4efa9e5e 100644 --- a/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx +++ b/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx @@ -14,7 +14,6 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; @@ -23,6 +22,7 @@ import { cn } from "@/lib/utils"; import { AccessRestrictionsSection } from "./access-restrictions-section"; import { type DailyResetMode, LimitRulePicker, type LimitType } from "./limit-rule-picker"; import { type LimitRuleDisplayItem, LimitRulesDisplay } from "./limit-rules-display"; +import { ProviderGroupSelect } from "./provider-group-select"; import { QuickExpirePicker } from "./quick-expire-picker"; export interface UserEditSectionProps { @@ -411,20 +411,15 @@ export function UserEditSection({ /> {showProviderGroup && translations.fields.providerGroup && ( -
- -
- {(user.providerGroup || PROVIDER_GROUP.DEFAULT) - .split(",") - .map((g) => g.trim()) - .filter(Boolean) - .map((group) => ( - - {group} - - ))} -
-
+ emitChange("providerGroup", val)} + disabled={false} + translations={{ + label: translations.fields.providerGroup.label, + placeholder: translations.fields.providerGroup.placeholder, + }} + /> )} From e66e5fbb217438e53f3be4b53b24d1cf273c5a1c Mon Sep 17 00:00:00 2001 From: NieiR Date: Wed, 7 Jan 2026 20:40:43 +0800 Subject: [PATCH 2/2] fix: add complete translations for ProviderGroupSelect in edit user dialog Pass full translations object to ProviderGroupSelect including: - tagInputErrors for validation messages (empty, duplicate, too_long, etc.) - errors.loadFailed for API error handling - providersSuffix for provider count display This fixes untranslated error messages when users input invalid provider group tags. --- messages/en/dashboard.json | 4 +++ messages/ja/dashboard.json | 4 +++ messages/ru/dashboard.json | 4 +++ messages/zh-CN/dashboard.json | 4 +++ messages/zh-TW/dashboard.json | 4 +++ .../user/forms/user-edit-section.tsx | 16 +++++++++--- .../user/hooks/use-user-translations.ts | 25 ++++++++++++++++++- 7 files changed, 56 insertions(+), 5 deletions(-) diff --git a/messages/en/dashboard.json b/messages/en/dashboard.json index c5d9330bc..139f1ba6a 100644 --- a/messages/en/dashboard.json +++ b/messages/en/dashboard.json @@ -1364,6 +1364,10 @@ "threeMonths": "In 3 months", "oneYear": "In 1 year" }, + "providerGroupSelect": { + "providersSuffix": "providers", + "loadFailed": "Failed to load provider groups" + }, "providerGroup": { "label": "Provider group", "placeholder": "Select provider group", diff --git a/messages/ja/dashboard.json b/messages/ja/dashboard.json index 43721adc4..c01066eb3 100644 --- a/messages/ja/dashboard.json +++ b/messages/ja/dashboard.json @@ -1326,6 +1326,10 @@ "threeMonths": "3か月後", "oneYear": "1年後" }, + "providerGroupSelect": { + "providersSuffix": "件のプロバイダー", + "loadFailed": "プロバイダーグループの読み込みに失敗しました" + }, "providerGroup": { "label": "プロバイダーグループ", "placeholder": "プロバイダーグループを選択", diff --git a/messages/ru/dashboard.json b/messages/ru/dashboard.json index 356e2afdf..1c7b6c834 100644 --- a/messages/ru/dashboard.json +++ b/messages/ru/dashboard.json @@ -1337,6 +1337,10 @@ "threeMonths": "Через 3 месяца", "oneYear": "Через год" }, + "providerGroupSelect": { + "providersSuffix": "провайдеров", + "loadFailed": "Не удалось загрузить группы провайдеров" + }, "providerGroup": { "label": "Группа провайдеров", "placeholder": "Выберите группу провайдеров", diff --git a/messages/zh-CN/dashboard.json b/messages/zh-CN/dashboard.json index 7074481a2..1e23cd898 100644 --- a/messages/zh-CN/dashboard.json +++ b/messages/zh-CN/dashboard.json @@ -1365,6 +1365,10 @@ "threeMonths": "三月后", "oneYear": "一年后" }, + "providerGroupSelect": { + "providersSuffix": "个供应商", + "loadFailed": "加载供应商分组失败" + }, "providerGroup": { "label": "供应商分组", "placeholder": "选择供应商分组", diff --git a/messages/zh-TW/dashboard.json b/messages/zh-TW/dashboard.json index c27eb01ee..50b32d253 100644 --- a/messages/zh-TW/dashboard.json +++ b/messages/zh-TW/dashboard.json @@ -1335,6 +1335,10 @@ "threeMonths": "三個月後", "oneYear": "一年後" }, + "providerGroupSelect": { + "providersSuffix": "個供應商", + "loadFailed": "載入供應商分組失敗" + }, "providerGroup": { "label": "供應商分組", "placeholder": "選擇供應商分組", diff --git a/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx b/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx index b4efa9e5e..1f8b30cdf 100644 --- a/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx +++ b/src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx @@ -69,6 +69,17 @@ export interface UserEditSectionProps { providerGroup?: { label: string; placeholder: string; + providersSuffix?: string; + tagInputErrors?: { + empty?: string; + duplicate?: string; + too_long?: string; + invalid_format?: string; + max_tags?: string; + }; + errors?: { + loadFailed?: string; + }; }; enableStatus?: { label: string; @@ -415,10 +426,7 @@ export function UserEditSection({ value={user.providerGroup || PROVIDER_GROUP.DEFAULT} onChange={(val) => emitChange("providerGroup", val)} disabled={false} - translations={{ - label: translations.fields.providerGroup.label, - placeholder: translations.fields.providerGroup.placeholder, - }} + translations={translations.fields.providerGroup} /> )} diff --git a/src/app/[locale]/dashboard/_components/user/hooks/use-user-translations.ts b/src/app/[locale]/dashboard/_components/user/hooks/use-user-translations.ts index 1360c99f1..96cb4e8de 100644 --- a/src/app/[locale]/dashboard/_components/user/hooks/use-user-translations.ts +++ b/src/app/[locale]/dashboard/_components/user/hooks/use-user-translations.ts @@ -26,6 +26,17 @@ export interface UserEditTranslations { providerGroup?: { label: string; placeholder: string; + providersSuffix?: string; + tagInputErrors?: { + empty?: string; + duplicate?: string; + too_long?: string; + invalid_format?: string; + max_tags?: string; + }; + errors?: { + loadFailed?: string; + }; }; enableStatus: { label: string; @@ -98,6 +109,7 @@ export function useUserTranslations( ): UserEditTranslations { const { showProviderGroup = false } = options; const t = useTranslations("dashboard.userManagement"); + const tUi = useTranslations("ui.tagInput"); return useMemo(() => { return { @@ -124,6 +136,17 @@ export function useUserTranslations( ? { label: t("userEditSection.fields.providerGroup.label"), placeholder: t("userEditSection.fields.providerGroup.placeholder"), + providersSuffix: t("providerGroupSelect.providersSuffix"), + tagInputErrors: { + empty: tUi("emptyTag"), + duplicate: tUi("duplicateTag"), + too_long: tUi("tooLong", { max: 50 }), + invalid_format: tUi("invalidFormat"), + max_tags: tUi("maxTags"), + }, + errors: { + loadFailed: t("providerGroupSelect.loadFailed"), + }, } : undefined, enableStatus: { @@ -187,5 +210,5 @@ export function useUserTranslations( year: t("quickExpire.oneYear"), }, }; - }, [t, showProviderGroup]); + }, [t, tUi, showProviderGroup]); }