diff --git a/web/src/pages/SubmitPlus.tsx b/web/src/pages/SubmitPlus.tsx index 7581f8cb3..699cc313f 100644 --- a/web/src/pages/SubmitPlus.tsx +++ b/web/src/pages/SubmitPlus.tsx @@ -1,4 +1,5 @@ import { baseUrl } from "@/api/baseUrl"; +import FilterCheckBox from "@/components/filter/FilterCheckBox"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -8,15 +9,37 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Event } from "@/types/event"; +import { FrigateConfig } from "@/types/frigateConfig"; import axios from "axios"; -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; +import { FaList, FaVideo } from "react-icons/fa"; import useSWR from "swr"; export default function SubmitPlus() { + // filters + + const [selectedCameras, setSelectedCameras] = useState(); + const [selectedLabels, setSelectedLabels] = useState(); + + // data + const { data: events, mutate: refresh } = useSWR([ "events", - { limit: 100, in_progress: 0, is_submitted: 0 }, + { + limit: 100, + in_progress: 0, + is_submitted: 0, + cameras: selectedCameras ? selectedCameras.join(",") : null, + labels: selectedLabels ? selectedLabels.join(",") : null, + }, ]); const [upload, setUpload] = useState(); @@ -54,52 +77,270 @@ export default function SubmitPlus() { ); return ( -
- (!open ? setUpload(undefined) : null)} - > - - - Submit To Frigate+ - - Objects in locations you want to avoid are not false positives. - Submitting them as false positives will confuse the model. - - - {`${upload?.label}`} - - - - - - - - - {events?.map((event) => { - return ( -
setUpload(event)} +
+ +
+
+ (!open ? setUpload(undefined) : null)} > - -
- ); - })} + + + Submit To Frigate+ + + Objects in locations you want to avoid are not false + positives. Submitting them as false positives will confuse the + model. + + + {`${upload?.label}`} + + + + + + + + + {events?.map((event) => { + return ( +
setUpload(event)} + > + +
+ ); + })} +
+
+
+ ); +} + +const ATTRIBUTES = ["amazon", "face", "fedex", "license_plate", "ups"]; + +type PlusFilterGroupProps = { + selectedCameras: string[] | undefined; + setSelectedCameras: (cameras: string[] | undefined) => void; + selectedLabels: string[] | undefined; + setSelectedLabels: (cameras: string[] | undefined) => void; +}; +function PlusFilterGroup({ + selectedCameras, + setSelectedCameras, + selectedLabels, + setSelectedLabels, +}: PlusFilterGroupProps) { + const { data: config } = useSWR("config"); + + const allCameras = useMemo(() => { + if (!config) { + return []; + } + + return Object.keys(config.cameras); + }, [config]); + const allLabels = useMemo(() => { + if (!config) { + return []; + } + + const labels = new Set(); + const cameras = selectedCameras || Object.keys(config.cameras); + + cameras.forEach((camera) => { + const cameraConfig = config.cameras[camera]; + cameraConfig.objects.track.forEach((label) => { + if (!ATTRIBUTES.includes(label)) { + labels.add(label); + } + }); + }); + + return [...labels].sort(); + }, [config, selectedCameras]); + + const [open, setOpen] = useState<"none" | "camera" | "label">("none"); + const [currentCameras, setCurrentCameras] = useState( + undefined, + ); + const [currentLabels, setCurrentLabels] = useState( + undefined, + ); + + return ( +
+ { + if (!open) { + setCurrentCameras(selectedCameras); + } + setOpen(open ? "camera" : "none"); + }} + > + + + + + + Filter Cameras + + + { + if (isChecked) { + setCurrentCameras(undefined); + } + }} + /> + + {allCameras.map((item) => ( + { + if (isChecked) { + const updatedCameras = currentCameras + ? [...currentCameras] + : []; + + updatedCameras.push(item); + setCurrentCameras(updatedCameras); + } else { + const updatedCameras = currentCameras + ? [...currentCameras] + : []; + + // can not deselect the last item + if (updatedCameras.length > 1) { + updatedCameras.splice(updatedCameras.indexOf(item), 1); + setCurrentCameras(updatedCameras); + } + } + }} + /> + ))} + +
+ +
+
+
+ { + if (!open) { + setCurrentLabels(selectedLabels); + } + setOpen(open ? "label" : "none"); + }} + > + + + + + + Filter Labels + + + { + if (isChecked) { + setCurrentLabels(undefined); + } + }} + /> + + {allLabels.map((item) => ( + { + if (isChecked) { + const updatedLabels = currentLabels ? [...currentLabels] : []; + + updatedLabels.push(item); + setCurrentLabels(updatedLabels); + } else { + const updatedLabels = currentLabels ? [...currentLabels] : []; + + // can not deselect the last item + if (updatedLabels.length > 1) { + updatedLabels.splice(updatedLabels.indexOf(item), 1); + setCurrentLabels(updatedLabels); + } + } + }} + /> + ))} + +
+ +
+
+
); }