Yaltopia-Tickets-App/app/invoices/[id].tsx
2026-03-11 22:48:53 +03:00

282 lines
9.9 KiB
TypeScript

import React, { useState, useEffect } from "react";
import { View, ScrollView, Pressable, ActivityIndicator } 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 {
FileText,
Calendar,
Share2,
Download,
ArrowLeft,
ExternalLink,
} from "@/lib/icons";
import { ScreenWrapper } from "@/components/ScreenWrapper";
import { ShadowWrapper } from "@/components/ShadowWrapper";
import { StandardHeader } from "@/components/StandardHeader";
import { api } from "@/lib/api";
import { toast } from "@/lib/toast-store";
export default function InvoiceDetailScreen() {
const nav = useSirouRouter<AppRoutes>();
const { id } = useLocalSearchParams();
const [loading, setLoading] = useState(true);
const [invoice, setInvoice] = useState<any>(null);
useEffect(() => {
fetchInvoice();
}, [id]);
const fetchInvoice = async () => {
try {
setLoading(true);
const data = await api.invoices.getById({ params: { id: id as string } });
setInvoice(data);
} catch (error: any) {
console.error("[InvoiceDetail] Error:", error);
toast.error("Error", "Failed to load invoice details");
} finally {
setLoading(false);
}
};
if (loading) {
return (
<ScreenWrapper className="bg-background">
<Stack.Screen options={{ headerShown: false }} />
<StandardHeader title="Invoice Details" showBack />
<View className="flex-1 justify-center items-center">
<ActivityIndicator color="#ea580c" size="large" />
</View>
</ScreenWrapper>
);
}
if (!invoice) {
return (
<ScreenWrapper className="bg-background">
<Stack.Screen options={{ headerShown: false }} />
<StandardHeader title="Invoice Details" showBack />
<View className="flex-1 justify-center items-center">
<Text variant="muted">Invoice not found</Text>
</View>
</ScreenWrapper>
);
}
return (
<ScreenWrapper className="bg-background">
<Stack.Screen options={{ headerShown: false }} />
<StandardHeader title="Invoice Details" showBack />
<ScrollView
className="flex-1"
contentContainerStyle={{ padding: 16, paddingBottom: 120 }}
showsVerticalScrollIndicator={false}
>
{/* Status Hero Card */}
<Card className="mb-4 overflow-hidden rounded-[6px] border-0 bg-primary">
<View className="p-5">
<View className="flex-row items-center justify-between mb-3">
<View className="bg-white/20 p-1.5 rounded-[6px]">
<FileText color="white" size={16} strokeWidth={2.5} />
</View>
<View
className={`rounded-[6px] px-3 py-1 ${invoice.status === "PAID" ? "bg-emerald-500/20" : "bg-white/15"}`}
>
<Text
className={`text-[10px] font-bold ${invoice.status === "PAID" ? "text-emerald-400" : "text-white"}`}
>
{invoice.status || "Pending"}
</Text>
</View>
</View>
<Text variant="small" className="text-white/70 mb-0.5">
Total Amount
</Text>
<Text variant="h3" className="text-white font-bold mb-3">
${Number(invoice.amount).toLocaleString()}
</Text>
<View className="flex-row items-center gap-3 border-t border-white/40 pt-3">
<View className="flex-row items-center gap-1.5">
<Calendar color="rgba(255,255,255,0.9)" size={12} />
<Text className="text-white/90 text-xs font-semibold">
Due {new Date(invoice.dueDate).toLocaleDateString()}
</Text>
</View>
<View className="h-3 w-[1px] bg-white/60" />
<Text className="text-white/90 text-xs font-semibold">
#{invoice.invoiceNumber || id}
</Text>
</View>
</View>
</Card>
{/* Recipient & Category — inline info strip */}
<Card className="bg-card rounded-[6px] mb-4">
<View className="flex-row px-4 py-2">
<View className="flex-1 flex-row items-center">
<View className="flex-col">
<Text className="text-foreground text-xs opacity-60">
Recipient
</Text>
<Text
variant="p"
className="text-foreground font-semibold"
numberOfLines={1}
>
{invoice.customerName || "—"}
</Text>
</View>
</View>
<View className="w-[1px] bg-border/70 mx-3" />
<View className="flex-1 flex-row items-center">
<View className="flex-col">
<Text className="text-foreground text-xs opacity-60">
Category
</Text>
<Text
variant="p"
className="text-foreground font-semibold"
numberOfLines={1}
>
General
</Text>
</View>
</View>
</View>
</Card>
{/* Items / Billing Summary */}
<Card className="mb-4 bg-card rounded-[6px]">
<View className="p-4">
<View className="flex-row items-center gap-2 mb-2">
<Text
variant="small"
className="font-bold opacity-60 uppercase text-[10px] tracking-widest"
>
Billing Summary
</Text>
</View>
<View className="flex-row justify-between py-3 border-b border-border/70">
<View className="flex-1 pr-4">
<Text
variant="p"
className="text-foreground font-semibold text-sm"
>
Subtotal
</Text>
</View>
<Text variant="p" className="text-foreground font-bold text-sm">
$
{(
Number(invoice.amount) - (Number(invoice.taxAmount) || 0)
).toLocaleString()}
</Text>
</View>
{Number(invoice.taxAmount) > 0 && (
<View className="flex-row justify-between py-3 border-b border-border/70">
<View className="flex-1 pr-4">
<Text
variant="p"
className="text-foreground font-semibold text-sm"
>
Tax
</Text>
</View>
<Text variant="p" className="text-foreground font-bold text-sm">
+ ${Number(invoice.taxAmount).toLocaleString()}
</Text>
</View>
)}
<View className="mt-3 pt-3 flex-row justify-between items-center border-t border-border/70">
<Text variant="muted" className="font-semibold text-sm">
Total Balance
</Text>
<Text
variant="h3"
className="text-foreground font-semibold text-xl tracking-tight"
>
${Number(invoice.amount).toLocaleString()}
</Text>
</View>
</View>
</Card>
{/* Notes Section (New) */}
{invoice.notes && (
<Card className="mb-4 bg-card rounded-[6px]">
<View className="p-4">
<Text
variant="small"
className="font-bold opacity-60 uppercase text-[10px] tracking-widest mb-2"
>
Additional Notes
</Text>
<Text
variant="p"
className="text-foreground font-medium text-xs leading-5"
>
{invoice.notes}
</Text>
</View>
</Card>
)}
{/* Timeline Section (New) */}
<View className="mt-2 mb-6 px-4 py-3 bg-secondary/20 rounded-[8px] border border-border/30">
<View className="flex-row justify-between mb-1.5">
<Text className="text-[10px] text-muted-foreground uppercase font-bold tracking-tighter">
Created
</Text>
<Text className="text-[10px] text-foreground font-bold">
{new Date(invoice.createdAt).toLocaleString()}
</Text>
</View>
<View className="flex-row justify-between">
<Text className="text-[10px] text-muted-foreground uppercase font-bold tracking-tighter">
Last Updated
</Text>
<Text className="text-[10px] text-foreground font-bold">
{new Date(invoice.updatedAt).toLocaleString()}
</Text>
</View>
</View>
{/* Actions */}
<View className="flex-row gap-3">
<Button
className=" flex-1 mb-4 h-12 rounded-[10px] bg-primary shadow-lg shadow-primary/20"
onPress={() => {}}
>
<Share2 color="#ffffff" size={16} strokeWidth={2.5} />
<Text className="ml-2 text-white text-[12px] font-black uppercase tracking-widest">
Share SMS
</Text>
</Button>
<ShadowWrapper>
<Button
className=" flex-1 mb-4 h-12 rounded-[10px] bg-card border border-border"
onPress={() => {}}
>
<Download color="#0f172a" size={16} strokeWidth={2.5} />
<Text className="ml-2 text-foreground text-[12px] font-black uppercase tracking-widest">
Get PDF
</Text>
</Button>
</ShadowWrapper>
</View>
</ScrollView>
</ScreenWrapper>
);
}