Optimierung 2.2: InteractsWithPressReleaseScheduling-Trait

Die in allen vier PM-Editoren wortgleiche Scheduling-Maschinerie
(updatedPublishMode/ScheduledDate/ScheduledTime, updated, syncScheduledAt,
resolveScheduledAt, validateScheduledAtWhenReady, notifyValidationError)
in einen gemeinsamen Concern ausgelagert (spiegelt InteractsWith-
PressReleaseTags). ~85 Zeilen je Datei entfernt.

Tests: volle Suite gruen (794 passed, 1 skipped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-06-19 08:41:21 +00:00
parent 0344ac26af
commit 64349ce6b4
5 changed files with 149 additions and 479 deletions

View file

@ -0,0 +1,145 @@
<?php
namespace App\Livewire\Concerns;
use Carbon\Carbon;
use Flux\Flux;
use Illuminate\Validation\ValidationException;
/**
* Termin-/Scheduling-Logik der PM-Editoren (Admin + Customer, Erstellen +
* Bearbeiten). Single Source of Truth für die Veröffentlichungsplanung.
*
* Die nutzende Livewire-Komponente muss bereitstellen:
* - string $publishMode ('now' | 'scheduled')
* - ?string $scheduledDate ('Y-m-d')
* - ?string $scheduledTime ('H:i')
* - ?string $scheduledAt ('Y-m-dTH:i', abgeleitet aus Datum + Zeit)
* - array formRules() Validierungsregeln (enthält 'scheduledAt')
*/
trait InteractsWithPressReleaseScheduling
{
public function updatedPublishMode(): void
{
$this->syncScheduledAt();
if ($this->publishMode === 'now') {
$this->scheduledDate = null;
$this->scheduledTime = null;
$this->scheduledAt = null;
$this->resetErrorBag(['scheduledDate', 'scheduledTime', 'scheduledAt']);
}
}
public function updatedScheduledDate(): void
{
$this->syncScheduledAt();
$this->validateScheduledAtWhenReady();
}
public function updatedScheduledTime(): void
{
$this->syncScheduledAt();
$this->validateScheduledAtWhenReady();
}
/**
* Live-Re-Validation: sobald für ein Property bereits ein Error im Bag
* liegt, wird es bei jeder Änderung neu geprüft. Die Termin-Synchronisierung
* liegt vollständig in den spezifischen `updated{PublishMode,ScheduledDate,
* ScheduledTime}`-Hooks; hier bleibt nur die generische Re-Validierung.
*/
public function updated(string $property): void
{
if (! $this->getErrorBag()->has($property)) {
return;
}
try {
$this->validateOnly($property, $this->formRules());
} catch (ValidationException) {
// Feld bleibt invalid — Error-Bag wird automatisch befüllt.
}
}
protected function syncScheduledAt(): void
{
if ($this->publishMode !== 'scheduled') {
$this->scheduledAt = null;
return;
}
if (blank($this->scheduledDate) && blank($this->scheduledTime) && filled($this->scheduledAt)) {
$scheduledAt = Carbon::parse($this->scheduledAt);
$this->scheduledDate = $scheduledAt->format('Y-m-d');
$this->scheduledTime = $scheduledAt->format('H:i');
return;
}
if (blank($this->scheduledDate) || blank($this->scheduledTime)) {
$this->scheduledAt = null;
return;
}
$this->scheduledAt = "{$this->scheduledDate}T{$this->scheduledTime}";
}
/**
* Parst den erfassten Termin in der Anwendungs-Zeitzone (Europe/Berlin).
* Null, wenn kein Termin gesetzt ist.
*/
protected function resolveScheduledAt(): ?Carbon
{
if (blank($this->scheduledAt)) {
return null;
}
return Carbon::parse($this->scheduledAt);
}
protected function validateScheduledAtWhenReady(): void
{
if (blank($this->scheduledAt)) {
return;
}
$this->resetErrorBag('scheduledAt');
$scheduledAt = $this->resolveScheduledAt();
if ($scheduledAt === null || $scheduledAt->lessThanOrEqualTo(now()->addMinutes(5))) {
$this->addError('scheduledAt', __('Der Veröffentlichungstermin muss mindestens 5 Minuten in der Zukunft liegen.'));
return;
}
try {
$this->validateOnly('scheduledAt', $this->formRules());
} catch (ValidationException) {
// Termin bleibt invalid — Error-Bag wird automatisch befüllt.
}
}
/**
* Toast mit Sammelhinweis nach fehlgeschlagener Validierung. Die einzelnen
* Feld-Errors werden weiterhin direkt am Input angezeigt.
*/
protected function notifyValidationError(?ValidationException $exception = null): void
{
$count = $exception
? array_sum(array_map('count', $exception->errors()))
: count($this->getErrorBag()->all());
Flux::toast(
heading: __('Bitte Eingaben prüfen'),
text: $count > 1
? __(':count Felder benötigen deine Aufmerksamkeit.', ['count' => $count])
: __('Ein Feld benötigt deine Aufmerksamkeit.'),
variant: 'danger',
duration: 6000,
);
}
}