wip(14-email-templates): CRUD szablonów e-mail z Quill.js + system zmiennych
APPLY in progress — checkpoint human-verify awaiting re-test po namespace fixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
319
.paul/phases/14-email-templates/14-01-PLAN.md
Normal file
319
.paul/phases/14-email-templates/14-01-PLAN.md
Normal file
@@ -0,0 +1,319 @@
|
||||
---
|
||||
phase: 14-email-templates
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["13-01"]
|
||||
files_modified:
|
||||
- src/Modules/Settings/EmailTemplateController.php
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- resources/views/settings/email-templates.php
|
||||
- resources/views/settings/email-template-form.php
|
||||
- routes/web.php
|
||||
- resources/views/layouts/app.php
|
||||
- resources/scss/app.scss
|
||||
- public/assets/css/app.css
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
CRUD szablonów wiadomości e-mail z edytorem rich-text (Quill.js) i systemem zmiennych do personalizacji treści (np. {{order.number}}, {{buyer.name}}).
|
||||
|
||||
## Purpose
|
||||
Użytkownik musi mieć możliwość tworzenia i zarządzania szablonami e-mail, które w fazie 15 będą używane do wysyłki wiadomości z poziomu zamówień. Szablony muszą obsługiwać zmienne dynamiczne rozwiązywane z danych zamówienia.
|
||||
|
||||
## Output
|
||||
- EmailTemplateController + EmailTemplateRepository (CRUD)
|
||||
- Widok listy szablonów + formularz edycji z Quill.js
|
||||
- System zmiennych: definicja dostępnych zmiennych, wstawianie z UI, podgląd
|
||||
- Link w sidebarze Settings
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/13-email-mailboxes/13-01-SUMMARY.md
|
||||
- email_templates table already created (Phase 13)
|
||||
- EmailMailboxRepository::listActive() ready for mailbox select
|
||||
- Pattern: EmailMailboxController CRUD (same structure to follow)
|
||||
|
||||
## Source Files
|
||||
@database/migrations/20260315_000055_create_email_templates_table.sql
|
||||
@src/Modules/Settings/EmailMailboxController.php
|
||||
@src/Modules/Settings/EmailMailboxRepository.php
|
||||
@resources/views/settings/email-mailboxes.php
|
||||
@routes/web.php
|
||||
@resources/views/layouts/app.php
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] sonar-scanner uruchomiony po APPLY
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Lista szablonów
|
||||
```gherkin
|
||||
Given użytkownik jest zalogowany i przechodzi do Ustawienia > Szablony e-mail
|
||||
When strona się ładuje
|
||||
Then widzi tabelę z kolumnami: Nazwa, Temat, Skrzynka, Status, Akcje
|
||||
And każdy szablon ma przyciski: Edytuj, Włącz/Wyłącz, Usuń
|
||||
```
|
||||
|
||||
## AC-2: Tworzenie i edycja szablonu
|
||||
```gherkin
|
||||
Given użytkownik jest na stronie szablonów e-mail
|
||||
When wypełnia formularz (nazwa, temat, skrzynka, treść HTML) i zapisuje
|
||||
Then szablon jest zapisany w bazie z poprawnym body_html z Quill.js
|
||||
And po edycji istniejącego szablonu treść Quill jest załadowana poprawnie
|
||||
```
|
||||
|
||||
## AC-3: Edytor Quill.js z toolbarem
|
||||
```gherkin
|
||||
Given użytkownik edytuje treść szablonu
|
||||
When korzysta z edytora
|
||||
Then dostępne są: bold, italic, underline, listy, linki, nagłówki, kolory, wyrównanie
|
||||
And treść jest zapisywana jako HTML w polu body_html
|
||||
```
|
||||
|
||||
## AC-4: System zmiennych — wstawianie
|
||||
```gherkin
|
||||
Given użytkownik edytuje szablon
|
||||
When klika przycisk "Wstaw zmienną" i wybiera zmienną z listy (np. {{zamowienie.numer}})
|
||||
Then zmienna jest wstawiana do treści edytora w miejscu kursora
|
||||
And zmienne są wizualnie wyróżnione w edytorze (np. badge/tag)
|
||||
```
|
||||
|
||||
## AC-5: System zmiennych — definicja
|
||||
```gherkin
|
||||
Given system zmiennych jest zaimplementowany
|
||||
When użytkownik otwiera listę zmiennych
|
||||
Then widzi pogrupowane zmienne:
|
||||
- Zamówienie: numer, źródło, kwota, waluta, data
|
||||
- Kupujący: imię i nazwisko, email, telefon, login
|
||||
- Adres dostawy: ulica, miasto, kod pocztowy, kraj
|
||||
- Firma: nazwa, NIP (jeśli faktura)
|
||||
And każda zmienna ma opis i placeholder (np. {{zamowienie.numer}})
|
||||
```
|
||||
|
||||
## AC-6: Toggle statusu i usuwanie
|
||||
```gherkin
|
||||
Given szablon istnieje na liście
|
||||
When użytkownik klika Włącz/Wyłącz
|
||||
Then status is_active się zmienia (toggle AJAX)
|
||||
When użytkownik klika Usuń i potwierdzi (OrderProAlerts.confirm)
|
||||
Then szablon jest usunięty z bazy
|
||||
```
|
||||
|
||||
## AC-7: Podgląd szablonu z przykładowymi danymi
|
||||
```gherkin
|
||||
Given użytkownik edytuje szablon z zmiennymi
|
||||
When klika przycisk "Podgląd"
|
||||
Then widzi modal z treścią szablonu gdzie zmienne są zastąpione przykładowymi danymi
|
||||
And temat wiadomości jest również rozwiązany z przykładowymi danymi
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: EmailTemplateRepository + EmailTemplateController + routes</name>
|
||||
<files>
|
||||
src/Modules/Settings/EmailTemplateRepository.php,
|
||||
src/Modules/Settings/EmailTemplateController.php,
|
||||
routes/web.php
|
||||
</files>
|
||||
<action>
|
||||
**EmailTemplateRepository** (wzorowany na EmailMailboxRepository):
|
||||
- listAll(): array — pobiera wszystkie szablony JOIN email_mailboxes (nazwa skrzynki), ORDER BY name
|
||||
- findById(int $id): ?array — jeden szablon po ID
|
||||
- save(array $data): void — INSERT lub UPDATE (id > 0 = update)
|
||||
- delete(int $id): void — DELETE po ID
|
||||
- toggleStatus(int $id): void — toggle is_active (UPDATE SET is_active = NOT is_active)
|
||||
- listActive(): array — szablony z is_active=1 (do użycia w fazie 15)
|
||||
- Medoo + prepared statements, bez surowego SQL
|
||||
|
||||
**EmailTemplateController** (wzorowany na EmailMailboxController):
|
||||
- __construct(TemplateEngine, Translator, Auth, EmailTemplateRepository, EmailMailboxRepository)
|
||||
- index(Request): Response — lista szablonów + formularz (edit mode jeśli ?edit=ID)
|
||||
- save(Request): Response — walidacja (name, subject, body_html required) + CSRF + zapis + redirect z Flash
|
||||
- delete(Request): Response — CSRF + usunięcie + redirect z Flash
|
||||
- toggleStatus(Request): Response — AJAX POST, JSON response {success: true}
|
||||
- preview(Request): Response — AJAX POST, przyjmuje subject + body_html, zwraca JSON z resolved variables (przykładowe dane)
|
||||
- getVariables(Request): Response — AJAX GET, zwraca JSON z definicją dostępnych zmiennych pogrupowanych
|
||||
|
||||
**Definicja zmiennych** (statyczna metoda lub stała w kontrolerze):
|
||||
```
|
||||
Zamówienie:
|
||||
{{zamowienie.numer}} — Numer wewnętrzny (OP...)
|
||||
{{zamowienie.numer_zewnetrzny}} — Numer z platformy
|
||||
{{zamowienie.zrodlo}} — Źródło (Allegro/shopPRO/...)
|
||||
{{zamowienie.kwota}} — Kwota brutto
|
||||
{{zamowienie.waluta}} — Waluta (PLN/EUR/...)
|
||||
{{zamowienie.data}} — Data zamówienia
|
||||
Kupujący:
|
||||
{{kupujacy.imie_nazwisko}} — Imię i nazwisko
|
||||
{{kupujacy.email}} — Adres e-mail
|
||||
{{kupujacy.telefon}} — Telefon
|
||||
{{kupujacy.login}} — Login platformy
|
||||
Adres dostawy:
|
||||
{{adres.ulica}} — Ulica z numerem
|
||||
{{adres.miasto}} — Miasto
|
||||
{{adres.kod_pocztowy}} — Kod pocztowy
|
||||
{{adres.kraj}} — Kraj
|
||||
Firma:
|
||||
{{firma.nazwa}} — Nazwa firmy
|
||||
{{firma.nip}} — NIP
|
||||
```
|
||||
|
||||
**Metoda resolveVariables(string $text, array $sampleData): string**
|
||||
- Zamienia {{klucz.pole}} na wartości z $sampleData
|
||||
- Dla podglądu: używa przykładowych (hardcoded) danych
|
||||
- Dla wysyłki (faza 15): otrzyma prawdziwe dane zamówienia
|
||||
|
||||
**Routes** (dodać w web.php obok email-mailboxes):
|
||||
- GET /settings/email-templates → index
|
||||
- POST /settings/email-templates/save → save
|
||||
- POST /settings/email-templates/delete → delete
|
||||
- POST /settings/email-templates/toggle → toggleStatus
|
||||
- POST /settings/email-templates/preview → preview (AJAX)
|
||||
- GET /settings/email-templates/variables → getVariables (AJAX)
|
||||
</action>
|
||||
<verify>
|
||||
- Klasy się ładują bez Fatal Error (PHP syntax check)
|
||||
- Routes zarejestrowane poprawnie
|
||||
- Strona /settings/email-templates renderuje się bez błędów
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-5, AC-6, AC-7 backend satisfied</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Widoki — lista szablonów + formularz z Quill.js + system zmiennych UI</name>
|
||||
<files>
|
||||
resources/views/settings/email-templates.php,
|
||||
resources/views/settings/email-template-form.php,
|
||||
resources/views/layouts/app.php,
|
||||
resources/scss/app.scss,
|
||||
public/assets/css/app.css
|
||||
</files>
|
||||
<action>
|
||||
**Widok listy** (resources/views/settings/email-templates.php):
|
||||
- Tabela: Nazwa, Temat, Skrzynka (nazwa lub "—"), Status (badge), Akcje
|
||||
- Przycisk "Edytuj" → ?edit=ID
|
||||
- Toggle status: AJAX POST do /settings/email-templates/toggle (jak w mailboxes)
|
||||
- Usuwanie: window.OrderProAlerts.confirm() → POST /settings/email-templates/delete
|
||||
- Flash messages (success/error)
|
||||
- Przycisk "Nowy szablon" → przejście do formularza
|
||||
|
||||
**Formularz edycji** (osobny plik lub sekcja w tym samym widoku):
|
||||
- Pola: nazwa (input), temat (input — z możliwością wpisania zmiennych ręcznie), skrzynka (select z listActive), status (checkbox)
|
||||
- Edytor Quill.js:
|
||||
- Załadować z CDN: quill@2 (https://cdn.quilljs.com/2.0.3/quill.snow.css + quill.js)
|
||||
- Toolbar: bold, italic, underline, strike, headers (h1-h3), lists (ordered/bullet), link, color, background, align
|
||||
- Hidden input #body_html synchronizowany z Quill content on form submit
|
||||
- Panel zmiennych (sidebar lub dropdown):
|
||||
- Przycisk "Wstaw zmienną" otwiera listę pogrupowaną (pobrana AJAX z /variables)
|
||||
- Klik na zmienną → wstawia {{zmienna.pole}} w miejsce kursora w Quill
|
||||
- Przycisk "Podgląd":
|
||||
- AJAX POST do /settings/email-templates/preview z subject + body_html
|
||||
- Wynik w modalu (resolved subject + resolved body_html)
|
||||
- Przycisk "Zapisz" → submit formularza
|
||||
|
||||
**Sidebar** (resources/views/layouts/app.php):
|
||||
- Dodać link "Szablony e-mail" pod "Skrzynki pocztowe" w sekcji Settings
|
||||
- activeSettings === 'email-templates'
|
||||
|
||||
**Style** (resources/scss/app.scss → public/assets/css/app.css):
|
||||
- Styl dla kontenera Quill (.ql-editor min-height: 200px)
|
||||
- Styl dla panelu zmiennych (.email-var-list, .email-var-group, .email-var-item)
|
||||
- Styl dla modala podglądu (reuse istniejący .modal jeśli jest, lub prosty modal)
|
||||
- Kompaktowy layout zgodnie z zasadami projektu
|
||||
|
||||
**Nie używać** natywnych alert()/confirm() — tylko OrderProAlerts.
|
||||
**Quill CDN** — nie dodawać jako zależność npm/composer, wystarczy CDN w widoku.
|
||||
</action>
|
||||
<verify>
|
||||
- Quill.js ładuje się i renderuje edytor
|
||||
- Formularz zapisuje poprawnie (body_html zawiera HTML z Quill)
|
||||
- Zmienne wstawiane w edytor poprawnie
|
||||
- Podgląd wyświetla resolved template
|
||||
- SCSS skompilowane do CSS
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3, AC-4, AC-5, AC-6, AC-7 UI satisfied</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>CRUD szablonów e-mail z Quill.js i systemem zmiennych</what-built>
|
||||
<how-to-verify>
|
||||
1. Otwórz: /settings/email-templates
|
||||
2. Sprawdź: link "Szablony e-mail" widoczny w sidebarze
|
||||
3. Kliknij "Nowy szablon":
|
||||
a. Wypełnij nazwę, temat (np. "Potwierdzenie zamówienia {{zamowienie.numer}}")
|
||||
b. Wybierz skrzynkę z listy
|
||||
c. W Quill wpisz treść, użyj formatowania (bold, listy)
|
||||
d. Kliknij "Wstaw zmienną" → wybierz np. {{kupujacy.imie_nazwisko}}
|
||||
e. Kliknij "Podgląd" → sprawdź czy zmienne zamienione na przykładowe dane
|
||||
f. Zapisz szablon
|
||||
4. Na liście: sprawdź czy szablon się pojawił z poprawną nazwą/tematem/skrzynką
|
||||
5. Edytuj szablon — sprawdź czy Quill załadował treść HTML poprawnie
|
||||
6. Toggle status (włącz/wyłącz) — badge powinien się zmienić
|
||||
7. Usuń szablon — potwierdzenie OrderProAlerts, usunięcie z listy
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- database/migrations/* (tabela email_templates już istnieje z fazy 13)
|
||||
- src/Modules/Settings/EmailMailboxController.php (tylko import/reuse)
|
||||
- src/Modules/Settings/EmailMailboxRepository.php (tylko wywołanie listActive)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie implementować wysyłki e-mail (faza 15)
|
||||
- Nie dodawać PHPMailer/SwiftMailer (faza 15)
|
||||
- Nie modyfikować tabeli email_templates (schemat zamrożony)
|
||||
- Quill.js z CDN — bez npm/bundlera
|
||||
- Zmienne rozwiązywane tylko z przykładowymi danymi (prawdziwe dane zamówienia w fazie 15)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] /settings/email-templates renderuje się bez błędów PHP
|
||||
- [ ] CRUD: create, read, update, delete szablonów działa
|
||||
- [ ] Quill.js edytor ładuje się i zapisuje HTML
|
||||
- [ ] System zmiennych: lista, wstawianie, podgląd z przykładowymi danymi
|
||||
- [ ] Toggle statusu via AJAX działa
|
||||
- [ ] Usuwanie z potwierdzeniem OrderProAlerts działa
|
||||
- [ ] Link w sidebarze Settings aktywny
|
||||
- [ ] SCSS skompilowane, brak inline styles w widokach
|
||||
- [ ] Brak natywnych alert()/confirm()
|
||||
- [ ] CSRF walidowany we wszystkich POST
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie AC-1 do AC-7 spełnione
|
||||
- Wszystkie taski zakończone
|
||||
- Checkpoint human-verify approved
|
||||
- Brak błędów PHP, brak broken UI
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/14-email-templates/14-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user