"use client"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { FileUpload } from "@/components/common/file-upload"; import { useRouter } from "next/navigation"; import { Loader2 } from "lucide-react"; import { useCreateCorrespondence, useUpdateCorrespondence } from "@/hooks/use-correspondence"; import { Organization } from "@/types/organization"; import { useOrganizations, useProjects, useCorrespondenceTypes, useDisciplines } from "@/hooks/use-master-data"; import { CreateCorrespondenceDto } from "@/types/dto/correspondence/create-correspondence.dto"; import { useState, useEffect } from "react"; import { correspondenceService } from "@/lib/services/correspondence.service"; // Updated Zod Schema with all required fields const correspondenceSchema = z.object({ projectId: z.number().min(1, "Please select a Project"), documentTypeId: z.number().min(1, "Please select a Document Type"), disciplineId: z.number().optional(), subject: z.string().min(5, "Subject must be at least 5 characters"), description: z.string().optional(), body: z.string().optional(), remarks: z.string().optional(), dueDate: z.string().optional(), // ISO Date string fromOrganizationId: z.number().min(1, "Please select From Organization"), toOrganizationId: z.number().min(1, "Please select To Organization"), importance: z.enum(["NORMAL", "HIGH", "URGENT"]), attachments: z.array(z.instanceof(File)).optional(), }); type FormData = z.infer; export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?: number }) { const router = useRouter(); const createMutation = useCreateCorrespondence(); const updateMutation = useUpdateCorrespondence(); // Fetch master data for dropdowns const { data: projects, isLoading: isLoadingProjects } = useProjects(); const { data: organizations, isLoading: isLoadingOrgs } = useOrganizations(); const { data: correspondenceTypes, isLoading: isLoadingTypes } = useCorrespondenceTypes(); const { data: disciplines, isLoading: isLoadingDisciplines } = useDisciplines(); // Extract initial values if editing const currentRev = initialData?.revisions?.find((r: any) => r.isCurrent) || initialData?.revisions?.[0]; const defaultValues: Partial = { projectId: initialData?.projectId || undefined, documentTypeId: initialData?.correspondenceTypeId || undefined, disciplineId: initialData?.disciplineId || undefined, subject: currentRev?.subject || currentRev?.title || "", description: currentRev?.description || "", body: currentRev?.body || "", remarks: currentRev?.remarks || "", dueDate: currentRev?.dueDate ? new Date(currentRev.dueDate).toISOString().split('T')[0] : undefined, fromOrganizationId: initialData?.originatorId || undefined, // Map initial recipient (TO) - Simplified for now toOrganizationId: initialData?.recipients?.find((r: any) => r.recipientType === 'TO')?.recipientOrganizationId || undefined, importance: currentRev?.details?.importance || "NORMAL", }; const { register, handleSubmit, setValue, watch, formState: { errors }, } = useForm({ resolver: zodResolver(correspondenceSchema), defaultValues: defaultValues as any, }); // Watch for controlled inputs const projectId = watch("projectId"); const documentTypeId = watch("documentTypeId"); const disciplineId = watch("disciplineId"); const fromOrgId = watch("fromOrganizationId"); const toOrgId = watch("toOrganizationId"); const onSubmit = (data: FormData) => { const payload: CreateCorrespondenceDto = { projectId: data.projectId, typeId: data.documentTypeId, disciplineId: data.disciplineId, subject: data.subject, description: data.description, body: data.body, remarks: data.remarks, dueDate: data.dueDate ? new Date(data.dueDate).toISOString() : undefined, originatorId: data.fromOrganizationId, recipients: [ { organizationId: data.toOrganizationId, type: 'TO' } ], details: { importance: data.importance }, }; if (id && initialData) { // UPDATE Mode updateMutation.mutate({ id, data: payload }, { onSuccess: () => router.push(`/correspondences/${id}`) }); } else { // CREATE Mode createMutation.mutate(payload, { onSuccess: () => router.push("/correspondences"), }); } }; const isPending = createMutation.isPending || updateMutation.isPending; // -- Preview Logic -- const [preview, setPreview] = useState<{ number: string; isDefaultTemplate: boolean } | null>(null); useEffect(() => { if (!projectId || !documentTypeId || !fromOrgId || !toOrgId) { setPreview(null); return; } const fetchPreview = async () => { try { const res = await correspondenceService.previewNumber({ projectId, typeId: documentTypeId, disciplineId, originatorId: fromOrgId, // Map recipients structure matching backend expectation recipients: [{ organizationId: toOrgId, type: 'TO' }], // Add date just to be safe, though service uses 'now' dueDate: new Date().toISOString(), // [Fix] Subject is required by DTO validation, send placeholder if empty subject: watch('subject') || "Preview Subject" }); setPreview(res); } catch (err) { setPreview(null); } }; const timer = setTimeout(fetchPreview, 500); return () => clearTimeout(timer); }, [projectId, documentTypeId, disciplineId, fromOrgId, toOrgId]); return (
{/* Existing Document Number (Read Only) */} {initialData?.correspondenceNumber && (
{preview && preview.number !== initialData.correspondenceNumber && ( Start Change Detected )}
)} {/* Preview Section */} {preview && (

{initialData?.correspondenceNumber ? "New Document Number (Preview)" : "Document Number Preview"} {preview.number !== initialData?.correspondenceNumber && initialData?.correspondenceNumber && ( Will Update )}

{preview.number} {preview.isDefaultTemplate && ( Default Template )}
{preview.number !== initialData?.correspondenceNumber && initialData?.correspondenceNumber && (

* The document number will be regenerated because critical fields were changed.

)}
)} {/* Document Metadata Section */}
{/* Project Dropdown */}
{errors.projectId && (

{errors.projectId.message}

)}
{/* Document Type Dropdown */}
{errors.documentTypeId && (

{errors.documentTypeId.message}

)}
{/* Discipline Dropdown (Optional) */}
{/* Subject */}
{errors.subject && (

{errors.subject.message}

)}
{/* Body */}