import { auth } from '@/lib/auth' import { prisma } from '@/lib/prisma' import { monthBounds, monthLabel } from '@/lib/utils/dates' import { NetWorthCard } from '@/components/dashboard/NetWorthCard' import { CashFlowCard } from '@/components/dashboard/CashFlowCard' import { RecentTransactions } from '@/components/dashboard/RecentTransactions' import { BudgetSummary } from '@/components/dashboard/BudgetSummary' import { MonthYearPicker } from '@/components/dashboard/MonthYearPicker' type SearchParams = Promise> export default async function DashboardPage({ searchParams }: { searchParams: SearchParams }) { const session = await auth() if (!session?.user?.id) return null const userId = session.user.id const sp = await searchParams const get = (k: string) => (Array.isArray(sp[k]) ? sp[k][0] : sp[k]) ?? '' const now = new Date() const month = Number(get('month')) || (now.getMonth() + 1) const year = Number(get('year')) || now.getFullYear() const isCurrentMonth = month === (now.getMonth() + 1) && year === now.getFullYear() const selectedDate = new Date(year, month - 1, 1) const { start, end } = monthBounds(selectedDate) const [cashFlowRows, budgets, spendRows, recentTx] = await Promise.all([ prisma.$queryRaw<{ type: string; total: bigint }[]>` SELECT t.type, COALESCE(SUM(t."amountCents"), 0)::bigint AS total FROM "Transaction" t JOIN "Account" a ON t."accountId" = a.id WHERE a."userId" = ${userId} AND a.type = 'BANK' AND t.type != 'TRANSFER' AND t.date >= ${start} AND t.date <= ${end} GROUP BY t.type `, prisma.budget.findMany({ where: { userId, isActive: true }, orderBy: { name: 'asc' }, }), prisma.$queryRaw<{ budgetId: string; total: bigint }[]>` SELECT t."budgetId", COALESCE(SUM(CASE WHEN t.type = 'DEBIT' THEN t."amountCents" WHEN t.type = 'CREDIT' THEN -t."amountCents" ELSE 0 END), 0)::bigint AS total FROM "Transaction" t JOIN "Account" a ON t."accountId" = a.id WHERE a."userId" = ${userId} AND t."budgetId" IS NOT NULL AND t.date >= ${start} AND t.date <= ${end} GROUP BY t."budgetId" `, prisma.transaction.findMany({ where: { account: { userId }, date: { gte: start, lte: end }, }, include: { account: { select: { name: true } } }, orderBy: { date: 'desc' }, take: 5, }), ]) // Net worth: live balances for current month, snapshots for past months let netWorthCents: number let netWorthAccounts: { id: string; name: string; balanceCents: number }[] if (isCurrentMonth) { const accounts = await prisma.account.findMany({ where: { userId, isActive: true, type: { in: ['BANK', 'INVESTMENT'] } }, select: { id: true, name: true, currentBalanceCents: true }, orderBy: { name: 'asc' }, }) netWorthAccounts = accounts.map((a) => ({ id: a.id, name: a.name, balanceCents: a.currentBalanceCents })) netWorthCents = netWorthAccounts.reduce((s, a) => s + a.balanceCents, 0) } else { const snapshots = await prisma.$queryRaw<{ accountId: string; name: string; balanceCents: bigint }[]>` SELECT bs."accountId", a.name, bs."balanceCents" FROM "BalanceSnapshot" bs JOIN "Account" a ON bs."accountId" = a.id WHERE a."userId" = ${userId} AND a.type IN ('BANK', 'INVESTMENT') AND bs.year = ${year} AND bs.month = ${month} ` netWorthAccounts = snapshots.map((s) => ({ id: s.accountId, name: s.name, balanceCents: Number(s.balanceCents), })) netWorthCents = netWorthAccounts.reduce((s, a) => s + a.balanceCents, 0) } const cfMap = Object.fromEntries(cashFlowRows.map((r) => [r.type, Number(r.total)])) const creditsCents = cfMap['CREDIT'] ?? 0 const debitsCents = cfMap['DEBIT'] ?? 0 const spendMap = new Map(spendRows.map((r) => [r.budgetId, Number(r.total)])) const recentTransactions = recentTx.map((t) => ({ id: t.id, date: t.date.toISOString(), description: t.description, amountCents: t.amountCents, type: t.type, accountName: t.account.name, })) const budgetsWithSpend = budgets.map((b) => ({ id: b.id, name: b.name, limitCents: b.limitCents, color: b.color, spendCents: spendMap.get(b.id) ?? 0, })) return (

Dashboard

) }