import { html, nothing } from 'lit'; import { LightElement } from '../lib/base.js'; const ADMIN_ID = 'admin'; export class RolesPage extends LightElement { static get properties() { return { _open: { state: true }, _roles: { state: true }, _groups: { state: true }, _error: { state: true }, _modal: { state: true }, // null | { mode: 'create'|'edit', role?, form } }; } constructor() { super(); this._open = false; this._roles = null; this._groups = null; this._error = null; this._modal = null; } connectedCallback() { super.connectedCallback(); window.addEventListener('llm-page-change', (e) => { this._open = e.detail.page === 'roles'; this.style.display = this._open ? 'flex' : 'none'; if (this._open) this._load(); }); } async _load() { this._error = null; try { const [rRes, gRes] = await Promise.all([ fetch('/api/roles'), fetch('/api/tool-permission-groups'), ]); if (!rRes.ok) throw new Error(`Roles: HTTP ${rRes.status}`); if (!gRes.ok) throw new Error(`Groups: HTTP ${gRes.status}`); this._roles = await rRes.json(); this._groups = await gRes.json(); } catch (e) { this._error = e.message; } } // ── Modal helpers ──────────────────────────────────────────────────────────── _openCreate() { this._modal = { mode: 'create', form: { id: '', label: '', permission_group: this._groups?.[0]?.id ?? 'default', attrs: '' }, }; } _openEdit(role) { this._modal = { mode: 'edit', role, form: { label: role.label, permission_group: role.permission_group, attrs: role.attrs ?? '' }, }; } _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.id.trim() || !form.label.trim()) { this._error = 'ID and label are required.'; return; } try { const res = await fetch('/api/roles', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: form.id.trim(), label: form.label.trim(), permission_group: form.permission_group, attrs: form.attrs.trim() || null, }), }); if (!res.ok) throw new Error(await res.text()); this._closeModal(); await this._load(); } catch (e) { this._error = e.message; } } else { const { role } = this._modal; if (!form.label.trim()) { this._error = 'Label is required.'; return; } try { const res = await fetch(`/api/roles/${role.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ label: form.label.trim(), permission_group: form.permission_group, attrs: form.attrs.trim() || null, }), }); if (!res.ok) throw new Error(await res.text()); this._closeModal(); await this._load(); } catch (e) { this._error = e.message; } } } async _delete(role) { if (!confirm(`Delete role "${role.label}"?`)) return; try { const res = await fetch(`/api/roles/${role.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(await res.text()); await this._load(); } catch (e) { this._error = e.message; } } // ── Render ────────────────────────────────────────────────────────────────── _groupLabel(groupId) { return this._groups?.find(g => g.id === groupId)?.name ?? groupId; } _renderModal() { if (!this._modal) return nothing; const { mode, form, role } = this._modal; const title = mode === 'create' ? 'New role' : `Edit ${role.label}`; return html`
`; } render() { if (!this._open) return nothing; const roles = this._roles ?? []; const loading = this._roles === null; return html`No roles.
| ID | Label | Permission group | |
|---|---|---|---|
${r.id} |
${r.label} | ${this._groupLabel(r.permission_group)} |
|