import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import useOptimisticState from "@/hooks/use-optimistic-state"; import { CustomClassificationModelConfig } from "@/types/frigateConfig"; import { TooltipPortal } from "@radix-ui/react-tooltip"; import axios from "axios"; import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import { LuPencil, LuTrash2 } from "react-icons/lu"; import useSWR from "swr"; type ModelTrainingViewProps = { model: CustomClassificationModelConfig; }; export default function ModelTrainingView({ model }: ModelTrainingViewProps) { const [page, setPage] = useState("train"); const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100); // dataset const { data: trainImages } = useSWR(`classification/${model.name}/train`); const { data: dataset } = useSWR(`classification/${model.name}/dataset`); // actions const trainModel = useCallback(() => { axios.post(`classification/${model.name}/train`); }, [model]); return (
{}} onRename={() => {}} />
); } type LibrarySelectorProps = { pageToggle: string | undefined; dataset: { [id: string]: string[] }; trainImages: string[]; setPageToggle: (toggle: string) => void; onDelete: (name: string, ids: string[], isName: boolean) => void; onRename: (old_name: string, new_name: string) => void; }; function LibrarySelector({ pageToggle, dataset, trainImages, setPageToggle, onDelete, onRename, }: LibrarySelectorProps) { const { t } = useTranslation(["views/faceLibrary"]); const [confirmDelete, setConfirmDelete] = useState(null); const [renameFace, setRenameFace] = useState(null); const handleDeleteFace = useCallback( (name: string) => { // Get all image IDs for this face const imageIds = dataset?.[name] || []; onDelete(name, imageIds, true); setPageToggle("train"); }, [dataset, onDelete, setPageToggle], ); const handleSetOpen = useCallback( (open: boolean) => { setRenameFace(open ? renameFace : null); }, [renameFace], ); return ( <> !open && setConfirmDelete(null)} > {t("deleteFaceLibrary.title")} {t("deleteFaceLibrary.desc", { name: confirmDelete })}
{ onRename(renameFace!, newName); setRenameFace(null); }} defaultValue={renameFace || ""} regexPattern={/^[\p{L}\p{N}\s'_-]{1,50}$/u} regexErrorMessage={t("description.invalidName")} /> setPageToggle("train")} >
{t("train.title")}
({trainImages.length})
{trainImages.length > 0 && Object.keys(dataset).length > 0 && ( <>
{t("collections")}
)} {Object.keys(dataset).map((id) => (
setPageToggle(id)} > {id} ({dataset?.[id].length})
{t("button.renameFace")} {t("button.deleteFace")}
))}
); }