mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-21 03:09:02 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c3e721a81 | ||
|
|
0de4922d72 | ||
|
|
208b80939b | ||
|
|
4da33493cd |
@@ -32,7 +32,11 @@
|
||||
"title": "Recent Recognitions",
|
||||
"titleShort": "Recent",
|
||||
"aria": "Select recent recognitions",
|
||||
"empty": "There are no recent face recognition attempts"
|
||||
"empty": "There are no recent face recognition attempts",
|
||||
"emptyNoLibrary": {
|
||||
"title": "Upload a face",
|
||||
"description": "You must add at least one face to the library for face recognition to function."
|
||||
}
|
||||
},
|
||||
"deleteFaceLibrary": {
|
||||
"title": "Delete Name",
|
||||
|
||||
@@ -446,6 +446,7 @@
|
||||
},
|
||||
"cameraManagement": {
|
||||
"title": "Manage Cameras",
|
||||
"description": "Add, edit, and delete cameras, control which cameras are enabled, and configure per-profile and camera type overrides. To configure streams, detection, motion, and other camera-specific settings, choose the specific section under Camera Configuration.",
|
||||
"addCamera": "Add New Camera",
|
||||
"deleteCamera": "Delete Camera",
|
||||
"deleteCameraDialog": {
|
||||
@@ -1744,4 +1745,4 @@
|
||||
"jinav2SmallModelSize": "The 'small' size with the Jina V2 model has high RAM and inference cost. The 'large' model with a discrete GPU is recommended."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ type EmptyCardProps = {
|
||||
description?: string;
|
||||
buttonText?: string;
|
||||
link?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
export function EmptyCard({
|
||||
className,
|
||||
@@ -21,6 +22,7 @@ export function EmptyCard({
|
||||
description,
|
||||
buttonText,
|
||||
link,
|
||||
onClick,
|
||||
}: EmptyCardProps) {
|
||||
let TitleComponent;
|
||||
|
||||
@@ -39,11 +41,16 @@ export function EmptyCard({
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
{buttonText?.length && (
|
||||
<Button size="sm" variant="select">
|
||||
<Link to={link ?? "#"}>{buttonText}</Link>
|
||||
</Button>
|
||||
)}
|
||||
{buttonText?.length &&
|
||||
(onClick ? (
|
||||
<Button size="sm" variant="select" onClick={onClick}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
) : (
|
||||
<Button size="sm" variant="select">
|
||||
<Link to={link ?? "#"}>{buttonText}</Link>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import AddFaceIcon from "@/components/icons/AddFaceIcon";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import { EmptyCard } from "@/components/card/EmptyCard";
|
||||
import CreateFaceWizardDialog from "@/components/overlay/detail/FaceCreateWizardDialog";
|
||||
import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog";
|
||||
import UploadImageDialog from "@/components/overlay/dialog/UploadImageDialog";
|
||||
@@ -473,7 +474,9 @@ export default function FaceLibrary() {
|
||||
attemptImages={trainImages}
|
||||
faceNames={faces}
|
||||
selectedFaces={selectedFaces}
|
||||
isLoading={faceData === undefined}
|
||||
onClickFaces={onClickFaces}
|
||||
onAddFace={() => setAddFace(true)}
|
||||
onRefresh={refreshFaces}
|
||||
/>
|
||||
) : (
|
||||
@@ -691,7 +694,9 @@ type TrainingGridProps = {
|
||||
attemptImages: string[];
|
||||
faceNames: string[];
|
||||
selectedFaces: string[];
|
||||
isLoading: boolean;
|
||||
onClickFaces: (images: string[], ctrl: boolean) => void;
|
||||
onAddFace: () => void;
|
||||
onRefresh: (
|
||||
data?:
|
||||
| FaceLibraryData
|
||||
@@ -708,7 +713,9 @@ function TrainingGrid({
|
||||
attemptImages,
|
||||
faceNames,
|
||||
selectedFaces,
|
||||
isLoading,
|
||||
onClickFaces,
|
||||
onAddFace,
|
||||
onRefresh,
|
||||
}: TrainingGridProps) {
|
||||
const { t } = useTranslation(["views/faceLibrary"]);
|
||||
@@ -762,6 +769,25 @@ function TrainingGrid({
|
||||
]);
|
||||
|
||||
if (attemptImages.length == 0) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 items-center text-center" />
|
||||
);
|
||||
}
|
||||
|
||||
if (faceNames.length == 0) {
|
||||
return (
|
||||
<EmptyCard
|
||||
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 items-center text-center"
|
||||
icon={<AddFaceIcon className="size-16" />}
|
||||
title={t("train.emptyNoLibrary.title")}
|
||||
description={t("train.emptyNoLibrary.description")}
|
||||
buttonText={t("button.addFace")}
|
||||
onClick={onAddFace}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center text-center">
|
||||
<LuFolderCheck className="size-16" />
|
||||
|
||||
@@ -122,9 +122,12 @@ export default function CameraManagementView({
|
||||
<div className="scrollbar-container flex-1 overflow-y-auto pb-2">
|
||||
{viewMode === "settings" ? (
|
||||
<>
|
||||
<Heading as="h4" className="mb-6">
|
||||
<Heading as="h4" className="mb-2">
|
||||
{t("cameraManagement.title")}
|
||||
</Heading>
|
||||
<p className="mb-6 max-w-5xl text-sm text-muted-foreground">
|
||||
{t("cameraManagement.description")}
|
||||
</p>
|
||||
|
||||
<div className="w-full max-w-5xl space-y-6">
|
||||
<div className="flex gap-2">
|
||||
|
||||
Reference in New Issue
Block a user