"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import useSWR, { mutate } from "swr"; import { Plus, MessageSquare, FileText, Trash2, Download, Upload, Save, Eye, Pencil, ExternalLink, Brain, LayoutPanelLeft, ShieldCheck } from "lucide-react"; import { api, fetcher, relTime } from "@/lib/client/fetcher"; import type { ArtifactDTO, ConversationDTO, MemoryDTO, ProjectDTO } from "@/lib/client/types"; import { useModels } from "@/lib/client/use-models"; import { Button, Empty, Spinner, Tabs, Textarea, Toggle, toast, cn, Badge } from "../ui"; import { Markdown } from "../chat/markdown"; import { MemoryCard } from "../memory/memory-card"; import { AddMemoryDialog } from "../memory/add-memory-dialog"; import { fmtBytes } from "./project-shared"; export interface ProjectFile { id: string; filename: string; mime: string; size_bytes: number; page_count: number | null; created_at: string } interface ToolInfo { name: string; description: string; permission: string; category: string } export function ConversationsTab({ project }: { project: ProjectDTO }) { const router = useRouter(); const { data } = useSWR<{ conversations: ConversationDTO[] }>(`/api/conversations?project=${project.id}`, fetcher); return (
{!data && } {data && !data.conversations.length && } title="Aucune conversation" hint="Les conversations démarrées depuis ce projet héritent de ses instructions, de son modèle et de sa mémoire." />}
{data?.conversations.map(c => ( {c.title}{c.message_count} messages · {Number(c.total_cost_usd).toFixed(3)} $ · {c.model_id ?? "auto"} {relTime(c.last_message_at ?? c.created_at)} ))}
); } export function InstructionsTab({ project, onSaved }: { project: ProjectDTO; onSaved: () => void }) { const { chat } = useModels(); const { data: settings } = useSWR<{ tools: ToolInfo[] }>("/api/settings", fetcher); const [instructions, setInstructions] = useState(project.instructions ?? ""); const [model, setModel] = useState(project.default_model ?? ""); const [perms, setPerms] = useState>(() => Object.fromEntries(Object.entries(project.tool_permissions ?? {}).map(([k, v]) => [k, !!v]))); const [busy, setBusy] = useState(false); useEffect(() => { setInstructions(project.instructions ?? ""); setModel(project.default_model ?? ""); }, [project.id, project.instructions, project.default_model]); const tools = settings?.tools ?? []; const allowed = (name: string) => perms[name] ?? true; async function save() { setBusy(true); try { await api(`/api/projects/${project.id}`, { method: "PATCH", json: { instructions: instructions.trim() || null, defaultModel: model || null, toolPermissions: Object.fromEntries(tools.map(t => [t.name, allowed(t.name)])) } }); toast("Instructions enregistrées", "ok"); onSaved(); } catch (e) { toast((e as Error).message, "error"); } finally { setBusy(false); } } return (