import { useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import useSWR from "swr"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { FrigateConfig } from "@/types/frigateConfig"; import ImagePicker from "@/components/overlay/ImagePicker"; import { Trigger, TriggerAction, TriggerType } from "@/types/trigger"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "../ui/textarea"; import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name"; type CreateTriggerDialogProps = { show: boolean; trigger: Trigger | null; selectedCamera: string; isLoading: boolean; onCreate: ( enabled: boolean, name: string, type: TriggerType, data: string, threshold: number, actions: TriggerAction[], ) => void; onEdit: (trigger: Trigger) => void; onCancel: () => void; }; export default function CreateTriggerDialog({ show, trigger, selectedCamera, isLoading, onCreate, onEdit, onCancel, }: CreateTriggerDialogProps) { const { t } = useTranslation("views/settings"); const { data: config } = useSWR("config"); const existingTriggerNames = useMemo(() => { if ( !config || !selectedCamera || !config.cameras[selectedCamera]?.semantic_search?.triggers ) { return []; } return Object.keys(config.cameras[selectedCamera].semantic_search.triggers); }, [config, selectedCamera]); const formSchema = z.object({ enabled: z.boolean(), name: z .string() .min(2, t("triggers.dialog.form.name.error.minLength")) .regex( /^[a-zA-Z0-9_-]+$/, t("triggers.dialog.form.name.error.invalidCharacters"), ) .refine( (value) => !existingTriggerNames.includes(value) || value === trigger?.name, t("triggers.dialog.form.name.error.alreadyExists"), ), type: z.enum(["thumbnail", "description"]), data: z.string().min(1, t("triggers.dialog.form.content.error.required")), threshold: z .number() .min(0, t("triggers.dialog.form.threshold.error.min")) .max(1, t("triggers.dialog.form.threshold.error.max")), actions: z.array(z.enum(["notification"])), }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { enabled: trigger?.enabled ?? true, name: trigger?.name ?? "", type: trigger?.type ?? "description", data: trigger?.data ?? "", threshold: trigger?.threshold ?? 0.5, actions: trigger?.actions ?? [], }, }); const onSubmit = async (values: z.infer) => { if (trigger) { onEdit({ ...values }); } else { onCreate( values.enabled, values.name, values.type, values.data, values.threshold, values.actions, ); } }; useEffect(() => { if (!show) { form.reset({ enabled: true, name: "", type: "description", data: "", threshold: 0.5, actions: [], }); } else if (trigger) { form.reset( { enabled: trigger.enabled, name: trigger.name, type: trigger.type, data: trigger.data, threshold: trigger.threshold, actions: trigger.actions, }, { keepDirty: false, keepTouched: false }, // Reset validation state ); // Trigger validation to ensure isValid updates // form.trigger(); } }, [show, trigger, form]); const handleCancel = () => { form.reset(); onCancel(); }; const cameraName = useCameraFriendlyName(selectedCamera); return ( {t( trigger ? "triggers.dialog.editTrigger.title" : "triggers.dialog.createTrigger.title", )} {t( trigger ? "triggers.dialog.editTrigger.desc" : "triggers.dialog.createTrigger.desc", { camera: cameraName, }, )}
( {t("triggers.dialog.form.name.title")} )} /> (
{t("enabled", { ns: "common" })}
{t("triggers.dialog.form.enabled.description")}
)} /> ( {t("triggers.dialog.form.type.title")} )} /> ( {t("triggers.dialog.form.content.title")} {form.watch("type") === "thumbnail" ? ( <> {t("triggers.dialog.form.content.imageDesc")} ) : ( <>