Files
orderPRO/.paul/phases/120-alert-component-unification/120-01-PLAN.md
Jacek Pyziak 933dfcc67b feat(120): alert component unification
Phase 120 - Plan 01:
- Reusable PHP alert component (resources/views/components/alert.php)
  with inline SVG icon per type, optional dismiss button.
- Missing .alert--info SCSS variant added (#eff6ff/#bfdbfe/#1e3a8a) -
  fixes black-on-white alert after Fakturownia test connection.
- Flash::push(type, message) + Flash::all() with BC for set/get;
  legacy key heuristic (error/.save/warning -> typed entries).
- Central flash renderer in 3 layouts (app/auth/public) iterating
  Flash::all() through component (.alerts-stack wrap).
- Vanilla JS alert-dismiss.js with idempotent guard and delegated
  click handler on [data-alert-dismiss].
- 36 views migrated off inline <div class="alert alert--TYPE">;
  .flash--error/success removed from views (orders/show, shipments/prepare).
- SCSS rebuilt: public/assets/css/{app,login}.css.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:47:41 +02:00

291 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 120-alert-component-unification
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- resources/scss/shared/_ui-components.scss
- resources/views/components/alert.php
- public/assets/js/modules/alert-dismiss.js
- resources/views/layouts/app.php
- resources/views/layouts/auth.php
- resources/views/layouts/public.php
- src/Core/Support/Flash.php
- resources/views/settings/fakturownia.php
- resources/views/settings/accounting-invoice-edit.php
- resources/views/settings/smsplanet.php
- resources/views/settings/hostedsms.php
- resources/views/settings/accounting.php
- resources/views/settings/accounting-receipt-edit.php
- resources/views/settings/accounting-receipts.php
- resources/views/settings/accounting-invoices.php
- resources/views/accounting/invoice_form.php
- resources/views/users/index.php
- resources/views/statistics/orders.php
- resources/views/settings/statuses.php
- resources/views/settings/shoppro.php
- resources/views/settings/printing.php
- resources/views/settings/project-mappings.php
- resources/views/settings/integrations.php
- resources/views/settings/email-templates.php
- resources/views/settings/inpost.php
- resources/views/settings/email-templates-form.php
- resources/views/settings/email-mailboxes.php
- resources/views/settings/delivery-statuses.php
- resources/views/settings/database.php
- resources/views/settings/delivery-status-form.php
- resources/views/settings/cron.php
- resources/views/settings/company.php
- resources/views/settings/apaczka.php
- resources/views/settings/allegro.php
- resources/views/orders/receipt-create.php
- resources/views/orders/list.php
- resources/views/automation/index.php
- resources/views/automation/form.php
- resources/views/auth/login.php
autonomous: true
delegation: off
---
<objective>
## Goal
Ujednolicic alerty/flash messages w calej aplikacji: reusable komponent widoku, brakujacy wariant `--info`, ikony i dismiss button, centralny renderer flash w layoutach. Po teście połączenia w `/settings/integrations/fakturownia` alert "OK (HTTP 200)" pojawia sie jako wyraznie wystylizowany `alert--info` z ikona i mozliwoscia zamkniecia.
## Purpose
Obecnie 34 widoki uzywaja inline `<div class="alert alert--TYPE">` z roznym wzorcem; brak `.alert--info` powoduje czarny tekst na bialym tle (bug widoczny w fakturownia.php). Brak centralnego renderowania flash zmusza kazdy controller+widok do recznego przekazywania zmiennych `$flashSave/$flashTest/$flashError`. Jedno zrodlo prawdy upraszcza UX i przyszle utrzymanie.
## Output
- `resources/views/components/alert.php` (komponent z paramami `$type`, `$message`, `$dismissible`)
- `resources/scss/shared/_ui-components.scss` z `.alert--info`, ikonami i stylami dismiss
- `public/assets/js/modules/alert-dismiss.js` (vanilla JS, idempotent guard)
- `src/Core/Support/Flash.php` rozszerzone o `push()` + `all()` z zachowaniem `set()/get()` (BC)
- Centralny render flash w `layouts/app.php` (+ `auth.php`, `public.php`)
- Migracja 34 widokow na komponent + zamiana `.flash--*` w login.php
</objective>
<context>
<clarifications>
- **Zakres** — Jak szeroki zakres migracji widokow w tej fazie?
→ Odpowiedz: Komponent + migracja wszystkich (34 widoki).
- **Funkcje** — Jakie warianty i funkcje ma obslugiwac komponent alert?
→ Odpowiedz: Wariant info (brakujacy), Ikona per typ (info/success/warning/danger), Przycisk dismiss (×). Auto-hide pominiete.
- **Flash legacy** — Czy ujednolicic rowniez stary wzorzec `.flash--error` (login.php) z nowym `.alert--*`?
→ Odpowiedz: Tak, zastapic `.flash--*` przez `.alert--*`.
- **Flash render** — Czy wyswietlanie alertow z sesji (Flash::get) zrobic tez przez komponent (centralny render np. w layoucie)?
→ Odpowiedz: Centralny renderer flash w layoucie app.php (i auth/public).
</clarifications>
## Project Context
@.paul/PROJECT.md
@.paul/STATE.md
@.paul/codebase/architecture.md
## Source Files
@resources/scss/shared/_ui-components.scss
@resources/views/settings/fakturownia.php
@resources/views/layouts/app.php
@resources/views/auth/login.php
@src/Core/Support/Flash.php
</context>
<acceptance_criteria>
## AC-1: Komponent alert + brakujacy wariant info
```gherkin
Given uzytkownik otwiera /settings/integrations/fakturownia i wykonuje udany test polaczenia
When kontroler ustawia Flash::push('info', 'OK (HTTP 200)') i widok renderuje sie ponownie
Then na gorze strony pokazuje sie alert z niebieskim tlem (#eff6ff), niebieska ramka, ikona "i" po lewej, tekstem "OK (HTTP 200)" w czytelnym kontraście oraz przyciskiem × po prawej; klikniecie × usuwa alert z DOM bez przeladowania
```
## AC-2: Centralny renderer flash w layoutach
```gherkin
Given kontroler wywoluje Flash::push('success', 'Zapisano') albo legacy Flash::set('module.save', 'Zapisano')
When renderowany jest widok dziedziczacy z layouts/app.php (lub auth.php / public.php)
Then layout automatycznie wyswietla wszystkie zakolejkowane flash entries u gory glownego obszaru, bez koniecznosci ifow w widoku; po pobraniu Flash::all() kolejne zadanie nie pokazuje juz tych wpisow
```
## AC-3: Migracja widokow i flash--error
```gherkin
Given kazdy z 34 widokow wymienionych w files_modified
When przegladam ich kod
Then zaden nie zawiera inline `<div class="alert alert--...">` z zmiennymi flash (zamiast tego uzywa komponentu lub renderowanie jest delegowane do layoutu); login.php nie zawiera klasy `.flash--error` (zostala zastapiona alert--danger przez komponent); wizualnie alerty na fakturownia/hostedsms/smsplanet/login wygladaja identycznie i spojnie
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Komponent alert + SCSS info/ikony/dismiss + JS</name>
<files>
resources/scss/shared/_ui-components.scss,
resources/views/components/alert.php,
public/assets/js/modules/alert-dismiss.js,
resources/views/layouts/app.php,
resources/views/layouts/auth.php,
resources/views/layouts/public.php
</files>
<action>
1) SCSS — w `_ui-components.scss` po istniejacych `.alert--success/warning/danger` dodac:
- `.alert` — display: flex; gap: 10px; align-items: flex-start; (zachowac padding/border-radius/border/font-size/min-height)
- `.alert__icon` — flex: 0 0 18px; line-height: 1; svg width/height 18px; color: inherit
- `.alert__body` — flex: 1; line-height: 1.4
- `.alert__dismiss` — margin-left: auto; background: none; border: 0; cursor: pointer; padding: 2px 6px; color: inherit; opacity: 0.6; &:hover { opacity: 1 }
- `.alert--info` — border-color: #bfdbfe; background: #eff6ff; color: #1e3a8a
- Subtelnie wzmocnic kontrast tla istniejacych wariantow tylko jezeli wymaga to drobnej zmiany (np. zostawic jak jest dla --success/warning/danger).
2) `resources/views/components/alert.php` — komponent renderujacy 1 alert. Params (przez extract):
- `$type` (string, default 'info') — jeden z: info|success|warning|danger
- `$message` (string) — escapowany przez `$e()`; alternatywnie `$messageHtml` (trusted HTML, opcjonalnie)
- `$dismissible` (bool, default true)
- Markup: `<div class="alert alert--{type}" role="alert" data-alert>` + `<span class="alert__icon" aria-hidden="true">{svgIconForType}</span>` + `<div class="alert__body">{message}</div>` + dismiss button (jezeli `$dismissible`).
- Mapping typ → svg inline (info: i in circle; success: check; warning: triangle !; danger: x in circle). Trzymac SVG inline jako private map array `$ICONS` zdefiniowany w komponencie.
- Walidacja: gdy `$type` nie z whitelist → fallback `info`.
3) `public/assets/js/modules/alert-dismiss.js` — vanilla JS:
- Idempotent guard: `if (window.__alertDismissBound) return; window.__alertDismissBound = true;`
- `document.addEventListener('click', (e) => { const btn = e.target.closest('[data-alert-dismiss]'); if (!btn) return; const alert = btn.closest('[data-alert]'); if (alert) alert.remove(); });`
- Brak zewnetrznych zaleznosci, brak jQuery.
4) Layouty (`app.php`, `auth.php`, `public.php`):
- Dodac `<link rel="stylesheet" href="/assets/css/app.css?ver=...">` juz istnieje — pliki SCSS sa kompilowane recznie (poza zakresem), nie ruszamy build pipeline.
- Dodac `<script src="/assets/js/modules/alert-dismiss.js?ver=...">` przed `</body>` (analogicznie do innych modulow).
- Dodac centralny render flash entries: na poczatku glownego content area, ITERACJA po `App\Core\Support\Flash::all()` i wywolanie komponentu `alert.php` dla kazdego wpisu `{type, message}`. Wzor:
```php
<?php foreach (\App\Core\Support\Flash::all() as $entry): ?>
<?php $type = (string) ($entry['type'] ?? 'info'); $message = (string) ($entry['message'] ?? ''); $dismissible = true; include __DIR__ . '/../components/alert.php'; ?>
<?php endforeach; ?>
```
- W `app.php` umiescic blok zaraz po otwarciu `<main>` (lub odpowiednim content wrap, znalezc grep "app-shell__main" / "content").
- W `auth.php` i `public.php` — analogicznie, u gory glownego obszaru.
Avoid:
- Nie dodawac jQuery / bundlerow.
- Nie zmieniac wygladu istniejacych alertow --success/--warning/--danger (poza dodaniem __icon + dismiss).
- SVG inline (nie eksternalizowac do osobnych plikow).
</action>
<verify>
1) `grep -rn "alert--info" resources/scss/shared/_ui-components.scss` zwraca definicje.
2) Plik `resources/views/components/alert.php` istnieje i zawiera `<div class="alert alert--<?= $type ?>"`.
3) `grep -rn "Flash::all" resources/views/layouts/` zwraca 3 wystapienia (app/auth/public).
4) Ladowanie strony /settings/integrations/fakturownia po tescie polaczenia pokazuje niebieski alert info z ikona i przyciskiem × (manualny smoke test po skompilowaniu SCSS).
</verify>
<done>AC-1, AC-2 spelnione w zakresie infrastruktury (komponent + central render + style + JS).</done>
</task>
<task type="auto">
<name>Task 2: Flash::push/all + BC dla Flash::set/get</name>
<files>src/Core/Support/Flash.php</files>
<action>
Rozszerzyc `App\Core\Support\Flash` o kolejke typowanych wpisow przy zachowaniu wstecznej kompatybilnosci.
1) Dodac sekcje kolejki w sesji: `$_SESSION['_flash_queue']` (lista `['type' => string, 'message' => string]`).
2) Nowa metoda: `public static function push(string $type, string $message): void`
- Whitelist `$type`: info/success/warning/danger; fallback 'info'.
- Append do `$_SESSION['_flash_queue']`.
3) Nowa metoda: `public static function all(): array`
- Zwraca i CZYSCI kolejke (consume-once).
- Dodatkowo: skanuje legacy `$_SESSION['_flash']` (klucz-string => wartosc-string) i konwertuje do typowanych entries:
- klucz konczacy sie na `.error` lub zawierajacy `error` → type=danger
- klucz konczacy sie na `.save` / `.success` / zawierajacy `success`/`saved`/`saved` → type=success
- klucz zawierajacy `warning` → type=warning
- reszta (`.test`, `.info`, etc.) → type=info
- Po konsumpcji czysci `_flash` analogicznie do `get()`.
- Zachowac kolejnosc: najpierw push-queue, potem legacy.
4) `set()`/`get()` pozostaja bez zmian (BC) — nie czyscic ich z `all()` jezeli `get()` nie zostal jeszcze wywolany; tj. `all()` POBIERA wszystkie pozostale legacy entries (controllery zwykle juz pobraly konkretne klucze przed renderem, wiec layout konsumuje to co zostalo).
5) Brak zmian sygnatury istniejacych metod, brak removed methods.
</action>
<verify>
1) `grep -n "public static function push" src/Core/Support/Flash.php` → 1 match.
2) `grep -n "public static function all" src/Core/Support/Flash.php` → 1 match.
3) Recznie: w widoku `Flash::set('fakturownia.test', 'OK (HTTP 200)'); Flash::all()` zwraca `[['type'=>'info','message'=>'OK (HTTP 200)']]`.
</verify>
<done>AC-2 spelnione w warstwie logiki (sesja → typowane entries).</done>
</task>
<task type="auto">
<name>Task 3: Migracja 34 widokow + login flash--error</name>
<files>
resources/views/settings/fakturownia.php, resources/views/settings/accounting-invoice-edit.php,
resources/views/settings/smsplanet.php, resources/views/settings/hostedsms.php,
resources/views/settings/accounting.php, resources/views/settings/accounting-receipt-edit.php,
resources/views/settings/accounting-receipts.php, resources/views/settings/accounting-invoices.php,
resources/views/accounting/invoice_form.php, resources/views/users/index.php,
resources/views/statistics/orders.php, resources/views/settings/statuses.php,
resources/views/settings/shoppro.php, resources/views/settings/printing.php,
resources/views/settings/project-mappings.php, resources/views/settings/integrations.php,
resources/views/settings/email-templates.php, resources/views/settings/inpost.php,
resources/views/settings/email-templates-form.php, resources/views/settings/email-mailboxes.php,
resources/views/settings/delivery-statuses.php, resources/views/settings/database.php,
resources/views/settings/delivery-status-form.php, resources/views/settings/cron.php,
resources/views/settings/company.php, resources/views/settings/apaczka.php,
resources/views/settings/allegro.php, resources/views/orders/receipt-create.php,
resources/views/orders/list.php, resources/views/automation/index.php,
resources/views/automation/form.php, resources/views/auth/login.php
</files>
<action>
Strategia migracji per widok:
1) Usunac inline bloki `<?php if ($flashXxx !== ''): ?><div class="alert alert--TYPE" role="...">...</div><?php endif; ?>`
— flash wyswietlany jest teraz centralnie przez layout (Flash::all() → komponent).
Kontrolery NIE wymagaja zmian w tej fazie (Flash::set zachowuje BC i jest konsumowany przez `Flash::all()` w layoucie).
2) Dla alertow NIE-flashowych (np. statyczne komunikaty/stany w widoku, nie z sesji) — zamienic na include komponentu:
```php
<?php $type='info'; $message='...'; $dismissible=false; include dirname(__DIR__) . '/components/alert.php'; ?>
```
Sprawdzic kazdy widok — jezeli alert jest oparty na zmiennej $flashXxx (z sesji), usunac caly blok. Jezeli alert pokazuje stan biezacy (np. "Brak konfiguracji"), zostawic ale przepuscic przez komponent.
3) `resources/views/auth/login.php` — zamienic `<div class="flash flash--error">...</div>` (lub podobny) na include komponentu `alert.php` z `$type='danger'`. Usunac wszystkie odwolania `.flash--error` w tym pliku. Login uzywa `layouts/auth.php` — flash messages tez przejda przez centralny render.
4) Po migracji NIE usuwac SCSS `.flash--error` jeszcze (separate cleanup task w przyszlosci) — wystarczy ze nie jest uzywany w widokach.
5) Smoke test recznie po kazdej grupie modulow (settings/, automation/, orders/, auth/) — sprawdzic w przegladarce ze flash success/error/info wyglada jak zaprojektowano.
Avoid:
- Nie dotykac kontrolerow (BC przez Flash::all() w layoucie).
- Nie zmieniac semantyki: jezeli widok renderowal blad jako `alert--danger`, po migracji ma byc nadal danger.
- Nie usuwac role="alert" / role="status" — komponent ma `role="alert"` na stale.
</action>
<verify>
1) `grep -rn 'class="alert alert--' resources/views/` zwraca tylko `resources/views/components/alert.php` (komponent jest jedynym renderem).
2) `grep -rn 'flash--error' resources/views/` zwraca 0 wynikow.
3) `grep -rn '\$flashError\|\$flashSave\|\$flashTest' resources/views/` zwraca 0 wynikow lub tylko miejsca gdzie zmienna jest przekazywana z kontrolera ale nie renderowana (akceptowalne — kontroler i tak ja przekazuje, layout konsumuje przez Flash::all()).
4) Smoke: /settings/integrations/fakturownia → test polaczenia → niebieski alert info z ikona i ×; klik × usuwa alert; reload strony nie pokazuje go ponownie.
5) /auth/login z bledem logowania → czerwony alert danger z ikona.
</verify>
<done>AC-3 spelnione: 34 widoki + login zmigrowane, flash--error usuniety z widokow, wszystkie alerty wygladaja spojnie.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `src/Modules/**/*Controller.php` — kontrolery dalej uzywaja `Flash::set('module.key', '...')` (BC).
- `resources/modules/jquery-alerts/` — modul dialogow OrderProAlerts (osobny system, nie alerty stronowe).
- `resources/scss/modules/_customer-risk-alert.scss` — inny komponent (banner ostrzezenia o kliencie).
- Istniejace warianty `.alert--success`, `.alert--warning`, `.alert--danger` (zachowac kolory; mozna tylko dodac `__icon`/`__dismiss` strukture).
## SCOPE LIMITS
- Brak migracji kontrolerow na `Flash::push()` w tej fazie (BC zapewnia layout przez `Flash::all()`).
- Brak auto-hide (timer JS) — zdjete z zakresu w odpowiedzi clarifications.
- Brak usuwania SCSS `.flash--error` z `_ui-components.scss` (deferred cleanup).
- Brak nowych endpointow API ani migracji DB.
- Build SCSS → CSS jest robiony recznie przez operatora poza tym planem; plan modyfikuje pliki .scss, ale `public/assets/css/app.css` zostanie zregenerowany osobno (operator/build pipeline).
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `grep -rn 'class="alert alert--' resources/views/` → tylko `components/alert.php`
- [ ] `grep -rn 'flash--error' resources/views/` → 0
- [ ] `grep -rn 'Flash::all' resources/views/layouts/` → 3
- [ ] Manual smoke: /settings/integrations/fakturownia po teste polaczenia → niebieski alert info z ikona + dismiss
- [ ] Manual smoke: /auth/login z blednym haslem → czerwony alert danger z ikona
- [ ] Manual smoke: zapis dowolnego ustawienia (np. fakturownia save) → zielony alert success
- [ ] AC-1, AC-2, AC-3 wszystkie spelnione
</verification>
<success_criteria>
- Komponent `alert.php` jest jedynym renderem alertu w widokach.
- `.alert--info` istnieje i ma czytelny kontrast.
- Centralny renderer flash dziala w 3 layoutach (app/auth/public).
- Flash::push/all dodane bez breakingu Flash::set/get.
- 34 widoki + login zmigrowane, brak inline `<div class="alert alert--...">` z flash zmiennymi.
</success_criteria>
<output>
After completion, create `.paul/phases/120-alert-component-unification/120-01-SUMMARY.md`
</output>