Rework artwork list

This commit is contained in:
2025-12-23 19:55:08 +01:00
parent 784153e9f6
commit 1363697103
9 changed files with 869 additions and 31 deletions

View File

@ -0,0 +1,15 @@
"use server";
import { prisma } from "@/lib/prisma";
export async function getArtworkFilterOptions() {
const [albums, categories] = await Promise.all([
prisma.album.findMany({ select: { id: true, name: true }, orderBy: { name: "asc" } }),
prisma.artCategory.findMany({
select: { id: true, name: true },
orderBy: { name: "asc" },
}),
]);
return { albums, categories };
}

View File

@ -0,0 +1,120 @@
"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<string, (desc: boolean) => 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;
}