diff --git a/app/(app)/reports/page.tsx b/app/(app)/reports/page.tsx index abb8f18..f416ecd 100644 --- a/app/(app)/reports/page.tsx +++ b/app/(app)/reports/page.tsx @@ -1,168 +1,193 @@ 'use client'; +import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; +import { api } from '@/lib/api'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; -import { api } from '@/lib/api'; -import { - BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, - PieChart, Pie, Cell, Legend, -} from 'recharts'; +import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts'; -const PIE_COLORS = ['#0891B2', '#059669', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899']; +const peso = (v: number) => '₱' + (v ?? 0).toLocaleString('en-PH', { minimumFractionDigits: 2 }); export default function ReportsPage() { - const { data: collection, isLoading: loadingCollection } = useQuery({ - queryKey: ['reports-collection'], - queryFn: async () => (await api.get('/api/v1/reports/collection')).data, + const [from, setFrom] = useState(() => { + const d = new Date(); + d.setDate(1); + return d.toISOString().split('T')[0]; + }); + const [to, setTo] = useState(() => new Date().toISOString().split('T')[0]); + + const { data: collection, isLoading: collLoading } = useQuery({ + queryKey: ['reports', 'collection', from, to], + queryFn: async () => { + const res = await api.get(`/api/v1/reports/collection?from=${from}&to=${to}`); + return res.data as Array<{ collector: string; totalAmount: number; paymentCount: number }>; + }, }); - const { data: aging, isLoading: loadingAging } = useQuery({ - queryKey: ['reports-aging'], - queryFn: async () => (await api.get('/api/v1/reports/aging')).data, + const { data: aging, isLoading: agingLoading } = useQuery({ + queryKey: ['reports', 'aging'], + queryFn: async () => { + const res = await api.get('/api/v1/reports/aging'); + return res.data as Array<{ bucket: string; invoiceCount: number; totalAmount: number }>; + }, }); - const { data: subscribers, isLoading: loadingSubs } = useQuery({ - queryKey: ['reports-subscribers'], - queryFn: async () => (await api.get('/api/v1/reports/subscribers')).data, + const { data: subscribers, isLoading: subLoading } = useQuery({ + queryKey: ['reports', 'subscribers'], + queryFn: async () => { + const res = await api.get('/api/v1/reports/subscribers'); + return res.data as Array<{ plan: string; count: number; revenue: number }>; + }, }); - const { data: revenue, isLoading: loadingRevenue } = useQuery({ - queryKey: ['reports-revenue'], - queryFn: async () => (await api.get('/api/v1/reports/revenue')).data, + const { data: revenue, isLoading: revLoading } = useQuery({ + queryKey: ['reports', 'revenue'], + queryFn: async () => { + const res = await api.get('/api/v1/reports/revenue'); + return res.data as Array<{ month: string; total: number }>; + }, }); - const { data: plans, isLoading: loadingPlans } = useQuery({ - queryKey: ['reports-plans'], - queryFn: async () => (await api.get('/api/v1/reports/plans')).data, - }); - - const collectionList = Array.isArray(collection) ? collection : []; - const agingList = Array.isArray(aging) ? aging : []; - const revList = Array.isArray(revenue) ? revenue : []; - const planList = Array.isArray(plans) ? plans : []; - - // Subscribers summary - const subSummary = subscribers ? [ - { name: 'Active', value: (subscribers as any).active ?? 0 }, - { name: 'Pending', value: (subscribers as any).pending ?? 0 }, - { name: 'Suspended', value: (subscribers as any).suspended ?? 0 }, - { name: 'Disconnected', value: (subscribers as any).disconnected ?? 0 }, - ].filter(s => s.value > 0) : []; - return (
Business performance overview
+Financial and operational analytics
No collections in this period
+ ) : ( +| Collector | +Payments | +Total | +
|---|---|---|
| {row.collector} | +{row.paymentCount} | +{peso(row.totalAmount)} | +
| Total | ++ {collection?.reduce((s, r) => s + r.paymentCount, 0)} + | ++ {peso(collection?.reduce((s, r) => s + r.totalAmount, 0) ?? 0)} + | +
No data
- ) : ( -No outstanding invoices
- ) : ( -No collection data
- ) : ( + {agingLoading ?{c.collector}
-{c.paymentCount} payments
+ {aging?.map((bucket) => ( +₱{Number(c.totalAmount).toLocaleString()}
+ {peso(bucket.totalAmount)} + {bucket.invoiceCount} invNo overdue invoices 🎉
+ )}No data
- ) : ( -{p.planName ?? p.name ?? 'Plan ' + (i+1)}
-{p.count}
-| Plan | +Subscribers | +Monthly Revenue | +
|---|---|---|
| {row.plan ?? row.name ?? '—'} | +{row.count ?? row.subscribers ?? 0} | ++ {peso(row.revenue ?? row.monthlyRevenue ?? 0)} + | +