Seating Module Guide
Seating is the event-operations module that manages table planning and guest placement for Tov+.
Purpose
Seating is the event-operations module that manages table planning and guest placement for Tov+.
It provides:
- occasion-level installation through the module platform
- event-level enablement for events that actually need seating
- planner-managed seating plans, zones, tables, and assignments
- guest-safe lookup of a guest's own published table or seat
- vendor-safe read access for published plans when explicit permission exists
Seating is not a frontend-only layout helper. It is a real module with server-side validation, persistence artifacts, and permission-aware APIs.
Scope model
Seating is installed once per occasion and enabled per event.
That means:
- the occasion installation owns the default module posture
- each event enablement controls whether guest publication is allowed through
publish_to_guests - every seating plan belongs to exactly one event
- v1 supports one seating plan per event
The current install config uses table_naming_strategy.
The current event enablement config uses publish_to_guests.
Seating plan model
The module stores four seating-specific entity types:
seating_plansseating_zonesseating_tablesseating_assignments
The generic module platform still owns:
- occasion installation records
- event enablement records
- centralized permissions
Each seating plan stores:
- occasion scope and event scope
- module installation and enablement linkage
plan_key- display name
- optional planner notes
- lifecycle status
- publication timestamps and publisher metadata
Statuses are:
draftpublished
Table and seat model
The baseline model supports tables plus optional seat positions.
Current behavior:
- tables are the core seating location
- each table has a
capacity - seat assignment is optional through
seat_numberandseat_label - planners can seat a guest at a table without specifying an exact seat
- planners can also capture explicit seat positions when they need finer control
This keeps v1 operationally usable while preserving a clean path to richer spatial layout later.
Zone model
The baseline also supports zones.
Zones exist to group tables into simple sections such as:
- main hall
- family section
- patio
- vendor mezzanine
Current behavior:
- a zone belongs to one seating plan
- a table may reference one zone or no zone
- guests do not receive full zone listings
- vendors only see zone data when they already have permission to view the published plan
Zones solve immediate operational grouping without forcing a full floorplan system into this task.
Assignment behavior
Assignments map one actor to one table within one seating plan.
The baseline rules are:
- one actor may only have one assignment per seating plan
- duplicate seat numbers on the same table are rejected
- seat numbers above table capacity are rejected
- table over-capacity is rejected
- declined invitees are rejected for assignment
- pending invitees remain assignable
Planner changes after publication are allowed, but any structural or assignment edit demotes the plan back to draft. The planner must publish again before guests or vendors consume the updated arrangement.
Actor eligibility
Seating is event-scoped, so eligibility is resolved per event.
The current candidate model includes only actors of type:
personhouseholdgroup
When RSVP invitations and responses exist, candidate selection is narrowed to event-invited actors. RSVP posture is then attached as:
attendingpendingdeclinednot_tracked
The practical baseline is:
- invited and pending actors can still be seated
- declined actors cannot be seated
- if no RSVP data exists, the module falls back to broader actor availability
The planner view also exposes group_labels derived from parent memberships so households and groups are easier to keep together manually.
Planner flow
Planner routes are:
GET /v1/occasions/:occasion_id/seatingPOST /v1/occasions/:occasion_id/seating/plansPOST /v1/occasions/:occasion_id/seating/plans/:seating_plan_id/zonesPATCH /v1/occasions/:occasion_id/seating/zones/:seating_zone_idPOST /v1/occasions/:occasion_id/seating/plans/:seating_plan_id/tablesPATCH /v1/occasions/:occasion_id/seating/tables/:seating_table_idPOST /v1/occasions/:occasion_id/seating/plans/:seating_plan_id/assignmentsDELETE /v1/occasions/:occasion_id/seating/assignments/:seating_assignment_idPOST /v1/occasions/:occasion_id/seating/plans/:seating_plan_id/publish
The planner dashboard route is:
/occasions/:occasionId/seating
The planner UI supports:
- selecting an enabled event
- creating a seating plan when the event has none yet
- creating zones and tables
- assigning, moving, and unassigning actors
- seeing capacity state and unassigned actors
- publishing the current seating plan
Guest flow
Guest route:
GET /v1/guest/occasions/:occasion_slug/seating
Guest app route:
/seating
Guest visibility rules are:
- the guest token must resolve to the same occasion
- the event must already be accessible to that token
- the seating plan must be
published - the event enablement must set
publish_to_guests = true - the guest only receives assignments for the resolved guest actor or invitation-unit actor
Guests do not receive unrelated seating assignments, the full table roster, or unpublished drafts.
Vendor flow
Vendor route:
GET /v1/vendors/occasions/:occasion_id/seating
Vendor visibility rules are:
- an active vendor actor is required
- the actor must belong to the occasion
- the plan must already be
published - centralized authorization must allow
resource:seating_plan:view
This is intentionally a seam rather than a full vendor workspace feature. It preserves the contract needed for venue, hall, and catering workflows later.
Permissions and visibility
Planner management uses centralized permission actions:
seating:listseating:createseating:updateseating:publish
Vendor reads use:
resource:seating_plan:view
Guest access is token-based and filtered server-side instead of sharing the planner response shape.
Unauthorized planner or vendor requests are rejected with the normal centralized authorization behavior. Guest requests with the wrong token or wrong occasion are rejected without exposing unrelated seating data.
Module-platform integration
Seating is a real shared module definition.
It currently declares:
- occasion installation scope
- event enablement scope
- dependency on the actor domain
- planner, guest, and vendor surfaces
- install and enablement schemas
The server requires a real installation for occasion-scoped reads and writes, and a real event enablement for event-scoped plan creation.
Future publishing seam
This task does not implement printable exports yet, but the current model preserves the seam for:
- seating chart exports
- printable table lists
- guest signage
- vendor-ready floorplan derivatives
Later publishing work can build on the existing plan, zone, table, and assignment records instead of inventing a parallel seating representation.