Added reports table to PostgreSQL and Drizzle schema. Rewrote reports API routes to use Drizzle ORM instead of PocketBase stub. Fixed shared schema to match frontend field names. Fixed TipTap v3 type errors in note-editor.tsx and report-editor.tsx (StarterKit cast+chain as any).
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { withAuth, createErrorResponse } from '@/lib/auth';
|
|
import { db, reports } from '@project-e/db';
|
|
import { eq } from 'drizzle-orm';
|
|
import { z } from 'zod';
|
|
|
|
const updateReportSchema = z.object({
|
|
title: z.string().min(1, 'Title is required').optional(),
|
|
content: z.string().optional(),
|
|
report_type: z.enum(['weekly', 'monthly', 'project', 'habit', 'custom']).optional(),
|
|
date_range_start: z.string().optional(),
|
|
date_range_end: z.string().optional(),
|
|
domain: z.string().optional(),
|
|
is_draft: z.boolean().optional(),
|
|
});
|
|
|
|
type RouteContext = { params: Promise<{ id: string }> };
|
|
|
|
// GET /api/reports/[id] — Get a single report
|
|
export const GET = withAuth<RouteContext>(async (request: NextRequest, _user, context) => {
|
|
const { id } = await context!.params;
|
|
|
|
const [report] = await db.select()
|
|
.from(reports)
|
|
.where(eq(reports.id, id))
|
|
.limit(1);
|
|
|
|
if (!report) {
|
|
return createErrorResponse('NOT_FOUND', 'Report not found', 404);
|
|
}
|
|
|
|
return NextResponse.json(report);
|
|
});
|
|
|
|
// PATCH /api/reports/[id] — Update a report
|
|
export const PATCH = withAuth<RouteContext>(async (request: NextRequest, _user, context) => {
|
|
try {
|
|
const { id } = await context!.params;
|
|
const body = await request.json();
|
|
const data = updateReportSchema.parse(body);
|
|
|
|
const updateValues: Record<string, any> = {};
|
|
if (data.title !== undefined) updateValues.title = data.title;
|
|
if (data.content !== undefined) updateValues.content = data.content;
|
|
if (data.report_type !== undefined) updateValues.reportType = data.report_type;
|
|
if (data.date_range_start !== undefined) updateValues.dateRangeStart = new Date(data.date_range_start);
|
|
if (data.date_range_end !== undefined) updateValues.dateRangeEnd = new Date(data.date_range_end);
|
|
if (data.domain !== undefined) updateValues.domain = data.domain;
|
|
if (data.is_draft !== undefined) updateValues.isDraft = data.is_draft;
|
|
updateValues.updatedAt = new Date();
|
|
|
|
const [report] = await db.update(reports)
|
|
.set(updateValues)
|
|
.where(eq(reports.id, id))
|
|
.returning();
|
|
|
|
if (!report) {
|
|
return createErrorResponse('NOT_FOUND', 'Report not found', 404);
|
|
}
|
|
|
|
return NextResponse.json(report);
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return createErrorResponse('VALIDATION_ERROR', 'Invalid input', 400, error.issues);
|
|
}
|
|
throw error;
|
|
}
|
|
});
|
|
|
|
// DELETE /api/reports/[id] — Delete a report
|
|
export const DELETE = withAuth<RouteContext>(async (request: NextRequest, _user, context) => {
|
|
const { id } = await context!.params;
|
|
|
|
const [deleted] = await db.delete(reports)
|
|
.where(eq(reports.id, id))
|
|
.returning({ id: reports.id });
|
|
|
|
if (!deleted) {
|
|
return createErrorResponse('NOT_FOUND', 'Report not found', 404);
|
|
}
|
|
|
|
return new NextResponse(null, { status: 204 });
|
|
});
|