import React, { useState, useEffect, useCallback, useMemo } from "react"; import { CommandPalette } from "@/components/CommandPalette"; import { View, ScrollView, Pressable, TextInput, ActivityIndicator, } from "react-native"; import { Text } from "@/components/ui/text"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { useSirouRouter } from "@sirou/react-native"; import { useFocusEffect } from "expo-router"; import { AppRoutes } from "@/lib/routes"; import { Plus, FileText, Search, ChevronRight, Inbox, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { EmptyState } from "@/components/EmptyState"; import { api } from "@/lib/api"; import { useAuthStore } from "@/lib/auth-store"; import { toast } from "@/lib/toast-store"; import { useColorScheme } from "nativewind"; import { getPlaceholderColor } from "@/lib/colors"; import { hasPermission, PERMISSION_MAP } from "@/lib/permissions"; type Tab = "proforma" | "request"; interface ProformaItem { id: string; proformaNumber: string; customerName: string; customerEmail: string; customerPhone: string; amount: any; currency: string; issueDate: string; dueDate: string; description: string; notes: string; taxAmount: any; discountAmount: any; pdfPath: string; userId: string; items: any[]; createdAt: string; updatedAt: string; } interface ProformaRequest { id: string; title: string; description: string; category: "EQUIPMENT" | "SERVICE" | "MIXED"; status: | "DRAFT" | "OPEN" | "UNDER_REVIEW" | "REVISION_REQUESTED" | "CLOSED" | "CANCELLED"; submissionDeadline: string; items: { id: string; itemName: string; quantity: number; unitOfMeasure: string }[]; createdAt: string; updatedAt: string; } const REQUEST_STATUS_COLORS: Record = { DRAFT: "#6b7280", OPEN: "#E46212", UNDER_REVIEW: "#2563eb", REVISION_REQUESTED: "#dc2626", CLOSED: "#16a34a", CANCELLED: "#6b7280", }; const REQUEST_STATUS_BG: Record = { DRAFT: "#6b728015", OPEN: "#E4621215", UNDER_REVIEW: "#2563eb15", REVISION_REQUESTED: "#dc262615", CLOSED: "#16a34a15", CANCELLED: "#6b728015", }; const CATEGORY_COLORS: Record = { EQUIPMENT: "#2563eb", SERVICE: "#16a34a", MIXED: "#E46212", }; export default function ProformaScreen() { const nav = useSirouRouter(); const permissions = useAuthStore((s) => s.permissions); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const [tab, setTab] = useState("proforma"); const [searchOpen, setSearchOpen] = useState(false); // Proforma state const [proformas, setProformas] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [search, setSearch] = useState(""); // Request state const [requests, setRequests] = useState([]); const [requestsLoading, setRequestsLoading] = useState(false); const [reqPage, setReqPage] = useState(1); const [reqHasMore, setReqHasMore] = useState(true); const [reqLoadingMore, setReqLoadingMore] = useState(false); const canCreateProformas = hasPermission( permissions, PERMISSION_MAP["proforma:create"], ); const fetchProformas = useCallback(async (pageNum: number) => { const { isAuthenticated } = useAuthStore.getState(); if (!isAuthenticated) return; try { pageNum === 1 ? setLoading(true) : setLoadingMore(true); const response = await api.proforma.getAll({ query: { page: pageNum, limit: 10 }, }); const newProformas = response.data; setProformas((prev) => pageNum === 1 ? newProformas : [...prev, ...newProformas], ); setHasMore(response.meta.hasNextPage); setPage(pageNum); } catch (err: any) { console.error("[Proforma] Fetch error:", err); setHasMore(false); } finally { setLoading(false); setLoadingMore(false); } }, []); const fetchRequests = useCallback(async (pageNum: number) => { const { isAuthenticated } = useAuthStore.getState(); if (!isAuthenticated) return; try { pageNum === 1 ? setRequestsLoading(true) : setReqLoadingMore(true); const response = await api.proformaRequests.getAll({ query: { page: pageNum, limit: 10 }, }); const newRequests = response.data; setRequests((prev) => pageNum === 1 ? newRequests : [...prev, ...newRequests], ); setReqHasMore(response.meta.hasNextPage); setReqPage(pageNum); } catch (err: any) { console.error("[ProformaRequests] Fetch error:", err); toast.error("Error", "Failed to fetch proforma requests."); } finally { setRequestsLoading(false); setReqLoadingMore(false); } }, []); useFocusEffect( useCallback(() => { if (tab === "proforma") fetchProformas(1); else fetchRequests(1); }, [tab, fetchProformas, fetchRequests]), ); const loadMore = () => { if (tab === "proforma" && hasMore && !loadingMore && !loading) { fetchProformas(page + 1); } if ( tab === "request" && reqHasMore && !reqLoadingMore && !requestsLoading ) { fetchRequests(reqPage + 1); } }; const filteredProformas = useMemo(() => { if (!search.trim()) return proformas; const q = search.toLowerCase(); const searchNum = parseFloat(q); return proformas.filter((p) => { if (p.customerName?.toLowerCase().includes(q)) return true; if (p.proformaNumber?.toLowerCase().includes(q)) return true; const pAmount = typeof p.amount === "object" ? parseFloat(p.amount.value) : parseFloat(p.amount); if (!isNaN(searchNum) && !isNaN(pAmount)) { if (pAmount === searchNum) return true; if (String(pAmount).includes(q)) return true; } return false; }); }, [proformas, search]); const filteredRequests = useMemo(() => { if (!search.trim()) return requests; const q = search.toLowerCase(); return requests.filter((r) => { if (r.title?.toLowerCase().includes(q)) return true; if (r.description?.toLowerCase().includes(q)) return true; if (r.category?.toLowerCase().includes(q)) return true; if (r.status?.toLowerCase().includes(q)) return true; return false; }); }, [requests, search]); const renderProformaCard = (item: ProformaItem) => { const amountVal = typeof item.amount === "object" ? item.amount.value : item.amount; const issuedStr = item.issueDate ? new Date(item.issueDate).toLocaleDateString() : ""; const dueStr = item.dueDate ? new Date(item.dueDate).toLocaleDateString() : ""; const itemsCount = Array.isArray(item.items) ? item.items.length : 0; return ( nav.go("proforma/[id]", { id: item.id })} className="mb-2" > {item.proformaNumber || "Proforma"} {item.currency || "ETB"}{" "} {(amountVal || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2, })} {item.customerName || "Customer"} · Issued {issuedStr} {dueStr ? ` · Due ${dueStr}` : ""} · {itemsCount} item {itemsCount !== 1 ? "s" : ""} ); }; const renderRequestCard = (req: ProformaRequest) => { const statusColor = REQUEST_STATUS_COLORS[req.status] || "#6b7280"; const statusBg = REQUEST_STATUS_BG[req.status] || "#6b728015"; const categoryColor = CATEGORY_COLORS[req.category] || "#6b7280"; const deadlineStr = req.submissionDeadline ? new Date(req.submissionDeadline).toLocaleDateString() : ""; const itemsCount = Array.isArray(req.items) ? req.items.length : 0; return ( nav.go("proforma-requests/[id]", { id: req.id })} className="mb-2" > {req.title || "Untitled request"} {req.status.replace(/_/g, " ")} {itemsCount} item{itemsCount !== 1 ? "s" : ""} · Deadline{" "} {deadlineStr || "—"} {req.category} ); }; const isLoading = tab === "proforma" ? loading && page === 1 : requestsLoading && reqPage === 1; if (isLoading) { return ( setSearchOpen(true)} /> ); } const items = tab === "proforma" ? filteredProformas : filteredRequests; return ( { const isCloseToBottom = nativeEvent.layoutMeasurement.height + nativeEvent.contentOffset.y >= nativeEvent.contentSize.height - 20; if (isCloseToBottom) loadMore(); }} scrollEventThrottle={400} > setSearchOpen(true)} /> {/* Tabs */} { if (tab !== "proforma") { setTab("proforma"); setSearch(""); } }} className={`flex-1 py-2 rounded-[8px] items-center ${ tab === "proforma" ? "bg-primary" : "" }`} > Proforma { if (tab !== "request") { setTab("request"); setSearch(""); } }} className={`flex-1 py-2 rounded-[8px] items-center ${ tab === "request" ? "bg-primary" : "" }`} > Requests {items.length > 0 ? ( tab === "proforma" ? (items as ProformaItem[]).map(renderProformaCard) : (items as ProformaRequest[]).map(renderRequestCard) ) : ( )} {(loadingMore || reqLoadingMore) && ( )} setSearchOpen(false)} /> ); }