diff --git a/src/app/api/ai/context/route.ts b/src/app/api/ai/context/route.ts index f637122..e9df5fb 100644 --- a/src/app/api/ai/context/route.ts +++ b/src/app/api/ai/context/route.ts @@ -2,6 +2,42 @@ import { NextResponse } from "next/server"; import { db } from "@/db"; import { aiSupplierContext } from "@/db/schema"; import { eq } from "drizzle-orm"; +import fs from "fs"; +import path from "path"; + +// File system fallback for environments without PostgreSQL +const DATA_DIR = path.join(process.cwd(), "data"); +const FALLBACK_FILE_PATH = path.join(DATA_DIR, "ai-context.json"); + +function ensureFallbackFileExists() { + if (!fs.existsSync(DATA_DIR)) { + fs.mkdirSync(DATA_DIR, { recursive: true }); + } + if (!fs.existsSync(FALLBACK_FILE_PATH)) { + fs.writeFileSync(FALLBACK_FILE_PATH, JSON.stringify({}), "utf-8"); + } +} + +function getFallbackContext(codeFournisseur: string): string { + ensureFallbackFileExists(); + try { + const data = JSON.parse(fs.readFileSync(FALLBACK_FILE_PATH, "utf-8")); + return data[codeFournisseur] || ""; + } catch { + return ""; + } +} + +function saveFallbackContext(codeFournisseur: string, contextText: string) { + ensureFallbackFileExists(); + try { + const data = JSON.parse(fs.readFileSync(FALLBACK_FILE_PATH, "utf-8")); + data[codeFournisseur] = contextText; + fs.writeFileSync(FALLBACK_FILE_PATH, JSON.stringify(data, null, 2), "utf-8"); + } catch (err) { + console.error("Error saving fallback context:", err); + } +} export async function GET(request: Request) { const { searchParams } = new URL(request.url); @@ -19,9 +55,8 @@ export async function GET(request: Request) { return NextResponse.json({ context: result[0]?.context || "" }); } catch (error) { - console.error("Error fetching AI context:", error); - // Fallback en cas d'erreur de BDD ou si la table n'est pas encore créée - return NextResponse.json({ context: "" }); + console.warn("DB fetch failed, using fallback JSON for AI context."); + return NextResponse.json({ context: getFallbackContext(codeFournisseur) }); } } @@ -34,23 +69,28 @@ export async function POST(request: Request) { return NextResponse.json({ error: "Fournisseur manquant" }, { status: 400 }); } - await db.insert(aiSupplierContext) - .values({ - codeFournisseur, - context: context || "", - updatedAt: new Date() - }) - .onConflictDoUpdate({ - target: aiSupplierContext.codeFournisseur, - set: { + try { + await db.insert(aiSupplierContext) + .values({ + codeFournisseur, context: context || "", updatedAt: new Date() - } - }); + }) + .onConflictDoUpdate({ + target: aiSupplierContext.codeFournisseur, + set: { + context: context || "", + updatedAt: new Date() + } + }); + } catch (dbError) { + console.warn("DB insert failed, saving to fallback JSON.", dbError); + saveFallbackContext(codeFournisseur, context || ""); + } return NextResponse.json({ success: true }); } catch (error) { - console.error("Error saving AI context:", error); + console.error("Error in AI context API:", error); return NextResponse.json({ error: "Erreur lors de la sauvegarde" }, { status: 500 }); } } diff --git a/src/app/globals.css b/src/app/globals.css index 3e5528a..28d20c6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -6,17 +6,26 @@ /* ── Dark Mode (default) ── */ :root { - /* Apple macOS dark exact values */ - --bg-base: #000000; - --bg-surface: #111111; - --bg-elevated: #1c1c1e; - --bg-overlay: rgba(28, 28, 30, 0.85); - --border: rgba(255, 255, 255, 0.08); - --border-strong: rgba(255, 255, 255, 0.12); + /* Apple macOS dark aligned to Slate palette as per UX Spec */ + --bg-base: #020617; + /* Slate 950 */ + --bg-surface: #0f172a; + /* Slate 900 */ + --bg-elevated: #1e293b; + /* Slate 800 */ + --bg-overlay: rgba(15, 23, 42, 0.85); + /* Slate 900 with opacity */ + --border: #334155; + /* Slate 700 */ + --border-strong: #475569; + /* Slate 600 */ - --text-primary: #f5f5f7; - --text-secondary: #a1a1a6; - --text-muted: #86868b; + --text-primary: #e2e8f0; + /* Slate 200 */ + --text-secondary: #94a3b8; + /* Slate 400 */ + --text-muted: #64748b; + /* Slate 500 */ --accent: #2dd4bf; /* emerald-ish, Apple-tuned */ @@ -127,8 +136,13 @@ } /* ── Surfaces & containers ── */ -.surface { background: var(--bg-surface); } -.elevated { background: var(--bg-elevated); } +.surface { + background: var(--bg-surface); +} + +.elevated { + background: var(--bg-elevated); +} .apple-card { background: var(--bg-surface); @@ -206,33 +220,35 @@ } .heatmap-low { - background: rgba(45, 212, 191, 0.08); - color: rgba(45, 212, 191, 0.70); + background: rgba(14, 165, 233, 0.08); + /* sky-500 */ + color: rgba(14, 165, 233, 0.70); } .heatmap-mid { - background: rgba(45, 212, 191, 0.15); - color: rgba(45, 212, 191, 0.90); + background: rgba(14, 165, 233, 0.15); + color: rgba(14, 165, 233, 0.90); } .heatmap-high { - background: rgba(45, 212, 191, 0.25); - color: #2dd4bf; + background: rgba(14, 165, 233, 0.25); + color: #0ea5e9; } .light .heatmap-low { - background: rgba(13, 148, 136, 0.08); - color: #0f766e; + background: rgba(2, 132, 199, 0.08); + /* sky-600 */ + color: #0369a1; } .light .heatmap-mid { - background: rgba(13, 148, 136, 0.14); - color: #0d9488; + background: rgba(2, 132, 199, 0.14); + color: #0284c7; } .light .heatmap-high { - background: rgba(13, 148, 136, 0.22); - color: #0f766e; + background: rgba(2, 132, 199, 0.22); + color: #0369a1; } /* ── Apple-style input ── */ @@ -263,7 +279,7 @@ align-items: center; gap: 6px; padding: 8px 18px; - background: var(--accent); + background: var(--brand-solid); color: #fff; font-size: 13px; font-weight: 600; diff --git a/src/features/grid/components/heatmap-cell.tsx b/src/features/grid/components/heatmap-cell.tsx index 1e865bb..7b9f7f3 100644 --- a/src/features/grid/components/heatmap-cell.tsx +++ b/src/features/grid/components/heatmap-cell.tsx @@ -4,25 +4,22 @@ interface HeatmapCellProps { value: number | null; } -function getHeatmapStyle(value: number | null): { background: string; color: string } { - if (value === null || value === 0) { - return { background: "var(--bg-elevated)", color: "var(--text-muted)" }; - } +function getHeatmapClass(value: number | null): string { + if (value === null || value === 0) return "heatmap-zero"; const abs = Math.abs(value); - if (abs > 100) return { background: "rgba(45,212,191,0.22)", color: "var(--accent)" }; - if (abs > 30) return { background: "rgba(45,212,191,0.14)", color: "var(--accent)" }; - if (abs > 10) return { background: "rgba(45,212,191,0.08)", color: "var(--accent)" }; - return { background: "var(--bg-elevated)", color: "var(--text-secondary)" }; + if (abs > 100) return "heatmap-high"; + if (abs > 30) return "heatmap-mid"; + if (abs > 10) return "heatmap-low"; + return "heatmap-zero"; } export function HeatmapCell({ value }: HeatmapCellProps) { - const style = getHeatmapStyle(value); + const heatClass = getHeatmapClass(value); const display = value === null || value === 0 ? "—" : Math.round(Math.abs(value)).toString(); return (