import { DeesElement, customElement, html, css, cssManager, property, type TemplateResult, } from '@design.estate/dees-element'; import type { ISgUser, ISgSession } from '../interfaces.js'; declare global { interface HTMLElementTagNameMap { 'sg-settings-view': SgSettingsView; } } @customElement('sg-settings-view') export class SgSettingsView extends DeesElement { public static demo = () => html`
`; public static demoGroups = ['Auth']; @property({ type: Object }) public accessor user: ISgUser = { id: '', email: '', username: '', displayName: '', isSystemAdmin: false, }; @property({ type: Array }) public accessor sessions: ISgSession[] = []; public static styles = [ cssManager.defaultStyles, css` :host { display: block; color: ${cssManager.bdTheme('#111', '#fff')}; } .container { display: flex; flex-direction: column; gap: 32px; } .page-title { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; } /* Section */ .section { display: flex; flex-direction: column; gap: 16px; } .section-box { background: ${cssManager.bdTheme('#fff', '#111')}; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; padding: 24px; display: flex; flex-direction: column; gap: 16px; } .section-title { font-size: 16px; font-weight: 600; } .section-subtitle { font-size: 13px; color: ${cssManager.bdTheme('#888', '#777')}; margin-top: -8px; } /* Form elements */ .form-group { display: flex; flex-direction: column; gap: 6px; } .form-label { font-size: 13px; font-weight: 600; color: ${cssManager.bdTheme('#111', '#ddd')}; text-transform: uppercase; letter-spacing: 0.04em; } .form-input { padding: 10px 12px; background: ${cssManager.bdTheme('#fff', '#0a0a0a')}; border: 1px solid ${cssManager.bdTheme('#ddd', '#333')}; font-size: 14px; color: ${cssManager.bdTheme('#111', '#fff')}; outline: none; font-family: inherit; max-width: 400px; } .form-input:focus { border-color: ${cssManager.bdTheme('#111', '#fff')}; } .form-input:disabled { opacity: 0.5; cursor: not-allowed; } .form-hint { font-size: 12px; color: ${cssManager.bdTheme('#aaa', '#666')}; } .save-btn { align-self: flex-start; padding: 8px 20px; background: ${cssManager.bdTheme('#111', '#fff')}; border: none; font-size: 13px; font-weight: 600; color: ${cssManager.bdTheme('#fff', '#111')}; cursor: pointer; transition: opacity 150ms ease; } .save-btn:hover { opacity: 0.85; } /* Admin badge */ .admin-badge { display: inline-flex; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; padding: 2px 8px; background: rgba(59, 130, 246, 0.15); color: #3b82f6; } /* Sessions */ .session-list { display: flex; flex-direction: column; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .session-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${cssManager.bdTheme('#fff', '#111')}; border-bottom: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .session-row:last-child { border-bottom: none; } .session-info { display: flex; flex-direction: column; gap: 4px; min-width: 0; } .session-agent { font-size: 13px; color: ${cssManager.bdTheme('#111', '#fff')}; font-family: 'JetBrains Mono', monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 500px; } .session-meta { display: flex; gap: 12px; font-size: 12px; color: ${cssManager.bdTheme('#888', '#777')}; } .session-status { font-size: 11px; font-weight: 600; text-transform: uppercase; padding: 1px 6px; } .session-status.active { background: rgba(34, 197, 94, 0.15); color: #22c55e; } .session-status.expired { background: rgba(239, 68, 68, 0.15); color: #ef4444; } .session-actions { flex-shrink: 0; } .revoke-session-btn { padding: 4px 12px; background: transparent; border: 1px solid ${cssManager.bdTheme('#ddd', '#333')}; font-size: 12px; color: ${cssManager.bdTheme('#666', '#999')}; cursor: pointer; transition: all 150ms ease; } .revoke-session-btn:hover { border-color: #ef4444; color: #ef4444; } /* Danger zone */ .danger-section { border-color: rgba(239, 68, 68, 0.3); } .danger-title { color: #ef4444; } .danger-text { font-size: 13px; color: ${cssManager.bdTheme('#666', '#aaa')}; line-height: 1.5; } .danger-btn { align-self: flex-start; padding: 8px 16px; background: transparent; border: 1px solid #ef4444; font-size: 13px; font-weight: 600; color: #ef4444; cursor: pointer; transition: all 150ms ease; } .danger-btn:hover { background: #ef4444; color: #fff; } `, ]; public render(): TemplateResult { return html`
Settings
Profile ${this.user.isSystemAdmin ? html`Admin` : ''}
Email cannot be changed
Username cannot be changed
Change Password
Active Sessions
${this.sessions.length > 0 ? html`
${this.sessions.map( (session) => html`
${this.parseUserAgent(session.userAgent || 'Unknown client')}
${session.ipAddress || 'unknown'} Active ${this.formatDate(session.lastActivityAt || '')} ${session.isValid ? 'Active' : 'Expired'}
${session.isValid ? html` ` : ''}
` )}
` : html`
No active sessions
`}
Danger Zone
Permanently delete your account and all associated data. This action cannot be undone. All your tokens will be revoked and organization memberships removed.
`; } private parseUserAgent(ua: string): string { if (!ua) return 'Unknown client'; if (ua.length > 80) return ua.substring(0, 77) + '...'; return ua; } private formatDate(dateStr: string): string { if (!dateStr) return ''; try { return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } catch { return dateStr; } } private handleSaveProfile() { const displayName = (this.shadowRoot?.getElementById('settings-displayname') as HTMLInputElement)?.value || ''; const avatarUrl = (this.shadowRoot?.getElementById('settings-avatar') as HTMLInputElement)?.value || ''; this.dispatchEvent( new CustomEvent('save-profile', { detail: { displayName, avatarUrl }, bubbles: true, composed: true, }) ); } private handleChangePassword() { const currentPassword = (this.shadowRoot?.getElementById('settings-current-pw') as HTMLInputElement)?.value || ''; const newPassword = (this.shadowRoot?.getElementById('settings-new-pw') as HTMLInputElement)?.value || ''; const confirmPassword = (this.shadowRoot?.getElementById('settings-confirm-pw') as HTMLInputElement)?.value || ''; if (!currentPassword || !newPassword) return; if (newPassword !== confirmPassword) return; this.dispatchEvent( new CustomEvent('change-password', { detail: { currentPassword, newPassword }, bubbles: true, composed: true, }) ); } private handleRevokeSession(sessionId: string) { this.dispatchEvent( new CustomEvent('revoke-session', { detail: { sessionId }, bubbles: true, composed: true, }) ); } private handleDeleteAccount() { const password = (this.shadowRoot?.getElementById('settings-delete-pw') as HTMLInputElement)?.value || ''; if (!password) return; this.dispatchEvent( new CustomEvent('delete-account', { detail: { password }, bubbles: true, composed: true, }) ); } }