import React, { useEffect, useState } from 'react'; import { Search, Plus, Edit, Trash2, Eye, FileText, Filter } from 'lucide-react'; import { invoiceService, Invoice } from '../../services/api'; import { toast } from 'react-toastify'; import Loading from '../../components/common/Loading'; import Pagination from '../../components/common/Pagination'; import { ExportButton } from '../../components/common'; import { useFormatCurrency } from '../../hooks/useFormatCurrency'; import { useNavigate } from 'react-router-dom'; import { formatDate } from '../../utils/format'; const InvoiceManagementPage: React.FC = () => { const { formatCurrency } = useFormatCurrency(); const navigate = useNavigate(); const [invoices, setInvoices] = useState([]); const [loading, setLoading] = useState(true); const [filters, setFilters] = useState({ search: '', status: '', }); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalItems, setTotalItems] = useState(0); const itemsPerPage = 10; useEffect(() => { setCurrentPage(1); }, [filters]); useEffect(() => { fetchInvoices(); }, [filters, currentPage]); const fetchInvoices = async () => { try { setLoading(true); const response = await invoiceService.getInvoices({ status: filters.status || undefined, page: currentPage, limit: itemsPerPage, }); if (response.status === 'success' && response.data) { let invoiceList = response.data.invoices || []; if (filters.search) { invoiceList = invoiceList.filter((inv) => inv.invoice_number.toLowerCase().includes(filters.search.toLowerCase()) || inv.customer_name.toLowerCase().includes(filters.search.toLowerCase()) || inv.customer_email.toLowerCase().includes(filters.search.toLowerCase()) || (inv.promotion_code && inv.promotion_code.toLowerCase().includes(filters.search.toLowerCase())) ); } setInvoices(invoiceList); setTotalPages(response.data.total_pages || 1); setTotalItems(response.data.total || 0); } } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load invoices'); } finally { setLoading(false); } }; const getStatusBadge = (status: string) => { const badges: Record = { draft: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Draft', border: 'border-slate-200' }, sent: { bg: 'bg-gradient-to-r from-blue-50 to-indigo-50', text: 'text-blue-800', label: 'Sent', border: 'border-blue-200' }, paid: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Paid', border: 'border-emerald-200' }, overdue: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Overdue', border: 'border-rose-200' }, cancelled: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Cancelled', border: 'border-slate-200' }, }; return badges[status] || badges.draft; }; const handleDelete = async (id: number) => { if (!window.confirm('Are you sure you want to delete this invoice?')) { return; } try { await invoiceService.deleteInvoice(id); toast.success('Invoice deleted successfully'); fetchInvoices(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to delete invoice'); } }; if (loading && invoices.length === 0) { return ; } return (
{}

Invoice Management

Manage and track all invoices

({ 'Invoice Number': i.invoice_number, 'Customer Name': i.customer_name, 'Customer Email': i.customer_email, 'Booking ID': i.booking_id || 'N/A', 'Subtotal': formatCurrency(i.subtotal), 'Tax Amount': formatCurrency(i.tax_amount), 'Discount Amount': formatCurrency(i.discount_amount), 'Total Amount': formatCurrency(i.total_amount), 'Amount Paid': formatCurrency(i.amount_paid), 'Balance Due': formatCurrency(i.balance_due), 'Status': i.status, 'Issue Date': i.issue_date ? formatDate(i.issue_date) : 'N/A', 'Due Date': i.due_date ? formatDate(i.due_date) : 'N/A', 'Paid Date': i.paid_date ? formatDate(i.paid_date) : 'N/A', 'Is Proforma': i.is_proforma ? 'Yes' : 'No' }))} filename="invoices" title="Invoice Management Report" />
{}
setFilters({ ...filters, search: e.target.value })} className="w-full pl-12 pr-4 py-3.5 bg-white border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700 placeholder-slate-400 font-medium shadow-sm hover:shadow-md" />
{totalItems} invoice{totalItems !== 1 ? 's' : ''}
{}
{invoices.length > 0 ? ( invoices.map((invoice, index) => { const statusBadge = getStatusBadge(invoice.status); return ( ); }) ) : ( )}
Invoice # Customer Booking Amount Promotion Status Due Date Actions
{invoice.invoice_number}
{invoice.customer_name}
{invoice.customer_email}
#{invoice.booking_id}
{formatCurrency(invoice.total_amount)}
{invoice.balance_due > 0 && (
Due: {formatCurrency(invoice.balance_due)}
)} {invoice.discount_amount > 0 && (
Discount: -{formatCurrency(invoice.discount_amount)}
)}
{invoice.promotion_code ? ( {invoice.promotion_code} ) : ( )} {invoice.is_proforma && (
Proforma
)}
{statusBadge.label} {formatDate(invoice.due_date, 'short')}

No invoices found

Create your first invoice to get started

{} {totalPages > 1 && (
)}
); }; export default InvoiceManagementPage;