Organize/lib/board.ts

95 lines
3.0 KiB
TypeScript

import "server-only";
import { prisma } from "@/lib/db";
import { categoryAccessFilter } from "@/lib/access";
import type { CategoryDTO } from "@/types/board";
import type { AiGroupContext } from "@/types/ai";
/**
* Fetches one board's worth of categories/groups/todos, shaped for the
* client -- shared by the Home board and every Project board, which are
* identical in every way except which categories they scope to.
*/
export async function getBoard(
where: { userId: string; projectId: null } | { projectId: string }
): Promise<CategoryDTO[]> {
const categories = await prisma.category.findMany({
where,
orderBy: { order: "asc" },
include: {
// Archived groups are kept in the database (not deleted) but
// excluded from the board query.
groups: {
where: { archivedAt: null },
orderBy: { order: "asc" },
include: { todos: { orderBy: { order: "asc" } } },
},
},
});
return categories.map((category) => ({
id: category.id,
name: category.name,
order: category.order,
groups: category.groups.map((group) => ({
id: group.id,
title: group.title,
color: group.color,
order: group.order,
noteContent: group.noteContent,
categoryId: group.categoryId,
todos: group.todos.map((todo) => ({
id: todo.id,
title: todo.title,
details: todo.details,
completed: todo.completed,
order: todo.order,
groupId: todo.groupId,
createdAt: todo.createdAt.toISOString(),
updatedAt: todo.updatedAt.toISOString(),
completedAt: todo.completedAt?.toISOString() ?? null,
})),
})),
}));
}
/**
* Every group the user can see across every board they own -- the Home
* board plus every Project (see lib/access.ts's categoryAccessFilter) --
* flattened into one list for feeding to the AI as context (see
* lib/ai/context.ts). Unlike getBoard, archived groups are deliberately
* *not* filtered out: the global chat feature needs to answer historical
* questions about finished work, not just describe what's currently on
* screen. One query, not one getBoard() call per project.
*/
export async function getAllGroupsForUser(userId: string): Promise<AiGroupContext[]> {
const categories = await prisma.category.findMany({
where: categoryAccessFilter(userId),
orderBy: { order: "asc" },
include: {
project: { select: { title: true } },
groups: {
orderBy: { order: "asc" },
include: { todos: { orderBy: { order: "asc" } } },
},
},
});
return categories.flatMap((category) =>
category.groups.map((group) => ({
groupId: group.id,
groupTitle: group.title,
scopeLabel: category.project?.title ?? "Home",
categoryName: category.name,
archived: group.archivedAt !== null,
noteContent: group.noteContent,
todos: group.todos.map((todo) => ({
title: todo.title,
details: todo.details,
completed: todo.completed,
completedAt: todo.completedAt?.toISOString() ?? null,
})),
}))
);
}