From 3ac32d9f3e6abbd60f641dff13d76d9ef8ff8e30 Mon Sep 17 00:00:00 2001 From: Till JS Date: Mon, 20 Apr 2026 20:48:14 +0200 Subject: [PATCH] feat(broadcast): M3 email HTML render + desktop/mobile/text preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 3 of the compose wizard (Preflight) now shows a real preview + pre-send checks. User sees exactly what the recipient gets, minus the substituted tracking URLs. Render (render/email-html.ts) - renderEmailHtml(): wraps Tiptap HTML in an email-client-compatible shell — inline style="" throughout, single-cell table layout, preheader block (hidden from body, visible in inbox preview), sender banner, footer with unsubscribe + legal address - HTML-escapes subject / sender name / address (Tiptap body stays verbatim since its schema already forbids + +
+
+ + + + {viewport === 'mobile' ? 'Mobile' : 'Desktop'} +
+ +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/broadcast/preview/PreviewTabs.svelte b/apps/mana/apps/web/src/lib/modules/broadcast/preview/PreviewTabs.svelte new file mode 100644 index 000000000..467763f07 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/broadcast/preview/PreviewTabs.svelte @@ -0,0 +1,160 @@ + + + +
+
+ + + +
+ + {#if tab === 'desktop'} + + {:else if tab === 'mobile'} + + {:else} +
+
{plainText}
+

+ Der Text wird als text/plain zusätzlich zur HTML-Version verschickt — wichtig für + Spam-Filter und Clients, die kein HTML anzeigen. +

+
+ {/if} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/broadcast/render/email-html.test.ts b/apps/mana/apps/web/src/lib/modules/broadcast/render/email-html.test.ts new file mode 100644 index 000000000..2b16487e8 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/broadcast/render/email-html.test.ts @@ -0,0 +1,134 @@ +import { describe, it, expect } from 'vitest'; +import { renderEmailHtml } from './email-html'; + +const campaign = { + subject: 'Hallo Welt', + preheader: 'Ein kurzer Vorschautext', + fromName: 'Till', + fromEmail: 'till@example.ch', +}; + +const settings = { + defaultFooter: 'Weitere Infos auf mana.how', + legalAddress: 'Till AG\nBahnhofstr. 1\n8000 Zürich', +}; + +describe('renderEmailHtml', () => { + it('produces a full HTML document', () => { + const html = renderEmailHtml({ + tiptapHtml: '

Hallo

', + campaign, + settings, + }); + expect(html.toLowerCase()).toContain(''); + expect(html).toContain(''); + }); + + it('includes the subject as the document title', () => { + const html = renderEmailHtml({ + tiptapHtml: '

body

', + campaign, + settings, + }); + expect(html).toContain('Hallo Welt'); + }); + + it('HTML-escapes the subject to prevent injection', () => { + const html = renderEmailHtml({ + tiptapHtml: '

body

', + campaign: { ...campaign, subject: 'Alert ' }, + settings, + }); + expect(html).not.toContain('