/** * EcoOS Displays View * Card-based display management */ import { html, DeesElement, customElement, property, state, css, type TemplateResult, } from '@design.estate/dees-element'; import { DeesButton, DeesPanel, DeesBadge } from '@design.estate/dees-catalog'; import { sharedStyles } from '../styles/shared.js'; import type { IDisplayInfo } from '../../ts_interfaces/display.js'; @customElement('ecoos-displays') export class EcoosDisplays extends DeesElement { @property({ type: Array }) public accessor displays: IDisplayInfo[] = []; @state() private accessor loading: boolean = false; @state() private accessor message: string = ''; @state() private accessor messageError: boolean = false; public static styles = [ sharedStyles, css` :host { display: block; padding: 16px; } .display-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; } .display-card { opacity: 1; transition: opacity 0.2s ease; } .display-card.disabled { opacity: 0.5; } .display-meta { font-size: var(--text-xs); color: var(--text-tertiary); margin-bottom: 12px; } .badge-row { margin-bottom: 12px; } .actions-row { display: flex; gap: 8px; flex-wrap: wrap; } .message-bar { margin-top: 16px; padding: 8px 12px; border-radius: 6px; font-size: var(--text-sm); } .message-bar.success { background: hsla(142.1, 76.2%, 36.3%, 0.15); color: var(--success); } .message-bar.error { background: hsla(0, 84.2%, 60.2%, 0.15); color: var(--error); } .empty-state { text-align: center; padding: 32px; color: var(--text-tertiary); } .disabled-section { margin-top: 16px; } .disabled-header { font-size: var(--text-sm); color: var(--text-tertiary); margin-bottom: 12px; cursor: pointer; display: flex; align-items: center; gap: 6px; } .disabled-header::before { content: '▶'; font-size: 8px; transition: transform 150ms ease; } .disabled-header.open::before { transform: rotate(90deg); } `, ]; render(): TemplateResult { const enabledDisplays = this.displays.filter(d => d.active); const disabledDisplays = this.displays.filter(d => !d.active); if (this.displays.length === 0) { return html`
No displays detected
`; } return html`
${enabledDisplays.map(d => this.renderDisplayCard(d))}
${disabledDisplays.length > 0 ? html`
Disabled Displays (${disabledDisplays.length})
${disabledDisplays.map(d => this.renderDisplayCard(d))}
` : ''} ${this.message ? html`
${this.message}
` : ''} `; } private renderDisplayCard(display: IDisplayInfo): TemplateResult { return html` ${display.make && display.make !== 'Unknown' ? html`
${display.make}${display.model ? ` ${display.model}` : ''}
` : ''} ${display.isPrimary ? html`
Primary
` : ''}
${display.active && !display.isPrimary ? html` this.setPrimary(display.name)} > ` : ''} this.toggleDisplay(display.name, !display.active)} >
`; } private async toggleDisplay(name: string, enable: boolean): Promise { this.loading = true; this.message = ''; try { const action = enable ? 'enable' : 'disable'; const response = await fetch(`/api/displays/${encodeURIComponent(name)}/${action}`, { method: 'POST', }); const result = await response.json(); this.message = result.message; this.messageError = !result.success; this.dispatchEvent(new CustomEvent('refresh-displays')); } catch (error) { this.message = `Error: ${error}`; this.messageError = true; } finally { this.loading = false; } } private async setPrimary(name: string): Promise { this.loading = true; this.message = ''; try { const response = await fetch(`/api/displays/${encodeURIComponent(name)}/primary`, { method: 'POST', }); const result = await response.json(); this.message = result.message; this.messageError = !result.success; this.dispatchEvent(new CustomEvent('refresh-displays')); } catch (error) { this.message = `Error: ${error}`; this.messageError = true; } finally { this.loading = false; } } }