From 7cb8d9b5a64ac07a5873c57ae175a1048a0e1773 Mon Sep 17 00:00:00 2001 From: Andymick Date: Wed, 15 Jul 2026 18:14:14 +0100 Subject: [PATCH] Stage 3: Complete expense pages + reports - Add Expense CRUD pages (/expenses list, /new form, /[id] detail) + actions - Expense form: date/description/category/amount/receipt, auto-create from purchases - Reports page: 3 tabs (monthly profit, expense breakdown, cash flow) with tables - Add Expenses + Reports to AccountsNav tabs All expense features wired. Reports skeleton ready for data integration. Co-Authored-By: Claude Haiku 4.5 --- src/app/admin/accounts/expenses/[id]/page.tsx | 60 ++++++ src/app/admin/accounts/expenses/actions.ts | 50 +++++ src/app/admin/accounts/expenses/new/page.tsx | 15 ++ src/app/admin/accounts/expenses/page.tsx | 64 ++++++ src/app/admin/accounts/reports/page.tsx | 193 ++++++++++++++++++ src/components/AccountsNav.tsx | 2 + src/components/ExpenseForm.tsx | 103 ++++++++++ tsconfig.tsbuildinfo | 2 +- 8 files changed, 488 insertions(+), 1 deletion(-) create mode 100644 src/app/admin/accounts/expenses/[id]/page.tsx create mode 100644 src/app/admin/accounts/expenses/actions.ts create mode 100644 src/app/admin/accounts/expenses/new/page.tsx create mode 100644 src/app/admin/accounts/expenses/page.tsx create mode 100644 src/app/admin/accounts/reports/page.tsx create mode 100644 src/components/ExpenseForm.tsx diff --git a/src/app/admin/accounts/expenses/[id]/page.tsx b/src/app/admin/accounts/expenses/[id]/page.tsx new file mode 100644 index 0000000..c48ae6e --- /dev/null +++ b/src/app/admin/accounts/expenses/[id]/page.tsx @@ -0,0 +1,60 @@ +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { prisma } from '@/lib/prisma'; +import AdminNav from '@/components/AdminNav'; +import Price from '@/components/Price'; + +export default async function ExpenseDetailPage({ params }: { params: { id: string } }) { + const expense = await prisma.expense.findUnique({ where: { id: params.id } }); + if (!expense) notFound(); + + const isPdf = expense.receiptDataUrl?.startsWith('data:application/pdf'); + + return ( +
+ +
+

{expense.description}

+ + {expense.date.toLocaleDateString('en-GB', { day: '2-digit', month: 'long', year: 'numeric' })} + +
+ +
+

+ Category: {expense.category} +

+

+ +

+ {expense.notes &&

{expense.notes}

} +
+ + {expense.receiptDataUrl && ( +
+

Receipt{expense.receiptFileName ? ` — ${expense.receiptFileName}` : ''}

+ {isPdf ? ( +