mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-20 13:38:08 +02:00
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:
1 parent
524faf4a7a
commit
5f364f7d52
2 files changed
+57
-18
No files matched your search
@@ -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'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'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 }}>
|
||||||
|
|||||||
Reference in new issue
Block a user