feat: HQ-wide Toast + Confirm — no more browser alert()/confirm() popups
Deploy to VPS / deploy (push) Has been cancelled
Deploy to VPS / deploy (push) Has been cancelled
The Inbox panel got the polished Toast + Confirm primitives a few
commits back. This commit propagates them across every other panel in
HQ Command so the editor experience is uniformly on-brand. No more
1990s browser dialogs interrupting the dark CMS look.
NINE PANELS, ELEVEN CALL SITES UPGRADED
- health/page.tsx — DB export errors → toast
- network/page.tsx — Delete deployment → confirm + toast
- heritage/page.tsx — Delete section → confirm + toast
- users/page.tsx — Revoke architect → confirm + error toast
- news/page.tsx — Delete article → confirm + toast
- applications/page.tsx — Save error toast + delete-app confirm
- parts/page.tsx — Delete component → confirm + toast
- hero/page.tsx — Delete slide → confirm + toast
- timeline/page.tsx — Delete milestone → confirm + toast
Each destructive confirm now spells out what it does ('Permanently
remove this case from the global map. The asset folder on disk is
kept for safety') instead of a generic 'Delete?' prompt — much
clearer for editors who aren't sure whether files get nuked too.
Each success toast names the action ('Component deleted', 'Slide
deleted', 'Architect access revoked') so the editor sees exactly
what fired. Errors come in as red toasts with the actual error text.
NO BACKEND CHANGES. Pure UX layer on top of existing actions.
The HqUiProvider was already mounted in src/app/hq-command/layout.tsx,
so wiring up was just useHqUi() per page + the replacement calls.
This commit is contained in:
1 parent
e177bca92f
commit
59a146ef10
9 files changed
+109
-14
No files matched your search
@@ -10,6 +10,7 @@ import {
|
||||
Maximize2, Minimize2, ChevronDown, FolderOpen, Upload, FolderPlus, ChevronRight, File, ArrowUpFromLine, Search, Grid3X3, LayoutList, Copy, Check
|
||||
} from "lucide-react";
|
||||
import { getApplications, createApplication, updateApplicationData, toggleApplication, deleteApplication } from "./actions";
|
||||
import { useHqUi } from "@/components/hq/Toast";
|
||||
|
||||
|
||||
// AssetBucketBrowser is the unified picker. The applications page uses an
|
||||
@@ -245,6 +246,7 @@ function MarkdownEditor({ name, defaultValue = "", required, rows = 12, placehol
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export default function ApplicationsManager() {
|
||||
const ui = useHqUi();
|
||||
const router = useRouter();
|
||||
const [apps, setApps] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -273,8 +275,14 @@ export default function ApplicationsManager() {
|
||||
formData.append("sectionsJson", JSON.stringify(sections));
|
||||
formData.append("dashboardMetricsJson", JSON.stringify(dashboardMetrics));
|
||||
const res = await updateApplicationData(formData);
|
||||
if (res.error) { alert("Error saving data: " + res.error); }
|
||||
else { setEditingApp(null); await fetchApps(); router.refresh(); }
|
||||
if (res.error) {
|
||||
ui.toast(`Error saving data: ${res.error}`, "error");
|
||||
} else {
|
||||
ui.toast("Application saved.", "success");
|
||||
setEditingApp(null);
|
||||
await fetchApps();
|
||||
router.refresh();
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
@@ -311,7 +319,18 @@ export default function ApplicationsManager() {
|
||||
<td className="p-6"><p className="font-medium text-white">{app.title}</p><p className="text-xs text-[#86868B] font-mono mt-1">/{app.slug}</p></td>
|
||||
<td className="p-6">{isPopulated ? <span className="bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 px-3 py-1 rounded-full text-[10px] uppercase tracking-wider flex items-center gap-1.5 w-fit font-semibold"><CheckCircle2 size={12} /> Populated</span> : <span className="bg-white/5 text-[#86868B] border border-white/10 px-3 py-1 rounded-full text-[10px] uppercase tracking-wider flex items-center gap-1.5 w-fit"><AlertCircle size={12} /> Pending Setup</span>}</td>
|
||||
<td className="p-6"><button onClick={() => {toggleApplication(app.slug, app.isActive); fetchApps();}} className={`text-xs font-medium px-3 py-1 rounded-full flex items-center gap-1.5 transition-colors ${app.isActive ? 'bg-purple-500/10 text-purple-400' : 'bg-red-500/10 text-red-400'}`}>{app.isActive ? <><Eye size={12} /> Visible</> : <><EyeOff size={12} /> Hidden</>}</button></td>
|
||||
<td className="p-6 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditModal(app)} className={`inline-flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all ${isPopulated ? 'bg-white/5 text-white hover:bg-white/10' : 'bg-purple-500 text-white hover:bg-purple-400'}`}><DatabaseZap size={14} /> Manage Core</button><button onClick={() => { if(confirm("Delete this application forever?")) { deleteApplication(app.slug); fetchApps(); } }} className="text-[#86868B] hover:text-red-400 p-2 rounded-lg transition-colors opacity-0 group-hover:opacity-100"><Trash2 size={18}/></button></div></td>
|
||||
<td className="p-6 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditModal(app)} className={`inline-flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all ${isPopulated ? 'bg-white/5 text-white hover:bg-white/10' : 'bg-purple-500 text-white hover:bg-purple-400'}`}><DatabaseZap size={14} /> Manage Core</button><button onClick={async () => {
|
||||
const ok = await ui.confirm({
|
||||
title: "Delete application",
|
||||
message: `Permanently remove "${app.title}" from the catalog. The asset folder on disk is preserved.`,
|
||||
confirmLabel: "Delete forever",
|
||||
destructive: true,
|
||||
});
|
||||
if (!ok) return;
|
||||
await deleteApplication(app.slug);
|
||||
ui.toast("Application deleted.", "success");
|
||||
fetchApps();
|
||||
}} className="text-[#86868B] hover:text-red-400 p-2 rounded-lg transition-colors opacity-0 group-hover:opacity-100"><Trash2 size={18}/></button></div></td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user