add attributes to more filters

This commit is contained in:
Josh Hawkins 2025-12-17 19:11:15 -06:00
parent cb9dee3dc9
commit 567dd2b7d1
2 changed files with 82 additions and 0 deletions

View File

@ -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<string[]>(
"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 })
}
/>
<AttributeFilterContent
allAttributes={allAttributes}
attributes={currentFilter.attributes}
setAttributes={(newAttributes) =>
setCurrentFilter({ ...currentFilter, attributes: newAttributes })
}
/>
<RecognizedLicensePlatesFilterContent
allRecognizedLicensePlates={allRecognizedLicensePlates}
recognizedLicensePlates={currentFilter.recognized_license_plate}
@ -1087,3 +1096,72 @@ export function RecognizedLicensePlatesFilterContent({
</div>
);
}
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 (
<div className="overflow-x-hidden">
<DropdownMenuSeparator className="mb-3" />
<div className="text-lg">{t("attributes.label")}</div>
<div className="mb-5 mt-2.5 flex items-center justify-between">
<Label
className="mx-2 cursor-pointer text-primary"
htmlFor="allAttributes"
>
{t("attributes.all")}
</Label>
<Switch
className="ml-1"
id="allAttributes"
checked={attributes == undefined}
onCheckedChange={(isChecked) => {
if (isChecked) {
setAttributes(undefined);
}
}}
/>
</div>
<div className="mt-2.5 flex flex-col gap-2.5">
{sortedAttributes.map((item) => (
<FilterSwitch
key={item}
label={item.replaceAll("_", " ")}
isChecked={attributes?.includes(item) ?? false}
onCheckedChange={(isChecked) => {
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);
}
}
}}
/>
))}
</div>
</div>
);
}

View File

@ -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;