SPB Git
3.9 KB · 49 lines tsx
1"use client";2import useSWR from "swr";3import { Settings } from "lucide-react";4import { api, fetcher } from "@/lib/client/fetcher";5import { Spinner, toast } from "@/components/ui";6import { MemoryToggles } from "@/components/memory/memory-shared";7import { AppearanceSection, ModelsSection, SpendingSection, ToolsSection, IntegrationsSection, SecuritySection, InstallHint, Section, type Spending, type ToolInfo } from "@/components/settings/sections";89interface Resp {10  settings: { memory?: Record<string, unknown>; spending?: Spending; routing?: { defaultMode?: string }; appearance?: Record<string, unknown>; registry?: { refreshedAt?: string; live?: number } };11  owner: { username: string; createdAt: string; totp: boolean } | null;12  tools: ToolInfo[];13  integrations: { openai: boolean; anthropic: boolean; tavily: boolean };14}1516const NAV = [["apparence", "Apparence"], ["modeles", "Modèles"], ["depenses", "Dépenses"], ["memoire", "Mémoire"], ["outils", "Outils"], ["integrations", "Intégrations"], ["securite", "Sécurité"], ["apropos", "À propos"]];1718export default function SettingsPage() {19  const { data, mutate } = useSWR<Resp>("/api/settings", fetcher);20  async function save(key: string, value: unknown) { try { await api("/api/settings", { method: "PATCH", json: { key, value } }); toast("Réglages enregistrés", "ok"); void mutate(); } catch (e) { toast((e as Error).message, "error"); } }21  return (22    <div className="min-h-0 flex-1 overflow-y-auto">23      <div className="mx-auto flex w-full max-w-5xl gap-8 px-4 py-6 md:px-8">24        <nav className="sticky top-6 hidden w-40 shrink-0 self-start lg:block">{NAV.map(([id, l]) => <a key={id} href={`#${id}`} className="block rounded-lg px-3 py-1.5 text-[13px] text-fg-2 hover:bg-surface-2 hover:text-fg">{l}</a>)}</nav>25        <div className="min-w-0 flex-1 space-y-8">26          <div><h1 className="flex items-center gap-2 text-xl font-semibold tracking-tight"><Settings className="h-5 w-5 text-accent" /> Réglages</h1><p className="mt-1 text-[13px] text-muted">Apparence, modèles, dépenses, mémoire, outils et sécurité.</p></div>27          <InstallHint />28          {!data && <Spinner className="h-5 w-5" />}29          <AppearanceSection />30          {data && <ModelsSection routing={data.settings.routing ?? {}} onSave={save} />}31          {data && <SpendingSection value={data.settings.spending ?? { dailyLimitUsd: 10, monthlyLimitUsd: 150, alertAtPercent: 80 }} onSave={save} />}32          <Section id="memoire" title="Mémoire" hint="Les mêmes réglages que la page Mémoire. Les souvenirs restent visibles, modifiables et supprimables à tout moment."><MemoryToggles compact /><div className="text-[12px] text-muted">Gérer les souvenirs : <a href="/memory" className="text-accent hover:underline">Mémoire</a>.</div></Section>33          {data && <ToolsSection tools={data.tools} />}34          {data && <IntegrationsSection integrations={data.integrations} />}35          {data && <SecuritySection owner={data.owner} />}36          <Section id="apropos" title="À propos">37            <div className="space-y-1.5 text-[13px] text-fg-2">38              <div><span className="font-medium text-fg">Chat SPBoucher AI</span> · version 0.1.0</div>39              <div>Interface privée multi-fournisseurs (OpenAI, Anthropic ; MacLustr local à venir). Toutes les données — conversations, documents, mémoire, journaux d'outils — sont stockées localement sur le cluster MacLustr, jamais chez un tiers autre que les fournisseurs de modèles interrogés.</div>40              {data?.settings.registry?.refreshedAt && <div>Registre des modèles rafraîchi le {new Date(data.settings.registry.refreshedAt).toLocaleString("fr-CA")} ({data.settings.registry.live} identifiants listés).</div>}41              <div><a href="/models" className="text-accent hover:underline">Laboratoire de modèles et rapport de benchmark →</a></div>42            </div>43          </Section>44        </div>45      </div>46    </div>47  );48}49
Files · main
Menu

Theme