From 2ab2e30336edb7c5f163e282da616d82674f6790 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 17 Jun 2025 14:37:05 +0000 Subject: [PATCH] fix: update dependencies and improve email view layout in OpsViewEmails component --- package.json | 6 +- pnpm-lock.yaml | 70 ++-- ts_web/elements/ops-view-emails.ts | 512 ++++++++++++++--------------- 3 files changed, 304 insertions(+), 284 deletions(-) diff --git a/package.json b/package.json index 4bf2c2e..9837b1e 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@api.global/typedserver": "^3.0.74", "@api.global/typedsocket": "^3.0.0", "@apiclient.xyz/cloudflare": "^6.4.1", - "@design.estate/dees-catalog": "^1.8.4", + "@design.estate/dees-catalog": "^1.8.8", "@design.estate/dees-element": "^2.0.42", "@push.rocks/projectinfo": "^5.0.1", "@push.rocks/qenv": "^6.1.0", @@ -41,11 +41,11 @@ "@push.rocks/smartjwt": "^2.2.1", "@push.rocks/smartlog": "^3.1.8", "@push.rocks/smartmail": "^2.1.0", - "@push.rocks/smartmetrics": "^2.0.9", + "@push.rocks/smartmetrics": "^2.0.10", "@push.rocks/smartnetwork": "^4.0.2", "@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpromise": "^4.0.3", - "@push.rocks/smartproxy": "^19.6.2", + "@push.rocks/smartproxy": "^19.6.6", "@push.rocks/smartrequest": "^2.1.0", "@push.rocks/smartrule": "^2.0.1", "@push.rocks/smartrx": "^3.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec7f032..97fb512 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^6.4.1 version: 6.4.1 '@design.estate/dees-catalog': - specifier: ^1.8.4 - version: 1.8.4 + specifier: ^1.8.8 + version: 1.8.8 '@design.estate/dees-element': specifier: ^2.0.42 version: 2.0.42 @@ -60,8 +60,8 @@ importers: specifier: ^2.1.0 version: 2.1.0 '@push.rocks/smartmetrics': - specifier: ^2.0.9 - version: 2.0.9 + specifier: ^2.0.10 + version: 2.0.10 '@push.rocks/smartnetwork': specifier: ^4.0.2 version: 4.0.2 @@ -72,8 +72,8 @@ importers: specifier: ^4.0.3 version: 4.2.3 '@push.rocks/smartproxy': - specifier: ^19.6.2 - version: 19.6.2(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4) + specifier: ^19.6.6 + version: 19.6.6(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4) '@push.rocks/smartrequest': specifier: ^2.1.0 version: 2.1.0 @@ -344,8 +344,8 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - '@design.estate/dees-catalog@1.8.4': - resolution: {integrity: sha512-G5N9CUzhWhTt3NuHbix4yyB/Fz1fqO2gTO1OoBffu5DTA1keJlOCvyBaUbgwjmZWnBBMN1O6r8cUhYIHcf4SJg==} + '@design.estate/dees-catalog@1.8.8': + resolution: {integrity: sha512-so0Jju95vZGsNUKi/ze1bHoK1LOgkCuU+xeiWDMfUpGkJA8OE9wPJmy5hSyIecc352JtlhOkgmTh/XxFTGpkQg==} '@design.estate/dees-comms@1.0.27': resolution: {integrity: sha512-GvzTUwkV442LD60T08iqSoqvhA02Mou5lFvvqBPc4yBUiU7cZISqBx+76xvMgMIEI9Dx9JfTl4/2nW8MoVAanw==} @@ -356,8 +356,8 @@ packages: '@design.estate/dees-element@2.0.42': resolution: {integrity: sha512-1PzHP6q/PtSiu4P0nCxjSeHtRHn62zoSouMy8JFW2h29FT/CSDVaTUAUqYqnvwE/U98aLNivWTmerZitDF7kBQ==} - '@design.estate/dees-wcctools@1.0.90': - resolution: {integrity: sha512-EHYWHiOe+P261e9fBbOBmkD7lIsOpD+tu4VZQr20oc8vhsFjeUGJqYeBm/Ghwg+Gck/dto+K9zyJNIyQ642cEw==} + '@design.estate/dees-wcctools@1.0.98': + resolution: {integrity: sha512-6EolTGBiXgF1wgr+KOeSXAIKpXqU95FU4vOJYPPEvb+e3ebFXCuL/B4UTFZYG3e1KuTZgxiaJ04L8ejm5HfTZA==} '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} @@ -713,6 +713,14 @@ packages: resolution: {integrity: sha512-mfOoUlIw8VBiJYPrl5RZfMzkXC/z7gbSpi2ecycrj/gRWLq2CMV+Q+0G+JPjeOmuNFgg0skEIzkVFzVYFP6URw==} engines: {node: '>=18.0.0'} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1017,8 +1025,8 @@ packages: '@push.rocks/smartmatch@2.0.0': resolution: {integrity: sha512-MBzP++1yNIBeox71X6VxpIgZ8m4bXnJpZJ4nWVH6IWpmO38MXTu4X0QF8tQnyT4LFcwvc9iiWaD15cstHa7Mmw==} - '@push.rocks/smartmetrics@2.0.9': - resolution: {integrity: sha512-3rKHItgIL4Pt/kFH12qCmTxsnB2EcBgO6H0MYe9dPjDI7yl7IDiLzZg2Abp4q9LEPVw009ATJsxklPQSNqI5gQ==} + '@push.rocks/smartmetrics@2.0.10': + resolution: {integrity: sha512-Fr4ZzJWFqTR67ThmPsj+uUfPoWZ+p87n7wItpXVjlQ2mf4pboWnsfmrrC1gqIXca1Dwa2i7L+GPoJ3hOZ+Qhfw==} '@push.rocks/smartmime@1.0.6': resolution: {integrity: sha512-PHd+I4UcsnOATNg8wjDsSAmmJ4CwQFrQCNzd0HSJMs4ZpiK3Ya91almd6GLpDPU370U4HFh4FaPF4eEAI6vkJQ==} @@ -1062,8 +1070,8 @@ packages: '@push.rocks/smartpromise@4.2.3': resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} - '@push.rocks/smartproxy@19.6.2': - resolution: {integrity: sha512-k7kmVqJLnCrm435U/NLyWWP0sJ5O61zHRk1RtPZbtrfMvLGO06b0Rjt5xh/UgdceKtvhol0kheibdfOL7vgOuw==} + '@push.rocks/smartproxy@19.6.6': + resolution: {integrity: sha512-AweTvBYlYubelO+g6Bf/4cg8RXb0fcMgYE1UKAT/m5PNbOuRWzTtXkja4JuFWfIdvmbfZxiWAaw9OhJvHIgIrw==} '@push.rocks/smartpuppeteer@2.0.5': resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} @@ -3090,8 +3098,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide@0.514.0: - resolution: {integrity: sha512-GQ3Rzj1qFANBvTmhe8RM3vR491RGnSjHsivA5wSuEj5taLwH1Sv4N7n6ae3ENGsYRzdrkVXZo4ieUbD1WOXmoA==} + lucide@0.515.0: + resolution: {integrity: sha512-n7tFK3R1HdsxwM5rMOa6twAi/4RGZCysc7dmk7Cr2GiOUcnHX0+MX9xw3xynpbekXCgS4+o9HeIWhBl3rZHIAA==} mailauth@4.8.6: resolution: {integrity: sha512-Ler6XMLCrXyCf3kmNOMA/1aUJN6let/w9HBtjl+2KzXOUxKIl4WPJM1FwqC4IHdVJO8kmHUrvyFIKIiEGj6mvg==} @@ -3312,6 +3320,10 @@ packages: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -5075,11 +5087,11 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@design.estate/dees-catalog@1.8.4': + '@design.estate/dees-catalog@1.8.8': dependencies: '@design.estate/dees-domtools': 2.3.2 '@design.estate/dees-element': 2.0.42 - '@design.estate/dees-wcctools': 1.0.90 + '@design.estate/dees-wcctools': 1.0.98 '@fortawesome/fontawesome-svg-core': 6.7.2 '@fortawesome/free-brands-svg-icons': 6.7.2 '@fortawesome/free-regular-svg-icons': 6.7.2 @@ -5092,7 +5104,7 @@ snapshots: apexcharts: 4.7.0 highlight.js: 11.11.1 ibantools: 4.5.1 - lucide: 0.514.0 + lucide: 0.515.0 monaco-editor: 0.52.2 pdfjs-dist: 4.10.38 xterm: 5.3.0 @@ -5148,7 +5160,7 @@ snapshots: - supports-color - vue - '@design.estate/dees-wcctools@1.0.90': + '@design.estate/dees-wcctools@1.0.98': dependencies: '@design.estate/dees-domtools': 2.3.2 '@design.estate/dees-element': 2.0.42 @@ -5454,6 +5466,12 @@ snapshots: dependencies: happy-dom: 15.11.7 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -6092,7 +6110,7 @@ snapshots: dependencies: matcher: 5.0.0 - '@push.rocks/smartmetrics@2.0.9': + '@push.rocks/smartmetrics@2.0.10': dependencies: '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartlog': 3.1.8 @@ -6221,7 +6239,7 @@ snapshots: '@push.rocks/smartpromise@4.2.3': {} - '@push.rocks/smartproxy@19.6.2(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)': + '@push.rocks/smartproxy@19.6.6(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)': dependencies: '@push.rocks/lik': 6.2.2 '@push.rocks/smartacme': 8.0.0(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4) @@ -6238,7 +6256,7 @@ snapshots: '@tsclass/tsclass': 9.2.0 '@types/minimatch': 5.1.2 '@types/ws': 8.18.1 - minimatch: 10.0.1 + minimatch: 10.0.3 pretty-ms: 9.2.0 ws: 8.18.2 transitivePeerDependencies: @@ -8776,7 +8794,7 @@ snapshots: lru-cache@7.18.3: {} - lucide@0.514.0: {} + lucide@0.515.0: {} mailauth@4.8.6: dependencies: @@ -9189,6 +9207,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 diff --git a/ts_web/elements/ops-view-emails.ts b/ts_web/elements/ops-view-emails.ts index 3d1b286..64e1513 100644 --- a/ts_web/elements/ops-view-emails.ts +++ b/ts_web/elements/ops-view-emails.ts @@ -94,15 +94,19 @@ export class OpsViewEmails extends DeesElement { .searchBox { flex: 1; + min-width: 200px; max-width: 400px; } .emailList { flex: 1; - overflow-y: auto; + overflow: hidden; } .emailPreview { + flex: 1; + display: flex; + flex-direction: column; background: white; border: 1px solid #e9ecef; border-radius: 8px; @@ -110,89 +114,51 @@ export class OpsViewEmails extends DeesElement { } .emailHeader { - padding: 20px; + padding: 24px; border-bottom: 1px solid #e9ecef; - background: #fafafa; } .emailSubject { - font-size: 20px; + font-size: 24px; font-weight: 600; - margin-bottom: 12px; + margin-bottom: 16px; + color: #333; } .emailMeta { display: flex; - gap: 16px; + flex-direction: column; + gap: 8px; font-size: 14px; color: #666; } + .emailMetaRow { + display: flex; + gap: 8px; + } + + .emailMetaLabel { + font-weight: 600; + min-width: 60px; + } + .emailBody { - padding: 20px; - max-height: 500px; + flex: 1; + padding: 24px; overflow-y: auto; + font-size: 15px; + line-height: 1.6; } .emailActions { display: flex; gap: 8px; - padding: 16px; + padding: 16px 24px; border-top: 1px solid #e9ecef; background: #fafafa; } - .composeModal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - } - - .composeContent { - background: white; - border-radius: 8px; - width: 90%; - max-width: 800px; - max-height: 90vh; - overflow: hidden; - display: flex; - flex-direction: column; - } - - .composeHeader { - padding: 20px; - border-bottom: 1px solid #e9ecef; - display: flex; - align-items: center; - justify-content: space-between; - } - - .composeTitle { - font-size: 20px; - font-weight: 600; - } - - .composeForm { - flex: 1; - overflow-y: auto; - padding: 20px; - } - - .composeActions { - padding: 20px; - border-top: 1px solid #e9ecef; - display: flex; - gap: 12px; - justify-content: flex-end; - } - .emptyState { display: flex; flex-direction: column; @@ -220,63 +186,77 @@ export class OpsViewEmails extends DeesElement {
- + + this.selectFolder('inbox')} + .iconName=${'inbox'} + .label=${'Inbox'} + .badgeText=${this.getEmailCount('inbox') > 0 ? String(this.getEmailCount('inbox')) : ''} + > + this.selectFolder('sent')} + .iconName=${'paperPlane'} + .label=${'Sent'} + .badgeText=${this.getEmailCount('sent') > 0 ? String(this.getEmailCount('sent')) : ''} + > + this.selectFolder('draft')} + .iconName=${'file'} + .label=${'Drafts'} + .badgeText=${this.getEmailCount('draft') > 0 ? String(this.getEmailCount('draft')) : ''} + > + this.selectFolder('trash')} + .iconName=${'trash'} + .label=${'Trash'} + .badgeText=${this.getEmailCount('trash') > 0 ? String(this.getEmailCount('trash')) : ''} + > + +
- -
- this.searchTerm = (e.target as any).value} - > - - - - this.refreshEmails()}> - ${this.isLoading ? html`` : html``} - - - this.markAllAsRead()}> - - Mark all read - -
- - - ${this.selectedEmail ? this.renderEmailPreview() : this.renderEmailList()} + ${this.selectedEmail ? this.renderEmailPreview() : this.renderEmailListView()}
- - - ${this.showCompose ? this.renderComposeModal() : ''} `; } - private renderFolderItem(folder: string, icon: string, label: string, count: number) { + private renderEmailListView() { return html` -
this.selectFolder(folder as any)} - > - - ${label} - ${count > 0 ? html`${count}` : ''} + +
+ this.searchTerm = (e.target as any).value} + > + + + + this.refreshEmails()}> + ${this.isLoading ? html`` : html``} + + + this.markAllAsRead()}> + + Mark all read +
+ + + ${this.renderEmailList()} `; } @@ -293,58 +273,58 @@ export class OpsViewEmails extends DeesElement { } 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); + ({ + '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); } } - ]} - .selectionMode=${'single'} - > -
+ }, + { + 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`} + > `; } @@ -356,114 +336,136 @@ export class OpsViewEmails extends DeesElement {
${this.selectedEmail.subject}
- From: ${this.selectedEmail.from} - To: ${this.selectedEmail.to.join(', ')} - Date: ${this.formatDate(this.selectedEmail.date)} +
+ 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}
` + html`
${this.selectedEmail.body}
` }
- this.selectedEmail = null}> - - Back - - this.replyToEmail(this.selectedEmail!)}> - - Reply - - this.replyAllToEmail(this.selectedEmail!)}> - - Reply All - - this.forwardEmail(this.selectedEmail!)}> - - Forward - - this.deleteEmail(this.selectedEmail!.id)} type="danger"> - - Delete + this.selectedEmail = null} type="secondary"> + + Back to List +
+ 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 renderComposeModal() { - return html` -
{ - if (e.target === e.currentTarget) this.showCompose = false; - }}> -
-
-
New Email
- this.showCompose = false} type="ghost"> - - -
- -
- this.sendEmail(e.detail)}> - - - - - - - - - - - - -
- this.showCompose = false} type="secondary"> - Cancel - - - - Send Email - -
-
-
+ 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[] { @@ -518,16 +520,15 @@ export class OpsViewEmails extends DeesElement { } private async sendEmail(formData: any) { - try { - // TODO: Implement actual email sending + // TODO: Implement actual email sending via API console.log('Sending email:', formData); - // Add to sent folder + // Add to sent folder (mock) const newEmail: IEmail = { id: `email-${Date.now()}`, from: 'me@dcrouter.local', - to: formData.to, + to: formData.to || [], cc: formData.cc || [], bcc: formData.bcc || [], subject: formData.subject, @@ -538,13 +539,13 @@ export class OpsViewEmails extends DeesElement { }; this.emails = [...this.emails, newEmail]; - this.showCompose = false; - // TODO: Implement toast notification when DeesToast.show is available + // Show success notification console.log('Email sent successfully'); + // TODO: Show toast notification when interface is available } catch (error: any) { - // TODO: Implement toast notification when DeesToast.show is available console.error('Failed to send email', error); + // TODO: Show error toast notification when interface is available } } @@ -581,18 +582,15 @@ export class OpsViewEmails extends DeesElement { } private async replyToEmail(email: IEmail) { - // TODO: Open compose with reply context - this.showCompose = true; + this.openComposeModal(email, false, false); } private async replyAllToEmail(email: IEmail) { - // TODO: Open compose with reply all context - this.showCompose = true; + this.openComposeModal(email, true, false); } private async forwardEmail(email: IEmail) { - // TODO: Open compose with forward context - this.showCompose = true; + this.openComposeModal(email, false, true); } private generateMockEmails() {