From 734a488db3cb23b23d687b359f4d61aae4bd83d4 Mon Sep 17 00:00:00 2001 From: Nick Mowen Date: Wed, 20 Dec 2023 13:25:57 -0700 Subject: [PATCH] Allow filtering on detail level --- .../filter/HistoryFilterPopover.tsx | 176 +++++++++++------- web/src/pages/History.tsx | 8 +- web/src/types/history.ts | 1 + 3 files changed, 118 insertions(+), 67 deletions(-) diff --git a/web/src/components/filter/HistoryFilterPopover.tsx b/web/src/components/filter/HistoryFilterPopover.tsx index 4a8390f9e..fe8111cb2 100644 --- a/web/src/components/filter/HistoryFilterPopover.tsx +++ b/web/src/components/filter/HistoryFilterPopover.tsx @@ -1,4 +1,4 @@ -import { LuCheck, LuFilter, LuFocus } from "react-icons/lu"; +import { LuCheck, LuFilter } from "react-icons/lu"; import { Button } from "../ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import useSWR from "swr"; @@ -8,6 +8,8 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu"; @@ -50,10 +52,11 @@ export default function HistoryFilterPopover({ [config, allLabels, allSubLabels] ); const [selectedFilters, setSelectedFilters] = useState({ - cameras: filter == undefined ? [] : filter.cameras, - labels: filter == undefined ? [] : filter.labels, + cameras: filter == undefined ? ["all"] : filter.cameras, + labels: filter == undefined ? ["all"] : filter.labels, before: filter?.before, after: filter?.after, + detailLevel: filter?.detailLevel ?? "normal", }); const dateRange = useMemo(() => { return selectedFilters?.before == undefined || @@ -65,6 +68,14 @@ export default function HistoryFilterPopover({ }; }, [selectedFilters]); + const allItems = useMemo(() => { + return { + cameras: + JSON.stringify(selectedFilters.cameras) == JSON.stringify(["all"]), + labels: JSON.stringify(selectedFilters.labels) == JSON.stringify(["all"]), + }; + }, [selectedFilters]); + return ( setOpen(open)}> @@ -78,7 +89,7 @@ export default function HistoryFilterPopover({ @@ -86,43 +97,50 @@ export default function HistoryFilterPopover({ Filter Cameras + { + if (isChecked) { + setSelectedFilters({ + ...selectedFilters, + cameras: ["all"], + }); + } + }} + /> + {filterValues.cameras.map((item) => ( { if (isChecked) { - const selectedCameras = [...selectedFilters.cameras]; + const selectedCameras = allItems.cameras + ? [] + : [...selectedFilters.cameras]; selectedCameras.push(item); setSelectedFilters({ ...selectedFilters, cameras: selectedCameras, }); } else { - const selectedCameraList = - selectedFilters.cameras.length == 0 - ? [...filterValues.cameras] - : [...selectedFilters.cameras]; - selectedCameraList.splice( - selectedCameraList.indexOf(item), - 1 - ); - setSelectedFilters({ - ...selectedFilters, - cameras: selectedCameraList, - }); + const selectedCameraList = [...selectedFilters.cameras]; + + // can not deselect the last item + if (selectedCameraList.length > 1) { + selectedCameraList.splice( + selectedCameraList.indexOf(item), + 1 + ); + setSelectedFilters({ + ...selectedFilters, + cameras: selectedCameraList, + }); + } } }} - onSingleSelect={() => { - setSelectedFilters({ - ...selectedFilters, - cameras: [item], - }); - }} /> ))} @@ -130,7 +148,7 @@ export default function HistoryFilterPopover({ @@ -138,6 +156,19 @@ export default function HistoryFilterPopover({ Filter Labels + { + if (isChecked) { + setSelectedFilters({ + ...selectedFilters, + labels: ["all"], + }); + } + }} + /> + {filterValues.labels.map((item) => ( { if (isChecked) { - const selectedLabels = [...selectedFilters.labels]; + const selectedLabels = allItems.labels + ? [] + : [...selectedFilters.labels]; selectedLabels.push(item); setSelectedFilters({ ...selectedFilters, labels: selectedLabels, }); } else { - const selectedLabelList = - selectedFilters.labels.length == 0 - ? [...filterValues.labels] - : selectedFilters.labels; - selectedLabelList.splice( - selectedLabelList.indexOf(item), - 1 - ); - setSelectedFilters({ - ...selectedFilters, - labels: selectedLabelList, - }); + const selectedLabelList = [...selectedFilters.labels]; + + // can not deselect the last item + if (selectedLabelList.length > 1) { + selectedLabelList.splice( + selectedLabelList.indexOf(item), + 1 + ); + setSelectedFilters({ + ...selectedFilters, + labels: selectedLabelList, + }); + } } }} - onSingleSelect={() => { - setSelectedFilters({ - ...selectedFilters, - labels: [item], - }); - }} /> ))} + + + + + + + Detail Level + + + { + setSelectedFilters({ + ...selectedFilters, + // @ts-ignore we know that value is one of the detailLevel + detailLevel: value, + }); + }} + > + + Normal + + + Extra + + Full + + + void; - onSingleSelect: () => void; }; function FilterCheckBox({ label, isChecked, onCheckedChange, - onSingleSelect, }: FilterCheckBoxProps) { return ( -
onCheckedChange(!isChecked)} > {isChecked ? ( - + ) : ( -
+
)}
{label}
- -
+ ); } diff --git a/web/src/pages/History.tsx b/web/src/pages/History.tsx index fffa1ebd9..bc174a8ba 100644 --- a/web/src/pages/History.tsx +++ b/web/src/pages/History.tsx @@ -80,7 +80,6 @@ function History() { { revalidateOnFocus: false } ); - const [detailLevel, _] = useState<"normal" | "extra" | "full">("normal"); const [playback, setPlayback] = useState(); const shouldAutoPlay = useMemo(() => { @@ -92,8 +91,11 @@ function History() { return []; } - return getHourlyTimelineData(timelinePages, detailLevel); - }, [detailLevel, timelinePages]); + return getHourlyTimelineData( + timelinePages, + historyFilter?.detailLevel ?? "normal" + ); + }, [historyFilter, timelinePages]); const isDone = (timelinePages?.[timelinePages.length - 1]?.count ?? 0) < API_LIMIT; diff --git a/web/src/types/history.ts b/web/src/types/history.ts index 8c156e46f..a5e7fa8a7 100644 --- a/web/src/types/history.ts +++ b/web/src/types/history.ts @@ -44,4 +44,5 @@ interface HistoryFilter extends FilterType { labels: string[]; before: number | undefined; after: number | undefined; + detailLevel: "normal" | "extra" | "full"; }