"use client" import React from "react" import { z } from "zod" import { AlertTriangle, Plus } from "lucide-react" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/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, RhfSelectField, RhfAsyncSelectField, } from "@/shared/components/form" import { useAuthApi } from "@/shared/useApi" import { RateType, DEPARTMENT_ROUTES, INVENTORY_ROUTES } from "@garage/api" import { toast } from "sonner" // ── Schema ── const schema = z.object({ rate_type: z.string().optional(), labor_rate: z.object({ value: z.string(), label: z.string() }).nullable().optional(), quantity: z.coerce.number().min(1, "Quantity is required"), rate: z.string().min(1, "Rate is required"), working_hours: z.string().optional(), labor_hours: z.string().optional(), tax: z.string().optional(), chart_of_account: z.string().optional(), department: z.object({ value: z.string(), label: z.string() }).nullable().optional(), description: z.string().optional(), }) type FormValues = z.infer const DEFAULT_VALUES: FormValues = { rate_type: "flat_rate", labor_rate: null, quantity: 1, rate: "", working_hours: "", labor_hours: "", tax: "", chart_of_account: "", department: null, description: "", } const RATE_TYPE_OPTIONS = RateType.map((v) => ({ value: v, label: v === "flat_rate" ? "Flat Rate" : "Hourly", })) const STORE_OBJECT = { getOptionValue: (o: any) => o, getOptionLabel: (o: any) => o.label } export type EstimateServiceConfigFormProps = { /** The selected service row from the picker dialog */ service: { id: number; labor_name?: string; selling_price?: string | number } estimateId: string onSuccess?: () => void onCancel?: () => void } export function EstimateServiceConfigForm({ service, estimateId, onSuccess, onCancel, }: EstimateServiceConfigFormProps) { const api = useAuthApi() const form = useForm({ resolver: zodResolver(schema) as any, defaultValues: { ...DEFAULT_VALUES, rate: service.selling_price != null ? String(service.selling_price) : "", }, }) const [error, setError] = React.useState(null) const [isPending, setIsPending] = React.useState(false) async function handleSubmit(values: FormValues) { setError(null) setIsPending(true) try { const promise = api.estimates.addService(estimateId, { service_id: service.id, rate_type: values.rate_type || undefined, labor_rate_id: values.labor_rate ? Number(values.labor_rate.value) : undefined, quantity: values.quantity, rate: values.rate || undefined, working_hours: values.working_hours || undefined, labor_hours: values.labor_hours || undefined, tax: values.tax || undefined, chart_of_account: values.chart_of_account || undefined, department_id: values.department ? Number(values.department.value) : undefined, description: values.description || undefined, }) toast.promise(promise, { loading: "Adding service...", success: "Service added successfully", error: "Failed to add service", }) await promise form.reset() onSuccess?.() } catch (err: any) { setError(err?.message ?? "An unexpected error occurred") } finally { setIsPending(false) } } return ( {error && ( Failed to add service {error} )}
{service.labor_name ?? `Service #${service.id}`}
api.inventory.listLaborRates()} mapOption={(item: any) => ({ value: String(item.id), label: item.title ?? String(item.id), })} {...STORE_OBJECT} />
api.departments.list()} mapOption={(item: any) => ({ value: String(item.id), label: item.name ?? String(item.id), })} {...STORE_OBJECT} />
{onCancel && ( )}
) }