Interface : passage de Next.js à une SPA React (Vite + React Router)

Le serveur étant réécrit en Rust, l'interface devient une application
React 19 côté navigateur, compilée par Vite dans dist/ et servie par le
serveur Rust (STATIC_DIR). Composants, styles et logique cliente
inchangés : seule la plomberie Next.js est remplacée.

- Outillage : Vite 8, @vitejs/plugin-react, Tailwind 4 via PostCSS,
  React Router 7 (mode déclaratif), index.html, src/main.tsx, alias @/,
  proxy /api → localhost:5643 en développement, ESLint sans plugin Next.
- Adaptations (src/shims) : next/link, next/navigation (useRouter avec
  refresh, usePathname/useSearchParams suivant aussi les replaceState de
  l'interface), next-auth/react (session lue sur /api/auth/session,
  déconnexion par /api/auth/logout).
- Actions serveur → POST /api/actions/{nom} (src/lib/rpc.ts) : mêmes noms,
  mêmes signatures, mêmes types. La connexion passe par /api/auth/login.
- Pages → table des routes (src/router.tsx) : données lues sur
  /api/pages/{nom} avec Suspense (loading.tsx au premier chargement,
  page conservée pendant une navigation ou un router.refresh()),
  error.tsx en frontière d'erreur, not-found.tsx, titres d'onglet,
  pages d'administration réservées aux administrateurs.
- Suppression du code serveur Next.js (routes /api, accès base, Qlik,
  authentification, middleware…) ; types utiles à l'interface recopiés
  dans src/types/ff.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LG2LLJuswrrTTPKJmunEVf
This commit is contained in:
Claude committed 2026-10-12 19:30:20 +00:00
1 parent 559e6ed714
commit f104719054
170 files changed
+2528 -24445

No files matched your search

+18 -21
View File
@@ -1,33 +1,30 @@
import type { Metadata } from "next";
import { ShoppingCart } from "lucide-react";
import { pgGetCommandesAuto, type PgCommandeAutoRow } from "@/lib/pg-ff-client";
import { listCadences, getFournisseursPourCadence } from "@/features/commandes-auto/actions";
import type { PgCommandeAutoRow } from "@/types/ff";
import type { CadenceView } from "@/features/commandes-auto/actions";
import { fetchPage } from "@/lib/rpc";
import { usePageData, usePageSearchParams } from "@/lib/page-data";
import { PageHeader } from "@/components/ui/page-header";
import { CommandesAutoTabs, type Onglet } from "./tabs";
import { cachedFF } from "@/lib/ff-cache";
export type { PgCommandeAutoRow };
export const metadata: Metadata = { title: "Commandes fournisseurs" };
/** Données de la page : propositions de l'API FF, cadencier et référentiel fournisseurs. */
interface DonneesCommandesAuto {
rows: PgCommandeAutoRow[];
cadences: CadenceView[];
fournisseurs: { code: string; nom: string }[];
}
// Données live (DB + API FF Nancy) : rendu à la requête, jamais prérendu au build.
export const dynamic = "force-dynamic";
export default async function CommandesAutoPage(props: { searchParams: Promise<{ onglet?: string | string[] }> }) {
const { onglet } = await props.searchParams;
// Données live (DB + API FF Nancy) : relues à chaque affichage. Les propositions
// sont gardées 2 minutes par le serveur, pour que chaque modification du
// cadencier — qui rafraîchit la page — n'attende pas de nouveau toute l'API.
export default function CommandesAutoPage() {
const { onglet } = usePageSearchParams();
const ongletInitial: Onglet = onglet === "cadencier" ? "cadencier" : "propositions";
const [rows, cadences, fournisseurs] = await Promise.all([
// Propositions de l'API FF (un appel, plus un par fournisseur sans franco) :
// gardées 2 minutes, pour que chaque modification du cadencier — qui
// re-rend la page — n'attende pas de nouveau toute l'API.
cachedFF("commandes-auto", () => pgGetCommandesAuto(), {
ttlMs: 2 * 60 * 1000,
cacheIf: (rows) => rows.length > 0,
}),
listCadences(),
getFournisseursPourCadence(),
]);
const { rows, cadences, fournisseurs } = usePageData("commandes-auto", () =>
fetchPage<DonneesCommandesAuto>("commandes-auto"),
);
return (
<div className="mx-auto w-full max-w-screen-2xl">