GrapesJS-mjml proved broken on our content (plugin v1.0.8 incompatible
with MJML v5 — canvas stays empty on load). Pivot to easy-email, a
mature OSS WYSIWYG email editor (MJML-based, MIT license, 4k stars).
Architecture: standalone React+Vite microservice deployed at
editor.gigafibre.ca, iframed from the ops UI's
/campaigns/templates/:name page. Talks to the hub's existing REST
endpoints (/campaigns/templates/*) for load + save. The hub stays the
source of truth — easy-email is purely the editing UI.
Scaffold delivered in this commit (Phase 1):
- services/email-editor/ — new top-level service directory
- package.json: React 18 + easy-email-{core,editor,extensions} 4.16.x
+ Vite 5 + TypeScript 5
- vite.config.ts: standard dev/build config, port 5173 in dev
- tsconfig.json: strict-false to keep iteration fast
- index.html: loads easy-email CSS bundles from unpkg (extensions, editor,
arco theme)
- src/main.tsx: React entry, mounts EmailEditorApp on #root
- src/EmailEditorApp.tsx:
• Reads template name from ?name=... URL param (defaults gift-email-fr)
• GET ${VITE_HUB_URL}/campaigns/templates/:name on mount
• Renders <EmailEditorProvider> + <StandardLayout> with our merge tags
map (firstname, amount, gift_url, description, expiry, etc.) so the
Variables panel shows our Mustache placeholders
• On save: JsonToMjml() converts easy-email's JSON → MJML, PUT to hub
→ hub compiles to HTML and persists both files
• postMessage({type: 'email-editor:saved', ...}) to parent window so
the iframing ops UI knows to refresh
- Dockerfile: multi-stage (Vite build → nginx alpine serve). SPA fallback
in nginx config so all routes return index.html.
- docker-compose.yml: container behind Traefik at editor.gigafibre.ca
with Let's Encrypt TLS via the shared proxy network.
- README.md documents the arch, URL params, postMessage protocol, dev
workflow, and the Phase 1 limitation (no MJML→JSON importer — editor
starts from empty page until Phase 3).
- .gitignore: standard node/vite/dist exclusions.
Build verified locally: 83 modules transformed, ~2.8 MB bundle (840 KB
gzipped) — large but acceptable since easy-email packages the full
email builder + drag-drop canvas.
Phase 2 (next): Docker deploy on prod + replace GrapesJS in the ops UI
TemplateEditorPage with an iframe pointing here.
Phase 3 (later): MJML → easy-email JSON parser so existing templates
auto-import into the canvas instead of starting blank.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
64 lines
1.8 KiB
Markdown
64 lines
1.8 KiB
Markdown
# TARGO Email Editor
|
|
|
|
Standalone email template editor microservice — React + Vite + [easy-email](https://github.com/zalify/easy-email)
|
|
(OSS WYSIWYG email builder, MJML-based).
|
|
|
|
Embedded as an iframe in the ops UI's `/campaigns/templates/:name` page.
|
|
Talks to the hub's `/campaigns/templates/*` REST endpoints for load/save.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
ops UI (Vue) → iframe → editor.gigafibre.ca → REST → msg.gigafibre.ca (hub)
|
|
└─ writes .mjml + .html
|
|
to /opt/targo-hub/templates/
|
|
```
|
|
|
|
## URL params
|
|
|
|
- `?name=gift-email-fr` — which template to load (defaults to gift-email-fr)
|
|
|
|
## postMessage protocol (to parent window)
|
|
|
|
Emitted on save success:
|
|
```js
|
|
{ type: 'email-editor:saved', template: 'gift-email-fr', ts: 1700000000000 }
|
|
```
|
|
|
|
The parent ops UI listens via:
|
|
```js
|
|
window.addEventListener('message', (e) => {
|
|
if (e.data.type === 'email-editor:saved') {
|
|
// refresh preview / show toast
|
|
}
|
|
})
|
|
```
|
|
|
|
## Local dev
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev # http://localhost:5173?name=gift-email-fr
|
|
```
|
|
|
|
Set `VITE_HUB_URL` env to point at a non-prod hub if needed.
|
|
|
|
## Build + deploy
|
|
|
|
```bash
|
|
docker compose build
|
|
docker compose up -d
|
|
```
|
|
|
|
Traefik auto-provisions HTTPS at `editor.gigafibre.ca` (Let's Encrypt via
|
|
the `letsencrypt` resolver shared with the rest of our stack).
|
|
|
|
## Known limitations (Phase 1)
|
|
|
|
- Existing MJML templates from the hub are NOT auto-imported into the
|
|
easy-email JSON tree (no MJML → JSON parser in easy-email out of the box).
|
|
The editor starts from an empty page. User rebuilds visually with the
|
|
hub's compiled HTML as visual reference.
|
|
- TODO Phase 3: integrate an MJML → easy-email-JSON parser (likely fork or
|
|
reverse-engineer JsonToMjml).
|