# Phase 4: Reservation System (Filament + Manager API + Customer API)

## Objective
Deliver a complete reservation system accessible from **all three interfaces**: Filament admin, restaurant manager mobile app, and customer mobile app. Includes table assignment, availability checking, multi-table support for large parties, and optional paid reservations.

---

## Filament Admin

### 4.1 ReservationsRelationManager

**File:** `app/Filament/Resources/RestaurantResource/RelationManagers/ReservationsRelationManager.php`

**Form:**
- `user_id` (Select, searchable — customer)
- `date` (DatePicker)
- `start_at` (TimePicker)
- `end_at` (TimePicker, auto-calculated from restaurant's reservation_duration)
- `party_size` (TextInput, numeric)
- `special_requests` (Textarea, nullable)
- `status` (Select from RestaurantReservationStatus enum)
- `tables` (Select multiple from restaurant's tables, filtered to show only available)

**Table Columns:**
- Date, Time (start_at), Customer name, Party size, Tables (numbers), Status (badge with colors), created_at

**Status Badge Colors:**
- PENDING → yellow
- CONFIRMED → blue
- SEATED → green
- COMPLETED → gray
- CANCELLED → red
- NO_SHOW → orange

**Filters:**
- Status, Date range, Party size range

**Actions:**
- **Confirm** — sets status to CONFIRMED
- **Seat** — sets status to SEATED, updates table statuses to OCCUPIED
- **Complete** — sets status to COMPLETED, updates table statuses to AVAILABLE
- **No-Show** — sets status to NO_SHOW
- **Cancel** — prompts for reason, sets status to CANCELLED

**Bulk Actions:**
- Confirm selected, Cancel selected

**Header Stats:**
- Today's reservations count, Pending count, Seated count, Expected covers

### 4.2 Standalone ReservationResource (optional)

**File:** `app/Filament/Resources/RestaurantReservationResource.php`

For cross-restaurant reservation management. Read-only list with filters by restaurant, date, status. Links to restaurant's relation manager for editing.

---

## API Endpoints (Restaurant Manager)

### 4.3 List Reservations

**Controller:** `app/Http/Controllers/Api/Employee/RestaurantManager/ListReservationsController.php`

- `GET /v1/places/{place}/restaurants/{restaurant}/reservations`
- Query params: `date` (defaults today), `status`, `from_date`, `to_date`, `page`, `size`
- Returns paginated reservations with customer info, tables, status
- Default sort: start_at ASC

### 4.4 Show Reservation

**Controller:** `app/Http/Controllers/Api/Employee/RestaurantManager/ShowReservationController.php`

- `GET /v1/places/{place}/restaurants/{restaurant}/reservations/{reservation}`
- Returns full detail including:
  - Customer info (name, email, phone)
  - Tables assigned (numbers, zones, seats)
  - Pre-order details if any
  - Payment info if paid reservation
  - Status history timestamps (confirmed_at, seated_at, etc.)

### 4.5 Create Reservation (Walk-in / Manager)

**Controller:** `app/Http/Controllers/Api/Employee/RestaurantManager/CreateReservationController.php`

- `POST /v1/places/{place}/restaurants/{restaurant}/reservations`
- Body:
```json
{
  "customer_id": "user_public_id",
  "date": "2026-03-14",
  "start_at": "19:30",
  "party_size": 4,
  "special_requests": "Window table preferred",
  "table_ids": ["table_public_id_1", "table_public_id_2"]
}
```
- If `customer_id` not provided, can also pass `customer_name` + `customer_phone` for walk-in
- Auto-calculates `end_at` from restaurant's `reservation_duration`
- If `auto_confirm_reservations` is true → status = CONFIRMED, else PENDING

### 4.6 Update Reservation Status

**Controller:** `app/Http/Controllers/Api/Employee/RestaurantManager/UpdateReservationStatusController.php`

- `PATCH /v1/places/{place}/restaurants/{restaurant}/reservations/{reservation}/status`
- Body: `{ status: "confirmed|seated|completed|no_show|cancelled", cancellation_reason?: "..." }`
- Status transitions:
  - PENDING → CONFIRMED, CANCELLED
  - CONFIRMED → SEATED, CANCELLED, NO_SHOW
  - SEATED → COMPLETED
  - COMPLETED → (terminal)
  - CANCELLED → (terminal)
  - NO_SHOW → (terminal)
- Side effects:
  - SEATED: assigned tables → status OCCUPIED
  - COMPLETED: assigned tables → status AVAILABLE
  - NO_SHOW: assigned tables → status AVAILABLE
  - CANCELLED: assigned tables → status AVAILABLE

### 4.7 Assign Tables to Reservation

**Controller:** `app/Http/Controllers/Api/Employee/RestaurantManager/AssignReservationTablesController.php`

- `POST /v1/places/{place}/restaurants/{restaurant}/reservations/{reservation}/tables`
- Body: `{ table_ids: ["table_public_id_1", "table_public_id_2"] }`
- Replaces existing table assignments
- Validates tables are available for the reservation time slot
- Validates total seats >= party_size (warning, not blocking)

---

## API Endpoints (Customer)

### 4.8 Get Available Slots

**Controller:** `app/Http/Controllers/Api/Restaurant/AvailableSlotsController.php`

- `GET /v1/places/{place}/restaurants/{restaurant}/availability`
- Query params: `date` (required), `party_size` (required)
- Returns available time slots based on:
  - Restaurant opening hours for that day
  - Service periods active for that day
  - Table availability (enough seats for party_size)
  - Exceptional closures
- Response:
```json
{
  "date": "2026-03-14",
  "party_size": 4,
  "slots": [
    { "start_at": "12:00", "end_at": "13:30", "service_period": "lunch" },
    { "start_at": "12:30", "end_at": "14:00", "service_period": "lunch" },
    { "start_at": "19:00", "end_at": "20:30", "service_period": "dinner" },
    { "start_at": "19:30", "end_at": "21:00", "service_period": "dinner" }
  ]
}
```

### 4.9 Create Reservation (Customer)

**Controller:** `app/Http/Controllers/Api/Restaurant/CreateReservationController.php`

- `POST /v1/places/{place}/restaurants/{restaurant}/reservations`
- Body:
```json
{
  "date": "2026-03-14",
  "start_at": "19:30",
  "party_size": 4,
  "special_requests": "Birthday celebration"
}
```
- Customer = authenticated user
- Auto-calculates `end_at` from restaurant's `reservation_duration`
- Status depends on `auto_confirm_reservations` setting
- Tables assigned later by restaurant manager (or auto-assigned if simple logic)

### 4.10 List My Reservations (Customer)

**Controller:** `app/Http/Controllers/Api/Restaurant/ListMyReservationsController.php`

- `GET /v1/places/{place}/restaurants/reservations`
- Returns customer's reservations across all restaurants for the place
- Query params: `status`, `from_date`
- Default: upcoming reservations (date >= today)

### 4.11 Cancel Reservation (Customer)

**Controller:** `app/Http/Controllers/Api/Restaurant/CancelReservationController.php`

- `DELETE /v1/places/{place}/restaurants/{restaurant}/reservations/{reservation}`
- Only allowed if:
  - Reservation belongs to authenticated user
  - Status is PENDING or CONFIRMED
  - Cancellation deadline not passed (configurable per restaurant via `reservation_delay`)
- Side effects: assigned tables → status AVAILABLE

---

## Actions

### 4.12 CreateReservationAction

**File:** `app/Actions/RestaurantManager/CreateReservationAction.php`

Shared between manager and customer controllers. Handles:
- Availability validation
- End time calculation
- Status determination (auto-confirm or pending)
- Table assignment (if provided)
- Notification dispatch

### 4.13 UpdateReservationStatusAction

**File:** `app/Actions/RestaurantManager/UpdateReservationStatusAction.php`

Handles:
- Status transition validation
- Table status side effects
- Timestamp updates (confirmed_at, seated_at, etc.)
- Notification dispatch

---

## Orderable Implementation (Optional Paid Reservations)

RestaurantReservation implements `Orderable` interface. When a restaurant charges for reservations:

- `orderableMoneyPrice(...)` — returns restaurant's configured reservation fee (stored on Restaurant model or as a fixed value)
- `orderableConstraints()`:
  - `RequiresMetadata(['date', 'start_at', 'party_size'])`
  - `MustBeTodayOrFuture()`
  - `RestaurantMustBeAvailable()`

### 4.14 RestaurantMustBeAvailable Constraint

**File:** `app/OrderableConstraints/RestaurantMustBeAvailable.php`

Custom orderable constraint:
- Checks restaurant is open at requested time
- Checks enough tables/seats available for party_size
- Uses `RestaurantAvailabilityService`

---

## Resources (API Serialization)

- `ReservationResource` — reservation summary for lists
- `ReservationDetailResource` — full detail with tables, customer, pre-order
- `CustomerReservationResource` — customer view (less detail)
- `AvailableSlotResource` — time slot

---

## Notifications

- `ReservationConfirmedNotification` — sent to customer when reservation confirmed
- `ReservationReminderNotification` — sent to customer N hours before (scheduled job, Phase 7)

---

## Tests

**File:** `tests/Feature/Controllers/RestaurantManager/ReservationsTest.php`

- [ ] Manager: List reservations for a date works
- [ ] Manager: Create reservation with tables works
- [ ] Manager: Create walk-in reservation works
- [ ] Manager: Status transition PENDING → CONFIRMED works
- [ ] Manager: Status transition CONFIRMED → SEATED updates table status
- [ ] Manager: Status transition SEATED → COMPLETED frees tables
- [ ] Manager: Invalid status transition returns 422
- [ ] Manager: Assign tables validates availability
- [ ] Manager: Assign tables warns if seats < party_size
- [ ] Customer: Get available slots returns correct windows
- [ ] Customer: Available slots respects closures
- [ ] Customer: Create reservation with auto-confirm works
- [ ] Customer: Create reservation without auto-confirm returns PENDING
- [ ] Customer: Create reservation for fully booked slot returns 422
- [ ] Customer: List my reservations shows only own
- [ ] Customer: Cancel PENDING reservation works
- [ ] Customer: Cancel SEATED reservation fails
- [ ] Customer: Cancel other user's reservation fails
- [ ] Multi-table: Assign 2 tables to large party works
- [ ] Paid reservation: Orderable flow creates order item
- [ ] Filament: CRUD operations match API
- [ ] All status badge colors render correctly
- [ ] All endpoints return public_id, never id
- [ ] Non-authorized users get 403

---

## Files Created/Modified

| Action | File |
|---|---|
| Create | `app/Filament/Resources/RestaurantResource/RelationManagers/ReservationsRelationManager.php` |
| Create | `app/Filament/Resources/RestaurantReservationResource.php` (optional) |
| Create | Controllers: 9 API controllers (5 manager + 4 customer) |
| Create | `app/Actions/RestaurantManager/CreateReservationAction.php` |
| Create | `app/Actions/RestaurantManager/UpdateReservationStatusAction.php` |
| Create | `app/OrderableConstraints/RestaurantMustBeAvailable.php` |
| Create | Resources: 4 API resources |
| Create | Requests: 5 form requests |
| Create | `app/Notifications/ReservationConfirmedNotification.php` |
| Create | `tests/Feature/Controllers/RestaurantManager/ReservationsTest.php` |
| Modify | `routes/api.php` — add reservation routes (manager + customer) |

---

## Acceptance Criteria

- [ ] Filament: Full reservation management from RestaurantResource
- [ ] Manager API: Create, list, update status, assign tables
- [ ] Customer API: Check availability, create, list, cancel reservations
- [ ] Status transitions enforced with correct side effects on tables
- [ ] Multi-table assignment works for large parties
- [ ] Availability calculation respects hours, closures, existing reservations
- [ ] Auto-confirm setting respected
- [ ] Paid reservation flow works via Orderable
- [ ] Confirmation notification sent
- [ ] Customer can only manage own reservations
- [ ] Swagger annotations on all controllers
- [ ] All tests pass
- [ ] `vendor/bin/pint` passes on all new/modified files
- [ ] `vendor/bin/phpstan --level=7` passes on all new/modified files
