import type { MediaAsset, MediaKind, MediaSource, MediaUsage, MediaUsageType, } from "@prisma/client"; import { prisma } from "@/lib/prisma"; export type MediaAssetView = Pick< MediaAsset, "id" | "source" | "kind" | "url" | "fileName" | "label" | "altText" | "mimeType" | "size" | "createdAt" > & { usages: Array< Pick >; }; export type MediaOption = Pick; export type PortfolioMediaBindings = { coverAssetId: string | null; sectionAssetIds: Record; assetIds: Record; }; function mapMediaAsset( asset: MediaAsset & { usages: MediaUsage[]; }, ): MediaAssetView { return { id: asset.id, source: asset.source, kind: asset.kind, url: asset.url, fileName: asset.fileName, label: asset.label, altText: asset.altText, mimeType: asset.mimeType, size: asset.size, createdAt: asset.createdAt, usages: asset.usages.map((usage) => ({ id: usage.id, usageType: usage.usageType, entityType: usage.entityType, entityId: usage.entityId, fieldKey: usage.fieldKey, })), }; } export async function getAdminMediaAssets() { const assets = await prisma.mediaAsset.findMany({ include: { usages: { orderBy: [{ createdAt: "desc" }], }, }, orderBy: [{ createdAt: "desc" }], }); return assets.map(mapMediaAsset); } export async function getMediaOptions(filters?: { kind?: MediaKind }) { const assets = await prisma.mediaAsset.findMany({ where: filters?.kind ? { kind: filters.kind } : undefined, orderBy: [{ createdAt: "desc" }], select: { id: true, kind: true, url: true, label: true, source: true, }, }); return assets; } export async function getMediaAssetById(id: string) { const asset = await prisma.mediaAsset.findUnique({ where: { id }, include: { usages: true, }, }); return asset ? mapMediaAsset(asset) : null; } export async function createMediaAsset(input: { source: MediaSource; kind: MediaKind; url: string; fileName: string; label: string; altText?: string | null; mimeType?: string | null; size?: number | null; }) { return prisma.mediaAsset.create({ data: { source: input.source, kind: input.kind, url: input.url, fileName: input.fileName, label: input.label, altText: input.altText ?? null, mimeType: input.mimeType ?? null, size: input.size ?? null, }, }); } export async function replaceEntityMediaUsages(input: { entityType: string; entityId: string; usages: Array<{ assetId: string; usageType: MediaUsageType; fieldKey: string; }>; }) { await prisma.$transaction(async (tx) => { await tx.mediaUsage.deleteMany({ where: { entityType: input.entityType, entityId: input.entityId, }, }); if (input.usages.length === 0) { return; } await tx.mediaUsage.createMany({ data: input.usages.map((usage) => ({ assetId: usage.assetId, usageType: usage.usageType, entityType: input.entityType, entityId: input.entityId, fieldKey: usage.fieldKey, })), }); }); } export async function deleteEntityMediaUsages(entityType: string, entityId: string) { await prisma.mediaUsage.deleteMany({ where: { entityType, entityId, }, }); } export async function getPortfolioMediaBindings(projectId: string): Promise { const usages = await prisma.mediaUsage.findMany({ where: { entityType: "portfolio-project", entityId: projectId, }, select: { assetId: true, usageType: true, fieldKey: true, }, }); return usages.reduce( (result, usage) => { if (usage.usageType === "PORTFOLIO_COVER") { result.coverAssetId = usage.assetId; } if (usage.usageType === "PORTFOLIO_SECTION") { result.sectionAssetIds[usage.fieldKey] = usage.assetId; } if (usage.usageType === "PORTFOLIO_ASSET") { result.assetIds[usage.fieldKey] = usage.assetId; } return result; }, { coverAssetId: null, sectionAssetIds: {}, assetIds: {}, }, ); } export async function countMediaUsageReferences(assetId: string) { return prisma.mediaUsage.count({ where: { assetId, }, }); }