From bec64dbeb35f26a3f4f4e187a4ad7bbaa5266853 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 20 Oct 2025 20:30:27 -0600 Subject: [PATCH] Add basic classification model wizard --- .../locales/en/views/classificationModel.json | 6 +- .../ClassificationModelWizardDialog.tsx | 66 +++++++++++++++++++ web/src/types/frigateConfig.ts | 4 +- .../classification/ModelSelectionView.tsx | 16 ++++- 4 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 web/src/components/classification/ClassificationModelWizardDialog.tsx diff --git a/web/public/locales/en/views/classificationModel.json b/web/public/locales/en/views/classificationModel.json index f28cb3753..dcfc5a1b2 100644 --- a/web/public/locales/en/views/classificationModel.json +++ b/web/public/locales/en/views/classificationModel.json @@ -49,5 +49,9 @@ "new": "Create New Class" }, "categorizeImageAs": "Classify Image As:", - "categorizeImage": "Classify Image" + "categorizeImage": "Classify Image", + "wizard": { + "title": "Create New Classification", + "description": "Create a new state or object classification model." + } } diff --git a/web/src/components/classification/ClassificationModelWizardDialog.tsx b/web/src/components/classification/ClassificationModelWizardDialog.tsx new file mode 100644 index 000000000..c6c06baa2 --- /dev/null +++ b/web/src/components/classification/ClassificationModelWizardDialog.tsx @@ -0,0 +1,66 @@ +import { useTranslation } from "react-i18next"; +import StepIndicator from "../indicators/StepIndicator"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "../ui/dialog"; +import { useState } from "react"; + +const STEPS = [ + "classificationWizard.steps.nameAndDefine", + "classificationWizard.steps.stateArea", + "classificationWizard.steps.chooseExamples", + "classificationWizard.steps.train", +]; + +type ClassificationModelWizardDialogProps = { + open: boolean; + onClose: () => void; +}; +export default function ClassificationModelWizardDialog({ + open, + onClose, +}: ClassificationModelWizardDialogProps) { + const { t } = useTranslation(["views/classificationModel"]); + + // step management + const [currentStep, setCurrentStep] = useState(0); + + return ( + { + if (!open) { + onClose; + } + }} + > + { + e.preventDefault(); + }} + > + + + {t("wizard.title")} + {currentStep === 0 && ( + {t("wizard.description")} + )} + + +
+
+
+
+
+ ); +} diff --git a/web/src/types/frigateConfig.ts b/web/src/types/frigateConfig.ts index f82ca9838..ffe4cc14d 100644 --- a/web/src/types/frigateConfig.ts +++ b/web/src/types/frigateConfig.ts @@ -304,10 +304,10 @@ export type CustomClassificationModelConfig = { enabled: boolean; name: string; threshold: number; - object_config: null | { + object_config?: { objects: string[]; }; - state_config: null | { + state_config?: { cameras: { [cameraName: string]: { crop: [number, number, number, number]; diff --git a/web/src/views/classification/ModelSelectionView.tsx b/web/src/views/classification/ModelSelectionView.tsx index c6366a139..e4db0ab5b 100644 --- a/web/src/views/classification/ModelSelectionView.tsx +++ b/web/src/views/classification/ModelSelectionView.tsx @@ -1,4 +1,5 @@ import { baseUrl } from "@/api/baseUrl"; +import ClassificationModelWizardDialog from "@/components/classification/ClassificationModelWizardDialog"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { Button } from "@/components/ui/button"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; @@ -52,6 +53,10 @@ export default function ModelSelectionView({ }); }, [config, pageToggle]); + // new model wizard + + const [newModel, setNewModel] = useState(false); + if (!config) { return ; } @@ -62,6 +67,11 @@ export default function ModelSelectionView({ return (
+ setNewModel(false)} + /> +
-