Add paycheck tracking workflow for v1
This commit is contained in:
@@ -1,15 +1,21 @@
|
||||
import { PaycheckWorkspace } from "@/components/paycheck-workspace";
|
||||
|
||||
export const metadata = {
|
||||
title: "Income & Paychecks | Monthy Tracker",
|
||||
};
|
||||
|
||||
export default function IncomePage() {
|
||||
return (
|
||||
<div className="rounded-[2rem] border border-stone-200 bg-white p-8 shadow-[0_24px_60px_rgba(120,90,50,0.08)]">
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-stone-500">Coming next</p>
|
||||
<h1 className="mt-3 text-4xl font-semibold text-stone-950">Paycheck tracking lands in the next implementation slice.</h1>
|
||||
<p className="mt-4 max-w-2xl text-lg leading-8 text-stone-600">
|
||||
The data model is already prepared for paychecks. This view will add create, list, and delete flows after expense tracking is validated.
|
||||
</p>
|
||||
<div className="space-y-8">
|
||||
<header className="max-w-2xl space-y-3">
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-emerald-700">Income & Paychecks</p>
|
||||
<h1 className="text-4xl font-semibold text-stone-950">Capture income on real pay dates, not rough monthly averages.</h1>
|
||||
<p className="text-lg leading-8 text-stone-600">
|
||||
This slice tracks each paycheck as a distinct event so later dashboard and AI guidance can reason about cash timing accurately.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<PaycheckWorkspace />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
23
src/app/paychecks/[id]/route.ts
Normal file
23
src/app/paychecks/[id]/route.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { removePaycheck } from "@/lib/paychecks";
|
||||
|
||||
type RouteContext = {
|
||||
params: Promise<{ id: string }>;
|
||||
};
|
||||
|
||||
export async function DELETE(_: Request, context: RouteContext) {
|
||||
const { id } = await context.params;
|
||||
|
||||
try {
|
||||
await removePaycheck(id);
|
||||
return new NextResponse(null, { status: 204 });
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2025") {
|
||||
return NextResponse.json({ error: "Paycheck not found." }, { status: 404 });
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
28
src/app/paychecks/route.ts
Normal file
28
src/app/paychecks/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { createPaycheck, listPaychecks } from "@/lib/paychecks";
|
||||
import { paycheckInputSchema } from "@/lib/validation";
|
||||
|
||||
export async function GET() {
|
||||
const paychecks = await listPaychecks();
|
||||
return NextResponse.json({ paychecks });
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const payload = await request.json();
|
||||
const parsed = paycheckInputSchema.safeParse(payload);
|
||||
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json(
|
||||
{ error: parsed.error.issues[0]?.message ?? "Invalid paycheck payload." },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const paycheck = await createPaycheck({
|
||||
amountCents: parsed.data.amount,
|
||||
payDate: parsed.data.payDate,
|
||||
});
|
||||
|
||||
return NextResponse.json({ paycheck }, { status: 201 });
|
||||
}
|
||||
Reference in New Issue
Block a user