import React, { useState, useCallback } from "react"; import { View, ScrollView, ActivityIndicator, Alert, Linking, Pressable, Modal, Dimensions, } 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 { FileText, Calendar, Download, Trash2, Package, Clock, User, Hash, AlertCircle, Edit, Mail, MessageSquare, Globe, MoreVertical, X, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { api, BASE_URL } from "@/lib/api"; import { toast } from "@/lib/toast-store"; import { useAuthStore } from "@/lib/auth-store"; import { useColorScheme } from "nativewind"; const { height: SCREEN_HEIGHT } = Dimensions.get("window"); 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 })}`; } const STATUS_THEME: Record = { PAID: { label: "Paid", bg: "bg-emerald-500/10", text: "text-emerald-600", dot: "bg-emerald-500" }, PENDING: { label: "Pending", bg: "bg-amber-500/10", text: "text-amber-600", dot: "bg-amber-500" }, DRAFT: { label: "Draft", bg: "bg-blue-500/10", text: "text-blue-600", dot: "bg-blue-500" }, CANCELLED: { label: "Cancelled", bg: "bg-slate-500/10", text: "text-slate-600", dot: "bg-slate-500" }, DEFAULT: { label: "Unknown", bg: "bg-slate-500/10", text: "text-slate-500", dot: "bg-slate-500" }, }; export default function ProformaDetailScreen() { const nav = useSirouRouter(); const { id } = useLocalSearchParams(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const [loading, setLoading] = useState(true); const [proforma, setProforma] = useState(null); const [showActions, setShowActions] = useState(false); useFocusEffect(useCallback(() => { fetchProforma(); }, [id])); const fetchProforma = async () => { try { setLoading(true); const pid = Array.isArray(id) ? id[0] : id; if (!pid) return; const data = await api.proforma.getById({ params: { id: pid } }); setProforma(data); } catch { toast.error("Error", "Failed to load proforma details"); } finally { setLoading(false); } }; const handleGetPdf = async () => { try { const { token } = useAuthStore.getState(); const pid = Array.isArray(id) ? id[0] : id; await Linking.openURL(`${BASE_URL}proforma/${pid}/pdf?token=${token}`); } catch { toast.error("Error", "Failed to open PDF"); } }; const handleDelete = () => { Alert.alert("Delete Proforma", "This cannot be undone.", [ { text: "Cancel", style: "cancel" }, { text: "Delete", style: "destructive", onPress: async () => { try { setLoading(true); const pid = Array.isArray(id) ? id[0] : id; await api.proforma.delete({ params: { id: pid } }); toast.success("Success", "Proforma deleted"); nav.back(); } catch { toast.error("Error", "Failed to delete proforma"); setLoading(false); } }, }, ]); }; if (loading) { return ( ); } if (!proforma) { return ( Proforma not found. ); } const currency = proforma.currency || "ETB"; const amount = safeVal(proforma.amount); const tax = safeVal(proforma.taxAmount); const discount = safeVal(proforma.discountAmount); const subtotal = amount + discount - tax; const items: any[] = proforma.items || []; const statusKey = (proforma.status || "DRAFT").toUpperCase(); const theme = STATUS_THEME[statusKey] || STATUS_THEME.DEFAULT; return ( nav.go("proforma/edit", { id: proforma.id })} /> {/* Hero — Amount + Status */} Total Amount {amount.toLocaleString("en-US", { minimumFractionDigits: 2 })} {currency} {theme.label} {/* Period Dates */} Issued {proforma.issueDate ? new Date(proforma.issueDate).toLocaleDateString() : "—"} Due {proforma.dueDate ? new Date(proforma.dueDate).toLocaleDateString() : "—"} {/* Customer */} {proforma.customerName && ( Customer {proforma.customerName} {(proforma.customerEmail || proforma.customerPhone) && ( {[proforma.customerEmail, proforma.customerPhone].filter(Boolean).join(" · ")} )} #{proforma.id?.slice(0, 8) || "—"} )} {/* Items */} {items.length > 0 && ( Items ({items.length}) {items.map((item: any, idx: number) => ( {item.description || `Item ${idx + 1}`} {fmt(safeVal(item.total || item.unitPrice) * safeVal(item.quantity || 1), currency)} {safeVal(item.quantity)} × {fmt(safeVal(item.unitPrice), currency)} ))} )} {items.length === 0 && ( No items )} {/* Summary */} Summary Subtotal {fmt(subtotal, currency)} {tax > 0 && ( Tax +{fmt(tax, currency)} )} {discount > 0 && ( Discount -{fmt(discount, currency)} )} Total {fmt(amount, currency)} {/* Description */} {proforma.description ? ( Description {proforma.description} ) : null} {/* Notes */} {proforma.notes ? ( Notes {proforma.notes} ) : null} {/* Actions Trigger */} setShowActions(true)} className="bg-primary h-10 rounded-[6px] flex-row items-center justify-center gap-2" > Actions {/* Actions Bottom Sheet */} setShowActions(false)} > setShowActions(false)}> e.stopPropagation()} > {/* Header */} Actions setShowActions(false)} className="h-8 w-8 bg-secondary/80 rounded-full items-center justify-center border border-border/10" > {/* PDF */} } label="Download PDF" description="Save proforma as PDF document" onPress={() => { setShowActions(false); handleGetPdf(); }} /> {/* Delete */} } label="Delete Proforma" description="Permanently remove this proforma" onPress={() => { setShowActions(false); handleDelete(); }} danger /> {/* Send as Email */} Send } label="Send as Email" description="Public accessible shortened link via yaltopia.com" /> } label="Send as SMS" description="Public accessible shortened link via yaltopia.com" /> ); } function ActionOption({ icon, label, description, onPress, danger, }: { icon: React.ReactNode; label: string; description: string; onPress?: () => void; danger?: boolean; }) { return ( {icon} {label} {description} ); }