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>
211 lines
8.6 KiB
PHP
211 lines
8.6 KiB
PHP
<?php
|
||
|
||
namespace App\Livewire\Admin\Cms;
|
||
|
||
use App\Models\CabinetTabletSetting;
|
||
use Livewire\Component;
|
||
|
||
class CabinetInfoTablet extends Component
|
||
{
|
||
// Store status mode
|
||
public string $storeStatus = 'auto';
|
||
|
||
public string $noticeHeadline = '';
|
||
|
||
public string $noticeSubtext = '';
|
||
|
||
// Override times for today
|
||
public ?string $overrideOpenToday = '';
|
||
|
||
public ?string $overrideCloseToday = '';
|
||
|
||
// Appointment
|
||
public ?string $nextAppointmentDate = null;
|
||
|
||
public ?string $nextAppointmentTime = '';
|
||
|
||
// Structured opening hours per weekday (open + close, empty = closed)
|
||
public ?string $hoursMondayOpen = '10:00';
|
||
|
||
public ?string $hoursMondayClose = '18:00';
|
||
|
||
public ?string $hoursTuesdayOpen = '10:00';
|
||
|
||
public ?string $hoursTuesdayClose = '18:00';
|
||
|
||
public ?string $hoursWednesdayOpen = '10:00';
|
||
|
||
public ?string $hoursWednesdayClose = '18:00';
|
||
|
||
public ?string $hoursThursdayOpen = '10:00';
|
||
|
||
public ?string $hoursThursdayClose = '18:00';
|
||
|
||
public ?string $hoursFridayOpen = '10:00';
|
||
|
||
public ?string $hoursFridayClose = '18:00';
|
||
|
||
public ?string $hoursSaturdayOpen = '10:00';
|
||
|
||
public ?string $hoursSaturdayClose = '14:00';
|
||
|
||
public ?string $hoursSundayOpen = '';
|
||
|
||
public ?string $hoursSundayClose = '';
|
||
|
||
// Contact
|
||
public string $contactPhone = '';
|
||
|
||
public string $contactEmail = '';
|
||
|
||
public function mount(): void
|
||
{
|
||
$s = CabinetTabletSetting::current();
|
||
|
||
$this->storeStatus = $s->store_status ?? 'auto';
|
||
$this->noticeHeadline = $s->notice_headline ?? '';
|
||
$this->noticeSubtext = $s->notice_subtext ?? '';
|
||
$this->overrideOpenToday = $s->override_open_today ?? '';
|
||
$this->overrideCloseToday = $s->override_close_today ?? '';
|
||
$this->nextAppointmentDate = $s->next_appointment_date?->format('Y-m-d');
|
||
$this->nextAppointmentTime = $s->next_appointment_time ?? '';
|
||
$this->hoursMondayOpen = $s->hours_monday_open ?? '';
|
||
$this->hoursMondayClose = $s->hours_monday_close ?? '';
|
||
$this->hoursTuesdayOpen = $s->hours_tuesday_open ?? '';
|
||
$this->hoursTuesdayClose = $s->hours_tuesday_close ?? '';
|
||
$this->hoursWednesdayOpen = $s->hours_wednesday_open ?? '';
|
||
$this->hoursWednesdayClose = $s->hours_wednesday_close ?? '';
|
||
$this->hoursThursdayOpen = $s->hours_thursday_open ?? '';
|
||
$this->hoursThursdayClose = $s->hours_thursday_close ?? '';
|
||
$this->hoursFridayOpen = $s->hours_friday_open ?? '';
|
||
$this->hoursFridayClose = $s->hours_friday_close ?? '';
|
||
$this->hoursSaturdayOpen = $s->hours_saturday_open ?? '';
|
||
$this->hoursSaturdayClose = $s->hours_saturday_close ?? '';
|
||
$this->hoursSundayOpen = $s->hours_sunday_open ?? '';
|
||
$this->hoursSundayClose = $s->hours_sunday_close ?? '';
|
||
$this->contactPhone = $s->contact_phone ?? '';
|
||
$this->contactEmail = $s->contact_email ?? '';
|
||
}
|
||
|
||
private function timeRule(): array
|
||
{
|
||
return ['nullable', 'string', 'regex:/^(\d{2}:\d{2})?$/'];
|
||
}
|
||
|
||
private function toNullIfEmpty(?string $value): ?string
|
||
{
|
||
return $value !== null && trim($value) !== '' ? trim($value) : null;
|
||
}
|
||
|
||
/**
|
||
* Zeit-Properties, die beim Speichern aus dem DOM uebernommen werden duerfen.
|
||
*
|
||
* @return array<int, string>
|
||
*/
|
||
private function hourProperties(): array
|
||
{
|
||
return [
|
||
'hoursMondayOpen', 'hoursMondayClose',
|
||
'hoursTuesdayOpen', 'hoursTuesdayClose',
|
||
'hoursWednesdayOpen', 'hoursWednesdayClose',
|
||
'hoursThursdayOpen', 'hoursThursdayClose',
|
||
'hoursFridayOpen', 'hoursFridayClose',
|
||
'hoursSaturdayOpen', 'hoursSaturdayClose',
|
||
'hoursSundayOpen', 'hoursSundayClose',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Der Flux-Zeit-Picker meldet das Leeren per X nicht zuverlaessig an wire:model.
|
||
* Deshalb liest das Formular die Werte beim Absenden aus dem DOM und uebergibt sie hier.
|
||
* Nur die Zeitfelder duerfen so gesetzt werden – alles andere kommt aus wire:model.
|
||
*
|
||
* @param array<string, string|null> $hours Zeit-Picker-Werte aus dem DOM
|
||
*/
|
||
public function save(array $hours = []): void
|
||
{
|
||
foreach ($this->hourProperties() as $prop) {
|
||
if (array_key_exists($prop, $hours)) {
|
||
$this->{$prop} = is_string($hours[$prop]) ? $hours[$prop] : '';
|
||
}
|
||
}
|
||
|
||
$timeRule = $this->timeRule();
|
||
|
||
$this->validate([
|
||
'storeStatus' => 'required|in:auto,notice,warning,closed',
|
||
'noticeHeadline' => 'nullable|string|max:40',
|
||
'noticeSubtext' => 'nullable|string|max:80',
|
||
'overrideOpenToday' => $timeRule,
|
||
'overrideCloseToday' => $timeRule,
|
||
'nextAppointmentDate' => 'nullable|date',
|
||
'nextAppointmentTime' => $timeRule,
|
||
'hoursMondayOpen' => $timeRule,
|
||
'hoursMondayClose' => $timeRule,
|
||
'hoursTuesdayOpen' => $timeRule,
|
||
'hoursTuesdayClose' => $timeRule,
|
||
'hoursWednesdayOpen' => $timeRule,
|
||
'hoursWednesdayClose' => $timeRule,
|
||
'hoursThursdayOpen' => $timeRule,
|
||
'hoursThursdayClose' => $timeRule,
|
||
'hoursFridayOpen' => $timeRule,
|
||
'hoursFridayClose' => $timeRule,
|
||
'hoursSaturdayOpen' => $timeRule,
|
||
'hoursSaturdayClose' => $timeRule,
|
||
'hoursSundayOpen' => $timeRule,
|
||
'hoursSundayClose' => $timeRule,
|
||
'contactPhone' => 'nullable|string|max:50',
|
||
'contactEmail' => 'nullable|email|max:100',
|
||
], [
|
||
'storeStatus.required' => 'Der Store-Status ist erforderlich.',
|
||
'storeStatus.in' => 'Ungültiger Status. Erlaubt: auto, notice, warning, closed.',
|
||
'noticeHeadline.max' => 'Die Headline darf maximal 40 Zeichen haben.',
|
||
'noticeSubtext.max' => 'Der Subtext darf maximal 80 Zeichen haben.',
|
||
'overrideOpenToday.regex' => 'Bitte im Format HH:MM eingeben.',
|
||
'overrideCloseToday.regex' => 'Bitte im Format HH:MM eingeben.',
|
||
'nextAppointmentTime.regex' => 'Bitte im Format HH:MM eingeben.',
|
||
'contactEmail.email' => 'Bitte eine gültige E-Mail-Adresse eingeben.',
|
||
]);
|
||
CabinetTabletSetting::current()->update([
|
||
'store_status' => $this->storeStatus,
|
||
'notice_headline' => $this->toNullIfEmpty($this->noticeHeadline),
|
||
'notice_subtext' => $this->toNullIfEmpty($this->noticeSubtext),
|
||
'override_open_today' => $this->toNullIfEmpty($this->overrideOpenToday),
|
||
'override_close_today' => $this->toNullIfEmpty($this->overrideCloseToday),
|
||
'next_appointment_date' => $this->toNullIfEmpty($this->nextAppointmentDate),
|
||
'next_appointment_time' => $this->toNullIfEmpty($this->nextAppointmentTime),
|
||
'hours_monday_open' => $this->toNullIfEmpty($this->hoursMondayOpen),
|
||
'hours_monday_close' => $this->toNullIfEmpty($this->hoursMondayClose),
|
||
'hours_tuesday_open' => $this->toNullIfEmpty($this->hoursTuesdayOpen),
|
||
'hours_tuesday_close' => $this->toNullIfEmpty($this->hoursTuesdayClose),
|
||
'hours_wednesday_open' => $this->toNullIfEmpty($this->hoursWednesdayOpen),
|
||
'hours_wednesday_close' => $this->toNullIfEmpty($this->hoursWednesdayClose),
|
||
'hours_thursday_open' => $this->toNullIfEmpty($this->hoursThursdayOpen),
|
||
'hours_thursday_close' => $this->toNullIfEmpty($this->hoursThursdayClose),
|
||
'hours_friday_open' => $this->toNullIfEmpty($this->hoursFridayOpen),
|
||
'hours_friday_close' => $this->toNullIfEmpty($this->hoursFridayClose),
|
||
'hours_saturday_open' => $this->toNullIfEmpty($this->hoursSaturdayOpen),
|
||
'hours_saturday_close' => $this->toNullIfEmpty($this->hoursSaturdayClose),
|
||
'hours_sunday_open' => $this->toNullIfEmpty($this->hoursSundayOpen),
|
||
'hours_sunday_close' => $this->toNullIfEmpty($this->hoursSundayClose),
|
||
'contact_phone' => $this->toNullIfEmpty($this->contactPhone),
|
||
'contact_email' => $this->toNullIfEmpty($this->contactEmail),
|
||
]);
|
||
|
||
session()->flash('success', 'Info-Tablet Einstellungen gespeichert!');
|
||
}
|
||
|
||
public function clearOverrides(): void
|
||
{
|
||
CabinetTabletSetting::current()->clearOverrides();
|
||
$this->overrideOpenToday = '';
|
||
$this->overrideCloseToday = '';
|
||
|
||
session()->flash('success', 'Sonderöffnungszeiten wurden zurückgesetzt!');
|
||
}
|
||
|
||
public function render(): \Illuminate\View\View
|
||
{
|
||
return view('livewire.admin.cms.cabinet-info-tablet');
|
||
}
|
||
}
|