"use client" import React from "react" import { AlertTriangle, Plus, Save } from "lucide-react" import { z } from "zod" import { Button } from "@/shared/components/ui/button" import { Alert, AlertTitle } from "@/shared/components/ui/alert" import { FieldGroup } from "@/shared/components/ui/field" import { Rhform, RhfTextField, RhfTextareaField, RhfAsyncSelectField, } from "@/shared/components/form" import { DepartmentInlineForm } from "@/modules/services/inline-forms/department-inline-form" import { toast } from "sonner" import { useAuthApi } from "@/shared/useApi" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" // ── Schema ── const jobCardPartFormSchema = z.object({ part: z .object({ value: z.string(), label: z.string() }) .nullable(), department: z .object({ value: z.string(), label: z.string() }) .nullable() .optional(), quantity: z.coerce.number().min(1, "Quantity is required"), rate: z.coerce.number().min(0, "Rate is required"), tax: z.string().optional(), description: z.string().optional(), }) type JobCardPartFormValues = z.infer // ── Props ── export type JobCardPartFormProps = { jobCardId: string jobCardPartId?: number | null initialData?: unknown onSuccess?: () => void onCancel?: () => void } const DEFAULT_VALUES: JobCardPartFormValues = { part: null, department: null, quantity: 1, rate: 0, tax: "", description: "", } const STORE_OBJECT = { getOptionValue: (o: any) => o, getOptionLabel: (o: any) => o.label } function mapToFormValues(data: unknown): JobCardPartFormValues { const d = (data as any) ?? {} return { part: d.part ? { value: String(d.part.id), label: d.part.title ?? String(d.part.id) } : null, department: d.department ? { value: String(d.department.id), label: d.department.name ?? String(d.department.id) } : null, quantity: d.quantity ?? 1, rate: d.rate != null ? Number(d.rate) : 0, tax: d.tax ?? "", description: d.description ?? "", } } // ── Component ── export function JobCardPartForm({ jobCardId, jobCardPartId, initialData, onSuccess, onCancel, }: JobCardPartFormProps) { const api = useAuthApi() const isEditing = !!jobCardPartId const form = useForm({ resolver: zodResolver(jobCardPartFormSchema) as any, defaultValues: initialData ? mapToFormValues(initialData) : DEFAULT_VALUES, }) const [error, setError] = React.useState(null) const [isPending, setIsPending] = React.useState(false) async function handleSubmit(values: JobCardPartFormValues) { setError(null) setIsPending(true) try { if (isEditing && jobCardPartId) { await toast.promise( api.jobCards.updatePart(jobCardId, { job_card_part_id: jobCardPartId, quantity: values.quantity, rate: values.rate, description: values.description || undefined, }), { loading: "Updating part...", success: "Part updated successfully", error: "Failed to update part", } ) } else { await toast.promise( api.jobCards.addPart(jobCardId, { part_id: values.part ? Number(values.part.value) : undefined, department_id: values.department ? Number(values.department.value) : undefined, quantity: values.quantity, rate: values.rate, tax: values.tax || undefined, description: values.description || undefined, }), { loading: "Adding part...", success: "Part added successfully", error: "Failed to add part", } ) } form.reset() onSuccess?.() } catch (err: any) { setError(err?.message ?? "An unexpected error occurred") } finally { setIsPending(false) } } return ( {error && ( {isEditing ? "Failed to update part" : "Failed to add part"} {error} )} {!isEditing && ( api.parts.list()} mapOption={(item: any) => ({ value: String(item.id), label: item.title ?? String(item.id), })} {...STORE_OBJECT} /> )}
{!isEditing && (
api.departments.list()} mapOption={(item: any) => ({ value: String(item.id), label: item.name ?? String(item.id), })} createForm={(props) => } createLabel="Department" {...STORE_OBJECT} />
)}
{onCancel && ( )}
) }