import { useEffect, useCallback, 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 { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Trigger, TriggerAction } from "@/types/trigger"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; export type Step3FormData = { threshold: number; actions: TriggerAction[]; }; type Step3ThresholdAndActionsProps = { initialData?: Step3FormData; trigger?: Trigger | null; camera: string; onNext: (data: Step3FormData) => void; onBack: () => void; isLoading?: boolean; }; export default function Step3ThresholdAndActions({ initialData, trigger, camera, onNext, onBack, isLoading = false, }: Step3ThresholdAndActionsProps) { const { t } = useTranslation("views/settings"); const { data: config } = useSWR("config"); const availableActions = useMemo(() => { if (!config) return []; if (config.cameras[camera].notifications.enabled_in_config) { return ["notification", "sub_label", "attribute"]; } return ["sub_label", "attribute"]; }, [config, camera]); const formSchema = z.object({ 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", "sub_label", "attribute"])), }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { threshold: initialData?.threshold ?? trigger?.threshold ?? 0.5, actions: initialData?.actions ?? (trigger?.actions as TriggerAction[]) ?? [], }, }); const onSubmit = useCallback( (values: z.infer) => { onNext({ threshold: values.threshold, actions: values.actions, }); }, [onNext], ); const handleSave = useCallback(() => { const formData = form.getValues(); // Basic validation if (formData.threshold < 0 || formData.threshold > 1) { return; } onNext(formData); }, [form, onNext]); useEffect(() => { if (initialData) { form.reset(initialData); } else if (trigger) { form.reset({ threshold: trigger.threshold, actions: trigger.actions, }); } }, [initialData, trigger, form]); return (
( {t("triggers.dialog.form.threshold.title")} { const value = parseFloat(e.target.value); field.onChange(isNaN(value) ? 0 : value); }} /> {t("triggers.dialog.form.threshold.desc")} )} /> ( {t("triggers.dialog.form.actions.title")}
{availableActions.map((action) => (
{ const currentActions = form.getValues("actions"); if (checked) { form.setValue("actions", [ ...currentActions, action as TriggerAction, ]); } else { form.setValue( "actions", currentActions.filter((a) => a !== action), ); } }} /> {t(`triggers.actions.${action}`)}
))}
{t("triggers.dialog.form.actions.desc")}
)} />
); }