Le problème du SPA monolithique
La version initiale de TravelPlan était un seul fichier HTML de 800 lignes servi à /planner/. Fonctionnel, mais avec des limitations :
- URL unique non bookmarkable par section
- Impossible de lier directement vers “Réservations” ou “Budget”
- Tout le JavaScript chargé d’un coup, même pour des sections non utilisées
- Pas de séparation des préoccupations
La solution : cinq applications, un état partagé
Chaque onglet de la barre de navigation gauche est désormais une application web autonome à son propre URL :
| App | URL | Rôle |
|---|---|---|
| Voyage | / | Formulaire de planification, entrée principale |
| Réservations | /reservations/ | Gestionnaire de réservations + extraction IA |
| Budget | /budget/ | Enveloppe totale, répartition par poste |
| Guide IA | /ia/ | Génération de fiches depuis texte, liens, PDF |
| Mes Fiches | /fiches/ | Collection de fiches voyage générées |
État partagé via localStorage
Toutes les apps lisent et écrivent dans le même objet JSON sous la clé travelplan_v1 :
{
title: "Mon Guide Voyage",
blocs: [...], // blocs générés par l'IA
resas: [...], // réservations
budgetCats: {...}, // répartition budgétaire
budgetCur: "€",
budgetTotal: 3000,
form: { // état du formulaire de voyage
dest: "Malaisie...",
from: "Koh Samui",
pax: "4",
...
}
}
L’avantage : naviguer entre les pages ne perd aucune donnée. L’IA génère une fiche dans /ia/, redirige vers /fiches/ — les données persistent.
Architecture Hugo
Chaque app utilise un layout Hugo dédié avec un partial partagé pour la navigation :
layouts/
index.html → App Voyage (homepage)
reservations/list.html → App Réservations
budget/list.html → App Budget
ia/list.html → App Guide IA
blocs/list.html → App Mes Fiches
partials/
planner-head.html → <head> commun
planner-topbar.html → Barre du haut (logo, titre, actions)
planner-sidenav.html → Navigation latérale
planner-modals.html → Modales partagées (résumé IA, partage)
static/
css/planner.css → Styles partagés
js/state.js → État, API Claude, fonctions communes
Le fichier state.js centralise :
tpGet()/tpSet()— lecture/écriture localStoragetpClaude()— appels à l’API AnthropictpExportPDF()— export depuis l’état complettpShare()— génération de lien de partagetpSummary()— résumé IA du voyagetpInitTopbar()— initialisation commune de la topbar
Topbar persistante : le titre de guide
Le titre du guide, visible dans la topbar sur chaque page, est synchronisé avec l’état. Modifier le titre sur n’importe quelle page le met à jour dans toutes les autres.
Blog intégré dans le même site Hugo
Le blog développeur coexiste avec les apps dans le même projet Hugo. Les pages /blog/* utilisent le thème PaperMod standard, les apps utilisent leurs propres layouts fullscreen. Les deux coexistent sans conflit grâce à l’organisation des layouts par section.
Prochaine étape — Semaine 3
- Carte interactive dans la vue Blocs (Leaflet.js avec les coordonnées GPS des fiches)
- Drag & drop pour réordonner les fiches
- Export vers un format partageable lisible sans l’app (page HTML autonome)
- Authentification optionnelle pour la synchronisation entre appareils
TravelPlan est open source — github.com/alx/travelPlan