Files
orderPRO/src/Core/Support/Flash.php
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

122 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Core\Support;
final class Flash
{
private const FLASH_KEY = '_flash';
private const QUEUE_KEY = '_flash_queue';
private const ALLOWED_TYPES = ['info', 'success', 'warning', 'danger'];
public static function set(string $key, mixed $value): void
{
if (!isset($_SESSION[self::FLASH_KEY]) || !is_array($_SESSION[self::FLASH_KEY])) {
$_SESSION[self::FLASH_KEY] = [];
}
$_SESSION[self::FLASH_KEY][$key] = $value;
}
public static function get(string $key, mixed $default = null): mixed
{
if (
!isset($_SESSION[self::FLASH_KEY]) ||
!is_array($_SESSION[self::FLASH_KEY]) ||
!array_key_exists($key, $_SESSION[self::FLASH_KEY])
) {
return $default;
}
$value = $_SESSION[self::FLASH_KEY][$key];
unset($_SESSION[self::FLASH_KEY][$key]);
if (empty($_SESSION[self::FLASH_KEY])) {
unset($_SESSION[self::FLASH_KEY]);
}
return $value;
}
public static function push(string $type, string $message): void
{
$normalizedType = in_array($type, self::ALLOWED_TYPES, true) ? $type : 'info';
if (!isset($_SESSION[self::QUEUE_KEY]) || !is_array($_SESSION[self::QUEUE_KEY])) {
$_SESSION[self::QUEUE_KEY] = [];
}
$_SESSION[self::QUEUE_KEY][] = [
'type' => $normalizedType,
'message' => $message,
];
}
/**
* Returns and clears all queued flash entries (typed queue + legacy key/value map).
*
* @return list<array{type: string, message: string}>
*/
public static function all(): array
{
$entries = [];
if (isset($_SESSION[self::QUEUE_KEY]) && is_array($_SESSION[self::QUEUE_KEY])) {
foreach ($_SESSION[self::QUEUE_KEY] as $entry) {
if (!is_array($entry)) {
continue;
}
$type = is_string($entry['type'] ?? null) ? $entry['type'] : 'info';
$message = is_string($entry['message'] ?? null) ? $entry['message'] : '';
if ($message === '') {
continue;
}
if (!in_array($type, self::ALLOWED_TYPES, true)) {
$type = 'info';
}
$entries[] = ['type' => $type, 'message' => $message];
}
unset($_SESSION[self::QUEUE_KEY]);
}
if (isset($_SESSION[self::FLASH_KEY]) && is_array($_SESSION[self::FLASH_KEY])) {
foreach ($_SESSION[self::FLASH_KEY] as $key => $value) {
if (!is_string($value) || $value === '') {
continue;
}
$entries[] = [
'type' => self::inferTypeFromKey((string) $key),
'message' => $value,
];
}
unset($_SESSION[self::FLASH_KEY]);
}
return $entries;
}
private static function inferTypeFromKey(string $key): string
{
$lower = strtolower($key);
if (str_contains($lower, 'error') || str_contains($lower, 'fail') || str_contains($lower, 'danger')) {
return 'danger';
}
if (str_contains($lower, 'warning') || str_contains($lower, 'warn')) {
return 'warning';
}
if (
str_contains($lower, 'success')
|| str_ends_with($lower, '.save')
|| str_contains($lower, 'saved')
|| str_contains($lower, '.created')
|| str_contains($lower, '.deleted')
|| str_contains($lower, '.toggled')
) {
return 'success';
}
return 'info';
}
}