@flamingo-stack/openframe-frontend-core 0.0.664 → 0.0.665-2372.7066.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"booking-form.d.ts","sourceRoot":"","sources":["../../../src/components/meeting-scheduler/booking-form.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAGvD,OAAO,EAEL,KAAK,uBAAuB,EAM5B,KAAK,mBAAmB,EAKzB,MAAM,sCAAsC,CAAC;AAsB9C;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yFAAyF;IACzF,IAAI,EAAE,uBAAuB,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,gFAAgF;IAChF,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,UAAU,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,gBAAgB,EAAE,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAiBD;6CAC6C;AAC7C,QAAA,MAAM,UAAU;;;;;CAKN,CAAC;AAOX,0GAA0G;AAC1G,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAYtD;iFACiF;AACjF,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,gBAIvD,CAAC;AAqJF,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,mBAAmB,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;qEACiE;IACjE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,uFAAuF;IACvF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,mFAAmF;IACnF,kBAAkB,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CACnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,EAC1B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,UAAU,EACV,QAAQ,EACR,SAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,UAAU,GACX,EAAE,gBAAgB,+BAoTlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,OAAO,EACP,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B;;qDAEiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,+BA2BA"}
1
+ {"version":3,"file":"booking-form.d.ts","sourceRoot":"","sources":["../../../src/components/meeting-scheduler/booking-form.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAGvD,OAAO,EAEL,KAAK,uBAAuB,EAQ5B,KAAK,mBAAmB,EAKzB,MAAM,sCAAsC,CAAC;AAsB9C;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yFAAyF;IACzF,IAAI,EAAE,uBAAuB,GAAG,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,gFAAgF;IAChF,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,UAAU,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,gBAAgB,EAAE,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAiBD;6CAC6C;AAC7C,QAAA,MAAM,UAAU;;;;;CAKN,CAAC;AAOX,0GAA0G;AAC1G,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAYtD;iFACiF;AACjF,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,gBAIvD,CAAC;AA+MF,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,mBAAmB,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;qEACiE;IACjE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,uFAAuF;IACvF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,mFAAmF;IACnF,kBAAkB,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CACnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,EAC1B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,UAAU,EACV,QAAQ,EACR,SAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,UAAU,GACX,EAAE,gBAAgB,+BAuTlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,OAAO,EACP,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B;;qDAEiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,+BA2BA"}
@@ -101,6 +101,18 @@ var optionValidator = (field, base) => base.refine((v) => !v || (_nullishCoalesc
101
101
  message: `Please choose a valid option for ${field.label}`
102
102
  });
103
103
  var DECIMAL_LITERAL_RE = /^-?\d+(\.\d+)?$/;
104
+ var MULTI_VALUE_SEPARATOR = ";";
105
+ var splitMultiValue = (value) => typeof value === "string" && value ? value.split(MULTI_VALUE_SEPARATOR) : [];
106
+ var PHONE_RE = /^\+?[\d\s().\-/]+(?:\s*(?:x|ext\.?)\s*\d+)?$/i;
107
+ var PHONE_MIN_DIGITS = 5;
108
+ var PHONE_MAX_DIGITS = 20;
109
+ var phoneDigits = (v) => v.replace(/\D/g, "").length;
110
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
111
+ var isRealIsoDate = (v) => {
112
+ if (!ISO_DATE_RE.test(v)) return false;
113
+ const d = /* @__PURE__ */ new Date(`${v}T00:00:00Z`);
114
+ return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === v;
115
+ };
104
116
  var FORM_FIELD_TYPES = {
105
117
  text: {
106
118
  kind: "string",
@@ -121,16 +133,88 @@ var FORM_FIELD_TYPES = {
121
133
  // accepts (`1e3`, `007`) to this before it is validated or sent.
122
134
  validator: (field, base) => base.max(32, { message: `${field.label} is too long` }).regex(DECIMAL_LITERAL_RE, { message: `${field.label} must be a number` })
123
135
  },
136
+ phone: {
137
+ kind: "string",
138
+ hasOptions: false,
139
+ validator: (field, base) => base.max(40, { message: `${field.label} is too long` }).refine(
140
+ (v) => !v || PHONE_RE.test(v) && phoneDigits(v) >= PHONE_MIN_DIGITS && phoneDigits(v) <= PHONE_MAX_DIGITS,
141
+ { message: `Please enter a valid phone number for ${field.label}` }
142
+ ),
143
+ placeholder: (field) => `Enter ${field.label}`
144
+ },
145
+ date: {
146
+ kind: "string",
147
+ hasOptions: false,
148
+ validator: (field, base) => base.refine((v) => !v || isRealIsoDate(v), { message: `${field.label} must be a date` })
149
+ },
124
150
  select: { kind: "string", hasOptions: true, validator: optionValidator },
125
151
  radio: { kind: "string", hasOptions: true, validator: optionValidator },
152
+ multiselect: {
153
+ kind: "string",
154
+ hasOptions: true,
155
+ validator: (field, base) => base.refine((v) => splitMultiValue(v).every((part) => (_nullishCoalesce(field.options, () => ( []))).includes(part)), {
156
+ message: `Please choose valid options for ${field.label}`
157
+ })
158
+ },
126
159
  checkbox: { kind: "boolean", hasOptions: false, validator: () => _zod.z.boolean() }
127
160
  };
128
161
  var SUPPORTED_FORM_FIELD_TYPES = Object.keys(FORM_FIELD_TYPES);
162
+ var FIELD_TYPE_CONTROLS = {
163
+ text: "text",
164
+ textarea: "textarea",
165
+ number: "number",
166
+ select: "select",
167
+ radio: "radio",
168
+ booleancheckbox: "checkbox",
169
+ phonenumber: "phone",
170
+ date: "date",
171
+ html: "display",
172
+ file: "unanswerable"
173
+ };
174
+ var DATA_TYPE_CONTROLS = {
175
+ string: "text",
176
+ number: "number",
177
+ enumeration: "select",
178
+ bool: "checkbox",
179
+ date: "date",
180
+ datetime: "date",
181
+ phone_number: "phone"
182
+ };
183
+ var hasDeclaredOptions = (field) => (_nullishCoalesce(_optionalChain([field, 'access', _2 => _2.options, 'optionalAccess', _3 => _3.length]), () => ( 0))) > 0;
184
+ function resolveFormFieldControl(field) {
185
+ const fieldType = (_nullishCoalesce(field.type, () => ( ""))).toLowerCase();
186
+ const withOptions = hasDeclaredOptions(field);
187
+ const usable = (r) => {
188
+ if (!r || r === "display" || r === "unanswerable") return r;
189
+ return FORM_FIELD_TYPES[r].hasOptions && !withOptions ? "text" : r;
190
+ };
191
+ if (fieldType === "checkbox") return withOptions ? "multiselect" : "checkbox";
192
+ if (fieldType.startsWith("calculation")) return "display";
193
+ const asControl = Object.prototype.hasOwnProperty.call(FORM_FIELD_TYPES, fieldType) ? fieldType : void 0;
194
+ return _nullishCoalesce(_nullishCoalesce(usable(_nullishCoalesce(FIELD_TYPE_CONTROLS[fieldType], () => ( asControl))), () => ( usable(DATA_TYPE_CONTROLS[(_nullishCoalesce(field.dataType, () => ( ""))).toLowerCase()]))), () => ( (withOptions ? "select" : "text")));
195
+ }
129
196
  var FORM_FIELD_TYPES_WITH_OPTIONS = SUPPORTED_FORM_FIELD_TYPES.filter(
130
197
  (type) => FORM_FIELD_TYPES[type].hasOptions
131
198
  );
132
- function isSupportedFormField(field) {
133
- return Object.prototype.hasOwnProperty.call(FORM_FIELD_TYPES, field.type);
199
+ function normalizeFormField(field) {
200
+ const control = resolveFormFieldControl(field);
201
+ if (control === "display" || control === "unanswerable") return null;
202
+ const normalized = {
203
+ ...field,
204
+ type: control,
205
+ hubspotType: _nullishCoalesce(field.hubspotType, () => ( field.type))
206
+ };
207
+ if (!FORM_FIELD_TYPES[control].hasOptions) {
208
+ delete normalized.options;
209
+ delete normalized.optionLabels;
210
+ }
211
+ return normalized;
212
+ }
213
+ function normalizeFormFields(fields) {
214
+ return fields.flatMap((f) => _nullishCoalesce(normalizeFormField(f), () => ( [])));
215
+ }
216
+ function blocksNativeBooking(field) {
217
+ return field.required && resolveFormFieldControl(field) === "unanswerable";
134
218
  }
135
219
  var BUILT_IN_BOOKING_FIELDS = [
136
220
  {
@@ -188,8 +272,8 @@ function isValidBcp47Locale(locale) {
188
272
  }
189
273
  function buildBookingSchema(formFields, legalConsent, slot) {
190
274
  const answers = {};
191
- for (const field of formFields) {
192
- if (!isSupportedFormField(field)) continue;
275
+ const drawn = normalizeFormFields(formFields);
276
+ for (const field of drawn) {
193
277
  const spec = FORM_FIELD_TYPES[field.type];
194
278
  let validator;
195
279
  if (spec.kind === "boolean") {
@@ -201,8 +285,8 @@ function buildBookingSchema(formFields, legalConsent, slot) {
201
285
  }
202
286
  answers[field.name] = validator;
203
287
  }
204
- const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _2 => _2.communicationConsentCheckboxes]), () => ( []))).filter((c) => c.required).map((c) => c.communicationTypeId);
205
- const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required);
288
+ const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _4 => _4.communicationConsentCheckboxes]), () => ( []))).filter((c) => c.required).map((c) => c.communicationTypeId);
289
+ const hasRequiredAnswers = drawn.some((f) => f.required);
206
290
  const answersObject = _zod.z.object(answers);
207
291
  return _zod.z.object({
208
292
  meetingId: _zod.z.string().min(1),
@@ -254,7 +338,7 @@ var BOOKING_IN_FLIGHT_MESSAGE = "Already submitting";
254
338
  function useMeetingBooking(options) {
255
339
  const { meetingId, apiBaseUrl = "", initialAvailability } = options;
256
340
  const queryClient = _reactquery.useQueryClient.call(void 0, );
257
- const seededOffset = _optionalChain([initialAvailability, 'optionalAccess', _3 => _3.meetingId]) === meetingId ? initialAvailability.monthOffset : void 0;
341
+ const seededOffset = _optionalChain([initialAvailability, 'optionalAccess', _5 => _5.meetingId]) === meetingId ? initialAvailability.monthOffset : void 0;
258
342
  const [monthOffset, setMonthOffsetState] = _react.useState.call(void 0, _nullishCoalesce(seededOffset, () => ( 0)));
259
343
  const setMonthOffset = _react.useCallback.call(void 0, (offset) => {
260
344
  setMonthOffsetState(Math.max(0, Math.min(_chunk6FD7ZEBYcjs.MAX_MONTH_OFFSET, offset)));
@@ -265,7 +349,7 @@ function useMeetingBooking(options) {
265
349
  prevMeetingIdRef.current = meetingId;
266
350
  setMonthOffsetState(0);
267
351
  }, [meetingId]);
268
- const seed = _optionalChain([initialAvailability, 'optionalAccess', _4 => _4.meetingId]) === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
352
+ const seed = _optionalChain([initialAvailability, 'optionalAccess', _6 => _6.meetingId]) === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
269
353
  const availabilityQuery = _reactquery.useQuery.call(void 0, {
270
354
  queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
271
355
  queryFn: async ({ signal }) => {
@@ -361,7 +445,9 @@ var evenSpan = (count, index) => {
361
445
  return Math.min(GRID_COLUMNS, Math.max(1, index < extra ? base + 1 : base));
362
446
  };
363
447
  var slotColumnClass = (row, slot, index) => _chunkX7CEWD6Vcjs.cn.call(void 0, row.length === 2 ? "col-span-1" : "col-span-2", SPAN_CLASS[_nullishCoalesce(slot.span, () => ( evenSpan(row.length, index)))]);
364
- var placeholderFor = (field) => _nullishCoalesce(field.placeholder, () => ( _optionalChain([fieldTypeSpec, 'call', _5 => _5(field.type), 'access', _6 => _6.placeholder, 'optionalCall', _7 => _7(field)])));
448
+ var placeholderFor = (field) => _nullishCoalesce(field.placeholder, () => ( _optionalChain([fieldTypeSpec, 'call', _7 => _7(field.type), 'access', _8 => _8.placeholder, 'optionalCall', _9 => _9(field)])));
449
+ var optionLabel = (field, value) => _nullishCoalesce(_optionalChain([field, 'access', _10 => _10.optionLabels, 'optionalAccess', _11 => _11[value]]), () => ( value));
450
+ var trimmed = (v) => String(_nullishCoalesce(v, () => ( ""))).trim();
365
451
  var canonicalNumber = (v) => {
366
452
  const s = String(_nullishCoalesce(v, () => ( ""))).trim();
367
453
  if (s === "" || DECIMAL_LITERAL_RE.test(s)) return s;
@@ -405,6 +491,32 @@ var FIELD_CONTROLS = {
405
491
  ...register(registerName, { setValueAs: canonicalNumber })
406
492
  }
407
493
  ),
494
+ phone: ({ field, id, registerName, error, register }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
495
+ _chunkQVSVRG7Icjs.Input,
496
+ {
497
+ id,
498
+ type: "tel",
499
+ inputMode: "tel",
500
+ autoComplete: "tel",
501
+ required: field.required,
502
+ "aria-invalid": Boolean(error),
503
+ placeholder: placeholderFor(field),
504
+ ...register(registerName, { setValueAs: trimmed })
505
+ }
506
+ ),
507
+ date: ({ field, id, registerName, error, register }) => (
508
+ // The native picker emits exactly the wire shape (`YYYY-MM-DD`).
509
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
510
+ _chunkQVSVRG7Icjs.Input,
511
+ {
512
+ id,
513
+ type: "date",
514
+ required: field.required,
515
+ "aria-invalid": Boolean(error),
516
+ ...register(registerName)
517
+ }
518
+ )
519
+ ),
408
520
  select: ({ field, id, registerName, error, control }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
409
521
  _reacthookform.Controller,
410
522
  {
@@ -412,7 +524,7 @@ var FIELD_CONTROLS = {
412
524
  name: registerName,
413
525
  render: ({ field: rhf }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk6FD7ZEBYcjs.Select, { value: _nullishCoalesce(rhf.value, () => ( "")), onValueChange: rhf.onChange, children: [
414
526
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectTrigger, { id, "aria-required": field.required || void 0, "aria-invalid": Boolean(error), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectValue, { placeholder: "Select\u2026" }) }),
415
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectContent, { children: (_nullishCoalesce(field.options, () => ( []))).map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectItem, { value: opt, children: opt }, opt)) })
527
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectContent, { children: (_nullishCoalesce(field.options, () => ( []))).map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.SelectItem, { value: opt, children: optionLabel(field, opt) }, opt)) })
416
528
  ] })
417
529
  }
418
530
  ),
@@ -430,12 +542,42 @@ var FIELD_CONTROLS = {
430
542
  "aria-invalid": Boolean(error),
431
543
  children: (_nullishCoalesce(field.options, () => ( []))).map((opt) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-[var(--spacing-system-xs)]", children: [
432
544
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6FD7ZEBYcjs.RadioGroupItem, { id: `${id}-${opt}`, value: opt }),
433
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGSD62VAYcjs.Label, { htmlFor: `${id}-${opt}`, children: opt })
545
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGSD62VAYcjs.Label, { htmlFor: `${id}-${opt}`, children: optionLabel(field, opt) })
434
546
  ] }, opt))
435
547
  }
436
548
  )
437
549
  }
438
550
  ),
551
+ multiselect: ({ field, id, registerName, error, control }) => (
552
+ // HubSpot's "multiple checkboxes": the answer is its `;`-joined value list,
553
+ // held in that wire shape so the validator and the POST see one string.
554
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
555
+ _reacthookform.Controller,
556
+ {
557
+ control,
558
+ name: registerName,
559
+ render: ({ field: rhf }) => {
560
+ const selected = splitMultiValue(rhf.value);
561
+ const toggle = (opt, on) => {
562
+ const next = (_nullishCoalesce(field.options, () => ( []))).filter((o) => o === opt ? on : selected.includes(o));
563
+ rhf.onChange(next.join(MULTI_VALUE_SEPARATOR));
564
+ };
565
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { id, role: "group", className: "flex flex-col gap-[var(--spacing-system-xs)]", children: (_nullishCoalesce(field.options, () => ( []))).map((opt) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-[var(--spacing-system-xs)]", children: [
566
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
567
+ _chunkQVSVRG7Icjs.Checkbox,
568
+ {
569
+ id: `${id}-${opt}`,
570
+ checked: selected.includes(opt),
571
+ onCheckedChange: (v) => toggle(opt, v === true),
572
+ "aria-invalid": Boolean(error)
573
+ }
574
+ ),
575
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGSD62VAYcjs.Label, { htmlFor: `${id}-${opt}`, children: optionLabel(field, opt) })
576
+ ] }, opt)) });
577
+ }
578
+ }
579
+ )
580
+ ),
439
581
  checkbox: ({ field, id, registerName, error, control }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
440
582
  _reacthookform.Controller,
441
583
  {
@@ -478,13 +620,13 @@ function BookingForm({
478
620
  getSignals
479
621
  }) {
480
622
  const { formFields, legalConsent } = availability;
481
- const supportedFields = _react.useMemo.call(void 0, () => formFields.filter(isSupportedFormField), [formFields]);
623
+ const supportedFields = _react.useMemo.call(void 0, () => normalizeFormFields(formFields), [formFields]);
482
624
  const schema = _react.useMemo.call(void 0,
483
625
  () => makeDeferredBookingSchema(supportedFields, legalConsent),
484
626
  [supportedFields, legalConsent]
485
627
  );
486
628
  const consentDefaults = _react.useMemo.call(void 0,
487
- () => (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _8 => _8.communicationConsentCheckboxes]), () => ( []))).map((c) => ({
629
+ () => (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _12 => _12.communicationConsentCheckboxes]), () => ( []))).map((c) => ({
488
630
  communicationTypeId: c.communicationTypeId,
489
631
  consented: false
490
632
  })),
@@ -515,8 +657,8 @@ function BookingForm({
515
657
  ..._nullishCoalesce(initialValues, () => ( {}))
516
658
  }
517
659
  });
518
- const priorConsents = _optionalChain([initialValues, 'optionalAccess', _9 => _9.legalConsentResponses]);
519
- const [consented, setConsented] = _react.useState.call(void 0, _optionalChain([initialValues, 'optionalAccess', _10 => _10[HOST_CONSENT_KEY]]) === true);
660
+ const priorConsents = _optionalChain([initialValues, 'optionalAccess', _13 => _13.legalConsentResponses]);
661
+ const [consented, setConsented] = _react.useState.call(void 0, _optionalChain([initialValues, 'optionalAccess', _14 => _14[HOST_CONSENT_KEY]]) === true);
520
662
  const [consentError, setConsentError] = _react.useState.call(void 0, null);
521
663
  const consentMissing = Boolean(consent) && !consented;
522
664
  _react.useEffect.call(void 0, () => {
@@ -525,7 +667,7 @@ function BookingForm({
525
667
  "legalConsentResponses",
526
668
  consentDefaults.map((next) => ({
527
669
  ...next,
528
- consented: _nullishCoalesce(_optionalChain([current, 'access', _11 => _11.find, 'call', _12 => _12((r) => r.communicationTypeId === next.communicationTypeId), 'optionalAccess', _13 => _13.consented]), () => ( false))
670
+ consented: _nullishCoalesce(_optionalChain([current, 'access', _15 => _15.find, 'call', _16 => _16((r) => r.communicationTypeId === next.communicationTypeId), 'optionalAccess', _17 => _17.consented]), () => ( false))
529
671
  })),
530
672
  { shouldDirty: false }
531
673
  );
@@ -547,12 +689,12 @@ function BookingForm({
547
689
  });
548
690
  });
549
691
  const submit = (event) => {
550
- if (consentMissing) setConsentError(_nullishCoalesce(_optionalChain([consent, 'optionalAccess', _14 => _14.errorMessage]), () => ( "Please agree to continue.")));
692
+ if (consentMissing) setConsentError(_nullishCoalesce(_optionalChain([consent, 'optionalAccess', _18 => _18.errorMessage]), () => ( "Please agree to continue.")));
551
693
  return submitValid(event);
552
694
  };
553
695
  const fieldError = (name) => {
554
- const err = _optionalChain([errors, 'access', _15 => _15.formFields, 'optionalAccess', _16 => _16[name]]);
555
- return _optionalChain([err, 'optionalAccess', _17 => _17.message]);
696
+ const err = _optionalChain([errors, 'access', _19 => _19.formFields, 'optionalAccess', _20 => _20[name]]);
697
+ return _optionalChain([err, 'optionalAccess', _21 => _21.message]);
556
698
  };
557
699
  const renderField = (field, where) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkQVSVRG7Icjs.FieldWrapper, { label: field.label, htmlFor: where.id, required: field.required, error: where.error, children: FIELD_CONTROLS[field.type]({
558
700
  field,
@@ -565,7 +707,7 @@ function BookingForm({
565
707
  const builtInFields = Object.fromEntries(
566
708
  BUILT_IN_BOOKING_FIELDS.map((field) => [
567
709
  field.name,
568
- renderField(field, { id: builtInId(field.name), registerName: field.name, error: _optionalChain([errors, 'access', _18 => _18[field.name], 'optionalAccess', _19 => _19.message]) })
710
+ renderField(field, { id: builtInId(field.name), registerName: field.name, error: _optionalChain([errors, 'access', _22 => _22[field.name], 'optionalAccess', _23 => _23.message]) })
569
711
  ])
570
712
  );
571
713
  const renderDeclaredField = (field) => renderField(field, {
@@ -626,7 +768,7 @@ function BookingForm({
626
768
  // box doesn't shove the submit button down the card. With no error the
627
769
  // wrapper is `display:contents` and the panel below is the flex item,
628
770
  // exactly as it was.
629
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkQVSVRG7Icjs.FieldWrapper, { error: _optionalChain([errors, 'access', _20 => _20.legalConsentResponses, 'optionalAccess', _21 => _21.message]), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-[var(--spacing-system-xs)]", children: [
771
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkQVSVRG7Icjs.FieldWrapper, { error: _optionalChain([errors, 'access', _24 => _24.legalConsentResponses, 'optionalAccess', _25 => _25.message]), children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-[var(--spacing-system-xs)]", children: [
630
772
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-secondary text-h6", children: legalConsent.processingConsentText }),
631
773
  legalConsent.communicationConsentText && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-secondary text-h6", children: legalConsent.communicationConsentText }),
632
774
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -645,7 +787,7 @@ function BookingForm({
645
787
  _chunk6FD7ZEBYcjs.CheckboxBlock,
646
788
  {
647
789
  id: `ms-consent-${box.communicationTypeId}`,
648
- checked: _nullishCoalesce(_optionalChain([current, 'optionalAccess', _22 => _22.consented]), () => ( false)),
790
+ checked: _nullishCoalesce(_optionalChain([current, 'optionalAccess', _26 => _26.consented]), () => ( false)),
649
791
  onCheckedChange: (v) => rhf.onChange(
650
792
  responses.map(
651
793
  (r) => r.communicationTypeId === box.communicationTypeId ? { ...r, consented: v } : r
@@ -740,7 +882,7 @@ function zoneLabel(tz) {
740
882
  timeZone: tz,
741
883
  timeZoneName: "shortOffset"
742
884
  }).formatToParts(/* @__PURE__ */ new Date());
743
- const offset = _nullishCoalesce(_optionalChain([parts, 'access', _23 => _23.find, 'call', _24 => _24((p) => p.type === "timeZoneName"), 'optionalAccess', _25 => _25.value]), () => ( ""));
885
+ const offset = _nullishCoalesce(_optionalChain([parts, 'access', _27 => _27.find, 'call', _28 => _28((p) => p.type === "timeZoneName"), 'optionalAccess', _29 => _29.value]), () => ( ""));
744
886
  return offset ? `${tz.replace(/_/g, " ")} (${offset})` : tz.replace(/_/g, " ");
745
887
  } catch (e4) {
746
888
  return tz.replace(/_/g, " ");
@@ -867,7 +1009,7 @@ function SchedulerContextPanel({
867
1009
  value: timezone,
868
1010
  disabled,
869
1011
  onChange: (tz) => {
870
- if (tz) _optionalChain([onTimezoneChange, 'optionalCall', _26 => _26(tz)]);
1012
+ if (tz) _optionalChain([onTimezoneChange, 'optionalCall', _30 => _30(tz)]);
871
1013
  },
872
1014
  options: zoneOptions,
873
1015
  placeholder: "Search timezone\u2026",
@@ -951,7 +1093,7 @@ function DayCalendar({ monthOffset, onMonthOffsetChange, selectedDay, onSelectDa
951
1093
  const startMonth = new Date(now.getFullYear(), now.getMonth(), 1);
952
1094
  const endMonth = new Date(now.getFullYear(), now.getMonth() + _chunk6FD7ZEBYcjs.MAX_MONTH_OFFSET + 1, 0);
953
1095
  const visibleMonth = new Date(now.getFullYear(), now.getMonth() + monthOffset, 1);
954
- const dayHasNoSlots = _react.useCallback.call(void 0, (day) => !_optionalChain([slotsByDay, 'optionalAccess', _27 => _27.has, 'call', _28 => _28(calendarDayKey(day))]), [slotsByDay]);
1096
+ const dayHasNoSlots = _react.useCallback.call(void 0, (day) => !_optionalChain([slotsByDay, 'optionalAccess', _31 => _31.has, 'call', _32 => _32(calendarDayKey(day))]), [slotsByDay]);
955
1097
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex min-h-0 flex-1 flex-col", "aria-busy": slotsByDay === null, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
956
1098
  _chunk6FD7ZEBYcjs.DatePickerCalendar,
957
1099
  {
@@ -969,7 +1111,7 @@ function DayCalendar({ monthOffset, onMonthOffsetChange, selectedDay, onSelectDa
969
1111
  const day = value;
970
1112
  if (!day) return;
971
1113
  const key = calendarDayKey(day);
972
- if (_optionalChain([slotsByDay, 'optionalAccess', _29 => _29.has, 'call', _30 => _30(key)])) onSelectDay(key);
1114
+ if (_optionalChain([slotsByDay, 'optionalAccess', _33 => _33.has, 'call', _34 => _34(key)])) onSelectDay(key);
973
1115
  },
974
1116
  disabledDays: slotsByDay === null ? DISABLE_EVERY_DAY : dayHasNoSlots
975
1117
  }
@@ -1193,7 +1335,7 @@ function MeetingSchedulerDirectory({
1193
1335
  };
1194
1336
  }, [initialData, includeAll, apiBaseUrl]);
1195
1337
  const rows = _react.useMemo.call(void 0, () => {
1196
- const purposes = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _31 => _31.purposes]), () => ( []));
1338
+ const purposes = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _35 => _35.purposes]), () => ( []));
1197
1339
  return purposes.flatMap((p) => p.links.map((link) => ({ link, audienceLabel: p.label })));
1198
1340
  }, [data]);
1199
1341
  const totalPages = _chunkVTYP25OWcjs.pageCount.call(void 0, rows.length, pageSize);
@@ -1293,7 +1435,7 @@ function SchedulerDegradedCard({
1293
1435
  ] });
1294
1436
  }
1295
1437
  function isNativelyBookable(availability) {
1296
- if (!availability.formFields.every(isSupportedFormField)) return false;
1438
+ if (availability.formFields.some(blocksNativeBooking)) return false;
1297
1439
  const consent = availability.legalConsent;
1298
1440
  if (consent) {
1299
1441
  if (typeof consent.processingConsentText !== "string") return false;
@@ -1349,7 +1491,7 @@ function HubSpotMeetingScheduler({
1349
1491
  onStageChangeRef.current = onStageChange;
1350
1492
  });
1351
1493
  _react.useEffect.call(void 0, () => {
1352
- _optionalChain([onStageChangeRef, 'access', _32 => _32.current, 'optionalCall', _33 => _33(stage)]);
1494
+ _optionalChain([onStageChangeRef, 'access', _36 => _36.current, 'optionalCall', _37 => _37(stage)]);
1353
1495
  }, [stage]);
1354
1496
  const [stash, setStash] = _react.useState.call(void 0, null);
1355
1497
  const inFlightRef = _react.useRef.call(void 0, false);
@@ -1380,8 +1522,8 @@ function HubSpotMeetingScheduler({
1380
1522
  _react.useEffect.call(void 0, () => {
1381
1523
  autoAdvanceCount.current = 0;
1382
1524
  }, [meetingId]);
1383
- const durations = _react.useMemo.call(void 0, () => _nullishCoalesce(_optionalChain([availability, 'optionalAccess', _34 => _34.durationsMs]), () => ( [])), [_optionalChain([availability, 'optionalAccess', _35 => _35.durationsMs])]);
1384
- const shownHosts = _nullishCoalesce(_nullishCoalesce(hosts, () => ( _optionalChain([availability, 'optionalAccess', _36 => _36.hosts]))), () => ( []));
1525
+ const durations = _react.useMemo.call(void 0, () => _nullishCoalesce(_optionalChain([availability, 'optionalAccess', _38 => _38.durationsMs]), () => ( [])), [_optionalChain([availability, 'optionalAccess', _39 => _39.durationsMs])]);
1526
+ const shownHosts = _nullishCoalesce(_nullishCoalesce(hosts, () => ( _optionalChain([availability, 'optionalAccess', _40 => _40.hosts]))), () => ( []));
1385
1527
  const durationMs = _nullishCoalesce(_nullishCoalesce(pickedDurationMs, () => ( durations[0])), () => ( null));
1386
1528
  const slots = _react.useMemo.call(void 0, () => {
1387
1529
  if (!availability || durationMs == null) return [];
@@ -1440,7 +1582,7 @@ function HubSpotMeetingScheduler({
1440
1582
  if (result.ok && result.confirmation) {
1441
1583
  setConfirmation(result.confirmation);
1442
1584
  setStep("confirmed");
1443
- _optionalChain([onBooked, 'optionalCall', _37 => _37(result.confirmation)]);
1585
+ _optionalChain([onBooked, 'optionalCall', _41 => _41(result.confirmation)]);
1444
1586
  return;
1445
1587
  }
1446
1588
  const code = _nullishCoalesce(result.code, () => ( "TEMPORARILY_UNAVAILABLE"));
@@ -1488,8 +1630,8 @@ function HubSpotMeetingScheduler({
1488
1630
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: stepPanelClass, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1489
1631
  BookingFormSkeleton,
1490
1632
  {
1491
- fieldRows: _optionalChain([detailsFormProps, 'optionalAccess', _38 => _38.fieldRows]),
1492
- consent: Boolean(_optionalChain([detailsFormProps, 'optionalAccess', _39 => _39.consent])),
1633
+ fieldRows: _optionalChain([detailsFormProps, 'optionalAccess', _42 => _42.fieldRows]),
1634
+ consent: Boolean(_optionalChain([detailsFormProps, 'optionalAccess', _43 => _43.consent])),
1493
1635
  footerNote: preset.footerNote
1494
1636
  }
1495
1637
  ) })
@@ -1550,7 +1692,7 @@ function HubSpotMeetingScheduler({
1550
1692
  deferSlot: detailsFirst,
1551
1693
  submitLabel: preset.submitLabel,
1552
1694
  footerNote: preset.footerNote,
1553
- initialValues: detailsFirst ? _optionalChain([stash, 'optionalAccess', _40 => _40.payload]) : void 0,
1695
+ initialValues: detailsFirst ? _optionalChain([stash, 'optionalAccess', _44 => _44.payload]) : void 0,
1554
1696
  isSubmitting,
1555
1697
  onSubmit: detailsFirst ? stashDetails : handleSubmit,
1556
1698
  honeypotInputProps,