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 { 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 { 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, })), })) ); }