query->getString('tag')); return $this->render('dashboard/index.html.twig', $this->board->context($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('/tag/{date}', name: 'app_dashboard_day', requirements: ['date' => DateParam::PATTERN], methods: ['GET'])] public function day(Request $request, string $date): Response { $selected = DateParam::parseOrToday($date); $time = self::timeOf($request); if (!$request->headers->has('HX-Request')) { return $this->redirectToRoute('app_dashboard', BoardPresenter::urlParameters($selected, $time)); } return $this->htmxOobResponse( 'dashboard/index.html.twig', ['calendar', 'floorplan'], $this->board->context($selected, $time, $this->currentUser()), $this->generateUrl('app_dashboard', BoardPresenter::urlParameters($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(); } }