Skip to content

Event Authoring

Use this page when changing the Add/Edit Event form or the contracts and workflows that make one authored Event durable. Add and Edit share one organizer-authored field tree. Their page workflows remain separate because creation is one atomic database request followed by media, while editing owns hydrated baselines, meaningful change plans, sequential revisions, conflicts, and persisted-media recovery.

LayerOwnerResponsibility
Shared contractpackages/api-contracts/src/types/events/eventAuthoring.tsStrict create shape, public identities, destination codes, attendance rules, and response identity.
Backend requestapps/wavemap-back-end/src/handlers/events/createEvent.tsAuthorization, schedule resolution, parent creation, initial relationship transaction, and stable errors.
Backend relationshipsapps/wavemap-back-end/src/queries/events/Event-scoped identity resolution, permissions, replacement or delta semantics, provenance, and revision behavior.
Frontend formapps/wavemap-front-end/src/app/[locale]/(events)/add-event/Components/AddOrEditEventForm/Discriminated Add/Edit TanStack Form draft, hydration, conditional attendance, accessible errors, and controls.
Add workflowapps/wavemap-front-end/src/utils/events/useEventAuthoringWorkflow.tsParent-first creation, media sequencing, partial-success recovery, cache refresh, and canonical navigation.
Edit workflowapps/wavemap-front-end/src/app/[locale]/(events)/edit-event/[eventID]/Meaningful changes, revision sequencing, conflicts, persisted-media recovery, cache refresh, and navigation.
Visible copypackages/i18n/locales/*/page--add-edit-event.json and server--errors.jsonEnglish/French field language, lifecycle messages, and stable server-error presentation.

Keep transport hooks route-sized. The form owns the organizer’s draft, and the workflow owns multi-step orchestration; neither should absorb backend transaction rules or provider storage behavior.

CreateEventRequestDTO accepts the scalar Event, organizer-authored schedule, and these initial collections:

  • venuePublicIds
  • artistPublicIds
  • digitalLocations
  • links
  • ticketLinks

The request crosses the app boundary with public entity IDs and stable destination codes only. The backend resolves database identities, inserts the Event, and persists every supplied database-only collection in the same transaction. Invalid active targets, destination codes, attendance combinations, or relationship persistence roll the parent back. Initial relationship writes do not increment revision separately; a successful created Event begins at revision one.

The collection write shape follows the editing job:

  • Event Artists use atomic add/remove deltas after creation because the relationship can become high-cardinality.
  • Venues, digital locations, external links, and ticket links use complete replacement after creation because they are ordered or deliberately small editor-owned sets.
  • Every changed post-create collection advances the Event revision once; a valid no-op preserves it.

Physical, online, and hybrid Events remain one occurrence. In-person Events reject digital locations, online Events require digital attendance and reject venues, and hybrid Events require both contexts. Venue time zones remain advice; they never silently replace the organizer-selected Event reference zone.

The Add form is one responsive page with progressive disclosure:

  1. Lifecycle feedback and Event media.
  2. Core details.
  3. Attendance.
  4. Relationships.
  5. Schedule.
  6. Submission and recovery actions.

Attendance appears before Schedule so selected venues can inform, but not decide, the reference-zone choice. Event type controls the physical and digital attendance fields. Artist and Venue fields keep compact previews in the form and move discovery and larger-set management into focused drawers. Venue-owned time-zone suggestions remain visible in the Attendance section as advisory context. Digital locations, external links, and ticket links are ordered TanStack Form arrays with explicit add, remove, and reorder controls.

The form does not own Event Series membership. It presents compact read-only Series and Edition context plus real links to the Series- or Edition-owned relationship editor. Contextual navigation preserves the Event draft when it contains JSON-only values and guards unsaved browser files explicitly. Every saved Event remains independently valid and independently scheduled regardless of membership.

AddOrEditEventForm uses a discriminated formMode contract. Add receives no Event details and starts from the empty organizer draft. Edit requires the canonical Event details DTO and hydrates scalar, schedule, attendance, relationship, and media values without treating response-only projections as write authority. The same TanStack fields, validation, section focus, dirty-state calculation, and English/French field language serve both modes; only the heading, final action, initial values, and enabled capability surfaces differ.

Authorization, route prefetch, mutation ordering, revisions, cache invalidation, canonical navigation, and recovery stay outside the form. This keeps the form reusable without turning it into a domain workflow or hiding Add/Edit lifecycle differences behind mode checks.

Public event.artists contains active Artists only. Editors with manage-event-artist-relationships additionally load GET /api/v1/events/:eventID/artists, whose strict response carries eventPublicId, revision and every linked Artist reference in items, including inactive targets. The backend reads the owner and collection in one read-only, repeatable-read transaction. The collection has no preview cap or mutation-batch limit.

useGetEventArtistsMembership keeps this protected read under an Event membership key qualified by the current user, with no anonymous request and no retained cache after its last observer leaves. It uses the Event cache family for mutation invalidation without replacing the public Event entry. The server route prefetch remains public.

useEditEventBaseline waits for matching Event and membership owner/revision before the shared form mounts. Failure or mismatch shows the existing retry surface; an empty or incomplete fallback must never become the editing baseline. Editors without the Artist capability use public Artist references as disabled read context.

Once accepted, the starting server baseline remains immutable for that mounted editor. TanStack Form owns the editable draft, and each successful capability or explicit recovery advances the separately held expected revision. Recovery refetches both authorized reads, checks their success and matching revisions, and preserves the mounted form. Comparing Artist intent against its original baseline avoids interpreting a concurrently added membership as a requested removal during an unrelated scalar save. Inactive linked Artists remain visible and explicitly removable through the existing Artist manager. Identity or permission changes remount the form from an appropriate baseline; logout drops the accepted baseline, including when the same user signs in again.

The shared form is named by its visible Add or Edit heading and exposes pending work through aria-busy. Pending status copy is announced, duplicate lifecycle actions are disabled, and validation becomes visible after the owning TanStack field has been touched. Invalid controls expose aria-invalid, while the existing FormField surface keeps the visible message associated with the field rather than introducing a second error-rendering path.

Schedule and relationship sections are stable focus-recovery targets for server-owned failures. Dynamic collection actions use item-specific accessible names: venue and Artist removal names the selected entity, while digital-location, ticket-link, and external-link move/remove controls include their current position. This distinction matters when several visually identical controls share one section.

Page wrappers translate independent Event permissions into form capabilities for Artists, digital locations, links, Venues, and media. Relationship controls can remain visible as read context while their editing affordances are disabled; the separately permissioned media authoring surface is omitted when unavailable. Add and Edit must perform the same mapping rather than assuming create or scalar-update authority grants every relationship capability.

Lifecycle feedback sits in the same bounded responsive column as the authoring card. It stays in document flow at narrow widths so errors, conflict recovery, and partial-media recovery do not overlap or detach from the form they describe.

Edit compares the submitted draft with the loaded organizer-authored baseline. It omits unchanged scalar and schedule fields, replaces small ordered collections, and sends an atomic Artist add/remove delta. Each successful capability write returns the revision required by the next write. No-op submission performs no mutation.

A revision conflict preserves the mounted form draft. Explicit recovery refetches and validates the latest Event and authorized Artist membership revisions, keeps the local draft, and asks the editor to submit again deliberately. If an earlier capability succeeded before a later write failed, the workflow retains that latest revision, refreshes affected Event projections, and reports partial success so the remaining draft can be reapplied without silently overwriting newer state.

Edit routes are keyed by public ID. Invalid or missing identities use the route not-found boundary; unexpected prefetch failures remain owned by the nearest error boundary. Cancel and successful save navigate through the canonical Event details route produced from the server identity.

Image media is not part of the Event JSON transaction because provider writes have a different failure boundary. The Add workflow:

  1. Creates the Event and its initial database relationships once.
  2. Uploads pending images after the Event public ID exists.
  3. Synchronizes the final Poster/Gallery collection.
  4. Refreshes the affected Event cache families and navigates to the canonical Event identity.

If upload or synchronization fails, the Event remains saved. The workflow preserves its public ID and completed upload state so retry does not create another Event or upload successful files again. Media controls are independently gated by manage-event-media; create authority does not imply media authority.

Edit hydrates persisted Poster/Gallery items into the same media surface. Deletion, restore, purpose, alt text, ordering, and new uploads remain local until submission. Scalar and relationship writes finish first; new files then upload once and one complete-set synchronization establishes the canonical collection. A failed upload or sync retains uploaded server identities, so retry resumes at the first unfinished file or repeats synchronization without duplicating media. Media-only no-ops do not issue synchronization requests.

Add cancellation targets the localized Events Index explicitly. Edit cancellation targets the localized canonical Event Details route, and successful Edit submission navigates with the identity returned by the update workflow so a changed slug is not lost. An untouched draft cancels immediately, while a dirty draft asks for confirmation and registers browser-exit protection. Pending submission disables duplicate lifecycle actions and is announced to assistive technology.

Stable event.schedule.* and relationship error keys map failures to the smallest owning form section. Schedule errors focus the relevant field or schedule region. Venue/digital errors focus Attendance; Artist/link/ticket errors focus Relationships. Recovery input clears the stale server message while retaining the rest of the draft.

Use the narrowest layer that owns the risk:

  • Contract tests prove strict create shape, attendance combinations, public identities, destination vocabulary, URL validation, ordering, and duplicates.
  • Mocked-request tests prove route authorization, stable error identity, and handler orchestration.
  • DB-backed tests prove the complete initial payload commits together, invalid relationships roll the Event back, and post-create revision/no-op semantics match the chosen collection shape.
  • Form integration tests prove conditional fields, focused management surfaces, ordering, field recovery, focus, announcements, and draft serialization.
  • Workflow/page integration tests prove create-once and edit-resume media sequencing, revision carry-forward, conflict and partial-success posture, permission gating, cache refresh, and canonical navigation intent.
  • Browser proof should remain narrow and target behavior that component tests cannot establish reliably, such as real navigation/exit behavior or responsive focus flow.
  • Event Details composition remains collaborator-owned.
  • Series and Edition relationship mutation remains owned by their protected authoring capabilities; Event authoring exposes context and navigation without becoming a competing relationship owner.
  • Venue address/provider authoring, ticket purchasing, inventory, pricing, and provider authentication are separate feature columns.

This page becomes stale when the shared create DTO, Add form section order, parent/media transaction split, permission grammar, or Edit Event reuse boundary changes. Update it with those source contracts rather than copying implementation history from the Events roadmap.