import { useEffect, type ReactElement, useState } from "react"; import { useForm, type SubmitHandler } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Modal, FormField, FormSelect, PrimaryButton, SecondaryButton, PaginatedSelect, } from "@/components/shared"; import type { Department, CreateDepartmentRequest, UpdateDepartmentRequest, } from "@/types/department"; import { departmentService } from "@/services/department-service"; // Validation schema const departmentSchema = z.object({ name: z.string().min(1, "Name is required"), code: z.string().min(1, "Code is required"), description: z.string().optional(), is_active: z.boolean(), parent_id: z.string().nullable().optional(), sort_order: z.number().int().min(0).optional(), }); type DepartmentFormData = z.infer; const statusOptions = [ { value: "true", label: "Active" }, { value: "false", label: "Inactive" }, ]; interface NewDepartmentModalProps { isOpen: boolean; onClose: () => void; onSubmit: (data: CreateDepartmentRequest) => Promise; isLoading?: boolean; tenantId?: string | null; } export const NewDepartmentModal = ({ isOpen, onClose, onSubmit, isLoading = false, tenantId, }: NewDepartmentModalProps): ReactElement | null => { const { register, handleSubmit, setValue, watch, reset, clearErrors, formState: { errors }, } = useForm({ resolver: zodResolver(departmentSchema), defaultValues: { is_active: true, parent_id: null, sort_order: 0, }, }); const statusValue = watch("is_active"); const parentIdValue = watch("parent_id"); useEffect(() => { if (!isOpen) { reset(); clearErrors(); } }, [isOpen, reset, clearErrors]); const loadDepartments = async () => { const response = await departmentService.list(tenantId, { active_only: true, }); return { options: response.data.map((dept) => ({ value: dept.id, label: dept.name, })), pagination: { page: 1, limit: response.data.length, total: response.data.length, totalPages: 1, hasMore: false, }, }; }; const handleFormSubmit: SubmitHandler = async (data) => { await onSubmit(data as CreateDepartmentRequest); }; return ( Cancel {isLoading ? "Creating..." : "Create Department"} } >
setValue("parent_id", value || null)} onLoadOptions={loadDepartments} error={errors.parent_id?.message} />
setValue("is_active", value === "true")} error={errors.is_active?.message} />
); }; interface EditDepartmentModalProps { isOpen: boolean; onClose: () => void; onSubmit: (id: string, data: UpdateDepartmentRequest) => Promise; department: Department | null; isLoading?: boolean; tenantId?: string | null; } export const EditDepartmentModal = ({ isOpen, onClose, onSubmit, department, isLoading = false, tenantId, }: EditDepartmentModalProps): ReactElement | null => { const [initialParentOption, setInitialParentOption] = useState<{ value: string; label: string; } | null>(null); const { register, handleSubmit, setValue, watch, reset, clearErrors, formState: { errors }, } = useForm({ resolver: zodResolver(departmentSchema), }); const statusValue = watch("is_active"); const parentIdValue = watch("parent_id"); useEffect(() => { if (isOpen && department) { reset({ name: department.name, code: department.code, description: department.description || "", is_active: department.is_active, parent_id: department.parent_id, sort_order: department.sort_order || 0, }); if (department.parent_id && department.parent_name) { setInitialParentOption({ value: department.parent_id, label: department.parent_name, }); } else { setInitialParentOption(null); } } else if (!isOpen) { reset(); clearErrors(); setInitialParentOption(null); } }, [isOpen, department, reset, clearErrors]); const loadDepartments = async () => { const response = await departmentService.list(tenantId, { active_only: true, }); // Filter out current department to prevent self-referencing let options = response.data .filter((d) => d.id !== department?.id) .map((dept) => ({ value: dept.id, label: dept.name, })); // Ensure initial parent is in options if not already there if (initialParentOption) { if (!options.find((o) => o.value === initialParentOption.value)) { options = [initialParentOption, ...options]; } } return { options, pagination: { page: 1, limit: options.length, total: options.length, totalPages: 1, hasMore: false, }, }; }; const handleFormSubmit: SubmitHandler = async (data) => { if (department) { await onSubmit(department.id, data as UpdateDepartmentRequest); } }; return ( Cancel {isLoading ? "Updating..." : "Update Department"} } >
setValue("parent_id", value || null)} onLoadOptions={loadDepartments} error={errors.parent_id?.message} />
setValue("is_active", value === "true")} error={errors.is_active?.message} />
); }; interface ViewDepartmentModalProps { isOpen: boolean; onClose: () => void; department: Department | null; } export const ViewDepartmentModal = ({ isOpen, onClose, department, }: ViewDepartmentModalProps): ReactElement | null => { return ( Close} > {department && (

Basic Information

Name

{department.name}

Code

{department.code}

Status

{department.is_active ? "Active" : "Inactive"}

Hierarchy & Stats

Parent Department

{department.parent_name || "-"}

Level

{department.level}

Sort Order

{department.sort_order}

Sub-departments

{department.child_count || 0}

Users

{department.user_count || 0}

Description

{department.description || "No description provided."}

Created: {new Date(department.created_at).toLocaleString()} | Updated: {new Date(department.updated_at).toLocaleString()}
)}
); };