mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
feat(grid): ajout algorithme score produit avec parametrage dynamique et page d'aide
This commit is contained in:
1 parent
f540e4f2b3
commit
3544c0c898
67 files changed
+19476
-15
No files matched your search
@@ -0,0 +1,74 @@
|
||||
"use client";
|
||||
|
||||
import { Search, X, Sun, Moon } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useGridStore } from "@/features/grid/store/use-grid-store";
|
||||
|
||||
export function Header() {
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const setFilter = useGridStore((s) => s.setFilter);
|
||||
const search = useGridStore((s) => s.filters.search);
|
||||
const isDark = resolvedTheme === "dark";
|
||||
|
||||
return (
|
||||
<header
|
||||
className="h-12 flex items-center justify-between px-5 sticky top-0 z-20 glass"
|
||||
style={{ borderBottom: "1px solid var(--border)" }}
|
||||
>
|
||||
{/* Search */}
|
||||
<div className="relative w-72">
|
||||
<Search
|
||||
className="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5"
|
||||
style={{ color: "var(--text-muted)" }}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Rechercher…"
|
||||
value={search}
|
||||
onChange={(e) => setFilter("search", e.target.value)}
|
||||
className="apple-input pr-8 h-8 text-[13px]"
|
||||
style={{ paddingLeft: "32px" }}
|
||||
/>
|
||||
{search && (
|
||||
<button
|
||||
onClick={() => setFilter("search", "")}
|
||||
className="absolute right-2.5 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<X className="w-3 h-3" style={{ color: "var(--text-muted)" }} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right controls */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Theme toggle */}
|
||||
<button
|
||||
onClick={() => setTheme(isDark ? "light" : "dark")}
|
||||
title={isDark ? "Mode clair" : "Mode sombre"}
|
||||
className="w-8 h-8 rounded-[8px] flex items-center justify-center transition-all duration-150"
|
||||
style={{ color: "var(--text-secondary)" }}
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.background = "var(--bg-elevated)"; }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.background = "transparent"; }}
|
||||
>
|
||||
{isDark
|
||||
? <Sun className="w-[15px] h-[15px]" strokeWidth={1.8} />
|
||||
: <Moon className="w-[15px] h-[15px]" strokeWidth={1.8} />}
|
||||
</button>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="w-px h-5 mx-1" style={{ background: "var(--border)" }} />
|
||||
|
||||
{/* User */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-[26px] h-[26px] rounded-full bg-emerald-500 dark:bg-emerald-600 flex items-center justify-center text-[11px] font-bold text-white shadow-sm border border-white/20">
|
||||
M
|
||||
</div>
|
||||
<span className="text-[13px] font-medium" style={{ color: "var(--text-secondary)" }}>
|
||||
Michael
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import { LayoutGrid, Camera, FileDown, Settings, Package, BarChart3 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ icon: LayoutGrid, label: "Grille", href: "/grid" },
|
||||
{ icon: Camera, label: "Snapshots", href: "/snapshots" },
|
||||
{ icon: FileDown, label: "Exports", href: "/exports" },
|
||||
{ icon: BarChart3, label: "Score", href: "/score" },
|
||||
{ icon: Settings, label: "Paramètres", href: "/settings" },
|
||||
];
|
||||
|
||||
export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<aside
|
||||
className="w-60 flex-shrink-0 flex flex-col h-screen fixed left-0 top-0 z-30 glass"
|
||||
style={{ borderRight: "1px solid var(--border)" }}
|
||||
>
|
||||
{/* Logo */}
|
||||
<div className="px-5 py-[18px]" style={{ borderBottom: "1px solid var(--border)" }}>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 rounded-[8px] bg-emerald-500 dark:bg-emerald-600 flex items-center justify-center shadow-sm border border-white/10">
|
||||
<Package className="w-[15px] h-[15px] text-white" strokeWidth={2.2} />
|
||||
</div>
|
||||
<span className="text-[15px] font-semibold tracking-[-0.3px]" style={{ color: "var(--text-primary)" }}>
|
||||
CollectFlow
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="px-2 pt-2.5 space-y-0.5">
|
||||
{NAV_ITEMS.map(({ icon: Icon, label, href }) => {
|
||||
const isActive = pathname.startsWith(href);
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
style={{
|
||||
color: isActive ? "var(--text-primary)" : "var(--text-secondary)",
|
||||
background: isActive ? "var(--bg-elevated)" : "transparent",
|
||||
}}
|
||||
className={cn(
|
||||
"flex items-center gap-2.5 px-3 py-[7px] rounded-[8px] text-[13px] font-medium transition-all duration-150",
|
||||
!isActive && "hover:bg-[var(--bg-elevated)]"
|
||||
)}
|
||||
onMouseEnter={(e) => {
|
||||
if (!isActive) (e.currentTarget as HTMLElement).style.background = "var(--bg-elevated)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!isActive) (e.currentTarget as HTMLElement).style.background = "transparent";
|
||||
}}
|
||||
>
|
||||
<Icon className="w-[15px] h-[15px] shrink-0" strokeWidth={1.8} />
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-auto px-4 py-3" style={{ borderTop: "1px solid var(--border)" }}>
|
||||
<p className="text-[11px]" style={{ color: "var(--text-muted)" }}>v1.0.0-beta</p>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<NextThemesProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem={false}
|
||||
storageKey="collectflow-theme"
|
||||
>
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user