"use client"; // Prompts système : sélection, édition monospace, historique des versions, // publication d'une nouvelle version avec confirmation. import { useCallback, useEffect, useState } from "react"; import { FileCode2, Send } from "lucide-react"; import { PageHeader } from "@/components/app-shell"; import { Badge, Button, Card, EmptyState, Modal, Skeleton, Spinner, Textarea, cn } from "@/components/ui"; import { ErrorBanner, SectionTitle, SuccessFlash, fmtDate, fmtInt, postJson, useFetchJson } from "./shared"; type HistoryRow = { id: number; version: number; active: number; created_by: string | null; created_at: string }; type PromptDetail = { name: string; content: string; history: HistoryRow[] }; const MAX_CHARS = 20_000; const MIN_CHARS = 10; export function AdminPrompts() { const { data, error, loading, reload } = useFetchJson<{ names: string[] }>("/api/admin/prompts"); const [selected, setSelected] = useState(null); const [detail, setDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const [detailError, setDetailError] = useState(null); const [content, setContent] = useState(""); const [confirmOpen, setConfirmOpen] = useState(false); const [publishing, setPublishing] = useState(false); const [publishError, setPublishError] = useState(null); const [flash, setFlash] = useState(null); const loadDetail = useCallback(async (name: string) => { setDetailLoading(true); setDetailError(null); try { const res = await fetch(`/api/admin/prompts?name=${encodeURIComponent(name)}`); const j = (await res.json().catch(() => null)) as (PromptDetail & { error?: string }) | null; if (!res.ok || !j) throw new Error(j?.error || `Erreur ${res.status}`); setDetail(j); setContent(j.content); } catch (e) { setDetailError(e instanceof Error ? e.message : "Impossible de charger le prompt."); } finally { setDetailLoading(false); } }, []); useEffect(() => { if (!selected && data?.names.length) { setSelected(data.names[0]); } }, [data, selected]); useEffect(() => { if (selected) loadDetail(selected); }, [selected, loadDetail]); const dirty = detail !== null && content !== detail.content; const tooShort = content.trim().length < MIN_CHARS; const tooLong = content.length > MAX_CHARS; const currentVersion = detail?.history.find((h) => h.active)?.version ?? detail?.history[0]?.version ?? 0; async function publish() { if (!selected) return; setPublishing(true); setPublishError(null); try { const r = await postJson<{ ok: boolean; version: number }>("/api/admin/prompts", { name: selected, content }); setConfirmOpen(false); setFlash(`Version ${r.version} de « ${selected} » publiée — elle s'applique dès les prochaines conversations.`); setTimeout(() => setFlash(null), 5000); await loadDetail(selected); } catch (e) { setPublishError(e instanceof Error ? e.message : "La publication a échoué."); } finally { setPublishing(false); } } return (
{flash &&
} {loading ? (
) : error || !data ? ( ) : data.names.length === 0 ? ( } title="Aucun prompt" description="Aucun prompt système n'a été trouvé (dossier prompts/ vide et base de données sans versions)." /> ) : (
{/* Liste des prompts */} {/* Éditeur */}
{detailLoading ? ( ) : detailError ? ( selected && loadDetail(selected)} /> ) : detail ? ( <>

{detail.name}

v{currentVersion} {dirty && Modifications non publiées}