feat: add the booking board with floor plan and calendar

This commit is contained in:
fromme committed 2026-09-13 15:18:20 +02:00
1 parent f4741e1bee
commit 59feca2f8e
22 files changed
+1015 -17

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace App\Booking;
use App\Entity\Booking;
use App\Entity\ResourceType;
use App\Entity\User;
use App\Repository\BookingRepository;
use App\Repository\ResourceRepository;
/**
* Builds the context behind the two fragments that make up the booking board: the month
* calendar and the floor plan.
*
* Both are always rendered from the same context so that one htmx round trip can swap both --
* picking a day updates the grid and the plan in a single out-of-band response.
*/
final readonly class BoardPresenter
{
private const WEEKS = 6;
public function __construct(
private BookingRepository $bookings,
private ResourceRepository $resources,
) {
}
/**
* @return array<string, mixed>
*/
public function context(\DateTimeImmutable $selected, User $user): array
{
$selected = $selected->setTime(0, 0);
$today = new \DateTimeImmutable('today');
$monthStart = $selected->modify('first day of this month');
$gridStart = $monthStart->modify('monday this week');
$gridEnd = $gridStart->modify(\sprintf('+%d days', self::WEEKS * 7 - 1));
$counts = $this->bookings->countPerDateInRange($gridStart, $gridEnd);
$mine = [];
foreach ($this->bookings->findForUserInRange($user, $gridStart, $gridEnd) as $booking) {
$mine[$booking->getDate()->format('Y-m-d')][] = $booking;
}
$weeks = [];
$cursor = $gridStart;
for ($week = 0; $week < self::WEEKS; ++$week) {
$days = [];
for ($weekday = 0; $weekday < 7; ++$weekday) {
$key = $cursor->format('Y-m-d');
$days[] = [
'date' => $cursor,
'in_month' => $cursor->format('Y-m') === $monthStart->format('Y-m'),
'is_today' => $cursor == $today,
'is_selected' => $cursor == $selected,
'is_past' => $cursor < $today,
'is_weekend' => (int) $cursor->format('N') >= 6,
'count' => $counts[$key] ?? 0,
'mine' => $mine[$key] ?? [],
];
$cursor = $cursor->modify('+1 day');
}
$weeks[] = $days;
}
$byResource = [];
foreach ($this->bookings->findForDate($selected) as $booking) {
$byResource[$booking->getResource()->getId()] = $booking;
}
return [
'selected_date' => $selected,
'month_start' => $monthStart,
'weeks' => $weeks,
'is_past' => $selected < $today,
'desks' => $this->resources->findActiveByType(ResourceType::DESK),
'parking' => $this->resources->findActiveByType(ResourceType::PARKING),
'bookings_by_resource' => $byResource,
'my_bookings_today' => $this->mineOn($selected, $mine),
];
}
/**
* @param array<string, list<Booking>> $mine
*
* @return list<Booking>
*/
private function mineOn(\DateTimeImmutable $date, array $mine): array
{
return $mine[$date->format('Y-m-d')] ?? [];
}
}