import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { Switch } from "@/components/ui/switch"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { useEffect, useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { useTranslation } from "react-i18next"; import { FrigateConfig } from "@/types/frigateConfig"; import { CameraNameLabel } from "../camera/FriendlyNameLabel"; import { isDesktop, isMobile } from "react-device-detect"; import { cn } from "@/lib/utils"; import { MobilePage, MobilePageContent, MobilePageDescription, MobilePageHeader, MobilePageTitle, } from "../mobile/MobilePage"; type CreateRoleOverlayProps = { show: boolean; config: FrigateConfig; onCreate: (role: string, cameras: string[]) => void; onCancel: () => void; }; export default function CreateRoleDialog({ show, config, onCreate, onCancel, }: CreateRoleOverlayProps) { const { t } = useTranslation(["views/settings"]); const [isLoading, setIsLoading] = useState(false); const cameras = Object.keys(config.cameras || {}); const existingRoles = Object.keys(config.auth?.roles || {}); const formSchema = z.object({ role: z .string() .min(1, t("roles.dialog.form.role.roleIsRequired")) .regex(/^[A-Za-z0-9._]+$/, { message: t("roles.dialog.form.role.roleOnlyInclude"), }) .refine((role) => !existingRoles.includes(role), { message: t("roles.dialog.form.role.roleExists"), }), cameras: z .array(z.string()) .min(1, t("roles.dialog.form.cameras.required")), }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { role: "", cameras: [], }, }); const onSubmit = async (values: z.infer) => { setIsLoading(true); try { await onCreate(values.role, values.cameras); form.reset(); } catch (error) { // Error handled in parent } finally { setIsLoading(false); } }; useEffect(() => { if (!show) { form.reset({ role: "", cameras: [], }); } }, [show, form]); const handleCancel = () => { form.reset({ role: "", cameras: [], }); onCancel(); }; const Overlay = isDesktop ? Dialog : MobilePage; const Content = isDesktop ? DialogContent : MobilePageContent; const Header = isDesktop ? DialogHeader : MobilePageHeader; const Description = isDesktop ? DialogDescription : MobilePageDescription; const Title = isDesktop ? DialogTitle : MobilePageTitle; return (
{t("roles.dialog.createRole.title")} {t("roles.dialog.createRole.desc")}
( {t("roles.dialog.form.role.title")} {t("roles.dialog.form.role.desc")} )} />
{t("roles.dialog.form.cameras.title")} {t("roles.dialog.form.cameras.desc")}
{cameras.map((camera) => ( { return (
{ return checked ? field.onChange([ ...(field.value as string[]), camera, ]) : field.onChange( (field.value as string[])?.filter( (value: string) => value !== camera, ) || [], ); }} />
); }} /> ))}
); }