# Phase 3: Tables & Availability (Filament + API) — DONE

**Completed:** 2026-03-13

## What was delivered

### New Model & Migration

- `app/Models/RestaurantExceptionalClosure.php` — Exceptional closure model with HasPublicId, LogsActivity
- `database/migrations/2026_08_01_000012_create_restaurant_exceptional_closures_table.php` — date, reason, full_day, closed_from, closed_to
- `database/factories/RestaurantExceptionalClosureFactory.php` — With partial() state for time-range closures

### Filament Admin

#### RelationManagers (2)
- `app/Filament/Resources/RestaurantResource/RelationManagers/TablesRelationManager.php` — Full CRUD with zone/status badges, filters (zone, status, is_active), bulk actions
- `app/Filament/Resources/RestaurantResource/RelationManagers/ExceptionalClosuresRelationManager.php` — CRUD with reactive full_day toggle, conditional time pickers

#### Modified
- `app/Filament/Resources/RestaurantResource.php` — Added TablesRelationManager + ExceptionalClosuresRelationManager to getRelations()

### API Resources (4)
- `app/Http/Resources/RestaurantManager/RestaurantTableResource.php` — Table with public_id, number, label, seats, zone, status
- `app/Http/Resources/RestaurantManager/RestaurantTableStatusResource.php` — Table with live reservation/customer info
- `app/Http/Resources/RestaurantManager/RestaurantHoursResource.php` — Opening hours + service periods + closures
- `app/Http/Resources/RestaurantManager/ExceptionalClosureResource.php` — Closure with date, reason, full_day, time range

### FormRequests (5)
- `CreateTableRequest` — number, seats, zone required
- `UpdateTableRequest` — all fields optional
- `UpdateTableStatusRequest` — status required, enum validated
- `UpdateRestaurantHoursRequest` — opening_hours and service_periods arrays
- `CreateExceptionalClosureRequest` — date required, conditional time fields

### API Controllers — Restaurant Manager (11)

#### Tables CRUD (4)
- `ListTablesController` — GET with zone, status, include_inactive filters
- `CreateTableController` — POST with auto restaurant_id
- `UpdateTableController` — PUT with public_id resolution
- `DeleteTableController` — DELETE with future reservations check (409)

#### Table Status (2)
- `UpdateTableStatusController` — PATCH status enum
- `TablesStatusController` — GET live status with summary (total/available/occupied/reserved/maintenance/seats)

#### Hours & Closures (5)
- `GetRestaurantHoursController` — GET opening_hours + service_periods + upcoming closures
- `UpdateRestaurantHoursController` — PUT opening_hours and/or service_periods
- `ListExceptionalClosuresController` — GET upcoming closures
- `CreateExceptionalClosureController` — POST with date validation
- `DeleteExceptionalClosureController` — DELETE by public_id

### Service (1)
- `app/Services/RestaurantManager/RestaurantAvailabilityService.php` — Shared availability logic:
  - `isOpen()` — Checks opening hours + exceptional closures
  - `isClosedExceptionally()` — Full day and partial closures
  - `getAvailableTables()` — Filters by capacity, status, reservation conflicts
  - `getAvailableSlots()` — Time slots by service period
  - `getCurrentServicePeriod()` — Returns active period
  - `getServicePeriodTimeRange()` — Period time range lookup
  - `isTableAvailable()` — Single table availability check

### Routes
- 11 Restaurant Manager routes registered under `v1/places/{place}/restaurants/{restaurant}/`
  - 6 table routes (list, status, create, update, delete, patch status)
  - 5 hours/closure routes (get hours, update hours, list closures, create closure, delete closure)

### Modified Files
- `app/Models/Restaurant.php` — Added `exceptionalClosures()` HasMany relation + @property-read
- `app/Filament/Resources/RestaurantResource.php` — Added 2 relation managers
- `routes/api.php` — Added Phase 3 routes

### Tests
- `tests/Feature/Controllers/RestaurantManager/TablesTest.php` — 16 test cases covering:
  - Table list with correct structure
  - Table filtering by zone
  - Table create with valid data
  - Duplicate table number constraint
  - Table status update via PATCH
  - Table delete (soft delete)
  - Table delete blocked by future reservations (409)
  - Tables status endpoint with summary stats
  - Get restaurant hours
  - Update restaurant hours
  - Create exceptional closure
  - List exceptional closures
  - Delete exceptional closure
  - Availability service: exceptional closures
  - Availability service: table availability (clean, overlapping, maintenance)
  - Authorization: non-restaurant-manager gets 403
  - Public ID: never exposes internal ID

## Quality Checks
- `vendor/bin/pint` — PASS (29 files, 3 auto-fixed style issues)
- `vendor/bin/phpstan --level=7` — PASS (0 errors, 28 files analysed)

## Files Summary

| Type | Count |
|---|---|
| Models | 1 |
| Migrations | 1 |
| Factories | 1 |
| Filament RelationManagers | 2 |
| API Resources | 4 |
| FormRequests | 5 |
| API Controllers | 11 |
| Services | 1 |
| Tests | 1 (16 test cases) |
| Modified files | 3 (Restaurant.php, RestaurantResource.php, routes/api.php) |
| **Total new files** | **26** |
