import React, { useState, useCallback } from "react"; import { View, ScrollView, ActivityIndicator, Linking, Pressable, Share, 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 { Inbox, Calendar, Clock, Hash, Share2, Package, X, Mail, Link2, Info, Truck, Send, AlertCircle, CheckCircle2, XCircle, Hourglass, } 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"; const STATUS_THEME: Record< string, { label: string; bg: string; text: string; dot: string; pillBg: string } > = { DRAFT: { label: "Draft", bg: "bg-slate-500/10", text: "text-slate-600", dot: "bg-slate-500", pillBg: "#6b728015", }, OPEN: { label: "Open", bg: "bg-primary/10", text: "text-primary", dot: "bg-primary", pillBg: "#E4621215", }, UNDER_REVIEW: { label: "Under Review", bg: "bg-blue-500/10", text: "text-blue-600", dot: "bg-blue-500", pillBg: "#2563eb15", }, REVISION_REQUESTED: { label: "Revision Requested", bg: "bg-red-500/10", text: "text-red-600", dot: "bg-red-500", pillBg: "#dc262615", }, CLOSED: { label: "Closed", bg: "bg-emerald-500/10", text: "text-emerald-600", dot: "bg-emerald-500", pillBg: "#16a34a15", }, CANCELLED: { label: "Cancelled", bg: "bg-slate-500/10", text: "text-slate-600", dot: "bg-slate-500", pillBg: "#6b728015", }, }; const CATEGORY_THEME: Record = { EQUIPMENT: { color: "#2563eb", bg: "#2563eb15" }, SERVICE: { color: "#16a34a", bg: "#16a34a15" }, MIXED: { color: "#E46212", bg: "#E4621215" }, }; const INVITE_STATUS_ICON: Record< string, { Icon: React.ComponentType; color: string; label: string } > = { PENDING: { Icon: Hourglass, color: "#94a3b8", label: "Pending" }, SENT: { Icon: CheckCircle2, color: "#16a34a", label: "Sent" }, FAILED: { Icon: XCircle, color: "#dc2626", label: "Failed" }, }; function fmtDate(d?: string) { if (!d) return "—"; return new Date(d).toLocaleDateString(); } export default function ProformaRequestDetailScreen() { const nav = useSirouRouter(); const { id } = useLocalSearchParams(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [showShareSheet, setShowShareSheet] = useState(false); const fetch = useCallback(async () => { try { setLoading(true); const reqId = Array.isArray(id) ? id[0] : id; if (!reqId) return; const result = await api.proformaRequests.getById({ params: { id: reqId }, }); setData(result); } catch (err: any) { toast.error("Error", "Failed to load proforma request"); } finally { setLoading(false); } }, [id]); useFocusEffect( useCallback(() => { fetch(); }, [fetch]), ); const handleShare = async (channel: "system" | "email") => { if (!data?.inviteUrl) { toast.error("No invite link", "This request has no invite URL yet"); return; } try { if (channel === "email") { await Linking.openURL( `mailto:?subject=${encodeURIComponent( data.title || "Proforma Request", )}&body=${encodeURIComponent(data.inviteUrl)}`, ); } else { await Share.share({ message: `${data.title || "Proforma Request"}\n${data.inviteUrl}`, }); } setShowShareSheet(false); } catch (err: any) { toast.error("Error", err?.message || "Failed to share"); } }; if (loading || !data) { return ( ); } const statusKey = (data.status || "DRAFT").toUpperCase(); const theme = STATUS_THEME[statusKey] || STATUS_THEME.DRAFT; const categoryKey = (data.category || "MIXED").toUpperCase(); const categoryTheme = CATEGORY_THEME[categoryKey] || CATEGORY_THEME.MIXED; const items: any[] = Array.isArray(data.items) ? data.items : []; const invites: any[] = Array.isArray(data.invites) ? data.invites : []; const submissionCount = data.submissionCount ?? 0; const totalQuantity = items.reduce( (s: number, i: any) => s + (Number(i.quantity) || 0), 0, ); return ( {/* Hero Card */} {data.title || "Untitled request"} {categoryKey} {theme.label} {data.description ? ( {data.description} ) : null} Deadline {fmtDate(data.submissionDeadline)} Submissions {submissionCount} {/* Items */} {items.length > 0 && ( Requested Items ({items.length}) {totalQuantity} units {items.map((item: any, idx: number) => ( {item.itemName || `Item ${idx + 1}`} {item.quantity || 0} {item.unitOfMeasure || "unit"} {item.itemDescription ? ( {item.itemDescription} ) : null} {item.technicalSpecifications && Object.keys(item.technicalSpecifications).length > 0 ? ( {Object.entries( item.technicalSpecifications as Record, ).map(([k, v]) => ( {k}: {String(v)} ))} ) : null} ))} )} {/* Commercial Terms */} Commercial Terms {data.paymentTerms ? ( ) : null} {data.incoterms ? ( ) : null} {data.validityPeriod != null ? ( ) : null} {data.discountStructure ? ( ) : null} {/* Invite link */} {data.inviteUrl ? ( Invite Link {data.inviteUrl} setShowShareSheet(true)} className="h-9 rounded-[6px] bg-primary flex-row items-center justify-center gap-1.5" > Share Invite Link ) : null} {/* Invites */} {invites.length > 0 && ( Invites ({invites.length}) {invites.map((inv: any, idx: number) => { const key = (inv.status || "PENDING").toUpperCase(); const invTheme = INVITE_STATUS_ICON[key] || INVITE_STATUS_ICON.PENDING; const { Icon, color, label } = invTheme; return ( {inv.customerName || "Customer"} {inv.sentTo ? ( {inv.sentTo} ) : null} {label} {inv.sendError ? ( {inv.sendError} ) : null} ); })} )} {/* Notes fallback / no-data state */} {items.length === 0 && !data.description && ( This request has no items or description yet. )} setShowShareSheet(false)} > setShowShareSheet(false)} className="flex-1 bg-black/40 justify-end" > {}} className="bg-background rounded-t-3xl p-5 pb-8" style={{ borderTopWidth: 1, borderColor: isDark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.05)", }} > Share Invite Link setShowShareSheet(false)} hitSlop={8}> handleShare("system")} /> handleShare("email")} /> ); } function TermRow({ icon: Icon, label, value, divider, multiline, }: { icon: React.ComponentType; label: string; value: string; divider?: boolean; multiline?: boolean; }) { return ( {label} {value} ); } function ShareOption({ icon: Icon, label, description, onPress, }: { icon: React.ComponentType; label: string; description: string; onPress: () => void; }) { return ( {label} {description} ); }