From 8038e0230341f0708c28ea5eca866e0f4727b15b Mon Sep 17 00:00:00 2001 From: Anais Saraiva Date: Mon, 21 Sep 2026 16:37:40 +0200 Subject: [PATCH] . --- another_prompt | 5 + frontend/src/components/AdminCenter.tsx | 194 ++++++++++++++++++++---- frontend/src/components/dialogs.tsx | 60 ++++---- frontend/src/lib/api.ts | 7 + src/index.ts | 36 +++++ 5 files changed, 247 insertions(+), 55 deletions(-) diff --git a/another_prompt b/another_prompt index a28778a..acaed36 100644 --- a/another_prompt +++ b/another_prompt @@ -53,3 +53,8 @@ L'administrateur lui, qand il demande les modèles dams le ollama, lui, force un --- ADMIN CENTER: SMTP CONNECTIVITY. Make a SMTP tester in the admin center, and also a viewer of the smtp credentials (default bluring the smtp pasword OBVIOUSLY) And also a SMTP tester , sending a mail to a provided one , to tets the SMTP connectivity + + +--- ARCHIVED FEATURES: +a way to archive conversaitons with the contetxual menue. +When conversations are archived. it's impossible to write in . only read-only. also, when conversations are archived, they are not anymore visible in the conversations side list. They are still unarchivable and checkable, in a new sub-category options into the settigns modal : "Archiveds Chats" diff --git a/frontend/src/components/AdminCenter.tsx b/frontend/src/components/AdminCenter.tsx index c7b07cc..bf2aee0 100644 --- a/frontend/src/components/AdminCenter.tsx +++ b/frontend/src/components/AdminCenter.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from "react"; -import { ArrowLeft, Inbox, LayoutDashboard, Mail, ShieldAlert, SlidersHorizontal, Users } from "lucide-react"; +import { ArrowLeft, Eye, EyeOff, Inbox, LayoutDashboard, Mail, Send, ShieldAlert, SlidersHorizontal, Users } from "lucide-react"; import { api } from "../lib/api.ts"; import { AdminPanel } from "./AdminPanel.tsx"; import { AccessRequestsPanel } from "./AccessRequests.tsx"; -import { FlowerMark, LoadingScreen, Spinner, Switch } from "./ui.tsx"; +import { Button, CopyButton, FlowerMark, Input, LoadingScreen, Spinner, Switch } from "./ui.tsx"; import { cn } from "../lib/cn.ts"; type Tab = "accounts" | "access" | "features" | "mail"; @@ -26,7 +26,6 @@ export function AdminCenter() { const [allowed, setAllowed] = useState(null); const [tab, setTab] = useState("accounts"); const [settings, setSettings] = useState<{ registrationEnabled: boolean; accessRequestEnabled: boolean; ocrEnabled: boolean } | null>(null); - const [mail, setMail] = useState<{ recovery: boolean; from?: string } | null>(null); const [error, setError] = useState(""); const [saving, setSaving] = useState(false); @@ -40,7 +39,6 @@ export function AdminCenter() { setAllowed(true); applyTheme(r.user.theme); api.adminGetSettings().then((s) => setSettings(s.settings)).catch((e) => setError(e instanceof Error ? e.message : "Load failed")); - api.methods().then((m) => setMail(m)).catch(() => {}); }) .catch(() => setAllowed(false)); }, []); @@ -164,32 +162,170 @@ export function AdminCenter() { )} - {tab === "mail" && ( -
-
- - - -
-

Key recovery emails

-

- {mail == null ? "Checking…" : mail.recovery ? `Enabled — sent from ${mail.from ?? "the configured sender"}` : "Disabled — set SMTP_HOST on the server to enable"} -

-
- - {mail?.recovery ? "On" : "Off"} - -
-

- SMTP credentials live in the server environment (see docs/OPERATIONS.md), never in this UI. - Users set their recovery address in profile settings or at account creation. -

-
- )} + {tab === "mail" && } ); } + +type SmtpInfo = { + enabled: boolean; + host: string; + port: number; + secure: boolean; + user: string; + pass: string; + hasPass: boolean; + from: string; + appUrl: string; +}; + +function MailPanel() { + const [smtp, setSmtp] = useState(null); + const [error, setError] = useState(""); + const [showPass, setShowPass] = useState(false); + const [to, setTo] = useState(""); + const [verifying, setVerifying] = useState(false); + const [sending, setSending] = useState(false); + const [result, setResult] = useState<{ ok: boolean; text: string } | null>(null); + + useEffect(() => { + api.adminMailStatus().then((r) => setSmtp(r.smtp)).catch((e) => setError(e instanceof Error ? e.message : "Load failed")); + }, []); + + async function verify() { + setVerifying(true); + setResult(null); + try { + await api.adminMailVerify(); + setResult({ ok: true, text: "Connection OK — SMTP host reachable, auth accepted." }); + } catch (e) { + setResult({ ok: false, text: e instanceof Error ? e.message : "Verification failed" }); + } finally { + setVerifying(false); + } + } + + async function sendTest() { + setSending(true); + setResult(null); + try { + const r = await api.adminMailTest(to.trim()); + setResult({ ok: true, text: `Test mail sent to ${to.trim()}${r.messageId ? ` (${r.messageId})` : ""}.` }); + } catch (e) { + setResult({ ok: false, text: e instanceof Error ? e.message : "Send failed" }); + } finally { + setSending(false); + } + } + + function Row({ label, children }: { label: string; children: React.ReactNode }) { + return ( +
+ {label} + {children} +
+ ); + } + + return ( +
+
+
+ + + +
+

Key recovery emails

+

+ {smtp == null ? "Checking…" : smtp.enabled ? `Enabled — sent from ${smtp.from}` : "Disabled — set SMTP_HOST on the server to enable"} +

+
+ + {smtp?.enabled ? "On" : "Off"} + +
+ {error &&

{error}

} + {!smtp && !error &&

Loading…

} + {smtp && ( +
+ {smtp.host || (not set)} + {smtp.port} · {smtp.secure ? "implicit TLS (465)" : "STARTTLS (587)"} + {smtp.user || (no auth)} + + + {!smtp.hasPass ? ( + (not set) + ) : showPass ? ( + {smtp.pass} + ) : ( + •••••••••• + )} + {smtp.hasPass && ( + <> + + {showPass && } + + )} + + + {smtp.from} + {smtp.appUrl} +
+ )} +

+ Credentials live in the server environment (see docs/OPERATIONS.md), read-only here. + Users set their recovery address in profile settings or at account creation. +

+
+ +
+

Connectivity test

+

Verify the connection, then send a real test mail to an address you control.

+
+ +
+
+ setTo(e.target.value)} + className="flex-1" + /> + +
+ {result && ( +

+ {result.text} +

+ )} +
+
+ ); +} diff --git a/frontend/src/components/dialogs.tsx b/frontend/src/components/dialogs.tsx index 489e1c4..2eec233 100644 --- a/frontend/src/components/dialogs.tsx +++ b/frontend/src/components/dialogs.tsx @@ -517,8 +517,8 @@ export function SettingsModal({ {uploading ? : } - {uploading ? "Uploading…" : dragOver ? "Drop it!" : "Drop an image or click to upload"} - jpg · png · webp — max 5MB — 5 changes per 2h + {uploading ? t("settings.uploading") : dragOver ? t("settings.dropActive") : t("settings.dropIdle")} + {t("settings.avatarHint")} {uploadError &&

{uploadError}

} - + setDisplayName(e.target.value)} maxLength={60} required /> - + setEmail(e.target.value)} placeholder="you@example.com" inputMode="email" /> @@ -545,26 +545,29 @@ export function SettingsModal({ {activeCat === "appearance" && ( - + + + + )} {activeCat === "features" && ( -

Turn composer capabilities on or off. Applied instantly, saved on this device.

+

{t("settings.featHint")}

)} @@ -578,13 +581,13 @@ export function SettingsModal({ {search.trim() && visibleCats.length === 0 && ( -

No settings match “{search.trim()}”.

+

{t("settings.noMatch", { q: search.trim() })}

)} {error &&

{error}

}
- - + +
@@ -606,6 +609,7 @@ function SettingsPane({ children }: { children: React.ReactNode }) { /* ---------- Accent color (settings) ---------- */ function AccentSection() { + const { t } = useT(); const [state, setState] = useState(() => currentAccent()); function pick(presetId: string) { @@ -622,13 +626,15 @@ function AccentSection() { return (
+ {t("settings.accent")} +

{t("settings.accentHint")}

{ACCENT_PRESETS.map((p) => (