Nouvelles routes : - /signup : inscription (fullName + email + password) → /onboarding/compte - /onboarding : layout avec brand + stepper, auth-guard - /onboarding/compte : étape 1 (nom + email, prefilled depuis la session) - /onboarding/entreprise : étape 2 (nom, SIRET optionnel, chips volume) - /onboarding/signature : étape 3 (signature email + aperçu live) Nouvelles primitives UI : - <Card variant="default|flat|hero" padding="sm|md|lg"> - <Stepper> wizard horizontal (current rubis, done rubis-glow + ✓, todo line) - <Chip selected> : pastille pill, glow + deep quand sélectionnée (le rubis plein reste réservé aux CTA, cf. règle "le rubis est rare") - <Textarea> : mêmes règles a11y/focus que <Input> MSW handlers étendus : - PATCH /api/v1/account/profile (fullName, email, signature) - PATCH /api/v1/organizations/me (name, siret, monthlyVolumeBucket) - mockDb : ajout des organizations, méthodes updateUser/updateOrg Wiring : - /login → "Créer un compte" pointe vers /signup (avant : loop) - /login succès → / (au lieu de /login) - / → /onboarding/compte si auth, /login sinon (placeholder dashboard) - /onboarding/signature succès → / Bundle prod : 113.87 KB gzip core (-2 KB grâce à MSW exclu en prod via import.meta.env.DEV). Chaque route en chunk dédié (1-2 KB gzip). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import {
|
|
Outlet,
|
|
Link,
|
|
createFileRoute,
|
|
redirect,
|
|
useRouterState,
|
|
} from "@tanstack/react-router";
|
|
|
|
import { authStore } from "@/lib/auth";
|
|
import { Brand } from "@/components/brand/Brand";
|
|
import { Stepper } from "@/components/ui/Stepper";
|
|
|
|
const ONBOARDING_STEPS = [
|
|
{ id: "compte", label: "Compte", path: "/onboarding/compte" },
|
|
{ id: "entreprise", label: "Entreprise", path: "/onboarding/entreprise" },
|
|
{ id: "signature", label: "Signature", path: "/onboarding/signature" },
|
|
] as const;
|
|
|
|
export const Route = createFileRoute("/onboarding")({
|
|
beforeLoad: ({ location }) => {
|
|
if (!authStore.isAuthenticated()) {
|
|
throw redirect({ to: "/login", search: { redirect: location.href } });
|
|
}
|
|
},
|
|
component: OnboardingLayout,
|
|
});
|
|
|
|
function OnboardingLayout() {
|
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
const currentIndex = Math.max(
|
|
0,
|
|
ONBOARDING_STEPS.findIndex((s) => pathname.startsWith(s.path)),
|
|
);
|
|
|
|
return (
|
|
<main className="min-h-screen bg-cream">
|
|
{/* Header minimal — pas de sidebar, pas de nav, juste la marque + le stepper. */}
|
|
<header className="border-b border-line bg-cream/85 backdrop-blur-sm">
|
|
<div className="mx-auto flex max-w-[920px] items-center justify-between px-6 py-5 lg:px-8">
|
|
<Link to="/login">
|
|
<Brand withSuffix />
|
|
</Link>
|
|
<p className="hidden text-[12.5px] text-ink-3 sm:block">
|
|
Étape {currentIndex + 1} sur {ONBOARDING_STEPS.length}
|
|
</p>
|
|
</div>
|
|
<div className="mx-auto max-w-[920px] px-6 pb-6 lg:px-8">
|
|
<Stepper
|
|
steps={ONBOARDING_STEPS.map(({ id, label }) => ({ id, label }))}
|
|
currentIndex={currentIndex}
|
|
/>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="mx-auto w-full max-w-[640px] px-6 py-12 lg:px-8 lg:py-16">
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|