import React, { useState, useEffect } from "react"; import { View, ScrollView, ActivityIndicator, Alert, Linking, } from "react-native"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { Stack, useLocalSearchParams } from "expo-router"; import { Text } from "@/components/ui/text"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Wallet, Link2, Clock, AlertTriangle, User, ShieldCheck, Building2, Hash, CheckCircle2, Eye, Trash2, Network, AlertCircle, Info, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { api, BASE_URL } from "@/lib/api"; import { useColorScheme } from "nativewind"; import { toast } from "@/lib/toast-store"; export default function PaymentDetailScreen() { const nav = useSirouRouter(); const { id } = useLocalSearchParams(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const [payment, setPayment] = useState(null); const [loading, setLoading] = useState(true); const [deleting, setDeleting] = useState(false); const [matching, setMatching] = useState(false); const paymentId = Array.isArray(id) ? id[0] : id; useEffect(() => { const fetchPayment = async () => { try { setLoading(true); if (!paymentId) throw new Error("No ID provided"); console.log("[PaymentDetail] Fetching ID:", paymentId); const response = await api.payments.getById({ params: { id: paymentId }, }); setPayment(response); console.log("[PaymentDetail] Response:", response); } catch (error) { console.error("[PaymentDetail] Error fetching payment:", error); toast.error("Error", "Failed to fetch payment details."); } finally { setLoading(false); } }; fetchPayment(); }, [paymentId]); const handleDelete = async () => { Alert.alert( "Delete Payment", "Are you sure you want to delete this payment record?", [ { text: "Cancel", style: "cancel" }, { text: "Delete", style: "destructive", onPress: async () => { setDeleting(true); try { if (!paymentId) return; await api.payments.delete({ params: { id: paymentId } }); toast.success("Deleted", "Payment record has been removed."); nav.back(); } catch (err: any) { toast.error("Error", err.message || "Failed to delete payment."); } finally { setDeleting(false); } }, }, ], ); }; const handleMatch = async () => { if (!payment || matching || !paymentId) return; setMatching(true); toast.info("Matching...", "Searching for a corresponding invoice."); try { // 1. Fetch all invoices const invoices = await api.invoices.getAll(); const invoiceList = Array.isArray(invoices) ? invoices : (invoices as any).data || []; // 2. Algorithm: Match Amount AND (Sender OR Receiver Name) const pAmount = Number(payment.amount); const pSender = (payment.senderName || "").toLowerCase().trim(); const pReceiver = (payment.receiverName || "").toLowerCase().trim(); const match = invoiceList.find((inv: any) => { const invAmount = Number(inv.amount); const invCustomer = (inv.customerName || "").toLowerCase().trim(); // Exact amount match is primary const amountMatches = Math.abs(invAmount - pAmount) < 0.01; // Name proximity match (either sender or receiver) const nameMatches = (invCustomer && pSender && pSender.includes(invCustomer)) || (invCustomer && pSender && invCustomer.includes(pSender)) || (invCustomer && pReceiver && pReceiver.includes(invCustomer)) || (invCustomer && pReceiver && invCustomer.includes(pReceiver)); return amountMatches && nameMatches; }); if (!match) { toast.info( "No Match Found", "Could not find an invoice with the same amount and customer name.", ); return; } // 3. Confirm match with user Alert.alert( "Match Found!", `Associate this payment with Invoice #${match.invoiceNumber} for ${match.customerName}?`, [ { text: "Cancel", style: "cancel" }, { text: "Associate", style: "default", onPress: async () => { try { await api.payments.associate({ params: { id: paymentId }, body: { invoiceId: match.id }, }); toast.success( "Success", "Payment successfully associated with invoice.", ); // Refresh data const updated = await api.payments.getById({ params: { id: paymentId }, }); setPayment(updated); } catch (err: any) { toast.error("Error", err.message || "Failed to associate."); } }, }, ], ); } catch (err: any) { console.error("[Match] Error:", err); toast.error("Error", "Failed to fetch invoices for matching."); } finally { setMatching(false); } }; if (loading) { return ( Retrieving Transaction... ); } if (!payment) { return ( Transaction Not Found The requested payment record could not be retrieved from the server. ); } const amountValue = Number( typeof payment.amount === "object" ? payment.amount.value : payment.amount, ); const paymentDate = payment.paymentDate ? new Date(payment.paymentDate) : new Date(payment.createdAt); const isFlagged = payment.isFlagged === true; const isScanned = payment.isScanned === true; const scanned = payment.scannedData || {}; const extracted = scanned.extractedFields || {}; const verification = payment.verification || scanned.verification || {}; const isFailed = verification.verificationStatus === "failed" || verification.isVerified === false; return ( {/* Urgent Alerts */} {isFlagged && ( Security Flag ({payment.flagReason || "Audit Needed"}) {payment.flagNotes || "System flagged this for manual review."} )} {/* Hero Section */} {/* Status Badges */} {payment.invoiceId ? "Matched" : "Pending Match"} {isFailed && ( Verify Failed )} {isScanned && ( Scanned )} Total Transaction Amount {amountValue.toLocaleString()} {payment.currency || "USD"} {/* Core Info Grid */} Merchant {extracted.merchantName || payment.merchantName || "Unknown Merchant"} Provider {extracted.provider || payment.paymentMethod || "Direct Payment"} {/* Sender / Payer Box */} Transaction Origin {payment.senderName || (payment.user ? `${payment.user.firstName} ${payment.user.lastName}` : "Business Account")} {payment.transactionId || "INTERNAL-TXN"} {paymentDate.toLocaleString()} {/* Notes */} {payment.notes && ( Transaction Notes " {payment.notes} " )} {/* Premium Actions */} {scanned?.imageUrl && ( )} {!payment.invoiceId && !isFailed && ( )} ); }