# SmartService Gobot API

API Laravel modulaire pour gérer des organisations/prestataires, leurs services, leurs disponibilités, leurs réservations, leurs médias et les webhooks liés à l’écosystème Gobot/WhatsApp.

Le projet sert à exposer :

- un espace **Super Admin** pour gérer les organisations ;
- un espace **Organisation** pour gérer les services, réservations, membres, disponibilités et médias ;
- une partie **publique** pour permettre aux clients de consulter les services et créer des réservations ;
- une base prête pour les médias locaux aujourd’hui et un passage vers S3 plus tard.

---

## Sommaire

1. [Stack technique](#stack-technique)
2. [Architecture générale](#architecture-générale)
3. [Installation](#installation)
4. [Configuration `.env`](#configuration-env)
5. [Authentification](#authentification)
6. [Gestion des rôles](#gestion-des-rôles)
7. [Organisations](#organisations)
8. [Services](#services)
9. [Disponibilités](#disponibilités)
10. [Réservations](#réservations)
11. [Médias et images](#médias-et-images)
12. [Statistiques](#statistiques)
13. [Webhooks](#webhooks)
14. [Routes publiques](#routes-publiques)
15. [Exemples de payloads](#exemples-de-payloads)
16. [Commandes utiles](#commandes-utiles)
17. [Bonnes pratiques avant déploiement](#bonnes-pratiques-avant-déploiement)

---

## Stack technique

- **Backend** : Laravel
- **Authentification** : Laravel Sanctum
- **Base de données** : PostgreSQL
- **Permissions** : Spatie Laravel Permission
- **Fichiers / médias** : Laravel Filesystem avec support `public` et S3
- **API** : JSON REST

---

## Architecture générale

Le code est organisé par modules dans :

```txt
app/Modules/
├── Auth/
├── Organisation/
├── Service/
├── Availability/
├── Booking/
├── Webhook/
└── Stats/
```

Chaque module contient généralement :

```txt
app/
config/
database/
routes/
resources/
```

Cette organisation permet de séparer proprement les responsabilités métier.

---

## Installation

### 1. Cloner le projet

```bash
git clone https://gitlab.com/ChrislainDev/gobot-smart-services-api.git
cd smartservice_api
```

### 2. Installer les dépendances PHP

```bash
composer install
```

### 3. Copier le fichier d’environnement

```bash
cp .env.example .env
```

### 4. Générer la clé Laravel

```bash
php artisan key:generate
```

### 5. Configurer la base de données

Dans `.env`, configurer PostgreSQL :

```env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=smartservice
DB_USERNAME=postgres
DB_PASSWORD=password
```

### 6. Lancer les migrations et seeders

```bash
php artisan migrate --seed
```

### 7. Créer le lien storage local

```bash
php artisan storage:link
```

### 8. Lancer le serveur local

```bash
php artisan serve
```

Base URL locale :

```txt
http://127.0.0.1:8000/api/v1
```

---

## Configuration `.env`

Exemple minimal :

```env
APP_NAME="SmartService Gobot"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://127.0.0.1:8000

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=smartservice
DB_USERNAME=postgres
DB_PASSWORD=password

FILESYSTEM_DISK=local
MEDIA_DISK=public

SANCTUM_STATEFUL_DOMAINS=localhost:5173,127.0.0.1:5173
SESSION_DOMAIN=localhost
```

### Différence entre `FILESYSTEM_DISK` et `MEDIA_DISK`

| Variable | Rôle |
|---|---|
| `FILESYSTEM_DISK` | Disk par défaut de Laravel pour les fichiers internes si aucun disk n’est précisé. |
| `MEDIA_DISK` | Disk personnalisé utilisé pour les médias publics : logos, photos de services, galeries, documents uploadés côté réservation. |

Configuration actuelle :

```env
FILESYSTEM_DISK=local
MEDIA_DISK=public
```

Plus tard, pour envoyer les médias vers S3 :

```env
FILESYSTEM_DISK=local
MEDIA_DISK=s3
```

Et si toute l’application doit passer sur S3 :

```env
FILESYSTEM_DISK=s3
MEDIA_DISK=s3
```

---

## Authentification

L’authentification utilise **Laravel Sanctum**.

Flux principal :

1. L’utilisateur envoie email + mot de passe.
2. L’API génère un OTP.
3. L’utilisateur confirme l’OTP.
4. L’API retourne la session/token selon la configuration.
5. Les routes protégées utilisent `auth:sanctum`.

### Routes Auth

Préfixe :

```txt
/api/v1/auth
```

| Méthode | Endpoint | Description |
|---|---|---|
| POST | `/login` | Connexion email/mot de passe et génération OTP |
| POST | `/verify-otp` | Vérification OTP |
| POST | `/resend-otp` | Renvoyer OTP |
| GET | `/me` | Utilisateur connecté |
| POST | `/logout` | Déconnexion |
| POST | `/update-profile` | Mise à jour profil utilisateur |
| POST | `/change-password` | Changer le mot de passe |
| POST | `/request-password-reset` | Demande de reset password |
| POST | `/reset-password` | Réinitialisation du mot de passe |

---

## Gestion des rôles

Le projet distingue principalement :

- **Super Admin** : gère toutes les organisations.
- **Owner organisation** : propriétaire d’une organisation.
- **Staff organisation** : membre interne d’une organisation.
- **Viewer organisation** : accès limité à certaines données.

Les routes utilisent notamment :

```txt
auth:sanctum
super.admin
resolve.Organisation
member.can_login
```

---

## Organisations

Une organisation représente un prestataire ou une entreprise cliente du système.

Exemples :

- Hotel Bel Azur Cotonou
- Madame Hôte Conciergerie - I TOUR

Champs importants :

| Champ | Description |
|---|---|
| `id` | UUID de l’organisation |
| `slug` | Identifiant lisible, encore utile côté admin |
| `name` | Nom de l’organisation |
| `description` | Description |
| `category_id` | Catégorie d’organisation |
| `logo_url` | URL publique du logo envoyée au front |
| `logo_disk` | Disk réel du logo, utilisé par le backend |
| `logo_path` | Chemin réel du logo, utilisé par le backend |
| `currency` | Devise, par défaut `XOF` |
| `timezone` | Fuseau horaire, par défaut `Africa/Porto-Novo` |
| `booking_policy` | Politique de réservation |
| `max_members` | Nombre maximum de membres |
| `is_active` | Organisation active ou non |

### Politique de réservation

Exemple :

```json
{
  "auto_accept": false,
  "deposit_percent": 70,
  "modification_free_hours": 72,
  "modification_fee": 5000,
  "cancellation_rules": []
}
```

| Clé | Description |
|---|---|
| `auto_accept` | Si `true`, la réservation est acceptée automatiquement. |
| `deposit_percent` | Pourcentage d’acompte demandé à la réservation. |
| `modification_free_hours` | Délai de modification gratuite. |
| `modification_fee` | Frais de modification. |
| `cancellation_rules` | Règles d'annulation spécifiques. |

### Routes Super Admin organisations

Préfixe :

```txt
/api/v1/admin/organisations
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/categories` | Liste des catégories d’organisation |
| POST | `/categories` | Créer une catégorie |
| PUT | `/categories/{id}` | Modifier une catégorie |
| DELETE | `/categories/{id}` | Supprimer une catégorie |
| GET | `/` | Liste des organisations |
| POST | `/` | Créer une organisation |
| GET | `/{organisation}` | Détail organisation |
| PUT | `/{organisation}` | Modifier organisation |
| DELETE | `/{organisation}` | Supprimer organisation |
| PATCH | `/{organisation}/toggle` | Activer/désactiver organisation |
| GET | `/{organisation}/members` | Membres de l’organisation |
| POST | `/{organisation}/members` | Ajouter un membre |
| PATCH | `/{organisation}/members/{member}` | Modifier membre |
| DELETE | `/{organisation}/members/{member}` | Supprimer membre |
| POST | `/{organisation}/transfer-ownership` | Transférer la propriété |
| GET | `/{organisation}/services` | Services d’une organisation |
| GET | `/{organisation}/bookings` | Réservations d’une organisation |
| GET | `/{organisation}/audit-log` | Journal d’audit |

### Routes profil organisation

Préfixe :

```txt
/api/v1/organisation
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/profile` | Profil organisation connecté |
| PUT | `/profile` | Modifier profil organisation |
| POST | `/profile/logo` | Uploader le logo |

---

## Services

Un service représente une prestation vendue par une organisation.

Exemples :

- Chambre standard
- Suite familiale
- Circuit touristique
- Transfert aéroport
- Restaurant

Champs importants :

| Champ | Description |
|---|---|
| `id` | UUID du service |
| `organisation_id` | Organisation propriétaire |
| `category_id` | Catégorie du service |
| `name` | Nom du service |
| `slug` | Slug du service |
| `description` | Description |
| `duration_minutes` | Durée du service |
| `min_participants` | Nombre minimum de participants |
| `max_participants` | Nombre maximum de participants |
| `max_capacity_per_slot` | Capacité maximum par créneau/jour/période |
| `pricing_type` | Type de tarification |
| `booking_mode` | Mode de réservation |
| `capacity_mode` | Mode de capacité |
| `assignment_enabled` | Si le service peut être assigné à un membre |
| `is_featured` | Service mis en avant |
| `is_active` | Service actif ou non |

### Types de prix

| Valeur | Description |
|---|---|
| `per_person` | Prix multiplié par le nombre de participants |
| `per_group` | Prix global pour le groupe ou l’unité |
| `per_hour` | Prix calculé selon la durée |

### Modes de réservation

| Valeur | Utilisation |
|---|---|
| `timeslot` | Réservation avec date + heure. Exemple : spa, restaurant, consultation. |
| `date_range` | Réservation avec date d’arrivée + date de départ. Exemple : hôtel. |
| `date_only` | Réservation sur une seule date. Exemple : excursion touristique. |

### Routes services organisation

Préfixe :

```txt
/api/v1/organisation/services
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/categories` | Liste catégories services |
| POST | `/categories` | Créer catégorie service |
| GET | `/categories/{id}` | Détail catégorie |
| PUT/PATCH | `/categories/{id}` | Modifier catégorie |
| DELETE | `/categories/{id}` | Supprimer catégorie |
| PATCH | `/categories/{serviceCategory}/toggle` | Activer/désactiver catégorie |
| GET | `/` | Liste services |
| POST | `/` | Créer service |
| GET | `/{service}` | Détail service |
| PUT | `/{service}` | Modifier service |
| DELETE | `/{service}` | Supprimer service |
| PATCH | `/{service}/toggle` | Activer/désactiver service |

---

## Options de services

Une option est un supplément ou une exigence liée à un service.

Exemples :

- Petit déjeuner
- Chambre vue mer
- Document de résidence
- Nombre d’enfants
- Information obligatoire à saisir

Champs importants :

| Champ | Description |
|---|---|
| `name` | Nom de l’option |
| `description` | Description |
| `price_note` | Note sur le prix |
| `is_required` | Option obligatoire ou non |
| `max_quantity` | Quantité maximale |
| `requires_document` | Document demandé ou non |
| `document_timing` | `upload_form` ou `day_of` |
| `document_label` | Libellé du document demandé |
| `requires_input` | Champ texte demandé ou non |
| `input_label` | Libellé du champ |
| `input_placeholder` | Placeholder front |
| `prices` | Prix de l’option |

### Routes options

Préfixe :

```txt
/api/v1/organisation/services/{service}/options
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/` | Liste des options |
| POST | `/` | Créer une option |
| PUT | `/{option}` | Modifier une option |
| DELETE | `/{option}` | Supprimer une option |

---

## Disponibilités

Le module Availability gère les horaires récurrents et les blocages exceptionnels.

### Availability rules

Une availability rule, c’est une règle qui dit :

Ce service est disponible chaque semaine, tel jour, entre telle heure et telle heure.

Exemple :

```json
{
  "day_of_week": 1,
  "start_time": "09:00",
  "end_time": "17:00",
  "max_slots": 10,
  "member_id": null
}
```

Ça veut dire :

Tous les lundis, ce service est disponible de 09h00 à 17h00.
La capacité maximale est de 10 places.
Ce n’est pas lié à un membre précis.

| Champ | Description |
|---|---|
| `day_of_week` | Jour de la semaine : 0 dimanche, 6 samedi |
| `start_time` | Heure de début |
| `end_time` | Heure de fin |
| `max_slots` | Capacité/créneaux max |
| `member_id` | Membre concerné, optionnel |

### Availability blocks

Une availability block, c’est l’inverse de availability rule.

Ça dit :

Normalement le service est disponible, mais à cette date ou à cette heure précise, il ne l’est pas.

Exemple journée complète :

```json
{
  "date": "2026-06-12",
  "block_type": "full_day",
  "reason": "Fermeture exceptionnelle"
}
```

Exemple plage horaire :

```json
{
  "date": "2026-06-12",
  "block_type": "time_range",
  "start_time": "12:00",
  "end_time": "14:00",
  "reason": "Pause équipe"
}
```

### Routes disponibilité organisation

Préfixe :

```txt
/api/v1/organisation/services/{service}
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/rules` | Liste des règles |
| POST | `/rules` | Créer une règle |
| PATCH | `/rules/{rule}` | Activer/désactiver règle |
| DELETE | `/rules/{rule}` | Supprimer règle |
| GET | `/blocks` | Liste des blocages |
| POST | `/blocks` | Créer blocage |
| DELETE | `/blocks/{block}` | Supprimer blocage |

### Route disponibilité publique

```txt
GET /api/v1/public/{organisation}/services/{service}/availability
```

Paramètres possibles selon le mode :

```txt
date=2026-06-12
member_id=<uuid optionnel>
```

---

## Réservations

Une réservation appartient à une organisation et à un service.

Champs importants :

| Champ | Description |
|---|---|
| `id` | UUID réservation |
| `reference` | Référence publique de réservation |
| `organisation_id` | Organisation concernée |
| `service_id` | Service réservé |
| `client_data` | Informations client |
| `selected_options` | Options choisies |
| `scheduled_date` | Date pour `timeslot` ou `date_only` |
| `scheduled_time` | Heure pour `timeslot` |
| `check_in_date` | Date d’arrivée pour `date_range` |
| `check_out_date` | Date de départ pour `date_range` |
| `rooms_quantity` | Nombre de chambres/unités |
| `participants_count` | Nombre de participants |
| `status` | Statut de réservation |
| `payment_status` | Statut de paiement |
| `pricing_snapshot` | Snapshot du prix au moment de la réservation |

### Statuts de réservation

| Statut | Description |
|---|---|
| `pending` | En attente |
| `accepted` | Acceptée |
| `rejected` | Rejetée |
| `cancelled_by_client` | Annulée par le client |
| `cancelled_by_provider` | Annulée par le prestataire |
| `completed` | Terminée |

### Statuts de paiement

| Statut | Description |
|---|---|
| `unpaid` | Non payé |
| `pending` | Paiement en attente |
| `paid` | Payé |
| `failed` | Échec paiement |
| `refunded` | Remboursé |

### Routes réservations publiques

Préfixe :

```txt
/api/v1/public/{organisation}/bookings
```

| Méthode | Endpoint | Description |
|---|---|---|
| POST | `/` | Créer une réservation |
| GET | `/{reference}` | Voir une réservation publique par référence |

### Routes réservations organisation

Préfixe :

```txt
/api/v1/organisation/bookings
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/agenda` | Vue agenda |
| GET | `/` | Liste réservations |
| GET | `/{booking}` | Détail réservation |
| PATCH | `/{booking}/accept` | Accepter |
| PATCH | `/{booking}/reject` | Rejeter |
| PATCH | `/{booking}/complete` | Marquer terminée |
| PATCH | `/{booking}/cancel` | Annuler |
| PATCH | `/{booking}/assign` | Assigner à un membre |
| PATCH | `/{booking}/reschedule` | Reprogrammer |

---

## Médias et images

Le projet utilise une table `media` polymorphique pour gérer les images.

Un média peut être lié à :

- une organisation ;
- un service ;
- une option de service.

Champs importants :

| Champ | Description |
|---|---|
| `url` | URL publique historique ou générée |
| `disk` | Disk de stockage réel : `public`, `s3`, etc. |
| `path` | Chemin réel dans le disk |
| `mime_type` | Type MIME |
| `size` | Taille du fichier |
| `is_featured` | Image principale |
| `caption` | Légende |
| `sort_order` | Ordre d’affichage |

### Principe important

Le front reçoit simplement :

```json
{
  "url": "https://..."
}
```

Le backend garde les détails techniques :

```txt
disk
path
```

Cela permet de passer plus facilement de `public` vers `s3` sans changer le front.

### Logos organisation

Les logos sont stockés avec :

```txt
logo_url
logo_disk
logo_path
```

Le front utilise uniquement :

```txt
logo_url
```

Le backend utilise :

```txt
logo_disk + logo_path
```

pour supprimer ou remplacer proprement le logo.

### Routes médias admin

Préfixe :

```txt
/api/v1/admin/organisations/{organisation}/media
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/` | Médias organisation |
| GET | `/service/{service}` | Médias d’un service |
| POST | `/` | Ajouter média |
| PATCH | `/{media}/featured` | Définir image principale |
| POST | `/reorder` | Réordonner médias |
| DELETE | `/{media}` | Supprimer média |

### Routes médias organisation

Préfixe :

```txt
/api/v1/organisation/media
```

| Méthode | Endpoint | Description |
|---|---|---|
| POST | `/` | Ajouter média |
| PATCH | `/{media}/featured` | Définir image principale |
| POST | `/reorder` | Réordonner médias |
| DELETE | `/{media}` | Supprimer média |

---

## Statistiques

### Routes statistiques organisation

Préfixe :

```txt
/api/v1/organisation/stats
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/overview` | Vue globale organisation |
| GET | `/bookings` | Statistiques réservations |

### Routes statistiques admin

Préfixe :

```txt
/api/v1/admin/stats
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/overview` | Statistiques globales plateforme |

---

## Webhooks

Les webhooks servent à connecter l’organisation avec des systèmes externes :

- bot WhatsApp ;
- paiement ;
- notifications ;
- automatisations.

### Routes webhooks organisation

Préfixe :

```txt
/api/v1/organisation/webhooks
```

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/` | Liste webhooks |
| POST | `/` | Créer webhook |
| PUT | `/{webhook}` | Modifier webhook |
| DELETE | `/{webhook}` | Supprimer webhook |
| GET | `/{webhook}/logs` | Logs webhook |

---

## Routes publiques

Les routes publiques utilisent maintenant l’UUID de l’organisation.

Base :

```txt
/api/v1/public/{organisation}
```

Ici `{organisation}` correspond à :

```txt
organisation.id
```

et non au slug.

| Méthode | Endpoint | Description |
|---|---|---|
| GET | `/public/{organisation}/services` | Liste des services actifs |
| GET | `/public/{organisation}/services/{service}` | Détail d’un service actif |
| GET | `/public/{organisation}/services/{service}/availability` | Disponibilités publiques |
| POST | `/public/{organisation}/bookings` | Créer réservation |
| GET | `/public/{organisation}/bookings/{reference}` | Consulter réservation |

### Vérifier les routes publiques

```bash
php artisan route:list --path=v1/public
```

---

## Exemples de payloads

### Créer une organisation

```http
POST /api/v1/admin/organisations
Content-Type: application/json
Authorization: Bearer <token>
```

```json
{
  "slug": "hotel-bel-azur-cotonou",
  "name": "Hotel Bel Azur Cotonou",
  "description": "Hôtel situé à Cotonou.",
  "category_id": "uuid-category",
  "contact_email": "contact@belazur.bj",
  "contact_phone": "+2290100000000",
  "whatsapp_number": "+2290100000000",
  "currency": "XOF",
  "timezone": "Africa/Porto-Novo",
  "owner_name": "Responsable Bel Azur",
  "owner_email": "owner@belazur.bj"
}
```

---

### Créer un service hôtel

```http
POST /api/v1/organisation/services
Content-Type: application/json
Authorization: Bearer <token>
```

```json
{
  "name": "Chambre standard",
  "description": "Chambre confortable pour 1 à 2 personnes.",
  "category_id": "uuid-service-category",
  "duration_minutes": 1440,
  "min_participants": 1,
  "max_participants": 2,
  "max_capacity_per_slot": 10,
  "pricing_type": "per_group",
  "booking_mode": "date_range",
  "capacity_mode": "per_date_range",
  "assignment_enabled": false,
  "prices": [
    {
      "label": "Prix par nuit",
      "amount": 35000,
      "currency": "XOF"
    }
  ],
  "options": [
    {
      "name": "Petit déjeuner",
      "description": "Petit déjeuner inclus ou ajouté à la réservation.",
      "is_required": false,
      "max_quantity": 2,
      "prices": [
        {
          "label": "Par personne",
          "amount": 5000,
          "currency": "XOF"
        }
      ]
    }
  ]
}
```

---

### Créer un service touristique

```json
{
  "name": "Circuit Ganvié",
  "description": "Visite touristique de Ganvié avec guide.",
  "category_id": "uuid-service-category",
  "duration_minutes": 480,
  "min_participants": 1,
  "max_participants": 15,
  "max_capacity_per_slot": 15,
  "pricing_type": "per_person",
  "booking_mode": "date_only",
  "capacity_mode": "per_day",
  "assignment_enabled": true,
  "prices": [
    {
      "label": "Résident",
      "amount": 15000,
      "currency": "XOF"
    },
    {
      "label": "International",
      "amount": 25000,
      "currency": "XOF"
    }
  ],
  "options": [
    {
      "name": "Certificat de résidence",
      "description": "Obligatoire pour bénéficier du tarif résident.",
      "is_required": false,
      "requires_document": true,
      "document_timing": "upload_form",
      "document_label": "Certificat de résidence",
      "prices": [
        {
          "label": "Document",
          "amount": 0,
          "currency": "XOF"
        }
      ]
    }
  ]
}
```

---

### Créer une règle de disponibilité

```http
POST /api/v1/organisation/services/{service}/rules
Content-Type: application/json
Authorization: Bearer <token>
```

```json
{
  "day_of_week": 1,
  "start_time": "09:00",
  "end_time": "17:00",
  "max_slots": 10,
  "member_id": null
}
```

---

### Créer un blocage

```http
POST /api/v1/organisation/services/{service}/blocks
Content-Type: application/json
Authorization: Bearer <token>
```

```json
{
  "date": "2026-06-12",
  "block_type": "full_day",
  "reason": "Fermeture exceptionnelle"
}
```

---

### Créer une réservation `date_range` pour hôtel

```http
POST /api/v1/public/{organisation}/bookings
Content-Type: application/json
```

```json
{
  "service_id": "uuid-service",
  "selected_price_id": "uuid-price",
  "check_in_date": "2026-06-20",
  "check_out_date": "2026-06-22",
  "rooms_quantity": 1,
  "participants_count": 2,
  "client_data": {
    "name": "Jean Client",
    "phone": "+2290100000000",
    "email": "jean@example.com"
  },
  "selected_options": [
    {
      "option_id": "uuid-option",
      "price_id": "uuid-option-price",
      "quantity": 2
    }
  ],
  "gobot_session_id": "session-whatsapp-123",
  "notes": "Arrivée prévue vers 18h"
}
```

---

### Créer une réservation avec document

Quand une option exige un fichier, envoyer la requête en `multipart/form-data`.

Champs :

```txt
service_id = uuid-service
selected_price_id = uuid-price
scheduled_date = 2026-06-20
participants_count = 1
client_data[name] = Jean Client
client_data[phone] = +2290100000000
selected_options[0][option_id] = uuid-option
selected_options[0][price_id] = uuid-option-price
selected_options[0][quantity] = 1
selected_options[0][document] = fichier.pdf
```

Important : le front envoie le fichier lui-même. Il ne doit pas envoyer `document_path`.

---

### Créer une réservation `timeslot`

```json
{
  "service_id": "uuid-service",
  "selected_price_id": "uuid-price",
  "scheduled_date": "2026-06-20",
  "scheduled_time": "14:00",
  "participants_count": 2,
  "client_data": {
    "name": "Jean Client",
    "phone": "+2290100000000"
  },
  "selected_options": []
}
```

---

### Créer une réservation `date_only`

```json
{
  "service_id": "uuid-service",
  "selected_price_id": "uuid-price",
  "scheduled_date": "2026-06-20",
  "participants_count": 4,
  "client_data": {
    "name": "Jean Client",
    "phone": "+2290100000000"
  },
  "selected_options": []
}
```

---

## Commandes utiles

### Lister toutes les routes

```bash
php artisan route:list
```

### Lister uniquement les routes publiques

```bash
php artisan route:list --path=v1/public
```

### Vider tous les caches Laravel

```bash
php artisan optimize:clear
php artisan route:clear
php artisan config:clear
php artisan cache:clear
```

### Recréer la base en développement

```bash
php artisan migrate:fresh --seed
```

### Optimiser en production

```bash
php artisan optimize
```

### Créer le lien storage

```bash
php artisan storage:link
```

---

## Bonnes pratiques avant déploiement

Avant d’envoyer ou déployer le projet :

```bash
php artisan optimize:clear
php artisan route:list --path=v1/public
```

Vérifier que les routes publiques utilisent bien :

```txt
{organisation}
```

et non :

```txt
{slug}
```

Ne pas envoyer dans un dépôt ou zip propre :

```txt
.env
vendor/
node_modules/
storage/logs/
bootstrap/cache/*.php
database/database.sqlite
```

Garder seulement :

```txt
.env.example
code source
migrations
seeders
README
collection Postman
```

---

## Résumé métier rapide

SmartService Gobot permet à une organisation de publier ses services, configurer ses prix, options et disponibilités, puis recevoir des réservations depuis une vitrine publique ou un bot WhatsApp.

Le client voit les services actifs, choisit une date ou une période selon le type de service, ajoute éventuellement des options, puis crée une réservation. L’organisation peut ensuite accepter, rejeter, assigner, reprogrammer, terminer ou annuler la réservation.

Les médias sont gérés de façon compatible avec un stockage local aujourd’hui et S3 demain.
