diff --git a/frontend/features/admin/components/sections/about/admin-about.tsx b/frontend/features/admin/components/sections/about/admin-about.tsx index a8000319..7054fca2 100644 --- a/frontend/features/admin/components/sections/about/admin-about.tsx +++ b/frontend/features/admin/components/sections/about/admin-about.tsx @@ -28,6 +28,7 @@ import { writeCachedLatestRelease, } from "@/features/admin/model/update-check"; import { AboutSettingsContent } from "@/shared/components/about-settings-content"; +import { useDialogSnapshot } from "@/shared/hooks/use-dialog-snapshot"; import { cn } from "@/lib/utils"; type GitHubRelease = { @@ -133,30 +134,31 @@ function UpdateResultDialog({ }) { const t = useTranslations("adminUsers.aboutPage"); const currentVersion = formatReleaseVersion(packageMeta.version); - const latestVersion = state?.type === "available" ? formatReleaseVersion(state.release.version) : ""; + const stableState = useDialogSnapshot(state); + const latestVersion = stableState?.type === "available" ? formatReleaseVersion(stableState.release.version) : ""; return ( - {state?.type === "available" + {stableState?.type === "available" ? t("updateDialog.availableTitle") - : state?.type === "failed" + : stableState?.type === "failed" ? t("updateDialog.failedTitle") : t("updateDialog.currentTitle")} - {state?.type === "available" + {stableState?.type === "available" ? t("updateDialog.availableDescription", { current: currentVersion, latest: latestVersion }) - : state?.type === "failed" + : stableState?.type === "failed" ? t("updateDialog.failedDescription") : t("updateDialog.currentDescription", { current: currentVersion })}
- {state?.type === "available" ? ( + {stableState?.type === "available" ? (
{t("updateDialog.currentVersion")} @@ -174,14 +176,14 @@ function UpdateResultDialog({ {t("updateDialog.close")} - {state?.type === "failed" ? ( + {stableState?.type === "failed" ? ( ) : null} - {state?.type === "available" ? ( + {stableState?.type === "available" ? ( diff --git a/frontend/features/admin/components/sections/accounts/accounts-users.tsx b/frontend/features/admin/components/sections/accounts/accounts-users.tsx index cfa781a4..b37e0773 100644 --- a/frontend/features/admin/components/sections/accounts/accounts-users.tsx +++ b/frontend/features/admin/components/sections/accounts/accounts-users.tsx @@ -47,6 +47,7 @@ import type { ImportOpenWebUIUsersData, ImportOpenWebUIUsersRequest } from "@/fe import { resolveAvatarImageSrc } from "@/shared/lib/avatar"; import { useAuthSession } from "@/shared/auth/auth-session-context"; import { resolveAccessToken } from "@/shared/auth/resolve-access-token"; +import { useDialogSnapshot } from "@/shared/hooks/use-dialog-snapshot"; import { TimeZoneSelect } from "@/shared/components/time-zone-select"; import type { AdminUserRole, AdminUserStatus } from "@/features/admin/api/admin.types"; import type { AdminBillingMode } from "@/features/admin/api/billing.types"; @@ -514,6 +515,8 @@ export function AccountsUsers({ onSetUsers, onSetTotal, }); + const stableEditDialogTarget = useDialogSnapshot(editDialogTarget); + const stableResetPasswordDraft = useDialogSnapshot(resetDialogTarget ? resetPasswordDraft : null) ?? ""; const virtualRows = useVirtualTableRows(filteredItems, { enabled: filteredItems.length > 100, estimateSize: 40, @@ -945,16 +948,16 @@ export function AccountsUsers({ onSubmit={handleImportOpenWebUI} /> - {editDialogTarget ? ( + {stableEditDialogTarget ? ( { if (!open && pendingAction !== "edit") { setEditDialogTarget(null); } }} pending={pendingAction === "edit"} - editDialogTarget={editDialogTarget} + editDialogTarget={stableEditDialogTarget} editPayload={editPayload} setEditPayload={setEditPayload} billingMode={billingMode} @@ -966,27 +969,27 @@ export function AccountsUsers({ onOpenEditAvatarDialog={() => { setAvatarDialog({ mode: "edit", - target: editDialogTarget, - value: editPayload.avatarURL.trim() || editDialogTarget.avatarURL.trim(), + target: stableEditDialogTarget, + value: editPayload.avatarURL.trim() || stableEditDialogTarget.avatarURL.trim(), }); }} onOpenResetPasswordDialog={() => { - setResetDialogTarget(editDialogTarget); + setResetDialogTarget(stableEditDialogTarget); setResetPasswordDraft(""); }} onOpenResetTwoFactorDialog={() => { - setResetTwoFactorDialogTarget(editDialogTarget); + setResetTwoFactorDialogTarget(stableEditDialogTarget); }} onOpenRevokeDialog={() => { - setRevokeDialogTarget(editDialogTarget); + setRevokeDialogTarget(stableEditDialogTarget); }} onOpenDeleteDialog={() => { - setDeleteDialogTarget(editDialogTarget); + setDeleteDialogTarget(stableEditDialogTarget); }} - resetPasswordPending={pendingAction === "reset-password" && actionUserID === editDialogTarget.id} - resetTwoFactorPending={pendingAction === "reset-2fa" && actionUserID === editDialogTarget.id} - revokePending={pendingAction === "revoke-sessions" && actionUserID === editDialogTarget.id} - deletePending={pendingAction === "delete" && actionUserID === editDialogTarget.id} + resetPasswordPending={pendingAction === "reset-password" && actionUserID === stableEditDialogTarget.id} + resetTwoFactorPending={pendingAction === "reset-2fa" && actionUserID === stableEditDialogTarget.id} + revokePending={pendingAction === "revoke-sessions" && actionUserID === stableEditDialogTarget.id} + deletePending={pendingAction === "delete" && actionUserID === stableEditDialogTarget.id} resolveUserInitial={resolveUserInitial} /> ) : null} @@ -1000,7 +1003,7 @@ export function AccountsUsers({ } }} pending={pendingAction === "reset-password"} - password={resetPasswordDraft} + password={stableResetPasswordDraft} onPasswordChange={setResetPasswordDraft} onConfirm={() => void onResetPassword()} onCancel={() => { diff --git a/frontend/features/admin/components/sections/billing/billing-plan.tsx b/frontend/features/admin/components/sections/billing/billing-plan.tsx index b2fc4b24..8d7789fa 100644 --- a/frontend/features/admin/components/sections/billing/billing-plan.tsx +++ b/frontend/features/admin/components/sections/billing/billing-plan.tsx @@ -10,6 +10,7 @@ import type { PermissionGroup } from "@/features/admin/api/permission-groups"; import { resolveAdminErrorMessage } from "@/features/admin/utils/admin-error"; import { createPlanFormState, parseIntValue, parsePrice, type PlanFormState } from "@/features/admin/model/billing-settings"; import { resolveAccessToken } from "@/shared/auth/resolve-access-token"; +import { useDialogSnapshot } from "@/shared/hooks/use-dialog-snapshot"; import { PlanBillingDialog } from "@/features/admin/components/sections/billing/billing-dialogs"; import { PeriodBillingTable } from "@/features/admin/components/sections/billing/billing-tables"; @@ -25,6 +26,7 @@ export function BillingPlanSection({ plans, setPlans, permissionGroups, loading const [saving, setSaving] = React.useState(false); const [editPlan, setEditPlan] = React.useState(null); const [planForm, setPlanForm] = React.useState(null); + const stablePlanForm = useDialogSnapshot(planForm); function openPlanEdit(plan: AdminBillingPlanDTO) { setEditPlan(plan); @@ -73,7 +75,7 @@ export function BillingPlanSection({ plans, setPlans, permissionGroups, loading { diff --git a/frontend/features/admin/components/sections/billing/billing-prices.tsx b/frontend/features/admin/components/sections/billing/billing-prices.tsx index e9fb0d94..5e2417b2 100644 --- a/frontend/features/admin/components/sections/billing/billing-prices.tsx +++ b/frontend/features/admin/components/sections/billing/billing-prices.tsx @@ -49,6 +49,7 @@ import { import { resolveAdminErrorMessage } from "@/features/admin/utils/admin-error"; import { LobeHubIcon } from "@/shared/components/lobehub-icon"; import { resolveAccessToken } from "@/shared/auth/resolve-access-token"; +import { useDialogSnapshot } from "@/shared/hooks/use-dialog-snapshot"; import { cn } from "@/lib/utils"; import { KNOWN_VENDOR_OPTIONS, resolveLobeHubIconURL, resolveModelIdentity } from "@/shared/lib/model-identity"; @@ -150,6 +151,9 @@ export function BillingPricesSection({ models, pricingItems, setPricingItems, lo const [officialPricingCatalogHasError, setOfficialPricingCatalogHasError] = React.useState(false); const [officialPricingSingleDialogOpen, setOfficialPricingSingleDialogOpen] = React.useState(false); const [freeSwitchPendingModel, setFreeSwitchPendingModel] = React.useState(""); + const stableEditRow = useDialogSnapshot(editRow); + const stableForm = useDialogSnapshot(form); + const stableOfficialPricingImportSuggestion = useDialogSnapshot(officialPricingImportSuggestion); const rows = React.useMemo(() => buildPricingRows(models, pricingItems), [models, pricingItems]); const vendorFilterOptions = React.useMemo(() => { @@ -689,7 +693,7 @@ export function BillingPricesSection({ models, pricingItems, setPricingItems, lo { if (!open && !saving) { @@ -729,7 +733,7 @@ export function BillingPricesSection({ models, pricingItems, setPricingItems, lo
- {editRow ? ( + {stableEditRow ? ( <>
@@ -909,7 +913,7 @@ export function BillingPricesSection({ models, pricingItems, setPricingItems, lo