add select all link to face library, classification, and explore

This commit is contained in:
Josh Hawkins 2025-12-10 08:28:48 -06:00
parent 44c92e84cd
commit 97ba7a6b78
6 changed files with 75 additions and 6 deletions

View File

@ -52,6 +52,7 @@
},
"selected_one": "{{count}} selected",
"selected_other": "{{count}} selected",
"select_all": "All",
"camera": "Camera",
"detected": "detected",
"normalActivity": "Normal",

View File

@ -29,6 +29,7 @@
},
"train": {
"title": "Recent Recognitions",
"titleShort": "Recent",
"aria": "Select recent recognitions",
"empty": "There are no recent face recognition attempts"
},

View File

@ -22,11 +22,15 @@ type SearchActionGroupProps = {
selectedObjects: string[];
setSelectedObjects: (ids: string[]) => void;
pullLatestData: () => void;
onSelectAllObjects: () => void;
totalItems: number;
};
export default function SearchActionGroup({
selectedObjects,
setSelectedObjects,
pullLatestData,
onSelectAllObjects,
totalItems,
}: SearchActionGroupProps) {
const { t } = useTranslation(["components/filter"]);
const isAdmin = useIsAdmin();
@ -124,6 +128,17 @@ export default function SearchActionGroup({
>
{t("button.unselect", { ns: "common" })}
</div>
{selectedObjects.length < totalItems && (
<>
<div className="p-1">{"|"}</div>
<div
className="cursor-pointer p-2 text-primary hover:rounded-lg hover:bg-secondary"
onClick={onSelectAllObjects}
>
{t("select_all", { ns: "views/events" })}
</div>
</>
)}
</div>
{isAdmin && (
<div className="flex items-center gap-1 md:gap-2">

View File

@ -52,7 +52,7 @@ import {
useRef,
useState,
} from "react";
import { isDesktop } from "react-device-detect";
import { isDesktop, isMobileOnly } from "react-device-detect";
import { Trans, useTranslation } from "react-i18next";
import {
LuFolderCheck,
@ -370,10 +370,10 @@ export default function FaceLibrary() {
/>
{selectedFaces?.length > 0 ? (
<div className="flex items-center justify-center gap-2">
<div className="mx-1 flex w-48 items-center justify-center text-sm text-muted-foreground">
<div className="mx-1 flex w-auto items-center justify-center text-sm text-muted-foreground">
<div className="p-1">
{t("selected", {
ns: "views/event",
ns: "views/events",
count: selectedFaces.length,
})}
</div>
@ -384,6 +384,24 @@ export default function FaceLibrary() {
>
{t("button.unselect", { ns: "common" })}
</div>
{selectedFaces.length <
(pageToggle === "train"
? trainImages.length
: faceImages.length) && (
<>
<div className="p-1">{"|"}</div>
<div
className="cursor-pointer p-2 text-primary hover:rounded-lg hover:bg-secondary"
onClick={() =>
setSelectedFaces([
...(pageToggle === "train" ? trainImages : faceImages),
])
}
>
{t("select_all", { ns: "views/events" })}
</div>
</>
)}
</div>
<Button
className="flex gap-2"
@ -482,6 +500,18 @@ function LibrarySelector({
[renameFace],
);
const pageTitle = useMemo(() => {
if (pageToggle != "train") {
return pageToggle;
}
if (isMobileOnly) {
return t("train.titleShort");
}
return t("train.title");
}, [pageToggle, t]);
return (
<>
<Dialog
@ -532,7 +562,7 @@ function LibrarySelector({
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button className="flex justify-between smart-capitalize">
{pageToggle == "train" ? t("train.title") : pageToggle}
{pageTitle}
<span className="ml-2 text-primary-variant">
({(pageToggle && faceData?.[pageToggle]?.length) || 0})
</span>

View File

@ -421,10 +421,10 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
isMobileOnly && "justify-between",
)}
>
<div className="flex w-48 items-center justify-center text-sm text-muted-foreground">
<div className="flex w-auto items-center justify-center text-sm text-muted-foreground md:w-auto">
<div className="p-1">
{t("selected", {
ns: "views/event",
ns: "views/events",
count: selectedImages.length,
})}
</div>
@ -435,6 +435,26 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
>
{t("button.unselect", { ns: "common" })}
</div>
{selectedImages.length <
(pageToggle === "train"
? trainImages?.length || 0
: dataset?.[pageToggle]?.length || 0) && (
<>
<div className="p-1">{"|"}</div>
<div
className="cursor-pointer p-2 text-primary hover:rounded-lg hover:bg-secondary"
onClick={() =>
setSelectedImages([
...(pageToggle === "train"
? trainImages || []
: dataset?.[pageToggle] || []),
])
}
>
{t("select_all", { ns: "views/events" })}
</div>
</>
)}
</div>
<Button
className="flex gap-2"

View File

@ -572,6 +572,8 @@ export default function SearchView({
selectedObjects={selectedObjects}
setSelectedObjects={setSelectedObjects}
pullLatestData={refresh}
onSelectAllObjects={onSelectAllObjects}
totalItems={uniqueResults.length}
/>
</div>
)}