import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Receipt, Plus, MoreVertical, Eye, CheckCircle, XCircle, Calendar, DollarSign, RefreshCw, Search, Send, CreditCard, } from 'lucide-react'; import { Button } from '@components/atoms/Button'; import { Card, CardHeader, CardTitle, CardContent } from '@components/molecules/Card'; import { DataTable, type Column } from '@components/organisms/DataTable'; import { Dropdown, type DropdownItem } from '@components/organisms/Dropdown'; import { Breadcrumbs } from '@components/organisms/Breadcrumbs'; import { ConfirmModal } from '@components/organisms/Modal'; import { NoDataEmptyState, ErrorEmptyState } from '@components/templates/EmptyState'; import { useInvoices } from '@features/financial/hooks'; import type { FinancialInvoice, FinancialInvoiceStatus, InvoiceType } from '@features/financial/types'; import { formatDate, formatNumber } from '@utils/formatters'; const statusLabels: Record = { draft: 'Borrador', open: 'Abierta', paid: 'Pagada', cancelled: 'Cancelada', }; const statusColors: Record = { draft: 'bg-gray-100 text-gray-700', open: 'bg-blue-100 text-blue-700', paid: 'bg-green-100 text-green-700', cancelled: 'bg-red-100 text-red-700', }; const invoiceTypeLabels: Record = { customer: 'Cliente', supplier: 'Proveedor', }; // Helper function to format currency with 2 decimals const formatCurrency = (value: number): string => { return formatNumber(value, 'es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }; export function InvoicesPage() { const navigate = useNavigate(); const [selectedStatus, setSelectedStatus] = useState(''); const [selectedType, setSelectedType] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [dateFrom, setDateFrom] = useState(''); const [dateTo, setDateTo] = useState(''); const [invoiceToValidate, setInvoiceToValidate] = useState(null); const [invoiceToCancel, setInvoiceToCancel] = useState(null); const { invoices, total, page, totalPages, isLoading, error, setPage, refresh, validateInvoice, cancelInvoice, } = useInvoices({ status: selectedStatus || undefined, invoiceType: selectedType || undefined, search: searchTerm || undefined, dateFrom: dateFrom || undefined, dateTo: dateTo || undefined, limit: 20, }); const getActionsMenu = (invoice: FinancialInvoice): DropdownItem[] => { const items: DropdownItem[] = [ { key: 'view', label: 'Ver detalle', icon: , onClick: () => navigate(`/financial/invoices/${invoice.id}`), }, ]; if (invoice.status === 'draft') { items.push({ key: 'validate', label: 'Validar factura', icon: , onClick: () => setInvoiceToValidate(invoice), }); items.push({ key: 'cancel', label: 'Cancelar', icon: , danger: true, onClick: () => setInvoiceToCancel(invoice), }); } if (invoice.status === 'open') { items.push({ key: 'payment', label: 'Registrar pago', icon: , // TODO: Implement payment modal - setShowPaymentModal(true) onClick: () => navigate(`/financial/invoices/${invoice.id}/payment`), }); items.push({ key: 'cancel', label: 'Cancelar factura', icon: , danger: true, onClick: () => setInvoiceToCancel(invoice), }); } return items; }; const columns: Column[] = [ { key: 'number', header: 'Factura', render: (invoice) => (
{invoice.number || 'Sin numero'}
{invoice.ref && (
Ref: {invoice.ref}
)}
), }, { key: 'type', header: 'Tipo', render: (invoice) => ( {invoiceTypeLabels[invoice.invoiceType]} ), }, { key: 'partner', header: 'Cliente/Proveedor', render: (invoice) => (
{invoice.partnerName || invoice.partnerId}
), }, { key: 'date', header: 'Fecha', sortable: true, render: (invoice) => ( {formatDate(invoice.invoiceDate, 'short')} ), }, { key: 'dueDate', header: 'Vencimiento', render: (invoice) => ( {invoice.dueDate ? formatDate(invoice.dueDate, 'short') : '-'} ), }, { key: 'amount', header: 'Total', sortable: true, render: (invoice) => (
${formatCurrency(invoice.amountTotal)}
{invoice.currencyCode && invoice.currencyCode !== 'MXN' && (
{invoice.currencyCode}
)}
), }, { key: 'residual', header: 'Pendiente', render: (invoice) => (
0 ? 'text-amber-600' : 'text-green-600'}`}> ${formatCurrency(invoice.amountResidual)}
), }, { key: 'status', header: 'Estado', render: (invoice) => ( {statusLabels[invoice.status]} ), }, { key: 'actions', header: '', render: (invoice) => ( } items={getActionsMenu(invoice)} align="right" /> ), }, ]; const handleValidate = async () => { if (invoiceToValidate) { await validateInvoice(invoiceToValidate.id); setInvoiceToValidate(null); } }; const handleCancel = async () => { if (invoiceToCancel) { await cancelInvoice(invoiceToCancel.id); setInvoiceToCancel(null); } }; // Calculate summary stats const draftCount = invoices.filter(i => i.status === 'draft').length; const openCount = invoices.filter(i => i.status === 'open').length; const totalAmount = invoices.reduce((sum, i) => sum + i.amountTotal, 0); const pendingAmount = invoices.filter(i => i.status === 'open').reduce((sum, i) => sum + i.amountResidual, 0); if (error) { return (
); } return (

Facturas

Gestiona facturas de clientes y proveedores

{/* Summary Stats */}
setSelectedStatus('draft')}>
Borradores
{draftCount}
setSelectedStatus('open')}>
Abiertas
{openCount}
Por Cobrar
${formatCurrency(pendingAmount)}
Total Facturado
${formatCurrency(totalAmount)}
Lista de Facturas
{/* Filters */}
setSearchTerm(e.target.value)} className="w-full rounded-md border border-gray-300 py-2 pl-10 pr-4 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
setDateFrom(e.target.value)} className="rounded-md border border-gray-300 px-3 py-2 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" /> - setDateTo(e.target.value)} className="rounded-md border border-gray-300 px-3 py-2 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
{(selectedStatus || selectedType || searchTerm || dateFrom || dateTo) && ( )}
{/* Table */} {invoices.length === 0 && !isLoading ? ( ) : ( )}
{/* Validate Invoice Modal */} setInvoiceToValidate(null)} onConfirm={handleValidate} title="Validar factura" message={`¿Validar la factura ${invoiceToValidate?.number || 'borrador'}? Total: $${invoiceToValidate ? formatCurrency(invoiceToValidate.amountTotal) : '0.00'}`} variant="success" confirmText="Validar" /> {/* Cancel Invoice Modal */} setInvoiceToCancel(null)} onConfirm={handleCancel} title="Cancelar factura" message={`¿Cancelar la factura ${invoiceToCancel?.number || 'borrador'}? Esta accion no se puede deshacer.`} variant="danger" confirmText="Cancelar factura" />
); } export default InvoicesPage;