Skip to content

Add event lineup and schedule, and customisable homepage sections - #1406

Merged
daveearley merged 4 commits into
developfrom
feature/event-lineup-schedule
Oct 16, 2026
Merged

daveearley merged 4 commits into
developfrom
feature/event-lineup-schedule

Conversation

@daveearley

Copy link
Copy Markdown
Contributor

What changes I've made

Lineup & schedule

  • People: organizer-level profiles (speakers, artists, hosts) with up to 8 photos, a tagline, bio and up to 12 links. Profiles are reused across the organizer's events and managed from a new People page. Link types are detected from the URL server-side (ProfileLinkType::fromUrl).
  • Event lineup: each event has its own lineup entries (billing tier, role label, hidden/secret acts, sort order), stages/rooms (optionally tied to one of the event's locations) and time slots with any number of people. The dashboard Lineup workspace has a people rail, a profile drawer and a drag-and-drop timetable (@mantine/schedule). Schedule days run 05:00 to 05:00, so late sets stay on their night, including across clock changes.
  • Public event page: a lineup grid and a schedule timeline (stage filter, slots happening now highlighted) with profile and session popups, add to calendar (Google Calendar or .ics), back navigation between popups and #session-<id> deep links. performer and subEvent schema.org data is added to the event's JSON-LD.
  • Run sheet: XLSX export (/lineup/export, with Schedule and Lineup sheets), a printable view, and a bulk import from CSV, XLSX or pasted spreadsheet rows. Files are parsed in the browser (DecompressionStream + DOMParser, no new library) and every row is previewed before anything is sent: new/updated/unchanged people, already scheduled slots, row-level errors and a day/month date-order toggle. The server receives clean JSON (max 500 people, 1,000 slots), matches people and stages by name, writes in bulk under an event lock and skips slots that already exist, so re-importing an export is a no-op.
  • Emails & widget: order and ticket emails include a lineup highlights block (also exposed as lineup.* Liquid tokens in custom templates, in the buyer's language, per date for recurring events). The ticket widget can show a lineup strip.
  • Housekeeping: duplicating an event copies its lineup (slot times shift with the start date for single events), account anonymisation scrubs profiles, and removing people cleans up the empty slots they leave behind.

Homepage sections

  • Event homepages render an ordered list of sections the organizer can reorder, toggle and retitle: tickets, about, lineup, schedule, location and organizer, plus text, gallery, FAQ, video (YouTube/Vimeo) and sponsor sections. The hero and footer stay fixed, and the tickets section is always on. Ticket headings keep coming from product categories.
  • Organizer pages use the same system (events, about, contact, past events and the custom sections), with the default order reproducing the existing organizer page.
  • HomepageSectionsSanitizer is the only writer: HTML is purified, image ids must belong to the event/organizer, and video links are limited to YouTube/Vimeo. Section text is included in the event spam check. Disabled sections are withheld from public API responses and page source.
  • Duplicating an event gives the copy its own gallery and sponsor images.

Also

  • The run sheet and seat map print views now print on a white page instead of the app's grey background.

Why I've made these changes

Festivals, conferences and club nights sell on who's playing and when, and organizers currently have to put their lineup in the event description or on another site. This gives them a structured lineup and timetable that appears on the event page, in emails and in search results, with a spreadsheet round trip for the large lineups they already keep in Excel or Google Sheets. Homepage sections let organizers shape the event and organizer pages (FAQs, galleries, sponsors, video) without custom HTML in the description.

How I've tested these changes

  • Backend: 1,946 unit tests and 970 feature tests pass. New coverage includes lineup CRUD and scoping (child ids scoped to the event/organizer, read-only and restricted members), import/export round trips, limits and rejections, public filtering of hidden people and disabled sections, email highlights, the 05:00 day boundary across DST changes, homepage section sanitising and duplication, and account anonymisation.
  • Static checks: pint --test is clean on all changed PHP files, php artisan scramble:analyze is clean, frontend tsc is unchanged at the 39 existing errors on develop, and ESLint reports no new errors in changed files.
  • Playwright: new lineup, lineup-import-export, homepage-sections, organizer-homepage-sections and image-fit specs, unit specs for the import parser, schedule-day and organizer layout rules, and updated read-only-access and seat-map-print specs. The related specs (55 tests) pass against the dev stack.

Reviewer notes

  • Migrations are additive: new profiles, event_lineup_entries, event_spaces, event_schedule_items and event_schedule_item_performers tables, images.sort_order, and nullable homepage_sections JSON on event_settings and organizer_settings. A null section list falls back to the default order, so existing event and organizer pages render exactly as before.
  • Mantine is bumped from 9.2.2 to 9.7.2, and @mantine/schedule is added for the timetable.
  • Translations are added for all frontend locales (ru intentionally untranslated) and the backend lang/*.json files.

Checklist

  • I have read the contributing guidelines.
  • My code follows the coding standards of the project.
  • I have tested my changes, and they work as expected.
  • I understand that this PR will be closed if I do not follow the contributor guidelines and if this PR template is left unedited.

Lineup & schedule
- Organizer-level profiles (speakers, artists, hosts) with photos and
  links, reused across events, managed from a new People page
- Per-event lineup with billing tiers, roles and hidden acts, stages and
  rooms (optionally tied to an event location) and a drag-and-drop
  timetable on a 05:00-to-05:00 day so late sets stay on their night
- Public lineup grid and schedule timeline with profile and session
  popups, add-to-calendar (.ics), deep links and schema.org
  performer/subEvent data
- Run sheet: XLSX export, printable view, and CSV/XLSX/paste import that
  previews every row in the browser before anything is uploaded
- Lineup highlights in order and ticket emails (plus Liquid tokens), and
  an optional lineup strip in the ticket widget
- Event duplication copies the lineup; account anonymisation scrubs
  profiles

Homepage sections
- Event homepages render an ordered, toggleable list of sections:
  tickets, about, lineup, schedule, location and organizer, plus text,
  gallery, FAQ, video (YouTube/Vimeo) and sponsor sections
- Organizer pages use the same system (events, about, contact, past
  events and the custom sections)
- Content is purified and spam-checked; disabled sections are withheld
  from public responses

Also prints the run sheet and seat map on a white page.
@railway-app
railway-app Bot temporarily deployed to Hi.Events / Hi.Events-pr-1406 October 15, 2026 21:19 Destroyed
@railway-app

railway-app Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the Hi.Events-pr-1406 environment in Hi.Events

Service Status Web Updated
Hi.Events Frontend ✅ Success (View Logs) Web Oct 16, 2026 at 6:15 am UTC
Hi.Events Queue Worker ✅ Success (View Logs) Oct 15, 2026 at 10:25 pm UTC
Hi.Events Scheduler ✅ Success (View Logs) Oct 15, 2026 at 9:24 pm UTC
Hi.Events API ✅ Success (View Logs) Web Oct 15, 2026 at 9:22 pm UTC
1 service not affected by this PR
  • Maildev

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ E2E smoke tests: Everything passed, but 1 test only passed on retry.

Mode Result ✅ Passed ❌ Failed ⚠️ Flaky ⏭️ Skipped ⏱️ Duration
Self-hosted ⚠️ Passed (some flaky) 101 0 1 0 2m 52s

⚠️ Flaky tests: failed first, then passed on retry (1)

  • Self-hosted · organizer/organizer-management.spec.ts:7 · organizer management › an organizer creates a second organizer from the global menu

Open the run

The font-loading effect ran after the early return for a missing event,
so React saw a different number of hooks between renders.
Profile and session popups clipped anything taller than the screen, such
as a Spotify player above a long bio. The popup body now scrolls, and the
back and close buttons stay pinned while it does.
@railway-app
railway-app Bot temporarily deployed to Hi.Events / Hi.Events-pr-1406 October 15, 2026 21:38 Destroyed
…port

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to Hi.Events / Hi.Events-pr-1406 October 16, 2026 06:12 Destroyed
@daveearley
daveearley merged commit 93fb35c into develop Oct 16, 2026
11 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 16, 2026

This branch was successfully deployed

No deployments
Hi.Events / Hi.Events-pr-1406 — 8a85983e Deployed Oct 16, 2026 by railway-app[bot]
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant