From 92cfb7bc1657314c4b9a987c33082d47db746116 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 6 Mar 2024 14:36:03 -0700 Subject: [PATCH] Support deleting and edit existing camera groups --- .../components/filter/CameraGroupSelector.tsx | 213 ++++++++++++------ 1 file changed, 144 insertions(+), 69 deletions(-) diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index 5ed50be7c..475da5d1e 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -1,4 +1,8 @@ -import { FrigateConfig, GROUP_ICONS } from "@/types/frigateConfig"; +import { + CameraGroupConfig, + FrigateConfig, + GROUP_ICONS, +} from "@/types/frigateConfig"; import { isDesktop } from "react-device-detect"; import useSWR from "swr"; import { MdHome } from "react-icons/md"; @@ -8,7 +12,7 @@ import { useNavigate } from "react-router-dom"; import { useCallback, useMemo, useState } from "react"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { getIconForGroup } from "@/utils/iconUtil"; -import { LuPlus } from "react-icons/lu"; +import { LuPencil, LuPlus, LuTrash } from "react-icons/lu"; import { Dialog, DialogContent, DialogTitle } from "../ui/dialog"; import { Input } from "../ui/input"; import { @@ -16,6 +20,7 @@ import { DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu"; import FilterCheckBox from "./FilterCheckBox"; @@ -72,7 +77,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { @@ -135,14 +140,21 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { type NewGroupDialogProps = { open: boolean; setOpen: (open: boolean) => void; - index: number; + currentGroups: [string, CameraGroupConfig][]; }; -function NewGroupDialog({ open, setOpen, index }: NewGroupDialogProps) { +function NewGroupDialog({ open, setOpen, currentGroups }: NewGroupDialogProps) { const { data: config, mutate: updateConfig } = useSWR("config"); + + // add fields + + const [editState, setEditState] = useState<"none" | "add" | "edit">("none"); const [newTitle, setNewTitle] = useState(""); const [icon, setIcon] = useState(""); const [cameras, setCameras] = useState([]); + + // validation + const [error, setError] = useState(""); const onCreateGroup = useCallback(async () => { @@ -162,7 +174,7 @@ function NewGroupDialog({ open, setOpen, index }: NewGroupDialogProps) { } setError(""); - const orderQuery = `camera_groups.${newTitle}.order=${index}`; + const orderQuery = `camera_groups.${newTitle}.order=${currentGroups.length}`; const iconQuery = `camera_groups.${newTitle}.icon=${icon}`; const cameraQueries = cameras .map((cam) => `&camera_groups.${newTitle}.cameras=${cam}`) @@ -181,73 +193,136 @@ function NewGroupDialog({ open, setOpen, index }: NewGroupDialogProps) { setCameras([]); updateConfig(); } - }, [index, cameras, newTitle, icon, setOpen, updateConfig]); + }, [currentGroups, cameras, newTitle, icon, setOpen, updateConfig]); + + const onDeleteGroup = useCallback( + async (name: string) => { + const req = axios.put(`config/set?camera_groups.${name}`, { + requires_restart: 0, + }); + + if ((await req).status == 200) { + updateConfig(); + } + }, + [updateConfig], + ); return ( - + { + setEditState("none"); + setNewTitle(""); + setIcon(""); + setCameras([]); + setOpen(open); + }} + > - Create New Camera Group - setNewTitle(e.target.value)} - /> - - -
- {icon.length == 0 ? "Select Icon" : "Icon: "} - {icon ? getIconForGroup(icon) :
} -
- - - - {GROUP_ICONS.map((gIcon) => ( - - {getIconForGroup(gIcon)} - {gIcon} - - ))} - - - - - -
- {cameras.length == 0 - ? "Select Cameras" - : `${cameras.length} Cameras`} -
-
- - {Object.keys(config?.cameras ?? {}).map((camera) => ( - { - if (checked) { - setCameras([...cameras, camera]); - } else { - const index = cameras.indexOf(camera); - setCameras([ - ...cameras.slice(0, index), - ...cameras.slice(index + 1), - ]); - } + Camera Groups + {currentGroups.map((group) => ( +
+ {group[0]} +
+ + > + + + +
+
+ ))} + + {editState == "none" && ( + + )} + {editState != "none" && ( + <> + setNewTitle(e.target.value)} + /> + + +
+ {icon.length == 0 ? "Select Icon" : "Icon: "} + {icon ? getIconForGroup(icon) :
} +
+ + + + {GROUP_ICONS.map((gIcon) => ( + + {getIconForGroup(gIcon)} + {gIcon} + + ))} + + + + + +
+ {cameras.length == 0 + ? "Select Cameras" + : `${cameras.length} Cameras`} +
+
+ + {Object.keys(config?.cameras ?? {}).map((camera) => ( + { + if (checked) { + setCameras([...cameras, camera]); + } else { + const index = cameras.indexOf(camera); + setCameras([ + ...cameras.slice(0, index), + ...cameras.slice(index + 1), + ]); + } + }} + /> + ))} + +
+ {error &&
{error}
} + + + )}
);