import { html, nothing } from 'lit'; import { LightElement } from '../lib/base.js'; export class ConfigPage extends LightElement { static properties = { _open: { state: true }, _properties: { state: true }, _values: { state: true }, // { [key]: string } _saving: { state: true }, // Set _saved: { state: true }, // Set (brief flash) _error: { state: true }, }; constructor() { super(); this._open = false; this._properties = []; this._values = {}; this._saving = new Set(); this._saved = new Set(); this._error = null; } connectedCallback() { super.connectedCallback(); window.addEventListener('llm-page-change', (e) => { this._open = e.detail.page === 'config'; this.style.display = this._open ? 'flex' : 'none'; if (this._open) this._load(); }); } async _load() { this._error = null; try { const res = await fetch('/api/config'); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); this._properties = data.sets ?? []; const vals = {}; for (const s of this._properties) for (const p of s.properties) vals[p.key] = p.value ?? ''; this._values = vals; } catch (e) { this._error = e.message; } } _setValue(key, val) { this._values = { ...this._values, [key]: val }; } async _save(prop) { const key = prop.key; const value = this._values[key] ?? ''; this._saving = new Set([...this._saving, key]); this.requestUpdate(); try { const res = await fetch(`/api/config/${encodeURIComponent(key)}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value }), }); if (!res.ok) throw new Error(await res.text()); this._saved = new Set([...this._saved, key]); setTimeout(() => { this._saved = new Set([...this._saved].filter(k => k !== key)); }, 1500); } catch (e) { alert(`Error saving ${prop.name}: ${e.message}`); } finally { this._saving = new Set([...this._saving].filter(k => k !== key)); } } _renderInput(prop) { const val = this._values[prop.key] ?? ''; if (prop.property_type === 'bool') { const effective = val !== '' ? val : (prop.default_value ?? 'true'); const checked = effective !== 'false'; return html` { this._setValue(prop.key, e.target.checked ? 'true' : 'false'); this._save(prop); }} /> ${checked ? 'Enabled' : 'Disabled'} `; } if (prop.property_type === 'int') { return html` this._setValue(prop.key, e.target.value)} />`; } if (prop.property_type === 'security_group') { const groups = prop.options ?? []; return html` this._setValue(prop.key, e.target.value)}> — default — ${groups.map(g => html` ${g.name}`)} `; } return html` this._setValue(prop.key, e.target.value)} />`; } _renderSet(set) { return html` ${set.name} ${set.description} ${set.properties.map(p => this._renderRow(p))} `; } _renderRow(prop) { const saving = this._saving.has(prop.key); const saved = this._saved.has(prop.key); return html` ${prop.name} ${prop.description} ${this._renderInput(prop)} ${prop.property_type !== 'bool' ? html` this._save(prop)}> ${saving ? html`` : saved ? 'Saved' : 'Save'} ` : nothing} `; } render() { return html` Config ${this._error ? html` ${this._error}` : nothing} ${this._properties.length === 0 && !this._error ? html` Loading…` : nothing} ${this._properties.map(s => this._renderSet(s))} `; } }
Loading…