From dedcbb888938a7ab789253d64eb1a32fbba54313 Mon Sep 17 00:00:00 2001 From: renee-png Date: Sat, 13 Jun 2026 00:14:13 -0400 Subject: [PATCH] Bill Approvals: Buildium two-column form + require vendor on all backend payments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bill Approvals Create/Edit dialogs redesigned to the same professional two-column Buildium layout used on the accounting Bills page: left attachment panel (drag-drop + live image/PDF preview), right grouped form, prominent blue total bar, primary-action-first footer - Accounting backend payments now require a vendor chosen from the dropdown: - Expenses: vendor is required; removed the free-text vendor fallback - Bills: must pick a 'Pay to' vendor before saving - Banking payments already enforced this (Reconciliation bank adjustments — interest/service charges — intentionally still allow no vendor, as they are not vendor payments) - Board members remain locked to approving/denying their own assigned rows plus commenting and submitting invoices (per request); all bill edits, GL, line items, status, and deletes stay read-only for board users Co-Authored-By: Claude Opus 4.8 --- src/pages/BillApprovalsPage.tsx | 188 ++++++++++-------- src/pages/accounting/AccountingBillsPage.tsx | 1 + .../accounting/AccountingExpensesPage.tsx | 9 +- 3 files changed, 112 insertions(+), 86 deletions(-) diff --git a/src/pages/BillApprovalsPage.tsx b/src/pages/BillApprovalsPage.tsx index 51c6e87..c38fe0f 100644 --- a/src/pages/BillApprovalsPage.tsx +++ b/src/pages/BillApprovalsPage.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { supabase } from "@/integrations/supabase/client"; import { useToast } from "@/hooks/use-toast"; import { useAuth } from "@/contexts/AuthContext"; -import { UserCheck, Plus, Search, Eye, Upload, X, ArrowUpDown, Edit, Trash2, MoreHorizontal, AlertTriangle, Loader2, Bell, Printer, Sparkles, Download } from "lucide-react"; +import { UserCheck, Plus, Search, Eye, Upload, X, ArrowUpDown, Edit, Trash2, MoreHorizontal, AlertTriangle, Loader2, Bell, Printer, Sparkles, Download, FileText, Save } from "lucide-react"; import { generateCheckPDF, type CheckData } from "@/pages/accounting/lib/checkPdf"; import { accounting } from "@/lib/accountingClient"; import { Alert, AlertDescription } from "@/components/ui/alert"; @@ -99,6 +99,68 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci const [uploadFile, setUploadFile] = useState(null); const [submitting, setSubmitting] = useState(false); const fileRef = useRef(null); + const [filePreview, setFilePreview] = useState(null); + const [dragOver, setDragOver] = useState(false); + useEffect(() => { + if (uploadFile && uploadFile.type.startsWith("image/")) { + const url = URL.createObjectURL(uploadFile); + setFilePreview(url); + return () => URL.revokeObjectURL(url); + } + setFilePreview(null); + }, [uploadFile]); + const handleFile = (f: File) => setUploadFile(f); + + // Buildium-style left attachment panel, shared by the Create and Edit dialogs. + const renderAttachmentPanel = (existingUrl?: string | null) => { + const isPdf = (existingUrl || "").toLowerCase().endsWith(".pdf"); + const empty = !filePreview && !uploadFile && !existingUrl; + return ( +
{ e.preventDefault(); setDragOver(true); }} + onDragLeave={() => setDragOver(false)} + onDrop={(e) => { e.preventDefault(); setDragOver(false); const f = e.dataTransfer.files?.[0]; if (f) handleFile(f); }} + className={`relative rounded-lg border-2 border-dashed min-h-[420px] flex flex-col items-center justify-center text-center p-6 transition-colors ${dragOver ? "border-primary bg-primary/5" : "border-border"} ${empty ? "cursor-pointer hover:bg-muted/40" : ""}`} + onClick={() => { if (empty) fileRef.current?.click(); }} + > + { const f = e.target.files?.[0]; if (f) handleFile(f); }} /> + {filePreview ? ( + Attachment + ) : uploadFile ? ( +
+ +
{uploadFile.name}
+
{(uploadFile.size / 1024).toFixed(1)} KB
+
+ ) : existingUrl ? ( + isPdf ? ( +