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>
This commit is contained in:
@@ -65,7 +65,7 @@ HTTP Request
|
||||
|
||||
### Order Lifecycle
|
||||
1. **Import** — Cron handler → API client → `OrderImportService` → `OrdersRepository::insertOrder()` → `AutomationService::executeForNewOrder()`
|
||||
2. **Re-import (Phase 111 + 112)** — `OrderImportRepository::upsertOrderAggregate` wykrywa tranzycje `payment_status` z 0/1 na 2 i zwraca `payment_transition=true`. `AllegroOrderImportService` i `ShopproOrdersSyncService` na tej fladze emituja `payment.status_changed`, co przez chain reguly automatyzacji #7 zmienia `status_code` na `w_realizacji`. Logika preservacji `status_code` z Phase 62 pozostaje rozdzielona (`statusOverwriteAllowed` = `currentStatus='nieoplacone' && newPaymentStatus===2`). **Phase 112-01 (delta-only re-import):** przy `created=false` repo nie wywoluje `replaceAddresses/replaceItems/replaceNotes/replaceShipments/replaceStatusHistory` — `order_items.id` i flagi lokalne (np. `project_generated` z Phase 97) pozostaja stabilne. `updateOrderDelta()` aktualizuje wylacznie `status_code` (warunkowo, z propagacja anulowania), `payment_status`, `total_paid`, `is_canceled_by_buyer`, `source_updated_at`, `payload_json`, `fetched_at`, `updated_at`. Anulowanie ze zrodla (`is_canceled_by_buyer=1` lub zmapowany pull `status_code='anulowane'`) nadpisuje preservacje statusu. Identical-payload guard (`normalizePayloadJson`) pomija UPDATE gdy znormalizowany payload nie rozni sie od DB i brak innych tranzycji.
|
||||
2. **Re-import (Phase 111 + 112 + 119)** — `OrderImportRepository::upsertOrderAggregate` wykrywa tranzycje `payment_status` z 0/1 na 2 i zwraca `payment_transition=true`. `AllegroOrderImportService` i `ShopproOrdersSyncService` na tej fladze emituja `payment.status_changed`, co przez chain reguly automatyzacji #7 zmienia `status_code` na `w_realizacji`. Logika preservacji `status_code` z Phase 62 pozostaje rozdzielona (`statusOverwriteAllowed` = `currentStatus='nieoplacone' && newPaymentStatus===2`). **Phase 112-01 (delta-only re-import):** przy `created=false` repo nie wywoluje `replaceAddresses/replaceItems/replaceNotes/replaceShipments/replaceStatusHistory` — `order_items.id` i flagi lokalne (np. `project_generated` z Phase 97) pozostaja stabilne. `updateOrderDelta()` aktualizuje wylacznie `status_code` (warunkowo, z propagacja anulowania), `payment_status`, `total_paid`, `is_canceled_by_buyer`, `source_updated_at`, `payload_json`, `fetched_at`, `updated_at`. Anulowanie ze zrodla (`is_canceled_by_buyer=1` lub zmapowany pull `status_code='anulowane'`) nadpisuje preservacje statusu. Identical-payload guard (`normalizePayloadJson`) pomija UPDATE gdy znormalizowany payload nie rozni sie od DB i brak innych tranzycji. **Phase 119-01 (total_paid protection):** gdy `paymentStatusUnchanged=true` (`oldPaymentStatus === newPaymentStatus`), `updateOrderDelta()` nie dolacza `total_paid` do UPDATE — chroni reczne korekty kwoty (np. zwroty czesciowe). `is_canceled_by_buyer` jest pomijane analogicznie, chyba ze `cancelledBySource=true` (cancel propagation ze zrodla zawsze wymusza wpis flagi). Pozostale pola (`status_code`, `payment_status`, `source_updated_at`, `payload_json`, `fetched_at`, `updated_at`) zachowuja niezmieniony kontrakt z Phase 112-01.
|
||||
3. **Status update** — `OrdersController::updateStatus()` → `OrdersRepository::updateStatus()` → automation check
|
||||
4. **Status sync** — Cron → `AllegroStatusSyncService` / `ShopproStatusSyncService` → carrier API
|
||||
|
||||
@@ -203,6 +203,28 @@ tests/
|
||||
### IntegrationsHubController
|
||||
- Nowy parametr konstruktora `FakturowniaIntegrationRepository $fakturownia` i nowa metoda `buildFakturowniaRow()` agregująca status wszystkich kont (count instancji, configured/active counts, ostatni test).
|
||||
|
||||
## Phase 118 — Fakturownia Single Instance
|
||||
|
||||
### FakturowniaIntegrationRepository
|
||||
- Zarzadza jedna globalna konfiguracja `fakturownia_integration_settings.id=1` i jednym rekordem `integrations.type='fakturownia'`.
|
||||
- `getSettings()` zasila formularz i hub integracji; `saveSettings()` zapisuje prefix, token, department/defaults i aktywnosc.
|
||||
- `getIntegrationId()` jest zrodlem prawdy dla delegowanych `invoice_configs.integration_id`.
|
||||
- `findAll()` zostaje kompatybilnym wrapperem zwracajacym liste z jednym elementem.
|
||||
|
||||
### FakturowniaIntegrationController + UI
|
||||
- `/settings/integrations/fakturownia` pokazuje jeden formularz i test polaczenia.
|
||||
- Legacy `/new` i `/edit` przekierowuja do globalnej konfiguracji; delete nie jest oferowany w UI.
|
||||
- Hub integracji pokazuje jedna instancje Fakturowni, bez licznika kont.
|
||||
|
||||
### Invoice Config Delegation
|
||||
- `InvoiceConfigRepository::save()` przy `is_delegated=1` ignoruje wieloinstancyjny wybor i ustawia globalny Fakturownia integration id.
|
||||
- UI konfiguracji faktury pokazuje status globalnej konfiguracji zamiast selecta kont.
|
||||
- `invoice_configs.integration_id` zostaje dla kompatybilnosci z `InvoiceService` i istniejaca historia faktur.
|
||||
|
||||
### Migration 20260512_000109
|
||||
- Wybiera aktywna instancje Fakturowni; fallback: uzywana w `invoice_configs`, potem najnizsze id.
|
||||
- Przepina delegowane `invoice_configs.integration_id` na zachowany rekord i usuwa nadmiarowe rekordy Fakturowni po przepieciu zaleznosci.
|
||||
|
||||
## Phase 115 — Wystawianie faktury z zamowienia
|
||||
|
||||
### InvoiceService (`src/Modules/Accounting/InvoiceService.php`)
|
||||
@@ -302,6 +324,38 @@ tests/
|
||||
### IntegrationsHubController
|
||||
- Dodaje wiersz SMSPLANET do `/settings/integrations` ze statusem konfiguracji, sekretu, aktywnosci i ostatniego testu.
|
||||
|
||||
## Phase 120 — Alert Component Unification
|
||||
|
||||
### Alert component (`resources/views/components/alert.php`)
|
||||
- Reusable alert renderer with params: `$type` (info|success|warning|danger; fallback 'info'), `$message` (escaped) lub `$messageHtml` (trusted), `$dismissible` (default true), `$role` ('alert'|'status').
|
||||
- Renders inline SVG icon per type + body + optional dismiss button. Markup: `<div class="alert alert--TYPE" data-alert>...<button data-alert-dismiss>...</button></div>`.
|
||||
- Used via `include __DIR__ . '/../components/alert.php'` po ustawieniu lokalnych `$type/$message/$dismissible`.
|
||||
|
||||
### SCSS — `.alert` w `resources/scss/shared/_ui-components.scss`
|
||||
- `.alert` jest teraz flex (icon + body + dismiss). Dodane: `.alert__icon`, `.alert__body`, `.alert__dismiss`.
|
||||
- Nowy wariant `.alert--info` (blue: border #bfdbfe, bg #eff6ff, color #1e3a8a) — wczesniej brakowal i renderowal sie jako czarny tekst na bialym tle.
|
||||
- Wariantow `--success/--warning/--danger` nie zmieniono kolorystycznie.
|
||||
- Wrapper `.alerts-stack` (gap 8px) do stackowania wielu alertow z layoutu.
|
||||
|
||||
### JS — `public/assets/js/modules/alert-dismiss.js`
|
||||
- Vanilla JS, idempotent guard (`window.__alertDismissBound`).
|
||||
- Delegated click handler na `[data-alert-dismiss]` — usuwa najblizszy `[data-alert]` z DOM bez przeladowania.
|
||||
- Ladowany globalnie w `layouts/app.php`, `layouts/auth.php`, `layouts/public.php`.
|
||||
|
||||
### Flash — `App\Core\Support\Flash` rozszerzenie
|
||||
- Nowa kolejka typowana `$_SESSION['_flash_queue']` z entries `{type, message}`.
|
||||
- `Flash::push(string $type, string $message): void` — append do kolejki (whitelist info/success/warning/danger, fallback info).
|
||||
- `Flash::all(): array` — zwraca i czysci kolejke + skanuje legacy `_flash` (heurystyka klucza: `error/fail/danger` → danger, `warning` → warning, `success/.save/.created/.deleted/.toggled` → success, reszta → info). BC zachowany: `Flash::set/get` dziala bez zmian.
|
||||
|
||||
### Centralny renderer flash w layoutach
|
||||
- `layouts/app.php`, `layouts/auth.php`, `layouts/public.php` na poczatku glownego content area iteruja `Flash::all()` i wlaczaja komponent `alert.php` per wpis (wrap `.alerts-stack`).
|
||||
- Kontrolery NIE wymagaly zmian — pre-fetched `Flash::get('module.key', '')` przekazany do widoku jako lokalna zmienna jest dalej renderowany inline przez widok (przez ten sam komponent). Centralny renderer przejmuje wpisy `Flash::push(...)` oraz nieskonsumowane legacy entries.
|
||||
|
||||
### Migracja widokow
|
||||
- Wszystkie inline `<div class="alert alert--TYPE">...</div>` w widokach (36 plikow razem ze `shipments/prepare.php` i `orders/show.php`) zastapione przez `<?php $type=...; $message=...; $dismissible=...; include dirname(__DIR__) . '/components/alert.php'; ?>`.
|
||||
- `.flash--error` / `.flash--success` w `orders/show.php` i `shipments/prepare.php` zastapione komponentem (klasa `.flash--*` w SCSS pozostaje bez uzycia, deferred cleanup).
|
||||
- Wyjatek: `settings/email-mailboxes.php` ma JS-generowane alerty (`resultDiv.className = 'mt-12 alert alert--success'`) z dynamicznej odpowiedzi AJAX test polaczenia SMTP — uzywaja klas SCSS bez markupu komponentu (out of scope dla tej fazy).
|
||||
|
||||
## Phase 114 — Accounting Configs Refactor
|
||||
|
||||
### Sekcja Ksiegowosc — struktura URL
|
||||
|
||||
Reference in New Issue
Block a user