rubis/apps/web/src/main.tsx
ordinarthur 7c0767f45e
All checks were successful
Build & Deploy Landing / build-and-deploy (push) Successful in 1m43s
Build & Deploy API / build-and-deploy (push) Successful in 2m29s
Build & Deploy Web / build-and-deploy (push) Successful in 1m24s
refactor(web): retire i18n EN, app SPA mono-langue FR
Pendant de cecbddc (landing). Retire le système react-i18next ajouté
en 254f65b — un seul brief utilisateur en français suffit pour l'audience
TPE-PME française visée en V1.

- Désinstalle i18next + react-i18next (≈ 13 kB gzip économisés).
- Supprime apps/web/src/i18n/ (fr.ts, en.ts, index.ts, types.ts) et
  le LanguageSwitcher de /parametres.
- Inline les chaînes FR dans les composants impactés : main.tsx
  (FallbackError), shell (AppSidebar, MobileTabBar, UserMenu), auth
  (login, signup, onboarding/compte), dashboard (_app/index),
  /parametres.
- Met à jour signup : « 30 jours gratuits » → « 14 jours gratuits »
  pour s'aligner sur la landing.

Côté UX visible : plus de switcher langue, plus de détection
navigator.language, plus de localStorage["rubis:locale"].

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 10:05:04 +02:00

125 lines
3.7 KiB
TypeScript

// Sentry init AVANT tout autre import non-essentiel pour capturer même
// les erreurs de bootstrap (cf. apps/web/src/lib/sentry.ts).
import "./lib/sentry";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import * as Sentry from "@sentry/react";
import { routeTree } from "./routeTree.gen";
import { env } from "./lib/env";
import { api } from "./lib/api";
import { authStore } from "./lib/auth";
import type { AuthSession } from "@rubis/shared";
import "./styles/app.css";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 30_000,
gcTime: 5 * 60_000,
retry: 1,
refetchOnWindowFocus: false,
},
mutations: {
retry: 0,
},
},
});
const router = createRouter({
routeTree,
context: { queryClient },
defaultPreload: "intent",
defaultPreloadStaleTime: 0,
});
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
async function enableMocking(): Promise<void> {
// import.meta.env.DEV est un booléen statique : Vite tree-shake la branche
// entière (et le chunk MSW avec) quand on build en mode production.
if (!import.meta.env.DEV || !env.VITE_USE_MOCKS) return;
const { worker } = await import("./mocks/browser");
await worker.start({
onUnhandledRequest: "bypass",
serviceWorker: {
url: "/mockServiceWorker.js",
},
});
// eslint-disable-next-line no-console
console.info(
"%c[MSW]%c Mocks API actifs — VITE_USE_MOCKS=true",
"background:#9F1239;color:white;padding:2px 6px;border-radius:3px;font-weight:600",
"color:#8A7F76",
);
}
/**
* Tente de récupérer la session à partir du refresh token (cf. ADR-017).
* Si le serveur (ou MSW) confirme une session valide → on rehydrate l'authStore
* AVANT le 1er render, ce qui évite le flash redirect /login pour les users
* déjà connectés.
*
* En mode dev avec MSW, la "session" persistée est en localStorage (cf.
* mocks/sessionStore). En prod réel, c'est le cookie httpOnly côté Adonis.
*/
async function bootstrapSession(): Promise<void> {
try {
const session = await api.post<AuthSession>(
"/api/v1/auth/refresh",
undefined,
{ anonymous: true },
);
authStore.setSession(session.accessToken, session.user);
} catch {
// Pas de session valide → on reste anonyme. _app guard redirigera vers /login.
}
}
function FallbackError() {
return (
<div className="min-h-screen flex items-center justify-center bg-cream px-6">
<div className="text-center max-w-md">
<h1 className="font-display text-3xl mb-3 text-ink">Quelque chose a coincé.</h1>
<p className="text-ink-2 mb-6">On a noté, on regarde. Rechargez la page pour réessayer.</p>
<button
onClick={() => location.reload()}
className="bg-rubis hover:bg-rubis-deep text-white px-5 py-2.5 rounded-md font-medium transition-colors"
>
Recharger
</button>
</div>
</div>
);
}
function render(): void {
const rootEl = document.getElementById("root");
if (!rootEl) throw new Error("#root introuvable dans index.html");
createRoot(rootEl).render(
<StrictMode>
<Sentry.ErrorBoundary fallback={<FallbackError />}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
</Sentry.ErrorBoundary>
</StrictMode>,
);
}
async function init(): Promise<void> {
await enableMocking();
await bootstrapSession();
render();
}
void init();