import React, { useEffect, useState } from "react"; import { apiFetch } from "../api"; import { useAuth } from "../state"; export function AdminUsersPage() { const { tokens, refresh, me } = useAuth(); const [users, setUsers] = useState([]); const [form, setForm] = useState({ email: "", password: "", role: "viewer" }); const [emailSettings, setEmailSettings] = useState({ enabled: false, smtp_host: "", smtp_port: 587, smtp_username: "", smtp_password: "", clear_smtp_password: false, from_email: "", use_starttls: true, use_ssl: false, alert_recipients: "", }); const [smtpState, setSmtpState] = useState({ has_password: false, updated_at: null }); const [testRecipient, setTestRecipient] = useState(""); const [smtpInfo, setSmtpInfo] = useState(""); const [error, setError] = useState(""); const load = async () => { const [userRows, smtp] = await Promise.all([ apiFetch("/admin/users", {}, tokens, refresh), apiFetch("/admin/settings/email", {}, tokens, refresh), ]); setUsers(userRows); setEmailSettings((prev) => ({ ...prev, enabled: !!smtp.enabled, smtp_host: smtp.smtp_host || "", smtp_port: smtp.smtp_port || 587, smtp_username: smtp.smtp_username || "", smtp_password: "", clear_smtp_password: false, from_email: smtp.from_email || "", use_starttls: !!smtp.use_starttls, use_ssl: !!smtp.use_ssl, alert_recipients: (smtp.alert_recipients || []).join(", "), })); setSmtpState({ has_password: !!smtp.has_password, updated_at: smtp.updated_at }); setTestRecipient(smtp.from_email || ""); }; useEffect(() => { if (me?.role === "admin") load().catch((e) => setError(String(e.message || e))); }, [me]); if (me?.role !== "admin") return
Admins only.
; const create = async (e) => { e.preventDefault(); try { await apiFetch("/admin/users", { method: "POST", body: JSON.stringify(form) }, tokens, refresh); setForm({ email: "", password: "", role: "viewer" }); await load(); } catch (e) { setError(String(e.message || e)); } }; const remove = async (id) => { try { await apiFetch(`/admin/users/${id}`, { method: "DELETE" }, tokens, refresh); await load(); } catch (e) { setError(String(e.message || e)); } }; const saveSmtp = async (e) => { e.preventDefault(); setError(""); setSmtpInfo(""); try { const recipients = emailSettings.alert_recipients .split(",") .map((item) => item.trim()) .filter(Boolean); const payload = { ...emailSettings, smtp_host: emailSettings.smtp_host.trim() || null, smtp_username: emailSettings.smtp_username.trim() || null, from_email: emailSettings.from_email.trim() || null, smtp_password: emailSettings.smtp_password || null, alert_recipients: recipients, }; await apiFetch("/admin/settings/email", { method: "PUT", body: JSON.stringify(payload) }, tokens, refresh); setSmtpInfo("SMTP settings saved."); await load(); } catch (e) { setError(String(e.message || e)); } }; const sendTestMail = async () => { setError(""); setSmtpInfo(""); try { const recipient = testRecipient.trim(); if (!recipient) { throw new Error("Please enter a test recipient email."); } await apiFetch( "/admin/settings/email/test", { method: "POST", body: JSON.stringify({ recipient }) }, tokens, refresh ); setSmtpInfo(`Test email sent to ${recipient}.`); } catch (e) { setError(String(e.message || e)); } }; return (

Admin Settings - Users

{error &&
{error}
}

User Management

setForm({ ...form, email: e.target.value })} /> setForm({ ...form, password: e.target.value })} />
{users.map((u) => ( ))}
ID Email Role Action
{u.id} {u.email} {u.role} {u.id !== me.id && ( )}

Alert Email Notifications (SMTP)

Configure outgoing SMTP for alert emails. This is send-only; no inbound mailbox is used.

{smtpInfo &&
{smtpInfo}
}
setEmailSettings({ ...emailSettings, smtp_host: e.target.value })} />
setEmailSettings({ ...emailSettings, smtp_port: Number(e.target.value || 587) })} />
setEmailSettings({ ...emailSettings, smtp_username: e.target.value })} />
setEmailSettings({ ...emailSettings, smtp_password: e.target.value, clear_smtp_password: false })} />
setEmailSettings({ ...emailSettings, from_email: e.target.value })} />
setEmailSettings({ ...emailSettings, alert_recipients: e.target.value })} />
setTestRecipient(e.target.value)} />
Last updated: {smtpState.updated_at ? new Date(smtpState.updated_at).toLocaleString() : "not configured yet"}
); }