Système de Réservation Publique (Public Booking System)
Vue d'ensemble
Le système de réservation publique permet aux clients et prospects de réserver des créneaux de disponibilité directement via une interface publique, sans authentification. Les réservations créent des événements Google Calendar avec le préfixe [MEETING] et envoient des notifications par email.
Architecture
Backend
Endpoints publics
GET /api/public/booking/:userId/info- Informations publiques du User (nom, logo, entreprise)GET /api/public/booking/:userId/availability- Liste des créneaux disponibles pour un UserPOST /api/public/booking/:userId/book- Créer une réservationPOST /api/public/booking/cancel/:token- Annuler une réservation via token
Services
-
BookingController (
backend/src/booking/booking.controller.ts)- Gère les réservations publiques
- Crée des événements Google Calendar (pas de sessions)
- Gère les tokens d'annulation
- Envoie des emails de confirmation et de notification
-
BookingCancelTokensService (
backend/src/booking/booking-cancel-tokens.service.ts)- Génère et valide les tokens d'annulation
- Stocke les tokens avec expiration (30 jours par défaut)
- Utilise
event_idpour lier les tokens aux événements
Base de données
- Table
booking_cancel_tokens:id(UUID)event_id(TEXT) - ID de l'événement Google Calendartoken(TEXT) - Token encryptéexpires_at(TIMESTAMPTZ)used_at(TIMESTAMPTZ, nullable)created_at,updated_at
Frontend
Pages publiques
-
BookingPage (
frontend/src/pages/booking/BookingPage/BookingPage.tsx)- Page publique de réservation
- Affiche le calendrier des créneaux disponibles
- Formulaire de réservation avec validation
- Page de confirmation après réservation
-
BookingCancelPage (
frontend/src/pages/booking/BookingCancelPage/BookingCancelPage.tsx)- Page publique d'annulation
- Utilise un token sécurisé pour annuler une réservation
Composants
- BookingCalendar - Affiche les créneaux disponibles par semaine
- BookingForm - Formulaire de réservation (nom, email, téléphone, type de rendez-vous, notes)
- BookingConfirmation - Page de confirmation après réservation
Hooks React Query
useBookingAvailability- Récupère les créneaux disponiblesuseBookingUserInfo- Récupère les informations publiques du UseruseCreateBooking- Crée une réservation (invalide automatiquement les queries d'availability)useCancelBooking- Annule une réservation
Flux de données
Création d'une réservation
graph TD
A[Client sélectionne un créneau] --> B[Client remplit le formulaire]
B --> C[POST /api/public/booking/:userId/book]
C --> D{Vérification disponibilité}
D -->|Non disponible| E[Erreur: Créneau déjà réservé]
D -->|Disponible| F[Création/Recherche Contact]
F --> G[Création événement Google Calendar avec préfixe MEETING]
G --> H[Création token d'annulation]
H --> I[Email confirmation au client]
H --> J[Email notification au User]
I --> K[Page de confirmation]
J --> L[Événement visible dans calendrier User]
Annulation d'une réservation
graph TD
A[Client clique sur lien d'annulation] --> B[POST /api/public/booking/cancel/:token]
B --> C[Validation token]
C -->|Token invalide| D[Erreur]
C -->|Token valide| E[Recherche événement dans calendrier User]
E --> F[Suppression événement Google Calendar]
F --> G[Marquage token comme utilisé]
G --> H[Email notification au User]
H --> I[Confirmation d'annulation]
Fonctionnalités
Filtrage des créneaux disponibles
- Les créneaux récurrents sont expansés en occurrences individuelles
- Les créneaux qui chevauchent avec des sessions existantes sont exclus
- Les créneaux qui chevauchent avec des événements de booking (avec
bookingType: "public") sont exclus - Seuls les créneaux de type
AVAILABLEsont retournés
Cache temporaire pour réservations récentes
Pour gérer le délai de propagation de l'API Google Calendar, un cache temporaire en mémoire est utilisé :
- Cache en mémoire :
recentBookingsCachedansBookingController - Durée de vie : 5 minutes par entrée
- Utilisation : Les réservations récentes sont ajoutées au cache immédiatement après création
- Filtrage : Le cache est consulté lors de
getAvailabilitypour exclure immédiatement les créneaux réservés - Nettoyage automatique : Les entrées de plus de 5 minutes sont supprimées automatiquement
Cela garantit que même si l'API Google Calendar n'a pas encore propagé l'événement, le créneau réservé n'apparaît plus dans la liste des disponibilités.
Gestion des événements
- Les réservations créent uniquement des événements Google Calendar (pas de sessions)
- Les événements sont préfixés avec
[MEETING]pour identification - Les événements contiennent
userIdetbookingType: "public"dansextendedProperties - Les événements peuvent être édités et supprimés depuis le calendrier User
Emails
- Email au client : Confirmation avec lien d'annulation
- Email au User : Notification de nouvelle réservation (avec détails du client)
- Email au User : Notification d'annulation (avec détails du rendez-vous annulé)
Notifications
Le système envoie des notifications in-app et push au User lors des événements de booking :
-
Notification in-app : Créée via
NotificationsService.createForUser()- Type
BOOKING_CREATEDpour les nouvelles réservations - Type
BOOKING_CANCELLEDpour les annulations - Inclut les métadonnées (eventId, type) pour navigation
- Émise via WebSocket en temps réel
- Type
-
Notification push : Envoyée via
PushNotificationsService.sendNotification()- Notification navigateur même si l'application est fermée
- Inclut les données pour navigation (URL, eventId)
- Tag unique pour éviter les doublons
Les notifications sont envoyées de manière non-bloquante avec gestion d'erreurs appropriée.
Sécurité
- Tokens d'annulation encryptés
- Tokens avec expiration (30 jours par défaut)
- Tokens marqués comme utilisés après annulation
- Validation de disponibilité avant création de réservation
Patterns et conventions
Préfixe des événements
Tous les événements créés via le système de réservation publique sont préfixés avec [MEETING] :
const eventTitle = `[MEETING] ${
data.title || `Rendez-vous avec ${data.client_name}`
}`;
ExtendedProperties
Les événements de booking contiennent dans extendedProperties.private :
{
userId: string; // ID du User propriétaire
bookingType: "public"; // Identifie les réservations publiques
}
Invalidation des queries
Après une réservation réussie, les queries d'availability sont automatiquement invalidées pour rafraîchir l'affichage :
onSuccess: (_data, variables) => {
// Invalidate availability queries for this user to refresh the available slots
queryClient.invalidateQueries({
exact: false,
queryKey: [BOOKING_QUERY_KEY, variables.userId, "availability"],
});
// Also remove the query from cache to force a fresh fetch
queryClient.removeQueries({
exact: false,
queryKey: [BOOKING_QUERY_KEY, variables.userId, "availability"],
});
};
Cette double invalidation (invalidate + remove) garantit un refetch complet des données, même si le cache React Query contient des données obsolètes.
Interface de gestion des bookings
Backend
Endpoints protégés
-
GET /api/bookings- Liste les bookings de l'utilisateur authentifié- Query params:
timeMin,timeMax(optionnels) - Retourne un tableau de bookings avec détails (eventId, dates, client, location, etc.)
- Query params:
-
GET /api/bookings/stats- Statistiques des bookings- Query params:
timeMin,timeMax(optionnels) - Retourne:
{ totalBookings, upcomingBookings, pastBookings }
- Query params:
-
PATCH /api/bookings/:eventId- Met à jour un booking- Body:
{ title?, startDate?, endDate?, location?, description?, addGoogleMeet?, attendeeEmails? } - Retourne:
{ message: string, success: boolean } - Vérifie que l'événement existe et est un booking public
- Utilise
GoogleCalendarService.updateEvent()pour la mise à jour
- Body:
-
DELETE /api/bookings/:eventId- Supprime un booking- Retourne:
{ message: string } - Vérifie que l'événement existe et est un booking public
- Supprime l'événement Google Calendar
- Retourne:
Services
-
BookingsController (
backend/src/booking/bookings.controller.ts)- Controller protégé (nécessite authentification)
- Utilise
GoogleCalendarService.getBookingEvents()pour récupérer les événements avecbookingType: "public"
-
GoogleCalendarService.getBookingEvents()
- Filtre les événements Google Calendar par
bookingType: "public"dansextendedProperties - Supporte le filtrage par plage de dates
- Filtre les événements Google Calendar par
Frontend
Composants
-
BookingsPanel (
frontend/src/pages/calendar/CalendarPage/components/BookingsPanel.tsx)- Affiche la liste des bookings du mois en cours
- Affiche les statistiques (total, upcoming, past)
- Permet de cliquer sur un booking pour voir les détails
- Tri automatique par date (upcoming first)
-
BookingDetailsDialog (
frontend/src/pages/calendar/CalendarPage/components/BookingDetailsDialog.tsx)- Dialog modal avec détails complets d'un booking
- Affiche: date/heure, client (email), location (téléphone ou Google Meet), notes
- Mode édition : Formulaire pour modifier le booking (titre, dates, participants, Google Meet, notes)
- Actions :
- Contact : Si le contact existe dans la base, navigation vers
/contacts/:id, sinon ouverture du client email - Join Call : Ouvre le lien Google Meet dans un nouvel onglet
- Edit : Active le mode édition avec formulaire complet
- Delete : Supprime le booking avec confirmation
- Contact : Si le contact existe dans la base, navigation vers
Hooks React Query
-
useBookings(options)- Récupère la liste des bookings- Options:
timeMin,timeMax,enabled - Invalide automatiquement après mutations
- Options:
-
useBookingStats(options)- Récupère les statistiques- Options:
timeMin,timeMax,enabled
- Options:
-
useUpdateBooking(options)- Met à jour un booking- Options:
onSuccess,onError - Invalide automatiquement les queries de bookings et d'événements après mise à jour
- Options:
-
useDeleteBooking(options)- Supprime un booking- Options:
onSuccess,onError - Invalide automatiquement les queries de bookings et d'événements après suppression
- Options:
Intégration dans CalendarPage
- Onglets pour basculer entre "Availability" et "Bookings"
- Le panel Bookings s'affiche dans la colonne latérale droite
- Filtrage automatique par mois en cours
Limitations connues
- ✅ Gestion des conflits : Résolu avec verrouillage distribué Redis. Voir BOOKING_IMPROVEMENTS.md.
- Cache en mémoire : Le cache temporaire des réservations récentes est en mémoire et ne persiste pas entre redémarrages du serveur. C'est acceptable car le cache sert uniquement à gérer le délai de propagation de Google Calendar (5 minutes).
- ✅ Statistiques : Complété avec graphiques, export (CSV, Excel, JSON), et filtres avancés. Voir BOOKING_IMPROVEMENTS.md.
- Filtrage limité : Filtrage uniquement par plage de dates (pas de filtres avancés par client, statut, etc.). Le panel Bookings affiche les bookings de 3 mois passés à 1 an futur.
- ✅ Gestion des timezones : Résolu avec détection automatique et conversion. Voir BOOKING_IMPROVEMENTS.md.
Améliorations futures
Voir BOOKING_IMPROVEMENTS.md pour la liste complète des améliorations prévues avec leur état d'avancement.
Résumé rapide
Fonctionnalités complétées : 10/10 (100%) ✅
- ✅ Système de réservation publique
- ✅ Documentation OpenAPI
- ✅ Tests unitaires (142 tests)
- ✅ Interface de gestion des bookings
- ✅ Notifications push
- ✅ Gestion des conflits (verrouillage Redis)
- ✅ Statistiques et analytics (avec export)
- ✅ Personnalisation de la page de booking
- ✅ Gestion des timezones
- ✅ Améliorations UX
Améliorations techniques complétées :
- ✅ Tests unitaires pour
ConflictDetectionHelper(26 tests) - ✅ Tests unitaires pour
CacheService(17 tests) - ✅ Tests unitaires pour
RecurringSlotExpander(14 tests) - ✅ Tests unitaires pour
SlotFilteringHelper(18 tests) - ✅ Tests unitaires pour
RecentBookingsCacheService(20 tests)
Prochaines étapes optionnelles :
- Tests d'intégration pour les conflits de réservation
- Tests de scénarios concurrents