# Reservation Wizard - Technical Documentation

## Overview

The Reservation Wizard is a multi-step public reservation booking system built with **Livewire**. It allows guests to make restaurant reservations without authentication. The wizard presents **5 or 6 steps** depending on the `require_payment` setting:

**When `require_payment` is `true` (default) — 6 steps:**

1. Date & Time Selection
2. Guest Information
3. Special Requests
4. Review Summary
5. Menu Selection
6. Payment

**When `require_payment` is `false` — 5 steps:**

1. Date & Time Selection
2. Guest Information
3. Special Requests
4. Review Summary
5. Menu Selection (final step — reservation created directly)

> **Note:** When payment is disabled, completing Step 5 creates the reservation immediately. The status is set to `confirmed` if `auto_confirm` is `true`, or `pending` if `false` (requiring admin manual confirmation). Appropriate emails are sent based on the `auto_confirm` and notification settings.

---

## Architecture

### Core Components

| Layer                    | File                                                    | Responsibility                                                        |
| ------------------------ | ------------------------------------------------------- | --------------------------------------------------------------------- |
| **Livewire Component**   | `app/Livewire/ReservationWizard.php`                    | State management, step validation, business orchestration             |
| **Blade View**           | `resources/views/livewire/reservation-wizard.blade.php` | 6-step UI rendering                                                   |
| **Availability Service** | `app/Services/AvailabilityService.php`                  | Time slot generation, table availability, blocked date checks         |
| **Reservation Service**  | `app/Services/ReservationService.php`                   | Reservation creation, cancellation, confirmation, deposit calculation |
| **Notification Service** | `app/Services/NotificationService.php`                  | Email notifications (confirmation, reminder, cancellation, admin)     |
| **Payment Service**      | `app/Services/Payment/PaytrService.php`                 | PayTR payment gateway integration                                     |
| **Reservation Model**    | `app/Models/Reservation.php`                            | Eloquent model (`res_reservations` table)                             |
| **Public Controller**    | `app/Http/Controllers/PublicReservationController.php`  | Route entry point                                                     |

### Routes

All routes are under the `reservation` prefix and do **not** require authentication:

```
GET  /reservation/{slug}                          → show wizard
POST /reservation/{slug}                          → store reservation
POST /reservation/{slug}/payment/initiate         → initiate PayTR payment
GET  /reservation/payment/checkout/{resId}/{payId} → PayTR checkout
GET  /reservation/payment/success/{resId}         → payment success
GET  /reservation/payment/fail/{resId}            → payment failure
POST /reservation/payment/callback                → PayTR callback
GET  /reservation/{slug}/success                  → success page
GET  /reservation/{slug}/failed                   → failure page
```

---

## Wizard Flow

### When `require_payment` is true (default — 6 steps)

```
graph LR
    A[Step 1: Date & Time] --> B[Step 2: Guest Info]
    B --> C[Step 3: Special Requests]
    C --> D[Step 4: Review Summary]
    D --> E[Step 5: Menu Selection]
    E --> F[Step 6: Payment]
```

### When `require_payment` is false (5 steps)

```
graph LR
    A[Step 1: Date & Time] --> B[Step 2: Guest Info]
    B --> C[Step 3: Special Requests]
    C --> D[Step 4: Review Summary]
    D --> E[Step 5: Menu + Confirm]
    E --> F[Success Page]
```

---

## Step-by-Step Breakdown

### Step 1 — Date & Time Selection (`currentStep === 1`)

**Purpose:** Select reservation date, time slot, and guest count.

**Component Properties:**

- `reservationDate` (string, `Y-m-d`)
- `reservationTime` (string, `H:i`)
- `guestCount` (int, default: `2`)
- `availableTimeSlots` (array)
- `closedDates` (array of `Y-m-d` strings)
- `duration` (int, minutes, from restaurant settings)

**UI Elements:**

- Guest count selector with `+` / `-` buttons (range: 1–20)
- Horizontal scrollable date carousel showing 30 days ahead
- Closed dates are filtered out from the carousel
- Time slot grid grouped by **service period** (e.g., lunch, dinner)

**Key Behaviors:**

1. On `mount()`, closed dates for the next 30 days are loaded via `AvailabilityService::isDateAvailable()`
2. When a date is selected (`selectDate()`), time slots are loaded via `AvailabilityService::getAvailableTimeSlots()`
3. Time slots are grouped by service period name in the view
4. Selecting a time slot (`selectTimeSlot()`) also determines the service period and pre-loads menu items, then auto-advances to Step 2

**Validation:**

- `reservationDate` — required, date, today or future
- `reservationTime` — required, string
- `guestCount` — required, integer, min 1

**Availability Logic (AvailabilityService):**

1. Check if date is blocked (`BlockedDate` model)
2. Get service periods for that day of week from `restaurant.settings.working_hours`
3. For each period, generate time slots at configurable intervals (default: 30 min)
4. For each slot, check table availability (no overlapping confirmed/pending reservations)
5. Only show slots where at least one table has sufficient capacity

---

### Step 2 — Guest Information (`currentStep === 2`)

**Purpose:** Collect guest contact details.

**Component Properties:**

- `guestName` (string)
- `guestEmail` (string)
- `guestPhone` (string)

**UI Elements:**

- Full name input (`wire:model="guestName"`)
- Email input (`wire:model="guestEmail"`)
- Phone input (`wire:model="guestPhone"`)
- Privacy notice card

**Validation:**

- `guestName` — required, string, max 255
- `guestEmail` — required, email, max 255
- `guestPhone` — required, string, max 20

---

### Step 3 — Special Requests (`currentStep === 3`)

**Purpose:** Allow guests to specify preferences and custom notes.

**Component Properties:**

- `specialRequests` (string, max 1000 chars)
- `selectedPreferences` (array of string tags)
- `menuNotes` (string, max 1000 chars)

**UI Elements:**

- Preference tag buttons (toggle on/off):
    - `birthday` — Birthday celebration
    - `window` — Window seat preference
    - `quiet` — Quiet table
    - `anniversary` — Anniversary celebration
    - `allergy` — Food allergy
- Custom notes textarea (`wire:model="specialRequests"`, max 1000 chars)
- Allergy warning disclaimer

**Key Behaviors:**

- Toggling a preference tag appends/removes a localized label string to `specialRequests`
- The `updateSpecialRequestsFromPreferences()` method manages merging preference labels with custom text

**Validation:**

- `specialRequests` — nullable, string, max 1000

---

### Step 4 — Review Summary (`currentStep === 4`)

**Purpose:** Display all reservation details for guest confirmation before proceeding.

**Component Properties:**

- `reservationSummary` (array)

**Summary Data Structure:**

```php
[
    'date'            => '2025-05-15',
    'time'            => '19:00',
    'guest_count'     => 4,
    'name'            => 'John Doe',
    'email'           => 'john@example.com',
    'phone'           => '05XX XXX XX XX',
    'special_requests' => 'Window seat, Birthday',
    'menu_notes'      => '',
    'duration'        => 120,
    'menu_items'      => [],
    'service_period'  => 'Dinner',
]
```

**UI Elements:**

- Date & time card (with "Change Schedule" button to go back)
- Guest count card
- Reservation holder card (name + email)
- Special requests block (if any)
- CTA button: "Proceed to Menu Selection"

**Key Behaviors:**

- `prepareSummary()` is called when entering Step 4
- The "Change Schedule" button calls `previousStep()` to return to Step 1

**Validation:**

- No additional validation (all data validated in previous steps)

---

### Step 5 — Menu Selection (`currentStep === 5`)

**Purpose:** Allow guests to browse and select menu options associated with the chosen service period.

**Component Properties:**

- `selectedMenuItems` (array, keyed by menu item ID)
- `availableMenuItems` (array)
- `selectedServicePeriod` (string, e.g., "Lunch", "Dinner")
- `menuNotes` (string)

**UI Elements:**

- Menu item cards in a 2-column grid
- Each card shows name, description, and price (informational only)
- Selected cards are highlighted with a secondary border
- Menu notes textarea (auto-populated with selected menu names)
- Selected items summary panel
- Sidebar with instructions ("How it works?")

**Key Behaviors:**

1. Menu items are loaded from `Menu::active()->where('service', $selectedServicePeriod)` when a time slot is selected (Step 1)
2. Two selection modes controlled by `restaurant.settings.reservation_settings.allow_multiple_menu_selection`:
    - **Multiple selection** — toggle on/off any combination
    - **Single selection** — only one item can be selected at a time
3. `toggleMenuItem()` adds/removes items and auto-updates `menuNotes` with `✓ MenuName` entries
4. Menu prices are **informational only** — they do not affect the deposit amount

**Validation:**

- `menuNotes` — nullable, string, max 1000

---

### Step 6 — Payment (`currentStep === 6`)

**Purpose:** Create the reservation in the database and present the payment form.

**Component Properties:**

- `reservationId` (int, set after creation)
- `depositAmount` (float)
- `paymentToken` (string, `'PENDING'` after creation)
- `iframeUrl` (string, `'PENDING'` after creation)
- `isLoading` (bool)

**UI Elements:**

- Selected menu preferences summary (informational)
- Deposit amount display (formatted as currency)
- "Make Payment" button (submits a form to PayController)

**Key Behaviors (when transitioning from Step 5 → 6):**

1. `sendAdminNotificationAndInitiatePayment()` is called:
    - Creates the reservation via `ReservationService::createReservation()`
    - Sends admin notification email via `NotificationService::sendAdminNotification()`
2. Reservation creation process:
    - Validates all required data
    - Checks date is not blocked
    - Calculates end time from `reservation_time` + `duration`
    - Finds an available table with sufficient capacity and no overlaps
    - Calculates deposit amount (fixed or percentage-based from settings)
    - Generates unique confirmation code (8-char alphanumeric)
    - Creates reservation with `status: 'pending_payment'`
    - Creates a notification record
3. Payment is initiated via a traditional POST form to `PayController::initiate()` (PayTR iframe integration)

**Reservation Status Lifecycle:**

```
pending_payment → paid → confirmed → completed
                                      ↘ no_show
pending_payment → cancelled (by customer/admin/system)
paid           → cancelled
confirmed      → cancelled
```

**Deposit Calculation:**

- **Fixed:** Uses `reservation_settings.deposit_value` directly
- **Percentage:** `(base_amount_per_guest × guest_count) × deposit_value / 100`

---

## Client-Side Interactions

### Livewire Events

| Event                      | Trigger                              | Effect                       |
| -------------------------- | ------------------------------------ | ---------------------------- |
| `scroll-to-top`            | Step change (next/previous)          | Smooth scrolls page to top   |
| `scroll-to-time-selection` | Date selected or guest count changed | Scrolls to time slot section |

### Auto-Advance Behaviors

- Selecting a time slot (`selectTimeSlot()`) automatically calls `nextStep()` to advance to Step 2
- This also triggers loading of menu items and determination of service period

---

## Database Model — Reservation

**Table:** `res_reservations`

| Column                | Type               | Description                                                                 |
| --------------------- | ------------------ | --------------------------------------------------------------------------- |
| `id`                  | int                | Primary key                                                                 |
| `user_id`             | int, nullable      | Auth user ID (null for public bookings)                                     |
| `current_team_id`     | int                | Team/tenant scope                                                           |
| `restaurant_id`       | int                | FK to restaurants                                                           |
| `table_id`            | int                | FK to tables (auto-assigned)                                                |
| `customer_name`       | string             | Guest full name                                                             |
| `customer_email`      | string             | Guest email                                                                 |
| `customer_phone`      | string             | Guest phone                                                                 |
| `reservation_date`    | date               | Booking date                                                                |
| `reservation_time`    | time               | Booking start time                                                          |
| `end_time`            | time               | Calculated end time                                                         |
| `party_size`          | int                | Number of guests                                                            |
| `duration_minutes`    | int                | Reservation duration                                                        |
| `status`              | string             | `pending_payment`, `paid`, `confirmed`, `cancelled`, `completed`, `no_show` |
| `payment_id`          | int, nullable      | FK to payments                                                              |
| `paid_amount`         | decimal(10,2)      | Deposit amount charged                                                      |
| `currency`            | string             | Currency code                                                               |
| `paid_at`             | datetime, nullable | When payment was received                                                   |
| `cancelled_at`        | datetime, nullable | When cancelled                                                              |
| `cancellation_reason` | string, nullable   | Reason for cancellation                                                     |
| `cancelled_by`        | string, nullable   | `customer`, `admin`, or `system`                                            |
| `special_requests`    | text, nullable     | Guest notes/preferences                                                     |
| `menu_notes`          | text, nullable     | Menu selections and notes                                                   |
| `confirmation_code`   | string, unique     | 8-char alphanumeric code                                                    |
| `admin_notes`         | text, nullable     | Internal admin notes                                                        |
| `created_at`          | datetime           |                                                                             |
| `updated_at`          | datetime           |                                                                             |
| `deleted_at`          | datetime, nullable | Soft deletes                                                                |

**Global Scope:** `CurrentTeamScope` — automatically filters by the current team ID.

---

## Email Notifications

| Type                  | Recipient                                        | Trigger                                | Template                          |
| --------------------- | ------------------------------------------------ | -------------------------------------- | --------------------------------- |
| Admin New Reservation | Admin emails (from settings or official address) | Step 5 → 6 transition                  | `emails.admin-new-reservation`    |
| Customer Confirmation | Guest email                                      | After successful payment               | `emails.reservation-confirmation` |
| Customer Reminder     | Guest email                                      | 24h before reservation (scheduled job) | `emails.reservation-reminder`     |
| Customer Cancellation | Guest email                                      | Reservation cancelled                  | `emails.reservation-cancellation` |

Notification settings are stored in `restaurant.settings.notification_settings`:

| Setting Key               | Type   | Default | Used In                                      | Description                               |
| ------------------------- | ------ | ------- | -------------------------------------------- | ----------------------------------------- |
| `send_confirmation_email` | bool   | true    | Admin Settings UI                            | Enable/disable customer confirmation      |
| `send_reminder_email`     | bool   | true    | Admin Settings UI                            | Enable/disable reminder emails            |
| `reminder_hours_before`   | int    | 24      | Admin Settings UI, `SendReservationReminder` | Hours before reservation to send reminder |
| `admin_email`             | string | —       | Admin Settings UI                            | Single admin notification email           |
| `admin_emails` _(legacy)_ | array  | []      | `NotificationService::sendAdminNotification` | Fallback: array of admin emails           |
| `admin_enabled`           | bool   | true    | `ReservationWizard`                          | Gate for admin notification at Step 6     |
| `customer_enabled`        | bool   | true    | `ReservationService`                         | Gate for customer confirmation email      |
| `reminder_enabled`        | bool   | true    | `SendReservationReminder` job                | Gate for scheduled reminder emails        |

**Admin email resolution order:**

1. `notification_settings.admin_emails` array (legacy)
2. Falls back to official address from `Address::getOfficialAddress()`
3. If neither is configured, admin notification is skipped with a warning log

All notifications are logged in the `ReservationNotification` model for tracking.

---

## Email Delivery Flow

### Path A: Payment Required (`deposit_value > 0`)

```
Guest completes Step 5 → Step 6
    │
    ├── Reservation created (status: 'pending_payment')
    ├── Admin notification email sent immediately
    │       guarded by: notification_settings['admin_enabled']
    │
    ├── Guest clicks "Make Payment" → PayTR checkout
    │
    └── PayTR callback (success)
            ├── ReservationService::confirmReservation()
            │       ├── Status updated to 'confirmed'
            │       └── Customer confirmation email sent
            │               guarded by: notification_settings['customer_enabled']
            └── If callback missed, PayController::success() also calls confirmReservation()
                    (idempotent — won't double-send if already confirmed)
```

### Path B: No Payment Required (`require_payment = false`)

When `require_payment` is disabled, the wizard ends at Step 5. The reservation is created directly:

```
Guest completes Step 5 (Menu Selection) → clicks "REZERVASYONU TAMAMLA"
    │
    ├── Reservation created with status based on auto_confirm:
    │       auto_confirm=true  → status: 'confirmed'
    │       auto_confirm=false → status: 'pending'
    │
    ├── Admin notification email sent
    │       guarded by: notification_settings['admin_enabled']
    │
    ├── (if auto_confirm=true) Customer confirmation email sent
    │       guarded by: notification_settings['customer_enabled']
    │
    └── Redirect to success page
            Shows "Onaylandı!" if confirmed
            Shows "Alındı!" if pending
```

### Key Guarantees

| Scenario                        | Admin Email | Customer Email       |
| ------------------------------- | ----------- | -------------------- |
| Reservation created (Step 6)    | ✅ Sent     | —                    |
| Payment succeeds (callback)     | —           | ✅ Sent              |
| Payment succeeds (success page) | —           | ✅ Sent (idempotent) |
| Admin manually confirms         | —           | ✅ Sent              |
| Reservation cancelled           | —           | ✅ Sent              |
| 24h before reservation (job)    | —           | ✅ Sent (if enabled) |

---

## Service Period Configuration

Working hours are stored in `restaurant.settings.working_hours` as a keyed array by day name:

```php
[
    'monday' => [
        ['name' => 'Lunch',  'open' => '12:00', 'close' => '15:00', 'is_closed' => false],
        ['name' => 'Dinner', 'open' => '18:00', 'close' => '23:00', 'is_closed' => false],
    ],
    'tuesday' => [
        ['open' => '12:00', 'close' => '23:00'], // old single-period format (also supported)
    ],
    'sunday' => [
        ['is_closed' => true], // closed all day
    ],
]
```

The system supports both:

- **Single period format** — `{open, close}` (backward compatible)
- **Multiple period format** — `[{name, open, close, is_closed}]`

---

## Restaurant Settings Reference

Key settings from `restaurant.settings.reservation_settings`:

| Setting                         | Type   | Default   | Description                                              |
| ------------------------------- | ------ | --------- | -------------------------------------------------------- |
| `default_duration`              | int    | 120       | Reservation duration in minutes                          |
| `slot_interval`                 | int    | 30        | Time between slot generations (minutes)                  |
| `deposit_type`                  | string | `'fixed'` | `'fixed'` or `'percentage'`                              |
| `deposit_value`                 | float  | 0         | Deposit amount or percentage (set to `0` for no deposit) |
| `base_amount_per_guest`         | float  | 100       | Base cost per guest (for percentage deposits)            |
| `require_payment`               | bool   | true      | Require payment step; if false, wizard is 5 steps        |
| `auto_confirm`                  | bool   | true      | Auto-confirm reservations; if false, admin must confirm  |
| `allow_multiple_menu_selection` | bool   | true      | Allow selecting multiple menu items                      |

---

## UI Design System

- **Framework:** Tailwind CSS
- **Icons:** Material Symbols Outlined
- **Fonts:** Noto Serif (headings), Inter (labels), system body font
- **Theme:** Light (`#fbf9f5` bg, `#00261e` primary) with dark mode support
- **Layout:** Mobile-first, `max-w-md` wrapper, fixed bottom navigation bar
- **Progress:** Animated progress bar with `cubic-bezier(0.22, 1, 0.36, 1)` easing
