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>
122 lines
3.7 KiB
PHP
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';
|
|
}
|
|
}
|