import { Button } from "../ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "../ui/form"; import { Input } from "../ui/input"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import ActivityIndicator from "../indicators/activity-indicator"; import { useEffect, useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select"; import { Shield, User } from "lucide-react"; import { LuCheck, LuX } from "react-icons/lu"; import { useTranslation } from "react-i18next"; import { isDesktop, isMobile } from "react-device-detect"; import { cn } from "@/lib/utils"; import { MobilePage, MobilePageContent, MobilePageDescription, MobilePageHeader, MobilePageTitle, } from "../mobile/MobilePage"; type CreateUserOverlayProps = { show: boolean; onCreate: (user: string, password: string, role: string) => void; onCancel: () => void; }; export default function CreateUserDialog({ show, onCreate, onCancel, }: CreateUserOverlayProps) { const { t } = useTranslation(["views/settings"]); const [isLoading, setIsLoading] = useState(false); const formSchema = z .object({ user: z .string() .min(1, t("users.dialog.form.usernameIsRequired")) .regex(/^[A-Za-z0-9._]+$/, { message: t("users.dialog.createUser.usernameOnlyInclude"), }), password: z.string().min(1, t("users.dialog.form.passwordIsRequired")), confirmPassword: z .string() .min(1, t("users.dialog.createUser.confirmPassword")), role: z.enum(["admin", "viewer"]), }) .refine((data) => data.password === data.confirmPassword, { message: t("users.dialog.form.password.notMatch"), path: ["confirmPassword"], }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { user: "", password: "", confirmPassword: "", role: "viewer", }, }); const onSubmit = async (values: z.infer) => { setIsLoading(true); await onCreate(values.user, values.password, values.role); form.reset(); setIsLoading(false); }; // Check if passwords match for real-time feedback const password = form.watch("password"); const confirmPassword = form.watch("confirmPassword"); const passwordsMatch = password === confirmPassword; const showMatchIndicator = password && confirmPassword; useEffect(() => { if (!show) { form.reset({ user: "", password: "", role: "viewer", }); } }, [show, form]); const handleCancel = () => { form.reset({ user: "", password: "", role: "viewer", }); 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("users.dialog.createUser.title")} {t("users.dialog.createUser.desc")}
( {t("users.dialog.form.user.title")} {t("users.dialog.form.user.desc")} )} /> ( {t("users.dialog.form.password.title")} )} /> ( {t("users.dialog.form.password.confirm.title")} {showMatchIndicator && (
{passwordsMatch ? ( <> {t("users.dialog.form.password.match")} ) : ( <> {t("users.dialog.form.password.notMatch")} )}
)}
)} /> ( {t("role.title", { ns: "common" })} {t("role.desc", { ns: "common" })} )} />
); }