"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 { SERVICE_ROUTES, ESTIMATE_ROUTES } from "@garage/api" import type { ServicesClient } from "@garage/api" import { serviceColumns } from "@/modules/services/services-columns" import { EstimateServiceConfigForm } from "./estimate-service-config-form" import { toast } from "sonner" type ServiceLine = { id: number service_id?: number labor_name?: string title?: string quantity: number rate: number | string description?: string service:any } type SelectedService = { id: number labor_name?: string selling_price?: string | number } export function EstimateServicesSection({ estimateId }: { estimateId: string }) { const api = useAuthApi() const queryClient = useQueryClient() const [pickerOpen, setPickerOpen] = useState(false) const [configService, setConfigService] = useState(null) const queryKey = [ESTIMATE_ROUTES.SERVICES, estimateId] const { data, isLoading } = useQuery({ queryKey, queryFn: async () => { const res = await api.estimates.listServices(estimateId) return ((res as any)?.data ?? []) as ServiceLine[] }, }) const items: ServiceLine[] = data ?? [] const invalidate = () => queryClient.invalidateQueries({ queryKey }) const updateMutation = useMutation({ mutationFn: ({ lineId, payload }: { lineId: string; payload: { rate?: string; quantity?: number } }) => api.estimates.updateService(estimateId, lineId, payload), onSuccess: () => { toast.success("Service updated") invalidate() }, onError: () => toast.error("Failed to update service"), }) const removeMutation = useMutation({ mutationFn: (lineId: string) => api.estimates.removeService(estimateId, lineId), onSuccess: invalidate, onError: () => toast.error("Failed to remove service"), }) const handlePickerConfirm = (rows: any[]) => { const row = rows[0] if (!row) return setConfigService({ id: row.id, labor_name: row.labor_name ?? row.name, selling_price: row.selling_price, }) } const getDisplayName = (item: ServiceLine) => item.service.labor_name ?? item.service.title ?? `Service #${item.service_id ?? item.id}` return ( Services {isLoading &&

Loading...

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

No services added yet.

)} {items.length > 0 && ( Service 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" /> updateMutation.mutate({ lineId: String(item.id), payload: { rate: e.target.value }, }) } className="h-8 w-24" /> {item.description || "—"} ))}
)}
{/* Step 1: Pick a service (single-select) */} title="Select Service" open={pickerOpen} onOpenChange={setPickerOpen} selectionMode="single" onConfirm={handlePickerConfirm} crudProps={{ routeKey: SERVICE_ROUTES.INDEX, getClient: (api) => api.services, columns: [ serviceColumns.name, serviceColumns.description, serviceColumns.sellingPrice, ], }} /> {/* Step 2: Configure and add the selected service */} { if (!v) setConfigService(null) }}> Configure Service {configService && ( { setConfigService(null) invalidate() }} onCancel={() => setConfigService(null)} /> )}
) }