import React, { useState, useCallback } from "react"; import { View, ScrollView, ActivityIndicator, Pressable, TextInput, Modal } from "react-native"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { Stack, useLocalSearchParams, useFocusEffect } from "expo-router"; import { Text } from "@/components/ui/text"; import { Button } from "@/components/ui/button"; import { User, Calendar, Clock, Building2, Send, Pencil, ChevronRight } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { api } from "@/lib/api"; import { useColorScheme } from "nativewind"; import { toast } from "@/lib/toast-store"; function useInputColors() { const { colorScheme } = useColorScheme(); const dark = colorScheme === "dark"; return { bg: dark ? "rgba(30,30,30,0.8)" : "rgba(241,245,249,0.2)", border: dark ? "rgba(255,255,255,0.08)" : "rgba(203,213,225,0.6)", text: dark ? "#f1f5f9" : "#0f172a", placeholder: "rgba(100,116,139,0.45)", }; } const STATUS_THEME: Record< string, { label: string; bg: string; text: string; dot: string } > = { DRAFT: { label: "Draft", bg: "bg-slate-500/10", text: "text-slate-600", dot: "bg-slate-500", }, SENT: { label: "Sent", bg: "bg-primary/10", text: "text-primary", dot: "bg-primary", }, OPENED: { label: "Opened", bg: "bg-blue-500/10", text: "text-blue-600", dot: "bg-blue-500", }, PAID: { label: "Paid", bg: "bg-emerald-500/10", text: "text-emerald-600", dot: "bg-emerald-500", }, EXPIRED: { label: "Expired", bg: "bg-red-500/10", text: "text-red-600", dot: "bg-red-500", }, CANCELLED: { label: "Cancelled", bg: "bg-slate-500/10", text: "text-slate-600", dot: "bg-slate-500", }, }; function safeVal(v: any): number { if (v == null) return 0; if (typeof v === "object") return Number(v.value) || 0; return Number(v) || 0; } function fmt(v: number, currency = "ETB") { return `${currency} ${v.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; } export default function PaymentRequestDetailScreen() { const nav = useSirouRouter(); const { id } = useLocalSearchParams(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const c = useInputColors(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [sending, setSending] = useState(false); const [showSendModal, setShowSendModal] = useState(false); const [sendChannel, setSendChannel] = useState("EMAIL"); const [sendRecipient, setSendRecipient] = useState(""); const fetch = useCallback(async () => { try { setLoading(true); const reqId = Array.isArray(id) ? id[0] : id; if (!reqId) return; const result = await api.paymentRequests.getById({ params: { id: reqId }, }); setData(result); await api.paymentRequests.open({ params: { id: reqId } }).catch(() => {}); } catch (err: any) { toast.error("Error", "Failed to load payment request"); } finally { setLoading(false); } }, [id]); useFocusEffect( useCallback(() => { fetch(); }, [fetch]), ); const openSendModal = () => { setSendChannel("EMAIL"); setSendRecipient(data?.customerEmail || ""); setShowSendModal(true); }; const handleSend = async () => { if (!sendRecipient.trim()) { toast.error("Validation", "Recipient is required"); return; } try { setSending(true); const reqId = Array.isArray(id) ? id[0] : id; await api.paymentRequests.send({ params: { id: reqId }, body: { channel: sendChannel, recipient: sendRecipient.trim(), }, headers: { "Content-Type": "application/json" }, }); toast.success("Sent", `Payment request sent via ${sendChannel.toLowerCase()}`); setShowSendModal(false); fetch(); } catch (err: any) { toast.error("Error", err?.message || "Failed to send payment request"); } finally { setSending(false); } }; if (loading || !data) { return ( ); } const statusKey = (data.status || "DRAFT").toUpperCase(); const theme = STATUS_THEME[statusKey] || STATUS_THEME.DRAFT; const items: any[] = data.items || []; const accounts: any[] = data.accounts || []; const currency = data.currency || "ETB"; const amount = safeVal(data.amount); const subtotal = items.reduce((s: number, i: any) => s + safeVal(i.total), 0); const tax = safeVal(data.taxAmount); const discount = safeVal(data.discountAmount); const total = amount || subtotal + tax - discount; return ( {/* Status Badge */} {theme.label} {/* Customer + Dates cluster */} Customer {data.customerName || "—"} {(data.customerEmail || data.customerPhone) && ( {[data.customerEmail, data.customerPhone] .filter(Boolean) .join(" · ")} )} Issued {data.issueDate ? new Date(data.issueDate).toLocaleDateString() : "—"} Due {data.dueDate ? new Date(data.dueDate).toLocaleDateString() : "—"} {/* Description */} {data.description ? ( Description {data.description} ) : null} {/* Items */} {items.length > 0 && ( Items ({items.length}) {items.map((item: any, idx: number) => ( {item.description || `Item ${idx + 1}`} {fmt(safeVal(item.total), currency)} {safeVal(item.quantity)} ×{" "} {fmt(safeVal(item.unitPrice), currency)} ))} )} {/* Totals */} Summary {subtotal > 0 && ( Subtotal {fmt(subtotal, currency)} )} {tax > 0 && ( Tax {fmt(tax, currency)} )} {discount > 0 && ( Discount -{fmt(discount, currency)} )} Total {fmt(total, currency)} {/* Accounts */} {accounts.length > 0 && ( Bank Accounts ({accounts.length}) {accounts.map((acc: any, idx: number) => ( {acc.bankName || "Bank"} {acc.currency || currency} {acc.accountName || "—"} {acc.accountNumber || "—"} ))} )} {/* Notes */} {data.notes ? ( Notes {data.notes} ) : null} {/* Actions */} {/* Send Modal */} setShowSendModal(false)} > setShowSendModal(false)} > e.stopPropagation()} > Send Payment Request setShowSendModal(false)} className="h-8 w-8 bg-secondary/80 rounded-full items-center justify-center" > {/* Channel Toggle */} {(["EMAIL", "PHONE"] as const).map((ch) => ( { setSendChannel(ch); if (ch === "EMAIL") { setSendRecipient(data?.customerEmail || ""); } else { setSendRecipient(data?.customerPhone || ""); } }} className={`flex-1 h-11 rounded-[6px] items-center justify-center border ${ sendChannel === ch ? "bg-primary border-primary" : "bg-card border-border" }`} > {ch === "EMAIL" ? "Email" : "SMS"} ))} {/* Recipient Input */} {sendChannel === "EMAIL" ? "Email Address" : "Phone Number"} ); }