"use client"; export const dynamic = "force-dynamic"; import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, Legend, LineChart, Line, CartesianGrid } from "recharts"; import { RefreshCw, TrendingUp, Users, AlertTriangle, DollarSign } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { Button } from "@/components/ui/Button"; import { Badge } from "@/components/ui/Badge"; import { formatCurrency } from "@/lib/utils"; import api from "@/lib/api"; const COLORS = ["#0891B2", "#059669", "#F59E0B", "#EF4444", "#8B5CF6", "#EC4899"]; function KpiCard({ title, value, sub, icon: Icon, color }: { title: string; value: string; sub?: string; icon: any; color: string }) { return (

{title}

{value}

{sub &&

{sub}

}
); } export default function ReportsPage() { const today = new Date(); const firstOfMonth = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().split("T")[0]; const [from, setFrom] = useState(firstOfMonth); const [to, setTo] = useState(today.toISOString().split("T")[0]); const { data: collection = [], isLoading: collLoading, refetch: refetchAll } = 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 = [] } = 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 = [] } = useQuery({ queryKey: ["reports-subscribers"], queryFn: async () => { const res = await api.get("/api/v1/reports/subscribers"); return res.data as Array<{ status: string; count: number; area?: string; plan?: string }>; }, }); const { data: revenue = [] } = useQuery({ queryKey: ["reports-revenue"], queryFn: async () => { const res = await api.get("/api/v1/reports/revenue"); return (res.data as Array<{ month: string; revenue: number; totalInvoiced: number }>) .filter(r => r.revenue > 0 || r.totalInvoiced > 0) .slice(-12); }, }); // Derived KPIs const totalCollected = collection.reduce((s, c) => s + Number(c.totalAmount), 0); const totalPayments = collection.reduce((s, c) => s + c.paymentCount, 0); const totalOutstanding = aging.reduce((s, a) => s + Number(a.totalAmount), 0); const overdueCount = aging.reduce((s, a) => s + a.invoiceCount, 0); // Subscriber summary (status-only rows, no area key) const subByStatus = subscribers.filter(s => !s.area && !s.plan); const activeCount = subByStatus.find(s => s.status === "ACTIVE")?.count ?? 0; const pendingCount = subByStatus.find(s => s.status === "PENDING")?.count ?? 0; const suspendedCount = subByStatus.find(s => s.status === "SUSPENDED")?.count ?? 0; const totalSubs = subByStatus.reduce((s, x) => s + x.count, 0); // Subscriber by area (rows with area key) const subByArea = subscribers.filter(s => !!s.area); const agingRisk: Record = { "0-30": "text-yellow-600", "31-60": "text-orange-600", "61-90": "text-red-500", "90+": "text-red-700" }; return (

Reports

Financial and operational analytics

setFrom(e.target.value)} className="border rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" /> setTo(e.target.value)} className="border rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
{/* KPI Summary */}
{/* Collection Report */}
Collection by Collector {collLoading ?
: collection.length === 0 ?

No collection data for this period

: ( <>
{collection.map((c, i) => (

{c.collector}

{c.paymentCount} payments

{formatCurrency(c.totalAmount)}

{totalCollected > 0 ? ((c.totalAmount / totalCollected) * 100).toFixed(1) : 0}%

))}
Total {formatCurrency(totalCollected)}
`₱${(v/1000).toFixed(0)}k`} /> formatCurrency(Number(v))} /> ) } {/* Aging Report */} Accounts Receivable Aging
{aging.map((a) => (

{a.bucket} days overdue

{a.invoiceCount} invoice{a.invoiceCount !== 1 ? "s" : ""}

0 ? agingRisk[a.bucket] ?? "text-gray-800" : "text-gray-400"}`}> {formatCurrency(a.totalAmount)}

))}
Total Outstanding {formatCurrency(totalOutstanding)}
{/* Revenue Trend */} {revenue.length > 0 && ( Revenue Trend (Monthly) `₱${(v/1000).toFixed(0)}k`} /> formatCurrency(Number(v))} /> )} {/* Subscribers by Status + Area */}
Subscribers by Status {subByStatus.length === 0 ?

No subscriber data

: (
{subByStatus.map((_, i) => )}
{subByStatus.map((s, i) => (
{s.status} {s.count}
))}
Total{totalSubs}
)} {subByArea.length > 0 && ( Active Subscribers by Area
{subByArea.map((a) => (
{a.area}
{a.count}
))}
)}
); }