add confirmation dialog for clearing groups and streaming settings

This commit is contained in:
Josh Hawkins
2026-09-24 13:27:29 -05:00
parent 1f5972426e
commit 2455e8be79
3 changed files with 78 additions and 5 deletions
+1
View File
@@ -131,6 +131,7 @@
"close": "Close", "close": "Close",
"expand": "Expand", "expand": "Expand",
"collapse": "Collapse", "collapse": "Collapse",
"clear": "Clear",
"copy": "Copy", "copy": "Copy",
"copiedToClipboard": "Copied to clipboard", "copiedToClipboard": "Copied to clipboard",
"back": "Back", "back": "Back",
+4 -2
View File
@@ -180,12 +180,14 @@
"storedLayouts": { "storedLayouts": {
"title": "Stored Layouts", "title": "Stored Layouts",
"desc": "The layout of cameras in a camera group can be dragged/resized. The positions are stored in your browser's local storage.", "desc": "The layout of cameras in a camera group can be dragged/resized. The positions are stored in your browser's local storage.",
"clearAll": "Clear All Layouts" "clearAll": "Clear All Layouts",
"clearConfirm": "This will clear the stored layout for every camera group in this browser. This cannot be undone."
}, },
"cameraGroupStreaming": { "cameraGroupStreaming": {
"title": "Camera Group Streaming Settings", "title": "Camera Group Streaming Settings",
"desc": "Streaming settings for each camera group are stored in your browser's local storage.", "desc": "Streaming settings for each camera group are stored in your browser's local storage.",
"clearAll": "Clear All Streaming Settings" "clearAll": "Clear All Streaming Settings",
"clearConfirm": "This will clear the streaming settings for every camera group in this browser. This cannot be undone."
}, },
"backupRestore": { "backupRestore": {
"title": "Backup & Restore", "title": "Backup & Restore",
+73 -3
View File
@@ -12,7 +12,7 @@ import {
useState, useState,
} from "react"; } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { Button } from "../../components/ui/button"; import { Button, buttonVariants } from "../../components/ui/button";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { import {
@@ -36,6 +36,17 @@ import {
CONTROL_COLUMN_CLASS_NAME, CONTROL_COLUMN_CLASS_NAME,
} from "@/components/card/SettingsGroupCard"; } from "@/components/card/SettingsGroupCard";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { cn } from "@/lib/utils";
import ImportUiSettingsDialog from "@/components/overlay/dialog/ImportUiSettingsDialog"; import ImportUiSettingsDialog from "@/components/overlay/dialog/ImportUiSettingsDialog";
import { import {
applyImportPayload, applyImportPayload,
@@ -54,6 +65,8 @@ import {
const WEEK_STARTS_ON = ["Sunday", "Monday"]; const WEEK_STARTS_ON = ["Sunday", "Monday"];
const IMPORT_FAILED_FLAG = "frigate-ui-settings-import-failed"; const IMPORT_FAILED_FLAG = "frigate-ui-settings-import-failed";
type ClearTarget = "layouts" | "streaming";
type SwitchSettingRowProps = { type SwitchSettingRowProps = {
id: string; id: string;
label: string; label: string;
@@ -204,6 +217,24 @@ export default function UiSettingsView() {
}); });
}, [config, t, username]); }, [config, t, username]);
const [pendingClear, setPendingClear] = useState<ClearTarget | null>(null);
const clearConfirmCopy = useCallback(
(target: ClearTarget) =>
// literal keys per branch: a template key would be invisible to
// npm run i18n:extract, which CI verifies
target === "layouts"
? {
title: t("general.storedLayouts.clearAll"),
description: t("general.storedLayouts.clearConfirm"),
}
: {
title: t("general.cameraGroupStreaming.clearAll"),
description: t("general.cameraGroupStreaming.clearConfirm"),
},
[t],
);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const [pendingImport, setPendingImport] = useState<{ const [pendingImport, setPendingImport] = useState<{
name: string; name: string;
@@ -450,7 +481,7 @@ export default function UiSettingsView() {
id="stored-layouts-clear" id="stored-layouts-clear"
aria-label={t("general.storedLayouts.clearAll")} aria-label={t("general.storedLayouts.clearAll")}
className="w-full md:w-auto" className="w-full md:w-auto"
onClick={clearStoredLayouts} onClick={() => setPendingClear("layouts")}
> >
{t("general.storedLayouts.clearAll")} {t("general.storedLayouts.clearAll")}
</Button> </Button>
@@ -466,7 +497,7 @@ export default function UiSettingsView() {
id="camera-group-streaming-clear" id="camera-group-streaming-clear"
aria-label={t("general.cameraGroupStreaming.clearAll")} aria-label={t("general.cameraGroupStreaming.clearAll")}
className="w-full md:w-auto" className="w-full md:w-auto"
onClick={clearStreamingSettings} onClick={() => setPendingClear("streaming")}
> >
{t("general.cameraGroupStreaming.clearAll")} {t("general.cameraGroupStreaming.clearAll")}
</Button> </Button>
@@ -604,6 +635,45 @@ export default function UiSettingsView() {
onConfirm={handleImportConfirm} onConfirm={handleImportConfirm}
/> />
)} )}
<AlertDialog
open={pendingClear != null}
onOpenChange={(open) => {
if (!open) {
setPendingClear(null);
}
}}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{pendingClear && clearConfirmCopy(pendingClear).title}
</AlertDialogTitle>
<AlertDialogDescription>
{pendingClear && clearConfirmCopy(pendingClear).description}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>
{t("button.cancel", { ns: "common" })}
</AlertDialogCancel>
<AlertDialogAction
className={cn(buttonVariants({ variant: "destructive" }))}
onClick={() => {
if (pendingClear === "layouts") {
clearStoredLayouts();
} else {
clearStreamingSettings();
}
setPendingClear(null);
}}
>
{t("button.clear", { ns: "common" })}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> </div>
); );
} }