Skip to content

Make schedules publishable #46

@chiptus

Description

@chiptus

Goal

Let the Core Team progressively reveal an edition's schedule (set times + stage assignments) to the public, independently of when the lineup goes live for voting.

Glossary, model rationale, and rejected alternatives are recorded in:

  • CONTEXT.md — the domain glossary
  • docs/adr/0001-schedule-reveal-level.md — the architectural decision

Locked decisions

Model

  • schedule_reveal_level — new ordered enum on festival_editions, default draft.
  • Levels (low → high): draftdaysstagesfull. Each implies the previous.
  • Independent of edition.published. edition.published continues to mean "lineup visible + voting open."
  • Schedule edits write through live (no staging table); the import wizard shows a warning at commit time when the current level is above draft.

Per-level field visibility (non-admins)

Level time_start date time_start time-of-day time_end stage_id
draft hidden hidden hidden hidden
days visible hidden hidden hidden
stages visible hidden hidden visible
full visible visible visible visible

Admins see everything regardless of level.

Masking mechanism — client-side

  • Server returns full set rows. Frontend hides embargoed fields based on (schedule_reveal_level, isAdmin).
  • Accepts the wire leak (devtools/direct Supabase queries can see the raw fields). Schedule is embargoed, not secret.
  • A small util — e.g. maskSetForReveal(set, level, isAdmin) — centralises the rule so new surfaces can't forget it.
  • Server-side hardening (a public_sets view + revoke public SELECT on sets) is the documented upgrade path if we ever need true leak-proofness.

Admin UX

  • Dropdown for schedule_reveal_level in the existing edition edit dialog (FestivalEditionManagement.tsx), next to edition.published.
  • Progressive action buttons on the Schedule tab (admin-only): "Reveal Days", "Reveal Stages", "Reveal Times". Each becomes a "Revealed ✓" badge with an undo affordance once active.
  • Demotion (e.g. fullstages) is allowed via the same controls — no separate flow.
  • Level is settable independently of edition.published. When the edition is unpublished, UI shows "configured but not yet active."

Public UX

  • Schedule tab stays in the nav at all levels. Below full, it shows a "Schedule not yet published" placeholder (existing pattern) with level-aware copy.
  • A small indicator icon on the Schedule tab button signals "not yet at full" to public users.
  • Set/Artist surfaces across the app (SetCard SetMetadata.tsx, SetDetails, ExploreSetPage) call the masking util and naturally hide on null.

Out of scope (separate task)

Implementation plan

  1. DB: migration adding schedule_reveal_level enum and column on festival_editions (NOT NULL, default draft); backfill existing rows. Regen src/integrations/supabase/types.ts.
  2. Util: maskSetForReveal(set, level, isAdmin) — single source of truth for which fields to null at which level.
  3. Edition query: include schedule_reveal_level in useFestivalEdition / useFestivalEditionBySlug results.
  4. Frontend hide rules: apply the util at the read boundary in useEditionSetsQuery / useSetsByEditionQuery (or call it in components — TBD which is cleanest). Verify each surface: SetMetadata.tsx, SetDetails, ExploreSetPage, set filters that depend on time/stage.
  5. Schedule tab: level-aware placeholder copy; add the "not yet at full" indicator icon on the tab nav button.
  6. Admin controls:
    • Dropdown in FestivalEditionManagement.tsx edit dialog.
    • Admin-only progressive buttons + demote affordance on the Schedule tab, gated by useUserPermissionsQuery(user?.id, "is_admin").
  7. Import wizard warning: in ScheduleImportWizard commit step, when schedule_reveal_level > draft, render a warning block summarising what becomes visible.
  8. Tests: unit tests for maskSetForReveal across the level × role matrix; component snapshots for SetCard at each level; admin level-change flow.

Deferred

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions