Réservation en ligne
Liens de réservation, parcours public, intégration en iframe, annulation et déplacement par le client.
La prise de rendez-vous en ligne est fermée par défaut. Elle demande LRVO_MODULE_BOOKING_ONLINE=true et au moins un lien de réservation actif.
Le lien de réservation
Un lien porte un identifiant d'URL, un titre, une introduction, et la portée de ce qu'il offre.
Ses deux listes sont des filtres, pas des autorisations :
| Ce qui est déclaré | Ce que le visiteur voit |
|---|---|
| Aucun type | Tous les types actifs. |
| Un seul type | Ce type, et l'étape du choix disparaît. |
| Trois types | Les trois, à choisir. |
| Aucun intervenant | Ceux que le type autorise. |
| Un seul intervenant | Celui-là, et l'étape du choix disparaît. |
| Quatre intervenants | Les quatre, plus l'option « Peu importe ». |
C'est ce qui permet à un même module de servir un lien à trois types et quatre personnes, et un lien à un seul type sans aucun choix.
Un lien peut porter une date d'ouverture et une date de fermeture : un lien de campagne s'ouvre et se ferme seul, sans tâche planifiée.
Un lien inactif, pas encore ouvert ou déjà fermé, affiche la raison plutôt qu'un 404 : le visiteur détient une vraie adresse et mérite de savoir que c'est la date qui ne va pas, pas le lien. Un identifiant inconnu, lui, reste un 404.
Le parcours
/lrvo/booking/<identifiant>
Un écran à la fois. Chaque étape pose sa question, seule, et le bouton Étape suivante ne s'active que lorsqu'elle a une réponse.
- Le service. Sauté si le lien n'en porte qu'un.
- La personne. Sauté si le type n'en autorise qu'une, ou si le lien en impose une. Sinon « Peu importe » est choisi d'avance : c'est une réponse, pas une absence de réponse, et l'étape laisse donc passer tout de suite.
- La date et l'heure. Un mois à la fois : seules les journées qui ont de la place sont cliquables, et les heures libres s'affichent à côté. Le mois ouvert est celui de la date du jour dans le fuseau du site, et ce fuseau est nommé sous la grille. Tant que la disponibilité du mois n'a pas répondu, les cases restent des emplacements vides : les numéros arrivent en même temps que ce qui est cliquable, plutôt qu'un mois entier grisé qui dirait qu'il n'y a rien de libre.
- Les coordonnées. Nom, courriel, téléphone, case de rappel, note, plus les questions du type. Son bouton confirme le rendez-vous.
Le nom et le courriel sont obligatoires, ainsi que les questions du type marquées comme telles. Leur libellé porte une étoile rouge, le même repère que les champs de l'administration : le visiteur voit ce qu'il doit remplir avant d'essayer d'envoyer, plutôt qu'après.
Un fil d'étapes numérotées surmonte l'écran. Les étapes déjà franchies y sont cliquables, celles à venir ne le sont pas : avancer est le travail du bouton, revenir celui du fil. Sur écran étroit, les libellés cèdent la place aux seuls numéros, avec la mention « Étape 2 sur 4 » en dessous.
Sous le fil, un récapitulatif reprend ce qui est déjà répondu, chaque ligne portant son propre lien Modifier. Au moment de saisir ses coordonnées, le visiteur a donc sous les yeux le service, la personne et le moment qu'il vient de choisir.
Sauter une étape, ou y revenir, est une décision d'affichage, rien d'autre. La requête qui crée le rendez-vous revalide le type, la personne, la fenêtre du lien et l'interrupteur en ligne. Un formulaire fabriqué à la main n'obtient rien de plus qu'un formulaire rempli normalement.
Le nombre d'étapes dépend des réponses : choisir un type qui n'autorise qu'une personne retire l'étape de la personne. L'étape courante est donc retenue par son nom, jamais par son rang, qui désignerait autre chose dès que le parcours change sous elle.
Un créneau pris entre l'affichage et l'envoi ne produit pas d'échec sec : le message le dit, la sélection est levée, le visiteur est ramené à l'étape du moment et la liste se recharge.
Le cadre de la page
Les pages publiques prennent le cadre commun du core, celui que servent aussi les partages de fichiers, les factures et les formulaires : même conteneur, même marque, même pied de page, même bascule de thème flottante. Un visiteur qui rencontre deux de ces pages ne doit pas avoir l'impression de changer de site en chemin. Voir Pages publiques.
Les couleurs suivent le thème du projet : le bouton principal, le créneau, le jour, le type et l'intervenant choisis, et les étapes franchies prennent sa couleur primary. Les couleurs des types et des intervenants, choisies dans l'administration, restent affichées telles qu'elles ont été saisies. Un type sans couleur prend le gris discret du thème.
Le parcours de réservation et la fiche de rendez-vous prennent l'en-tête en barre : un en-tête collant en haut de l'écran, bordure basse, mini logo à gauche et titre du lien au centre. C'est la silhouette des pages qu'on ouvre pour faire quelque chose. Le titre de la barre est le seul titre de la page, l'introduction du lien s'affiche sous la barre, et l'indicateur d'étapes arrive tout de suite après : la hauteur d'écran va à la tâche, pas à l'annonce.
Deux dispositions, parce que les états n'ont pas le même poids :
| Disposition | Pour | Forme |
|---|---|---|
| Barre, large | Le parcours de réservation et la fiche du rendez-vous | Conteneur max-w-5xl, barre collante de 48 pixels |
| Centrée | Un lien inactif, pas encore ouvert ou fermé | Boîte de max-w-md centrée, logo centré, comme la page de connexion du core |
Les deux pages qui font quelque chose sont larges parce que les deux portent le même sélecteur de moment : celui de l'étape de date, et celui du déplacement d'un rendez-vous. Il pose le choix du jour et celui de l'heure côte à côte à partir de 1024 pixels. En dessous les deux panneaux s'empilent : les séparer plus tôt réduisait les heures à une bande trop étroite pour se lire. Le sélecteur de jour garde une largeur fixe de 480 pixels, sans quoi ses cases carrées grandiraient avec la page jusqu'à l'absurde, et la largeur gagnée va aux heures libres, sur deux colonnes qui occupent exactement la hauteur du sélecteur de jour et défilent au delà.
Cette hauteur est celle du mois affiché, et rien d'autre : les semaines qui ne contiennent que le mois suivant ne sont pas dessinées, donc un mois de cinq semaines donne un panneau plus court qu'un mois de six. La carte des heures est sortie du flux pour y arriver. Laissée dedans, sa hauteur aurait fixé celle de la rangée, la rangée aurait fixé celle de la carte, et rien n'aurait jamais borné la liste : c'est ce qui la faisait s'allonger sans fin sous le calendrier.
Toutes les étapes tiennent la même largeur, celle du conteneur : le récapitulatif, le formulaire de coordonnées et la confirmation ne sont pas plus étroits que le choix de la date. Et toutes posent leurs boutons au même endroit, sous les cartes et non dedans, Retour à gauche et l'action à droite. Le bouton qui confirme le rendez-vous est donc hors du formulaire : c'est son attribut form qui les relie, ce qui lui conserve la validation native des champs et la soumission à la touche Entrée.
Dans la barre, le logo monte jusqu'à 24 pixels de haut, sans déformation, et devient cliquable quand LRVO_MODULE_BOOKING_SITE_URL est posée. Sans logo configuré, rien n'est affiché à sa place : un nom posé là où l'œil attend une image se lit comme un logo qui n'a pas chargé, et le nom du site reste dans le pied de page. Sans variable de marque propre au module, c'est celle du site qui sert, déclarée une fois avec LRVO_LOGO_URL et LRVO_SITE_URL.
Sous le nom du site, le pied de page porte une mention propre à la page. Le parcours de réservation dit « Vos coordonnées ne servent qu'à ce rendez-vous. Une confirmation vous est envoyée par courriel. », la fiche du rendez-vous dit que son lien est personnel. La page d'un lien indisponible n'en porte aucune : elle ne demande rien et n'envoie rien.
Un changement d'étape ramène l'œil en haut du parcours, sous la barre et non dessous : l'indicateur d'étapes reste visible.
Le titre de l'onglet s'aligne sur le titre affiché une fois la page rendue dans le navigateur. Celui que rend le serveur reste neutre : c'est celui que voient un robot et un aperçu de lien.
Protection contre les robots
Trois couches, empruntées au module Formulaires :
- un champ appât, invisible à l'écran, tentant pour un robot ;
- l'horodatage du montage du formulaire : un envoi plus rapide qu'un humain ne lit la page est refusé ;
- le plafond par adresse courriel et par jour, compté en base.
Aucune ne remplace le rate limit du routeur du core, qui s'applique par-dessus.
Intégration sur un site externe
La page de chaque lien affiche son code d'intégration :
<script src="https://exemple.ca/lrvo/api/booking/public/embed.js" data-slug="consultation"></script>Le script insère une iframe vers /lrvo/booking/<identifiant>?embed=1 et écoute la hauteur que la page lui renvoie pour se redimensionner. En mode embed, la page perd son logo, son titre, son pied de page et sa bascule de thème : elle vit dans la page de quelqu'un d'autre, qui a déjà les siens.
Attributs acceptés par la balise : data-slug obligatoire, data-title pour le titre de l'iframe, data-height pour la hauteur de départ, en pixels.
Le script est servi en application/javascript par une route d'API, et non rendu par la page publique : un navigateur n'exécute un script que si le type de contenu le dit. Rien n'est à configurer côté hôte, le core ne pose X-Frame-Options que sur les réponses de fichier, jamais sur les pages.
Ce que le client peut faire ensuite
Le courriel de confirmation porte un lien vers /lrvo/booking/rendez-vous/<jeton>, où le jeton fait 64 caractères hexadécimaux. Ce jeton est la seule crédential : quiconque l'a peut ouvrir la page, personne ne peut le deviner, et il n'y a ni compte ni mot de passe.
Le client y voit son rendez-vous, peut télécharger le fichier .ics, et peut l'annuler ou le déplacer tant que le délai le permet.
Déplacer se fait avec le sélecteur du parcours, le même calendrier mensuel et la même liste d'heures à droite. Quelqu'un qui a réservé en ligne a déjà choisi un moment une fois : lui présenter un autre objet pour refaire le même geste est ce qui rendait cet écran difficile à lire. Le mois s'ouvre sur le rendez-vous lui-même et le jour du rendez-vous est déjà sélectionné, donc les heures libres de ce jour-là sont à l'écran sans un clic. Les flèches vont chercher les autres mois, un mois à la fois, et chaque mois est demandé au serveur au moment où il s'affiche.
L'heure actuelle du rendez-vous occupe une place, la sienne, et un service à une place ne l'offre donc pas parmi les heures libres. Un jour où il n'y a rien d'autre le dit en toutes lettres.
Cette page ne dépend pas de LRVO_MODULE_BOOKING_ONLINE. Elle répond sur son jeton seul, y compris prise en ligne fermée : un rendez-vous créé dans l'admin envoie une confirmation qui porte ce lien, et le client garde le droit de le consulter. Ce qu'il peut y faire vient de LRVO_MODULE_BOOKING_ALLOW_CANCEL, LRVO_MODULE_BOOKING_ALLOW_RESCHEDULE et LRVO_MODULE_BOOKING_MAX_RESCHEDULES. Les trois fermés laissent une page de consultation, ce qui reste utile : le client relit l'heure et le lieu.
Le serveur décide seul. Les boutons ne font que refléter ce que la réponse a calculé : une page laissée ouverte toute la nuit ne propose pas une action que le serveur refusera. Le déplacement se fait sur place, sans changer le jeton, ce qui tient la promesse que le lien du courriel reste utile aussi longtemps que le rendez-vous existe.
La page est en noindex sans condition et son titre n'interroge jamais la base : un titre calculé depuis un jeton ferait de la réponse un oracle sur les jetons qui existent.