feat: add Merge Notebook feature, stack dissolve/rename, and fix stale view on notebook/tag deletion

This commit is contained in:
chrisf committed 2026-10-22 22:29:50 -06:00
1 parent 507302e6a6
commit 247b63691a
3 files changed
+559 -43

No files matched your search

+73 -1
View File
@@ -6,6 +6,68 @@ export async function POST(req: NextRequest) {
const userId = await getCurrentUserId();
const body = await req.json();
if (body.action === 'merge') {
const sourceNotebookId = body.sourceNotebookId?.trim();
const targetNotebookId = body.targetNotebookId?.trim();
if (!sourceNotebookId || !targetNotebookId) {
return NextResponse.json(
{ error: 'Source and target notebook IDs are required' },
{ status: 400 }
);
}
if (sourceNotebookId === targetNotebookId) {
return NextResponse.json(
{ error: 'Source and target notebooks cannot be the same' },
{ status: 400 }
);
}
const [sourceNb, targetNb] = await Promise.all([
prisma.notebook.findFirst({ where: { id: sourceNotebookId, userId } }),
prisma.notebook.findFirst({ where: { id: targetNotebookId, userId } }),
]);
if (!sourceNb || !targetNb) {
return NextResponse.json(
{ error: 'One or both notebooks not found' },
{ status: 404 }
);
}
// If source notebook was default, make target notebook default
if (sourceNb.isDefault) {
await prisma.notebook.update({
where: { id: targetNotebookId },
data: { isDefault: true },
});
}
// Move all notes (trashed and active) to target notebook
const movedNotes = await prisma.note.updateMany({
where: { notebookId: sourceNotebookId, userId },
data: { notebookId: targetNotebookId },
});
// Update any auto-sort rules that pointed to the source notebook
await prisma.autoSortRule.updateMany({
where: { targetNotebookId: sourceNotebookId, userId },
data: { targetNotebookId },
});
// Delete the source notebook
await prisma.notebook.delete({
where: { id: sourceNotebookId },
});
return NextResponse.json({
success: true,
movedCount: movedNotes.count,
sourceNotebookName: sourceNb.name,
targetNotebookName: targetNb.name,
targetNotebookId,
});
}
if (body.type === 'stack') {
const stackName = body.name?.trim();
if (!stackName) {
@@ -120,6 +182,10 @@ export async function DELETE(req: NextRequest) {
}
if (type === 'stack') {
await prisma.notebook.updateMany({
where: { stackId: id, userId },
data: { stackId: null },
});
await prisma.notebookStack.delete({ where: { id } });
return NextResponse.json({ deleted: true });
}
@@ -134,10 +200,16 @@ export async function DELETE(req: NextRequest) {
// Move notes in this notebook to the default notebook before deleting
await prisma.note.updateMany({
where: { notebookId: id },
where: { notebookId: id, userId },
data: { notebookId: defaultNb.id },
});
// Update any auto-sort rules that pointed to this notebook
await prisma.autoSortRule.updateMany({
where: { targetNotebookId: id, userId },
data: { targetNotebookId: defaultNb.id },
});
await prisma.notebook.delete({ where: { id } });
return NextResponse.json({ deleted: true });
} catch (err: unknown) {