Umsetzung von AP0-AP6 aus dev/2026-08-28/entwicklungsplan.md sowie der vorab gefundenen Befunde T1 und B1-B13. Befunde - CmsContentSeeder überschrieb bei jedem Lauf alle redaktionellen Inhalte; jetzt Redaktionsschutz plus gezielter Relaunch-Seeder (B1) - Magazin-CMS war nicht ans Frontend angebunden, Artikel jetzt über Slugs mit 301 von den alten numerischen URLs (B2) - Vier veröffentlichte Display-Playlists zeigten auf b2in.test und wären live nicht ladbar gewesen (B13) - Info-Tablet speicherte geleerte Öffnungszeiten nicht (T1) - /service und /portfolio lieferten seit Februar 500 (B12) Website - Startseite neu strukturiert und umgetextet, eigene Seite /marken mit Detailseite je Kernmarke, /netzwerk und Altrouten leiten dorthin - Galerie je Marke, getrennt nach realisierten Projekten und Visualisierungen; ohne gesetzte Kategorie gilt ein Bild als Entwurf - Seite /referenzen, gespeist aus den Markengalerien - Showroom-Display im 9:16-Rahmen über eine schreibgeschützte Embed-Route ohne Preview-Token - Terminanfrage-Widget auf allen Seiten, Markenauswahl aus dem CMS, WhatsApp per Klick-zum-Chat-Link mit vorbereitetem Text - FAQ, Über-uns, Kontakt und Magazin inhaltlich auf Interior umgestellt Sonstiges - Vorschau-Instanzen liefern noindex und verlinken sich bei unbekanntem Host auf sich selbst statt auf eine fremde Domain - Seeder-Zugangsdaten aus dem Code in die Konfiguration (B8) - Ablauf für die Vorschau: dev/2026-08-28/preview-deployment.md 833 Tests grün (3084 Assertions), Pint sauber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 lines
3.3 KiB
PHP
108 lines
3.3 KiB
PHP
<?php
|
||
|
||
namespace App\Support;
|
||
|
||
use App\Enums\DisplayVersionType;
|
||
|
||
class DisplayModuleSettings
|
||
{
|
||
/** @var list<string> */
|
||
private const B2IN_BOOLEAN_KEYS = [
|
||
'show_logo',
|
||
'show_claim',
|
||
'show_footer',
|
||
'display_active',
|
||
];
|
||
|
||
/**
|
||
* @return array<string, mixed>
|
||
*/
|
||
public static function defaults(DisplayVersionType|string $type): array
|
||
{
|
||
$typeValue = $type instanceof DisplayVersionType ? $type->value : $type;
|
||
|
||
return match ($typeValue) {
|
||
DisplayVersionType::VideoDisplay->value => [
|
||
'qr_label' => 'Website',
|
||
],
|
||
DisplayVersionType::B2in->value => [
|
||
'theme' => 'dark',
|
||
// Standard-Theme ist dunkel, das Logo liegt dort auf einem dunklen Scrim –
|
||
// deshalb die negative (weisse) Variante.
|
||
'header_logo_url' => '/_cabinet/assets/b2in-interior-logo-negative.svg',
|
||
'header_claim' => 'Premium-Einrichtung. Persönlich gemacht.',
|
||
'logo_position' => 'top-left',
|
||
'claim_position' => 'top-right',
|
||
'show_logo' => true,
|
||
'show_claim' => true,
|
||
'show_footer' => true,
|
||
'footer_url' => 'B2in.eu',
|
||
'footer_name' => '',
|
||
'footer_prefix' => 'by',
|
||
'qr_url' => '',
|
||
'transition' => [
|
||
'type' => 'crossfade',
|
||
'duration_ms' => 800,
|
||
],
|
||
'default_image_duration' => 10,
|
||
'rotation_weights' => ['immobilien' => 70, 'moebel' => 30],
|
||
'display_active' => true,
|
||
],
|
||
DisplayVersionType::Offers->value => [
|
||
'loop' => true,
|
||
'transition' => [
|
||
'type' => 'fade',
|
||
'duration' => 600,
|
||
],
|
||
],
|
||
default => [],
|
||
};
|
||
}
|
||
|
||
/**
|
||
* @param array<string, mixed>|null $settings
|
||
* @return array<string, mixed>
|
||
*/
|
||
public static function merge(DisplayVersionType|string $type, ?array $settings): array
|
||
{
|
||
$merged = array_replace_recursive(self::defaults($type), $settings ?? []);
|
||
|
||
$typeValue = $type instanceof DisplayVersionType ? $type->value : $type;
|
||
|
||
if ($typeValue === DisplayVersionType::B2in->value) {
|
||
return self::normalizeB2inBooleans($merged);
|
||
}
|
||
|
||
if ($typeValue === DisplayVersionType::Offers->value && array_key_exists('loop', $merged)) {
|
||
$merged['loop'] = self::toBool($merged['loop']);
|
||
}
|
||
|
||
return $merged;
|
||
}
|
||
|
||
/**
|
||
* @param array<string, mixed> $settings
|
||
* @return array<string, mixed>
|
||
*/
|
||
private static function normalizeB2inBooleans(array $settings): array
|
||
{
|
||
foreach (self::B2IN_BOOLEAN_KEYS as $key) {
|
||
if (array_key_exists($key, $settings)) {
|
||
$settings[$key] = self::toBool($settings[$key]);
|
||
}
|
||
}
|
||
|
||
return $settings;
|
||
}
|
||
|
||
private static function toBool(mixed $value): bool
|
||
{
|
||
if (is_bool($value)) {
|
||
return $value;
|
||
}
|
||
|
||
$filtered = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||
|
||
return $filtered ?? (bool) $value;
|
||
}
|
||
}
|