/** * gallery.yaml ⇄ the page builder's model (#608). * * This is the code that decides what ends up in gallery.yaml when the * builder saves. It lived inside the 1,200-line PageBuilder component, where * nothing could test it; as pure functions it has a table of cases in * app/admin/__tests__/gallery-yaml.test.ts. */ import { parseAlbumEntries, serializeAlbumEntries } from './albumEntries'; import { seedCoverGrid, type AlbumEntry, type Subpage } from './types'; export interface GalleryState { hero: string[]; albums: AlbumEntry[]; subpages: Subpage[]; } /** * Accepts both subpage shapes gallery.yaml allows: the list of objects the * builder writes, and the older map of name → albums (or name → object). */ export function parseGalleryYaml(raw: Record): GalleryState { const hero = Array.isArray(raw.hero) ? raw.hero : raw.hero ? [raw.hero as string] : []; const albums = parseAlbumEntries( raw.albums as Array> | undefined, ); let subpages: Subpage[] = []; if (Array.isArray(raw.subpages)) { subpages = (raw.subpages as Array>).map((sp) => ({ name: (sp.name as string) || '', title: sp.title as string | undefined, subtitle: sp.subtitle as string | undefined, password: sp.password as string | undefined, enabled: sp.enabled !== false, hidden: sp.hidden === true, proofing: typeof sp.proofing === 'boolean' ? sp.proofing : undefined, location: sp.location as string | undefined, essayText: sp.essayText as string | undefined, essayFile: sp.essayFile as string | undefined, albums: parseAlbumEntries(sp.albums as Array> | undefined), sections: sp.sections ? (sp.sections as Array>).map((sec) => ({ title: (sec.title as string) || '', description: sec.description as string | undefined, albums: parseAlbumEntries(sec.albums as Array>), })) : undefined, grid: sp.grid as Subpage['grid'], coverGrid: (sp.coverGrid as Subpage['coverGrid']) ?? seedCoverGrid(sp.grid as Subpage['grid']), })); } else if (raw.subpages && typeof raw.subpages === 'object') { subpages = Object.entries(raw.subpages as Record).map(([name, value]) => { if (Array.isArray(value)) { return { name, albums: parseAlbumEntries(value), sections: undefined, enabled: true }; } const sp = value as Record; return { name, title: sp.title as string | undefined, subtitle: sp.subtitle as string | undefined, password: sp.password as string | undefined, enabled: sp.enabled !== false, hidden: sp.hidden === true, proofing: typeof sp.proofing === 'boolean' ? sp.proofing : undefined, location: sp.location as string | undefined, essayText: sp.essayText as string | undefined, essayFile: sp.essayFile as string | undefined, albums: parseAlbumEntries(sp.albums as Array> | undefined), sections: undefined, grid: sp.grid as Subpage['grid'], coverGrid: (sp.coverGrid as Subpage['coverGrid']) ?? seedCoverGrid(sp.grid as Subpage['grid']), }; }); } return { hero, albums, subpages }; } /** The gallery.yaml object for a builder state. Empty groups are left out. */ export function serializeGallery(gallery: GalleryState): Record { const yamlData: Record = {}; if (gallery.hero.length > 0) { yamlData.hero = gallery.hero; } if (gallery.albums.length > 0) { yamlData.albums = serializeAlbumEntries(gallery.albums); } if (gallery.subpages.length > 0) { yamlData.subpages = gallery.subpages.map((sp) => { const entry: Record = { name: sp.name }; if (sp.title) entry.title = sp.title; if (sp.subtitle) entry.subtitle = sp.subtitle; if (sp.password) entry.password = sp.password; if (sp.enabled === false) entry.enabled = false; if (sp.hidden === true) entry.hidden = true; // Both values mean something: false switches proofing off for this page. if (sp.proofing !== undefined) entry.proofing = sp.proofing; if (sp.essayText) entry.essayText = sp.essayText; if (sp.essayFile) entry.essayFile = sp.essayFile; if (sp.grid) entry.grid = sp.grid; if (sp.coverGrid) entry.coverGrid = sp.coverGrid; if (sp.location) entry.location = sp.location; if (sp.sections && sp.sections.length > 0) { entry.sections = sp.sections.map((sec) => { const s: Record = { title: sec.title, albums: serializeAlbumEntries(sec.albums), }; if (sec.description) s.description = sec.description; return s; }); if (sp.albums.length > 0) { entry.albums = serializeAlbumEntries(sp.albums); } } else { entry.albums = serializeAlbumEntries(sp.albums); } return entry; }); } return yamlData; }