/** * Invoices Screen * * Invoice list with filters */ import { useState, useEffect, useCallback } from 'react'; import { View, Text, StyleSheet, FlatList, TouchableOpacity, RefreshControl, ActivityIndicator, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { invoicesApi } from '@/services/api'; import { Invoice } from '@/types'; type StatusFilter = 'all' | 'draft' | 'posted' | 'paid'; const statusConfig = { draft: { label: 'Borrador', color: '#6b7280', bg: '#f3f4f6' }, posted: { label: 'Publicada', color: '#d97706', bg: '#fef3c7' }, paid: { label: 'Pagada', color: '#059669', bg: '#d1fae5' }, cancelled: { label: 'Cancelada', color: '#dc2626', bg: '#fee2e2' }, }; export default function InvoicesScreen() { const [invoices, setInvoices] = useState([]); const [isLoading, setIsLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [filter, setFilter] = useState('all'); const fetchInvoices = async () => { try { const params: any = { limit: 50 }; if (filter !== 'all') params.state = filter; const response = await invoicesApi.list(params); setInvoices(response.data || []); } catch (error) { console.error('Error fetching invoices:', error); // Use mock data for demo setInvoices([ { id: '1', number: 'INV-2024-001', partnerId: '1', partnerName: 'Empresa ABC', invoiceType: 'out_invoice', state: 'paid', invoiceDate: '2024-01-15', amountTotal: 15999.00, amountDue: 0, currency: 'MXN' }, { id: '2', number: 'INV-2024-002', partnerId: '2', partnerName: 'Cliente XYZ', invoiceType: 'out_invoice', state: 'posted', invoiceDate: '2024-01-18', dueDate: '2024-02-18', amountTotal: 8500.00, amountDue: 8500.00, currency: 'MXN' }, { id: '3', number: 'INV-2024-003', partnerId: '3', partnerName: 'Distribuidor Norte', invoiceType: 'out_invoice', state: 'draft', invoiceDate: '2024-01-20', amountTotal: 12300.00, amountDue: 12300.00, currency: 'MXN' }, { id: '4', number: 'INV-2024-004', partnerId: '1', partnerName: 'Empresa ABC', invoiceType: 'out_invoice', state: 'posted', invoiceDate: '2024-01-22', dueDate: '2024-02-22', amountTotal: 5670.00, amountDue: 5670.00, currency: 'MXN' }, ]); } finally { setIsLoading(false); setRefreshing(false); } }; useEffect(() => { fetchInvoices(); }, [filter]); const onRefresh = useCallback(() => { setRefreshing(true); fetchInvoices(); }, [filter]); const formatCurrency = (amount: number) => { return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN', }).format(amount); }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString('es-MX', { day: '2-digit', month: 'short', year: 'numeric', }); }; const filteredInvoices = filter === 'all' ? invoices : invoices.filter((inv) => inv.state === filter); const renderInvoice = ({ item }: { item: Invoice }) => { const status = statusConfig[item.state] || statusConfig.draft; return ( {item.number} {status.label} {item.partnerName} {formatDate(item.invoiceDate)} {item.dueDate && item.state !== 'paid' && ( Vence: {formatDate(item.dueDate)} )} Total {formatCurrency(item.amountTotal)} {item.amountDue > 0 && item.state !== 'draft' && ( Pendiente {formatCurrency(item.amountDue)} )} ); }; return ( {/* Filters */} {(['all', 'draft', 'posted', 'paid'] as StatusFilter[]).map((status) => ( setFilter(status)} > {status === 'all' ? 'Todas' : statusConfig[status]?.label || status} ))} {/* List */} {isLoading ? ( ) : ( item.id} contentContainerStyle={styles.listContent} refreshControl={ } ListEmptyComponent={ No se encontraron facturas } /> )} {/* FAB */} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#f8fafc', }, filters: { flexDirection: 'row', padding: 16, gap: 8, }, filterButton: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 20, backgroundColor: '#ffffff', }, filterButtonActive: { backgroundColor: '#1e40af', }, filterText: { fontSize: 14, color: '#64748b', }, filterTextActive: { color: '#ffffff', fontWeight: '500', }, listContent: { padding: 16, paddingTop: 0, }, invoiceCard: { backgroundColor: '#ffffff', borderRadius: 12, padding: 16, marginBottom: 12, }, invoiceHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8, }, invoiceNumber: { fontSize: 16, fontWeight: '700', color: '#1f2937', }, statusBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, }, statusText: { fontSize: 12, fontWeight: '600', }, partnerName: { fontSize: 14, color: '#64748b', marginBottom: 12, }, invoiceDetails: { flexDirection: 'row', gap: 16, marginBottom: 12, }, detailItem: { flexDirection: 'row', alignItems: 'center', gap: 4, }, detailText: { fontSize: 13, color: '#9ca3af', }, invoiceFooter: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-end', paddingTop: 12, borderTopWidth: 1, borderTopColor: '#f1f5f9', }, amountLabel: { fontSize: 12, color: '#9ca3af', }, amountValue: { fontSize: 18, fontWeight: '700', color: '#1f2937', }, amountDue: { alignItems: 'flex-end', }, dueLabel: { fontSize: 12, color: '#dc2626', }, dueValue: { fontSize: 16, fontWeight: '600', color: '#dc2626', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, emptyContainer: { alignItems: 'center', paddingVertical: 48, }, emptyText: { fontSize: 16, color: '#9ca3af', marginTop: 12, }, fab: { position: 'absolute', bottom: 24, right: 24, width: 56, height: 56, borderRadius: 28, backgroundColor: '#1e40af', justifyContent: 'center', alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 4, elevation: 5, }, });