feat(grid): refine grid page chrome (refined dense-pro, theme kept)

Redesign the grid page shell while preserving the Slate/teal Apple theme:
- header reworked into a branded bar: teal accent marker + uppercase
  eyebrow + supplier name as the hero, with "Magasin" and "Références"
  metric pills
- loading/error state shown as a teal (or error-red) accent chip
- "Valider" save button restyled with the brand gradient + soft shadow
- grid container radius/shadow aligned to the apple-card system

No change to table cell logic or the network columns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 4.8 committed 2026-06-21 09:38:14 +02:00
1 parent 524faf4a7a
commit 5f364f7d52
2 files changed
+57 -18

No files matched your search

+55 -16
View File
@@ -30,6 +30,27 @@ type GridRowsStreamMessage =
| { type: "done"; loaded: number; total: number } | { type: "done"; loaded: number; total: number }
| { type: "error"; error: string }; | { type: "error"; error: string };
/** Petite pastille métrique (libellé + valeur) — densité pro, thème Slate/teal. */
function MetricPill({ label, value, accent = false }: { label: string; value: string; accent?: boolean }) {
return (
<div
className="flex flex-col items-start px-2.5 py-1 rounded-lg shrink-0"
style={{ background: "var(--bg-elevated)", border: "1px solid var(--border)" }}
>
<span className="text-[8.5px] font-semibold uppercase tracking-wider leading-none mb-1" style={{ color: "var(--text-muted)" }}>
{label}
</span>
<span
className="text-[12px] font-bold leading-none tabular-nums max-w-[160px] truncate"
style={{ color: accent ? "var(--accent)" : "var(--text-primary)" }}
title={value}
>
{value}
</span>
</div>
);
}
export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, magasins, magasin, filters }: GridClientProps) { export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, magasins, magasin, filters }: GridClientProps) {
const { data: session } = useSession(); const { data: session } = useSession();
const isAdmin = (session?.user as { role?: string } | undefined)?.role === "admin"; const isAdmin = (session?.user as { role?: string } | undefined)?.role === "admin";
@@ -174,28 +195,42 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
return ( return (
<div className="flex flex-col h-full space-y-3 min-h-0 pb-12"> <div className="flex flex-col h-full space-y-3 min-h-0 pb-12">
{/* Header */} {/* Header — chrome raffiné (eyebrow teal + fournisseur héro + pastilles métriques) */}
<div className="flex items-end justify-between shrink-0"> <header className="shrink-0 flex items-center justify-between gap-4 pb-2.5" style={{ borderBottom: "1px solid var(--border)" }}>
<div> <div className="flex items-center gap-3.5 min-w-0">
<h1 className="text-2xl font-bold tracking-tight">Révision d&apos;Assortiment</h1> <div
<p className="text-[13px] mt-0.5" style={{ color: "var(--text-secondary)" }}> className="h-10 w-[3px] rounded-full shrink-0"
<span className="font-semibold" style={{ color: "var(--text-primary)" }}>{nomFournisseur}</span> style={{ background: "linear-gradient(to bottom, var(--accent), var(--accent-success))" }}
{" "}• <span className="font-medium" style={{ color: "var(--text-muted)" }}>{activeStoreNom}</span> />
{" "}• {totalRows?.toLocaleString("fr-FR") ?? rows.length.toLocaleString("fr-FR")} références <div className="min-w-0">
</p> <div className="text-[10px] font-bold uppercase tracking-[0.18em] leading-none mb-1.5" style={{ color: "var(--accent)" }}>
Révision d&apos;assortiment
</div>
<h1 className="text-[19px] font-bold tracking-tight truncate leading-none" style={{ color: "var(--text-primary)" }}>
{nomFournisseur}
</h1>
</div>
<div className="hidden md:flex items-center gap-2 ml-1 pl-3 shrink-0" style={{ borderLeft: "1px solid var(--border)" }}>
<MetricPill label="Magasin" value={activeStoreNom} />
<MetricPill label="Références" value={(totalRows ?? rows.length).toLocaleString("fr-FR")} accent />
</div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-2.5 shrink-0">
{(isLoadingRows || loadError) && ( {(isLoadingRows || loadError) && (
<div <div
className="flex items-center gap-2 px-3 py-1.5 rounded-md text-xs font-semibold" className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-semibold tabular-nums"
style={{ background: "var(--bg-elevated)", border: "1px solid var(--border)", color: loadError ? "var(--accent-error)" : "var(--text-secondary)" }} style={{
background: loadError ? "var(--accent-error-bg)" : "var(--accent-bg)",
border: `1px solid ${loadError ? "var(--accent-error)" : "var(--accent-border)"}`,
color: loadError ? "var(--accent-error)" : "var(--accent)",
}}
> >
{isLoadingRows && <Loader2 className="w-3.5 h-3.5 animate-spin" />} {isLoadingRows && <Loader2 className="w-3.5 h-3.5 animate-spin" />}
{loadError {loadError
? loadError ? loadError
: totalRows : totalRows
? `${rowsLoaded.toLocaleString("fr-FR")} / ${totalRows.toLocaleString("fr-FR")}` ? `${rowsLoaded.toLocaleString("fr-FR")} / ${totalRows.toLocaleString("fr-FR")}`
: "Chargement produits..."} : "Chargement…"}
</div> </div>
)} )}
<div id="grid-toolbar-actions"></div> <div id="grid-toolbar-actions"></div>
@@ -204,7 +239,11 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
<button <button
onClick={handleSave} onClick={handleSave}
disabled={isPending} disabled={isPending}
className="flex items-center gap-2 px-4 py-2 bg-emerald-700 hover:bg-emerald-600 text-white text-sm font-semibold rounded-lg disabled:opacity-60 transition-colors" className="flex items-center gap-2 px-4 h-9 text-white text-[13px] font-semibold rounded-xl disabled:opacity-60 transition-all hover:brightness-110 active:scale-[0.97]"
style={{
background: "linear-gradient(180deg, var(--brand-solid), var(--brand-hover))",
boxShadow: "0 4px 12px rgba(16,185,129,0.25), inset 0 1px 1px rgba(255,255,255,0.2)",
}}
> >
{isPending ? ( {isPending ? (
<Loader2 className="w-4 h-4 animate-spin" /> <Loader2 className="w-4 h-4 animate-spin" />
@@ -214,7 +253,7 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
<AlertCircle className="w-4 h-4" /> <AlertCircle className="w-4 h-4" />
) : null} ) : null}
{isPending {isPending
? "Sauvegarde..." ? "Sauvegarde…"
: saveStatus === "success" : saveStatus === "success"
? "Sauvegardé !" ? "Sauvegardé !"
: saveStatus === "error" : saveStatus === "error"
@@ -223,7 +262,7 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
</button> </button>
)} )}
</div> </div>
</div> </header>
{/* Filters */} {/* Filters */}
<div className="shrink-0 print:hidden"> <div className="shrink-0 print:hidden">
@@ -805,11 +805,11 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) {
<div <div
ref={tableContainerRef} ref={tableContainerRef}
className="h-full w-full overflow-auto rounded-[12px] relative" className="h-full w-full overflow-auto rounded-[14px] relative"
style={{ style={{
background: "var(--bg-surface)", background: "var(--bg-surface)",
border: "1px solid var(--border)", border: "1px solid var(--border)",
boxShadow: "var(--shadow-sm)" boxShadow: "var(--shadow-md)"
}} }}
> >
<table className="text-sm block" style={{ width: "100%", minWidth: totalWidth }}> <table className="text-sm block" style={{ width: "100%", minWidth: totalWidth }}>