Add ability to clear region grids from the frontend (#22277)

* backend

* frontend

* i18n

* tweaks
This commit is contained in:
Josh Hawkins
2026-03-05 17:19:30 -06:00
committed by GitHub
parent 02678f4a09
commit 229436c94a
5 changed files with 167 additions and 7 deletions

View File

@@ -40,7 +40,8 @@ import UsersView from "@/views/settings/UsersView";
import RolesView from "@/views/settings/RolesView";
import UiSettingsView from "@/views/settings/UiSettingsView";
import FrigatePlusSettingsView from "@/views/settings/FrigatePlusSettingsView";
import MaintenanceSettingsView from "@/views/settings/MaintenanceSettingsView";
import MediaSyncSettingsView from "@/views/settings/MediaSyncSettingsView";
import RegionGridSettingsView from "@/views/settings/RegionGridSettingsView";
import SystemDetectionModelSettingsView from "@/views/settings/SystemDetectionModelSettingsView";
import {
SingleSectionPage,
@@ -154,7 +155,8 @@ const allSettingsViews = [
"roles",
"notifications",
"frigateplus",
"maintenance",
"mediaSync",
"regionGrid",
] as const;
type SettingsType = (typeof allSettingsViews)[number];
@@ -444,7 +446,10 @@ const settingsGroups = [
},
{
label: "maintenance",
items: [{ key: "maintenance", component: MaintenanceSettingsView }],
items: [
{ key: "mediaSync", component: MediaSyncSettingsView },
{ key: "regionGrid", component: RegionGridSettingsView },
],
},
];
@@ -471,6 +476,7 @@ const CAMERA_SELECT_BUTTON_PAGES = [
"masksAndZones",
"motionTuner",
"triggers",
"regionGrid",
];
const ALLOWED_VIEWS_FOR_VIEWER = ["ui", "debug", "notifications"];
@@ -478,7 +484,8 @@ const ALLOWED_VIEWS_FOR_VIEWER = ["ui", "debug", "notifications"];
const LARGE_BOTTOM_MARGIN_PAGES = [
"masksAndZones",
"motionTuner",
"maintenance",
"mediaSync",
"regionGrid",
];
// keys for camera sections

View File

@@ -15,7 +15,7 @@ import { cn } from "@/lib/utils";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { MediaSyncStats } from "@/types/ws";
export default function MaintenanceSettingsView() {
export default function MediaSyncSettingsView() {
const { t } = useTranslation("views/settings");
const [selectedMediaTypes, setSelectedMediaTypes] = useState<string[]>([
"all",
@@ -103,7 +103,7 @@ export default function MaintenanceSettingsView() {
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
<div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
<div className="col-span-1">
<Heading as="h4" className="mb-2">
<Heading as="h4" className="mb-2 hidden md:block">
{t("maintenance.sync.title")}
</Heading>

View File

@@ -0,0 +1,124 @@
import Heading from "@/components/ui/heading";
import { Button, buttonVariants } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Toaster } from "@/components/ui/sonner";
import { useCallback, useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import axios from "axios";
import { toast } from "sonner";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { cn } from "@/lib/utils";
type RegionGridSettingsViewProps = {
selectedCamera: string;
};
export default function RegionGridSettingsView({
selectedCamera,
}: RegionGridSettingsViewProps) {
const { t } = useTranslation("views/settings");
const { addMessage } = useContext(StatusBarMessagesContext)!;
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
const [isClearing, setIsClearing] = useState(false);
const [imageKey, setImageKey] = useState(0);
const handleClear = useCallback(async () => {
setIsClearing(true);
try {
await axios.delete(`${selectedCamera}/region_grid`);
toast.success(t("maintenance.regionGrid.clearSuccess"), {
position: "top-center",
});
setImageKey((prev) => prev + 1);
addMessage(
"region_grid_restart",
t("maintenance.regionGrid.restartRequired"),
undefined,
"region_grid_settings",
);
} catch {
toast.error(t("maintenance.regionGrid.clearError"), {
position: "top-center",
});
} finally {
setIsClearing(false);
setIsConfirmOpen(false);
}
}, [selectedCamera, t, addMessage]);
return (
<>
<div className="flex size-full flex-col md:flex-row">
<Toaster position="top-center" closeButton={true} />
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
<Heading as="h4" className="mb-2 hidden md:block">
{t("maintenance.regionGrid.title")}
</Heading>
<div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-muted-foreground">
<p>{t("maintenance.regionGrid.desc")}</p>
</div>
</div>
<div className="mb-4 max-w-5xl rounded-lg border border-secondary">
<img
key={imageKey}
src={`api/${selectedCamera}/grid.jpg?cache=${imageKey}`}
alt={t("maintenance.regionGrid.title")}
className="w-full"
/>
</div>
<div className="flex w-full flex-row items-center gap-2 py-2 md:w-[50%]">
<Button
onClick={() => setIsConfirmOpen(true)}
disabled={isClearing}
variant="destructive"
className="flex flex-1 text-white md:max-w-sm"
>
{t("maintenance.regionGrid.clear")}
</Button>
</div>
</div>
</div>
<AlertDialog open={isConfirmOpen} onOpenChange={setIsConfirmOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("maintenance.regionGrid.clearConfirmTitle")}
</AlertDialogTitle>
<AlertDialogDescription>
{t("maintenance.regionGrid.clearConfirmDesc")}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>
{t("button.cancel", { ns: "common" })}
</AlertDialogCancel>
<AlertDialogAction
className={cn(
buttonVariants({ variant: "destructive" }),
"text-white",
)}
onClick={handleClear}
>
{t("maintenance.regionGrid.clear")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}