Yaltopia-Tickets-App/app/proforma/[id].tsx

297 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
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";
export default function ProformaDetailScreen() {
const nav = useSirouRouter<AppRoutes>();
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");
} 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}
>
{/* Blue Summary Card */}
<Card className="overflow-hidden rounded-[6px] border-0 bg-primary mb-4">
<View className="p-5">
<View className="flex-row items-center justify-between mb-3">
<View className="bg-white/20 p-1.5 rounded-[6px]">
<DraftingCompass color="white" size={16} strokeWidth={2.5} />
</View>
<View className="bg-amber-500/20 px-3 py-1 rounded-[6px] border border-white/10">
<Text className="text-[10px] font-bold text-white uppercase tracking-widest">
ACTIVE
</Text>
</View>
</View>
<Text variant="small" className="text-white/70 mb-0.5">
Customer: {proforma.customerName}
</Text>
<Text variant="h3" className="text-white font-bold mb-3">
{proforma.description || "Proforma Request"}
</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">
<Clock color="rgba(255,255,255,0.9)" size={12} />
<Text className="text-white/90 text-xs font-semibold">
Due {new Date(proforma.dueDate).toLocaleDateString()}
</Text>
</View>
<View className="h-3 w-[1px] bg-white/60" />
<Text className="text-white/90 text-xs font-semibold">
{proforma.proformaNumber}
</Text>
</View>
</View>
</Card>
{/* Customer Info Strip (Added for functionality while keeping style) */}
<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-[10px] opacity-60 uppercase font-bold">
Email
</Text>
<Text
variant="p"
className="text-foreground font-semibold text-xs"
numberOfLines={1}
>
{proforma.customerEmail || "N/A"}
</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-[10px] opacity-60 uppercase font-bold">
Phone
</Text>
<Text
variant="p"
className="text-foreground font-semibold text-xs"
numberOfLines={1}
>
{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="flex-row gap-3">
<Button
className="flex-1 h-11 rounded-[6px] bg-primary"
onPress={() => {}}
>
<Send color="#ffffff" size={14} strokeWidth={2.5} />
<Text className="ml-2 text-white font-bold text-[11px] uppercase tracking-widest">
Share
</Text>
</Button>
<Button
className="flex-1 h-11 rounded-[6px] bg-card border border-border"
onPress={() => nav.back()}
>
<Text className="text-foreground font-semibold text-[11px] uppercase tracking-widest">
Back
</Text>
</Button>
</View>
</ScrollView>
</ScreenWrapper>
);
}