@medalsocial/booking 0.0.0-placeholder.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -1
- package/dist/core/age.d.mts +34 -0
- package/dist/core/age.mjs +95 -0
- package/dist/core/age.mjs.map +1 -0
- package/dist/core/attempt-store.d.mts +111 -0
- package/dist/core/attempt-store.mjs +159 -0
- package/dist/core/attempt-store.mjs.map +1 -0
- package/dist/core/categories.d.mts +27 -0
- package/dist/core/categories.mjs +42 -0
- package/dist/core/categories.mjs.map +1 -0
- package/dist/core/clock.d.mts +33 -0
- package/dist/core/clock.mjs +251 -0
- package/dist/core/clock.mjs.map +1 -0
- package/dist/core/config.d.mts +171 -0
- package/dist/core/config.mjs +392 -0
- package/dist/core/config.mjs.map +1 -0
- package/dist/core/consent.d.mts +14 -0
- package/dist/core/consent.mjs +17 -0
- package/dist/core/consent.mjs.map +1 -0
- package/dist/core/deep-link.d.mts +40 -0
- package/dist/core/deep-link.mjs +160 -0
- package/dist/core/deep-link.mjs.map +1 -0
- package/dist/core/display-name.d.mts +18 -0
- package/dist/core/display-name.mjs +30 -0
- package/dist/core/display-name.mjs.map +1 -0
- package/dist/core/draft-store.d.mts +83 -0
- package/dist/core/draft-store.mjs +115 -0
- package/dist/core/draft-store.mjs.map +1 -0
- package/dist/core/dto.d.mts +46 -0
- package/dist/core/dto.mjs +157 -0
- package/dist/core/dto.mjs.map +1 -0
- package/dist/core/ics.d.mts +63 -0
- package/dist/core/ics.mjs +130 -0
- package/dist/core/ics.mjs.map +1 -0
- package/dist/core/index.d.mts +34 -0
- package/dist/core/index.mjs +31 -0
- package/dist/core/labels.d.mts +88 -0
- package/dist/core/labels.mjs +93 -0
- package/dist/core/labels.mjs.map +1 -0
- package/dist/core/machine.d.mts +598 -0
- package/dist/core/machine.mjs +878 -0
- package/dist/core/machine.mjs.map +1 -0
- package/dist/core/money.d.mts +12 -0
- package/dist/core/money.mjs +51 -0
- package/dist/core/money.mjs.map +1 -0
- package/dist/core/next-available.d.mts +20 -0
- package/dist/core/next-available.mjs +36 -0
- package/dist/core/next-available.mjs.map +1 -0
- package/dist/core/next-free.d.mts +25 -0
- package/dist/core/next-free.mjs +44 -0
- package/dist/core/next-free.mjs.map +1 -0
- package/dist/core/party-slots.d.mts +72 -0
- package/dist/core/party-slots.mjs +223 -0
- package/dist/core/party-slots.mjs.map +1 -0
- package/dist/core/paths.d.mts +11 -0
- package/dist/core/paths.mjs +56 -0
- package/dist/core/paths.mjs.map +1 -0
- package/dist/core/phone.d.mts +17 -0
- package/dist/core/phone.mjs +58 -0
- package/dist/core/phone.mjs.map +1 -0
- package/dist/core/portal/cookies.d.mts +29 -0
- package/dist/core/portal/dto.d.mts +140 -0
- package/dist/core/portal/dto.mjs +142 -0
- package/dist/core/portal/dto.mjs.map +1 -0
- package/dist/core/portal/family.d.mts +39 -0
- package/dist/core/portal/family.mjs +39 -0
- package/dist/core/portal/family.mjs.map +1 -0
- package/dist/core/portal/login-input.d.mts +29 -0
- package/dist/core/portal/login-input.mjs +29 -0
- package/dist/core/portal/login-input.mjs.map +1 -0
- package/dist/core/portal/next-path.d.mts +12 -0
- package/dist/core/portal/next-path.mjs +98 -0
- package/dist/core/portal/next-path.mjs.map +1 -0
- package/dist/core/portal/return-path.d.mts +7 -0
- package/dist/core/portal/return-path.mjs +49 -0
- package/dist/core/portal/return-path.mjs.map +1 -0
- package/dist/core/portal/session-cookie.d.mts +27 -0
- package/dist/core/portal/session-cookie.mjs +30 -0
- package/dist/core/portal/session-cookie.mjs.map +1 -0
- package/dist/core/portal/vipps-link.d.mts +56 -0
- package/dist/core/portal/vipps-link.mjs +136 -0
- package/dist/core/portal/vipps-link.mjs.map +1 -0
- package/dist/core/portal/vipps-return.d.mts +39 -0
- package/dist/core/portal/vipps-return.mjs +69 -0
- package/dist/core/portal/vipps-return.mjs.map +1 -0
- package/dist/core/rebook-store.d.mts +28 -0
- package/dist/core/rebook-store.mjs +60 -0
- package/dist/core/rebook-store.mjs.map +1 -0
- package/dist/core/restore-gate.d.mts +40 -0
- package/dist/core/restore-gate.mjs +96 -0
- package/dist/core/restore-gate.mjs.map +1 -0
- package/dist/core/stores.d.mts +8 -0
- package/dist/core/stores.mjs +23 -0
- package/dist/core/stores.mjs.map +1 -0
- package/dist/core/types.d.mts +241 -0
- package/dist/core/wire.d.mts +37 -0
- package/dist/next/cache/memory.d.mts +14 -0
- package/dist/next/cache/memory.mjs +51 -0
- package/dist/next/cache/memory.mjs.map +1 -0
- package/dist/next/cache/next-data.d.mts +6 -0
- package/dist/next/cache/next-data.mjs +35 -0
- package/dist/next/cache/next-data.mjs.map +1 -0
- package/dist/next/cache/noop.d.mts +6 -0
- package/dist/next/cache/noop.mjs +15 -0
- package/dist/next/cache/noop.mjs.map +1 -0
- package/dist/next/cache/workers.d.mts +29 -0
- package/dist/next/cache/workers.mjs +74 -0
- package/dist/next/cache/workers.mjs.map +1 -0
- package/dist/next/catalogue.d.mts +38 -0
- package/dist/next/catalogue.mjs +197 -0
- package/dist/next/catalogue.mjs.map +1 -0
- package/dist/next/handler.d.mts +11 -0
- package/dist/next/handler.mjs +150 -0
- package/dist/next/handler.mjs.map +1 -0
- package/dist/next/index.d.mts +17 -0
- package/dist/next/index.mjs +14 -0
- package/dist/next/loaders.d.mts +79 -0
- package/dist/next/loaders.mjs +195 -0
- package/dist/next/loaders.mjs.map +1 -0
- package/dist/next/manage-dto.d.mts +6 -0
- package/dist/next/manage-dto.mjs +28 -0
- package/dist/next/manage-dto.mjs.map +1 -0
- package/dist/next/medal.d.mts +98 -0
- package/dist/next/medal.mjs +154 -0
- package/dist/next/medal.mjs.map +1 -0
- package/dist/next/options.d.mts +158 -0
- package/dist/next/options.mjs +65 -0
- package/dist/next/options.mjs.map +1 -0
- package/dist/next/portal/guardian.d.mts +42 -0
- package/dist/next/portal/guardian.mjs +76 -0
- package/dist/next/portal/guardian.mjs.map +1 -0
- package/dist/next/portal/medal-portal.d.mts +161 -0
- package/dist/next/portal/medal-portal.mjs +825 -0
- package/dist/next/portal/medal-portal.mjs.map +1 -0
- package/dist/next/portal/session.d.mts +38 -0
- package/dist/next/portal/session.mjs +40 -0
- package/dist/next/portal/session.mjs.map +1 -0
- package/dist/next/portal/vipps-flash.d.mts +14 -0
- package/dist/next/portal/vipps-flash.mjs +41 -0
- package/dist/next/portal/vipps-flash.mjs.map +1 -0
- package/dist/next/portal.d.mts +205 -0
- package/dist/next/portal.mjs +469 -0
- package/dist/next/portal.mjs.map +1 -0
- package/dist/next/request.d.mts +51 -0
- package/dist/next/request.mjs +123 -0
- package/dist/next/request.mjs.map +1 -0
- package/dist/next/routes/catalogue-routes.mjs +330 -0
- package/dist/next/routes/catalogue-routes.mjs.map +1 -0
- package/dist/next/routes/create.mjs +422 -0
- package/dist/next/routes/create.mjs.map +1 -0
- package/dist/next/routes/manage.mjs +191 -0
- package/dist/next/routes/manage.mjs.map +1 -0
- package/dist/next/routes/portal-routes.mjs +312 -0
- package/dist/next/routes/portal-routes.mjs.map +1 -0
- package/dist/next/routes/shared.mjs +134 -0
- package/dist/next/routes/shared.mjs.map +1 -0
- package/dist/next/routes/vipps-return.mjs +166 -0
- package/dist/next/routes/vipps-return.mjs.map +1 -0
- package/dist/next/runtime.d.mts +49 -0
- package/dist/next/runtime.mjs +94 -0
- package/dist/next/runtime.mjs.map +1 -0
- package/dist/next/seed.d.mts +71 -0
- package/dist/next/seed.mjs +495 -0
- package/dist/next/seed.mjs.map +1 -0
- package/dist/next/server.d.mts +26 -0
- package/dist/next/server.mjs +63 -0
- package/dist/next/server.mjs.map +1 -0
- package/dist/react/BookingLink.d.mts +61 -0
- package/dist/react/BookingLink.mjs +102 -0
- package/dist/react/BookingLink.mjs.map +1 -0
- package/dist/react/BookingWizard.d.mts +49 -0
- package/dist/react/BookingWizard.mjs +577 -0
- package/dist/react/BookingWizard.mjs.map +1 -0
- package/dist/react/LoginPageQuery.d.mts +37 -0
- package/dist/react/LoginPageQuery.mjs +106 -0
- package/dist/react/LoginPageQuery.mjs.map +1 -0
- package/dist/react/LoginSheet.d.mts +49 -0
- package/dist/react/LoginSheet.mjs +98 -0
- package/dist/react/LoginSheet.mjs.map +1 -0
- package/dist/react/ManageBooking.d.mts +50 -0
- package/dist/react/ManageBooking.mjs +197 -0
- package/dist/react/ManageBooking.mjs.map +1 -0
- package/dist/react/PortalDashboard.d.mts +10 -0
- package/dist/react/Provider.d.mts +82 -0
- package/dist/react/Provider.mjs +132 -0
- package/dist/react/Provider.mjs.map +1 -0
- package/dist/react/actions.d.mts +133 -0
- package/dist/react/actions.mjs +90 -0
- package/dist/react/actions.mjs.map +1 -0
- package/dist/react/index.d.mts +28 -0
- package/dist/react/index.mjs +20 -0
- package/dist/react/kit.d.mts +54 -0
- package/dist/react/kit.mjs +127 -0
- package/dist/react/kit.mjs.map +1 -0
- package/dist/react/labels/index.d.mts +23 -0
- package/dist/react/labels/index.mjs +44 -0
- package/dist/react/labels/index.mjs.map +1 -0
- package/dist/react/labels/login.d.mts +10 -0
- package/dist/react/labels/login.mjs +13 -0
- package/dist/react/labels/login.mjs.map +1 -0
- package/dist/react/labels/manage.d.mts +15 -0
- package/dist/react/labels/manage.mjs +19 -0
- package/dist/react/labels/manage.mjs.map +1 -0
- package/dist/react/labels/portal.d.mts +25 -0
- package/dist/react/labels/portal.mjs +41 -0
- package/dist/react/labels/portal.mjs.map +1 -0
- package/dist/react/labels/screens.d.mts +11 -0
- package/dist/react/labels/screens.mjs +709 -0
- package/dist/react/labels/screens.mjs.map +1 -0
- package/dist/react/labels/wizard.d.mts +69 -0
- package/dist/react/labels/wizard.mjs +95 -0
- package/dist/react/labels/wizard.mjs.map +1 -0
- package/dist/react/link/index.d.mts +5 -0
- package/dist/react/link/index.mjs +5 -0
- package/dist/react/portal/SessionTouch.d.mts +19 -0
- package/dist/react/portal/SessionTouch.mjs +40 -0
- package/dist/react/portal/SessionTouch.mjs.map +1 -0
- package/dist/react/portal/age-prompt.d.mts +10 -0
- package/dist/react/portal/age-prompt.mjs +21 -0
- package/dist/react/portal/age-prompt.mjs.map +1 -0
- package/dist/react/portal/book-again.d.mts +18 -0
- package/dist/react/portal/book-again.mjs +21 -0
- package/dist/react/portal/book-again.mjs.map +1 -0
- package/dist/react/portal/child-summary.d.mts +17 -0
- package/dist/react/portal/child-summary.mjs +45 -0
- package/dist/react/portal/child-summary.mjs.map +1 -0
- package/dist/react/portal/dashboard.d.mts +74 -0
- package/dist/react/portal/dashboard.mjs +204 -0
- package/dist/react/portal/dashboard.mjs.map +1 -0
- package/dist/react/portal/leave.d.mts +17 -0
- package/dist/react/portal/leave.mjs +21 -0
- package/dist/react/portal/leave.mjs.map +1 -0
- package/dist/react/portal/sections.d.mts +12 -0
- package/dist/react/portal/sections.mjs +48 -0
- package/dist/react/portal/sections.mjs.map +1 -0
- package/dist/react/portal/tabs.d.mts +23 -0
- package/dist/react/portal/tabs.mjs +36 -0
- package/dist/react/portal/tabs.mjs.map +1 -0
- package/dist/react/portal/vipps-flash.d.mts +11 -0
- package/dist/react/portal/vipps-flash.mjs +18 -0
- package/dist/react/portal/vipps-flash.mjs.map +1 -0
- package/dist/react/portal/wired.mjs +202 -0
- package/dist/react/portal/wired.mjs.map +1 -0
- package/dist/react/screen-labels.mjs +14 -0
- package/dist/react/screen-labels.mjs.map +1 -0
- package/dist/react/shared.d.mts +13 -0
- package/dist/react/shared.mjs +7 -0
- package/dist/react/use-next-free.d.mts +19 -0
- package/dist/react/use-next-free.mjs +56 -0
- package/dist/react/use-next-free.mjs.map +1 -0
- package/dist/react/useBooking.d.mts +177 -0
- package/dist/react/useBooking.mjs +1205 -0
- package/dist/react/useBooking.mjs.map +1 -0
- package/dist/react/wizard/adapters.mjs +187 -0
- package/dist/react/wizard/adapters.mjs.map +1 -0
- package/dist/react/wizard/index.d.mts +11 -0
- package/dist/react/wizard/index.mjs +6 -0
- package/package.json +126 -5
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import { BookingConfig } from "./config.mjs";
|
|
2
|
+
//#region src/core/machine.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The three steps the design draws, and the three the machine holds.
|
|
5
|
+
*
|
|
6
|
+
* `when` is one screen and two questions — the stylist and the hour — because
|
|
7
|
+
* that is what a parent answers in one breath and what the frames show as
|
|
8
|
+
* «Frisør og tid». They were separate steps until the redesign, and the pair of
|
|
9
|
+
* them cost a parent a whole extra «Neste» to say a thing they had already
|
|
10
|
+
* decided.
|
|
11
|
+
*
|
|
12
|
+
* There is no `login` step, and there was one. It sat between the hour and
|
|
13
|
+
* «Bekreft» and asked who the parent was — Vipps, an e-mail code, or neither —
|
|
14
|
+
* at the one moment a walk-in parent least wants a decision. The login is an
|
|
15
|
+
* offer in a sheet now (the login sheet), opened from a row
|
|
16
|
+
* on `service` and `details`, and the machine knows nothing about it: a login
|
|
17
|
+
* changes who the contact fields are filled from, never which step is next.
|
|
18
|
+
*/
|
|
19
|
+
export type WizardStep = 'who' | 'service' | 'when' | 'details';
|
|
20
|
+
/**
|
|
21
|
+
* «Hvem skal klippes?» — step 1's answer (SP10), one entry per chair.
|
|
22
|
+
*
|
|
23
|
+
* The wizard used to learn who a haircut was for last, in two text fields on
|
|
24
|
+
* «Bekreft», and a logged-in parent's children were a row of name chips there.
|
|
25
|
+
* Asking first is what lets everything after it be ABOUT the child: step 2
|
|
26
|
+
* offers «Samme som sist» for Theo, the ages that filter the menu are Theo's
|
|
27
|
+
* on the appointment day, and the booking goes to Medal as Theo's person id
|
|
28
|
+
* rather than a name a parent typed.
|
|
29
|
+
*
|
|
30
|
+
* `key` identifies the chair across edits and is never shown: `p:<id>` for a
|
|
31
|
+
* saved child, `self` for the logged-in parent, `adult` for a guest adult,
|
|
32
|
+
* `guest:<n>` for a guest's n-th child. Everything else is optional and ABSENT
|
|
33
|
+
* rather than blank, the rule the line items follow — a guest child has no
|
|
34
|
+
* name until «Bekreft» asks for one.
|
|
35
|
+
*/
|
|
36
|
+
export interface WizardPerson {
|
|
37
|
+
key: string;
|
|
38
|
+
/** A grown-up: no child fields on «Bekreft», the full menu on step 2. */
|
|
39
|
+
adult?: boolean;
|
|
40
|
+
/** Medal's id for a saved child — what `booked_for_person_id` carries. */
|
|
41
|
+
personId?: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
birthYear?: number;
|
|
44
|
+
/** 1–12, when the parent gave one. */
|
|
45
|
+
birthMonth?: number;
|
|
46
|
+
}
|
|
47
|
+
/** The logged-in parent themselves, on step 1's «Meg selv (voksen)». */
|
|
48
|
+
export declare const SELF_KEY = "self";
|
|
49
|
+
/**
|
|
50
|
+
* A seat only a GUEST's step 1 can draw: a count chip's `guest:n`, a child
|
|
51
|
+
* named in the guest sheet (`new:…`), the «Voksen» chip's `adult`. A parent's
|
|
52
|
+
* step 1 draws cards for saved children and «Meg selv» and nothing else — so
|
|
53
|
+
* one of these under a family is a seat nobody can see or untick, counting
|
|
54
|
+
* towards the three.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isGuestSeat(person: WizardPerson): boolean;
|
|
57
|
+
export interface WizardService {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
category: string;
|
|
61
|
+
durationMinutes: number;
|
|
62
|
+
priceOre: number;
|
|
63
|
+
maxPerBooking: number;
|
|
64
|
+
/** Drives step 3's weekend note and the surcharged price shown on the chip. */
|
|
65
|
+
weekendSurchargePct: number;
|
|
66
|
+
/**
|
|
67
|
+
* The salon's prep and cleanup time around this service, in minutes.
|
|
68
|
+
*
|
|
69
|
+
* Carried because a FAMILY needs them: two children seated back to back on
|
|
70
|
+
* one stylist are separated by the first one's cleanup plus the second one's
|
|
71
|
+
* prep, and `itemStartTimes` cannot honour a gap it does not know about.
|
|
72
|
+
* Availability is asked per service and each opening is judged on its own, so
|
|
73
|
+
* both instants can come back free while the engine refuses the combined
|
|
74
|
+
* submission — a visit offered to the parent and then rejected.
|
|
75
|
+
*
|
|
76
|
+
* Zero is the ordinary value and the wire's own default.
|
|
77
|
+
*/
|
|
78
|
+
bufferBeforeMinutes: number;
|
|
79
|
+
bufferAfterMinutes: number;
|
|
80
|
+
/**
|
|
81
|
+
* Who the service is for, in whole years, inclusive (SP10) — «Barnehageklipp»
|
|
82
|
+
* up to 6, say. Used to SUGGEST, never to refuse: Medal does not reject a
|
|
83
|
+
* booking on age, and a parent may know better. Absent is «any age».
|
|
84
|
+
*/
|
|
85
|
+
ageMinYears?: number;
|
|
86
|
+
ageMaxYears?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface WizardItem {
|
|
89
|
+
service: WizardService;
|
|
90
|
+
/** The child this line is for. Asked inline in step 4. */
|
|
91
|
+
bookedForName?: string;
|
|
92
|
+
/** Optional fødselsår — powers "Jonas (9)" in the CRM later. */
|
|
93
|
+
bookedForBirthYear?: number;
|
|
94
|
+
/**
|
|
95
|
+
* The saved child this line is for (SP10) — sent as `booked_for_person_id`
|
|
96
|
+
* when the phone rule allows it (see the create route). Never typed: it
|
|
97
|
+
* comes only from a child card on step 1.
|
|
98
|
+
*/
|
|
99
|
+
bookedForPersonId?: string;
|
|
100
|
+
/** The child's birth month, for the age on the appointment day. */
|
|
101
|
+
bookedForBirthMonth?: number;
|
|
102
|
+
/** A grown-up's line: «Bekreft» asks nothing about a child for it. */
|
|
103
|
+
adult?: boolean;
|
|
104
|
+
}
|
|
105
|
+
export interface WizardState {
|
|
106
|
+
step: WizardStep;
|
|
107
|
+
/**
|
|
108
|
+
* Who is coming — step 1's answer. One entry per chair, in the order the
|
|
109
|
+
* line items follow: `items[i]` is `people[i]`'s haircut once every chair
|
|
110
|
+
* has a service.
|
|
111
|
+
*/
|
|
112
|
+
people: WizardPerson[];
|
|
113
|
+
/**
|
|
114
|
+
* Step 2's answers so far, one per person — `null` where that person has no
|
|
115
|
+
* service yet. `items` is built from these and `people` only once none is
|
|
116
|
+
* `null`, so a family half-way through step 2 has no basket and step 3
|
|
117
|
+
* stays shut.
|
|
118
|
+
*/
|
|
119
|
+
choices: Array<WizardService | null>;
|
|
120
|
+
items: WizardItem[];
|
|
121
|
+
/** null = «Første ledige» — the default, and the utilisation-maximising one. */
|
|
122
|
+
resourceId: string | null;
|
|
123
|
+
/**
|
|
124
|
+
* Whether the stylist question has been ANSWERED, as against left at its
|
|
125
|
+
* default.
|
|
126
|
+
*
|
|
127
|
+
* `resourceId` cannot say. `null` is «Første ledige», which is both the value
|
|
128
|
+
* a fresh wizard starts on and a real choice a parent makes by tapping «Alle»
|
|
129
|
+
* — and while the stylist and the hour were separate steps the difference was
|
|
130
|
+
* legible in `step` alone: a visitor who had answered was on `time`. One
|
|
131
|
+
* screen for both questions took that away, and the thing that noticed is
|
|
132
|
+
* `applyPrefill`, whose whole contract is «fills blanks, never overwrites»:
|
|
133
|
+
* without this a «Bestill igjen» link naming Sara would land on a parent who
|
|
134
|
+
* had just tapped «Alle» and quietly replace their answer.
|
|
135
|
+
*
|
|
136
|
+
* Set by `pickResource` either way, and by the parallel `setPartyMode` — «two
|
|
137
|
+
* stylists at once» is an answer about who cuts the hair even though it
|
|
138
|
+
* clears the preference. Cleared whenever the preference itself is cleared
|
|
139
|
+
* for a reason the parent did not choose: a replaced basket, or a sibling the
|
|
140
|
+
* named stylist cannot take.
|
|
141
|
+
*/
|
|
142
|
+
stylistAnswered: boolean;
|
|
143
|
+
partyMode: 'sequential' | 'parallel';
|
|
144
|
+
startTs: number | null;
|
|
145
|
+
resolvedResourceId: string | null;
|
|
146
|
+
/**
|
|
147
|
+
* The stylist each line resolved to, in basket order — the half of a party
|
|
148
|
+
* slot that `resolvedResourceId` cannot hold.
|
|
149
|
+
*
|
|
150
|
+
* `null` for everything except a party slot chosen on step 3, where
|
|
151
|
+
* `resolvedResourceId` still answers for the whole visit. Non-null it is one
|
|
152
|
+
* id per line item, and it is what a parallel party actually needs: two
|
|
153
|
+
* children seen at the same instant are seen by two *different* stylists, so
|
|
154
|
+
* there is no single id that describes the visit.
|
|
155
|
+
*
|
|
156
|
+
* Read through `itemResourceIds` rather than directly, so the submission and
|
|
157
|
+
* the confirmation card cannot disagree about who is cutting whose hair.
|
|
158
|
+
*/
|
|
159
|
+
partyResourceIds: string[] | null;
|
|
160
|
+
contact: {
|
|
161
|
+
phone: string;
|
|
162
|
+
name: string;
|
|
163
|
+
email: string;
|
|
164
|
+
};
|
|
165
|
+
notes: string;
|
|
166
|
+
consentTerms: boolean;
|
|
167
|
+
consentMarketing: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* A code, never a sentence. The Norwegian copy for each case belongs to the
|
|
170
|
+
* component that shows it, so the same rule can be a toast in one place and
|
|
171
|
+
* inline text in another without this file knowing either.
|
|
172
|
+
*
|
|
173
|
+
* Wider than what `reduce` can currently produce, on purpose. The submit route
|
|
174
|
+
* answers with `conflict`, `invalidInput`, `unconfigured` and `upstreamError`
|
|
175
|
+
* as well as `slotTaken`, and those have to land somewhere. Somewhere is here:
|
|
176
|
+
* if the union only admitted what this file raises, the details step would grow
|
|
177
|
+
* a second error slot of its own and the sticky bar and the form would start
|
|
178
|
+
* disagreeing about whether anything is wrong.
|
|
179
|
+
*
|
|
180
|
+
* `upstreamError` is the create route's 502 — Medal answered something this
|
|
181
|
+
* site has no mapping for, or did not answer at all. It is an outage rather
|
|
182
|
+
* than a mistake the visitor made, but it arrives with them waiting on a
|
|
183
|
+
* button they have just pressed, so it needs a sentence of its own rather
|
|
184
|
+
* than being folded into `conflict` — which says «prøv en gang til», and
|
|
185
|
+
* trying again is exactly what will not help here.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* A service a link named for a parent whose family is known, held until
|
|
189
|
+
* step 1 says who it is for (`holdService`). Each person seated after that
|
|
190
|
+
* starts on it where it suits them — a kids' service for a child, anything
|
|
191
|
+
* else for a grown-up — instead of a guest chair being invented for it.
|
|
192
|
+
*/
|
|
193
|
+
pendingService: WizardService | null;
|
|
194
|
+
error: 'maxParty' | 'slotTaken' | 'conflict' | 'invalidInput' | 'unconfigured' | 'upstreamError' |
|
|
195
|
+
/**
|
|
196
|
+
* The create route's 409 for a submission Medal already holds under its
|
|
197
|
+
* key: it may well be booked, so the parent is sent to their e-mail
|
|
198
|
+
* rather than to the button.
|
|
199
|
+
*/
|
|
200
|
+
'inProgress' | null;
|
|
201
|
+
}
|
|
202
|
+
export type WizardAction =
|
|
203
|
+
/**
|
|
204
|
+
* Back to an empty wizard, for «Bestill ny time» on the confirmation.
|
|
205
|
+
*
|
|
206
|
+
* A parent who has just booked one child and needs a SEPARATE appointment —
|
|
207
|
+
* another service whose `maxPerBooking` is 1, or a sibling who wants a
|
|
208
|
+
* different hour — has to be able to start over. The confirmation is now
|
|
209
|
+
* restored across a reload, so without this the only ways out of it are
|
|
210
|
+
* closing the tab or waiting for the stored attempt to age out.
|
|
211
|
+
*/
|
|
212
|
+
{
|
|
213
|
+
type: 'startOver';
|
|
214
|
+
} |
|
|
215
|
+
/**
|
|
216
|
+
* Step 1's answer: who is coming. Replaces the party; a person who stays
|
|
217
|
+
* (the same `key`) keeps the service already chosen for them, so going back
|
|
218
|
+
* to add a sibling does not re-ask the first child. `advance` moves on to
|
|
219
|
+
* step 2 — a guest's «2 barn» chip is one tap, a logged-in parent ticks
|
|
220
|
+
* children and presses «Neste».
|
|
221
|
+
*/
|
|
222
|
+
{
|
|
223
|
+
type: 'choosePeople';
|
|
224
|
+
people: WizardPerson[];
|
|
225
|
+
advance?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* «Samme som sist»: a service to start each person NEW to the party on,
|
|
228
|
+
* aligned with `people` — the child's last haircut, where the shell
|
|
229
|
+
* knows one. A person already in the party keeps their own answer.
|
|
230
|
+
*/
|
|
231
|
+
services?: ReadonlyArray<WizardService | null>;
|
|
232
|
+
} |
|
|
233
|
+
/**
|
|
234
|
+
* Step 2's answer for ONE person. For a party of one it is the old one-tap
|
|
235
|
+
* `pickService` and moves on; for a family it fills that child's line and
|
|
236
|
+
* waits for the rest. `resourceServiceIds` is `addService`'s: what the named
|
|
237
|
+
* stylist can do, so a service they cannot do releases the preference.
|
|
238
|
+
*/
|
|
239
|
+
{
|
|
240
|
+
type: 'pickServiceFor';
|
|
241
|
+
index: number;
|
|
242
|
+
service: WizardService;
|
|
243
|
+
resourceServiceIds?: readonly string[];
|
|
244
|
+
} | {
|
|
245
|
+
type: 'pickService';
|
|
246
|
+
service: WizardService;
|
|
247
|
+
} |
|
|
248
|
+
/**
|
|
249
|
+
* The parent's family has appeared (a login, a guardian read after the
|
|
250
|
+
* first render) or step 1 is shown again under it: the guest seats go —
|
|
251
|
+
* see `isGuestSeat` — and a guest «Voksen» becomes «Meg selv». Only while
|
|
252
|
+
* nothing is held on them (steps 1 and 2); from the hour on they are real
|
|
253
|
+
* chairs with a time, and «Bekreft» names them. A party emptied by it goes
|
|
254
|
+
* back to step 1, where the children are.
|
|
255
|
+
*/
|
|
256
|
+
{
|
|
257
|
+
type: 'seatFamily';
|
|
258
|
+
} |
|
|
259
|
+
/**
|
|
260
|
+
* One person joins the party as it is WHEN this lands — a child the parent
|
|
261
|
+
* just created in Medal, after the await. Refused at the limit, a no-op for
|
|
262
|
+
* somebody already seated.
|
|
263
|
+
*/
|
|
264
|
+
{
|
|
265
|
+
type: 'addPerson';
|
|
266
|
+
person: WizardPerson;
|
|
267
|
+
} |
|
|
268
|
+
/** A link's service, held for the people step 1 is about to seat — see `pendingService`. */
|
|
269
|
+
{
|
|
270
|
+
type: 'holdService';
|
|
271
|
+
service: WizardService | null;
|
|
272
|
+
} |
|
|
273
|
+
/**
|
|
274
|
+
* A sibling, added on step 1 — and the one fact about the stylist catalogue
|
|
275
|
+
* this file is allowed to know.
|
|
276
|
+
*
|
|
277
|
+
* `resourceServiceIds` is what the currently preferred stylist can do. It
|
|
278
|
+
* rides on the action because the alternative was worse in both directions:
|
|
279
|
+
* the machine cannot fetch the catalogue, and a shell that cleared the
|
|
280
|
+
* preference itself would need an action that changes `resourceId` without
|
|
281
|
+
* moving the visitor off step 1, which `pickResource` is not.
|
|
282
|
+
*
|
|
283
|
+
* Absent is «the caller does not know», not «the stylist does nothing». A
|
|
284
|
+
* missing list keeps the preference, because silence is not evidence.
|
|
285
|
+
*/
|
|
286
|
+
{
|
|
287
|
+
type: 'addService';
|
|
288
|
+
service: WizardService;
|
|
289
|
+
resourceServiceIds?: readonly string[];
|
|
290
|
+
} | {
|
|
291
|
+
type: 'pickResource';
|
|
292
|
+
resourceId: string | null;
|
|
293
|
+
} | {
|
|
294
|
+
type: 'pickSlot';
|
|
295
|
+
startTs: number;
|
|
296
|
+
resourceId: string | null;
|
|
297
|
+
} |
|
|
298
|
+
/**
|
|
299
|
+
* How the party is seen — step 2's second question, and the one that decides
|
|
300
|
+
* which search step 3 runs.
|
|
301
|
+
*
|
|
302
|
+
* Its own action rather than a field on `pickResource`, because the two
|
|
303
|
+
* answers are independent: «Sara» and «rett etter hverandre» are one visit,
|
|
304
|
+
* and a parent who changes only the mode has not changed their mind about
|
|
305
|
+
* the stylist.
|
|
306
|
+
*/
|
|
307
|
+
{
|
|
308
|
+
type: 'setPartyMode';
|
|
309
|
+
mode: WizardState['partyMode'];
|
|
310
|
+
} |
|
|
311
|
+
/**
|
|
312
|
+
* A whole family seated in one tap: the instant the visit starts and the
|
|
313
|
+
* stylist each child got.
|
|
314
|
+
*
|
|
315
|
+
* Separate from `pickSlot` because a party slot answers a question
|
|
316
|
+
* `pickSlot` has no room for. `pickSlot` carries one resolved stylist, which
|
|
317
|
+
* is all a single booking ever has; a party in `parallel` has one per child
|
|
318
|
+
* and they are deliberately different people.
|
|
319
|
+
*
|
|
320
|
+
* `resourceIds` is one id per line item, in basket order — never null,
|
|
321
|
+
* because a slot that cannot name the two stylists cannot promise that two
|
|
322
|
+
* children are seen at once.
|
|
323
|
+
*
|
|
324
|
+
* `mode` travels with the slot rather than being read off the state, and it
|
|
325
|
+
* is what makes step 3's «men begge kan tas samtidig kl. 15:00» work: that
|
|
326
|
+
* offer appears while the machine still says `sequential`, and seating a
|
|
327
|
+
* parallel slot sequentially would submit the second child half an hour
|
|
328
|
+
* after a stylist who is free at the hour itself. Carrying it here also
|
|
329
|
+
* removes the ordering trap — `setPartyMode` clears the slot, so a caller
|
|
330
|
+
* that dispatched the two actions the other way round would throw away the
|
|
331
|
+
* time the parent had just accepted.
|
|
332
|
+
*/
|
|
333
|
+
{
|
|
334
|
+
type: 'pickPartySlot';
|
|
335
|
+
startTs: number;
|
|
336
|
+
resourceIds: string[];
|
|
337
|
+
mode: WizardState['partyMode'];
|
|
338
|
+
} | {
|
|
339
|
+
type: 'slotTaken';
|
|
340
|
+
} |
|
|
341
|
+
/**
|
|
342
|
+
* A submission the create route refused, in the vocabulary `WizardState.error`
|
|
343
|
+
* already speaks.
|
|
344
|
+
*
|
|
345
|
+
* Without it the four codes below are a union nothing can produce: the route
|
|
346
|
+
* answers `conflict`, `invalidInput`, `unconfigured` and `upstreamError`, and
|
|
347
|
+
* step 4 has a Norwegian sentence for each, but there was no action that could
|
|
348
|
+
* put one into `state.error` — so the failure would have had to be held beside
|
|
349
|
+
* the machine, in a second error slot the machine's own clearing rules never
|
|
350
|
+
* touch.
|
|
351
|
+
*
|
|
352
|
+
* `slotTaken` and `maxParty` are excluded rather than admitted. Each already
|
|
353
|
+
* has an action that does more than raise a code — `slotTaken` drops the slot
|
|
354
|
+
* and sends the visitor back to step 3, `addService` refuses the child while
|
|
355
|
+
* leaving the ones already chosen alone — and a second spelling of either is
|
|
356
|
+
* how a stolen slot ends up raised without the slot being dropped.
|
|
357
|
+
*/
|
|
358
|
+
{
|
|
359
|
+
type: 'submitFailed';
|
|
360
|
+
error: Exclude<NonNullable<WizardState['error']>, 'slotTaken' | 'maxParty'>;
|
|
361
|
+
} | {
|
|
362
|
+
type: 'goToStep';
|
|
363
|
+
step: WizardStep;
|
|
364
|
+
} | {
|
|
365
|
+
type: 'setContact';
|
|
366
|
+
field: keyof WizardState['contact'];
|
|
367
|
+
value: string;
|
|
368
|
+
} |
|
|
369
|
+
/**
|
|
370
|
+
* The two answers about the child, written onto the line item they belong to.
|
|
371
|
+
*
|
|
372
|
+
* Indexed rather than keyed by service, because two children in one party can
|
|
373
|
+
* genuinely share a service and the index is the only thing that tells those
|
|
374
|
+
* rows apart — the same reason step 4 keys its fields on it.
|
|
375
|
+
*
|
|
376
|
+
* Split over the two fields rather than taking a `Partial<WizardItem>`, so a
|
|
377
|
+
* caller cannot reach through this action and overwrite `service`, which is
|
|
378
|
+
* step 1's answer and not step 4's to change.
|
|
379
|
+
*/
|
|
380
|
+
{
|
|
381
|
+
type: 'setItemField';
|
|
382
|
+
index: number;
|
|
383
|
+
field: 'bookedForName';
|
|
384
|
+
value: string;
|
|
385
|
+
} |
|
|
386
|
+
/** `null` is «cleared», which an emptied year input is and `undefined` would
|
|
387
|
+
* be ambiguous about — a patch that says nothing versus one that says nothing
|
|
388
|
+
* is there. */
|
|
389
|
+
{
|
|
390
|
+
type: 'setItemField';
|
|
391
|
+
index: number;
|
|
392
|
+
field: 'bookedForBirthYear';
|
|
393
|
+
value: number | null;
|
|
394
|
+
} |
|
|
395
|
+
/** A saved child picked for a guest line on «Bekreft» (a family chip); `null` detaches. */
|
|
396
|
+
{
|
|
397
|
+
type: 'setItemField';
|
|
398
|
+
index: number;
|
|
399
|
+
field: 'bookedForPersonId';
|
|
400
|
+
value: string | null;
|
|
401
|
+
} | {
|
|
402
|
+
type: 'setNotes';
|
|
403
|
+
value: string;
|
|
404
|
+
} |
|
|
405
|
+
/** One action for both boxes rather than two near-identical ones: they are the
|
|
406
|
+
* same gesture, and the pair that must never be confused — a marketing opt-in
|
|
407
|
+
* standing in for terms — is harder to write by accident when the difference
|
|
408
|
+
* is a required discriminant. */
|
|
409
|
+
{
|
|
410
|
+
type: 'setConsent';
|
|
411
|
+
which: 'terms' | 'marketing';
|
|
412
|
+
accepted: boolean;
|
|
413
|
+
} |
|
|
414
|
+
/**
|
|
415
|
+
* A «Bestill igjen» link, applied. The shell dispatches this rather than
|
|
416
|
+
* calling `applyPrefill` on the state it holds, because `useReducer` is the
|
|
417
|
+
* only writer of that state — see `applyPrefill` for the rules.
|
|
418
|
+
*/
|
|
419
|
+
{
|
|
420
|
+
type: 'prefill';
|
|
421
|
+
prefill: WizardPrefill;
|
|
422
|
+
catalogue: WizardCatalogue;
|
|
423
|
+
};
|
|
424
|
+
/**
|
|
425
|
+
* What a `/min-side` «Bestill igjen» link can carry into the wizard:
|
|
426
|
+
* `?service=<id>&stylist=<id>` off the query string, and the child's name
|
|
427
|
+
* from the tab's session store (`rebook-store.ts`) — never from the URL.
|
|
428
|
+
*
|
|
429
|
+
* Every field is optional AND nullable, because `URLSearchParams.get` answers
|
|
430
|
+
* `null` for a missing key and the shell should not have to translate that.
|
|
431
|
+
* An empty string means the same as absent.
|
|
432
|
+
*/
|
|
433
|
+
export interface WizardPrefill {
|
|
434
|
+
serviceId?: string | null;
|
|
435
|
+
/** An id, or — from a «Bestill hos Siv» link — a display name or its slug. */
|
|
436
|
+
resourceId?: string | null;
|
|
437
|
+
who?: string | null;
|
|
438
|
+
/**
|
|
439
|
+
* `?antall=`: how many children. Clamped to the service's `maxPerBooking`
|
|
440
|
+
* and only meaningful for a kids' service, where it pre-creates that many
|
|
441
|
+
* lines; ignored for anything else.
|
|
442
|
+
*/
|
|
443
|
+
party?: number | null;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* What a prefill is checked against — the services step 1 would let the visitor
|
|
447
|
+
* tap, and the stylists step 2 would show for them.
|
|
448
|
+
*
|
|
449
|
+
* The SHELL decides what goes in here, and that is the point: the machine has
|
|
450
|
+
* no `bookableOnline` and no fetched stylist list, so «this id is one the
|
|
451
|
+
* visitor could have chosen» is a fact only the caller can supply. An id absent
|
|
452
|
+
* from the catalogue is ignored, whatever the link said.
|
|
453
|
+
*/
|
|
454
|
+
export interface WizardCatalogue {
|
|
455
|
+
services: readonly WizardService[];
|
|
456
|
+
resources: ReadonlyArray<{
|
|
457
|
+
id: string;
|
|
458
|
+
name?: string;
|
|
459
|
+
serviceIds: readonly string[];
|
|
460
|
+
}>;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The engine's own bound on `booked_for_name` — `maxLength: 200` in the SDK's
|
|
464
|
+
* OpenAPI document. Step 4's input has no `maxLength`, so this is the one limit
|
|
465
|
+
* that exists, and a prefilled name is cut to it rather than being carried into
|
|
466
|
+
* a body the engine answers 400 to.
|
|
467
|
+
*/
|
|
468
|
+
export declare const BOOKED_FOR_NAME_MAX_LENGTH = 200;
|
|
469
|
+
export declare function initialState(): WizardState;
|
|
470
|
+
/** The guest child in chair `n` (1-based) — a chip on step 1, or a sibling a link added. */
|
|
471
|
+
export declare function guestChild(n: number): WizardPerson;
|
|
472
|
+
/**
|
|
473
|
+
* True when the visitor may leave the current step — and on `details`, when they
|
|
474
|
+
* may submit at all. Answers about `state.step` only, so a Neste button can be
|
|
475
|
+
* `disabled={!canAdvance(state)}` and nothing else.
|
|
476
|
+
*/
|
|
477
|
+
export declare function canAdvance(state: WizardState): boolean;
|
|
478
|
+
/**
|
|
479
|
+
* True when a step may be jumped to directly — the predicate behind whether a
|
|
480
|
+
* progress dot is tappable, and the guard `goToStep` applies to itself.
|
|
481
|
+
*
|
|
482
|
+
* Bounded by what is *behind* the target, never the target itself: reaching
|
|
483
|
+
* `details` requires a service and a time, but obviously not the contact
|
|
484
|
+
* details you are going there to type. Going back is therefore always allowed,
|
|
485
|
+
* because nothing precedes `service` — which is the case that matters, since
|
|
486
|
+
* back is what the dots are mostly for.
|
|
487
|
+
*/
|
|
488
|
+
export declare function canGoToStep(state: WizardState, step: WizardStep): boolean;
|
|
489
|
+
/**
|
|
490
|
+
* When each child in the party sits down.
|
|
491
|
+
*
|
|
492
|
+
* `sequential` is the report's default and the ordinary case — one stylist,
|
|
493
|
+
* «rett etter hverandre», so the second child starts when the first is
|
|
494
|
+
* finished. `parallel` is two stylists at once, and every line starts together.
|
|
495
|
+
*
|
|
496
|
+
* The engine takes a `start_ts` per line item rather than one for the visit, so
|
|
497
|
+
* somebody has to do this arithmetic, and it is a rule about the booking rather
|
|
498
|
+
* than about a screen. Step 4 submits with it and the confirmation card reads
|
|
499
|
+
* «15:00 Jonas / 15:30 Emma» off the same function, which is the only way those
|
|
500
|
+
* two can be made to agree.
|
|
501
|
+
*
|
|
502
|
+
* Task 9 owns the slot SEARCH for a party; this is only the seating of a slot
|
|
503
|
+
* already chosen.
|
|
504
|
+
*/
|
|
505
|
+
export declare function itemStartTimes(items: WizardItem[], startTs: number, partyMode: WizardState['partyMode']): number[];
|
|
506
|
+
/**
|
|
507
|
+
* How long the family is in the salon — the number the summary bar quotes as
|
|
508
|
+
* «60 min totalt» and the length of the .ics entry.
|
|
509
|
+
*
|
|
510
|
+
* The two modes are two different visits: `sequential` is one chair used twice,
|
|
511
|
+
* so the minutes add up, while `parallel` is two chairs at once and the family
|
|
512
|
+
* leaves when the *longest* cut finishes. Quoting the sum for a parallel party
|
|
513
|
+
* would tell a parent to set aside an hour for a visit that takes half of one,
|
|
514
|
+
* which is the whole reason «To frisører samtidig» is worth offering.
|
|
515
|
+
*
|
|
516
|
+
* `Math.max` seeded with 0 rather than spread over the durations alone:
|
|
517
|
+
* `Math.max()` of nothing is `-Infinity`, and an empty basket is a visit of no
|
|
518
|
+
* length rather than a negative one.
|
|
519
|
+
*/
|
|
520
|
+
export declare function visitMinutes(items: WizardItem[], partyMode: WizardState['partyMode']): number;
|
|
521
|
+
/**
|
|
522
|
+
* How much EARLIER a family has to start than a single first child would.
|
|
523
|
+
*
|
|
524
|
+
* The schedule endpoint answers for one service, so `lastStartTs` is «the last
|
|
525
|
+
* start a Gutteklipp fits» — the last minute at which that service's prep,
|
|
526
|
+
* duration and cleanup all land inside the day's window. A family needs its
|
|
527
|
+
* whole chain to fit, so its own last start is earlier by exactly the part of
|
|
528
|
+
* the chain that hangs off the end of the first child's busy span.
|
|
529
|
+
*
|
|
530
|
+
* NOT `visitMinutes - firstDuration`, which was the first attempt and is wrong
|
|
531
|
+
* in a way that only shows on a mixed basket: `visitMinutes` deliberately
|
|
532
|
+
* excludes the LAST child's cleanup — the family has left by then — so the
|
|
533
|
+
* subtraction silently keeps the FIRST child's cleanup in place of the last
|
|
534
|
+
* one's. Where the later service has the bigger `bufferAfterMinutes` the cutoff
|
|
535
|
+
* comes out too late, which is the error in the direction that matters: it says
|
|
536
|
+
* «Fullt» about an afternoon that is actually over.
|
|
537
|
+
*
|
|
538
|
+
* Sequential is the tail of the chain: every child after the first costs their
|
|
539
|
+
* prep, their duration and their cleanup, and the first child's own cleanup is
|
|
540
|
+
* already inside the number being adjusted. Parallel is one chair each, so what
|
|
541
|
+
* has to fit is the LONGEST busy span rather than the sum.
|
|
542
|
+
*/
|
|
543
|
+
export declare function visitTailMinutes(items: WizardItem[], partyMode: WizardState['partyMode']): number;
|
|
544
|
+
/** When the family leaves. The same arithmetic as `visitMinutes`, which is why
|
|
545
|
+
* it is that function and not a second copy of it: an .ics that ended before
|
|
546
|
+
* `visitMinutes` said the visit did would tell a calendar the parent was free
|
|
547
|
+
* while a child was still in the chair. */
|
|
548
|
+
export declare function visitEndTs(items: WizardItem[], startTs: number, partyMode: WizardState['partyMode']): number;
|
|
549
|
+
/**
|
|
550
|
+
* Whether step 2 asks how the party is seen.
|
|
551
|
+
*
|
|
552
|
+
* One child is not a party, and «Samme frisør, rett etter hverandre» is not a
|
|
553
|
+
* choice when there is nobody to be after. The machine answers rather than the
|
|
554
|
+
* component so that the picker and the search cannot disagree about when a
|
|
555
|
+
* booking has become a family visit.
|
|
556
|
+
*/
|
|
557
|
+
export declare function showsPartyMode(state: WizardState): boolean;
|
|
558
|
+
/**
|
|
559
|
+
* Who is cutting whose hair, one answer per line item.
|
|
560
|
+
*
|
|
561
|
+
* The single reader for both shapes the machine can be in: a party slot names a
|
|
562
|
+
* stylist per child, and everything else has one answer for the whole visit —
|
|
563
|
+
* including `null`, which is «Første ledige» still to be resolved by the engine.
|
|
564
|
+
*
|
|
565
|
+
* Step 4's submission and the confirmation card both read it, which is the only
|
|
566
|
+
* way «hos Marcus» on the card can be guaranteed to be the `resource_id` that
|
|
567
|
+
* was actually booked for that child.
|
|
568
|
+
*/
|
|
569
|
+
export declare function itemResourceIds(state: WizardState): Array<string | null>;
|
|
570
|
+
export type WizardConfig = Pick<BookingConfig, 'party' | 'categories' | 'fallbackCategory' | 'timeZone' | 'locale' | 'currency' | 'dayparts' | 'labels'>;
|
|
571
|
+
export interface Wizard {
|
|
572
|
+
/**
|
|
573
|
+
* The most chairs one visit can take (`party.maxPeople`). The business's own
|
|
574
|
+
* ceiling is per service (`maxPerBooking`), and step 2 still honours it; this
|
|
575
|
+
* is the ceiling of the question on step 1, before any service is known.
|
|
576
|
+
*/
|
|
577
|
+
readonly maxPeople: number;
|
|
578
|
+
initialState: typeof initialState;
|
|
579
|
+
reduce(state: WizardState, action: WizardAction): WizardState;
|
|
580
|
+
applyPrefill(state: WizardState, prefill: WizardPrefill, catalogue: WizardCatalogue): WizardState;
|
|
581
|
+
canAdvance: typeof canAdvance;
|
|
582
|
+
canGoToStep: typeof canGoToStep;
|
|
583
|
+
itemPriceOre(service: WizardService, startTs: number | null): number;
|
|
584
|
+
totalPriceOre(items: WizardItem[], startTs: number | null): number;
|
|
585
|
+
itemStartTimes: typeof itemStartTimes;
|
|
586
|
+
visitMinutes: typeof visitMinutes;
|
|
587
|
+
visitTailMinutes: typeof visitTailMinutes;
|
|
588
|
+
visitEndTs: typeof visitEndTs;
|
|
589
|
+
showsPartyMode: typeof showsPartyMode;
|
|
590
|
+
itemResourceIds: typeof itemResourceIds;
|
|
591
|
+
summaryLine(state: WizardState, resolveStylistName: (resourceId: string) => string | null, now?: number): string;
|
|
592
|
+
guestChild: typeof guestChild;
|
|
593
|
+
isGuestSeat: typeof isGuestSeat;
|
|
594
|
+
}
|
|
595
|
+
/** The machine for one site: its party ceiling, kids' categories, clock, currency and words. */
|
|
596
|
+
export declare function createWizard(config: WizardConfig): Wizard;
|
|
597
|
+
//#endregion
|
|
598
|
+
//# sourceMappingURL=machine.d.mts.map
|