"use server"; import { prisma } from "@/lib/prisma"; import { ArtworkTableInput, artworkTableInputSchema, artworkTableOutputSchema } from "@/schemas/artworks/tableSchema"; function triToBool(tri: "any" | "true" | "false"): boolean | undefined { if (tri === "any") return undefined; return tri === "true"; } function mapSortingToOrderBy(sorting: ArtworkTableInput["sorting"]) { const allowed: Record any> = { createdAt: (desc) => ({ createdAt: desc ? "desc" : "asc" }), updatedAt: (desc) => ({ updatedAt: desc ? "desc" : "asc" }), sortIndex: (desc) => ({ sortIndex: desc ? "desc" : "asc" }), name: (desc) => ({ name: desc ? "desc" : "asc" }), slug: (desc) => ({ slug: desc ? "desc" : "asc" }), published: (desc) => ({ published: desc ? "desc" : "asc" }), nsfw: (desc) => ({ nsfw: desc ? "desc" : "asc" }), needsWork: (desc) => ({ needsWork: desc ? "desc" : "asc" }), // relation counts: Prisma supports ordering by _count albumsCount: (desc) => ({ albums: { _count: desc ? "desc" : "asc" } }), categoriesCount: (desc) => ({ categories: { _count: desc ? "desc" : "asc" } }), tagsCount: (desc) => ({ tags: { _count: desc ? "desc" : "asc" } }), }; const orderBy = sorting .map((s) => allowed[s.id]?.(s.desc)) .filter(Boolean); orderBy.push({ id: "desc" }); return orderBy; } export async function getArtworksTablePage(input: unknown) { const parsed = artworkTableInputSchema.safeParse(input); if (!parsed.success) throw new Error(parsed.error.message); const { pagination, sorting, filters } = parsed.data; const { pageIndex, pageSize } = pagination; const published = triToBool(filters.published); const nsfw = triToBool(filters.nsfw); const needsWork = triToBool(filters.needsWork); const where: any = { ...(typeof published === "boolean" ? { published } : {}), ...(typeof nsfw === "boolean" ? { nsfw } : {}), ...(typeof needsWork === "boolean" ? { needsWork } : {}), ...(filters.name ? { name: { contains: filters.name, mode: "insensitive" } } : {}), ...(filters.slug ? { slug: { contains: filters.slug, mode: "insensitive" } } : {}), ...(filters.galleryId ? { galleryId: filters.galleryId } : {}), ...(filters.albumIds?.length ? { albums: { some: { id: { in: filters.albumIds } } } } : {}), ...(filters.categoryIds?.length ? { categories: { some: { id: { in: filters.categoryIds } } } } : {}), }; const orderBy = mapSortingToOrderBy(sorting); const [total, items] = await Promise.all([ prisma.artwork.count({ where }), prisma.artwork.findMany({ where, orderBy, skip: pageIndex * pageSize, take: pageSize, select: { id: true, name: true, slug: true, published: true, nsfw: true, needsWork: true, createdAt: true, updatedAt: true, sortIndex: true, file: { select: { fileKey: true } }, gallery: { select: { id: true, name: true } }, albums: { select: { id: true, name: true } }, categories: { select: { id: true, name: true } }, _count: { select: { albums: true, categories: true, tags: true } }, }, }), ]); const rows = items.map((a) => ({ id: a.id, name: a.name, slug: a.slug, published: a.published, nsfw: a.nsfw, needsWork: a.needsWork, createdAt: a.createdAt.toISOString(), updatedAt: a.updatedAt.toISOString(), fileKey: a.file.fileKey, gallery: a.gallery ? { id: a.gallery.id, name: a.gallery.name } : null, albums: a.albums, categories: a.categories, albumsCount: a._count.albums, categoriesCount: a._count.categories, tagsCount: a._count.tags, })); const out = { rows, total, pageIndex, pageSize }; const outParsed = artworkTableOutputSchema.safeParse(out); if (!outParsed.success) throw new Error(outParsed.error.message); return outParsed.data; }