Consomme le socle backend de #42/#44 (`GET /reference/tours`, `GET/POST /profile/tours`). Deux tours : orientation générale (auto sur `/`) et correction des métadonnées de recette (auto à l'ouverture du premier popover de correction). - Dépendance `driver.js` (~5 ko, MIT, zéro dépendance). - `features/tour/tour-engine.ts` : wrapper vanilla autour de `driver.js` — gère ce que la lib ne fait pas seule : la navigation React Router entre étapes situées sur des routes différentes, et un `onFinish` unique (`"completed"` seulement si on atteint la fin, `"skipped"` pour la croix / Échap / clic sur l'overlay). - `features/tour/TourContext.tsx` : `TourProvider` (charge scénarios + progression, keyed sur `user?.id` comme `ThemeContext`) et `useTour()` (`startTour(key)` / `isTourCompleted(key)` / `tours` / `isReady`). `isReady` est **calculé au render** contre l'id utilisateur chargé — pas stocké — pour qu'un changement de `user` le repasse à `false` immédiatement, avant qu'un effet enfant (l'auto-déclenchement) ne tourne sur des données de complétion périmées. - `main.tsx` : `<TourProvider>` sous `ThemeProvider`. - Attributs `data-tour-*` : liens de nav (`AppLayout`), grille / bouton `+` du 1er créneau / bouton Cuisiner (`PlanningPage`), page liste de courses (`ShoppingListPage`), description d'étape + section technique du popover (`StepDescription` / `TechStepCorrectionPopover`). - Déclenchement auto : tour `general` sur `/` (`PlanningPage`, ref-gardé), tour `techStepMetadata` à `activeCorrection` non-null (`StepDescription`). - Section « Revoir un tutoriel » dans `/parametres/preferences-utilisateur` — un `map` sur `tours`, un bouton « Revoir » par scénario. - `tour.scss` re-skinne le popover `driver.js` sur les tokens du thème. - i18n `tour.*` (chrome uniquement : boutons du popover, section « Revoir » ; le contenu des étapes vient de la base). Tests Cypress (`tour.feature`, 5 scénarios) : auto-démarrage + élément surligné pour un nouveau venu ; « Terminé » → complétion enregistrée `skipped:false` ; croix → `skipped:true` ; un tour déjà vu ne se relance pas ; « Revoir » le relance depuis les paramètres. Vérifié 5/5 en local. `pnpm build` + `pnpm lint` verts. Refs #42 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
60 lines
2.3 KiB
TypeScript
60 lines
2.3 KiB
TypeScript
import { Capacitor } from "@capacitor/core";
|
|
import { StrictMode } from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
import { BrowserRouter, HashRouter } from "react-router-dom";
|
|
import { App } from "./App";
|
|
import { setTokenStore } from "./api/client";
|
|
import { AuthProvider } from "./features/auth/AuthContext";
|
|
import { ThemeProvider } from "./features/theme/ThemeContext";
|
|
import { TourProvider } from "./features/tour/TourContext";
|
|
// Side-effect import: initializes the i18next instance before anything
|
|
// renders (react-i18next reads it via context under the hood). See i18n/i18n.ts.
|
|
import "./i18n/i18n";
|
|
// Global stylesheet (theme tokens + minimal reset) — the only .scss import
|
|
// that isn't colocated with a specific component/page. See styles/global.scss.
|
|
import "./styles/global.scss";
|
|
|
|
// `BrowserRouter` needs a server that rewrites deep links back to
|
|
// `index.html`; a packaged Capacitor app serves `dist/` straight off the
|
|
// WebView's local origin with no such rewrite, so a direct hit on a deep
|
|
// route (or a reload) would 404. `HashRouter` keeps the whole route in the
|
|
// fragment — zero server cooperation needed — and the URL bar is invisible
|
|
// in the native shell anyway. Web is untouched: `isNativePlatform()` is
|
|
// `false` in every browser (and in Cypress).
|
|
const isNative = Capacitor.isNativePlatform();
|
|
const Router = isNative ? HashRouter : BrowserRouter;
|
|
|
|
/**
|
|
* On native, swap the no-op token store for the `@capacitor/preferences`
|
|
* one *before* the first render — `AuthProvider` fires `GET /auth/me` on
|
|
* mount and it must carry the `Authorization` header. Lazily imported so
|
|
* `@capacitor/preferences` never enters the web bundle; web takes no
|
|
* `await` on this path, so rendering isn't deferred.
|
|
*/
|
|
async function bootstrap() {
|
|
if (isNative) {
|
|
const { capacitorTokenStore } = await import("./api/capacitor-token-store");
|
|
setTokenStore(capacitorTokenStore);
|
|
}
|
|
|
|
const rootElement = document.getElementById("root");
|
|
if (!rootElement) {
|
|
throw new Error("Root element not found");
|
|
}
|
|
|
|
createRoot(rootElement).render(
|
|
<StrictMode>
|
|
<Router>
|
|
<AuthProvider>
|
|
<ThemeProvider>
|
|
<TourProvider>
|
|
<App />
|
|
</TourProvider>
|
|
</ThemeProvider>
|
|
</AuthProvider>
|
|
</Router>
|
|
</StrictMode>,
|
|
);
|
|
}
|
|
|
|
void bootstrap();
|