feat(grid): preserve initial gamme reference and add modification icon

This commit is contained in:
Michael committed 2026-02-21 18:30:48 +01:00
1 parent a0f8d9c373
commit 9493a39d12
5 files changed
+42 -37

No files matched your search

-30
View File
@@ -1,30 +0,0 @@
import { db } from "./src/db";
import { ventesProduits } from "./src/db/schema";
import { sql } from "drizzle-orm";
async function diagnose() {
console.log("Checking DB connection...");
try {
await db.execute(sql`SELECT 1`);
console.log("DB Connection OK.");
console.log("Checking table 'ventes_produits'...");
const tableCheck = await db.execute(sql`SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'ventes_produits'
)`);
console.log("Table exists:", tableCheck.rows[0].exists);
if (tableCheck.rows[0].exists) {
const count = await db.select({ count: sql`count(*)` }).from(ventesProduits);
console.log("Total rows in 'ventes_produits':", count[0].count);
}
} catch (err) {
console.error("Diagnosis failed:", err);
} finally {
process.exit(0);
}
}
diagnose();
+1
View File
@@ -14,6 +14,7 @@ export const ventesProduits = pgTable("ventes_produits", {
// Gamme & Nomenclature // Gamme & Nomenclature
codeGamme: varchar("code_gamme", { length: 20 }), codeGamme: varchar("code_gamme", { length: 20 }),
codeGammeInit: varchar("code_gamme_init", { length: 20 }),
code3: varchar("code3", { length: 20 }), code3: varchar("code3", { length: 20 }),
libelle3: varchar("libelle3", { length: 500 }), libelle3: varchar("libelle3", { length: 500 }),
+24 -1
View File
@@ -2,10 +2,12 @@
import { db } from "@/db"; import { db } from "@/db";
import { ventesProduits } from "@/db/schema"; import { ventesProduits } from "@/db/schema";
import { eq, and, ne, like } from "drizzle-orm"; import { eq, and, ne, like, sql } from "drizzle-orm";
import type { ProductRow, GammeCode, GridFilters } from "@/types/grid"; import type { ProductRow, GammeCode, GridFilters } from "@/types/grid";
import { computeProductScores } from "@/lib/score-engine"; import { computeProductScores } from "@/lib/score-engine";
let isSchemaInitialized = false;
interface GetProductRowsInput { interface GetProductRowsInput {
codeFournisseur: string; codeFournisseur: string;
magasin?: string; magasin?: string;
@@ -15,6 +17,26 @@ interface GetProductRowsInput {
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> { export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
const { codeFournisseur, magasin = "TOTAL", filters } = input; const { codeFournisseur, magasin = "TOTAL", filters } = input;
// Self-healing: Ensure codeGammeInit column exists and is populated
if (!isSchemaInitialized) {
try {
await db.execute(sql`
ALTER TABLE ventes_produits
ADD COLUMN IF NOT EXISTS code_gamme_init VARCHAR(20)
`);
await db.execute(sql`
UPDATE ventes_produits
SET code_gamme_init = code_gamme
WHERE code_gamme_init IS NULL
`);
isSchemaInitialized = true;
console.log("[Schema] codeGammeInit initialized successfully.");
} catch (err) {
console.error("[Schema] Failed to initialize codeGammeInit:", err);
// We continue anyway, the mapping fallback will handle it for the UI
}
}
// ... (allowedPeriods calculation remains the same) // ... (allowedPeriods calculation remains the same)
const now = new Date(); const now = new Date();
const allowedPeriods = new Set<string>(); const allowedPeriods = new Set<string>();
@@ -106,6 +128,7 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
code3: c3, code3: c3,
libelle3: row.libelle3 ?? "", libelle3: row.libelle3 ?? "",
codeGamme: (row.codeGamme as GammeCode | null), codeGamme: (row.codeGamme as GammeCode | null),
codeGammeInit: (row.codeGammeInit as GammeCode | null) ?? (row.codeGamme as GammeCode | null),
codeGammeDraft: null, codeGammeDraft: null,
sales12m: { [periode]: qty }, sales12m: { [periode]: qty },
totalQuantite: qty, totalQuantite: qty,
+15 -6
View File
@@ -285,7 +285,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
size: 56, size: 56,
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-center font-bold text-[12px]" style={{ color: "var(--text-secondary)" }}> <div className="text-center font-bold text-[12px]" style={{ color: "var(--text-secondary)" }}>
{row.original.codeGamme || "-"} {row.original.codeGammeInit || "-"}
</div> </div>
), ),
}, },
@@ -295,12 +295,21 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
size: 110, size: 110,
cell: ({ row }) => { cell: ({ row }) => {
const effectiveGamme = (draftChanges[row.original.codein] ?? row.original.codeGamme) as GammeCode | null; const effectiveGamme = (draftChanges[row.original.codein] ?? row.original.codeGamme) as GammeCode | null;
const isModified = row.original.codeGamme !== row.original.codeGammeInit && row.original.codeGammeInit !== null;
return ( return (
<GammeSelect <div className="relative group/gamme">
value={effectiveGamme} <GammeSelect
isDraft={!!draftChanges[row.original.codein]} value={effectiveGamme}
onChange={(g: GammeCode) => setDraftGamme(row.original.codein, g)} isDraft={!!draftChanges[row.original.codein]}
/> onChange={(g: GammeCode) => setDraftGamme(row.original.codein, g)}
/>
{isModified && !draftChanges[row.original.codein] && (
<div className="absolute -right-2 -top-2 bg-emerald-500 text-white rounded-full p-0.5 shadow-sm z-10 animate-in zoom-in-50" title="Modifié et validé">
<Check className="w-2.5 h-2.5" />
</div>
)}
</div>
); );
}, },
}, },
+2
View File
@@ -13,6 +13,7 @@ export interface VenteProduit {
gtin: string | null; gtin: string | null;
reference: string | null; reference: string | null;
codeGamme: GammeCode | null; codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
code3: string | null; code3: string | null;
libelle3: string | null; libelle3: string | null;
magasin: string; magasin: string;
@@ -43,6 +44,7 @@ export interface ProductRow {
code3: string; code3: string;
libelle3: string; libelle3: string;
codeGamme: GammeCode | null; codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
/** Draft gamme value before saving */ /** Draft gamme value before saving */
codeGammeDraft: GammeCode | null; codeGammeDraft: GammeCode | null;
/** 12-month sales quantities, indexed by YYYYMM key */ /** 12-month sales quantities, indexed by YYYYMM key */