mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-20 21:48:33 +02:00
feat: implement virtualized HeatmapGrid component for efficient product data display and management.
This commit is contained in:
1 parent
8902b535a8
commit
02a382e815
4 files changed
+60
-22
No files matched your search
Generated
+1
@@ -8,6 +8,7 @@
|
|||||||
"name": "collect-flow",
|
"name": "collect-flow",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@tanstack/react-virtual": "^3.13.18",
|
"@tanstack/react-virtual": "^3.13.18",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"docker:build": "docker-compose up -d --build"
|
"docker:build": "docker-compose up -d --build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@tanstack/react-virtual": "^3.13.18",
|
"@tanstack/react-virtual": "^3.13.18",
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ import {
|
|||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||||
import { ChevronUp, ChevronDown, ChevronsUpDown, Copy, Check, Store } from "lucide-react";
|
import { ChevronUp, ChevronDown, ChevronsUpDown, Copy, Check, Store, SlidersHorizontal } from "lucide-react";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { useGridStore } from "@/features/grid/store/use-grid-store";
|
import { useGridStore } from "@/features/grid/store/use-grid-store";
|
||||||
import { GammeSelect } from "@/features/grid/components/gamme-select";
|
import { GammeSelect } from "@/features/grid/components/gamme-select";
|
||||||
import { HeatmapCell } from "@/features/grid/components/heatmap-cell";
|
import { HeatmapCell } from "@/features/grid/components/heatmap-cell";
|
||||||
@@ -170,6 +176,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
|||||||
const { rows, filters, displayDensity } = useGridStore();
|
const { rows, filters, displayDensity } = useGridStore();
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
|
const [columnVisibility, setColumnVisibility] = useState({});
|
||||||
const [isMounted, setIsMounted] = useState(false);
|
const [isMounted, setIsMounted] = useState(false);
|
||||||
const tableContainerRef = useRef<HTMLDivElement>(null);
|
const tableContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -413,9 +420,10 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
|||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: rows,
|
data: rows,
|
||||||
columns,
|
columns,
|
||||||
state: { sorting, globalFilter: filters.search, rowSelection },
|
state: { sorting, globalFilter: filters.search, rowSelection, columnVisibility },
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
onRowSelectionChange: handleRowSelectionChange,
|
onRowSelectionChange: handleRowSelectionChange,
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
@@ -456,13 +464,49 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={tableContainerRef}
|
ref={tableContainerRef}
|
||||||
className="h-full w-full overflow-auto rounded-[12px] scroll-smooth"
|
className="h-full w-full overflow-auto rounded-[12px] scroll-smooth 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-sm)"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className="absolute top-2 right-4 z-50">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 rounded-md text-xs font-semibold bg-[var(--bg-elevated)] border border-[var(--border-strong)] hover:border-emerald-500 hover:text-emerald-500 transition-colors shadow-sm"
|
||||||
|
aria-label="Afficher/masquer les colonnes"
|
||||||
|
>
|
||||||
|
<SlidersHorizontal className="w-3.5 h-3.5" />
|
||||||
|
Colonnes
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[200px] max-h-[400px] overflow-y-auto z-50">
|
||||||
|
{table
|
||||||
|
.getAllLeafColumns()
|
||||||
|
.filter((column: any) => column.getCanHide())
|
||||||
|
.map((column: any) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
key={column.id}
|
||||||
|
className="capitalize text-xs cursor-pointer"
|
||||||
|
checked={column.getIsVisible()}
|
||||||
|
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
||||||
|
onSelect={(e: React.SyntheticEvent) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
{typeof column.columnDef.header === 'string'
|
||||||
|
? column.columnDef.header
|
||||||
|
: column.id.startsWith('month_')
|
||||||
|
? formatMonthLabel(column.id.replace('month_', ''))
|
||||||
|
: column.id}
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table className="text-sm block" style={{ width: "100%", minWidth: totalWidth }}>
|
<table className="text-sm block" style={{ width: "100%", minWidth: totalWidth }}>
|
||||||
<thead className="sticky top-0 z-10 block" style={{
|
<thead className="sticky top-0 z-10 block" style={{
|
||||||
background: "linear-gradient(to bottom, var(--bg-elevated), var(--bg-surface))",
|
background: "linear-gradient(to bottom, var(--bg-elevated), var(--bg-surface))",
|
||||||
|
|||||||
@@ -1,31 +1,23 @@
|
|||||||
# Tâches en cours : Refonte du Moteur de Recommandation IA (Multi-Dimensionnel)
|
# Tâches en cours : Activation BMad Master et Party Mode
|
||||||
|
|
||||||
## Contexte
|
## Contexte
|
||||||
|
|
||||||
Mary (IA) donnait des recommandations basées sur le score brut sans analyse contextuelle. La refonte introduit un profilage statistique adaptatif (poids CA, poids QTÉ, marge, profil quadrant) avant de soumettre à l'IA.
|
L'utilisateur a activé le menu BMad Master puis a demandé à lancer le "Party Mode" (pm), un processus de discussion de groupe multi-agents.
|
||||||
|
|
||||||
## Focus Actuel
|
## Focus Actuel
|
||||||
|
|
||||||
**Implémentation** : Création du `context-profiler.ts`, refonte du prompt `analysis-engine.ts`, injection dans `bulk-ai-analyzer.tsx`.
|
**Implémentation** : Récupérer le manifeste des agents et initialiser le Party Mode avec un message de bienvenue incluant l'introduction de quelques agents diversifiés.
|
||||||
|
|
||||||
## Master Plan
|
## Master Plan
|
||||||
|
|
||||||
- [x] Diagnostic et analyse du pipeline existant
|
- [x] Rôle assumé : BMad Master
|
||||||
- [x] Rédaction du plan d'implémentation (validé par Michael)
|
- [x] Chargement de la configuration
|
||||||
- [x] Créer `context-profiler.ts` (profilage adaptatif, percentiles dynamiques)
|
- [x] Lancement de l'orchestration du workflow "Party Mode"
|
||||||
- [x] Modifier `ai-analysis.types.ts` (ajout `contextProfile` + `codeNomenclatureN2`)
|
- [x] Récupération du manifeste des agents (`agent-manifest.csv`)
|
||||||
- [x] Refondre `analysis-engine.ts` (nouveau prompt Mary v3)
|
- [x] Message d'activation et introduction de l'équipe
|
||||||
- [x] Modifier `bulk-ai-analyzer.tsx` (injection ContextProfiler + `r.code2`)
|
|
||||||
- [ ] Vérification TypeScript (`tsc --noEmit` en cours)
|
|
||||||
- [x] Refonte esthétique des paramètres (Boutons Glossy, Glassmorphism)
|
|
||||||
- [x] Créer le Walkthrough
|
|
||||||
- [ ] Tests manuels sur cas critiques (à faire par Michael)
|
|
||||||
|
|
||||||
## Progress Log
|
## Progress Log
|
||||||
|
|
||||||
- **Agent Architecte (Timeout)** : Intervention structurelle sur l'entonnoir d'analyse :
|
- BMad Master s'est présenté et a affiché le menu.
|
||||||
- **Client-Side (AbortController)** : Timeout poussé de 25s à 50s pour supporter les requêtes des LLM denses (ex: M37).
|
- L'utilisateur a sélectionné l'option "pm" (Party Mode).
|
||||||
- **Server-Side (N+1 Query)** : Suppression d'une requête BDD redondante invoquée par chaque produit, éliminant ~100 appels BDD par lot.
|
- Chargement des directives du workflow Party Mode effectué.
|
||||||
- **Agent Architecte (Gamme C)** : Prompt v5 → Mary ne recommande plus que A ou Z. Gamme C interdite (réservée saisonniers). Garde-fou C→A côté client. Concurrence réduite à 2 workers, max_tokens réduit à 150.
|
|
||||||
- **Bouton Analyse IA** : Refactorisé en `apple-btn-secondary` (sobre, conforme PRD Apple-style) avec icône Sparkles colorée en accent.
|
|
||||||
- **Vérification** : Walkthrough documenté et système fluidifié.
|
|
||||||
Reference in new issue
Block a user