forked from EduCraft/curriculum-project-hub
feat: add paginated project discovery
This commit is contained in:
+18
-3
@@ -4,7 +4,7 @@
|
||||
* Folders are transparent navigation nodes (not ACL resources). Project grants
|
||||
* stay on PROJECT. Archive folder refuses when active children/projects remain.
|
||||
*/
|
||||
import type { Prisma, PrismaClient } from "@prisma/client";
|
||||
import { Prisma, type PrismaClient } from "@prisma/client";
|
||||
import {
|
||||
archiveFeishuChatBinding,
|
||||
createFolder,
|
||||
@@ -123,11 +123,25 @@ export async function renameFolder(
|
||||
return prisma.$transaction(async (tx) => {
|
||||
await lockActiveOrganization(tx, input.organizationId);
|
||||
const folder = await requireActiveFolder(tx, input.folderId, input.organizationId);
|
||||
if (folder.kind === "SYSTEM_INBOX" && (input.name !== undefined || input.parentId !== undefined)) {
|
||||
throw new Error("system Inbox cannot be renamed or moved");
|
||||
}
|
||||
if (input.parentId !== undefined && input.parentId !== null) {
|
||||
if (input.parentId === folder.id) {
|
||||
throw new Error("folder cannot be its own parent");
|
||||
}
|
||||
await requireActiveFolder(tx, input.parentId, input.organizationId);
|
||||
const descendant = await tx.$queryRaw<Array<{ found: boolean }>>(Prisma.sql`
|
||||
WITH RECURSIVE descendants AS (
|
||||
SELECT "id" FROM "Folder" WHERE "parentId" = ${folder.id} AND "archivedAt" IS NULL
|
||||
UNION ALL
|
||||
SELECT child."id" FROM "Folder" child
|
||||
JOIN descendants parent ON child."parentId" = parent."id"
|
||||
WHERE child."archivedAt" IS NULL
|
||||
)
|
||||
SELECT EXISTS(SELECT 1 FROM descendants WHERE "id" = ${input.parentId}) AS found
|
||||
`);
|
||||
if (descendant[0]?.found === true) throw new Error("folder cannot be moved below its descendant");
|
||||
}
|
||||
const name =
|
||||
input.name !== undefined ? requireNonEmpty(input.name, "folder name") : undefined;
|
||||
@@ -152,6 +166,7 @@ export async function archiveFolder(
|
||||
return prisma.$transaction(async (tx) => {
|
||||
await lockActiveOrganization(tx, input.organizationId);
|
||||
const folder = await requireActiveFolder(tx, input.folderId, input.organizationId);
|
||||
if (folder.kind === "SYSTEM_INBOX") throw new Error("system Inbox cannot be archived");
|
||||
const childFolders = await tx.folder.count({
|
||||
where: { parentId: folder.id, archivedAt: null },
|
||||
});
|
||||
@@ -324,10 +339,10 @@ async function requireActiveFolder(
|
||||
prisma: PrismaClient | Prisma.TransactionClient,
|
||||
folderId: string,
|
||||
organizationId: string,
|
||||
): Promise<{ readonly id: string; readonly organizationId: string }> {
|
||||
): Promise<{ readonly id: string; readonly organizationId: string; readonly kind: "REGULAR" | "SYSTEM_INBOX" }> {
|
||||
const folder = await prisma.folder.findUnique({
|
||||
where: { id: folderId },
|
||||
select: { id: true, organizationId: true, archivedAt: true },
|
||||
select: { id: true, organizationId: true, kind: true, archivedAt: true },
|
||||
});
|
||||
if (folder === null || folder.archivedAt !== null || folder.organizationId !== organizationId) {
|
||||
throw new Error(`active folder not found: ${folderId}`);
|
||||
|
||||
Reference in New Issue
Block a user