"use client"; import { useState, useCallback } from "react"; import { Save, Eye, EyeOff, CheckCircle, AlertCircle, Loader2, RefreshCw, Sun, Moon, RotateCcw } from "lucide-react"; import { useTheme } from "next-themes"; import { useGridStore } from "@/features/grid/store/use-grid-store"; import { useScoreSettingsStore } from "@/features/score/store/use-score-settings-store"; import { useDbSettingsStore } from "@/features/settings/store/use-db-settings-store"; 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; } function Section({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) { return (

{title}

{subtitle &&

{subtitle}

}
{children}
); } function Field({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) { return (
{children} {hint &&

{hint}

}
); } export default function SettingsPage() { const { resolvedTheme, setTheme } = useTheme(); const isDark = resolvedTheme === "dark"; const displayDensity = useGridStore((s) => s.displayDensity); const setDisplayDensity = useGridStore((s) => s.setDisplayDensity); const seuilAxeFort = useScoreSettingsStore((s) => s.seuilAxeFort); const bonusParAxe = useScoreSettingsStore((s) => s.bonusParAxe); const setSeuilAxeFort = useScoreSettingsStore((s) => s.setSeuilAxeFort); const setBonusParAxe = useScoreSettingsStore((s) => s.setBonusParAxe); const resetScoreDefaults = useScoreSettingsStore((s) => s.resetDefaults); const [showKey, setShowKey] = useState(false); const [apiKey, setApiKey] = useState(""); const [dbStatus, setDbStatus] = useState<"idle" | "testing" | "ok" | "error">("idle"); const [dbError, setDbError] = useState(null); const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved">("idle"); const [models, setModels] = useState([]); const [selectedModel, setSelectedModel] = useState("google/gemini-flash-1.5"); const [modelsStatus, setModelsStatus] = useState<"idle" | "loading" | "ok" | "error">("idle"); const [isMounted, setIsMounted] = useState(false); const { host, setHost, port, setPort, database, setDatabase, user, setUser, password, setPassword, ssl, setSsl, getDatabaseUrl } = useDbSettingsStore(); const fetchModels = useCallback(async (key: string) => { if (!key.trim()) return; setModelsStatus("loading"); try { const res = await fetch("/api/openrouter/models", { headers: { "x-openrouter-key": key } }); if (!res.ok) throw new Error(); const data = await res.json(); setModels(data.models ?? []); setModelsStatus("ok"); } catch { setModelsStatus("error"); } }, []); const reloadFromServer = useCallback(async () => { const config = await getSavedDatabaseConfig(); if (config) { if (config.url) { // Parser l'URL pour remettre dans le store try { const url = new URL(config.url.replace("postgres://", "http://")); // URL parser helper setHost(url.hostname); setPort(url.port || "5432"); setDatabase(url.pathname.slice(1).split("?")[0]); setUser(url.username); setPassword(decodeURIComponent(url.password)); setSsl(config.url.includes("sslmode=require")); } catch (e) { console.error("Failed to parse saved URL", e); } } if (config.openRouterKey) { setApiKey(config.openRouterKey); fetchModels(config.openRouterKey); } if (config.openRouterModel) { setSelectedModel(config.openRouterModel); } } }, [fetchModels, setApiKey, setDatabase, setHost, setPassword, setPort, setSelectedModel, setSsl, setUser]); useEffect(() => { setIsMounted(true); reloadFromServer(); }, [reloadFromServer]); const testDb = async () => { setDbStatus("testing"); setDbError(null); const url = getDatabaseUrl(); const res = await testDatabaseConnection(url); if (res.success) { setDbStatus("ok"); } else { setDbStatus("error"); setDbError(res.error || "Erreur de connexion inconnue"); } }; const handleSave = async () => { setSaveStatus("saving"); const url = getDatabaseUrl(); const res = await saveDatabaseSettings(url, apiKey, selectedModel); if (res.success) { setSaveStatus("saved"); setTimeout(() => setSaveStatus("idle"), 2500); } else { setSaveStatus("idle"); alert("Erreur lors de la sauvegarde : " + res.error); } }; if (!isMounted) { return null; } return (

Paramètres

Configuration de la connexion et des services.

{/* PostgreSQL */}
setHost(e.target.value)} className="apple-input font-mono" />
setPort(e.target.value)} className="apple-input font-mono" /> setDatabase(e.target.value)} className="apple-input font-mono" />
setUser(e.target.value)} className="apple-input font-mono" /> setPassword(e.target.value)} className="apple-input font-mono" />
setSsl(e.target.checked)} className="w-3.5 h-3.5 rounded accent-teal-500 cursor-pointer" />
{dbStatus === "error" && (
Connexion refusée {dbError && {dbError}}
)} {dbStatus === "ok" && ( Connexion réussie ! )}
{/* OpenRouter */}
setApiKey(e.target.value)} className="apple-input font-mono pr-10" />
0 ? ` (${models.length} disponibles)` : ""}`} hint={modelsStatus === "error" ? "⚠ Erreur — vérifiez votre clé puis rechargez" : modelsStatus === "idle" ? "Entrez votre clé et cliquez « Charger les modèles »" : undefined} >
{/* Apparence */}

Thème

Interface claire ou sombre

{/* Segment control */}

Densité de la grille

Hauteur de ligne par défaut

{/* Score Produit */}
setSeuilAxeFort(parseFloat(e.target.value))} className="flex-1 accent-emerald-500 cursor-pointer" />
setSeuilAxeFort(parseFloat(e.target.value) || 30.0)} className="apple-input font-mono-nums text-right pr-6" /> %
setBonusParAxe(parseInt(e.target.value, 10))} className="flex-1 accent-emerald-500 cursor-pointer" />
setBonusParAxe(parseInt(e.target.value, 10) || 0)} className="apple-input font-mono-nums text-right" />
{/* Gestion des Utilisateurs */}
{/* Save */}
); }