Preskoči na sadržaj

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:

  1. Admin saves seat map JSON from canvas editor
  2. Admin triggers "Generate Seats" action
  3. System parses JSON seat map
  4. For each seat entry in JSON:
    • Map row index to row label (from row_labels array)
    • Calculate seat number based on numbering_direction
    • Skip gaps (missing entries in array)
    • Create Seat record with seat_type from JSON
  5. New seats created with seat_type from 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:

  1. Create seat map with Draw tool: verify seats added to JSON
  2. Use Erase tool: verify gaps created (missing seats in JSON)
  3. Save and reload: verify canvas renders correctly from JSON
  4. Generate seats: verify Seat records created with correct row/number
  5. Configure RTL numbering: verify seat numbers right-to-left
  6. Mark seats as technical: verify excluded from sellable capacity
  7. View inventory on canvas: verify sold seats show correct color

Dependencies

Implementation Tasks

See: - E2: Match & Stadium Tasks - Backend tasks - Visual Stadium Editor Tasks - Frontend canvas editor tasks

Doc References


Last Updated: February 2026