M2: Status & Wochentage – Schritt 2 (UI): Tagesansicht, htmx-CheckOff/Uncheck-Partials, Archiv-Tab + Feature-Tests
This commit is contained in:
@@ -6,25 +6,30 @@ namespace App\UI\Http\Essen;
|
||||
|
||||
use App\Logic\Essen\Dto\MahlzeitCreateRequest;
|
||||
use App\Logic\Essen\Dto\MahlzeitListRequest;
|
||||
use App\Logic\Essen\Dto\MahlzeitResponse;
|
||||
use App\Logic\Essen\Dto\MahlzeitUpdateRequest;
|
||||
use App\Logic\Essen\Manager\MahlzeitManagerInterface;
|
||||
use App\Logic\Essen\Model\Mahlzeit;
|
||||
use App\Logic\Essen\Model\MahlzeitStatus;
|
||||
use App\Logic\Essen\Model\Wochentag;
|
||||
use App\Logic\Essen\Query\MahlzeitListQuery;
|
||||
use App\Logic\Essen\UseCase\MahlzeitCheckOffUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitCreateUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitDeleteUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitUncheckUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitUpdateUseCase;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* CRUD-Slice "Mahlzeit" (M1) – Web-UI.
|
||||
*
|
||||
* Stil: klassisches POST/Redirect/Flash (htmx-Toggles kommen in M2).
|
||||
* CRUD-Slice "Mahlzeit" (M1) + Status & Tagesansicht (M2) – Web-UI.
|
||||
*
|
||||
* - Reads über `MahlzeitListQuery` / `MahlzeitManagerInterface::find`.
|
||||
* - Writes über die UseCases (Create/Update/Delete).
|
||||
* - Writes über die UseCases (Create/Update/Delete/CheckOff/Uncheck).
|
||||
* - M1: klassisches POST/Redirect/Flash (Anlegen/Bearbeiten/Löschen).
|
||||
* - M2: Check-Off als htmx-Toggle – die Endpunkte liefern nur das geänderte
|
||||
* Mahlzeiten-Partial zurück (Fragment, kein Voll-HTML).
|
||||
* - Syntaktische Pflichtfeld-Prüfung (Name) passiert hier im UI-Layer
|
||||
* (rein PHP, s. architektur.md §1.13); fachliche Invarianten bleiben
|
||||
* im UseCase/Model und laufen zentral über den ExceptionSubscriber.
|
||||
@@ -35,15 +40,30 @@ final class MahlzeitController extends AbstractController
|
||||
private readonly MahlzeitCreateUseCase $create,
|
||||
private readonly MahlzeitUpdateUseCase $update,
|
||||
private readonly MahlzeitDeleteUseCase $delete,
|
||||
private readonly MahlzeitCheckOffUseCase $checkOff,
|
||||
private readonly MahlzeitUncheckUseCase $uncheck,
|
||||
private readonly MahlzeitListQuery $list,
|
||||
private readonly MahlzeitManagerInterface $mahlzeiten,
|
||||
) {}
|
||||
|
||||
/** GET /mahlzeiten — Planungsliste. */
|
||||
/** GET /mahlzeiten — Tagesansicht: alle Mahlzeiten nach Wochentag gruppiert. */
|
||||
public function index(): Response
|
||||
{
|
||||
$mahlzeiten = $this->list->execute(new MahlzeitListRequest())->items;
|
||||
|
||||
return $this->render('mahlzeiten/index.html.twig', [
|
||||
'mahlzeiten' => $this->list->execute(new MahlzeitListRequest())->items,
|
||||
'tage' => $this->gruppierenNachTag($mahlzeiten),
|
||||
'total' => \count($mahlzeiten),
|
||||
]);
|
||||
}
|
||||
|
||||
/** GET /mahlzeiten/archiv — Archiv: gegessene Mahlzeiten. */
|
||||
public function archiv(): Response
|
||||
{
|
||||
$mahlzeiten = $this->list->execute(new MahlzeitListRequest(status: MahlzeitStatus::Gegessen))->items;
|
||||
|
||||
return $this->render('mahlzeiten/archiv.html.twig', [
|
||||
'mahlzeiten' => $mahlzeiten,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -118,6 +138,31 @@ final class MahlzeitController extends AbstractController
|
||||
return $this->redirectToRoute('mahlzeiten_index');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /mahlzeiten/{id}/checkoff — htmx-Partial: abhaken (geplant → gegessen).
|
||||
*
|
||||
* Liefert nur das aktualisierte Mahlzeiten-Partial zurück (Fragment), das
|
||||
* htmx an der Stelle des Klicks austauscht. Wirft 404 bei unbekannter ID.
|
||||
*/
|
||||
public function checkoff(Request $request, int $id): Response
|
||||
{
|
||||
$mahlzeit = $this->checkOff->execute($id);
|
||||
|
||||
return $this->render('mahlzeiten/_mahlzeit.html.twig', ['m' => $mahlzeit]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /mahlzeiten/{id}/uncheck — htmx-Partial: aufheben (gegessen → geplant).
|
||||
*
|
||||
* Liefert nur das aktualisierte Mahlzeiten-Partial zurück (Fragment).
|
||||
*/
|
||||
public function uncheck(Request $request, int $id): Response
|
||||
{
|
||||
$mahlzeit = $this->uncheck->execute($id);
|
||||
|
||||
return $this->render('mahlzeiten/_mahlzeit.html.twig', ['m' => $mahlzeit]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bindet die Formularfelder (Name/Notiz/Wochentage) aus dem Request.
|
||||
*
|
||||
@@ -145,4 +190,35 @@ final class MahlzeitController extends AbstractController
|
||||
'wochentage_choices' => Wochentag::alle(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gruppiert Mahlzeiten nach ihren Wochentagen (Mo…So).
|
||||
*
|
||||
* Eine Mahlzeit mit mehreren Wochentagen erscheint unter jedem dieser Tage.
|
||||
* Mahlzeiten ohne Wochentag landen in der Gruppe "Ohne Tag". Leere Gruppen
|
||||
* werden herausgefiltert; die Reihenfolge folgt Mo…So, danach "Ohne Tag".
|
||||
*
|
||||
* @param list<MahlzeitResponse> $mahlzeiten
|
||||
* @return array<string, list<MahlzeitResponse>>
|
||||
*/
|
||||
private function gruppierenNachTag(array $mahlzeiten): array
|
||||
{
|
||||
$tage = [];
|
||||
foreach (Wochentag::alle() as $tag) {
|
||||
$tage[$tag->name] = [];
|
||||
}
|
||||
$tage['Ohne Tag'] = [];
|
||||
|
||||
foreach ($mahlzeiten as $m) {
|
||||
if ($m->wochentage === []) {
|
||||
$tage['Ohne Tag'][] = $m;
|
||||
continue;
|
||||
}
|
||||
foreach ($m->wochentage as $tag) {
|
||||
$tage[$tag->name][] = $m;
|
||||
}
|
||||
}
|
||||
|
||||
return \array_filter($tage, static fn (array $liste) => $liste !== []);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user