import { redirect } from "next/navigation" import { and, eq, gte } from "drizzle-orm" import { db } from "@/lib/db" import { properties as propertiesTable, rent_payments, expenses as expensesTable } from "@/lib/db/schema" import { getSessionUser } from "@/lib/session" import { getEffectiveOwnerId } from "@/lib/account" import { formatCurrency } from "@/lib/utils" import { TrendingUp, TrendingDown, Building2, DollarSign, Receipt, BarChart3 } from "lucide-react" import { ReportsClient } from "./reports-client" import { CsvExportButton } from "@/components/forms/csv-export-button" export const metadata = { title: "Reports" } export default async function ReportsPage() { const user = await getSessionUser() if (!user) redirect("/login") const ownerId = await getEffectiveOwnerId(user.id) // Last 6 months range const sixMonthsAgo = new Date() sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 5) sixMonthsAgo.setDate(1) const rangeStart = sixMonthsAgo.toISOString().slice(0, 10) const [properties, payments, expenses] = await Promise.all([ db .select({ id: propertiesTable.id, name: propertiesTable.name }) .from(propertiesTable) .where(eq(propertiesTable.user_id, ownerId)), db .select({ amount: rent_payments.amount, status: rent_payments.status, due_date: rent_payments.due_date, property_id: rent_payments.property_id, }) .from(rent_payments) .where(and(eq(rent_payments.user_id, ownerId), gte(rent_payments.due_date, rangeStart))), db .select({ amount: expensesTable.amount, expense_date: expensesTable.expense_date, property_id: expensesTable.property_id, category: expensesTable.category, }) .from(expensesTable) .where(and(eq(expensesTable.user_id, ownerId), gte(expensesTable.expense_date, rangeStart))), ]) // Build monthly buckets for last 6 months const months: { key: string; label: string }[] = [] for (let i = 5; i >= 0; i--) { const d = new Date() d.setMonth(d.getMonth() - i) d.setDate(1) const key = d.toISOString().slice(0, 7) const label = d.toLocaleDateString("en-US", { month: "short", year: "2-digit" }) months.push({ key, label }) } // Monthly revenue & expenses const monthlyData = months.map(({ key, label }) => { const revenue = (payments ?? []).filter(p => p.status === "paid" && p.due_date?.startsWith(key)).reduce((s, p) => s + Number(p.amount), 0) const expense = (expenses ?? []).filter(e => e.expense_date?.startsWith(key)).reduce((s, e) => s + Number(e.amount), 0) return { key, label, revenue, expense, net: revenue - expense } }) // Per-property P&L const propertyPnL = (properties ?? []).map((p: any) => { const revenue = (payments ?? []).filter(pm => pm.status === "paid" && pm.property_id === p.id).reduce((s, pm) => s + Number(pm.amount), 0) const expense = (expenses ?? []).filter(e => e.property_id === p.id).reduce((s, e) => s + Number(e.amount), 0) const net = revenue - expense const margin = revenue > 0 ? Math.round((net / revenue) * 100) : 0 return { ...p, revenue, expense, net, margin } }).sort((a, b) => b.net - a.net) // Summary totals const totalRevenue = (payments ?? []).filter(p => p.status === "paid").reduce((s, p) => s + Number(p.amount), 0) const totalExpenses = (expenses ?? []).reduce((s, e) => s + Number(e.amount), 0) const totalNet = totalRevenue - totalExpenses const avgMargin = totalRevenue > 0 ? Math.round((totalNet / totalRevenue) * 100) : 0 return (
{/* Header + CSV exports */}

Reports

Revenue, expenses & profit — last 6 months

{/* Summary KPI cards */}
{[ { label: "Total Revenue", value: formatCurrency(totalRevenue), icon: DollarSign, color: "text-emerald-400", bg: "bg-emerald-500/10" }, { label: "Total Expenses", value: formatCurrency(totalExpenses), icon: Receipt, color: "text-rose-400", bg: "bg-rose-500/10" }, { label: "Net Income", value: formatCurrency(totalNet), icon: totalNet >= 0 ? TrendingUp : TrendingDown, color: totalNet >= 0 ? "text-emerald-400" : "text-red-400", bg: totalNet >= 0 ? "bg-emerald-500/10" : "bg-red-500/10" }, { label: "Profit Margin", value: `${avgMargin}%`, icon: BarChart3, color: "text-indigo-400", bg: "bg-indigo-500/10" }, ].map((s) => (

{s.label}

{s.value}

Last 6 months

))}
{/* Client component for interactive charts */}
) }