Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 3x 10x 7x | /**
* Published-phase gate for public event surfaces.
*
* The generic `GET /api/v1/events` list returns every event regardless of publication
* state, so public surfaces must filter to events the organizer has actively published.
* Only a `TOPIC` / `SPEAKERS` / `AGENDA` phase counts — the unpublished sentinel `NONE`
* is a truthy string, so a bare `Boolean(phase)` check would wrongly pass it. Mirrors the
* backend `/events/current` gate (`publishedAt != null || currentPublishedPhase != 'none'`).
*/
export const PUBLISHED_PHASES = ['TOPIC', 'SPEAKERS', 'AGENDA'] as const;
export type PublishedPhase = (typeof PUBLISHED_PHASES)[number];
export function isPublishedPhase(phase: string | null | undefined): boolean {
return PUBLISHED_PHASES.includes((phase ?? '') as PublishedPhase);
}
interface SelfNominationGateInput {
/** Expanded topic object (present once the topic is set), or a raw code/string, or null. */
topic?: { name?: string } | string | null;
currentPublishedPhase?: string | null;
}
/**
* Story 7.2 "I Could Speak on That": speaker self-nomination is open ONLY during the TOPIC
* publishing phase — from when the topic is published until the speakers are published. Once the
* lineup is published (SPEAKERS/AGENDA), the program is set and no further nominations are taken.
* Callers add their own login gate (the panel self-gates) and, where relevant, a future-event check.
*/
export function canOfferSelfNomination(event: SelfNominationGateInput): boolean {
return (
typeof event.topic === 'object' &&
event.topic != null &&
(event.currentPublishedPhase ?? '') === 'TOPIC'
);
}
|