feat(14-email-templates): CRUD szablonów e-mail z Quill.js + załączniki
Phase 14 complete (2 plans):
- 14-01: CRUD szablonów, Quill.js editor, system zmiennych {{grupa.pole}}, podgląd, toggle
- 14-02: Select "Załącznik nr 1" z mapą ATTACHMENT_TYPES, kolumna w liście, migracja attachment_1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów sprzedaży i n
|
||||
|
||||
- [x] DB Foundation: tabele email_mailboxes, email_templates, email_logs — Phase 13
|
||||
- [x] Skrzynki pocztowe SMTP (CRUD + test połączenia) — Phase 13
|
||||
- [ ] Szablony wiadomości e-mail (CRUD + Quill.js + system zmiennych) — Phase 14
|
||||
- [x] Szablony wiadomości e-mail (CRUD + Quill.js + system zmiennych + załączniki) — Phase 14
|
||||
- [ ] Wysyłka e-mail z zamówień (resolwer zmiennych, załączniki, log) — Phase 15
|
||||
|
||||
### Planned (Next)
|
||||
@@ -97,6 +97,8 @@ PHP (XAMPP/Laravel), integracje z API marketplace'ów (Allegro, Erli) oraz API p
|
||||
| Snapshot pattern: seller/buyer/items jako JSON w receipts | Dane zamrożone w momencie wystawienia — niezależne od przyszłych zmian źródła | 2026-03-15 | Active |
|
||||
| Atomowe numerowanie paragonów: INSERT ON DUPLICATE KEY UPDATE | Bezpieczne generowanie kolejnych numerów bez race conditions | 2026-03-15 | Active |
|
||||
| Moduł Accounting w osobnym namespace | App\Modules\Accounting — separacja od Settings | 2026-03-15 | Active |
|
||||
| ATTACHMENT_TYPES jako centralna mapa typów załączników | Rozszerzalność: nowy typ = 1 linia w tablicy PHP, bez zmian DB/widoku | 2026-03-16 | Active |
|
||||
| Quill.js 2.0.3 CDN dla edytora szablonów | Brak build pipeline w projekcie; CDN prostszy | 2026-03-16 | Active |
|
||||
|
||||
## Success Metrics
|
||||
|
||||
@@ -128,4 +130,4 @@ Quick Reference:
|
||||
|
||||
---
|
||||
*PROJECT.md — Updated when requirements or context change*
|
||||
*Last updated: 2026-03-15 after milestone v0.3 (Moduł Paragonów complete)*
|
||||
*Last updated: 2026-03-16 after Phase 14 (Szablony wiadomości e-mail complete)*
|
||||
|
||||
@@ -13,7 +13,7 @@ Skrzynki pocztowe SMTP, szablony wiadomości z systemem zmiennych (Quill.js), wy
|
||||
| Phase | Name | Plans | Status |
|
||||
|-------|------|-------|--------|
|
||||
| 13 | DB + Skrzynki pocztowe | 1/1 | Complete ✓ |
|
||||
| 14 | Szablony wiadomości | 0/1 | Planning |
|
||||
| 14 | Szablony wiadomości | 2/2 | Complete ✓ |
|
||||
| 15 | Wysyłka e-mail z zamówień | TBD | Not started |
|
||||
|
||||
## Completed Milestones
|
||||
@@ -69,4 +69,4 @@ Archive: `.paul/milestones/v0.1-ROADMAP.md`
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-03-12*
|
||||
*Last updated: 2026-03-15 — milestone v0.4 started*
|
||||
*Last updated: 2026-03-16 — phase 14 complete*
|
||||
|
||||
@@ -10,26 +10,26 @@ See: .paul/PROJECT.md (updated 2026-03-12)
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.4 Moduł E-mail
|
||||
Phase: [2] of [3] (Szablony wiadomości) — Planning
|
||||
Plan: 14-01 created, awaiting approval
|
||||
Status: PLAN created, ready for APPLY
|
||||
Last activity: 2026-03-16 — Created .paul/phases/14-email-templates/14-01-PLAN.md
|
||||
Phase: [3] of [3] (Wysyłka e-mail z zamówień) — Not started
|
||||
Plan: Not started
|
||||
Status: Ready to plan
|
||||
Last activity: 2026-03-16 — Phase 14 complete, transitioned to Phase 15
|
||||
|
||||
Progress:
|
||||
- v0.1 Initial Release: [██████████] 100% ✓
|
||||
- v0.2 Pre-Expansion Fixes: [██████████] 100% ✓
|
||||
- v0.3 Moduł Paragonów: [██████████] 100% ✓
|
||||
- v0.4 Moduł E-mail: [███░░░░░░░] 33%
|
||||
- v0.4 Moduł E-mail: [███████░░░] 67%
|
||||
- Phase 13: [██████████] 100% ✓
|
||||
- Phase 14: [░░░░░░░░░░] 0% ← planning
|
||||
- Phase 15: [░░░░░░░░░░] 0%
|
||||
- Phase 14: [██████████] 100% ✓
|
||||
- Phase 15: [░░░░░░░░░░] 0% ← next
|
||||
|
||||
## Loop Position
|
||||
|
||||
Current loop state:
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ◐ ○ [APPLY in progress — Task 3 checkpoint awaiting approval]
|
||||
○ ○ ○ [Ready for next PLAN — Phase 15]
|
||||
```
|
||||
|
||||
## Accumulated Context
|
||||
@@ -57,6 +57,13 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
| 2026-03-15 | ftp-kr vendor/ nie ignorowany (zmiana na /vendor/bin) | Faza 11 | Automatyczny upload vendor/ przy zmianach; rewizja decyzji z fazy 07 |
|
||||
| 2026-03-15 | PhpSpreadsheet v5.5 dla eksportu XLSX | Faza 12 | Nowa zależność composer; XLSX lepszy od CSV dla księgowości |
|
||||
| 2026-03-15 | POST eksport z CSRF + dwa tryby (zaznaczone/wszystkie z filtra) | Faza 12 | Bezpieczny eksport; selectable table-list reuse |
|
||||
| 2026-03-16 | ATTACHMENT_TYPES jako centralna mapa typów załączników | Faza 14 | Rozszerzalność: nowy typ = 1 linia w tablicy PHP |
|
||||
| 2026-03-16 | Quill.js 2.0.3 CDN dla edytora szablonów | Faza 14 | Brak build pipeline; CDN prostszy |
|
||||
|
||||
### Skill Audit (Faza 14, Plan 02)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
| sonar-scanner | ○ | Required — do uruchomienia przed kolejnym UNIFY |
|
||||
|
||||
### Skill Audit (Faza 13, Plan 01)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
@@ -147,7 +154,7 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
- **Delivery mapping "Szukaj..." layout** — JS `attachSelectFilter()` w allegro.php tworzy input search dla InPost/Apaczka selectów, wizualnie wygląda jakby należał do wiersza powyżej. Pre-existing bug, do naprawy osobno.
|
||||
|
||||
### Git State
|
||||
Last commit: 3223aac (feat(13-email-mailboxes): phase 13 complete — email DB foundation + SMTP mailbox CRUD)
|
||||
Last commit: pending (feat(14-email-templates): phase 14 complete)
|
||||
Branch: main
|
||||
Feature branches merged: none
|
||||
|
||||
@@ -157,16 +164,15 @@ Brak.
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-03-16
|
||||
Stopped at: APPLY 14-01 in progress — Task 3 checkpoint human-verify (2 namespace bugs fixed, awaiting re-test)
|
||||
Next action: User re-tests /settings/email-templates → if approved → finalize APPLY → sonar-scanner → /paul:unify
|
||||
Resume file: .paul/HANDOFF-2026-03-16.md
|
||||
Stopped at: Phase 14 complete, ready to plan Phase 15
|
||||
Next action: /paul:plan for Phase 15 (Wysyłka e-mail z zamówień)
|
||||
Resume file: .paul/ROADMAP.md
|
||||
Resume context:
|
||||
- v0.1: COMPLETE ✓ (6 phases, 15 plans)
|
||||
- v0.2: COMPLETE ✓ (1 phase, 5 plans)
|
||||
- v0.3: COMPLETE ✓ (5 phases, 5 plans) — Moduł Paragonów
|
||||
- v0.4: IN PROGRESS — Phase 13 complete, Phase 14 APPLY checkpoint
|
||||
- Faza 0 (nieaktywne przyciski) zrobiona poza planem
|
||||
- 2 namespace fixes applied: AuthService, Flash
|
||||
- v0.4: IN PROGRESS — Phase 13+14 complete, Phase 15 next
|
||||
- Phase 14: CRUD szablonów + Quill.js + zmienne + załączniki (ATTACHMENT_TYPES)
|
||||
|
||||
---
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
88
.paul/phases/14-email-templates/14-01-SUMMARY.md
Normal file
88
.paul/phases/14-email-templates/14-01-SUMMARY.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
phase: 14-email-templates
|
||||
plan: 01
|
||||
subsystem: database, ui
|
||||
tags: [email, templates, quill, variables, crud]
|
||||
|
||||
requires:
|
||||
- phase: 13-email-mailboxes
|
||||
provides: Tabele email_mailboxes, email_templates, email_logs + SMTP CRUD
|
||||
provides:
|
||||
- CRUD szablonów e-mail z Quill.js
|
||||
- System zmiennych {{grupa.pole}} z podglądem
|
||||
- Toggle status (AJAX) + usuwanie z potwierdzeniem
|
||||
affects: [14-email-templates/14-02, 15-email-sending]
|
||||
|
||||
tech-stack:
|
||||
added: [quill.js 2.0.3 CDN]
|
||||
patterns: [VARIABLE_GROUPS const, resolveVariables regex, Quill.js rich text]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- src/Modules/Settings/EmailTemplateController.php
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- resources/views/settings/email-templates.php
|
||||
modified:
|
||||
- resources/views/layouts/app.php
|
||||
- routes/web.php
|
||||
- resources/scss/app.scss
|
||||
|
||||
key-decisions:
|
||||
- "Quill.js CDN zamiast npm — brak build pipeline w projekcie"
|
||||
- "VARIABLE_GROUPS jako stała w kontrolerze — centralna definicja zmiennych"
|
||||
- "Namespace fixes: AuthService → App\\Modules\\Auth, Flash → App\\Core\\Support"
|
||||
|
||||
duration: ~45min
|
||||
started: 2026-03-16
|
||||
completed: 2026-03-16
|
||||
---
|
||||
|
||||
# Phase 14 Plan 01: CRUD szablonów e-mail z Quill.js — Summary
|
||||
|
||||
**Pełny CRUD szablonów e-mail z edytorem Quill.js, systemem zmiennych {{grupa.pole}}, podglądem, toggle statusu i linkiem w sidebarze.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~45min |
|
||||
| Completed | 2026-03-16 |
|
||||
| Tasks | 3 completed (2 auto + 1 checkpoint) |
|
||||
| Files modified | 6 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| CRUD szablonów | Pass | Lista, dodawanie, edycja, usuwanie |
|
||||
| Quill.js editor | Pass | Rich text z toolbar |
|
||||
| System zmiennych | Pass | Panel z grupami, wstawianie do edytora |
|
||||
| Podgląd z rozwiązanymi zmiennymi | Pass | AJAX preview z sample data |
|
||||
| Toggle aktywności | Pass | AJAX bez przeładowania |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- EmailTemplateController + EmailTemplateRepository (pełny CRUD)
|
||||
- Quill.js 2.0.3 rich text editor z toolbar
|
||||
- System zmiennych: 4 grupy (zamówienie, kupujący, adres, firma) z 16 zmiennymi
|
||||
- Podgląd szablonu z rozwiązanymi zmiennymi (sample data)
|
||||
- AJAX toggle statusu + usuwanie z OrderProAlerts.confirm
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Namespace bugs**
|
||||
- AuthService: `App\Core\Auth\AuthService` → `App\Modules\Auth\AuthService`
|
||||
- Flash: `App\Core\Session\Flash` → `App\Core\Support\Flash`
|
||||
- Odkryte podczas testów na serwerze, naprawione natychmiast
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Szablony gotowe do rozszerzenia o załączniki (plan 14-02)
|
||||
- resolveVariables() gotowy do użycia w fazie 15
|
||||
|
||||
---
|
||||
*Phase: 14-email-templates, Plan: 01*
|
||||
*Completed: 2026-03-16*
|
||||
213
.paul/phases/14-email-templates/14-02-PLAN.md
Normal file
213
.paul/phases/14-email-templates/14-02-PLAN.md
Normal file
@@ -0,0 +1,213 @@
|
||||
---
|
||||
phase: 14-email-templates
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["14-01"]
|
||||
files_modified:
|
||||
- database/migrations/20260316_000001_add_attachment1_to_email_templates.sql
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- src/Modules/Settings/EmailTemplateController.php
|
||||
- resources/views/settings/email-templates.php
|
||||
- DOCS/DB_SCHEMA.md
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodać pole "Załącznik nr 1" do szablonów e-mail z opcją "paragon". Pole zapisywane w DB jako `attachment_1 VARCHAR(50)`, wyświetlane jako select w formularzu szablonu.
|
||||
|
||||
## Purpose
|
||||
Przygotowanie infrastruktury załączników na poziomie szablonu — faza 15 (wysyłka e-mail) odczyta tę konfigurację i automatycznie dołączy PDF paragonu do wiadomości, jeśli istnieje.
|
||||
|
||||
## Output
|
||||
- Migracja SQL dodająca kolumnę `attachment_1`
|
||||
- Repository/Controller obsługujący nowe pole
|
||||
- Select w widoku z opcjami "— brak —" / "Paragon"
|
||||
- Zaktualizowany DB_SCHEMA.md
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/14-email-templates/14-01-PLAN.md — CRUD szablonów, Quill.js, system zmiennych
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Settings/EmailTemplateController.php
|
||||
@src/Modules/Settings/EmailTemplateRepository.php
|
||||
@resources/views/settings/email-templates.php
|
||||
@DOCS/DB_SCHEMA.md
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Kolumna attachment_1 w bazie danych
|
||||
```gherkin
|
||||
Given tabela email_templates istnieje
|
||||
When migracja 20260316_000001 zostanie wykonana
|
||||
Then kolumna attachment_1 VARCHAR(50) DEFAULT NULL istnieje w tabeli email_templates
|
||||
```
|
||||
|
||||
## AC-2: Zapis i odczyt attachment_1 przez formularz
|
||||
```gherkin
|
||||
Given użytkownik edytuje szablon e-mail
|
||||
When wybierze "Paragon" w polu "Załącznik nr 1" i zapisze
|
||||
Then wartość 'receipt' jest zapisana w kolumnie attachment_1
|
||||
And przy ponownej edycji select pokazuje "Paragon" jako wybraną opcję
|
||||
```
|
||||
|
||||
## AC-3: Opcja "brak" czyści attachment_1
|
||||
```gherkin
|
||||
Given szablon ma ustawiony attachment_1 = 'receipt'
|
||||
When użytkownik zmieni select na "— brak —" i zapisze
|
||||
Then wartość attachment_1 w bazie to NULL
|
||||
```
|
||||
|
||||
## AC-4: Lista szablonów pokazuje informację o załączniku
|
||||
```gherkin
|
||||
Given szablon ma attachment_1 = 'receipt'
|
||||
When użytkownik otwiera listę szablonów
|
||||
Then w kolumnie "Załącznik" widoczne jest "Paragon"
|
||||
And dla szablonów bez załącznika kolumna pokazuje "—"
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Migracja DB + aktualizacja Repository i Controller</name>
|
||||
<files>
|
||||
database/migrations/20260316_000001_add_attachment1_to_email_templates.sql,
|
||||
src/Modules/Settings/EmailTemplateRepository.php,
|
||||
src/Modules/Settings/EmailTemplateController.php
|
||||
</files>
|
||||
<action>
|
||||
1. Utworzyć migrację SQL:
|
||||
```sql
|
||||
ALTER TABLE email_templates ADD COLUMN attachment_1 VARCHAR(50) DEFAULT NULL AFTER mailbox_id;
|
||||
```
|
||||
|
||||
2. EmailTemplateRepository — dodać `attachment_1` do:
|
||||
- `listAll()` — dodać do SELECT (i do listActive() też)
|
||||
- `findById()` — dodać do SELECT
|
||||
- `save()` — dodać do INSERT i UPDATE, wartość: NULL gdy puste/'none', string gdy wybrane
|
||||
|
||||
3. EmailTemplateController — dodać stałą ATTACHMENT_TYPES jako centralną mapę:
|
||||
```php
|
||||
private const ATTACHMENT_TYPES = [
|
||||
'receipt' => 'Paragon',
|
||||
];
|
||||
```
|
||||
Dodanie nowego typu w przyszłości = jedna linia w tej tablicy.
|
||||
|
||||
4. EmailTemplateController::index() — przekazać `'attachmentTypes' => self::ATTACHMENT_TYPES` do widoku.
|
||||
|
||||
5. EmailTemplateController::save() — pobrać `attachment_1` z requestu:
|
||||
- Jeśli wartość to '' → null
|
||||
- Jeśli wartość istnieje w kluczach ATTACHMENT_TYPES → zapisz
|
||||
- Inaczej → null (walidacja)
|
||||
- Przekazać do repository
|
||||
</action>
|
||||
<verify>
|
||||
- Migracja wykonana na serwerze bez błędów
|
||||
- DESCRIBE email_templates pokazuje kolumnę attachment_1
|
||||
- Zapis szablonu z attachment_1 = 'receipt' widoczny w DB
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3 satisfied</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Select w widoku + kolumna w liście + aktualizacja DB_SCHEMA</name>
|
||||
<files>
|
||||
resources/views/settings/email-templates.php,
|
||||
DOCS/DB_SCHEMA.md
|
||||
</files>
|
||||
<action>
|
||||
1. Widok — formularz: dodać select "Załącznik nr 1" w nowym wierszu form-grid-2 (pod tematem/aktywny).
|
||||
Select generowany dynamicznie z `$attachmentTypes` (przekazane z kontrolera):
|
||||
```php
|
||||
<option value="">— brak —</option>
|
||||
<?php foreach ($attachmentTypes as $key => $label): ?>
|
||||
<option value="<?= $e($key) ?>" ...selected...><?= $e($label) ?></option>
|
||||
<?php endforeach; ?>
|
||||
```
|
||||
Drugi slot w tym wierszu zostawić pusty lub użyć na przyszły attachment_2.
|
||||
|
||||
2. Widok — tabela listy: dodać kolumnę "Załącznik" między "Skrzynka" a "Status":
|
||||
- Odczytać label z `$attachmentTypes[$tpl['attachment_1']]` jeśli klucz istnieje
|
||||
- W przeciwnym razie → "—"
|
||||
|
||||
3. DOCS/DB_SCHEMA.md — dodać opis kolumny attachment_1 w sekcji email_templates.
|
||||
</action>
|
||||
<verify>
|
||||
- Formularz wyświetla select z dwoma opcjami
|
||||
- Przy edycji szablonu z attachment_1='receipt' select zaznacza "Paragon"
|
||||
- Lista szablonów pokazuje kolumnę "Załącznik"
|
||||
</verify>
|
||||
<done>AC-2, AC-3, AC-4 satisfied</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Select "Załącznik nr 1" w formularzu szablonu + kolumna "Załącznik" w liście</what-built>
|
||||
<how-to-verify>
|
||||
1. Wejdź na /settings/email-templates
|
||||
2. Sprawdź czy lista ma kolumnę "Załącznik"
|
||||
3. Edytuj istniejący szablon — sprawdź select "Załącznik nr 1" z opcjami "— brak —" i "Paragon"
|
||||
4. Wybierz "Paragon", zapisz
|
||||
5. Ponownie edytuj — select powinien mieć "Paragon" zaznaczony
|
||||
6. W liście przy tym szablonie kolumna "Załącznik" powinna pokazywać "Paragon"
|
||||
7. Zmień na "— brak —", zapisz — kolumna powinna pokazywać "—"
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- resources/views/layouts/app.php — sidebar already configured in 14-01
|
||||
- Logika wysyłania maili — to jest faza 15
|
||||
- Quill.js editor i system zmiennych — bez zmian
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Tylko konfiguracja załącznika na poziomie szablonu (jaki typ)
|
||||
- NIE implementować generowania/dołączania pliku — to faza 15
|
||||
- Tylko jeden slot załącznika (attachment_1), bez dynamicznego dodawania
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Migracja wykonana, kolumna attachment_1 istnieje
|
||||
- [ ] Zapis/odczyt attachment_1 działa (receipt / null)
|
||||
- [ ] Select w formularzu z poprawnymi opcjami
|
||||
- [ ] Kolumna "Załącznik" w liście szablonów
|
||||
- [ ] DB_SCHEMA.md zaktualizowany
|
||||
- [ ] Brak regresji w istniejącym CRUD szablonów
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Kolumna attachment_1 w DB
|
||||
- Formularz szablonu pozwala wybrać "Paragon" lub "brak"
|
||||
- Lista szablonów informuje o skonfigurowanym załączniku
|
||||
- Faza 15 może odczytać attachment_1 z szablonu i na tej podstawie dołączyć PDF
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/14-email-templates/14-02-SUMMARY.md`
|
||||
</output>
|
||||
112
.paul/phases/14-email-templates/14-02-SUMMARY.md
Normal file
112
.paul/phases/14-email-templates/14-02-SUMMARY.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
phase: 14-email-templates
|
||||
plan: 02
|
||||
subsystem: database, ui
|
||||
tags: [email, templates, attachments, receipt]
|
||||
|
||||
requires:
|
||||
- phase: 14-email-templates/14-01
|
||||
provides: CRUD szablonów e-mail, Quill.js editor, system zmiennych
|
||||
provides:
|
||||
- Konfiguracja załącznika na poziomie szablonu (attachment_1)
|
||||
- Mapa typów załączników ATTACHMENT_TYPES w kontrolerze
|
||||
affects: [15-email-sending]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [ATTACHMENT_TYPES const map for extensible attachment options]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260316_000001_add_attachment1_to_email_templates.sql
|
||||
modified:
|
||||
- src/Modules/Settings/EmailTemplateController.php
|
||||
- src/Modules/Settings/EmailTemplateRepository.php
|
||||
- resources/views/settings/email-templates.php
|
||||
- DOCS/DB_SCHEMA.md
|
||||
|
||||
key-decisions:
|
||||
- "ATTACHMENT_TYPES jako stała mapa w kontrolerze — rozszerzalność bez zmian DB/widoku"
|
||||
- "attachment_1 VARCHAR(50) zamiast ENUM — elastyczność dodawania nowych typów"
|
||||
|
||||
patterns-established:
|
||||
- "Centralna mapa typów załączników: dodanie nowego typu = 1 linia w ATTACHMENT_TYPES"
|
||||
|
||||
duration: ~15min
|
||||
started: 2026-03-16
|
||||
completed: 2026-03-16
|
||||
---
|
||||
|
||||
# Phase 14 Plan 02: Załączniki w szablonach e-mail — Summary
|
||||
|
||||
**Select "Załącznik nr 1" w szablonach e-mail z opcją "Paragon", rozszerzalną mapą typów ATTACHMENT_TYPES, kolumną w liście i pełnym CRUD.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Started | 2026-03-16 |
|
||||
| Completed | 2026-03-16 |
|
||||
| Tasks | 3 completed (2 auto + 1 checkpoint) |
|
||||
| Files modified | 5 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Kolumna attachment_1 w DB | Pass | VARCHAR(50) DEFAULT NULL, zweryfikowane DESCRIBE |
|
||||
| AC-2: Zapis i odczyt przez formularz | Pass | receipt zapisywane/odczytywane poprawnie |
|
||||
| AC-3: Opcja "brak" czyści attachment_1 | Pass | Pusta wartość → NULL w DB |
|
||||
| AC-4: Kolumna "Załącznik" w liście | Pass | "Paragon" / "—" wyświetlane poprawnie |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Kolumna `attachment_1` w `email_templates` z migracją na serwerze
|
||||
- Centralna mapa `ATTACHMENT_TYPES` w kontrolerze — dodanie nowego typu załącznika = 1 linia PHP
|
||||
- Select w formularzu generowany dynamicznie z mapy (nie hardkodowany)
|
||||
- Kolumna "Zalacznik" w liście szablonów z rozwiązywaniem label z mapy
|
||||
- Walidacja po stronie serwera: tylko klucze z ATTACHMENT_TYPES akceptowane
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `database/migrations/20260316_000001_add_attachment1_to_email_templates.sql` | Created | Migracja: kolumna attachment_1 |
|
||||
| `src/Modules/Settings/EmailTemplateController.php` | Modified | ATTACHMENT_TYPES const, walidacja, przekazanie do widoku |
|
||||
| `src/Modules/Settings/EmailTemplateRepository.php` | Modified | attachment_1 w SELECT, INSERT, UPDATE |
|
||||
| `resources/views/settings/email-templates.php` | Modified | Select w formularzu + kolumna w tabeli |
|
||||
| `DOCS/DB_SCHEMA.md` | Modified | Opis kolumny attachment_1 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| ATTACHMENT_TYPES jako stała mapa | User request: w przyszłości inne typy niż paragon | Dodanie nowego typu = 1 linia w tablicy PHP |
|
||||
| VARCHAR(50) zamiast ENUM | Nowe typy bez ALTER TABLE | Elastyczność kosztem minimalnej walidacji (robiona w PHP) |
|
||||
| Walidacja przez array_key_exists | Tylko znane typy akceptowane | Bezpieczeństwo: nieznane wartości → NULL |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Faza 15 może odczytać `attachment_1` z szablonu (`listActive()` zawiera pole)
|
||||
- Jeśli `attachment_1 === 'receipt'` → dołączyć PDF paragonu (dompdf już dostępny z fazy 11)
|
||||
- Mapa ATTACHMENT_TYPES może być referencją dla logiki generowania załączników
|
||||
|
||||
**Concerns:**
|
||||
- Brak — prosta konfiguracja, logika generowania w fazie 15
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 14-email-templates, Plan: 02*
|
||||
*Completed: 2026-03-16*
|
||||
@@ -95,6 +95,7 @@ Migracje z prefiksem `ensure_` to migracje kompensujące — zostały dodane
|
||||
- 2026-03-15: Dodano migracje `20260315_000054_create_email_mailboxes_table.sql` — tabela skrzynek pocztowych SMTP (credentials szyfrowane IntegrationSecretCipher).
|
||||
- 2026-03-15: Dodano migracje `20260315_000055_create_email_templates_table.sql` — tabela szablonow wiadomosci email z FK do email_mailboxes.
|
||||
- 2026-03-15: Dodano migracje `20260315_000056_create_email_logs_table.sql` — tabela logow wyslanych wiadomosci z FK do email_templates, email_mailboxes i indeksami na order_id, status, sent_at.
|
||||
- 2026-03-16: Dodano migracje `20260316_000001_add_attachment1_to_email_templates.sql` — kolumna attachment_1 VARCHAR(50) w email_templates (typ zalacznika, np. 'receipt').
|
||||
|
||||
## Tabele
|
||||
|
||||
@@ -411,6 +412,7 @@ Migracje z prefiksem `ensure_` to migracje kompensujące — zostały dodane
|
||||
- `subject` VARCHAR(500) NOT NULL — temat (moze zawierac zmienne)
|
||||
- `body_html` TEXT NOT NULL — tresc HTML (Quill.js output)
|
||||
- `mailbox_id` INT UNSIGNED DEFAULT NULL — FK do email_mailboxes ON DELETE SET NULL
|
||||
- `attachment_1` VARCHAR(50) DEFAULT NULL — typ zalacznika nr 1 (np. 'receipt' = paragon); NULL = brak
|
||||
- `is_active` TINYINT(1) NOT NULL DEFAULT 1
|
||||
- `created_at`, `updated_at` DATETIME
|
||||
- Indeksy: `idx_email_templates_mailbox` (mailbox_id)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE email_templates ADD COLUMN attachment_1 VARCHAR(50) DEFAULT NULL AFTER mailbox_id;
|
||||
@@ -4,6 +4,7 @@ $mailboxes = is_array($mailboxes ?? null) ? $mailboxes : [];
|
||||
$et = is_array($editTemplate ?? null) ? $editTemplate : null;
|
||||
$isEdit = $et !== null;
|
||||
$variableGroups = is_array($variableGroups ?? null) ? $variableGroups : [];
|
||||
$attachmentTypes = is_array($attachmentTypes ?? null) ? $attachmentTypes : [];
|
||||
?>
|
||||
|
||||
<section class="card">
|
||||
@@ -31,6 +32,7 @@ $variableGroups = is_array($variableGroups ?? null) ? $variableGroups : [];
|
||||
<th>Nazwa</th>
|
||||
<th>Temat</th>
|
||||
<th>Skrzynka</th>
|
||||
<th>Zalacznik</th>
|
||||
<th>Status</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
@@ -41,6 +43,7 @@ $variableGroups = is_array($variableGroups ?? null) ? $variableGroups : [];
|
||||
<td><?= $e((string) ($tpl['name'] ?? '')) ?></td>
|
||||
<td><?= $e((string) ($tpl['subject'] ?? '')) ?></td>
|
||||
<td><?= $e((string) ($tpl['mailbox_name'] ?? '—')) ?></td>
|
||||
<td><?= isset($tpl['attachment_1'], $attachmentTypes[$tpl['attachment_1']]) ? $e($attachmentTypes[$tpl['attachment_1']]) : '—' ?></td>
|
||||
<td>
|
||||
<?php if (((int) ($tpl['is_active'] ?? 0)) === 1): ?>
|
||||
<span class="badge badge--success js-status-badge">Aktywny</span>
|
||||
@@ -110,6 +113,19 @@ $variableGroups = is_array($variableGroups ?? null) ? $variableGroups : [];
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid-2 mt-0">
|
||||
<label class="form-field">
|
||||
<span class="field-label">Zalacznik nr 1</span>
|
||||
<select class="form-control" name="attachment_1">
|
||||
<option value="">— brak —</option>
|
||||
<?php foreach ($attachmentTypes as $atKey => $atLabel): ?>
|
||||
<option value="<?= $e($atKey) ?>"<?= ((string) ($et['attachment_1'] ?? '')) === $atKey ? ' selected' : '' ?>><?= $e($atLabel) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<div class="form-field"></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-12">
|
||||
<span class="field-label">Tresc wiadomosci *</span>
|
||||
<div class="email-tpl-editor-wrap mt-4">
|
||||
|
||||
@@ -54,6 +54,10 @@ final class EmailTemplateController
|
||||
],
|
||||
];
|
||||
|
||||
private const ATTACHMENT_TYPES = [
|
||||
'receipt' => 'Paragon',
|
||||
];
|
||||
|
||||
private const SAMPLE_DATA = [
|
||||
'zamowienie.numer' => 'OP000001234',
|
||||
'zamowienie.numer_zewnetrzny' => 'ALG-98765432',
|
||||
@@ -104,6 +108,7 @@ final class EmailTemplateController
|
||||
'mailboxes' => $mailboxes,
|
||||
'editTemplate' => $editTemplate,
|
||||
'variableGroups' => self::VARIABLE_GROUPS,
|
||||
'attachmentTypes' => self::ATTACHMENT_TYPES,
|
||||
'successMessage' => Flash::get('settings.email_templates.success', ''),
|
||||
'errorMessage' => Flash::get('settings.email_templates.error', ''),
|
||||
], 'layouts/app');
|
||||
@@ -127,6 +132,9 @@ final class EmailTemplateController
|
||||
return Response::redirect('/settings/email-templates');
|
||||
}
|
||||
|
||||
$attachment1Raw = trim((string) $request->input('attachment_1', ''));
|
||||
$attachment1 = isset(self::ATTACHMENT_TYPES[$attachment1Raw]) ? $attachment1Raw : '';
|
||||
|
||||
try {
|
||||
$this->repository->save([
|
||||
'id' => $request->input('id', ''),
|
||||
@@ -134,6 +142,7 @@ final class EmailTemplateController
|
||||
'subject' => $subject,
|
||||
'body_html' => $bodyHtml,
|
||||
'mailbox_id' => $request->input('mailbox_id', ''),
|
||||
'attachment_1' => $attachment1,
|
||||
'is_active' => $request->input('is_active', null),
|
||||
]);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ final class EmailTemplateRepository
|
||||
public function listAll(): array
|
||||
{
|
||||
$statement = $this->pdo->prepare(
|
||||
'SELECT t.id, t.name, t.subject, t.mailbox_id, t.is_active, t.created_at, t.updated_at,
|
||||
'SELECT t.id, t.name, t.subject, t.mailbox_id, t.attachment_1, t.is_active, t.created_at, t.updated_at,
|
||||
m.name AS mailbox_name
|
||||
FROM email_templates t
|
||||
LEFT JOIN email_mailboxes m ON m.id = t.mailbox_id
|
||||
@@ -37,7 +37,7 @@ final class EmailTemplateRepository
|
||||
public function findById(int $id): ?array
|
||||
{
|
||||
$statement = $this->pdo->prepare(
|
||||
'SELECT id, name, subject, body_html, mailbox_id, is_active, created_at, updated_at
|
||||
'SELECT id, name, subject, body_html, mailbox_id, attachment_1, is_active, created_at, updated_at
|
||||
FROM email_templates
|
||||
WHERE id = :id'
|
||||
);
|
||||
@@ -53,7 +53,7 @@ final class EmailTemplateRepository
|
||||
public function listActive(): array
|
||||
{
|
||||
$statement = $this->pdo->prepare(
|
||||
'SELECT id, name, subject, mailbox_id
|
||||
'SELECT id, name, subject, mailbox_id, attachment_1
|
||||
FROM email_templates
|
||||
WHERE is_active = 1
|
||||
ORDER BY name ASC'
|
||||
@@ -75,11 +75,16 @@ final class EmailTemplateRepository
|
||||
? (int) $data['mailbox_id']
|
||||
: null;
|
||||
|
||||
$attachment1 = isset($data['attachment_1']) && $data['attachment_1'] !== ''
|
||||
? (string) $data['attachment_1']
|
||||
: null;
|
||||
|
||||
$params = [
|
||||
'name' => trim((string) ($data['name'] ?? '')),
|
||||
'subject' => trim((string) ($data['subject'] ?? '')),
|
||||
'body_html' => (string) ($data['body_html'] ?? ''),
|
||||
'mailbox_id' => $mailboxId,
|
||||
'attachment_1' => $attachment1,
|
||||
'is_active' => isset($data['is_active']) ? 1 : 0,
|
||||
];
|
||||
|
||||
@@ -88,13 +93,13 @@ final class EmailTemplateRepository
|
||||
$statement = $this->pdo->prepare(
|
||||
'UPDATE email_templates
|
||||
SET name = :name, subject = :subject, body_html = :body_html,
|
||||
mailbox_id = :mailbox_id, is_active = :is_active
|
||||
mailbox_id = :mailbox_id, attachment_1 = :attachment_1, is_active = :is_active
|
||||
WHERE id = :id'
|
||||
);
|
||||
} else {
|
||||
$statement = $this->pdo->prepare(
|
||||
'INSERT INTO email_templates (name, subject, body_html, mailbox_id, is_active)
|
||||
VALUES (:name, :subject, :body_html, :mailbox_id, :is_active)'
|
||||
'INSERT INTO email_templates (name, subject, body_html, mailbox_id, attachment_1, is_active)
|
||||
VALUES (:name, :subject, :body_html, :mailbox_id, :attachment_1, :is_active)'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user