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:
2026-05-12 18:47:41 +02:00
parent 3a2c419c25
commit 933dfcc67b
51 changed files with 1109 additions and 210 deletions

View File

@@ -0,0 +1,36 @@
<?php
/**
* Reusable alert component.
*
* Required scope vars:
* - $e : escape callable from template engine
* - $type : string (info|success|warning|danger), default 'info'
* - $message : string, escaped via $e()
* Optional:
* - $messageHtml : trusted HTML payload (used INSTEAD of $message when set)
* - $dismissible : bool, default true
* - $role : 'alert' (default) or 'status'
*/
$allowedTypes = ['info', 'success', 'warning', 'danger'];
$alertType = isset($type) && in_array($type, $allowedTypes, true) ? $type : 'info';
$alertMessage = isset($message) ? (string) $message : '';
$alertMessageRaw = isset($messageHtml) ? (string) $messageHtml : null;
$alertDismissible = !isset($dismissible) || (bool) $dismissible;
$alertRole = (isset($role) && $role === 'status') ? 'status' : 'alert';
$alertIcons = [
'info' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
'success' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
'warning' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
'danger' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
];
?>
<div class="alert alert--<?= $e($alertType) ?>" role="<?= $e($alertRole) ?>" data-alert>
<span class="alert__icon" aria-hidden="true"><?= $alertIcons[$alertType] ?></span>
<div class="alert__body"><?php if ($alertMessageRaw !== null): ?><?= $alertMessageRaw ?><?php else: ?><?= $e($alertMessage) ?><?php endif; ?></div>
<?php if ($alertDismissible): ?>
<button type="button" class="alert__dismiss" data-alert-dismiss aria-label="Zamknij">&times;</button>
<?php endif; ?>
</div>