feat: show my bookings on a month calendar

This commit is contained in:
fromme committed 2026-09-21 10:43:37 +02:00
1 parent 5a8227f4d9
commit 4517b84023
12 files changed
+891 -21

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* The six-week skeleton behind a month calendar: always whole weeks, always starting on a Monday.
*
* Six weeks rather than "as many as the month needs" so the grid never changes height as you page
* through it -- a month can span six week rows (a 31-day month starting on a Sunday does), and a
* calendar that grows and shrinks under the cursor is worse than one with a trailing empty row.
*
* This is only the skeleton. Both calendars in this app draw the same grid and then disagree
* entirely about what goes in a cell: the board shows everybody's occupancy for one kind of
* resource plus the day you picked, "Meine Buchungen" shows one person's own bookings across all
* kinds and has no selected day at all. So the payload stays with the caller, which decorates
* these days by MonthDay::key(). gridStart and gridEnd are public precisely so that decoration
* can be one range query over the visible window instead of 42 per-day ones.
*/
final readonly class MonthGrid
{
private const WEEKS = 6;
/**
* @param list<list<MonthDay>> $weeks
*/
private function __construct(
public \DateTimeImmutable $monthStart,
public \DateTimeImmutable $gridStart,
public \DateTimeImmutable $gridEnd,
public array $weeks,
) {
}
/**
* The grid containing $date, with "today" injectable so the flags can be tested without
* waiting for the calendar to roll over. BookingHorizon::latest() takes the same optional
* argument for the same reason.
*/
public static function around(\DateTimeImmutable $date, ?\DateTimeImmutable $today = null): self
{
$today = ($today ?? new \DateTimeImmutable('today'))->setTime(0, 0);
$monthStart = $date->setTime(0, 0)->modify('first day of this month');
// "monday this week" is a no-op when the month already starts on a Monday, so this is the
// whole leading-days calculation -- no weekday arithmetic needed.
$gridStart = $monthStart->modify('monday this week');
$weeks = [];
$cursor = $gridStart;
for ($week = 0; $week < self::WEEKS; ++$week) {
$days = [];
for ($weekday = 0; $weekday < 7; ++$weekday) {
$days[] = new MonthDay(
date: $cursor,
inMonth: $cursor->format('Y-m') === $monthStart->format('Y-m'),
isToday: $cursor == $today,
isPast: $cursor < $today,
isWeekend: (int) $cursor->format('N') >= 6,
);
$cursor = $cursor->modify('+1 day');
}
$weeks[] = $days;
}
// The cursor has already stepped past the grid, hence the day back.
return new self($monthStart, $gridStart, $cursor->modify('-1 day'), $weeks);
}
}