mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-14 18:58:17 +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,79 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
const AnalyzeSchema = z.object({
|
||||
codein: z.string(),
|
||||
libelle1: z.string(),
|
||||
totalCa: z.number(),
|
||||
tauxMarge: z.number(),
|
||||
totalQuantite: z.number(),
|
||||
sales12m: z.record(z.string(), z.number()),
|
||||
codeGamme: z.string().nullable(),
|
||||
});
|
||||
|
||||
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
||||
const SYSTEM_PROMPT = `Tu es un expert en analyse de gammes de produits B2B pour un acheteur retail professionnel.
|
||||
En analysant les données de ventes fournies, génère une recommandation de gamme concise (A=Cœur, B=Complémentaire, C=Saisonnier, Z=Sortie).
|
||||
Ta réponse doit être en 1-2 phrases maximum, en français, directe et actionnable.
|
||||
Format: "[Recommandation]: [Justification courte basée sur les données]"`;
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const apiKey = process.env.OPENROUTER_API_KEY;
|
||||
if (!apiKey) {
|
||||
return NextResponse.json({ error: "OPENROUTER_API_KEY not configured" }, { status: 503 });
|
||||
}
|
||||
|
||||
const body = await req.json();
|
||||
const parsed = AnalyzeSchema.safeParse(body);
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 400 });
|
||||
}
|
||||
|
||||
const { codein, libelle1, totalCa, tauxMarge, totalQuantite, sales12m, codeGamme } = parsed.data;
|
||||
|
||||
// Format monthly sales for the prompt
|
||||
const monthlySummary = Object.entries(sales12m)
|
||||
.map(([k, v]) => `${k}: ${Math.round(v)} unités`)
|
||||
.join(", ");
|
||||
|
||||
const userMessage = `Produit: "${libelle1}" (Code: ${codein})
|
||||
Gamme actuelle: ${codeGamme ?? "Non définie"}
|
||||
CA total 12m: ${totalCa.toFixed(0)}€ | Taux de marge: ${tauxMarge.toFixed(1)}% | Volume: ${Math.round(totalQuantite)} unités
|
||||
Historique mensuel: ${monthlySummary}
|
||||
|
||||
Quelle gamme recommandes-tu et pourquoi ?`;
|
||||
|
||||
try {
|
||||
const response = await fetch(OPENROUTER_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
"HTTP-Referer": "https://collectflow.app",
|
||||
"X-Title": "CollectFlow AI Copilot",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "google/gemini-flash-1.5",
|
||||
messages: [
|
||||
{ role: "system", content: SYSTEM_PROMPT },
|
||||
{ role: "user", content: userMessage },
|
||||
],
|
||||
max_tokens: 150,
|
||||
temperature: 0.3,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const err = await response.text();
|
||||
return NextResponse.json({ error: `OpenRouter error: ${err}` }, { status: response.status });
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const insight = data.choices?.[0]?.message?.content ?? "Analyse indisponible.";
|
||||
|
||||
return NextResponse.json({ insight, codein });
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : "Unknown error";
|
||||
return NextResponse.json({ error: msg }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user