import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element'; import * as appstate from '../appstate.js'; import * as shared from './shared/index.js'; declare global { interface HTMLElementTagNameMap { 'ops-view-emails': OpsViewEmails; } } interface IEmail { id: string; from: string; to: string[]; cc?: string[]; bcc?: string[]; subject: string; body: string; html?: string; attachments?: Array<{ filename: string; size: number; contentType: string; }>; date: number; read: boolean; folder: 'inbox' | 'sent' | 'draft' | 'trash'; flags?: string[]; messageId?: string; inReplyTo?: string; } @customElement('ops-view-emails') export class OpsViewEmails extends DeesElement { @state() private selectedFolder: 'inbox' | 'sent' | 'draft' | 'trash' = 'inbox'; @state() private emails: IEmail[] = []; @state() private selectedEmail: IEmail | null = null; @state() private showCompose = false; @state() private isLoading = false; @state() private searchTerm = ''; constructor() { super(); this.loadEmails(); } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css` :host { display: block; height: 100%; } .emailLayout { display: flex; gap: 16px; height: 100%; min-height: 600px; } .sidebar { flex-shrink: 0; width: 280px; } .mainArea { flex: 1; display: flex; flex-direction: column; gap: 16px; overflow: hidden; } .emailToolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .searchBox { flex: 1; min-width: 200px; max-width: 400px; } .emailList { flex: 1; overflow: hidden; } .emailPreview { flex: 1; display: flex; flex-direction: column; background: ${cssManager.bdTheme('#fff', '#222')}; border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')}; border-radius: 8px; overflow: hidden; } .emailHeader { padding: 24px; border-bottom: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')}; } .emailSubject { font-size: 24px; font-weight: 600; margin-bottom: 16px; color: ${cssManager.bdTheme('#333', '#ccc')}; } .emailMeta { display: flex; flex-direction: column; gap: 8px; font-size: 14px; color: ${cssManager.bdTheme('#666', '#999')}; } .emailMetaRow { display: flex; gap: 8px; } .emailMetaLabel { font-weight: 600; min-width: 60px; } .emailBody { flex: 1; padding: 24px; overflow-y: auto; font-size: 15px; line-height: 1.6; } .emailActions { display: flex; gap: 8px; padding: 16px 24px; border-top: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')}; background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')}; } .emptyState { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: ${cssManager.bdTheme('#999', '#666')}; } .emptyIcon { font-size: 64px; margin-bottom: 16px; opacity: 0.3; } .emptyText { font-size: 18px; } .email-read { color: ${cssManager.bdTheme('#999', '#666')}; } .email-unread { color: ${cssManager.bdTheme('#1976d2', '#4a90e2')}; } .attachment-icon { color: ${cssManager.bdTheme('#666', '#999')}; } `, ]; public render() { if (this.selectedEmail) { return html` Emails
${this.renderEmailPreview()}
`; } return html` Emails
this.openComposeModal()} type="highlighted"> Compose this.searchTerm = (e.target as any).value} > this.refreshEmails()}> ${this.isLoading ? html`` : html``} Refresh this.markAllAsRead()}> Mark all read
this.selectFolder('inbox')} .type=${this.selectedFolder === 'inbox' ? 'highlighted' : 'normal'} > Inbox ${this.getEmailCount('inbox') > 0 ? `(${this.getEmailCount('inbox')})` : ''} this.selectFolder('sent')} .type=${this.selectedFolder === 'sent' ? 'highlighted' : 'normal'} > Sent this.selectFolder('draft')} .type=${this.selectedFolder === 'draft' ? 'highlighted' : 'normal'} > Drafts ${this.getEmailCount('draft') > 0 ? `(${this.getEmailCount('draft')})` : ''} this.selectFolder('trash')} .type=${this.selectedFolder === 'trash' ? 'highlighted' : 'normal'} > Trash
${this.renderEmailList()} `; } private renderEmailList() { const filteredEmails = this.getFilteredEmails(); if (filteredEmails.length === 0) { return html`
No emails in ${this.selectedFolder}
`; } return html` ({ 'Status': html``, From: email.from, Subject: html`${email.subject}`, Date: this.formatDate(email.date), 'Attach': html` ${email.attachments?.length ? html`` : ''} `, })} .dataActions=${[ { name: 'Read', iconName: 'eye', type: ['doubleClick', 'inRow'], actionFunc: async (actionData) => { this.selectedEmail = actionData.item; if (!actionData.item.read) { this.markAsRead(actionData.item.id); } } }, { name: 'Reply', iconName: 'reply', type: ['contextmenu'], actionFunc: async (actionData) => { this.replyToEmail(actionData.item); } }, { name: 'Forward', iconName: 'share', type: ['contextmenu'], actionFunc: async (actionData) => { this.forwardEmail(actionData.item); } }, { name: 'Delete', iconName: 'trash', type: ['contextmenu'], actionFunc: async (actionData) => { this.deleteEmail(actionData.item.id); } } ]} .selectionMode=${'single'} heading1=${this.selectedFolder.charAt(0).toUpperCase() + this.selectedFolder.slice(1)} heading2=${`${filteredEmails.length} emails`} > `; } private renderEmailPreview() { if (!this.selectedEmail) return ''; return html`
${this.selectedEmail.subject}
From: ${this.selectedEmail.from}
To: ${this.selectedEmail.to.join(', ')}
${this.selectedEmail.cc?.length ? html`
CC: ${this.selectedEmail.cc.join(', ')}
` : ''}
Date: ${new Date(this.selectedEmail.date).toLocaleString()}
${this.selectedEmail.html ? html`
` : html`
${this.selectedEmail.body}
` }
this.replyToEmail(this.selectedEmail!)}> Reply this.replyAllToEmail(this.selectedEmail!)}> Reply All this.forwardEmail(this.selectedEmail!)}> Forward this.deleteEmail(this.selectedEmail!.id)} type="danger"> Delete
`; } private async openComposeModal(replyTo?: IEmail, replyAll = false, forward = false) { const { DeesModal } = await import('@design.estate/dees-catalog'); await DeesModal.createAndShow({ heading: forward ? 'Forward Email' : replyTo ? 'Reply to Email' : 'New Email', content: html`
{ await this.sendEmail(e.detail); // Close modal after sending const modals = document.querySelectorAll('dees-modal'); modals.forEach(m => (m as any).destroy?.()); }}> a.indexOf(v) === i) : [replyTo.from]) : []} required >
`, menuOptions: [ { name: 'Send', iconName: 'paperPlane', action: async (modalArg) => { const form = modalArg.shadowRoot?.querySelector('dees-form') as any; form?.submit(); } }, { name: 'Cancel', iconName: 'xmark', action: async (modalArg) => await modalArg.destroy() } ] }); } private getFilteredEmails(): IEmail[] { let emails = this.emails.filter(e => e.folder === this.selectedFolder); if (this.searchTerm) { const search = this.searchTerm.toLowerCase(); emails = emails.filter(e => e.subject.toLowerCase().includes(search) || e.from.toLowerCase().includes(search) || e.body.toLowerCase().includes(search) ); } return emails.sort((a, b) => b.date - a.date); } private getEmailCount(folder: string): number { return this.emails.filter(e => e.folder === folder && !e.read).length; } private selectFolder(folder: 'inbox' | 'sent' | 'draft' | 'trash') { this.selectedFolder = folder; this.selectedEmail = null; } private formatDate(timestamp: number): string { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); const hours = diff / (1000 * 60 * 60); if (hours < 24) { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } else if (hours < 168) { // 7 days return date.toLocaleDateString([], { weekday: 'short' }); } else { return date.toLocaleDateString([], { month: 'short', day: 'numeric' }); } } private async loadEmails() { // TODO: Load real emails from server // For now, generate mock data this.generateMockEmails(); } private async refreshEmails() { this.isLoading = true; await this.loadEmails(); this.isLoading = false; } private async sendEmail(formData: any) { try { // TODO: Implement actual email sending via API console.log('Sending email:', formData); // Add to sent folder (mock) const newEmail: IEmail = { id: `email-${Date.now()}`, from: 'me@dcrouter.local', to: formData.to || [], cc: formData.cc || [], bcc: formData.bcc || [], subject: formData.subject, body: formData.body, date: Date.now(), read: true, folder: 'sent', }; this.emails = [...this.emails, newEmail]; // Show success notification console.log('Email sent successfully'); // TODO: Show toast notification when interface is available } catch (error: any) { console.error('Failed to send email', error); // TODO: Show error toast notification when interface is available } } private async markAsRead(emailId: string) { const email = this.emails.find(e => e.id === emailId); if (email) { email.read = true; this.emails = [...this.emails]; } } private async markAllAsRead() { this.emails = this.emails.map(e => e.folder === this.selectedFolder ? { ...e, read: true } : e ); } private async deleteEmail(emailId: string) { const email = this.emails.find(e => e.id === emailId); if (email) { if (email.folder === 'trash') { // Permanently delete this.emails = this.emails.filter(e => e.id !== emailId); } else { // Move to trash email.folder = 'trash'; this.emails = [...this.emails]; } if (this.selectedEmail?.id === emailId) { this.selectedEmail = null; } } } private async replyToEmail(email: IEmail) { this.openComposeModal(email, false, false); } private async replyAllToEmail(email: IEmail) { this.openComposeModal(email, true, false); } private async forwardEmail(email: IEmail) { this.openComposeModal(email, false, true); } private generateMockEmails() { const subjects = [ 'Server Alert: High CPU Usage', 'Daily Report - Network Activity', 'Security Update Required', 'New User Registration', 'Backup Completed Successfully', 'DNS Query Spike Detected', 'SSL Certificate Renewal Notice', 'Monthly Usage Summary', ]; const senders = [ 'monitoring@dcrouter.local', 'alerts@system.local', 'admin@company.com', 'noreply@service.com', 'support@vendor.com', ]; const bodies = [ 'This is an automated alert regarding your server status.', 'Please review the attached report for detailed information.', 'Action required: Update your security settings.', 'Your daily summary is ready for review.', 'All systems are operating normally.', ]; this.emails = Array.from({ length: 50 }, (_, i) => ({ id: `email-${i}`, from: senders[Math.floor(Math.random() * senders.length)], to: ['admin@dcrouter.local'], subject: subjects[Math.floor(Math.random() * subjects.length)], body: bodies[Math.floor(Math.random() * bodies.length)], date: Date.now() - (i * 3600000), // 1 hour apart read: Math.random() > 0.3, folder: i < 40 ? 'inbox' : i < 45 ? 'sent' : 'trash', attachments: Math.random() > 0.8 ? [{ filename: 'report.pdf', size: 1024 * 1024, contentType: 'application/pdf', }] : undefined, })); } }