Polish catalog studio: undo delete, save status pill, faster totals

This commit is contained in:
openhands committed 2026-09-14 18:24:47 +02:00
1 parent 6e0ffff94b
commit 67430e7e9d
5 files changed
+187 -9

No files matched your search

@@ -2,6 +2,7 @@
import {
AlertCircle,
Check,
ChevronDown,
ChevronRight,
Languages,
@@ -12,7 +13,7 @@ import {
Settings,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { updateCatalogPage } from "@/actions/catalog";
import { updateBcPage } from "@/actions/catalog-bc";
@@ -135,6 +136,15 @@ function InlineEditorSession({
const [page, setPage] = useState<PageDetail | null>(null);
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [justSaved, setJustSaved] = useState(false);
const savedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(
() => () => {
if (savedTimer.current) clearTimeout(savedTimer.current);
},
[],
);
const [form, setForm] = useState<FormState>({});
const [original, setOriginal] = useState<FormState>({});
const [showTexts, setShowTexts] = useState(false);
@@ -308,6 +318,9 @@ function InlineEditorSession({
if (!request.isCurrent()) return;
if (!result.ok) throw new Error(result.error || "Failed to save");
toast.success(tActions("saved"));
setJustSaved(true);
if (savedTimer.current) clearTimeout(savedTimer.current);
savedTimer.current = setTimeout(() => setJustSaved(false), 2500);
setOriginal({ ...form });
setPage((prev) =>
prev
@@ -387,14 +400,32 @@ function InlineEditorSession({
>
{form.pageLayout as string}
</Badge>
{(isDirty || offersDirty) && (
{saving ? (
<Badge
variant="outline"
className="h-4 px-1.5 text-[9px] text-[var(--admin-warning)] border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]"
className="h-4 px-1.5 text-[9px] text-[var(--admin-info)] border-[var(--admin-info-border)] bg-[var(--admin-info-subtle)]"
>
<AlertCircle className="h-2.5 w-2.5 mr-0.5" />
{t("unsaved")}
<Loader2 className="h-2.5 w-2.5 mr-0.5 animate-spin" />
{tActions("saving")}
</Badge>
) : justSaved && !isDirty && !offersDirty ? (
<Badge
variant="outline"
className="h-4 px-1.5 text-[9px] text-[var(--admin-success)] border-[var(--admin-success-border)] bg-[var(--admin-success-subtle)]"
>
<Check className="h-2.5 w-2.5 mr-0.5" />
{tActions("saved")}
</Badge>
) : (
(isDirty || offersDirty) && (
<Badge
variant="outline"
className="h-4 px-1.5 text-[9px] text-[var(--admin-warning)] border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]"
>
<AlertCircle className="h-2.5 w-2.5 mr-0.5" />
{t("unsaved")}
</Badge>
)
)}
</div>
<p className="text-xs text-muted-foreground">