- #10 Ramble box: new /ramble sticky-note board (post/pin/archive/delete), ported from FMB. New Ramble model + migration, /api/rambles routes, nav entry. - #6 Dark-mode toggle: sun/moon button in the top bar (dark palette already existed in CSS, just had no control). - #9 Pantry pull reason: structured disposition (Immediate use / Preserved / Gave away / Waste) on ItemLog.reason, added to single + bulk pull dialogs and the audit payload. New migration. - #2 Suggestions: drop the stale white-card wrapper; render SuggestionsPanel bare like FMB so it's readable and thumbnails show. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { z } from "zod";
|
|
import { db } from "@/lib/db";
|
|
import { requireAuth } from "@/lib/auth";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const patchSchema = z.object({
|
|
body: z.string().trim().min(1).max(5000).optional(),
|
|
pinned: z.boolean().optional(),
|
|
archived: z.boolean().optional(),
|
|
});
|
|
|
|
// PATCH /api/rambles/:id — edit body, pin/unpin, or archive/restore.
|
|
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
|
|
let session;
|
|
try {
|
|
session = await requireAuth();
|
|
} catch {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
let input;
|
|
try {
|
|
input = patchSchema.parse(await req.json());
|
|
} catch (err) {
|
|
const msg = err instanceof z.ZodError ? err.errors[0].message : "Invalid input";
|
|
return NextResponse.json({ error: msg }, { status: 400 });
|
|
}
|
|
|
|
const data: { body?: string; pinned?: boolean; archivedAt?: Date | null } = {};
|
|
if (input.body !== undefined) data.body = input.body;
|
|
if (input.pinned !== undefined) data.pinned = input.pinned;
|
|
if (input.archived !== undefined) data.archivedAt = input.archived ? new Date() : null;
|
|
|
|
try {
|
|
const ramble = await db.ramble.update({ where: { id: params.id }, data });
|
|
await db.auditEvent.create({
|
|
data: { action: "ramble.update", entityId: ramble.id, actorId: session.user.id, payload: input },
|
|
});
|
|
return NextResponse.json(ramble);
|
|
} catch {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
}
|
|
|
|
// DELETE /api/rambles/:id — hard delete (a ramble is disposable by design).
|
|
export async function DELETE(_req: Request, { params }: { params: { id: string } }) {
|
|
let session;
|
|
try {
|
|
session = await requireAuth();
|
|
} catch {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
try {
|
|
await db.ramble.delete({ where: { id: params.id } });
|
|
await db.auditEvent.create({
|
|
data: { action: "ramble.delete", entityId: params.id, actorId: session.user.id },
|
|
});
|
|
return NextResponse.json({ ok: true });
|
|
} catch {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
}
|