"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 { ESTIMATE_ROUTES, EXPENSE_ITEM_ROUTES } from "@garage/api" import type { ExpenseItemsClient } from "@garage/api" import { expenseItemColumns } from "@/modules/expense-items/expense-items-columns" import { EstimateExpenseItemConfigForm } from "./estimate-expense-item-config-form" import { toast } from "sonner" type ExpenseLine = { id: number expense_item_id?: number item_name?: string title?: string quantity: number rate: number | string description?: string } type SelectedExpenseItem = { id: number name?: string purchase_price?: string | number } export function EstimateExpenseItemsSection({ estimateId }: { estimateId: string }) { const api = useAuthApi() const queryClient = useQueryClient() const [pickerOpen, setPickerOpen] = useState(false) const [configExpenseItem, setConfigExpenseItem] = useState(null) const queryKey = [ESTIMATE_ROUTES.EXPENSE_ITEMS, estimateId] const { data, isLoading } = useQuery({ queryKey, queryFn: async () => { const res = await api.estimates.listExpenseItems(estimateId) return ((res as any)?.data ?? []) as ExpenseLine[] }, }) const items: ExpenseLine[] = data ?? [] const invalidate = () => queryClient.invalidateQueries({ queryKey }) const updateMutation = useMutation({ mutationFn: ({ lineId, payload }: { lineId: string; payload: { quantity?: number } }) => api.estimates.updateExpenseItem(estimateId, lineId, payload), onSuccess: () => { toast.success("Expense item updated") invalidate() }, onError: () => toast.error("Failed to update expense item"), }) const removeMutation = useMutation({ mutationFn: (lineId: string) => api.estimates.removeExpenseItem(estimateId, lineId), onSuccess: invalidate, onError: () => toast.error("Failed to remove expense item"), }) const handlePickerConfirm = (rows: any[]) => { const row = rows[0] if (!row) return setConfigExpenseItem({ id: row.id, name: row.name ?? row.title, purchase_price: row.purchase_price, }) } const getDisplayName = (item: ExpenseLine) => item.item_name ?? item.title ?? `Item #${item.expense_item_id ?? item.id}` return ( Expense Items {isLoading &&

Loading...

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

No expense items added yet.

)} {items.length > 0 && ( Item 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 an expense item (single-select) */} title="Select Expense Item" open={pickerOpen} onOpenChange={setPickerOpen} selectionMode="single" onConfirm={handlePickerConfirm} crudProps={{ routeKey: EXPENSE_ITEM_ROUTES.INDEX, getClient: (api) => api.expenseItems, columns: [ expenseItemColumns.name, expenseItemColumns.purchasePrice, expenseItemColumns.chartOfAccount, ], }} /> {/* Step 2: Configure and add the selected expense item */} { if (!v) setConfigExpenseItem(null) }}> Configure Expense Item {configExpenseItem && ( { setConfigExpenseItem(null) invalidate() }} onCancel={() => setConfigExpenseItem(null)} /> )}
) }