diff --git a/web/src/components/filter/SearchFilterGroup.tsx b/web/src/components/filter/SearchFilterGroup.tsx index 5904ff07e..3fdda71fe 100644 --- a/web/src/components/filter/SearchFilterGroup.tsx +++ b/web/src/components/filter/SearchFilterGroup.tsx @@ -18,9 +18,14 @@ import { SearchFilter, SearchSource } from "@/types/search"; import { DateRange } from "react-day-picker"; import { cn } from "@/lib/utils"; -const SEARCH_FILTERS = ["cameras", "date", "general"] as const; +const SEARCH_FILTERS = ["cameras", "date", "general", "sub"] as const; type SearchFilters = (typeof SEARCH_FILTERS)[number]; -const DEFAULT_REVIEW_FILTERS: SearchFilters[] = ["cameras", "date", "general"]; +const DEFAULT_REVIEW_FILTERS: SearchFilters[] = [ + "cameras", + "date", + "general", + "sub", +]; type SearchFilterGroupProps = { className: string; @@ -72,6 +77,8 @@ export default function SearchFilterGroup({ return [...labels].sort(); }, [config, filterList, filter]); + const { data: allSubLabels } = useSWR("sub_labels"); + const allZones = useMemo(() => { if (filterList?.zones) { return filterList.zones; @@ -181,6 +188,15 @@ export default function SearchFilterGroup({ } /> )} + {filters.includes("sub") && ( + + onUpdateFilter({ ...filter, subLabels: newSubLabels }) + } + /> + )} ); } @@ -509,3 +525,175 @@ export function GeneralFilterContent({ ); } + +type SubFilterButtonProps = { + allSubLabels: string[]; + selectedSubLabels: string[] | undefined; + updateSubLabelFilter: (labels: string[] | undefined) => void; +}; +function SubFilterButton({ + allSubLabels, + selectedSubLabels, + updateSubLabelFilter, +}: SubFilterButtonProps) { + const [open, setOpen] = useState(false); + const [currentSubLabels, setCurrentSubLabels] = useState< + string[] | undefined + >(selectedSubLabels); + + const trigger = ( + + ); + const content = ( + setOpen(false)} + /> + ); + + if (isMobile) { + return ( + { + if (!open) { + setCurrentSubLabels(selectedSubLabels); + } + + setOpen(open); + }} + > + {trigger} + + {content} + + + ); + } + + return ( + { + if (!open) { + setCurrentSubLabels(selectedSubLabels); + } + + setOpen(open); + }} + > + {trigger} + {content} + + ); +} + +type SubFilterContentProps = { + allSubLabels: string[]; + selectedSubLabels: string[] | undefined; + currentSubLabels: string[] | undefined; + updateSubLabelFilter: (labels: string[] | undefined) => void; + setCurrentSubLabels: (labels: string[] | undefined) => void; + onClose: () => void; +}; +export function SubFilterContent({ + allSubLabels, + selectedSubLabels, + currentSubLabels, + updateSubLabelFilter, + setCurrentSubLabels, + onClose, +}: SubFilterContentProps) { + return ( + <> +
+
+ + { + if (isChecked) { + setCurrentSubLabels(undefined); + } + }} + /> +
+
+ {allSubLabels.map((item) => ( + { + if (isChecked) { + const updatedLabels = currentSubLabels + ? [...currentSubLabels] + : []; + + updatedLabels.push(item); + setCurrentSubLabels(updatedLabels); + } else { + const updatedLabels = currentSubLabels + ? [...currentSubLabels] + : []; + + // can not deselect the last item + if (updatedLabels.length > 1) { + updatedLabels.splice(updatedLabels.indexOf(item), 1); + setCurrentSubLabels(updatedLabels); + } + } + }} + /> + ))} +
+
+ +
+ + +
+ + ); +} diff --git a/web/src/pages/Search.tsx b/web/src/pages/Search.tsx index b51a0afc9..6d5f4d4bb 100644 --- a/web/src/pages/Search.tsx +++ b/web/src/pages/Search.tsx @@ -67,6 +67,7 @@ export default function Search() { query: similaritySearch.id, cameras: searchSearchParams["cameras"], labels: searchSearchParams["labels"], + sub_labels: searchSearchParams["subLabels"], zones: searchSearchParams["zones"], before: searchSearchParams["before"], after: searchSearchParams["after"], @@ -83,6 +84,7 @@ export default function Search() { query: searchTerm, cameras: searchSearchParams["cameras"], labels: searchSearchParams["labels"], + sub_labels: searchSearchParams["subLabels"], zones: searchSearchParams["zones"], before: searchSearchParams["before"], after: searchSearchParams["after"], @@ -97,6 +99,7 @@ export default function Search() { { cameras: searchSearchParams["cameras"], labels: searchSearchParams["labels"], + sub_labels: searchSearchParams["subLabels"], zones: searchSearchParams["zones"], before: searchSearchParams["before"], after: searchSearchParams["after"], diff --git a/web/src/types/search.ts b/web/src/types/search.ts index 1bb12731c..a36a4af56 100644 --- a/web/src/types/search.ts +++ b/web/src/types/search.ts @@ -18,6 +18,7 @@ export type SearchResult = { export type SearchFilter = { cameras?: string[]; labels?: string[]; + subLabels?: string[]; zones?: string[]; before?: number; after?: number;