import React, { useState, useCallback } from "react"; import { View, ScrollView, ActivityIndicator, Linking, Pressable, useColorScheme, } 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 { Calendar, Trash2, AlertCircle, FileText, ExternalLink, Download, ArrowLeft, Edit, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { api } from "@/lib/api"; import { toast } from "@/lib/toast-store"; import { ActionModal } from "@/components/ActionModal"; const STATUS_COLORS: Record = { DRAFT: { bg: "bg-slate-500/10", text: "text-slate-500", dot: "bg-slate-500" }, SUBMITTED: { bg: "bg-amber-500/10", text: "text-amber-500", dot: "bg-amber-500" }, PAID: { bg: "bg-emerald-500/10", text: "text-emerald-500", dot: "bg-emerald-500" }, CANCELLED: { bg: "bg-red-500/10", text: "text-red-500", dot: "bg-red-500" }, }; export default function DeclarationDetailScreen() { const nav = useSirouRouter(); const { id } = useLocalSearchParams(); const colorScheme = useColorScheme(); const isDark = colorScheme === "dark"; const [loading, setLoading] = useState(true); const [declaration, setDeclaration] = useState(null); const [showDeleteModal, setShowDeleteModal] = useState(false); useFocusEffect( useCallback(() => { fetchDeclaration(); }, [id]), ); const fetchDeclaration = async () => { try { setLoading(true); const declId = Array.isArray(id) ? id[0] : id; if (!declId) throw new Error("No ID provided"); const data = await api.declarations.getById({ params: { id: declId } }); setDeclaration(data); } catch (error: any) { console.error("[DeclarationDetail] Error:", error); toast.error("Error", "Failed to load declaration"); } finally { setLoading(false); } }; const confirmDelete = async () => { try { await api.declarations.create({ body: { id: Array.isArray(id) ? id[0] : id, isActive: false }, }); toast.success("Success", "Declaration removed"); setShowDeleteModal(false); nav.back(); } catch (error: any) { toast.error("Error", error?.message || "Failed to delete"); setShowDeleteModal(false); } }; const openUrl = async (url: string) => { if (url) { try { await Linking.openURL(url); } catch { toast.error("Error", "Could not open file"); } } }; if (loading) { return ( ); } if (!declaration) { return ( Not Found This declaration could not be retrieved. ); } const st = STATUS_COLORS[(declaration.status || "DRAFT").toUpperCase()] || STATUS_COLORS.DRAFT; const typeBadge = declaration.type === "VAT" ? { bg: "bg-blue-500/10", text: "text-blue-600" } : { bg: "bg-purple-500/10", text: "text-purple-600" }; const formatDate = (d: string | null) => d ? new Date(d).toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric", }) : "—"; return ( nav.back()} className="h-9 w-9 rounded-[10px] bg-card items-center justify-center border border-border" > Declaration Details nav.go("declarations/edit", { id: declaration?.id })} className="h-9 w-9 rounded-[10px] bg-card items-center justify-center border border-border" > {/* Hero */} {declaration.type?.replace("_", " ")} {declaration.title || "Declaration"} #{declaration.declarationNumber} {declaration.period && ( <> · {declaration.period} )} {declaration.status || "DRAFT"} {declaration.daysUntilDue != null && ( {declaration.isOverdue ? ( ) : ( )} {declaration.isOverdue ? `Overdue by ${Math.abs(declaration.daysUntilDue)} days` : `${declaration.daysUntilDue} days until due`} )} {/* Period Dates (two-column card like invoice dates) */} Period Start {formatDate(declaration.periodStart)} Period End {formatDate(declaration.periodEnd)} {/* Tax Details */} Tax Details {declaration.tin && ( )} {declaration.taxAccountNumber && ( )} {declaration.taxCentre && ( )} {!declaration.tin && !declaration.taxAccountNumber && !declaration.taxCentre && ( No tax details available )} {/* Submission Info (if available) */} {(declaration.submissionNumber || declaration.submissionDate) && ( Submission {declaration.submissionNumber && ( )} {declaration.submissionDate && ( )} )} {/* Files */} {declaration.fileUrl && ( Declaration File openUrl(declaration.fileUrl)} className="bg-card rounded-[6px] border border-border p-3.5 flex-row items-center" > {declaration.fileName || "Declaration PDF"} {declaration.fileSize && ( {(declaration.fileSize / 1024).toFixed(1)} KB )} )} {/* Receipts */} {declaration.receiptFiles?.length > 0 && ( Receipts ({declaration.receiptFiles.length}) {declaration.receiptFiles.map((rf: any, idx: number) => ( openUrl(rf.url)} className="bg-card rounded-[6px] border border-border p-3 flex-row items-center" > {rf.fileName || `Receipt ${idx + 1}`} ))} )} {/* Linked Invoices */} {declaration.invoices?.length > 0 && ( Linked Invoices ({declaration.invoices.length}) {declaration.invoices.map((inv: any) => ( nav.go("invoices/[id]", { id: inv.id })} className="bg-card rounded-[6px] border border-border p-3 flex-row items-center" > {inv.customerName || "Invoice"} {inv.invoiceNumber} · {Number(inv.amount?.value || inv.amount || 0).toLocaleString()} {inv.currency} ))} )} {/* Notes */} {declaration.notes && ( Notes {declaration.notes} )} {/* Delete */} setShowDeleteModal(true)} className="h-11 rounded-[6px] bg-red-500 flex-row items-center justify-center gap-2" > Delete Declaration setShowDeleteModal(false)} onConfirm={confirmDelete} title="Delete Declaration" description="Are you sure you want to delete this declaration?" confirmText="Delete" confirmVariant="destructive" icon={Trash2} iconColor="#ef4444" /> ); } function DetailRow({ label, value, isLast }: { label: string; value: string; isLast?: boolean }) { return ( {label} {value} ); }