first build commit

This commit is contained in:
2026-04-19 00:44:43 -04:00
parent bc271b7ce1
commit 55debd082b
82 changed files with 6217 additions and 97 deletions

View File

@@ -0,0 +1,104 @@
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'
export default async function DashboardPage() {
const session = await auth()
if (!session?.user?.id) return null
const userId = session.user.id
const { start, end } = monthBounds()
const [accounts, cashFlowRows, budgets, spendRows, recentTx] = await Promise.all([
prisma.account.findMany({
where: { userId, isActive: true },
select: { id: true, name: true, type: true, currentBalanceCents: true },
orderBy: { name: 'asc' },
}),
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.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(t."amountCents"), 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.type = 'DEBIT'
AND t.date >= ${start}
AND t.date <= ${end}
GROUP BY t."budgetId"
`,
prisma.transaction.findMany({
where: { account: { userId } },
include: { account: { select: { name: true } } },
orderBy: { date: 'desc' },
take: 5,
}),
])
const bankAccounts = accounts.filter((a) => a.type === 'BANK')
const netWorthCents = bankAccounts.reduce((s, a) => s + a.currentBalanceCents, 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 (
<div className="p-6 space-y-6">
<div>
<h1 className="text-2xl font-bold">Dashboard</h1>
<p className="text-sm text-muted-foreground">{monthLabel()}</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<NetWorthCard netWorthCents={netWorthCents} bankAccounts={bankAccounts} />
<CashFlowCard
monthLabel={monthLabel()}
creditsCents={creditsCents}
debitsCents={debitsCents}
netCents={creditsCents - debitsCents}
/>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<RecentTransactions transactions={recentTransactions} />
<BudgetSummary budgets={budgetsWithSpend} />
</div>
</div>
)
}