InitTrait
Prepares the data of both wizard states (month grid / day slots) and the header with the public calendar switcher.
IMPORTANT: day clickability and the trim of today by leadTimeHours are computed here on the
server, not in Latte or JS; the template only renders the ready bookable flag.
Table of Contents
Constants
- MONTH_KEYS : array<string|int, mixed> = ['01' => 'jan', '02' => 'feb', '03' => 'mar', '...
- `MM` mapped to a short key under `calendar.months.*` (shared with the old component, see `front.*.neon`).
- WEEKDAY_KEYS : array<string|int, mixed> = [1 => 'mon', 2 => 'tue', 3 => 'wed', 4 => 'thu'...
- ISO day number (`N`) mapped to a short key under `calendar.weekdays*.*`.
Methods
- bookableStarts() : array<int, array{from: string, to: string, maxTo: string}>
- Returns the offered day starts after the lead-time trim. The Api already trims them (free time is cut from the left by `now + leadTimeHours`), but the front end does not rely on it: time can pass between computing and rendering (cache, long request) and offering a slot the lead time no longer allows would mean a click into an error.
- buildWeeks() : array<int, array<int, array{date: string, dayNumber: int, weekdayKey: string, state: string, freeMinutesLabel: string|null, isPast: bool, isToday: bool, bookable: bool, startCount: int}|null>>
- Builds the month grid by weeks (Monday first); a `null` cell is padding outside the month and is never clickable.
- currentMonth() : string
- fetchAvailability() : array<string, mixed>
- Returns {@see \App\UI\Front\Company\Models\Managers\TimeSlotManager::availability()} memoized for the lifetime of this component instance (one request), keyed by month and calendar.
- findCalendar() : array{id: int, name: string, description: string|null}|null
- formatDayHeading() : string
- Formats `YYYY-MM-DD` as e.g. "Thursday, September 10, 2026" by the `day.heading` pattern of the language.
- formatMinutes() : string
- Minutes since midnight to `H:MM` (same as the Api `TimeSlotManager::formatMinutes()`, without a cross-module import).
- isDayBookable() : bool
- Applies the day clickability rules: past days are not bookable, `closed` or without starts are not, today only when at least one start remains after the `leadTimeHours` trim. Padding is handled by the template (a `null` cell) and never gets here.
- prepareWizard() : void
- resolveCalendarId() : int
- Resolves the calendar id. IMPORTANT: the persistent `calendarId` from the URL is not trusted; only values from the list of public calendars of the presented company are valid. A foreign, non-public or nonsensical id falls back to the first public calendar (and `0` when the company has none).
- resolveDay() : string|null
- Accepts the chosen day only when it belongs to the displayed month, exists in the Api response and is bookable; otherwise the wizard returns to the "month" state. Without this check a hand-edited URL could open the slot list of a past or closed day.
- resolveMonth() : string
- `YYYY-MM`; empty, invalid or past input falls back to the server's current month.
- shiftMonth() : string
Constants
MONTH_KEYS
`MM` mapped to a short key under `calendar.months.*` (shared with the old component, see `front.*.neon`).
private
array<string|int, mixed>
MONTH_KEYS
= ['01' => 'jan', '02' => 'feb', '03' => 'mar', '04' => 'apr', '05' => 'may', '06' => 'jun', '07' => 'jul', '08' => 'aug', '09' => 'sep', '10' => 'oct', '11' => 'nov', '12' => 'dec']
WEEKDAY_KEYS
ISO day number (`N`) mapped to a short key under `calendar.weekdays*.*`.
private
array<string|int, mixed>
WEEKDAY_KEYS
= [1 => 'mon', 2 => 'tue', 3 => 'wed', 4 => 'thu', 5 => 'fri', 6 => 'sat', 7 => 'sun']
Methods
bookableStarts()
Returns the offered day starts after the lead-time trim. The Api already trims them (free time is cut from the left by `now + leadTimeHours`), but the front end does not rely on it: time can pass between computing and rendering (cache, long request) and offering a slot the lead time no longer allows would mean a click into an error.
private
static bookableStarts(string $date, mixed $dayData, int $leadTimeHours) : array<int, array{from: string, to: string, maxTo: string}>
Parameters
- $date : string
- $dayData : mixed
-
raw day payload from the fail-soft Api response (can be anything)
- $leadTimeHours : int
Return values
array<int, array{from: string, to: string, maxTo: string}>buildWeeks()
Builds the month grid by weeks (Monday first); a `null` cell is padding outside the month and is never clickable.
private
static buildWeeks(string $month, array<string, mixed> $days, int $leadTimeHours) : array<int, array<int, array{date: string, dayNumber: int, weekdayKey: string, state: string, freeMinutesLabel: string|null, isPast: bool, isToday: bool, bookable: bool, startCount: int}|null>>
Parameters
- $month : string
- $days : array<string, mixed>
- $leadTimeHours : int
-
trim of today; computed here, not in Latte
Return values
array<int, array<int, array{date: string, dayNumber: int, weekdayKey: string, state: string, freeMinutesLabel: string|null, isPast: bool, isToday: bool, bookable: bool, startCount: int}|null>>currentMonth()
private
static currentMonth() : string
Return values
stringfetchAvailability()
Returns {@see \App\UI\Front\Company\Models\Managers\TimeSlotManager::availability()} memoized for the lifetime of this component instance (one request), keyed by month and calendar.
private
fetchAvailability(string $month, int $calendarId) : array<string, mixed>
Parameters
- $month : string
- $calendarId : int
Return values
array<string, mixed>findCalendar()
private
findCalendar(array<int, array{id: int, name: string, description: string|null}> $calendars, int $calendarId) : array{id: int, name: string, description: string|null}|null
Parameters
- $calendars : array<int, array{id: int, name: string, description: string|null}>
- $calendarId : int
Return values
array{id: int, name: string, description: string|null}|nullformatDayHeading()
Formats `YYYY-MM-DD` as e.g. "Thursday, September 10, 2026" by the `day.heading` pattern of the language.
private
formatDayHeading(string $day) : string
IMPORTANT: built in PHP because both variable parts are translations (calendar.weekdaysLong.*,
calendar.monthsInflected.*) and the order differs per language; in Latte this would need a
nested {_} inside a {_} parameter.
Parameters
- $day : string
Return values
stringformatMinutes()
Minutes since midnight to `H:MM` (same as the Api `TimeSlotManager::formatMinutes()`, without a cross-module import).
private
static formatMinutes(int $minutes) : string
Parameters
- $minutes : int
Return values
stringisDayBookable()
Applies the day clickability rules: past days are not bookable, `closed` or without starts are not, today only when at least one start remains after the `leadTimeHours` trim. Padding is handled by the template (a `null` cell) and never gets here.
private
static isDayBookable(string $date, mixed $dayData, int $leadTimeHours) : bool
Parameters
- $date : string
- $dayData : mixed
-
raw day payload from the fail-soft Api response (can be anything)
- $leadTimeHours : int
Return values
boolprepareWizard()
private
prepareWizard() : void
resolveCalendarId()
Resolves the calendar id. IMPORTANT: the persistent `calendarId` from the URL is not trusted; only values from the list of public calendars of the presented company are valid. A foreign, non-public or nonsensical id falls back to the first public calendar (and `0` when the company has none).
private
resolveCalendarId(array<int, array{id: int, name: string, description: string|null}> $calendars) : int
Parameters
- $calendars : array<int, array{id: int, name: string, description: string|null}>
Return values
intresolveDay()
Accepts the chosen day only when it belongs to the displayed month, exists in the Api response and is bookable; otherwise the wizard returns to the "month" state. Without this check a hand-edited URL could open the slot list of a past or closed day.
private
resolveDay(array<string, mixed> $days, int $leadTimeHours) : string|null
Parameters
- $days : array<string, mixed>
- $leadTimeHours : int
Return values
string|nullresolveMonth()
`YYYY-MM`; empty, invalid or past input falls back to the server's current month.
private
static resolveMonth(string|null $month) : string
Parameters
- $month : string|null
Return values
stringshiftMonth()
private
static shiftMonth(string $month, int $delta) : string
Parameters
- $month : string
- $delta : int