diff --git a/web/frontend/src/i18n/locales/en.json b/web/frontend/src/i18n/locales/en.json index 1c96e0a7c..c218352d6 100644 --- a/web/frontend/src/i18n/locales/en.json +++ b/web/frontend/src/i18n/locales/en.json @@ -73,7 +73,8 @@ "cancel": "Cancel", "save": "Save", "saving": "Saving...", - "reset": "Reset" + "reset": "Reset", + "confirm": "Confirm" }, "models": { "description": "Configure API keys for each AI provider. Only configured models are available for chat.", @@ -163,11 +164,14 @@ "json_placeholder": "Enter valid JSON configuration...", "save_success": "Configuration saved successfully.", "save_error": "Failed to save configuration.", - "reset_success": "Configuration has been reset.", + "reset_confirm_title": "Reset Changes", + "reset_confirm_desc": "Are you sure you want to reset your unsaved changes back to the last saved state?", + "reset_success": "Changes have been reset to the last saved state.", "invalid_json": "Invalid JSON format.", "format_success": "JSON formatted successfully.", "format_error": "Invalid JSON format.", - "format": "Format" + "format": "Format", + "lose_unsaved_changes": "You have unsaved changes. Are you sure you want to reset and lose these changes?" }, "logs": { "description": "System logs and monitoring." diff --git a/web/frontend/src/i18n/locales/zh.json b/web/frontend/src/i18n/locales/zh.json index a8ef0002e..57fd113d8 100644 --- a/web/frontend/src/i18n/locales/zh.json +++ b/web/frontend/src/i18n/locales/zh.json @@ -73,7 +73,8 @@ "cancel": "取消", "save": "保存", "saving": "保存中...", - "reset": "重置" + "reset": "重置", + "confirm": "确认" }, "models": { "description": "为每个 AI 服务商配置 API Key。只有已配置的模型可用于对话。", @@ -163,11 +164,15 @@ "json_placeholder": "请输入有效的 JSON 配置...", "save_success": "配置保存成功。", "save_error": "配置保存失败。", - "reset_success": "配置已重置。", + "reset_confirm_title": "重置更改", + "reset_confirm_desc": "您确定要重置回上次保存的状态吗?", + "reset_success": "更改已重置为上次保存的状态。", "invalid_json": "JSON 格式无效。", "format_success": "JSON 格式化成功。", "format_error": "JSON 格式无效。", - "format": "格式化" + "format": "格式化", + "lose_unsaved_changes": "您有未保存的更改。确定要重置并丢失这些更改吗?", + "unsaved_changes": "您有未保存的更改。" }, "logs": { "description": "系统日志和监控。" diff --git a/web/frontend/src/routes/config.tsx b/web/frontend/src/routes/config.tsx index 7ebed8bf7..a2b4cc12a 100644 --- a/web/frontend/src/routes/config.tsx +++ b/web/frontend/src/routes/config.tsx @@ -36,7 +36,7 @@ function ConfigPage() { return (
-
+
@@ -73,7 +73,17 @@ function RawJsonPanel() { }, onSuccess: () => { toast.success(t("pages.config.save_success", "Configuration saved successfully.")) - queryClient.invalidateQueries({ queryKey: ["config"] }) + // Update last saved config and reset dirty state + try { + const savedConfig = JSON.parse(editorValue) + setLastSavedConfig(savedConfig) + setIsDirty(false) + // Important: Invalidate the query to refresh the cached data + queryClient.invalidateQueries({ queryKey: ["config"] }) + } catch (error) { + // If JSON parsing fails, invalidate to get fresh data + queryClient.invalidateQueries({ queryKey: ["config"] }) + } }, onError: () => { toast.error(t("pages.config.save_error", "Failed to save configuration.")) @@ -81,20 +91,29 @@ function RawJsonPanel() { }) const [editorValue, setEditorValue] = useState("") + const [isDirty, setIsDirty] = useState(false) + + // Store the last saved config to detect changes + const [lastSavedConfig, setLastSavedConfig] = useState(null) useEffect(() => { - if (config) { - setEditorValue(JSON.stringify(config, null, 2)) + if (config && JSON.stringify(config) !== JSON.stringify(lastSavedConfig)) { + // Only update if there are no unsaved changes or if this is the initial load + if (!isDirty || !lastSavedConfig) { + setEditorValue(JSON.stringify(config, null, 2)) + setLastSavedConfig(config) + setIsDirty(false) + } } - }, [config]) + }, [config, lastSavedConfig, isDirty]) const handleSave = () => { try { // Validate JSON before saving JSON.parse(editorValue) mutation.mutate(editorValue) - } catch (e) { - toast.error(t("pages.config.invalid_json", "Invalid JSON format.")) + } catch (error) { + toast.error(t("pages.config.invalid_json", error instanceof Error ? error.message : "Invalid JSON format.")) } } @@ -104,15 +123,22 @@ function RawJsonPanel() { setEditorValue(formatted) toast.success(t("pages.config.format_success", "JSON formatted successfully.")) } catch (error) { - toast.error(t("pages.config.format_error", "Invalid JSON format.")) + toast.error(t("pages.config.format_error", error instanceof Error ? error.message : "Invalid JSON format.")) } } const [showResetDialog, setShowResetDialog] = useState(false) const confirmReset = () => { - queryClient.invalidateQueries({ queryKey: ["config"] }) - toast.info(t("pages.config.reset_success", "Configuration has been reset.")) + // Reset editor content to the last saved configuration + if (lastSavedConfig) { + setEditorValue(JSON.stringify(lastSavedConfig, null, 2)) + } else if (config) { + // Fallback to current config if no last saved config + setEditorValue(JSON.stringify(config, null, 2)) + } + setIsDirty(false) + toast.info(t("pages.config.reset_success", "Changes have been reset to the last saved state.")) setShowResetDialog(false) } @@ -135,12 +161,20 @@ function RawJsonPanel() {

{t("labels.loading", "Loading...")}

) : ( -
-
+
+ {isDirty && ( +
+ {t("pages.config.unsaved_changes", "You have unsaved changes.")} +
+ )} +