import React, { useCallback, useMemo, useState } from "react"; import { View, ScrollView, Pressable, ActivityIndicator, RefreshControl, } from "react-native"; import { useFocusEffect } from "expo-router"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { api } from "@/lib/api"; import { Text } from "@/components/ui/text"; import { Button } from "@/components/ui/button"; import { EmptyState } from "@/components/EmptyState"; import { Card, CardContent } from "@/components/ui/card"; import { FileText, Plus } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; const STATUSES = ["All", "Draft", "Pending", "Paid", "Overdue", "Cancelled"]; export default function InvoicesListScreen() { const nav = useSirouRouter(); const [activeFilter, setActiveFilter] = useState("All"); const [allInvoices, setAllInvoices] = useState([]); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const fetchPage = useCallback(async (pageNum: number, replace = false) => { try { pageNum === 1 && !replace ? setLoading(true) : setLoadingMore(true); const response = await api.invoices.getAll({ query: { page: pageNum, limit: 10 }, }); const newInvoices = response.data || []; setAllInvoices((prev) => replace || pageNum === 1 ? newInvoices : [...prev, ...newInvoices], ); setHasMore(response.meta?.hasNextPage ?? false); setPage(pageNum); } catch (e) { console.error("[InvoicesList] Failed to fetch invoices:", e); } finally { setLoading(false); setRefreshing(false); setLoadingMore(false); } }, []); useFocusEffect( useCallback(() => { fetchPage(1); }, [fetchPage]), ); const onRefresh = () => { setRefreshing(true); fetchPage(1, true); }; const loadMore = () => { if (hasMore && !loadingMore && !loading) { fetchPage(page + 1); } }; const filteredInvoices = useMemo(() => { if (activeFilter === "All") return allInvoices; return allInvoices.filter( (inv) => inv.status === activeFilter.toUpperCase(), ); }, [allInvoices, activeFilter]); if (loading && page === 1) { return ( ); } return ( } onScroll={({ nativeEvent }) => { const isCloseToBottom = nativeEvent.layoutMeasurement.height + nativeEvent.contentOffset.y >= nativeEvent.contentSize.height - 20; if (isCloseToBottom) loadMore(); }} scrollEventThrottle={400} > {/* Status Filters — client-side */} {STATUSES.map((filter) => ( setActiveFilter(filter)} className={`rounded-[4px] px-4 py-1.5 ${ activeFilter === filter ? "bg-primary" : "bg-card border border-border" }`} > {filter} ))} {/* Invoices List */} {loading ? ( ) : filteredInvoices.length > 0 ? ( filteredInvoices.map((inv) => ( nav.go("invoices/[id]", { id: inv.id })} > {inv.customerName} {new Date(inv.issueDate).toLocaleDateString()} · {inv.invoiceNumber ? ` #${inv.invoiceNumber}` : " Proforma"} ${Number(inv.amount).toLocaleString()} {inv.status} )) ) : ( )} {loadingMore && ( )} ); }