BookingWizard
extends BaseComponent
in package
uses
InitTrait, SignalTrait, ModalTrait, FormTrait
Booking wizard: the successor of {@see \App\UI\Front\Company\Components\BookingCalendarComponent\BookingCalendar} on the new "book a time slot" page.
IMPORTANT: the old component and its page stay unchanged; this one lives next to it and
shares only the SCSS file _booking_calendar.scss (extended additively).
Two states, not two steps below each other: month (day grid) and day (list of the day's
slots) are persistent parameters and the day replaces the month: after a click on a day the
grid is hidden and a back arrow appears on top.
The visitor chooses a calendar (the purpose, e.g. "Shop" / "Service"), not a service or worker.
The switcher in the header is rendered only when there is more than one public calendar.
Presentation names and descriptions come from CalendarText (fallback to the operational
calendars.name) and are filtered to public = 1; the Api does both
(CalendarManager::findPublicPresentation()).
Data is read fail-soft through TimeSlotManager::availability() (an Api error gives an empty month, not a crash). Writing happens in the booking modal through ReservationManager::book(); the component fetches the manager only together with the form, to hold no unused dependency.
IMPORTANT: public availability never carries the identity of foreign reservations, so the
component renders only starts, never busy/confirmed/pending blocks.
Table of Contents
Constants
- ERROR_MESSAGE_KEYS : array<string|int, mixed> = [ 2 => 'form.messages.errors.validationFailed',...
- `ResultCode->value` (Api) mapped to the translated message key; an unknown code gives the generic message.
- MODAL_TARGET : string = 'companyBookingWizardModal'
- The only target of the wizard modal; the calendar and the form share the same window.
- 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*.*`.
Properties
- $calendarId : int
- Chosen public calendar; `0` means the first public calendar of the company (see `resolveCalendarId()`).
- $container : Container
- $day : string|null
- Chosen day `YYYY-MM-DD`; `null` means the "month" state (grid).
- $inModal : bool
- Render mode: `false` = inline (the wizard is directly in the page), `true` = modal (the wizard lives in a modal window). Persistent so links inside the modal carry it; `render()` takes no arguments.
- $month : string
- Displayed month `YYYY-MM`; empty means the server's current month (see `resolveMonth()`).
- $routerDispatcher : RouterDispatcher
- $slotFrom : string|null
- Start `HH:MM` of the tapped slot; `null` means no slot chosen. Persistent because the booking form in the modal is submitted by another request and must know which block it came from (the server verifies the pair against `dayStarts` anyway; the client is not trusted).
- $slotTo : string|null
- End `HH:MM` of the tapped slot; prefills `timeTo` (the `maxTo` cap is handled by `FormTrait`).
- $translator : ITranslator
- $appContext : AppContext
- $componentParameters : array<string, mixed>
- $currencyContext : CurrencyContext|null
- $deviceDetector : DeviceDetector|null
- $languageContext : LanguageContext|null
- $localizationContext : LocalizationContext|null
- $settingContext : SettingContext
- $themeContext : ThemeContext|null
- $translatorDomain : string|null
- $availabilityCache : array<string, array<string, mixed>>
- Memoizes {@see TimeSlotManager::availability()} within one request, keyed by `month|calendar`; the component asks at most twice per request (the default calendar for the list of public calendars plus the chosen calendar).
- $bookingMessage : string|null
- Message printed inside the modal (not through the session flash, which would duplicate the inline output on the page).
- $bookingMessageType : string
- $dayStarts : array<int, array{from: string, to: string, maxTo: string}>
- Offered starts of the chosen day after the server-side lead-time trim. Computed by `InitTrait::prepareWizard()` and read by the slot-choice signal too, to verify the `(from, to)` pair sent by the client against server data, not against the template.
- $eventDispatcher : EventDispatcher
- $modalRegistry : ModalRegistry
- $reservationManager : ReservationManager
- $slotLengthMinutes : int
- Slot length of the chosen calendar in minutes; the step of the end offer in the booking form.
- $timeSlotManager : TimeSlotManager
Methods
- __call() : mixed
- Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
- __construct() : mixed
- createComponent() : IComponent|null
- getAppContext() : AppContext
- getComponentParameter() : mixed
- Returns a component parameter, or null when it is not set.
- getContainer() : Container
- getCurrencyContext() : CurrencyContext|null
- getLanguageContext() : LanguageContext|null
- getLocalizationContext() : LocalizationContext|null
- getRouterDispatcher() : RouterDispatcher
- getSettingContext() : SettingContext
- getSettings() : array<int, array<string, mixed>>
- getThemeContext() : ThemeContext|null
- handleBackToCalendar() : void
- Back arrow from the booking form to the calendar (modal mode only).
- handleBackToMonth() : void
- handleChangeMonth() : void
- handleSelectCalendar() : void
- handleSelectDay() : void
- handleSelectSlot() : void
- Handles a click on a slot block: opens the booking modal with the recap and the form.
- handleShowCalendarModal() : void
- Handles the "book a time slot" button on the "my time slots" page: opens the wizard (month grid) in a modal window. From now on the component is in modal mode and all its links carry it in the persistent `inModal`.
- hasServiceByType() : bool
- Returns whether the container has a service of the given type.
- initializeAllContexts() : void
- injectDeviceDetector() : void
- onBookingFormError() : void
- Validation errors (missing consent, empty name...); the modal stays open with the form.
- onBookingFormSuccess() : void
- onCreate() : void
- render() : void
- IMPORTANT: takes no arguments. The context (mode, calendar) belongs in persistent parameters and signals, otherwise an AJAX redraw of the snippet would render nothing.
- renderButton() : void
- Second render mode: on the "my time slots" page the component renders only the "book a time slot" button; the whole wizard opens in a modal ({@see ModalTrait::handleShowCalendarModal()}).
- setAppContext() : $this
- setComponentParameter() : void
- Sets a component parameter.
- setCurrencyContext() : $this
- setLanguageContext() : $this
- setLocalizationContext() : $this
- setSettingContext() : $this
- setThemeContext() : $this
- setup() : void
- setupAppContext() : void
- typedComponent() : T
- Returns a subcomponent with a verified type.
- createComponentBookingForm() : Form
- frontPresenter() : BasePresenter|null
- Returns the front presenter the component is attached to (null outside the front end or when detached).
- getTranslator() : ITranslator
- setTemplateFile() : void
- setTranslator() : self
- 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.
- buildEndOptions() : array<string, string>
- Returns the ends: the union of reachable ends over all starts of the day, in steps of `slotLengthMinutes`, capped by `maxTo`. Not authoritative; the server verifies the pair again.
- buildStartOptions() : array<string, string>
- Returns the starts: the server-trimmed `starts` of the chosen day ({@see InitTrait::bookableStarts()}).
- 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.
- createFormContent() : string
- Second view: recap + booking form.
- createModalEvent() : ModalWindowEvent
- createMultiplierComponent() : Multiplier<string|int, IComponent>
- Creates a multiplier for the component.
- createNewComponentInstance() : IComponent|null
- Creates a new component instance without adding it to the presenter; used by the multiplier.
- createResultContent() : string
- Second view after submit: the result message instead of the form (the form is no longer offered).
- createStandardComponent() : IComponent|null
- Creates a standard component (not a multiplier).
- createWizardContent() : string
- First view: the same wizard (header + month/day) as in inline mode, rendered into a string.
- 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
- finishBooking() : void
- Success (also the silent honeypot one); the message stays in the modal and the calendar below is redrawn.
- 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).
- formatTime() : string
- Minutes since midnight to `HH:MM` (the end offer must match `starts` in shape, i.e. with a leading zero).
- 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.
- openOrUpdateModal() : void
- Opens the modal or, when it already exists in the registry, only replaces its content.
- prefillIdentity() : void
- prepareFormTemplate() : void
- Recap data and messages for `Sections/Form.latte`.
- prepareWizard() : void
- refreshWizardAfterBooking() : void
- Second snippet next to the modal: in inline mode the redrawn calendar on the booking page, in modal mode the client's reservation list on the "my time slots" page (a new one was added).
- refreshWizardView() : void
- Redraws the wizard after a navigation signal: in inline mode through the `wizard` snippet, in modal mode by replacing the modal content.
- renderPartial() : string
- Renders a partial into a string (like `WatchdogFormComponent\Traits\ModalTrait`).
- 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.
- resolveErrorMessageKey() : string
- An unknown or missing code gives the generic message.
- resolveMonth() : string
- `YYYY-MM`; empty, invalid or past input falls back to the server's current month.
- shiftMonth() : string
- str() : string
- `ArrayHash` values are `mixed`; safely narrows to string (a non-string becomes empty).
- toMinutes() : int
Constants
ERROR_MESSAGE_KEYS
`ResultCode->value` (Api) mapped to the translated message key; an unknown code gives the generic message.
private
array<string|int, mixed>
ERROR_MESSAGE_KEYS
= [
2 => 'form.messages.errors.validationFailed',
// occupied, in the past or lead time
4 => 'form.messages.errors.notAvailable',
303 => 'form.messages.errors.dbDuplicateEntry',
304 => 'form.messages.errors.dbLocked',
404 => 'form.messages.errors.rateLimitExceeded',
]
MODAL_TARGET
The only target of the wizard modal; the calendar and the form share the same window.
private
string
MODAL_TARGET
= 'companyBookingWizardModal'
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']
Properties
$calendarId
Chosen public calendar; `0` means the first public calendar of the company (see `resolveCalendarId()`).
public
int
$calendarId
= 0
Attributes
- #[Persistent]
$container
public
Container
$container
Attributes
- #[Inject]
$day
Chosen day `YYYY-MM-DD`; `null` means the "month" state (grid).
public
string|null
$day
= null
Attributes
- #[Persistent]
$inModal
Render mode: `false` = inline (the wizard is directly in the page), `true` = modal (the wizard lives in a modal window). Persistent so links inside the modal carry it; `render()` takes no arguments.
public
bool
$inModal
= false
Attributes
- #[Persistent]
$month
Displayed month `YYYY-MM`; empty means the server's current month (see `resolveMonth()`).
public
string
$month
= ''
Attributes
- #[Persistent]
$routerDispatcher
public
RouterDispatcher
$routerDispatcher
$slotFrom
Start `HH:MM` of the tapped slot; `null` means no slot chosen. Persistent because the booking form in the modal is submitted by another request and must know which block it came from (the server verifies the pair against `dayStarts` anyway; the client is not trusted).
public
string|null
$slotFrom
= null
Attributes
- #[Persistent]
$slotTo
End `HH:MM` of the tapped slot; prefills `timeTo` (the `maxTo` cap is handled by `FormTrait`).
public
string|null
$slotTo
= null
Attributes
- #[Persistent]
$translator
public
ITranslator
$translator
Tags
$appContext
protected
AppContext
$appContext
$componentParameters
protected
array<string, mixed>
$componentParameters
= []
component parameters set by setComponentParameter()
$currencyContext
protected
CurrencyContext|null
$currencyContext
= NULL
$deviceDetector
protected
DeviceDetector|null
$deviceDetector
= NULL
$languageContext
protected
LanguageContext|null
$languageContext
= NULL
$localizationContext
protected
LocalizationContext|null
$localizationContext
= NULL
$settingContext
protected
SettingContext
$settingContext
$themeContext
protected
ThemeContext|null
$themeContext
= NULL
$translatorDomain
protected
string|null
$translatorDomain
= 'front.company.components.bookingWizard'
$availabilityCache
Memoizes {@see TimeSlotManager::availability()} within one request, keyed by `month|calendar`; the component asks at most twice per request (the default calendar for the list of public calendars plus the chosen calendar).
private
array<string, array<string, mixed>>
$availabilityCache
= []
The return type is deliberately loose: availability is fail-soft and consumers guard the keys.
$bookingMessage
Message printed inside the modal (not through the session flash, which would duplicate the inline output on the page).
private
string|null
$bookingMessage
= null
$bookingMessageType
private
string
$bookingMessageType
= 'alert-info'
$dayStarts
Offered starts of the chosen day after the server-side lead-time trim. Computed by `InitTrait::prepareWizard()` and read by the slot-choice signal too, to verify the `(from, to)` pair sent by the client against server data, not against the template.
private
array<int, array{from: string, to: string, maxTo: string}>
$dayStarts
= []
$eventDispatcher read-only
private
EventDispatcher
$eventDispatcher
$modalRegistry read-only
private
ModalRegistry
$modalRegistry
$reservationManager read-only
private
ReservationManager
$reservationManager
$slotLengthMinutes
Slot length of the chosen calendar in minutes; the step of the end offer in the booking form.
private
int
$slotLengthMinutes
= 0
$timeSlotManager read-only
private
TimeSlotManager
$timeSlotManager
Methods
__call()
Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
public
__call(string $name, array<string|int, mixed> $args) : mixed
Called only when the component has no renderXyz() method of its own.
Parameters
- $name : string
- $args : array<string|int, mixed>
__construct()
public
__construct(TimeSlotManager $timeSlotManager, ReservationManager $reservationManager, ModalRegistry $modalRegistry, EventDispatcher $eventDispatcher) : mixed
Parameters
- $timeSlotManager : TimeSlotManager
- $reservationManager : ReservationManager
- $modalRegistry : ModalRegistry
- $eventDispatcher : EventDispatcher
createComponent()
public
createComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nullgetAppContext()
public
getAppContext() : AppContext
Return values
AppContextgetComponentParameter()
Returns a component parameter, or null when it is not set.
public
getComponentParameter(string $name) : mixed
Parameters
- $name : string
getContainer()
public
getContainer() : Container
Return values
ContainergetCurrencyContext()
public
getCurrencyContext() : CurrencyContext|null
Return values
CurrencyContext|nullgetLanguageContext()
public
getLanguageContext() : LanguageContext|null
Return values
LanguageContext|nullgetLocalizationContext()
public
getLocalizationContext() : LocalizationContext|null
Return values
LocalizationContext|nullgetRouterDispatcher()
public
getRouterDispatcher() : RouterDispatcher
Return values
RouterDispatchergetSettingContext()
public
getSettingContext() : SettingContext
Return values
SettingContextgetSettings()
public
getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>getThemeContext()
public
getThemeContext() : ThemeContext|null
Return values
ThemeContext|nullhandleBackToCalendar()
Back arrow from the booking form to the calendar (modal mode only).
public
handleBackToCalendar() : void
handleBackToMonth()
public
handleBackToMonth() : void
handleChangeMonth()
public
handleChangeMonth(string $month) : void
Parameters
- $month : string
handleSelectCalendar()
public
handleSelectCalendar(int $calendarId) : void
Parameters
- $calendarId : int
handleSelectDay()
public
handleSelectDay(string $day) : void
Parameters
- $day : string
handleSelectSlot()
Handles a click on a slot block: opens the booking modal with the recap and the form.
public
handleSelectSlot(string $from, string $to) : void
IMPORTANT: the (from, to) pair is verified against the server-computed starts of the chosen
day; the client is not trusted even here. An invalid pair does not open the modal, it only says
the slot is gone.
Parameters
- $from : string
- $to : string
handleShowCalendarModal()
Handles the "book a time slot" button on the "my time slots" page: opens the wizard (month grid) in a modal window. From now on the component is in modal mode and all its links carry it in the persistent `inModal`.
public
handleShowCalendarModal() : void
hasServiceByType()
Returns whether the container has a service of the given type.
public
hasServiceByType(string $className) : bool
The name is composed dynamically (module plus component), so class_exists() runs first, and
a true result guarantees a valid type.
Parameters
- $className : string
Tags
Return values
boolinitializeAllContexts()
public
initializeAllContexts() : void
injectDeviceDetector()
public
injectDeviceDetector(DeviceDetector $detector) : void
Parameters
- $detector : DeviceDetector
onBookingFormError()
Validation errors (missing consent, empty name...); the modal stays open with the form.
public
onBookingFormError(Form $form) : void
Parameters
- $form : Form
onBookingFormSuccess()
public
onBookingFormSuccess(Form $form, ArrayHash $values) : void
Parameters
- $form : Form
- $values : ArrayHash
onCreate()
public
onCreate() : void
render()
IMPORTANT: takes no arguments. The context (mode, calendar) belongs in persistent parameters and signals, otherwise an AJAX redraw of the snippet would render nothing.
public
render() : void
renderButton()
Second render mode: on the "my time slots" page the component renders only the "book a time slot" button; the whole wizard opens in a modal ({@see ModalTrait::handleShowCalendarModal()}).
public
renderButton() : void
The grid is deliberately not built here (no prepareWizard()); one availability query is enough
to tell whether the company has reservations enabled and a public calendar. Without it the
button would lead to an empty modal.
setAppContext()
public
setAppContext(AppContext $appContext) : $this
Parameters
- $appContext : AppContext
Return values
$thissetComponentParameter()
Sets a component parameter.
public
setComponentParameter(string $name, mixed $value) : void
Parameters
- $name : string
- $value : mixed
setCurrencyContext()
public
setCurrencyContext(CurrencyContext|null $currencyContext) : $this
Parameters
- $currencyContext : CurrencyContext|null
Return values
$thissetLanguageContext()
public
setLanguageContext(LanguageContext|null $languageContext) : $this
Parameters
- $languageContext : LanguageContext|null
Return values
$thissetLocalizationContext()
public
setLocalizationContext(LocalizationContext|null $localizationContext) : $this
Parameters
- $localizationContext : LocalizationContext|null
Return values
$thissetSettingContext()
public
setSettingContext(SettingContext $settingContext) : $this
Parameters
- $settingContext : SettingContext
Return values
$thissetThemeContext()
public
setThemeContext(ThemeContext|null $themeContext) : $this
Parameters
- $themeContext : ThemeContext|null
Return values
$thissetup()
public
setup(BasePresenter|BasePresenter $presenter) : void
Parameters
- $presenter : BasePresenter|BasePresenter
setupAppContext()
public
setupAppContext(AppContext $appContext) : void
Parameters
- $appContext : AppContext
typedComponent()
Returns a subcomponent with a verified type.
public
typedComponent(string $name, T> $type) : T
Use it instead of an inline @var above $this['x'] (which does not narrow the type) and
instead of the generic IComponent that $this['x'] returns. See
Form::typedComponent().
Parameters
- $name : string
- $type : T>
Tags
Return values
TcreateComponentBookingForm()
protected
createComponentBookingForm() : Form
Return values
FormfrontPresenter()
Returns the front presenter the component is attached to (null outside the front end or when detached).
protected
frontPresenter() : BasePresenter|null
Return values
BasePresenter|nullgetTranslator()
protected
getTranslator() : ITranslator
Return values
ITranslatorsetTemplateFile()
protected
setTemplateFile(string $dir, string $filename) : void
Parameters
- $dir : string
- $filename : string
setTranslator()
protected
setTranslator(ITranslator $translator) : self
Parameters
- $translator : ITranslator
Return values
selfbookableStarts()
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}>buildEndOptions()
Returns the ends: the union of reachable ends over all starts of the day, in steps of `slotLengthMinutes`, capped by `maxTo`. Not authoritative; the server verifies the pair again.
private
buildEndOptions() : array<string, string>
Return values
array<string, string>buildStartOptions()
Returns the starts: the server-trimmed `starts` of the chosen day ({@see InitTrait::bookableStarts()}).
private
buildStartOptions() : array<string, string>
Return values
array<string, 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>>createFormContent()
Second view: recap + booking form.
private
createFormContent() : string
Return values
stringcreateModalEvent()
private
createModalEvent(string $content[, array<string, mixed> $extraOptions = [] ]) : ModalWindowEvent
Parameters
- $content : string
- $extraOptions : array<string, mixed> = []
Return values
ModalWindowEventcreateMultiplierComponent()
Creates a multiplier for the component.
private
createMultiplierComponent(string $name) : Multiplier<string|int, IComponent>
Parameters
- $name : string
Return values
Multiplier<string|int, IComponent>createNewComponentInstance()
Creates a new component instance without adding it to the presenter; used by the multiplier.
private
createNewComponentInstance(string $baseName[, int|string|null $uniqueKey = null ]) : IComponent|null
Parameters
- $baseName : string
- $uniqueKey : int|string|null = null
Return values
IComponent|nullcreateResultContent()
Second view after submit: the result message instead of the form (the form is no longer offered).
private
createResultContent() : string
Return values
stringcreateStandardComponent()
Creates a standard component (not a multiplier).
private
createStandardComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nullcreateWizardContent()
First view: the same wizard (header + month/day) as in inline mode, rendered into a string.
private
createWizardContent() : string
Return values
stringcurrentMonth()
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}|nullfinishBooking()
Success (also the silent honeypot one); the message stays in the modal and the calendar below is redrawn.
private
finishBooking(string $messageKey, string $type) : void
Parameters
- $messageKey : string
- $type : string
formatDayHeading()
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
stringformatTime()
Minutes since midnight to `HH:MM` (the end offer must match `starts` in shape, i.e. with a leading zero).
private
static formatTime(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
boolopenOrUpdateModal()
Opens the modal or, when it already exists in the registry, only replaces its content.
private
openOrUpdateModal(string $content) : void
IMPORTANT: the distinction is required. ADDED redraws the whole modal container, which would
drop the Bootstrap instance of an already open window; UPDATE touches only the modalBody
snippet, so the window stays open and only its inside changes (flash inside the modal plus a
redrawn calendar).
Parameters
- $content : string
prefillIdentity()
private
static prefillIdentity(Form $form, User|null $user) : void
Parameters
- $form : Form
- $user : User|null
prepareFormTemplate()
Recap data and messages for `Sections/Form.latte`.
private
prepareFormTemplate() : void
prepareWizard()
private
prepareWizard() : void
refreshWizardAfterBooking()
Second snippet next to the modal: in inline mode the redrawn calendar on the booking page, in modal mode the client's reservation list on the "my time slots" page (a new one was added).
private
refreshWizardAfterBooking() : void
refreshWizardView()
Redraws the wizard after a navigation signal: in inline mode through the `wizard` snippet, in modal mode by replacing the modal content.
private
refreshWizardView() : void
renderPartial()
Renders a partial into a string (like `WatchdogFormComponent\Traits\ModalTrait`).
private
renderPartial(string $file, string $errorMessageKey) : string
A template failure must not crash the request; the modal shows a message instead of the content.
Parameters
- $file : string
- $errorMessageKey : string
Return values
stringresolveCalendarId()
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|nullresolveErrorMessageKey()
An unknown or missing code gives the generic message.
private
static resolveErrorMessageKey(mixed $code) : string
Parameters
- $code : mixed
Return values
stringresolveMonth()
`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
Return values
stringstr()
`ArrayHash` values are `mixed`; safely narrows to string (a non-string becomes empty).
private
static str(mixed $value) : string
Parameters
- $value : mixed
Return values
stringtoMinutes()
private
static toMinutes(string $time) : int
Parameters
- $time : string