import React, { useState } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Link } from "react-router-dom"; import { createPageUrl } from "@/utils"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { FileText, Plus, DollarSign, Search, Eye, Download } from "lucide-react"; import { format, parseISO, isPast } from "date-fns"; import PageHeader from "../components/common/PageHeader"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; const statusColors = { 'Open': 'bg-orange-500 text-white', 'Confirmed': 'bg-purple-500 text-white', 'Overdue': 'bg-red-500 text-white', 'Resolved': 'bg-blue-500 text-white', 'Paid': 'bg-green-500 text-white', 'Reconciled': 'bg-yellow-600 text-white', 'Disputed': 'bg-gray-500 text-white', 'Verified': 'bg-teal-500 text-white', 'Pending': 'bg-amber-500 text-white', }; export default function Invoices() { const [activeTab, setActiveTab] = useState("all"); const [searchTerm, setSearchTerm] = useState(""); const [selectedInvoice, setSelectedInvoice] = useState(null); const [showPaymentDialog, setShowPaymentDialog] = useState(false); const [showCreateDialog, setShowCreateDialog] = useState(false); const [paymentMethod, setPaymentMethod] = useState(""); const queryClient = useQueryClient(); const { data: user } = useQuery({ queryKey: ['current-user-invoices'], queryFn: () => base44.auth.me(), }); const { data: invoices = [], isLoading } = useQuery({ queryKey: ['invoices'], queryFn: () => base44.entities.Invoice.list('-issue_date'), initialData: [], }); const userRole = user?.user_role || user?.role; // Filter invoices based on user role const visibleInvoices = React.useMemo(() => { if (userRole === "client") { return invoices.filter(inv => inv.business_name === user?.company_name || inv.manager_name === user?.full_name || inv.created_by === user?.email ); } if (userRole === "vendor") { return invoices.filter(inv => inv.vendor_name === user?.company_name); } // Admin, procurement, operator can see all return invoices; }, [invoices, userRole, user]); const updateInvoiceMutation = useMutation({ mutationFn: ({ id, data }) => base44.entities.Invoice.update(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['invoices'] }); setShowPaymentDialog(false); setSelectedInvoice(null); }, }); const getFilteredInvoices = () => { let filtered = visibleInvoices; // Status filter if (activeTab !== "all") { const statusMap = { open: "Open", disputed: "Disputed", resolved: "Resolved", verified: "Verified", overdue: "Overdue", reconciled: "Reconciled", paid: "Paid" }; filtered = filtered.filter(inv => inv.status === statusMap[activeTab]); } // Search filter if (searchTerm) { filtered = filtered.filter(inv => inv.invoice_number?.toLowerCase().includes(searchTerm.toLowerCase()) || inv.business_name?.toLowerCase().includes(searchTerm.toLowerCase()) || inv.manager_name?.toLowerCase().includes(searchTerm.toLowerCase()) || inv.event_name?.toLowerCase().includes(searchTerm.toLowerCase()) ); } return filtered; }; const filteredInvoices = getFilteredInvoices(); // Calculate metrics const getStatusCount = (status) => { if (status === "all") return visibleInvoices.length; return visibleInvoices.filter(inv => inv.status === status).length; }; const getTotalAmount = (status) => { const filtered = status === "all" ? visibleInvoices : visibleInvoices.filter(inv => inv.status === status); return filtered.reduce((sum, inv) => sum + (inv.amount || 0), 0); }; const allTotal = getTotalAmount("all"); const openTotal = getTotalAmount("Open"); const overdueTotal = getTotalAmount("Overdue"); const paidTotal = getTotalAmount("Paid"); const openPercentage = allTotal > 0 ? ((openTotal / allTotal) * 100).toFixed(1) : 0; const overduePercentage = allTotal > 0 ? ((overdueTotal / allTotal) * 100).toFixed(1) : 0; const paidPercentage = allTotal > 0 ? ((paidTotal / allTotal) * 100).toFixed(1) : 0; const handleRecordPayment = () => { if (selectedInvoice && paymentMethod) { updateInvoiceMutation.mutate({ id: selectedInvoice.id, data: { ...selectedInvoice, status: "Paid", paid_date: new Date().toISOString().split('T')[0], payment_method: paymentMethod } }); } }; return (
All
${allTotal.toLocaleString()}
100%
Open
${openTotal.toLocaleString()}
{openPercentage}%
Overdue
${overdueTotal.toLocaleString()}
{overduePercentage}%
Paid
${paidTotal.toLocaleString()}
{paidPercentage}%