Yaltopia-Tickets-App/app/(tabs)/proforma.tsx

210 lines
6.5 KiB
TypeScript

import React, { useState, useEffect, useCallback } from "react";
import {
View,
Pressable,
ActivityIndicator,
FlatList,
ListRenderItem,
} from "react-native";
import { Text } from "@/components/ui/text";
import { Card } from "@/components/ui/card";
import { useSirouRouter } from "@sirou/react-native";
import { AppRoutes } from "@/lib/routes";
import { Plus, Send, FileText, Clock } from "@/lib/icons";
import { ScreenWrapper } from "@/components/ScreenWrapper";
import { StandardHeader } from "@/components/StandardHeader";
import { Button } from "@/components/ui/button";
import { api } from "@/lib/api";
import { useAuthStore } from "@/lib/auth-store";
interface ProformaItem {
id: string;
proformaNumber: string;
customerName: string;
amount: any;
currency: string;
issueDate: string;
dueDate: string;
description: string;
}
export default function ProformaScreen() {
const nav = useSirouRouter<AppRoutes>();
const [proformas, setProformas] = useState<ProformaItem[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loadingMore, setLoadingMore] = useState(false);
const fetchProformas = useCallback(
async (pageNum: number, isRefresh = false) => {
const { isAuthenticated } = useAuthStore.getState();
if (!isAuthenticated) return;
try {
if (!isRefresh) {
pageNum === 1 ? setLoading(true) : setLoadingMore(true);
}
const response = await api.proforma.getAll({
query: { page: pageNum, limit: 10 },
});
const newData = response.data;
if (isRefresh) {
setProformas(newData);
} else {
setProformas((prev) =>
pageNum === 1 ? newData : [...prev, ...newData],
);
}
setHasMore(response.meta.hasNextPage);
setPage(pageNum);
} catch (err: any) {
console.error("[Proforma] Fetch error:", err);
} finally {
setLoading(false);
setRefreshing(false);
setLoadingMore(false);
}
},
[],
);
useEffect(() => {
fetchProformas(1);
}, [fetchProformas]);
const onRefresh = () => {
setRefreshing(true);
fetchProformas(1, true);
};
const loadMore = () => {
if (hasMore && !loadingMore && !loading) {
fetchProformas(page + 1);
}
};
const renderProformaItem: ListRenderItem<ProformaItem> = ({ item }) => {
const amountVal =
typeof item.amount === "object" ? item.amount.value : item.amount;
const dateStr = new Date(item.issueDate).toLocaleDateString();
return (
<Pressable
key={item.id}
onPress={() => nav.go("proforma/[id]", { id: item.id })}
className="mb-3"
>
<Card className="rounded-[10px] bg-card overflow-hidden">
<View className="p-4">
<View className="flex-row justify-between items-start mb-3">
<View className="bg-primary/10 p-2.5 rounded-[12px] border border-primary/5">
<FileText color="#ea580c" size={20} strokeWidth={2.5} />
</View>
<View className="items-end">
<Text variant="p" className="text-foreground font-bold text-lg">
{item.currency || "$"}
{amountVal?.toLocaleString()}
</Text>
<Text
variant="muted"
className="text-[10px] font-bold uppercase tracking-widest mt-0.5"
>
{item.proformaNumber}
</Text>
</View>
</View>
<Text variant="p" className="text-foreground font-bold mb-1">
{item.customerName}
</Text>
{item.description && (
<Text
variant="muted"
className="text-xs line-clamp-1 mb-4 opacity-70"
>
{item.description}
</Text>
)}
<View className="h-[1px] bg-border/50 mb-4" />
<View className="flex-row justify-between items-center">
<View className="flex-row items-center gap-2">
<View className="p-1 bg-secondary/80 rounded-md">
<Clock color="#64748b" size={12} strokeWidth={2.5} />
</View>
<Text variant="muted" className="text-[11px] font-medium">
Issued: {dateStr}
</Text>
</View>
<Pressable
className="bg-primary/10 px-3.5 py-1.5 rounded-full border border-primary/20 flex-row items-center gap-1.5"
onPress={(e) => {
e.stopPropagation();
// Handle share
}}
>
<Send color="#ea580c" size={12} strokeWidth={2.5} />
<Text className="text-primary text-[11px] font-bold uppercase tracking-tight">
Share
</Text>
</Pressable>
</View>
</View>
</Card>
</Pressable>
);
};
return (
<ScreenWrapper className="bg-background">
<StandardHeader />
<FlatList
data={proformas}
renderItem={renderProformaItem}
keyExtractor={(item) => item.id}
contentContainerStyle={{ padding: 20, paddingBottom: 150 }}
showsVerticalScrollIndicator={false}
onRefresh={onRefresh}
refreshing={refreshing}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListHeaderComponent={
<Button
className="mb-6 h-12 rounded-[14px] bg-primary shadow-lg shadow-primary/30"
onPress={() => nav.go("proforma/create")}
>
<Plus color="white" size={20} strokeWidth={3} />
<Text className="text-white text-sm font-bold uppercase tracking-widest ml-1">
Create New Proforma
</Text>
</Button>
}
ListFooterComponent={
loadingMore ? (
<ActivityIndicator color="#ea580c" className="py-4" />
) : null
}
ListEmptyComponent={
!loading ? (
<View className="py-20 items-center">
<Text variant="muted">No proformas found</Text>
</View>
) : (
<View className="py-20">
<ActivityIndicator size="large" color="#ea580c" />
</View>
)
}
/>
</ScreenWrapper>
);
}