feat: add settings page for database, OpenRouter, score, theme, and display density configurations.

This commit is contained in:
Michael committed 2026-02-22 14:16:49 +01:00
1 parent 359d196c4d
commit cfd079fbf5
7 files changed
+41 -28

No files matched your search

-2
View File
@@ -9,8 +9,6 @@ import { useDbSettingsStore } from "@/features/settings/store/use-db-settings-st
import { testDatabaseConnection, saveDatabaseSettings, getSavedDatabaseConfig } from "@/features/settings/actions";
import { useEffect } from "react";
import { UserManagement } from "@/features/admin/components/user-management";
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
interface OpenRouterModel { id: string; name: string; free: boolean; }
@@ -12,9 +12,9 @@ En analysant les données de ventes fournies, génère une recommandation de gam
Critères de pondération et Règles Métier Strictes :
1. Normalisation Totale (Base 2 magasins) : TOUTES les statistiques fournies sont PONDÉRÉES par 2 si le produit n'est au catalogue que de 1 magasin.
2. Segmentation par Rayon (Univers) : Privilégie la comparaison "Intra-Rayon". Un produit doit être performant par rapport aux standards de son propre Rayon (Niveau 2).
3. Seuil "Permanent" (A) : Pour être A, un produit doit avoir un volume PONDÉRÉ significatif (ex: > 30-50 unités/an).
4. Seuil "Sortie" (Z) : Un produit avec moins de 10 unités pondérées par an et un CA faible est un candidat à la sortie (Z).
5. Équilibre : Un produit "A" doit justifier sa place par son flux OU sa rentabilité brute.
3. Score de Performance Global (0-10) : Un score > 7 est un indicateur fort pour "Permanent" (A). Un score < 3 est un indicateur fort pour "Sortie" (Z).
4. Régularité des Ventes (0-12 mois) : Un produit avec une vente régulière (> 8 mois/an) est un candidat idéal pour "Permanent" (A). Une régularité faible (< 3 mois) couplée à un volume total faible indique une "Sortie" (Z).
5. Équilibre : Un produit "A" doit justifier sa place par son flux, sa rentabilité brute OU son score global.
Ta réponse doit être courte, directe et sans complaisance.
Format impératif : "[Recommandation]: [Justification courte]"`;
@@ -38,8 +38,10 @@ Benchmarks Rayon ("${p.libelleNiveau2}") :
Rayon : ${p.libelleNiveau2 || "Non classé"}
Gamme actuelle : ${p.codeGamme ?? "Non définie"}
${volumeInfo}${benchmarks}
Indicateurs Clés :
- Marge : ${p.tauxMarge.toFixed(1)}%
Indicateurs de Performance :
- Score Global App : ${p.score.toFixed(1)}/10
- Régularité des ventes : ${p.regularityScore}/12 mois
- Marge brute : ${p.tauxMarge.toFixed(1)}%
- Historique mensuel (Pondéré) : ${monthlySummary}
Quelle est ta recommandation (A, C ou Z) et pourquoi ?`;
@@ -15,6 +15,8 @@ export interface ProductAnalysisInput {
storeCount: number;
sales12m: Record<string, number>;
codeGamme: string | null;
score: number;
regularityScore: number;
}
export interface AnalysisResult {
@@ -80,6 +80,8 @@ export function BulkAiAnalyzer() {
Object.entries(r.sales12m || {}).map(([month, val]) => [month, val * weight])
),
codeGamme: r.codeGamme || null,
score: r.score || 0,
regularityScore: Object.values(r.sales12m || {}).filter(v => v > 0).length,
};
});
+24
View File
@@ -0,0 +1,24 @@
import type { NextAuthConfig } from "next-auth";
export const authConfig = {
pages: {
signIn: "/login",
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = (user as any).role;
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (session.user) {
(session.user as any).role = token.role as string;
(session.user as any).id = token.id as string;
}
return session;
},
},
providers: [], // Les providers seront ajoutés dans auth.ts
} satisfies NextAuthConfig;
+2 -20
View File
@@ -5,8 +5,10 @@ import { users } from "@/db/schema";
import { eq } from "drizzle-orm";
import { verifyPassword } from "@/features/auth/logic/auth-logic";
import { ensureAdminExists } from "@/features/auth/logic/seed-admin";
import { authConfig } from "./auth.config";
export const { handlers, auth, signIn, signOut } = NextAuth({
...authConfig,
providers: [
Credentials({
name: "Credentials",
@@ -15,7 +17,6 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
password: { label: "Mot de passe", type: "password" }
},
async authorize(credentials) {
// S'assure que le compte admin par défaut existe
await ensureAdminExists();
if (!credentials?.username || !credentials?.password) return null;
@@ -42,23 +43,4 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
}
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = (user as any).role;
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (session.user) {
(session.user as any).role = token.role as string;
(session.user as any).id = token.id as string;
}
return session;
},
},
pages: {
signIn: "/login",
}
});
+4 -1
View File
@@ -1,6 +1,9 @@
import { auth } from "@/lib/auth";
import NextAuth from "next-auth";
import { authConfig } from "@/lib/auth.config";
import { NextResponse } from "next/server";
const { auth } = NextAuth(authConfig);
export default auth((req) => {
const isLoggedIn = !!req.auth;
const { nextUrl } = req;