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
codeGamme: varchar("code_gamme", { length: 20 }),
codeGammeInit: varchar("code_gamme_init", { length: 20 }),
code3: varchar("code3", { length: 20 }),
libelle3: varchar("libelle3", { length: 500 }),
+24 -1
View File
@@ -2,10 +2,12 @@
import { db } from "@/db";
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 { computeProductScores } from "@/lib/score-engine";
let isSchemaInitialized = false;
interface GetProductRowsInput {
codeFournisseur: string;
magasin?: string;
@@ -15,6 +17,26 @@ interface GetProductRowsInput {
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
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)
const now = new Date();
const allowedPeriods = new Set<string>();
@@ -106,6 +128,7 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
code3: c3,
libelle3: row.libelle3 ?? "",
codeGamme: (row.codeGamme as GammeCode | null),
codeGammeInit: (row.codeGammeInit as GammeCode | null) ?? (row.codeGamme as GammeCode | null),
codeGammeDraft: null,
sales12m: { [periode]: qty },
totalQuantite: qty,
+15 -6
View File
@@ -285,7 +285,7 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
size: 56,
cell: ({ row }) => (
<div className="text-center font-bold text-[12px]" style={{ color: "var(--text-secondary)" }}>
{row.original.codeGamme || "-"}
{row.original.codeGammeInit || "-"}
</div>
),
},
@@ -295,12 +295,21 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
size: 110,
cell: ({ row }) => {
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 (
<GammeSelect
value={effectiveGamme}
isDraft={!!draftChanges[row.original.codein]}
onChange={(g: GammeCode) => setDraftGamme(row.original.codein, g)}
/>
<div className="relative group/gamme">
<GammeSelect
value={effectiveGamme}
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;
reference: string | null;
codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
code3: string | null;
libelle3: string | null;
magasin: string;
@@ -43,6 +44,7 @@ export interface ProductRow {
code3: string;
libelle3: string;
codeGamme: GammeCode | null;
codeGammeInit: GammeCode | null;
/** Draft gamme value before saving */
codeGammeDraft: GammeCode | null;
/** 12-month sales quantities, indexed by YYYYMM key */