import { Prisma } from "@prisma/client"; import { db } from "@/lib/db"; /** * The still that stands for a project. * * There is no poster field on Project, and adding one would mean somebody * maintaining it. The shots already carry the images, and one of them is * already marked as the shot that represents the show — `isKeyShot` — so the * poster is chosen rather than uploaded: * * 1. a key shot with a still, then * 2. the most recently touched shot with a still. * * Within each, `thumbnailUrl` beats `generatedThumbnailUrl`, the same * precedence the rest of the app displays shots by (lib/shot-thumbnail.ts): * the reference frame from the brief is a deliberate choice, a promoted render * frame is whatever the pipeline happened to land on. * * The choice is deterministic, not random — a card whose image changes between * two loads of the same page reads as a glitch, and the eye stops using the * image to find the project. */ export interface ProjectPoster { url: string; shotCode: string; /** True when this is the show's marked key shot rather than a fallback. */ isKeyShot: boolean; } /** * One poster per project in a single query. * * DISTINCT ON is Postgres-only and deliberate: the alternative is either a * query per project or dragging back every shot that has a still just to keep * the first of each in memory. */ export async function getProjectPosters( projectIds: string[] ): Promise> { if (projectIds.length === 0) return new Map(); const rows = await db.$queryRaw< { projectId: string; url: string; shotCode: string; isKeyShot: boolean }[] >(Prisma.sql` SELECT DISTINCT ON (s."projectId") s."projectId" AS "projectId", COALESCE(NULLIF(s."thumbnailUrl", ''), NULLIF(s."generatedThumbnailUrl", '')) AS "url", s."shotCode" AS "shotCode", s."isKeyShot" AS "isKeyShot" FROM "shots" s WHERE s."projectId" IN (${Prisma.join(projectIds)}) AND COALESCE(NULLIF(s."thumbnailUrl", ''), NULLIF(s."generatedThumbnailUrl", '')) IS NOT NULL ORDER BY s."projectId", s."isKeyShot" DESC, s."updatedAt" DESC `); return new Map( rows.map((row) => [ row.projectId, { url: row.url, shotCode: row.shotCode, isKeyShot: row.isKeyShot }, ]) ); }