330 lines
12 KiB
TypeScript
330 lines
12 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 { useRouter } from "expo-router";
|
||
import { Text } from "@/components/ui/text";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Card } from "@/components/ui/card";
|
||
import {
|
||
ArrowLeft,
|
||
DraftingCompass,
|
||
Clock,
|
||
Send,
|
||
ExternalLink,
|
||
ChevronRight,
|
||
CheckCircle2,
|
||
} from "@/lib/icons";
|
||
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
||
import { StandardHeader } from "@/components/StandardHeader";
|
||
import { api } from "@/lib/api";
|
||
import { toast } from "@/lib/toast-store";
|
||
|
||
const dummyData = {
|
||
id: "dummy-1",
|
||
proformaNumber: "PF-001",
|
||
customerName: "John Doe",
|
||
customerEmail: "john@example.com",
|
||
customerPhone: "+1234567890",
|
||
amount: { value: 1000, currency: "USD" },
|
||
currency: "USD",
|
||
issueDate: "2026-03-10T11:51:36.134Z",
|
||
dueDate: "2026-03-10T11:51:36.134Z",
|
||
description: "Dummy proforma",
|
||
notes: "Test notes",
|
||
taxAmount: { value: 100, currency: "USD" },
|
||
discountAmount: { value: 50, currency: "USD" },
|
||
pdfPath: "dummy.pdf",
|
||
userId: "user-1",
|
||
items: [
|
||
{
|
||
id: "item-1",
|
||
description: "Test item",
|
||
quantity: 1,
|
||
unitPrice: { value: 1000, currency: "USD" },
|
||
total: { value: 1000, currency: "USD" }
|
||
}
|
||
],
|
||
createdAt: "2026-03-10T11:51:36.134Z",
|
||
updatedAt: "2026-03-10T11:51:36.134Z"
|
||
};
|
||
|
||
export default function ProformaDetailScreen() {
|
||
const nav = useSirouRouter<AppRoutes>();
|
||
const router = useRouter();
|
||
const { id } = useLocalSearchParams();
|
||
|
||
const [loading, setLoading] = useState(true);
|
||
const [proforma, setProforma] = useState<any>(null);
|
||
|
||
useEffect(() => {
|
||
fetchProforma();
|
||
}, [id]);
|
||
|
||
const fetchProforma = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const data = await api.proforma.getById({ params: { id: id as string } });
|
||
setProforma(data);
|
||
} catch (error: any) {
|
||
console.error("[ProformaDetail] Error:", error);
|
||
toast.error("Error", "Failed to load proforma details");
|
||
setProforma(dummyData); // Use dummy data for testing
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
<StandardHeader title="Proforma" showBack />
|
||
<View className="flex-1 justify-center items-center">
|
||
<ActivityIndicator color="#ea580c" size="large" />
|
||
</View>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|
||
|
||
if (!proforma) {
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
<StandardHeader title="Proforma" showBack />
|
||
<View className="flex-1 justify-center items-center">
|
||
<Text variant="muted">Proforma not found</Text>
|
||
</View>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|
||
|
||
const subtotal =
|
||
proforma.items?.reduce(
|
||
(acc: number, item: any) => acc + (Number(item.total) || 0),
|
||
0,
|
||
) || 0;
|
||
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
|
||
{/* Header */}
|
||
<StandardHeader title="Proforma" showBack />
|
||
|
||
<ScrollView
|
||
className="flex-1"
|
||
contentContainerStyle={{ padding: 16, paddingBottom: 120 }}
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
{/* Proforma Info Card */}
|
||
<Card className="bg-card rounded-[12px] mb-4 border border-border">
|
||
<View className="p-4">
|
||
<View className="flex-row items-center gap-3 mb-3">
|
||
<View className="bg-primary/10 p-2 rounded-[8px]">
|
||
<DraftingCompass color="#ea580c" size={16} strokeWidth={2.5} />
|
||
</View>
|
||
<Text className="text-foreground font-bold text-sm uppercase tracking-widest">
|
||
Proforma Details
|
||
</Text>
|
||
</View>
|
||
|
||
<View className="gap-2">
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Proforma Number</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{proforma.proformaNumber}</Text>
|
||
</View>
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Issued Date</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{new Date(proforma.issueDate).toLocaleDateString()}</Text>
|
||
</View>
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Due Date</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{new Date(proforma.dueDate).toLocaleDateString()}</Text>
|
||
</View>
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Currency</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{proforma.currency}</Text>
|
||
</View>
|
||
{proforma.description && (
|
||
<View className="mt-2">
|
||
<Text variant="muted" className="text-xs font-medium mb-1">Description</Text>
|
||
<Text className="text-foreground text-sm">{proforma.description}</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</Card>
|
||
|
||
{/* Customer Info Card */}
|
||
<Card className="bg-card rounded-[12px] mb-4 border border-border">
|
||
<View className="p-4">
|
||
<View className="flex-row items-center gap-3 mb-3">
|
||
<View className="bg-primary/10 p-2 rounded-[8px]">
|
||
<CheckCircle2 color="#ea580c" size={16} strokeWidth={2.5} />
|
||
</View>
|
||
<Text className="text-foreground font-bold text-sm uppercase tracking-widest">
|
||
Customer Information
|
||
</Text>
|
||
</View>
|
||
|
||
<View className="gap-2">
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Name</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{proforma.customerName}</Text>
|
||
</View>
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Email</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{proforma.customerEmail || "N/A"}</Text>
|
||
</View>
|
||
<View className="flex-row justify-between">
|
||
<Text variant="muted" className="text-xs font-medium">Phone</Text>
|
||
<Text className="text-foreground font-semibold text-sm">{proforma.customerPhone || "N/A"}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</Card>
|
||
|
||
|
||
|
||
{/* Line Items Card */}
|
||
<Card className="bg-card rounded-[6px] mb-4">
|
||
<View className="p-4">
|
||
<View className="flex-row items-center gap-2 mb-2">
|
||
<Text
|
||
variant="small"
|
||
className="font-bold uppercase tracking-widest text-[10px] opacity-60"
|
||
>
|
||
Line Items
|
||
</Text>
|
||
</View>
|
||
|
||
{proforma.items?.map((item: any, i: number) => (
|
||
<View
|
||
key={item.id || i}
|
||
className={`flex-row justify-between py-3 ${i < proforma.items.length - 1 ? "border-b border-border/40" : ""}`}
|
||
>
|
||
<View className="flex-1 pr-4">
|
||
<Text
|
||
variant="p"
|
||
className="text-foreground font-semibold text-sm"
|
||
>
|
||
{item.description}
|
||
</Text>
|
||
<Text variant="muted" className="text-[10px] mt-0.5">
|
||
{item.quantity} × {proforma.currency}{" "}
|
||
{Number(item.unitPrice).toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
<Text variant="p" className="text-foreground font-bold text-sm">
|
||
{proforma.currency} {Number(item.total).toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
))}
|
||
|
||
<View className="mt-3 pt-3 border-t border-border/40 gap-2">
|
||
<View className="flex-row justify-between">
|
||
<Text
|
||
variant="p"
|
||
className="text-foreground font-semibold text-sm"
|
||
>
|
||
Subtotal
|
||
</Text>
|
||
<Text variant="p" className="text-foreground font-bold text-sm">
|
||
{proforma.currency} {subtotal.toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
{Number(proforma.taxAmount) > 0 && (
|
||
<View className="flex-row justify-between">
|
||
<Text
|
||
variant="p"
|
||
className="text-foreground font-semibold text-sm"
|
||
>
|
||
Tax
|
||
</Text>
|
||
<Text
|
||
variant="p"
|
||
className="text-foreground font-bold text-sm"
|
||
>
|
||
{proforma.currency}{" "}
|
||
{Number(proforma.taxAmount).toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{Number(proforma.discountAmount) > 0 && (
|
||
<View className="flex-row justify-between">
|
||
<Text
|
||
variant="p"
|
||
className="text-red-500 font-semibold text-sm"
|
||
>
|
||
Discount
|
||
</Text>
|
||
<Text variant="p" className="text-red-500 font-bold text-sm">
|
||
-{proforma.currency}{" "}
|
||
{Number(proforma.discountAmount).toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
<View className="flex-row justify-between items-center mt-1">
|
||
<Text variant="p" className="text-foreground font-bold">
|
||
Total Amount
|
||
</Text>
|
||
<Text
|
||
variant="h4"
|
||
className="text-foreground font-bold tracking-tight"
|
||
>
|
||
{proforma.currency} {Number(proforma.amount).toLocaleString()}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</Card>
|
||
|
||
{/* Notes Section (New) */}
|
||
{proforma.notes && (
|
||
<Card className="bg-card rounded-[6px] mb-4">
|
||
<View className="p-4">
|
||
<Text
|
||
variant="small"
|
||
className="font-bold uppercase tracking-widest text-[10px] opacity-60 mb-2"
|
||
>
|
||
Additional Notes
|
||
</Text>
|
||
<Text
|
||
variant="p"
|
||
className="text-foreground font-medium text-xs leading-5"
|
||
>
|
||
{proforma.notes}
|
||
</Text>
|
||
</View>
|
||
</Card>
|
||
)}
|
||
|
||
{/* Actions */}
|
||
<View className="gap-3">
|
||
<Button
|
||
className="h-12 rounded-[10px] bg-transparent border border-border"
|
||
onPress={() => router.push("/proforma/edit?id=" + proforma.id)}
|
||
>
|
||
<DraftingCompass color="#fff" size={16} strokeWidth={2.5} />
|
||
<Text className="ml-2 text-foreground font-black text-[12px] uppercase tracking-widest">
|
||
Edit
|
||
</Text>
|
||
</Button>
|
||
<Button
|
||
className="h-12 rounded-[10px] bg-primary shadow-lg shadow-primary/20"
|
||
onPress={() => {}}
|
||
>
|
||
<Send color="#ffffff" size={16} strokeWidth={2.5} />
|
||
<Text className="ml-2 text-white font-black text-[12px] uppercase tracking-widest">
|
||
Share SMS
|
||
</Text>
|
||
</Button>
|
||
|
||
</View>
|
||
</ScrollView>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|