import { createSupabaseServiceRoleClient } from "@/lib/supabase/service-role"; import { getFavoritePhotoIds, mapPublicGalleryPhoto, } from "@/server/site/gallery-photos"; import type { CollectionPhoto } from "@/types/admin/collection"; type PublicCollectionRow = { id: string; title: string; slug: string; description: string | null; cover_photo_id: string | null; published_at: string | null; created_at: string; }; type CollectionItemRow = { photo_id: string; sort_order: number; }; const collectionSelect = "id,title,slug,description,cover_photo_id,published_at,created_at"; const photoSelect = "id,unsplash_id,slug,description,alt_description,width,height,color,color_family,likes,urls,user_info,photo_created_at,picked_at,oss_url"; function getPhotoUrl(photo: CollectionPhoto | null) { if (!photo) { return null; } const urls = photo.urls as Record | undefined; return ( photo.oss_url ?? urls?.regular ?? urls?.small ?? urls?.raw ?? urls?.thumb ?? null ); } async function getPhotosById(photoIds: string[]) { if (photoIds.length === 0) { return new Map(); } const supabase = createSupabaseServiceRoleClient(); const { data, error } = await supabase .from("wg_gallery_photos") .select(photoSelect) .eq("is_picked", true) .in("id", photoIds); if (error) { throw error; } return new Map( ((data ?? []) as CollectionPhoto[]).map((photo) => [photo.id, photo]), ); } export async function listPublishedCollections({ page, pageSize, userId, }: { page: number; pageSize: number; userId: string | null; }) { const supabase = createSupabaseServiceRoleClient(); const { data, error, count } = await supabase .from("wg_photo_collections") .select(collectionSelect, { count: "exact" }) .eq("is_published", true) .order("published_at", { ascending: false, nullsFirst: false }) .order("created_at", { ascending: false }) .range((page - 1) * pageSize, page * pageSize - 1); if (error) { throw error; } const collections = (data ?? []) as PublicCollectionRow[]; const collectionIds = collections.map((collection) => collection.id); const coverPhotoIds = collections .map((collection) => collection.cover_photo_id) .filter((id): id is string => Boolean(id)); const { data: items, error: itemsError } = collectionIds.length > 0 ? await supabase .from("wg_photo_collection_items") .select("collection_id,photo_id") .in("collection_id", collectionIds) : { data: [], error: null }; if (itemsError) { throw itemsError; } const photosById = await getPhotosById(coverPhotoIds); const favoritePhotoIds = await getFavoritePhotoIds({ userId, photoIds: coverPhotoIds, }); const itemRows = (items ?? []) as Array<{ collection_id: string; photo_id: string; }>; const countByCollection = itemRows.reduce( (counts, item) => { counts[item.collection_id] = (counts[item.collection_id] ?? 0) + 1; return counts; }, {} as Record, ); return { data: collections.map((collection) => { const coverPhoto = collection.cover_photo_id ? (photosById.get(collection.cover_photo_id) ?? null) : null; return { id: collection.id, title: collection.title, slug: collection.slug, description: collection.description, coverPhoto: coverPhoto ? mapPublicGalleryPhoto({ photo: coverPhoto, favoritePhotoIds, }) : null, coverUrl: getPhotoUrl(coverPhoto), photoCount: countByCollection[collection.id] ?? 0, publishedAt: collection.published_at, }; }), total: count ?? 0, }; } export async function getPublishedCollectionBySlug({ page, pageSize, slug, userId, }: { page: number; pageSize: number; slug: string; userId: string | null; }) { const supabase = createSupabaseServiceRoleClient(); const { data: collection, error } = await supabase .from("wg_photo_collections") .select(collectionSelect) .eq("slug", slug) .eq("is_published", true) .maybeSingle(); if (error) { throw error; } if (!collection) { return null; } const { data: items, error: itemsError, count, } = await supabase .from("wg_photo_collection_items") .select("photo_id,sort_order", { count: "exact" }) .eq("collection_id", collection.id) .order("sort_order", { ascending: true }) .order("created_at", { ascending: true }) .range((page - 1) * pageSize, page * pageSize - 1); if (itemsError) { throw itemsError; } const itemRows = (items ?? []) as CollectionItemRow[]; const photoIds = [ ...itemRows.map((item) => item.photo_id), ...(collection.cover_photo_id ? [collection.cover_photo_id] : []), ]; const photosById = await getPhotosById(Array.from(new Set(photoIds))); const favoritePhotoIds = await getFavoritePhotoIds({ userId, photoIds, }); const coverPhoto = collection.cover_photo_id ? (photosById.get(collection.cover_photo_id) ?? null) : null; return { collection: { id: collection.id, title: collection.title, slug: collection.slug, description: collection.description, coverPhoto: coverPhoto ? mapPublicGalleryPhoto({ photo: coverPhoto, favoritePhotoIds }) : null, coverUrl: getPhotoUrl(coverPhoto), photoCount: count ?? 0, publishedAt: collection.published_at, }, photos: itemRows .map((item) => photosById.get(item.photo_id)) .filter((photo): photo is CollectionPhoto => Boolean(photo)) .map((photo) => mapPublicGalleryPhoto({ photo, favoritePhotoIds })), total: count ?? 0, }; }