mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
style(ux): harmonize ui according to ergonomics specification (silent ui, tabular nums, muted icons)
This commit is contained in:
1 parent
9ef846a5d9
commit
c2dbaa7fbc
2 files changed
+40
-22
No files matched your search
@@ -101,30 +101,46 @@ export function GridFilterBar({ fournisseurs, magasins, nomenclature }: GridFilt
|
||||
|
||||
{/* Gamme quick filter */}
|
||||
<div
|
||||
className="flex items-center gap-1 p-0.5 rounded-md"
|
||||
className="flex items-center gap-1 p-0.5 rounded-lg"
|
||||
style={{ background: "var(--bg-elevated)", border: "1px solid var(--border)" }}
|
||||
>
|
||||
{GAMME_FILTERS.map(({ label, value }) => {
|
||||
const count = value === null ? rows.length : gammeCounts[value];
|
||||
const isActive = filters.codeGamme === value;
|
||||
|
||||
// Semantic colors for the badge based on Gamme
|
||||
const getBadgeStyles = () => {
|
||||
if (!isActive) return { background: "rgba(0,0,0,0.05)", color: "var(--text-muted)" };
|
||||
switch (value) {
|
||||
case "A": return { background: "rgba(16, 185, 129, 0.15)", color: "rgb(5, 150, 105)" }; // Emerald
|
||||
case "B": return { background: "rgba(59, 130, 246, 0.15)", color: "rgb(37, 99, 235)" }; // Blue
|
||||
case "C": return { background: "rgba(245, 158, 11, 0.15)", color: "rgb(217, 119, 6)" }; // Amber
|
||||
case "Z": return { background: "rgba(244, 63, 94, 0.15)", color: "rgb(225, 29, 72)" }; // Rose
|
||||
default: return { background: "rgba(0,0,0,0.1)", color: "var(--text-primary)" };
|
||||
}
|
||||
};
|
||||
|
||||
const badgeStyles = getBadgeStyles();
|
||||
|
||||
return (
|
||||
<button
|
||||
key={label}
|
||||
onClick={() => setFilter("codeGamme", value)}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-2.5 py-1 text-[11px] font-semibold rounded-[4px] transition-colors",
|
||||
filters.codeGamme === value
|
||||
? "shadow-sm"
|
||||
: "hover:bg-[var(--bg-surface)]"
|
||||
"flex items-center gap-2 px-2.5 py-1 text-[11px] font-semibold rounded-[6px] transition-all duration-200",
|
||||
isActive
|
||||
? "shadow-[0_1px_2px_rgba(0,0,0,0.05)]"
|
||||
: "hover:bg-[var(--bg-surface)] opacity-70 hover:opacity-100"
|
||||
)}
|
||||
style={{
|
||||
background: filters.codeGamme === value ? "var(--bg-surface)" : "transparent",
|
||||
color: filters.codeGamme === value ? "var(--text-primary)" : "var(--text-secondary)",
|
||||
background: isActive ? "var(--bg-surface)" : "transparent",
|
||||
color: isActive ? "var(--text-primary)" : "var(--text-secondary)",
|
||||
}}
|
||||
>
|
||||
<span>{label}</span>
|
||||
<span
|
||||
className="px-1.5 py-0.5 rounded-full text-[10px] bg-black/5 dark:bg-white/10"
|
||||
style={{ color: filters.codeGamme === value ? "var(--text-primary)" : "var(--text-muted)" }}
|
||||
className="inline-flex items-center justify-center min-w-[20px] px-1.5 py-0.5 rounded-full text-[9px] font-bold tabular-nums tracking-tight transition-colors"
|
||||
style={badgeStyles}
|
||||
>
|
||||
{count}
|
||||
</span>
|
||||
|
||||
@@ -42,6 +42,7 @@ function formatMonthLabel(key: string): string {
|
||||
|
||||
/**
|
||||
* Generates a deterministic color for a store name.
|
||||
* Harmonized with the "Silent UI" philosophy: professional, muted, and non-distracting.
|
||||
*/
|
||||
function getStoreColor(name: string) {
|
||||
let hash = 0;
|
||||
@@ -49,15 +50,16 @@ function getStoreColor(name: string) {
|
||||
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
|
||||
// Spread hues more widely and use golden ratio for better distribution
|
||||
const goldenRatioConjugate = 0.618033988749895;
|
||||
let h = (Math.abs(hash) * goldenRatioConjugate * 360) % 360;
|
||||
// Use a narrower, more professional range of hues (mostly Ardois/Blue/Teal/Sage)
|
||||
// and lower saturation for a "muted" look.
|
||||
const h = Math.abs(hash % 360);
|
||||
const s = 15 + (Math.abs(hash) % 20); // Low saturation: 15-35%
|
||||
const l = 40 + (Math.abs(hash) % 15); // Medium lightness: 40-55%
|
||||
|
||||
// Ensure colors are vibrant but readable
|
||||
return {
|
||||
bg: `hsl(${h}, 75%, 45%)`,
|
||||
border: `hsl(${h}, 85%, 30%)`,
|
||||
text: "#fff"
|
||||
bg: `hsl(${h}, ${s}%, ${l}%)`,
|
||||
border: `hsl(${h}, ${s}%, ${l - 10}%)`,
|
||||
text: "rgba(255, 255, 255, 0.95)"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -160,7 +162,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
className="group flex items-center gap-1.5 cursor-pointer hover:text-emerald-500 transition-colors"
|
||||
title="Copier le code"
|
||||
>
|
||||
<span className="font-mono-nums font-bold text-[12px] tracking-tight opacity-70 group-hover:opacity-100" style={{ color: "var(--text-muted)" }}>
|
||||
<span className="tabular-nums font-bold text-[12px] tracking-tight opacity-70 group-hover:opacity-100" style={{ color: "var(--text-muted)" }}>
|
||||
{value}
|
||||
</span>
|
||||
<div className="shrink-0 transition-all duration-200">
|
||||
@@ -223,7 +225,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
const val = getValue<number>();
|
||||
const color = val >= 80 ? "text-emerald-500" : val >= 50 ? "text-amber-500" : "text-rose-500";
|
||||
return (
|
||||
<div className={cn("text-center font-black text-[13px]", color)}>
|
||||
<div className={cn("text-center font-black text-[13px] tabular-nums", color)}>
|
||||
{val}
|
||||
</div>
|
||||
);
|
||||
@@ -243,7 +245,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
header: () => <div className="text-center w-full">Tot. 12m</div>,
|
||||
size: 90,
|
||||
cell: ({ getValue }) => (
|
||||
<div className="mx-auto w-[60px] text-center px-1 font-mono-nums text-sm font-black py-1.5 rounded-md border" style={{
|
||||
<div className="mx-auto w-[60px] text-center px-1 tabular-nums text-sm font-black py-1.5 rounded-md border" style={{
|
||||
background: "var(--bg-elevated)",
|
||||
color: "var(--text-primary)",
|
||||
borderColor: "var(--border-strong)"
|
||||
@@ -257,7 +259,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
header: () => <div className="text-center w-full">CA</div>,
|
||||
size: 90,
|
||||
cell: ({ getValue }) => (
|
||||
<div className="text-center font-mono-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
|
||||
<div className="text-center tabular-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
|
||||
{Math.round(getValue<number>()).toLocaleString("fr-FR")} €
|
||||
</div>
|
||||
),
|
||||
@@ -268,10 +270,10 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
||||
size: 110,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<span className="font-mono-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
|
||||
<span className="tabular-nums text-[13px] font-bold" style={{ color: "var(--text-primary)" }}>
|
||||
{Math.round(row.original.totalMarge).toLocaleString("fr-FR")} €
|
||||
</span>
|
||||
<span className="font-mono-nums text-[10px] font-bold opacity-70" style={{
|
||||
<span className="tabular-nums text-[10px] font-bold opacity-70" style={{
|
||||
color: row.original.tauxMarge >= 40 ? "var(--accent-success)" : row.original.tauxMarge >= 25 ? "var(--accent-warning)" : "var(--accent-error)"
|
||||
}}>
|
||||
{row.original.tauxMarge.toFixed(1)}%
|
||||
|
||||
Reference in new issue
Block a user