refactor(ui): shared DraftSaveBar (Settings + Config save bars)
One Save/Discard bar with an inline (Settings card) and floating (Config page) variant, replacing the two near-identical hand-rolled bars + their state machine.
This commit is contained in:
parent
53c75f69e2
commit
f6b9ac2dd1
3 changed files with 109 additions and 66 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Bell, Check, Monitor, RotateCcw, Save, Trash2, User } from "lucide-react";
|
||||
import { Bell, Monitor, Trash2, User } from "lucide-react";
|
||||
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
|
||||
import { api, type Me } from "../lib/api";
|
||||
import { LS, usePersistedState } from "../lib/storage";
|
||||
|
|
@ -9,6 +9,7 @@ import Avatar from "./Avatar";
|
|||
import { notify, type NotifSettings } from "../lib/notifications";
|
||||
import Tooltip from "./Tooltip";
|
||||
import { Section, SettingRow, Switch } from "./ui/form";
|
||||
import { DraftSaveBar } from "./ui/DraftSaveBar";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
|
||||
// The Settings page edits server-persisted preferences as a draft: changes apply locally
|
||||
|
|
@ -104,37 +105,22 @@ export default function SettingsPanel({
|
|||
|
||||
function PrefsSaveBar({ prefs }: { prefs: PrefsController }) {
|
||||
const { t } = useTranslation();
|
||||
// Stay mounted while a transient "saved"/"error" message is fading, even after dirty clears.
|
||||
if (!prefs.dirty && prefs.saveState === "idle") return null;
|
||||
const saving = prefs.saveState === "saving";
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-3 border-t border-border/60 px-4 py-3 bg-card/40">
|
||||
<span className="text-sm text-muted">
|
||||
{prefs.saveState === "saved"
|
||||
? <span className="text-emerald-500 inline-flex items-center gap-1.5"><Check className="w-4 h-4" />{t("settings.save.saved")}</span>
|
||||
: prefs.saveState === "error"
|
||||
? <span className="text-red-500">{t("settings.save.failed")}</span>
|
||||
: t("settings.save.unsaved")}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={prefs.discard}
|
||||
disabled={saving || !prefs.dirty}
|
||||
className="glass-card glass-hover flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm disabled:opacity-40 transition"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
{t("settings.save.discard")}
|
||||
</button>
|
||||
<button
|
||||
onClick={prefs.save}
|
||||
disabled={saving || !prefs.dirty}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm bg-accent text-accent-fg font-medium shadow-md shadow-accent/20 disabled:opacity-40 transition"
|
||||
>
|
||||
<Save className="w-4 h-4" />
|
||||
{saving ? t("settings.save.saving") : t("settings.save.save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<DraftSaveBar
|
||||
variant="inline"
|
||||
dirty={prefs.dirty}
|
||||
state={prefs.saveState}
|
||||
onSave={prefs.save}
|
||||
onDiscard={prefs.discard}
|
||||
labels={{
|
||||
saved: t("settings.save.saved"),
|
||||
failed: t("settings.save.failed"),
|
||||
unsaved: t("settings.save.unsaved"),
|
||||
discard: t("settings.save.discard"),
|
||||
save: t("settings.save.save"),
|
||||
saving: t("settings.save.saving"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue