diff --git a/src/components/cases/expenses-tab.tsx b/src/components/cases/expenses-tab.tsx index 7cb78f3..2181233 100644 --- a/src/components/cases/expenses-tab.tsx +++ b/src/components/cases/expenses-tab.tsx @@ -6,7 +6,7 @@ import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/lib/auth"; -import { Plus, Trash2, Loader2, Receipt as ReceiptIcon, CheckCircle2, Undo2, Download, RefreshCw } from "lucide-react"; +import { Plus, Trash2, Loader2, Receipt as ReceiptIcon, CheckCircle2, Undo2, Download, RefreshCw, Pencil } from "lucide-react"; import { formatCurrency, formatDate } from "@/lib/format"; import { ensureMarkedInvoicedPlaceholder } from "@/lib/invoice-generation"; import { toast } from "sonner"; @@ -19,6 +19,7 @@ import { SelectValue, } from "@/components/ui/select"; import { SearchableSelect } from "@/components/ui/searchable-select"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; interface FeeItem { id: string; @@ -47,6 +48,15 @@ export function CaseExpensesTab({ caseId }: { caseId: string }) { const [fees, setFees] = useState([]); const [selectedFeeId, setSelectedFeeId] = useState(""); const [showImport, setShowImport] = useState(false); + const [staff, setStaff] = useState>([]); + const [editItem, setEditItem] = useState(null); + + useEffect(() => { + (async () => { + const { data } = await supabase.from("profiles").select("id, full_name, email").order("full_name"); + setStaff((data ?? []) as any); + })(); + }, []); const load = async () => { const { data, error } = await supabase @@ -356,6 +366,7 @@ export function CaseExpensesTab({ caseId }: { caseId: string }) { )} {!e.invoice_id && } + {!e.invoice_id && } @@ -373,6 +384,14 @@ export function CaseExpensesTab({ caseId }: { caseId: string }) { clientId={clientId} onImported={load} /> + + setEditItem(null)} + onSaved={() => { setEditItem(null); load(); }} + /> ); } @@ -385,3 +404,125 @@ function Stat({ label, value }: { label: string; value: string }) { ); } + +function EditExpenseDialog({ + item, + staff, + currentUserId, + onClose, + onSaved, +}: { + item: any | null; + staff: Array<{ id: string; full_name: string | null; email: string | null }>; + currentUserId: string; + onClose: () => void; + onSaved: () => void; +}) { + const [form, setForm] = useState({ + expense_date: "", + user_id: "", + description: "", + quantity: "1", + unit_price: "", + billable: true, + }); + const [saving, setSaving] = useState(false); + + useEffect(() => { + if (item) { + setForm({ + expense_date: item.expense_date ?? "", + user_id: item.user_id ?? currentUserId, + description: item.description ?? "", + quantity: String(item.quantity ?? 1), + unit_price: String(item.unit_price ?? item.amount ?? ""), + billable: !!item.billable, + }); + } + }, [item, currentUserId]); + + const save = async () => { + if (!item) return; + const quantity = parseFloat(form.quantity); + const unit_price = parseFloat(form.unit_price); + if (isNaN(quantity) || quantity <= 0) { toast.error("Quantity must be > 0"); return; } + if (isNaN(unit_price) || unit_price < 0) { toast.error("Invalid unit price"); return; } + if (!form.description.trim()) { toast.error("Description required"); return; } + const amount = +(quantity * unit_price).toFixed(2); + setSaving(true); + const { error } = await supabase + .from("expenses") + .update({ + expense_date: form.expense_date, + user_id: form.user_id, + description: form.description.trim(), + quantity, + unit_price, + amount, + billable: form.billable, + }) + .eq("id", item.id); + setSaving(false); + if (error) { toast.error(error.message); return; } + toast.success("Expense updated"); + onSaved(); + }; + + return ( + { if (!o) onClose(); }}> + + Edit expense +
+
+
+ + setForm({ ...form, expense_date: e.target.value })} /> +
+
+ + +
+
+
+
+ + setForm({ ...form, quantity: e.target.value })} /> +
+
+ + setForm({ ...form, unit_price: e.target.value })} /> +
+
+ + +
+
+
+ + setForm({ ...form, description: e.target.value })} maxLength={500} /> +
+ +
+ + + + +
+
+ ); +}