feat: dashboard shows top 5 tickets + invoices with View all links
This commit is contained in:
@@ -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 ── */}
|
||||||
|
|||||||
Reference in New Issue
Block a user