@flamingo-stack/openframe-frontend-core 0.0.574-snapshot.20260827223923 → 0.0.574
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/dist/components/meeting-scheduler/index.cjs +93 -96
- package/dist/components/meeting-scheduler/index.cjs.map +1 -1
- package/dist/components/meeting-scheduler/index.js +90 -93
- package/dist/components/meeting-scheduler/index.js.map +1 -1
- package/dist/hooks/use-meeting-booking.d.ts +1 -1
- package/dist/hooks/use-meeting-booking.d.ts.map +1 -1
- package/dist/schemas/meeting-booking-schema.cjs +0 -9
- package/dist/schemas/meeting-booking-schema.cjs.map +1 -1
- package/dist/schemas/meeting-booking-schema.d.ts +1 -10
- package/dist/schemas/meeting-booking-schema.d.ts.map +1 -1
- package/dist/schemas/meeting-booking-schema.js +1 -9
- package/dist/schemas/meeting-booking-schema.js.map +1 -1
- package/package.json +1 -1
- package/src/hooks/use-meeting-booking.ts +13 -6
- package/src/schemas/meeting-booking-schema.ts +2 -18
|
@@ -79,104 +79,13 @@ var _react = require('react');
|
|
|
79
79
|
// src/hooks/use-meeting-booking.ts
|
|
80
80
|
var _reactquery = require('@tanstack/react-query');
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
// src/schemas/meeting-booking-schema.ts
|
|
84
|
-
var _zod = require('zod');
|
|
85
|
-
var MEETING_BOOKING_ERROR_CODES = [
|
|
82
|
+
var KNOWN_CODES = [
|
|
86
83
|
"SLOT_TAKEN",
|
|
87
84
|
"VALIDATION",
|
|
88
|
-
"INVALID_EMAIL",
|
|
89
85
|
"LINK_GONE",
|
|
90
86
|
"TEMPORARILY_UNAVAILABLE",
|
|
91
87
|
"MEETING_UNAVAILABLE"
|
|
92
88
|
];
|
|
93
|
-
var SUPPORTED_FORM_FIELD_TYPES = ["text", "textarea", "select", "radio", "checkbox"];
|
|
94
|
-
function isSupportedFormField(field) {
|
|
95
|
-
return SUPPORTED_FORM_FIELD_TYPES.includes(field.type);
|
|
96
|
-
}
|
|
97
|
-
var IANA_TZ_RE = /^(?:UTC|[A-Za-z_]+(?:\/[A-Za-z0-9_+-]+)+)$/;
|
|
98
|
-
function isValidIanaTimezone(tz) {
|
|
99
|
-
if (!IANA_TZ_RE.test(tz)) return false;
|
|
100
|
-
try {
|
|
101
|
-
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
102
|
-
return true;
|
|
103
|
-
} catch (e) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function isValidBcp47Locale(locale) {
|
|
108
|
-
try {
|
|
109
|
-
return Intl.getCanonicalLocales(locale).length > 0;
|
|
110
|
-
} catch (e2) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function makeBookingSchema(formFields, legalConsent) {
|
|
115
|
-
const answers = {};
|
|
116
|
-
for (const field of formFields) {
|
|
117
|
-
if (!isSupportedFormField(field)) continue;
|
|
118
|
-
let validator;
|
|
119
|
-
switch (field.type) {
|
|
120
|
-
case "checkbox":
|
|
121
|
-
validator = _zod.z.boolean();
|
|
122
|
-
break;
|
|
123
|
-
case "select":
|
|
124
|
-
case "radio":
|
|
125
|
-
validator = _zod.z.string().refine((v) => !v || (_nullishCoalesce(field.options, () => ( []))).includes(v), {
|
|
126
|
-
message: `Please choose a valid option for ${field.label}`
|
|
127
|
-
});
|
|
128
|
-
break;
|
|
129
|
-
case "textarea":
|
|
130
|
-
validator = _zod.z.string().max(5e3, { message: `${field.label} is too long` });
|
|
131
|
-
break;
|
|
132
|
-
case "text":
|
|
133
|
-
default:
|
|
134
|
-
validator = _zod.z.string().max(1e3, { message: `${field.label} is too long` });
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
if (field.required) {
|
|
138
|
-
validator = field.type === "checkbox" ? _zod.z.literal(true, { message: `${field.label} is required` }) : validator.min(1, { message: `${field.label} is required` });
|
|
139
|
-
} else if (field.type !== "checkbox") {
|
|
140
|
-
validator = validator.optional().or(_zod.z.literal(""));
|
|
141
|
-
} else {
|
|
142
|
-
validator = _zod.z.boolean().optional();
|
|
143
|
-
}
|
|
144
|
-
answers[field.name] = validator;
|
|
145
|
-
}
|
|
146
|
-
const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _2 => _2.communicationConsentCheckboxes]), () => ( []))).filter((c) => c.required).map((c) => c.communicationTypeId);
|
|
147
|
-
const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required);
|
|
148
|
-
const answersObject = _zod.z.object(answers);
|
|
149
|
-
return _zod.z.object({
|
|
150
|
-
meetingId: _zod.z.string().min(1),
|
|
151
|
-
startTimeMs: _zod.z.number().int().positive(),
|
|
152
|
-
durationMs: _zod.z.number().int().positive(),
|
|
153
|
-
firstName: _zod.z.string().min(1, { message: "First name is required" }).max(255),
|
|
154
|
-
lastName: _zod.z.string().min(1, { message: "Last name is required" }).max(255),
|
|
155
|
-
email: _zod.z.string().email({ message: "Please enter a valid email address" }).max(255),
|
|
156
|
-
timezone: _zod.z.string().refine(isValidIanaTimezone, { message: "Invalid timezone" }),
|
|
157
|
-
locale: _zod.z.string().refine(isValidBcp47Locale, { message: "Invalid locale" }).optional(),
|
|
158
|
-
// Plain `.optional()` (no `.default()`) so zod's input and output types
|
|
159
|
-
// match — react-hook-form's zodResolver needs them identical, and the
|
|
160
|
-
// server handles `undefined` explicitly anyway. REQUIRED when the link
|
|
161
|
-
// declares required questions (see `hasRequiredAnswers` above).
|
|
162
|
-
formFields: hasRequiredAnswers ? answersObject : answersObject.optional(),
|
|
163
|
-
legalConsentResponses: _zod.z.array(_zod.z.object({ communicationTypeId: _zod.z.string(), consented: _zod.z.boolean() })).optional()
|
|
164
|
-
}).superRefine((data, ctx) => {
|
|
165
|
-
const responses = _nullishCoalesce(data.legalConsentResponses, () => ( []));
|
|
166
|
-
const ok = requiredConsentIds.every(
|
|
167
|
-
(id) => responses.some((r) => r.communicationTypeId === id && r.consented === true)
|
|
168
|
-
);
|
|
169
|
-
if (!ok) {
|
|
170
|
-
ctx.addIssue({
|
|
171
|
-
code: _zod.z.ZodIssueCode.custom,
|
|
172
|
-
path: ["legalConsentResponses"],
|
|
173
|
-
message: "Required consent checkboxes must be accepted"
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// src/hooks/use-meeting-booking.ts
|
|
180
89
|
var AVAILABILITY_STALE_MS = 0;
|
|
181
90
|
function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
182
91
|
return ["meeting-availability", apiBaseUrl, meetingId, monthOffset];
|
|
@@ -184,7 +93,7 @@ function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
|
184
93
|
function useMeetingBooking(options) {
|
|
185
94
|
const { meetingId, apiBaseUrl = "", initialAvailability } = options;
|
|
186
95
|
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
187
|
-
const seededOffset = _optionalChain([initialAvailability, 'optionalAccess',
|
|
96
|
+
const seededOffset = _optionalChain([initialAvailability, 'optionalAccess', _2 => _2.meetingId]) === meetingId ? initialAvailability.monthOffset : void 0;
|
|
188
97
|
const [monthOffset, setMonthOffsetState] = _react.useState.call(void 0, _nullishCoalesce(seededOffset, () => ( 0)));
|
|
189
98
|
const setMonthOffset = _react.useCallback.call(void 0, (offset) => {
|
|
190
99
|
setMonthOffsetState(Math.max(0, Math.min(_chunkQDOBY3TPcjs.MAX_MONTH_OFFSET, offset)));
|
|
@@ -195,7 +104,7 @@ function useMeetingBooking(options) {
|
|
|
195
104
|
prevMeetingIdRef.current = meetingId;
|
|
196
105
|
setMonthOffsetState(0);
|
|
197
106
|
}, [meetingId]);
|
|
198
|
-
const seed = _optionalChain([initialAvailability, 'optionalAccess',
|
|
107
|
+
const seed = _optionalChain([initialAvailability, 'optionalAccess', _3 => _3.meetingId]) === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
|
|
199
108
|
const availabilityQuery = _reactquery.useQuery.call(void 0, {
|
|
200
109
|
queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
|
|
201
110
|
queryFn: async ({ signal }) => {
|
|
@@ -225,9 +134,9 @@ function useMeetingBooking(options) {
|
|
|
225
134
|
const data = await res.json().catch(() => ({}));
|
|
226
135
|
if (res.ok) return { ok: true, confirmation: data };
|
|
227
136
|
const rawCode = typeof data.code === "string" ? data.code : "";
|
|
228
|
-
const code =
|
|
137
|
+
const code = KNOWN_CODES.includes(rawCode) ? rawCode : "TEMPORARILY_UNAVAILABLE";
|
|
229
138
|
return { ok: false, code, message: typeof data.error === "string" ? data.error : void 0 };
|
|
230
|
-
} catch (
|
|
139
|
+
} catch (e) {
|
|
231
140
|
return { ok: false, code: "TEMPORARILY_UNAVAILABLE" };
|
|
232
141
|
}
|
|
233
142
|
},
|
|
@@ -264,6 +173,94 @@ function useMeetingBooking(options) {
|
|
|
264
173
|
};
|
|
265
174
|
}
|
|
266
175
|
|
|
176
|
+
// src/schemas/meeting-booking-schema.ts
|
|
177
|
+
var _zod = require('zod');
|
|
178
|
+
var SUPPORTED_FORM_FIELD_TYPES = ["text", "textarea", "select", "radio", "checkbox"];
|
|
179
|
+
function isSupportedFormField(field) {
|
|
180
|
+
return SUPPORTED_FORM_FIELD_TYPES.includes(field.type);
|
|
181
|
+
}
|
|
182
|
+
var IANA_TZ_RE = /^(?:UTC|[A-Za-z_]+(?:\/[A-Za-z0-9_+-]+)+)$/;
|
|
183
|
+
function isValidIanaTimezone(tz) {
|
|
184
|
+
if (!IANA_TZ_RE.test(tz)) return false;
|
|
185
|
+
try {
|
|
186
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
187
|
+
return true;
|
|
188
|
+
} catch (e2) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function isValidBcp47Locale(locale) {
|
|
193
|
+
try {
|
|
194
|
+
return Intl.getCanonicalLocales(locale).length > 0;
|
|
195
|
+
} catch (e3) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function makeBookingSchema(formFields, legalConsent) {
|
|
200
|
+
const answers = {};
|
|
201
|
+
for (const field of formFields) {
|
|
202
|
+
if (!isSupportedFormField(field)) continue;
|
|
203
|
+
let validator;
|
|
204
|
+
switch (field.type) {
|
|
205
|
+
case "checkbox":
|
|
206
|
+
validator = _zod.z.boolean();
|
|
207
|
+
break;
|
|
208
|
+
case "select":
|
|
209
|
+
case "radio":
|
|
210
|
+
validator = _zod.z.string().refine((v) => !v || (_nullishCoalesce(field.options, () => ( []))).includes(v), {
|
|
211
|
+
message: `Please choose a valid option for ${field.label}`
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
case "textarea":
|
|
215
|
+
validator = _zod.z.string().max(5e3, { message: `${field.label} is too long` });
|
|
216
|
+
break;
|
|
217
|
+
case "text":
|
|
218
|
+
default:
|
|
219
|
+
validator = _zod.z.string().max(1e3, { message: `${field.label} is too long` });
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (field.required) {
|
|
223
|
+
validator = field.type === "checkbox" ? _zod.z.literal(true, { message: `${field.label} is required` }) : validator.min(1, { message: `${field.label} is required` });
|
|
224
|
+
} else if (field.type !== "checkbox") {
|
|
225
|
+
validator = validator.optional().or(_zod.z.literal(""));
|
|
226
|
+
} else {
|
|
227
|
+
validator = _zod.z.boolean().optional();
|
|
228
|
+
}
|
|
229
|
+
answers[field.name] = validator;
|
|
230
|
+
}
|
|
231
|
+
const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _4 => _4.communicationConsentCheckboxes]), () => ( []))).filter((c) => c.required).map((c) => c.communicationTypeId);
|
|
232
|
+
const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required);
|
|
233
|
+
const answersObject = _zod.z.object(answers);
|
|
234
|
+
return _zod.z.object({
|
|
235
|
+
meetingId: _zod.z.string().min(1),
|
|
236
|
+
startTimeMs: _zod.z.number().int().positive(),
|
|
237
|
+
durationMs: _zod.z.number().int().positive(),
|
|
238
|
+
firstName: _zod.z.string().min(1, { message: "First name is required" }).max(255),
|
|
239
|
+
lastName: _zod.z.string().min(1, { message: "Last name is required" }).max(255),
|
|
240
|
+
email: _zod.z.string().email({ message: "Please enter a valid email address" }).max(255),
|
|
241
|
+
timezone: _zod.z.string().refine(isValidIanaTimezone, { message: "Invalid timezone" }),
|
|
242
|
+
locale: _zod.z.string().refine(isValidBcp47Locale, { message: "Invalid locale" }).optional(),
|
|
243
|
+
// Plain `.optional()` (no `.default()`) so zod's input and output types
|
|
244
|
+
// match — react-hook-form's zodResolver needs them identical, and the
|
|
245
|
+
// server handles `undefined` explicitly anyway. REQUIRED when the link
|
|
246
|
+
// declares required questions (see `hasRequiredAnswers` above).
|
|
247
|
+
formFields: hasRequiredAnswers ? answersObject : answersObject.optional(),
|
|
248
|
+
legalConsentResponses: _zod.z.array(_zod.z.object({ communicationTypeId: _zod.z.string(), consented: _zod.z.boolean() })).optional()
|
|
249
|
+
}).superRefine((data, ctx) => {
|
|
250
|
+
const responses = _nullishCoalesce(data.legalConsentResponses, () => ( []));
|
|
251
|
+
const ok = requiredConsentIds.every(
|
|
252
|
+
(id) => responses.some((r) => r.communicationTypeId === id && r.consented === true)
|
|
253
|
+
);
|
|
254
|
+
if (!ok) {
|
|
255
|
+
ctx.addIssue({
|
|
256
|
+
code: _zod.z.ZodIssueCode.custom,
|
|
257
|
+
path: ["legalConsentResponses"],
|
|
258
|
+
message: "Required consent checkboxes must be accepted"
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
267
264
|
// src/components/meeting-scheduler/booking-form.tsx
|
|
268
265
|
var _zod3 = require('@hookform/resolvers/zod');
|
|
269
266
|
|