From 567dd2b7d1bbaf667cf450b73c2d1278617f44cc Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 17 Dec 2025 19:11:15 -0600 Subject: [PATCH] add attributes to more filters --- .../overlay/dialog/SearchFilterDialog.tsx | 78 +++++++++++++++++++ web/src/types/search.ts | 4 + 2 files changed, 82 insertions(+) diff --git a/web/src/components/overlay/dialog/SearchFilterDialog.tsx b/web/src/components/overlay/dialog/SearchFilterDialog.tsx index 3ee2052d0..2e63a2f84 100644 --- a/web/src/components/overlay/dialog/SearchFilterDialog.tsx +++ b/web/src/components/overlay/dialog/SearchFilterDialog.tsx @@ -65,6 +65,7 @@ export default function SearchFilterDialog({ const { t } = useTranslation(["components/filter"]); const [currentFilter, setCurrentFilter] = useState(filter ?? {}); const { data: allSubLabels } = useSWR(["sub_labels", { split_joined: 1 }]); + const { data: allAttributes } = useSWR("classification/attributes"); const { data: allRecognizedLicensePlates } = useSWR( "recognized_license_plates", ); @@ -91,6 +92,7 @@ export default function SearchFilterDialog({ (currentFilter.max_speed ?? 150) < 150 || (currentFilter.zones?.length ?? 0) > 0 || (currentFilter.sub_labels?.length ?? 0) > 0 || + (currentFilter.attributes?.length ?? 0) > 0 || (currentFilter.recognized_license_plate?.length ?? 0) > 0), [currentFilter], ); @@ -133,6 +135,13 @@ export default function SearchFilterDialog({ setCurrentFilter({ ...currentFilter, sub_labels: newSubLabels }) } /> + + setCurrentFilter({ ...currentFilter, attributes: newAttributes }) + } + /> ); } + +type AttributeFilterContentProps = { + allAttributes: string[]; + attributes: string[] | undefined; + setAttributes: (labels: string[] | undefined) => void; +}; +export function AttributeFilterContent({ + allAttributes, + attributes, + setAttributes, +}: AttributeFilterContentProps) { + const { t } = useTranslation(["components/filter"]); + const sortedAttributes = useMemo( + () => + [...(allAttributes || [])].sort((a, b) => + a.toLowerCase().localeCompare(b.toLowerCase()), + ), + [allAttributes], + ); + return ( +
+ +
{t("attributes.label")}
+
+ + { + if (isChecked) { + setAttributes(undefined); + } + }} + /> +
+
+ {sortedAttributes.map((item) => ( + { + if (isChecked) { + const updatedAttributes = attributes ? [...attributes] : []; + + updatedAttributes.push(item); + setAttributes(updatedAttributes); + } else { + const updatedAttributes = attributes ? [...attributes] : []; + + // can not deselect the last item + if (updatedAttributes.length > 1) { + updatedAttributes.splice(updatedAttributes.indexOf(item), 1); + setAttributes(updatedAttributes); + } + } + }} + /> + ))} +
+
+ ); +} diff --git a/web/src/types/search.ts b/web/src/types/search.ts index 8fb81dfc6..d47e95584 100644 --- a/web/src/types/search.ts +++ b/web/src/types/search.ts @@ -5,6 +5,7 @@ const SEARCH_FILTERS = [ "general", "zone", "sub", + "attribute", "source", "sort", ] as const; @@ -16,6 +17,7 @@ export const DEFAULT_SEARCH_FILTERS: SearchFilters[] = [ "general", "zone", "sub", + "attribute", "source", "sort", ]; @@ -71,6 +73,7 @@ export type SearchFilter = { cameras?: string[]; labels?: string[]; sub_labels?: string[]; + attributes?: string[]; recognized_license_plate?: string[]; zones?: string[]; before?: number; @@ -95,6 +98,7 @@ export type SearchQueryParams = { cameras?: string[]; labels?: string[]; sub_labels?: string[]; + attributes?: string[]; recognized_license_plate?: string[]; zones?: string[]; before?: string;