/* ═══════════════════════════════════════════════════════════════ TIMELINE REFS Turns a timeline item into a destination and an image. Both were answered locally here before, which is how the timeline came to link events at /event/:id while the router only knew about /retreats/:id — two files holding the same opinion, one of them wrong, neither aware of the other. Now this file knows about timeline items and nothing else. Where a record lives is hrefs.ts; where an image lives is media.ts. ── The shape this reads, from history.js ── item.href explicit link_url override, may be off-site item.ref { kind, id, orgKind? } — orgKind only on organizations, because only they have it item.logo { file, kind } — v_timeline COALESCEs the filename across five tables, so the kind is what says which directory it came from item.team { id, name, orgId? } on a team ref item.people[] { id, name, photo?, title? } Every one of those is optional. An entry is a standalone milestone until proven otherwise, and the two accessors below return null rather than assuming a shape that isn't there — which is the other half of the /organizations/undefined bug: reaching into a ref that wasn't sent yields undefined, and undefined interpolates into a path perfectly happily. ═══════════════════════════════════════════════════════════════ */ import { refHref } from './hrefs.ts' import { logoForKind, personPhoto } from './media.ts' import type { TimelineItem } from './timeline.ts' /** * Where this entry points, or null if nowhere. * * An explicit link_url wins: it's the editor deliberately * overriding the record's own page, usually to send someone to an * external write-up. TimelineEntry checks for a scheme and renders * an instead of a , so this returns it unchanged. * * Otherwise the reference decides, and refHref returns null for a * kind with no page yet ('person', until /people/:id exists) as * well as for a ref that didn't resolve. Null means the entry * renders as a plain
— right for a milestone, and right for * a reference that's missing an id, which used to render as a link * to a 404. */ export function hrefFor(item: TimelineItem): string | null { if (item.href) return item.href const ref = item.ref if (!ref) return null return refHref(ref.kind, ref.id, ref.orgKind) } /** * The entry's image, resolved against the directory for whatever * kind of record the filename came from. */ export function logoSrc(item: TimelineItem): string | null { if (!item.logo) return null return logoForKind(item.logo.kind, item.logo.file) } /** * A roster member's photo. Same rule as everywhere else — the API * sends a bare filename. */ export function photoSrc(photo?: string | null): string | null { return personPhoto(photo) }