feat: implement AI Copilot for product analysis, featuring dedicated API, business logic, UI components, and comprehensive documentation.

This commit is contained in:
Michael committed 2026-02-25 10:58:12 +01:00
1 parent 7a6b515db7
commit 1c03403e9a
8 files changed
+229 -11

No files matched your search

+18
View File
@@ -2,6 +2,9 @@ import { NextRequest, NextResponse } from "next/server";
import { getSavedDatabaseConfig } from "@/features/settings/actions";
import { OpenRouterClient } from "@/features/ai-copilot/data/open-router-client";
import { ProductAnalysisInput } from "@/features/ai-copilot/models/ai-analysis.types";
import { db } from "@/db";
import { aiSupplierContext } from "@/db/schema";
import { eq } from "drizzle-orm";
export async function POST(req: NextRequest) {
const config = await getSavedDatabaseConfig();
@@ -16,6 +19,21 @@ export async function POST(req: NextRequest) {
try {
const body: ProductAnalysisInput = await req.json();
// Inject supplier context if available
if (body.codeFournisseur) {
try {
const contextRecord = await db.select()
.from(aiSupplierContext)
.where(eq(aiSupplierContext.codeFournisseur, body.codeFournisseur))
.limit(1);
if (contextRecord.length > 0 && contextRecord[0].context) {
body.supplierContext = contextRecord[0].context;
}
} catch (err) {
console.error("[AI] Failed to fetch supplier context:", err);
}
}
const client = new OpenRouterClient({ apiKey, model });
const result = await client.analyzeProduct(body);
+56
View File
@@ -0,0 +1,56 @@
import { NextResponse } from "next/server";
import { db } from "@/db";
import { aiSupplierContext } from "@/db/schema";
import { eq } from "drizzle-orm";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const codeFournisseur = searchParams.get("fournisseur");
if (!codeFournisseur) {
return NextResponse.json({ error: "Fournisseur manquant" }, { status: 400 });
}
try {
const result = await db.select()
.from(aiSupplierContext)
.where(eq(aiSupplierContext.codeFournisseur, codeFournisseur))
.limit(1);
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: "" });
}
}
export async function POST(request: Request) {
try {
const body = await request.json();
const { codeFournisseur, context } = body;
if (!codeFournisseur) {
return NextResponse.json({ error: "Fournisseur manquant" }, { status: 400 });
}
await db.insert(aiSupplierContext)
.values({
codeFournisseur,
context: context || "",
updatedAt: new Date()
})
.onConflictDoUpdate({
target: aiSupplierContext.codeFournisseur,
set: {
context: context || "",
updatedAt: new Date()
}
});
return NextResponse.json({ success: true });
} catch (error) {
console.error("Error saving AI context:", error);
return NextResponse.json({ error: "Erreur lors de la sauvegarde" }, { status: 500 });
}
}