import { html, nothing } from 'lit'; import { LightElement } from '../lib/base.js'; export class UsersPage extends LightElement { static get properties() { return { _open: { state: true }, _users: { state: true }, _roles: { state: true }, _error: { state: true }, _modal: { state: true }, // null | { mode: 'create'|'edit'|'password', user?, form } }; } constructor() { super(); this._open = false; this._users = null; this._roles = null; this._error = null; this._modal = null; } connectedCallback() { super.connectedCallback(); window.addEventListener('llm-page-change', (e) => { this._open = e.detail.page === 'users'; this.style.display = this._open ? 'flex' : 'none'; if (this._open) this._load(); }); } async _load() { this._error = null; try { const [uRes, rRes] = await Promise.all([ fetch('/api/users'), fetch('/api/roles'), ]); if (!uRes.ok) throw new Error(`Users: HTTP ${uRes.status}`); if (!rRes.ok) throw new Error(`Roles: HTTP ${rRes.status}`); this._users = await uRes.json(); this._roles = await rRes.json(); } catch (e) { this._error = e.message; } } // ── Modal helpers ──────────────────────────────────────────────────────────── _openCreate() { this._modal = { mode: 'create', form: { username: '', display_name: '', role_id: this._roles?.[0]?.id ?? '', password: '', encrypted: false }, }; } _openEdit(user) { this._modal = { mode: 'edit', user, form: { username: user.username, display_name: user.display_name ?? '', role_id: user.role_id, active: user.active }, }; } _openPassword(user) { this._modal = { mode: 'password', user, form: { password: '' } }; } _closeModal() { this._modal = null; this._error = null; } _patch(field, value) { this._modal = { ...this._modal, form: { ...this._modal.form, [field]: value } }; } // ── API actions ────────────────────────────────────────────────────────────── async _save() { const { mode, form } = this._modal; this._error = null; if (mode === 'create') { if (!form.username.trim() || !form.password) { this._error = 'Username and password are required.'; return; } try { const res = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: form.username.trim(), display_name: form.display_name.trim() || null, role_id: form.role_id, password: form.password, encrypted: form.encrypted, }), }); if (!res.ok) throw new Error(await res.text()); this._closeModal(); await this._load(); } catch (e) { this._error = e.message; } } else if (mode === 'edit') { const { user } = this._modal; if (!form.username.trim()) { this._error = 'Username is required.'; return; } try { const res = await fetch(`/api/users/${user.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: form.username.trim(), display_name: form.display_name.trim() || null, role_id: form.role_id, active: form.active, }), }); if (!res.ok) throw new Error(await res.text()); this._closeModal(); await this._load(); } catch (e) { this._error = e.message; } } else if (mode === 'password') { const { user } = this._modal; if (!form.password) { this._error = 'Password must not be empty.'; return; } try { const res = await fetch(`/api/users/${user.id}/password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: form.password }), }); if (!res.ok) throw new Error(await res.text()); this._closeModal(); } catch (e) { this._error = e.message; } } } async _delete(user) { if (!confirm(`Delete user "${user.username}"? This permanently erases their database and all conversation history.`)) return; try { const res = await fetch(`/api/users/${user.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(await res.text()); await this._load(); } catch (e) { this._error = e.message; } } // ── Render ────────────────────────────────────────────────────────────────── _roleLabel(roleId) { return this._roles?.find(r => r.id === roleId)?.label ?? roleId; } _renderModal() { if (!this._modal) return nothing; const { mode, form, user } = this._modal; const title = mode === 'create' ? 'New user' : mode === 'edit' ? `Edit ${user.username}` : `Reset password — ${user.username}`; return html`
`; } render() { if (!this._open) return nothing; const users = this._users ?? []; const loading = this._users === null; return html`No users.
| Username | Display name | Role | DB | Status | |
|---|---|---|---|---|---|
| ${u.username} | ${u.display_name ?? '—'} | ${this._roleLabel(u.role_id)} | ${u.encrypted ? html`Encrypted` : html`Cleartext`} | ${u.active ? html`Active` : html`Inactive`} |
|