"use client" import { useState } from "react" import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" import { Plus, Trash2 } from "lucide-react" import { Button } from "@/shared/components/ui/button" import { Input } from "@/shared/components/ui/input" import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/shared/components/ui/table" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/shared/components/ui/dialog" import { ScrollArea } from "@/shared/components/ui/scroll-area" import { ResourceSelectorDialog } from "@/shared/components/resource-selector/resource-selector-dialog" import { useAuthApi } from "@/shared/useApi" import { PARTS_ROUTES, ESTIMATE_ROUTES } from "@garage/api" import type { PartsClient } from "@garage/api" import { partColumns } from "@/modules/parts/parts-columns" import { EstimatePartConfigForm } from "./estimate-part-config-form" import { toast } from "sonner" type PartLine = { id: number part_id?: number title?: string quantity: number rate: number | string description?: string part:any } type SelectedPart = { id: number title?: string purchase_price?: string | number } export function EstimatePartsSection({ estimateId }: { estimateId: string }) { const api = useAuthApi() const queryClient = useQueryClient() const [pickerOpen, setPickerOpen] = useState(false) const [configPart, setConfigPart] = useState(null) const queryKey = [ESTIMATE_ROUTES.PARTS, estimateId] const { data, isLoading } = useQuery({ queryKey, queryFn: async () => { const res = await api.estimates.listParts(estimateId) return ((res as any)?.data ?? []) as PartLine[] }, }) const items: PartLine[] = data ?? [] const invalidate = () => queryClient.invalidateQueries({ queryKey }) const updateMutation = useMutation({ mutationFn: ({ lineId, payload }: { lineId: string; payload: { quantity?: number } }) => api.estimates.updatePart(estimateId, lineId, payload), onSuccess: () => { toast.success("Part updated") invalidate() }, onError: () => toast.error("Failed to update part"), }) const removeMutation = useMutation({ mutationFn: (lineId: string) => api.estimates.removePart(estimateId, lineId), onSuccess: invalidate, onError: () => toast.error("Failed to remove part"), }) const handlePickerConfirm = (rows: any[]) => { const row = rows[0] if (!row) return setConfigPart({ id: row.id, title: row.title ?? row.name, purchase_price: row.purchase_price, }) } const getDisplayName = (item: PartLine) => item.part.title ?? `Part #${item.part_id ?? item.id}` return ( Parts {isLoading &&

Loading...

} {!isLoading && items.length === 0 && (

No parts added yet.

)} {items.length > 0 && ( Part Qty Rate Description {items.map((item) => ( {getDisplayName(item)} updateMutation.mutate({ lineId: String(item.id), payload: { quantity: Number(e.target.value) || 1 }, }) } className="h-8 w-20" /> {Number(item.rate).toFixed(2)} {item.description || "—"} ))}
)}
{/* Step 1: Pick a part (single-select) */} title="Select Part" open={pickerOpen} onOpenChange={setPickerOpen} selectionMode="single" onConfirm={handlePickerConfirm} crudProps={{ routeKey: PARTS_ROUTES.INDEX, getClient: (api) => api.parts, columns: [ partColumns.title, partColumns.partNumber, partColumns.manufacturer, partColumns.purchasePrice, partColumns.stock, ], }} /> {/* Step 2: Configure and add the selected part */} { if (!v) setConfigPart(null) }}> Configure Part {configPart && ( { setConfigPart(null) invalidate() }} onCancel={() => setConfigPart(null)} /> )}
) }