195 lines
7.5 KiB
TypeScript
195 lines
7.5 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 { Text } from "@/components/ui/text";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import {
|
|
FileText,
|
|
ChevronRight,
|
|
TrendingUp,
|
|
TrendingDown,
|
|
Clock,
|
|
} from "@/lib/icons";
|
|
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
|
import { ShadowWrapper } from "@/components/ShadowWrapper";
|
|
import { StandardHeader } from "@/components/StandardHeader";
|
|
import { EmptyState } from "@/components/EmptyState";
|
|
import { api } from "@/lib/api";
|
|
import { Stack } from "expo-router";
|
|
|
|
export default function HistoryScreen() {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const [loading, setLoading] = useState(true);
|
|
const [stats, setStats] = useState({ totalRevenue: 0, pending: 0 });
|
|
const [invoices, setInvoices] = useState<any[]>([]);
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
const fetchData = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const [statsRes, invoicesRes] = await Promise.all([
|
|
api.invoices.stats(),
|
|
api.invoices.getAll({ query: { limit: 100 } }),
|
|
]);
|
|
setStats(statsRes);
|
|
setInvoices(invoicesRes.data || []);
|
|
} catch (error) {
|
|
console.error("[HistoryScreen] Error fetching history:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<Stack.Screen options={{ headerShown: false }} />
|
|
<StandardHeader title="Activity History" showBack />
|
|
|
|
<ScrollView
|
|
className="flex-1"
|
|
contentContainerStyle={{ padding: 16, paddingBottom: 150 }}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View className="flex-row gap-4 mb-10">
|
|
<ShadowWrapper className="flex-1">
|
|
<View className="bg-card rounded-[10px] p-4 border border-border/5">
|
|
<View className="h-10 w-10 bg-emerald-500/10 rounded-[8px] items-center justify-center mb-3">
|
|
<TrendingUp color="#10b981" size={20} strokeWidth={2.5} />
|
|
</View>
|
|
<Text
|
|
variant="muted"
|
|
className="font-bold text-[10px] uppercase tracking-widest opacity-60"
|
|
>
|
|
Total Inflow
|
|
</Text>
|
|
<Text variant="h3" className="text-foreground font-black mt-1">
|
|
${stats.totalRevenue.toLocaleString()}
|
|
</Text>
|
|
</View>
|
|
</ShadowWrapper>
|
|
|
|
<ShadowWrapper className="flex-1">
|
|
<View className="bg-card rounded-[10px] p-4 border border-border/5">
|
|
<View className="h-10 w-10 bg-amber-500/10 rounded-[8px] items-center justify-center mb-3">
|
|
<TrendingDown color="#f59e0b" size={20} strokeWidth={2.5} />
|
|
</View>
|
|
<Text
|
|
variant="muted"
|
|
className="font-bold text-[10px] uppercase tracking-widest opacity-60"
|
|
>
|
|
Pending
|
|
</Text>
|
|
<Text variant="h3" className="text-foreground font-black mt-1">
|
|
${stats.pending.toLocaleString()}
|
|
</Text>
|
|
</View>
|
|
</ShadowWrapper>
|
|
</View>
|
|
|
|
<Text variant="h4" className="text-foreground mb-4 tracking-tight">
|
|
All Activity
|
|
</Text>
|
|
|
|
{loading ? (
|
|
<View className="py-20 items-center">
|
|
<ActivityIndicator color="#ea580c" />
|
|
</View>
|
|
) : (
|
|
<View className="gap-2">
|
|
{invoices.length > 0 ? (
|
|
invoices.map((inv) => (
|
|
<Pressable
|
|
key={inv.id}
|
|
onPress={() => nav.go("invoices/[id]", { id: inv.id })}
|
|
>
|
|
<ShadowWrapper level="xs">
|
|
<Card className="rounded-[8px] bg-card overflow-hidden border-0">
|
|
<CardContent className="flex-row items-center py-4 px-3">
|
|
<View className="bg-secondary/40 h-10 w-10 rounded-[8px] items-center justify-center mr-3 border border-border/10">
|
|
<FileText
|
|
size={20}
|
|
color="#ea580c"
|
|
strokeWidth={2.5}
|
|
/>
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text
|
|
variant="p"
|
|
className="text-foreground font-bold"
|
|
>
|
|
{inv.customerName}
|
|
</Text>
|
|
<Text
|
|
variant="muted"
|
|
className="text-[11px] font-medium opacity-60"
|
|
>
|
|
{new Date(inv.issueDate).toLocaleDateString()} ·
|
|
Proforma
|
|
</Text>
|
|
</View>
|
|
<View className="items-end">
|
|
<Text
|
|
variant="p"
|
|
className="text-foreground font-black"
|
|
>
|
|
${Number(inv.amount).toLocaleString()}
|
|
</Text>
|
|
<View
|
|
className={`mt-1 rounded-[5px] px-2.5 py-1 border border-border/20 ${
|
|
inv.status === "PAID"
|
|
? "bg-emerald-500/10"
|
|
: inv.status === "PENDING"
|
|
? "bg-amber-500/10"
|
|
: inv.status === "DRAFT"
|
|
? "bg-secondary/50"
|
|
: "bg-red-500/10"
|
|
}`}
|
|
>
|
|
<Text
|
|
className={`text-[8px] font-black uppercase tracking-widest ${
|
|
inv.status === "PAID"
|
|
? "text-emerald-600"
|
|
: inv.status === "PENDING"
|
|
? "text-amber-600"
|
|
: inv.status === "DRAFT"
|
|
? "text-muted-foreground"
|
|
: "text-red-600"
|
|
}`}
|
|
>
|
|
{inv.status}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<ChevronRight
|
|
size={14}
|
|
color="#94a3b8"
|
|
className="ml-2"
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
</ShadowWrapper>
|
|
</Pressable>
|
|
))
|
|
) : (
|
|
<View className="py-6">
|
|
<EmptyState
|
|
title="No activity yet"
|
|
description="Payments and invoices you create will show up here so you can track everything in one place."
|
|
hint="Create a proforma invoice to generate your first activity."
|
|
actionLabel="Create Proforma"
|
|
onActionPress={() => nav.go("proforma/create")}
|
|
previewLines={4}
|
|
/>
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|