import { html, nothing } from 'lit'; import { LightElement } from '../lib/base.js'; import { t, I18nMixin, LOCALES, setLocale, getLocale } from '../lib/i18n.js'; export class ProfilePage extends I18nMixin(LightElement) { static get properties() { return { _open: { state: true }, _me: { state: true }, _displayName: { state: true }, _savingName: { state: true }, _nameMsg: { state: true }, _locale: { state: true }, _localeMsg: { state: true }, _pwCurrent: { state: true }, _pwNew: { state: true }, _pwConfirm: { state: true }, _savingPw: { state: true }, _pwMsg: { state: true }, }; } constructor() { super(); this._open = false; this._me = null; this._displayName = ''; this._savingName = false; this._nameMsg = null; this._locale = ''; this._localeMsg = null; this._pwCurrent = ''; this._pwNew = ''; this._pwConfirm = ''; this._savingPw = false; this._pwMsg = null; } connectedCallback() { super.connectedCallback(); window.addEventListener('llm-page-change', (e) => { this._open = e.detail.page === 'profile'; this.style.display = this._open ? 'flex' : 'none'; if (this._open) this._load(); }); } async _load() { try { const res = await fetch('/api/auth/me'); if (res.ok) { this._me = await res.json(); this._displayName = this._me.display_name ?? ''; this._locale = this._me.locale ?? ''; } } catch { /* ignore */ } } async _saveName() { if (this._savingName) return; this._savingName = true; this._nameMsg = null; try { const res = await fetch('/api/auth/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ display_name: this._displayName.trim() || null }), }); if (!res.ok) throw new Error(await res.text()); this._nameMsg = { type: 'ok', text: t('profile.saved') }; await this._load(); } catch (e) { this._nameMsg = { type: 'err', text: e.message }; } finally { this._savingName = false; } } // '' → back to the instance default; otherwise a concrete locale id. async _changeLocale(value) { this._localeMsg = null; try { const res = await fetch('/api/auth/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ locale: value === '' ? null : value }), }); if (!res.ok) throw new Error(await res.text()); this._locale = value; // Apply immediately: the explicit choice, or the instance default when reset. setLocale(value === '' ? (this._me?.default_locale || 'en') : value); this._localeMsg = { type: 'ok', text: t('profile.saved') }; } catch (e) { this._localeMsg = { type: 'err', text: e.message }; } } async _savePassword() { if (this._savingPw) return; this._pwMsg = null; if (this._pwNew.length < 4) { this._pwMsg = { type: 'err', text: t('profile.pw.short') }; return; } if (this._pwNew !== this._pwConfirm) { this._pwMsg = { type: 'err', text: t('profile.pw.mismatch') }; return; } this._savingPw = true; try { const res = await fetch('/api/auth/change-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ current_password: this._pwCurrent || null, new_password: this._pwNew, }), }); if (!res.ok) throw new Error(await res.text()); this._pwMsg = { type: 'ok', text: t('profile.pw.changed') }; this._pwCurrent = ''; this._pwNew = ''; this._pwConfirm = ''; } catch (e) { this._pwMsg = { type: 'err', text: e.message }; } finally { this._savingPw = false; } } render() { if (!this._open) return nothing; const me = this._me; const defaultLabel = LOCALES.find(l => l.id === me?.default_locale)?.label ?? me?.default_locale ?? 'English'; return html`