feat: replace the navigation with a dashboard and per-area boards
This commit is contained in:
1 parent
d63df2cc1b
commit
bce8f58be3
26 files changed
+810
-328
No files matched your search
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Booking\BoardPresenter;
|
||||
use App\Booking\DateParam;
|
||||
use App\Booking\TimeRange;
|
||||
use App\Entity\ResourceType;
|
||||
use App\Htmx\HxTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
/**
|
||||
* One booking board per resource kind: a month calendar next to that kind's floor plan.
|
||||
*
|
||||
* The kind is a route parameter rather than a separate controller per kind, so a new
|
||||
* ResourceType case gets a working board without touching this file.
|
||||
*/
|
||||
final class BoardController extends AbstractController
|
||||
{
|
||||
use CurrentUserTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(private readonly BoardPresenter $board)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/buchen/{type}', name: 'app_board', requirements: ['type' => '[a-z]+'], methods: ['GET'])]
|
||||
public function index(Request $request, string $type): Response
|
||||
{
|
||||
$resourceType = $this->resourceType($type);
|
||||
$date = DateParam::parseOrToday($request->query->getString('tag'));
|
||||
|
||||
return $this->render('board/index.html.twig', $this->board->context(
|
||||
$resourceType,
|
||||
$date,
|
||||
self::timeOf($request),
|
||||
$this->currentUser(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Picking a day -- or narrowing the span -- swaps the calendar and the floor plan in one
|
||||
* response.
|
||||
*
|
||||
* This is the whole reason htmxOobResponse exists: two regions, one round trip, no
|
||||
* client-side event choreography. Requests without the htmx header get a plain redirect
|
||||
* rather than a bare pair of fragments, so the URL stays shareable.
|
||||
*/
|
||||
#[Route('/buchen/{type}/tag/{date}', name: 'app_board_day', requirements: ['type' => '[a-z]+', 'date' => DateParam::PATTERN], methods: ['GET'])]
|
||||
public function day(Request $request, string $type, string $date): Response
|
||||
{
|
||||
$resourceType = $this->resourceType($type);
|
||||
$selected = DateParam::parseOrToday($date);
|
||||
$time = self::timeOf($request);
|
||||
|
||||
if (!$request->headers->has('HX-Request')) {
|
||||
return $this->redirectToRoute('app_board', BoardPresenter::urlParameters($resourceType, $selected, $time));
|
||||
}
|
||||
|
||||
return $this->htmxOobResponse(
|
||||
'board/index.html.twig',
|
||||
['calendar', 'floorplan'],
|
||||
$this->board->context($resourceType, $selected, $time, $this->currentUser()),
|
||||
$this->generateUrl('app_board', BoardPresenter::urlParameters($resourceType, $selected, $time)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A malformed or missing span falls back to the whole day rather than erroring: these are
|
||||
* hand-editable query parameters, not route requirements.
|
||||
*/
|
||||
public static function timeOf(Request $request): TimeRange
|
||||
{
|
||||
return TimeRange::parse($request->query->getString('von'), $request->query->getString('bis'))
|
||||
?? TimeRange::wholeDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Route requirements cannot call a method, so the slug is matched loosely and resolved here.
|
||||
*/
|
||||
private function resourceType(string $slug): ResourceType
|
||||
{
|
||||
try {
|
||||
return ResourceType::fromSlug($slug);
|
||||
} catch (\ValueError $e) {
|
||||
throw $this->createNotFoundException($e->getMessage(), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user