E2-F10: Sector Seat Map Configuration¶
Epic: E2: Match & Stadium Management
Size: L (Large)
Problem / Outcome¶
Stadium sectors require detailed seat definitions before tickets can be sold. The system needs individual seat records for:
- Snake algorithm seat assignment to function correctly
- Seat inventory management (blocking, status changes)
- Stadium visualization in admin interface (fans never see seat maps)
- Ticket generation with specific seat assignments (row/seat numbers)
This feature enables administrators to visually design seat maps using a canvas-based editor (Konva.js), then generate the individual seat records needed by dependent features.
Scope¶
In-Scope:
- Visual Canvas Seat Map Editor (Konva.js):
- Define grid dimensions (rows × max seats per row)
- Draw seats on canvas using mouse (click/drag to add)
- Erase seats to create gaps (staircases, walls, aisles)
- Select seats for bulk operations (type assignment)
- Zoom/pan navigation for large seat maps
- Configure row labels (A, B, C... or 1, 2, 3...)
- Configure numbering direction (LTR or RTL per row)
- Mark specific seats as technical (not for sale - camera positions, media, etc.)
- Designate accessibility seats (wheelchair, companion seats)
- System auto-generates individual seat entities from canvas data
- Validation to prevent duplicate row/seat combinations
- Admin-only inventory visualization - view reserved/sold seats on the same map
Out-of-Scope:
- Import from CAD/DXF/external seat map files (admin draws manually)
- 3D stadium visualization
- Customer-facing seat selection - Fans buy by ticket quantity, snake algorithm assigns seats
- Per-match seat modifications (covered in E3 - Seat Inventory Management)
Visual Editor Workflow¶
1. Admin creates sector in stadium template
2. Admin opens "Design Seat Map" for sector
3. Canvas editor loads with empty grid (or existing map)
4. Admin uses tools:
- Draw (pencil): Click/drag to add seats
- Erase (eraser): Click/drag to remove seats (create gaps)
- Select: Click/drag to select seats for bulk operations
- Fill: Fill rectangular area with seats
5. Admin configures row labels and numbering direction
6. Admin marks seat types (technical, accessibility)
7. Admin saves seat map → JSON stored in database
8. Admin clicks "Generate Seats" → System creates Seat records
9. Sector ready for match configuration
Acceptance Criteria¶
- AC1: Given a sector exists, when admin opens seat map editor, then empty canvas loads with configurable grid dimensions
- AC2: Given canvas is open, when admin draws seats with pencil tool, then seats appear on grid
- AC3: Given seats exist on canvas, when admin uses eraser tool, then seats are removed (creating gaps)
- AC4: Given seats exist on canvas, when admin saves, then JSON seat map is stored in database
- AC5: Given seat map exists, when admin clicks "Generate Seats", then individual Seat records are created
- AC6: Given seats are selected, when admin changes type to technical, then seats are marked as non-sellable
- AC7: Given seats are selected, when admin changes type to accessibility, then seats are flagged for accessible seating
- AC8: Given row/seat combination, when duplicate would be created, then system prevents save and shows error
- AC9: Given seat map loaded, when admin sets numbering direction to RTL, then seat numbers display right-to-left
- AC10: Given a match is configured, when admin views sector inventory, then sold/reserved seats show with status colors
Data Model Impact¶
sector_seat_maps table:
- id (UUID, PK)
- sector_id (UUID, FK → Sector)
- seat_map_json (JSONB) -- 2D seat map from canvas editor
- total_seats (INTEGER) -- computed from JSON
- configuration_status (ENUM: draft, active)
- created_at (TIMESTAMP)
- updated_at (TIMESTAMP)
UNIQUE constraint: (sector_id)
seat_map_json Structure:
{
"dimensions": { "rows": 50, "cols": 50 },
"seats": [
{ "row": 0, "col": 0, "type": "standard" },
{ "row": 0, "col": 1, "type": "standard" },
{ "row": 0, "col": 4, "type": "technical" },
// gaps are simply missing entries (e.g., no col 2, 3 = staircase)
],
"row_labels": ["A", "B", "C", "D"],
"numbering_direction": "LTR"
}
seats table:
- id (UUID, PK)
- sector_id (UUID, FK → Sector)
- row_identifier (VARCHAR) -- e.g., "A", "1", "AA"
- seat_number (VARCHAR)
- seat_type (ENUM: standard, technical, accessibility, companion)
- created_at (TIMESTAMP)
UNIQUE constraint: (sector_id, row_identifier, seat_number)
sector_snake_configs table:
- id (UUID, PK)
- sector_id (UUID, FK → Sector)
- best_row_index (INTEGER, default 1) -- starting row for snake
- traversal_direction (ENUM: TOP_TO_BOTTOM, BOTTOM_TO_TOP)
- within_row_strategy (ENUM: CENTER_OUTWARD, LEFT_TO_RIGHT, RIGHT_TO_LEFT)
- subsector_grouping_json (JSONB, nullable)
- created_at (TIMESTAMP)
Seat Generation Process:
- Admin saves seat map JSON from canvas editor
- Admin triggers "Generate Seats" action
- System parses JSON seat map
- For each seat entry in JSON:
- Map row index to row label (from
row_labelsarray) - Calculate seat number based on
numbering_direction - Skip gaps (missing entries in array)
- Create Seat record with
seat_typefrom JSON
- Map row index to row label (from
- New seats created with
seat_typefrom canvas data
API Endpoints¶
| Endpoint | Method | Description |
|---|---|---|
/admin/sectors/{sectorId}/seat-map |
GET | Get seat map JSON for canvas editor |
/admin/sectors/{sectorId}/seat-map |
POST | Create new seat map from canvas |
/admin/sectors/{sectorId}/seat-map |
PUT | Update existing seat map |
/admin/sectors/{sectorId}/seat-map |
DELETE | Delete seat map |
/admin/sectors/{sectorId}/seats/generate |
POST | Generate Seat records from seat map |
/admin/sectors/{sectorId}/seats/classify |
POST | Bulk classify seats by type |
/admin/seats/{seatId}/type |
PUT | Update individual seat type |
/admin/sectors/{sectorId}/snake-config |
GET | Get snake algorithm configuration |
/admin/sectors/{sectorId}/snake-config |
PUT | Update snake algorithm configuration |
See API Specification for full endpoint definitions.
Technology Stack¶
| Component | Technology | Purpose |
|---|---|---|
| Canvas Library | Konva.js | 2D canvas rendering for seat map |
| Frontend Framework | Symfony Twig + Alpine.js | Admin portal integration |
| Data Format | JSON (JSONB column) | Flexible seat map storage |
| Backend | PHP 8.2+ / Symfony 7.x | API and business logic |
Permissions/Roles¶
- Admin (Stadium Manager role)
How to Verify¶
./vendor/bin/phpunit tests/E2/F10/ --testdox
Test Scenarios:
- Create seat map with Draw tool: verify seats added to JSON
- Use Erase tool: verify gaps created (missing seats in JSON)
- Save and reload: verify canvas renders correctly from JSON
- Generate seats: verify Seat records created with correct row/number
- Configure RTL numbering: verify seat numbers right-to-left
- Mark seats as technical: verify excluded from sellable capacity
- View inventory on canvas: verify sold seats show correct color
Dependencies¶
- E2-F3: Stadium Template Management - Sectors must exist before seat maps can be configured
Related Features¶
- E3-F1: Seat Status Model - Uses generated seat records
- E3-F5: Stadium Visualization - Admin inventory viewing
- E4-F3: Snake Algorithm Seat Assignment - Requires individual seats for assignment
Implementation Tasks¶
See: - E2: Match & Stadium Tasks - Backend tasks - Visual Stadium Editor Tasks - Frontend canvas editor tasks
Doc References¶
- Admin Stadium Configuration Flow
- Seat Map Configuration Guide
- Stadium Design Tool Tech
- API Specification
Last Updated: February 2026