feat: dashboard shows top 5 tickets + invoices with View all links

This commit is contained in:
Nemo
2026-03-24 15:09:06 +08:00
parent 882e416a40
commit 5123d8c114

View File

@@ -171,7 +171,7 @@ export default function DashboardScreen() {
{ {
queryKey: ['dashboard-tickets'], queryKey: ['dashboard-tickets'],
queryFn: async () => { queryFn: async () => {
const res = await api.get('/api/v1/tickets?limit=50'); const res = await api.get('/api/v1/tickets?limit=20');
const all: any[] = res.data?.data ?? res.data ?? []; const all: any[] = res.data?.data ?? res.data ?? [];
return all.filter((t: any) => t.status === 'OPEN' || t.status === 'IN_PROGRESS'); return all.filter((t: any) => t.status === 'OPEN' || t.status === 'IN_PROGRESS');
}, },
@@ -179,7 +179,7 @@ export default function DashboardScreen() {
{ {
queryKey: ['dashboard-invoices'], queryKey: ['dashboard-invoices'],
queryFn: async () => { queryFn: async () => {
const res = await api.get('/api/v1/invoices?limit=50'); const res = await api.get('/api/v1/invoices?limit=20');
const all: any[] = res.data?.data ?? res.data ?? []; const all: any[] = res.data?.data ?? res.data ?? [];
const unpaid = all.filter((inv: any) => ['SENT','PARTIAL','OVERDUE'].includes(inv.status) && Number(inv.balance) > 0); const unpaid = all.filter((inv: any) => ['SENT','PARTIAL','OVERDUE'].includes(inv.status) && Number(inv.balance) > 0);
unpaid.sort((a: any, b: any) => { unpaid.sort((a: any, b: any) => {
@@ -187,7 +187,7 @@ export default function DashboardScreen() {
const db = b.dueDate ? new Date(b.dueDate).getTime() : Infinity; const db = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
return da - db; return da - db;
}); });
return unpaid.slice(0, 10); return unpaid.slice(0, 5);
}, },
}, },
{ {
@@ -213,7 +213,7 @@ export default function DashboardScreen() {
const seen = new Set<string>(); const seen = new Set<string>();
const mergedTickets = [...assignedToMe, ...unassigned].filter((t: any) => { const mergedTickets = [...assignedToMe, ...unassigned].filter((t: any) => {
if (seen.has(t.id)) return false; seen.add(t.id); return true; if (seen.has(t.id)) return false; seen.add(t.id); return true;
}).slice(0, 10).sort((a: any, b: any) => (a.priority === 'HIGH' ? -1 : 1) - (b.priority === 'HIGH' ? -1 : 1)); }).slice(0, 5).sort((a: any, b: any) => (a.priority === 'HIGH' ? -1 : 1) - (b.priority === 'HIGH' ? -1 : 1));
const refetchAll = () => { summaryQ.refetch(); ticketsQ.refetch(); invoicesQ.refetch(); leadsQ.refetch(); }; const refetchAll = () => { summaryQ.refetch(); ticketsQ.refetch(); invoicesQ.refetch(); leadsQ.refetch(); };
@@ -274,10 +274,13 @@ export default function DashboardScreen() {
<Text style={{ fontSize: 15, color: '#94A3B8' }}>No active tickets 🎉</Text> <Text style={{ fontSize: 15, color: '#94A3B8' }}>No active tickets 🎉</Text>
</View> </View>
) : ( ) : (
<View style={{ marginBottom: 20 }}> <View style={{ marginBottom: 4 }}>
{mergedTickets.map((t: any) => ( {mergedTickets.map((t: any) => (
<TicketRow key={t.id} task={t} onPress={() => router.push(`/(app)/tasks/${t.id}`)} /> <TicketRow key={t.id} task={t} onPress={() => router.push(`/(app)/tasks/${t.id}`)} />
))} ))}
<TouchableOpacity onPress={() => router.push('/(app)/tasks')} style={{ alignItems: 'center', paddingVertical: 10, marginBottom: 12 }}>
<Text style={{ fontSize: 13, color: '#0891B2', fontWeight: '600' }}>View all tickets →</Text>
</TouchableOpacity>
</View> </View>
)} )}
@@ -293,10 +296,13 @@ export default function DashboardScreen() {
<Text style={{ fontSize: 15, color: '#94A3B8' }}>No unpaid invoices 🎉</Text> <Text style={{ fontSize: 15, color: '#94A3B8' }}>No unpaid invoices 🎉</Text>
</View> </View>
) : ( ) : (
<View style={{ marginBottom: 20 }}> <View style={{ marginBottom: 4 }}>
{(unpaidInvoices as any[]).map((inv: any) => ( {(unpaidInvoices as any[]).map((inv: any) => (
<InvoiceRow key={inv.id} inv={inv} onPay={() => openPay(inv)} /> <InvoiceRow key={inv.id} inv={inv} onPay={() => openPay(inv)} />
))} ))}
<TouchableOpacity onPress={() => router.push('/(app)/payments')} style={{ alignItems: 'center', paddingVertical: 10, marginBottom: 12 }}>
<Text style={{ fontSize: 13, color: '#0891B2', fontWeight: '600' }}>View all unpaid invoices →</Text>
</TouchableOpacity>
</View> </View>
)} )}
{/* ── Leads ── */} {/* ── Leads ── */}