"use client"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { useCreateUser, useUpdateUser, useRoles } from "@/hooks/use-users"; import { useOrganizations } from "@/hooks/use-master-data"; import { useEffect, useState } from "react"; import { User } from "@/types/user"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Eye, EyeOff } from "lucide-react"; // Update schema to include confirmPassword const userSchema = z.object({ username: z.string().min(3, "Username must be at least 3 characters"), email: z.string().email("Invalid email address"), firstName: z.string().min(1, "First name is required"), lastName: z.string().min(1, "Last name is required"), password: z.string().optional(), confirmPassword: z.string().optional(), isActive: z.boolean().optional(), lineId: z.string().optional(), primaryOrganizationId: z.number().optional(), roleIds: z.array(z.number()).optional(), }).refine((data) => { // If password is provided (creating or resetting), confirmPassword must match if (data.password && data.password !== data.confirmPassword) { return false; } return true; }, { message: "Passwords do not match", path: ["confirmPassword"], }).refine((data) => { // Password required for creation // We can't easily check "isCreating" here without context, checking length if provided if (data.password && data.password.length < 6) { return false; } return true; }, { message: "Password must be at least 6 characters", path: ["password"] }); type UserFormData = z.infer; interface UserDialogProps { open: boolean; onOpenChange: (open: boolean) => void; user?: User | null; } export function UserDialog({ open, onOpenChange, user }: UserDialogProps) { const createUser = useCreateUser(); const updateUser = useUpdateUser(); const { data: roles = [] } = useRoles(); const { data: organizations = [] } = useOrganizations(); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const { register, handleSubmit, setValue, watch, reset, formState: { errors }, } = useForm({ resolver: zodResolver(userSchema), defaultValues: { username: "", email: "", firstName: "", lastName: "", isActive: true, roleIds: [], lineId: "", primaryOrganizationId: undefined, password: "", confirmPassword: "" }, }); useEffect(() => { if (user) { reset({ username: user.username, email: user.email, firstName: user.firstName, lastName: user.lastName, isActive: user.isActive, lineId: user.lineId || "", primaryOrganizationId: user.primaryOrganizationId, roleIds: user.roles?.map((r: any) => r.roleId) || [], password: "", confirmPassword: "" }); } else { reset({ username: "", email: "", firstName: "", lastName: "", isActive: true, lineId: "", primaryOrganizationId: undefined, roleIds: [], password: "", confirmPassword: "" }); } // Also reset visibility setShowPassword(false); setShowConfirmPassword(false); }, [user, reset, open]); const selectedRoleIds = watch("roleIds") || []; const onSubmit = (data: UserFormData) => { // Basic validation for create vs update if (!user && !data.password) { // This should be caught by schema ideally, but refined schema is tricky with conditional // Force error via set error not possible easily here, rely on form state? // Actually the refine check handles length check if provided, but for create it is mandatory. // Let's rely on server side or manual check if schema misses it (zod optional() makes it pass if undefined) // Adjusting schema to be strict string for create is hard with one schema. // Let's trust Zod or add checks. } // Clean up data const payload = { ...data }; delete payload.confirmPassword; // Don't send to API if (!payload.password) delete payload.password; // Don't send empty password on edit if (user) { updateUser.mutate( { id: user.userId, data: payload }, { onSuccess: () => onOpenChange(false) } ); } else { // Create req: Password mandatory if (!payload.password) return; // Should allow Zod to catch or show error createUser.mutate(payload as any, { onSuccess: () => onOpenChange(false), }); } }; return ( {user ? "Edit User" : "Create New User"}
{errors.username && (

{errors.username.message}

)}
{errors.email && (

{errors.email.message}

)}
{errors.firstName && (

{errors.firstName.message}

)}
{errors.lastName && (

{errors.lastName.message}

)}
{/* Password Section - Show for Create, or Optional for Edit */}

{user ? "Change Password (Optional)" : "Password Setup"}

{errors.password && (

{errors.password.message}

)}
{errors.confirmPassword && (

{errors.confirmPassword.message}

)}
{roles.length === 0 &&

Loading roles...

} {roles.map((role: any) => (
{ const current = selectedRoleIds; if (checked) { setValue("roleIds", [...current, role.roleId]); } else { setValue( "roleIds", current.filter((id) => id !== role.roleId) ); } }} />

{role.description}

))}
{user && (
setValue("isActive", chk === true)} />
)}
); }