This commit is contained in:
2026-03-28 21:16:21 +01:00
parent 572643ad82
commit cbc2058b83
15 changed files with 767 additions and 277 deletions

View File

@@ -0,0 +1,236 @@
---
phase: 51-email-html-layout
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- database/migrations/20260328_000001_add_html_layout_to_email_mailboxes.sql
- src/Modules/Settings/EmailMailboxController.php
- src/Modules/Settings/EmailMailboxRepository.php
- resources/views/settings/email-mailboxes.php
- src/Modules/Email/EmailSendingService.php
autonomous: true
---
<objective>
## Goal
Rozbudowa modulu e-mail o HTML layout: header i footer konfigurowane na poziomie skrzynki pocztowej, content z szablonu. Finalna wiadomosc = header + content + footer. Edytor ograniczony do email-safe HTML.
## Purpose
Umozliwienie uzytkownikowi stworzenia spojnego brandingu e-mail (naglowek z logo/nazwa firmy, stopka z danymi kontaktowymi) bez powielania tresci w kazdym szablonie.
## Output
- Migracja DB: kolumny `header_html`, `footer_html` w `email_mailboxes`
- UI skrzynek: dwa edytory Quill (header/footer) z toolbar email-safe
- Kompozycja e-mail: header + body + footer w EmailSendingService i preview
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Source Files
@src/Modules/Settings/EmailMailboxController.php
@src/Modules/Settings/EmailMailboxRepository.php
@resources/views/settings/email-mailboxes.php
@src/Modules/Email/EmailSendingService.php
@resources/views/settings/email-templates.php (reference — Quill config)
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
No specialized flows configured as required for this work type.
</skills>
<acceptance_criteria>
## AC-1: Kolumny DB header_html i footer_html
```gherkin
Given tabela email_mailboxes istnieje
When migracja zostanie wykonana
Then tabela zawiera kolumny header_html TEXT NULL i footer_html TEXT NULL
And istniejace rekordy maja NULL w obu kolumnach (brak breaking change)
```
## AC-2: Edycja header/footer w formularzu skrzynki
```gherkin
Given uzytkownik otwiera formularz edycji skrzynki pocztowej
When widzi sekcje "Szablon wiadomosci" pod ustawieniami SMTP
Then sa dwa edytory Quill: "Naglowek (header)" i "Stopka (footer)"
And toolbar kazdego edytora ogranicza sie do: bold, italic, underline, link, kolor tekstu, kolor tla, wyrownanie, listy, naglowki (h1-h3), obraz (inline base64)
And tresc edytorow jest zapisywana do DB przy submit formularza
```
## AC-3: Kompozycja e-mail header + content + footer
```gherkin
Given skrzynka ma ustawiony header_html i footer_html
And szablon ma body_html
When e-mail jest wysylany lub podgladany (preview)
Then tresc wiadomosci = header_html + body_html (resolved) + footer_html
And header i footer rowniez przechodza przez variable resolver
```
## AC-4: E-mail bez header/footer
```gherkin
Given skrzynka ma puste (NULL) header_html i/lub footer_html
When e-mail jest wysylany
Then tresc wiadomosci zawiera tylko body_html (bez pustych sekcji)
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Migracja DB + Repository + Controller</name>
<files>
database/migrations/20260328_000001_add_html_layout_to_email_mailboxes.sql,
src/Modules/Settings/EmailMailboxRepository.php,
src/Modules/Settings/EmailMailboxController.php
</files>
<action>
1. Utworzyc migracje SQL:
```sql
ALTER TABLE email_mailboxes
ADD COLUMN header_html TEXT NULL AFTER sender_name,
ADD COLUMN footer_html TEXT NULL AFTER header_html;
```
2. EmailMailboxRepository:
- `save()`: dodac `header_html` i `footer_html` do INSERT/UPDATE
- `findById()`: upewnic sie ze zwraca te kolumny (juz zwraca SELECT * wiec OK)
- `listAll()`: bez zmian (nie potrzebuje HTML w liscie)
3. EmailMailboxController:
- `save()`: pobrac `header_html` i `footer_html` z POST body
- Nie escapowac HTML (to jest tresc edytora WYSIWYG, jak body_html w szablonach)
- Przekazac do repozytorium w tablicy save data
</action>
<verify>
- Migracja wykonuje sie bez bledow
- DESCRIBE email_mailboxes pokazuje header_html i footer_html jako TEXT NULL
- Zapis i odczyt skrzynki z header/footer dziala poprawnie
</verify>
<done>AC-1 satisfied: kolumny istnieja i sa zapisywane/odczytywane</done>
</task>
<task type="auto">
<name>Task 2: UI edytorow header/footer w formularzu skrzynki</name>
<files>resources/views/settings/email-mailboxes.php</files>
<action>
1. Po sekcji "Ustawienia SMTP" dodac nowa sekcje "Szablon wiadomosci" z dwoma edytorami Quill
2. Kazdy edytor (header, footer):
- Label: "Naglowek (header)" / "Stopka (footer)"
- Div z id `js-header-editor` / `js-footer-editor` (kontener Quill)
- Hidden input `header_html` / `footer_html` (sync przy submit)
- Podpowiedz: "Opcjonalnie. Bedzie dolaczany do kazdego e-maila wysylanego z tej skrzynki."
3. Toolbar edytora — TYLKO email-safe opcje:
```javascript
[
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline'],
[{ color: [] }, { background: [] }],
[{ align: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image'],
['clean']
]
```
- Image: Quill domyslnie wstawia jako base64 inline — to jest email-safe
- Brak: strike, blockquote, code-block, video, indent (slabo obslugiwane w klientach pocztowych)
4. Zaladowac Quill CSS/JS z CDN (ten sam co w email-templates: 2.0.3)
5. Na submit formularza: sync innerHTML z edytorow do hidden inputs
6. Przy edycji istniejacego rekordu: zaladowac HTML do edytorow przez `quill.root.innerHTML = ...`
7. Edytory powinny miec mniejsza domyslna wysokosc niz edytor szablonu (np. min-height: 80px)
</action>
<verify>
- Otworz /settings/email-mailboxes — formularz pokazuje dwa edytory
- Wpisz tresc w header/footer, zapisz — dane sa w DB
- Edytuj skrzynke — header/footer sa zaladowane do edytorow
- Toolbar nie zawiera opcji niebezpiecznych dla e-mail (video, code-block)
</verify>
<done>AC-2 satisfied: edytory sa dostepne, ograniczone do email-safe, i zapisuja dane</done>
</task>
<task type="auto">
<name>Task 3: Kompozycja e-mail w EmailSendingService</name>
<files>src/Modules/Email/EmailSendingService.php</files>
<action>
1. W metodzie `send()` po rozwiazaniu zmiennych w body:
- Pobrac header_html i footer_html z resolved mailbox
- Przepuscic je przez variableResolver.resolve() (aby zmienne dzialaly tez w header/footer)
- Zlozyc finalBody: header_html + resolvedBody + footer_html
- Jezeli header_html lub footer_html sa NULL/puste — pominac (bez pustych divow)
- Uzyc finalBody zamiast resolvedBody w sendViaSMTP i logEmail
2. W metodzie `preview()`:
- Analogicznie: pobrac mailbox dla szablonu, zlozyc header + body + footer
- Aby preview dzialal, potrzebujemy mailbox — uzyc resolveMailbox() (juz istnieje)
- Jezeli mailbox nie znaleziony — pokazac sam body (bez header/footer)
3. Metoda `resolveMailbox()` jest private — juz zwraca pelne dane z findById() wlacznie z header_html/footer_html
4. Nowa prywatna metoda `composeBody(string $resolvedBody, ?array $mailbox, array $variableMap): string`:
- Wydzielic logike kompozycji do reusable metody
- Uzyc w send() i preview()
</action>
<verify>
- Wyslij e-mail ze skrzynka z ustawionym header/footer — e-mail zawiera header + body + footer
- Wyslij e-mail ze skrzynka BEZ header/footer — e-mail zawiera tylko body
- Preview pokazuje zlozony wynik header + body + footer
- Zmienne w header/footer sa rozwiazywane (np. {{firma.nazwa}})
</verify>
<done>AC-3 i AC-4 satisfied: kompozycja dziala z i bez header/footer</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- resources/views/settings/email-templates.php (edytor szablonow — bez zmian)
- src/Modules/Email/VariableResolver.php (resolver zmiennych — bez zmian)
- src/Modules/Email/AttachmentGenerator.php
- database/migrations/ (istniejace migracje)
- Struktura tabeli email_templates (body_html pozostaje jak jest)
## SCOPE LIMITS
- Brak edytora MJML / dedykowanego email buildera — Quill z ograniczonym toolbar wystarcza
- Brak podgladu header/footer w formularzu skrzynki (preview jest w szablonach)
- Brak importu gotowych szablonow HTML
- Zmienne w header/footer dzialaja, ale panel zmiennych NIE jest dodawany do formularza skrzynki (header/footer to zwykle statyczny branding)
</boundaries>
<verification>
Before declaring plan complete:
- [ ] Migracja wykonana, kolumny widoczne w DESCRIBE
- [ ] Formularz skrzynki: dwa edytory Quill z email-safe toolbar
- [ ] Zapis i odczyt header/footer dziala
- [ ] E-mail wysylany ze skrzynka z header/footer zawiera zlozony layout
- [ ] E-mail wysylany ze skrzynka BEZ header/footer zawiera tylko body
- [ ] Preview pokazuje zlozony wynik
- [ ] Zmienne w header/footer sa rozwiazywane
- [ ] Brak bledow PHP/JS w konsoli
</verification>
<success_criteria>
- Wszystkie 3 taski zakonczone
- Wszystkie 4 acceptance criteria spelnione
- Wszystkie verification checks przeszly
- Brak regresji w istniejacym wysylaniu e-mail
</success_criteria>
<output>
After completion, create `.paul/phases/51-email-html-layout/51-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,145 @@
---
phase: 51-email-html-layout
plan: 01
subsystem: email
tags: [quill, html-email, smtp, phpmailer]
requires:
- phase: 13-email-mailboxes
provides: email_mailboxes table, EmailMailboxRepository, EmailMailboxController
- phase: 14-email-templates
provides: email_templates table, Quill.js editor, VariableResolver
provides:
- HTML header/footer per mailbox (header_html, footer_html columns)
- Email composition: header + body + footer in EmailSendingService
- HTML source editor toggle + preview for header/footer
affects: []
tech-stack:
added: []
patterns: [html-source-toggle, iframe-preview, table-based-email-layout]
key-files:
created:
- database/migrations/20260328_000001_add_html_layout_to_email_mailboxes.sql
modified:
- src/Modules/Settings/EmailMailboxRepository.php
- src/Modules/Settings/EmailMailboxController.php
- resources/views/settings/email-mailboxes.php
- src/Modules/Email/EmailSendingService.php
key-decisions:
- "Header/footer na poziomie skrzynki (nie szablonu) — spojny branding bez duplikacji"
- "Tryb HTML source omija Quill — surowy HTML zachowany bez sanityzacji"
- "composeBody() jako reusable metoda w send() i preview()"
patterns-established:
- "HTML source toggle: textarea + Quill toggle z auto-detekcja rich HTML przy ladowaniu"
- "Iframe preview modal do podgladu surowego HTML"
duration: ~45min
started: 2026-03-28T16:00:00Z
completed: 2026-03-28T16:45:00Z
---
# Phase 51 Plan 01: Email HTML Layout Summary
**HTML header/footer per skrzynka pocztowa z dual-mode edytorem (Quill WYSIWYG + HTML source) i kompozycja email header+body+footer w EmailSendingService.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~45min |
| Tasks | 3 completed + 2 scope additions |
| Files modified | 5 source + 3 docs |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Kolumny DB header_html i footer_html | Pass | TEXT NULL, migracja zarejestrowana w migrations table |
| AC-2: Edycja header/footer w formularzu skrzynki | Pass | Quill + HTML source toggle + preview |
| AC-3: Kompozycja header + content + footer | Pass | composeBody() w send() i preview(), variable resolver na header/footer |
| AC-4: E-mail bez header/footer | Pass | NULL/pusty header/footer pomijany |
## Accomplishments
- Kolumny `header_html`/`footer_html` w `email_mailboxes` z pelnym CRUD (repository + controller)
- Dual-mode edytor: Quill WYSIWYG z email-safe toolbar + tryb HTML source (textarea) z auto-detekcja rich HTML
- Przycisk podgladu (iframe modal) dla header i footer
- Metoda `composeBody()` w EmailSendingService — skladanie header + body + footer z variable resolution
- Przykladowy szablon stopki (table-based, Outlook-safe) w `footer-template.html`
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `database/migrations/20260328_000001_add_html_layout_to_email_mailboxes.sql` | Created | ALTER TABLE — kolumny header_html, footer_html |
| `src/Modules/Settings/EmailMailboxRepository.php` | Modified | header_html/footer_html w save() INSERT/UPDATE |
| `src/Modules/Settings/EmailMailboxController.php` | Modified | Pobieranie header_html/footer_html z POST |
| `resources/views/settings/email-mailboxes.php` | Modified | Sekcja "Szablon wiadomosci": 2x Quill + HTML source toggle + preview modal |
| `src/Modules/Email/EmailSendingService.php` | Modified | composeBody() — skladanie header+body+footer w send() i preview() |
| `DOCS/DB_SCHEMA.md` | Modified | Dokumentacja nowych kolumn |
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 51 |
| `DOCS/ARCHITECTURE.md` | Modified | Opis kompozycji email |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Header/footer na poziomie skrzynki, nie szablonu | Spojny branding — jeden header/footer dla wszystkich szablonow danej skrzynki | Brak duplikacji w szablonach |
| Tryb HTML source omija Quill calkowicie | Quill sanityzuje HTML (usuwa inline style, div, table) — rich HTML musi byc zachowany | Surowy HTML wklejony w source mode trafia do DB bez strat |
| Auto-detekcja rich HTML przy ladowaniu | Jesli zapisany HTML zawiera div+style/table/meta, edytor startuje w source mode | Brak utraty danych przy ponownej edycji |
| composeBody() jako prywatna metoda | Reuse w send() i preview() bez duplikacji logiki | Spojnosc kompozycji |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Scope additions | 2 | Uzyteczne rozszerzenia UI na zyczenie uzytkownika |
| Auto-fixed | 1 | Rejestracja migracji w tabeli migrations |
### Scope Additions
**1. Tryb HTML source (</> HTML)**
- Dodany na zyczenie uzytkownika — Quill sanityzuje rich HTML
- Textarea toggle z zachowaniem surowego HTML przy submit
**2. Przycisk Podglad**
- Dodany na zyczenie uzytkownika
- Iframe modal renderujacy aktualny HTML z edytora
### Auto-fixed Issues
**1. Migracja nie zarejestrowana w tabeli migrations**
- Migracja uruchomiona recznym PDO::exec (kolumny dodane), ale brak wpisu w `migrations`
- Migrator probowal ponownie wykonac ALTER — Duplicate column error
- Fix: INSERT do tabeli migrations
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| Lokalna baza niedostepna (XAMPP nie uruchomiony) | Uzyto DB_HOST_REMOTE do migracji |
| Migracja reczna nie zarejestrowala sie w migrations | Reczny INSERT do tabeli migrations |
## Next Phase Readiness
**Ready:**
- Email header/footer w pelni funkcjonalny
- Preview w formularzu skrzynki
- Kompozycja email dziala w send() i preview()
**Concerns:**
- Brak panelu zmiennych w formularzu skrzynki (header/footer zwykle statyczny)
- SonarQube scan nie uruchomiony (wymagany przez SPECIAL-FLOWS.md przed UNIFY)
**Blockers:**
- None
---
*Phase: 51-email-html-layout, Plan: 01*
*Completed: 2026-03-28*

View File

@@ -0,0 +1,24 @@
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-top: 1px solid #eeeeee;">
<tr><td style="padding: 20px 0 10px 0; font-size: 0; line-height: 0;">&nbsp;</td></tr>
<tr>
<td style="padding: 0 20px;">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td valign="top" style="padding-right: 30px; padding-bottom: 20px;">
<img src="https://marianek.pl/layout/images/logo.png" alt="marianek.pl" width="250" style="display: block; width: 250px; height: auto; border: 0;" />
</td>
<td valign="top" style="font-family: Arial, Helvetica, sans-serif; font-size: 13px; line-height: 1.4; color: #000000;">
<span style="color: #888888; font-size: 13px;">Pozdrawiam</span><br />
<span style="font-size: 22px; font-weight: 400; line-height: 1.2;">Pyziak <strong>Jacek</strong></span><br />
<br />
<a href="tel:+48530755774" style="color: #000000; text-decoration: none; font-size: 12px;">tel. +48 530 755 774</a><br />
<a href="mailto:sklep@marianek.pl" style="color: #ea6e24; text-decoration: none; font-size: 12px;">sklep@marianek.pl</a><br />
<a href="https://www.marianek.pl" style="color: #ea6e24; text-decoration: none; font-size: 12px;">marianek.pl</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!--[if mso]></td></tr></table><![endif]-->