@flamingo-stack/openframe-frontend-core 0.0.574 → 0.0.575-snapshot.20260827225444
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 +105 -98
- package/dist/components/meeting-scheduler/index.cjs.map +1 -1
- package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.js +103 -96
- 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 +9 -0
- package/dist/schemas/meeting-booking-schema.cjs.map +1 -1
- package/dist/schemas/meeting-booking-schema.d.ts +10 -1
- package/dist/schemas/meeting-booking-schema.d.ts.map +1 -1
- package/dist/schemas/meeting-booking-schema.js +9 -1
- package/dist/schemas/meeting-booking-schema.js.map +1 -1
- package/package.json +1 -1
- package/src/components/meeting-scheduler/index.tsx +16 -14
- package/src/hooks/use-meeting-booking.ts +6 -13
- package/src/schemas/meeting-booking-schema.ts +18 -2
|
@@ -27,6 +27,7 @@ require('../../chunk-GQWM3ECA.cjs');
|
|
|
27
27
|
require('../../chunk-TON3YCCW.cjs');
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
|
|
30
31
|
var _chunk2BFZHTSDcjs = require('../../chunk-2BFZHTSD.cjs');
|
|
31
32
|
require('../../chunk-D55IENZJ.cjs');
|
|
32
33
|
require('../../chunk-7Z64COGD.cjs');
|
|
@@ -79,102 +80,17 @@ var _react = require('react');
|
|
|
79
80
|
// src/hooks/use-meeting-booking.ts
|
|
80
81
|
var _reactquery = require('@tanstack/react-query');
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
|
|
84
|
+
// src/schemas/meeting-booking-schema.ts
|
|
85
|
+
var _zod = require('zod');
|
|
86
|
+
var MEETING_BOOKING_ERROR_CODES = [
|
|
83
87
|
"SLOT_TAKEN",
|
|
84
88
|
"VALIDATION",
|
|
89
|
+
"INVALID_EMAIL",
|
|
85
90
|
"LINK_GONE",
|
|
86
91
|
"TEMPORARILY_UNAVAILABLE",
|
|
87
92
|
"MEETING_UNAVAILABLE"
|
|
88
93
|
];
|
|
89
|
-
var AVAILABILITY_STALE_MS = 0;
|
|
90
|
-
function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
91
|
-
return ["meeting-availability", apiBaseUrl, meetingId, monthOffset];
|
|
92
|
-
}
|
|
93
|
-
function useMeetingBooking(options) {
|
|
94
|
-
const { meetingId, apiBaseUrl = "", initialAvailability } = options;
|
|
95
|
-
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
96
|
-
const seededOffset = _optionalChain([initialAvailability, 'optionalAccess', _2 => _2.meetingId]) === meetingId ? initialAvailability.monthOffset : void 0;
|
|
97
|
-
const [monthOffset, setMonthOffsetState] = _react.useState.call(void 0, _nullishCoalesce(seededOffset, () => ( 0)));
|
|
98
|
-
const setMonthOffset = _react.useCallback.call(void 0, (offset) => {
|
|
99
|
-
setMonthOffsetState(Math.max(0, Math.min(_chunkQDOBY3TPcjs.MAX_MONTH_OFFSET, offset)));
|
|
100
|
-
}, []);
|
|
101
|
-
const prevMeetingIdRef = _react.useRef.call(void 0, meetingId);
|
|
102
|
-
_react.useLayoutEffect.call(void 0, () => {
|
|
103
|
-
if (prevMeetingIdRef.current === meetingId) return;
|
|
104
|
-
prevMeetingIdRef.current = meetingId;
|
|
105
|
-
setMonthOffsetState(0);
|
|
106
|
-
}, [meetingId]);
|
|
107
|
-
const seed = _optionalChain([initialAvailability, 'optionalAccess', _3 => _3.meetingId]) === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
|
|
108
|
-
const availabilityQuery = _reactquery.useQuery.call(void 0, {
|
|
109
|
-
queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
|
|
110
|
-
queryFn: async ({ signal }) => {
|
|
111
|
-
const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
|
|
112
|
-
const res = await _chunkTRC37VRCcjs.contentFetch.call(void 0, `${apiBaseUrl}/api/meetings/availability?${params}`, { signal });
|
|
113
|
-
if (!res.ok) throw new Error(`availability ${res.status}`);
|
|
114
|
-
return await res.json();
|
|
115
|
-
},
|
|
116
|
-
staleTime: AVAILABILITY_STALE_MS,
|
|
117
|
-
initialData: seed,
|
|
118
|
-
// Stamped as infinitely old on purpose: the seed is first-paint
|
|
119
|
-
// scaffolding for a 60-second-volatile resource, not truth, so it paints
|
|
120
|
-
// immediately AND is refetched immediately.
|
|
121
|
-
initialDataUpdatedAt: seed ? 0 : void 0
|
|
122
|
-
});
|
|
123
|
-
const refetchAvailability = _react.useCallback.call(void 0, async () => {
|
|
124
|
-
await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
|
|
125
|
-
}, [queryClient, apiBaseUrl, meetingId, monthOffset]);
|
|
126
|
-
const bookMutation = _reactquery.useMutation.call(void 0, {
|
|
127
|
-
mutationFn: async (payload) => {
|
|
128
|
-
try {
|
|
129
|
-
const res = await _chunkTRC37VRCcjs.contentFetch.call(void 0, `${apiBaseUrl}/api/meetings/book`, {
|
|
130
|
-
method: "POST",
|
|
131
|
-
headers: { "Content-Type": "application/json" },
|
|
132
|
-
body: JSON.stringify(payload)
|
|
133
|
-
});
|
|
134
|
-
const data = await res.json().catch(() => ({}));
|
|
135
|
-
if (res.ok) return { ok: true, confirmation: data };
|
|
136
|
-
const rawCode = typeof data.code === "string" ? data.code : "";
|
|
137
|
-
const code = KNOWN_CODES.includes(rawCode) ? rawCode : "TEMPORARILY_UNAVAILABLE";
|
|
138
|
-
return { ok: false, code, message: typeof data.error === "string" ? data.error : void 0 };
|
|
139
|
-
} catch (e) {
|
|
140
|
-
return { ok: false, code: "TEMPORARILY_UNAVAILABLE" };
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
// A booking is not a read — never retried, at any layer.
|
|
144
|
-
retry: false
|
|
145
|
-
});
|
|
146
|
-
const submittingRef = _react.useRef.call(void 0, null);
|
|
147
|
-
const { mutateAsync } = bookMutation;
|
|
148
|
-
const book = _react.useCallback.call(void 0,
|
|
149
|
-
async (payload) => {
|
|
150
|
-
if (submittingRef.current !== null) return { ok: false, code: "VALIDATION", message: "Already submitting" };
|
|
151
|
-
const submitToken = {};
|
|
152
|
-
submittingRef.current = submitToken;
|
|
153
|
-
try {
|
|
154
|
-
return await mutateAsync(payload);
|
|
155
|
-
} finally {
|
|
156
|
-
if (submittingRef.current === submitToken) submittingRef.current = null;
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
[mutateAsync]
|
|
160
|
-
);
|
|
161
|
-
return {
|
|
162
|
-
availability: _nullishCoalesce(availabilityQuery.data, () => ( null)),
|
|
163
|
-
/** No availability for this month yet — the times column has nothing to show. */
|
|
164
|
-
isLoadingAvailability: availabilityQuery.isPending,
|
|
165
|
-
/** ANY request in flight, including a silent revalidation of cached data. */
|
|
166
|
-
isFetchingAvailability: availabilityQuery.isFetching,
|
|
167
|
-
availabilityError: availabilityQuery.error ? availabilityQuery.error.message : null,
|
|
168
|
-
monthOffset,
|
|
169
|
-
setMonthOffset,
|
|
170
|
-
refetchAvailability,
|
|
171
|
-
book,
|
|
172
|
-
isSubmitting: bookMutation.isPending
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// src/schemas/meeting-booking-schema.ts
|
|
177
|
-
var _zod = require('zod');
|
|
178
94
|
var SUPPORTED_FORM_FIELD_TYPES = ["text", "textarea", "select", "radio", "checkbox"];
|
|
179
95
|
function isSupportedFormField(field) {
|
|
180
96
|
return SUPPORTED_FORM_FIELD_TYPES.includes(field.type);
|
|
@@ -185,14 +101,14 @@ function isValidIanaTimezone(tz) {
|
|
|
185
101
|
try {
|
|
186
102
|
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
187
103
|
return true;
|
|
188
|
-
} catch (
|
|
104
|
+
} catch (e) {
|
|
189
105
|
return false;
|
|
190
106
|
}
|
|
191
107
|
}
|
|
192
108
|
function isValidBcp47Locale(locale) {
|
|
193
109
|
try {
|
|
194
110
|
return Intl.getCanonicalLocales(locale).length > 0;
|
|
195
|
-
} catch (
|
|
111
|
+
} catch (e2) {
|
|
196
112
|
return false;
|
|
197
113
|
}
|
|
198
114
|
}
|
|
@@ -228,7 +144,7 @@ function makeBookingSchema(formFields, legalConsent) {
|
|
|
228
144
|
}
|
|
229
145
|
answers[field.name] = validator;
|
|
230
146
|
}
|
|
231
|
-
const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess',
|
|
147
|
+
const requiredConsentIds = (_nullishCoalesce(_optionalChain([legalConsent, 'optionalAccess', _2 => _2.communicationConsentCheckboxes]), () => ( []))).filter((c) => c.required).map((c) => c.communicationTypeId);
|
|
232
148
|
const hasRequiredAnswers = formFields.some((f) => isSupportedFormField(f) && f.required);
|
|
233
149
|
const answersObject = _zod.z.object(answers);
|
|
234
150
|
return _zod.z.object({
|
|
@@ -261,6 +177,94 @@ function makeBookingSchema(formFields, legalConsent) {
|
|
|
261
177
|
});
|
|
262
178
|
}
|
|
263
179
|
|
|
180
|
+
// src/hooks/use-meeting-booking.ts
|
|
181
|
+
var AVAILABILITY_STALE_MS = 0;
|
|
182
|
+
function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
183
|
+
return ["meeting-availability", apiBaseUrl, meetingId, monthOffset];
|
|
184
|
+
}
|
|
185
|
+
function useMeetingBooking(options) {
|
|
186
|
+
const { meetingId, apiBaseUrl = "", initialAvailability } = options;
|
|
187
|
+
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
188
|
+
const seededOffset = _optionalChain([initialAvailability, 'optionalAccess', _3 => _3.meetingId]) === meetingId ? initialAvailability.monthOffset : void 0;
|
|
189
|
+
const [monthOffset, setMonthOffsetState] = _react.useState.call(void 0, _nullishCoalesce(seededOffset, () => ( 0)));
|
|
190
|
+
const setMonthOffset = _react.useCallback.call(void 0, (offset) => {
|
|
191
|
+
setMonthOffsetState(Math.max(0, Math.min(_chunkQDOBY3TPcjs.MAX_MONTH_OFFSET, offset)));
|
|
192
|
+
}, []);
|
|
193
|
+
const prevMeetingIdRef = _react.useRef.call(void 0, meetingId);
|
|
194
|
+
_react.useLayoutEffect.call(void 0, () => {
|
|
195
|
+
if (prevMeetingIdRef.current === meetingId) return;
|
|
196
|
+
prevMeetingIdRef.current = meetingId;
|
|
197
|
+
setMonthOffsetState(0);
|
|
198
|
+
}, [meetingId]);
|
|
199
|
+
const seed = _optionalChain([initialAvailability, 'optionalAccess', _4 => _4.meetingId]) === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
|
|
200
|
+
const availabilityQuery = _reactquery.useQuery.call(void 0, {
|
|
201
|
+
queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
|
|
202
|
+
queryFn: async ({ signal }) => {
|
|
203
|
+
const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
|
|
204
|
+
const res = await _chunkTRC37VRCcjs.contentFetch.call(void 0, `${apiBaseUrl}/api/meetings/availability?${params}`, { signal });
|
|
205
|
+
if (!res.ok) throw new Error(`availability ${res.status}`);
|
|
206
|
+
return await res.json();
|
|
207
|
+
},
|
|
208
|
+
staleTime: AVAILABILITY_STALE_MS,
|
|
209
|
+
initialData: seed,
|
|
210
|
+
// Stamped as infinitely old on purpose: the seed is first-paint
|
|
211
|
+
// scaffolding for a 60-second-volatile resource, not truth, so it paints
|
|
212
|
+
// immediately AND is refetched immediately.
|
|
213
|
+
initialDataUpdatedAt: seed ? 0 : void 0
|
|
214
|
+
});
|
|
215
|
+
const refetchAvailability = _react.useCallback.call(void 0, async () => {
|
|
216
|
+
await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
|
|
217
|
+
}, [queryClient, apiBaseUrl, meetingId, monthOffset]);
|
|
218
|
+
const bookMutation = _reactquery.useMutation.call(void 0, {
|
|
219
|
+
mutationFn: async (payload) => {
|
|
220
|
+
try {
|
|
221
|
+
const res = await _chunkTRC37VRCcjs.contentFetch.call(void 0, `${apiBaseUrl}/api/meetings/book`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
headers: { "Content-Type": "application/json" },
|
|
224
|
+
body: JSON.stringify(payload)
|
|
225
|
+
});
|
|
226
|
+
const data = await res.json().catch(() => ({}));
|
|
227
|
+
if (res.ok) return { ok: true, confirmation: data };
|
|
228
|
+
const rawCode = typeof data.code === "string" ? data.code : "";
|
|
229
|
+
const code = MEETING_BOOKING_ERROR_CODES.includes(rawCode) ? rawCode : "TEMPORARILY_UNAVAILABLE";
|
|
230
|
+
return { ok: false, code, message: typeof data.error === "string" ? data.error : void 0 };
|
|
231
|
+
} catch (e3) {
|
|
232
|
+
return { ok: false, code: "TEMPORARILY_UNAVAILABLE" };
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
// A booking is not a read — never retried, at any layer.
|
|
236
|
+
retry: false
|
|
237
|
+
});
|
|
238
|
+
const submittingRef = _react.useRef.call(void 0, null);
|
|
239
|
+
const { mutateAsync } = bookMutation;
|
|
240
|
+
const book = _react.useCallback.call(void 0,
|
|
241
|
+
async (payload) => {
|
|
242
|
+
if (submittingRef.current !== null) return { ok: false, code: "VALIDATION", message: "Already submitting" };
|
|
243
|
+
const submitToken = {};
|
|
244
|
+
submittingRef.current = submitToken;
|
|
245
|
+
try {
|
|
246
|
+
return await mutateAsync(payload);
|
|
247
|
+
} finally {
|
|
248
|
+
if (submittingRef.current === submitToken) submittingRef.current = null;
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
[mutateAsync]
|
|
252
|
+
);
|
|
253
|
+
return {
|
|
254
|
+
availability: _nullishCoalesce(availabilityQuery.data, () => ( null)),
|
|
255
|
+
/** No availability for this month yet — the times column has nothing to show. */
|
|
256
|
+
isLoadingAvailability: availabilityQuery.isPending,
|
|
257
|
+
/** ANY request in flight, including a silent revalidation of cached data. */
|
|
258
|
+
isFetchingAvailability: availabilityQuery.isFetching,
|
|
259
|
+
availabilityError: availabilityQuery.error ? availabilityQuery.error.message : null,
|
|
260
|
+
monthOffset,
|
|
261
|
+
setMonthOffset,
|
|
262
|
+
refetchAvailability,
|
|
263
|
+
book,
|
|
264
|
+
isSubmitting: bookMutation.isPending
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
264
268
|
// src/components/meeting-scheduler/booking-form.tsx
|
|
265
269
|
var _zod3 = require('@hookform/resolvers/zod');
|
|
266
270
|
|
|
@@ -1076,6 +1080,7 @@ function HubSpotMeetingScheduler({
|
|
|
1076
1080
|
const [confirmation, setConfirmation] = _react.useState.call(void 0, null);
|
|
1077
1081
|
const [bookingError, setBookingError] = _react.useState.call(void 0, null);
|
|
1078
1082
|
const { honeypotInputProps, getSignals, resetSignals } = _chunk2BFZHTSDcjs.useHumanitySignals.call(void 0, );
|
|
1083
|
+
const { toast } = _chunk2BFZHTSDcjs.useToast.call(void 0, );
|
|
1079
1084
|
const autoAdvanceCount = _react.useRef.call(void 0, 0);
|
|
1080
1085
|
const [machineFor, setMachineFor] = _react.useState.call(void 0, meetingId);
|
|
1081
1086
|
if (machineFor !== meetingId) {
|
|
@@ -1137,9 +1142,15 @@ function HubSpotMeetingScheduler({
|
|
|
1137
1142
|
setStep("slot");
|
|
1138
1143
|
resetSignals();
|
|
1139
1144
|
void refetchAvailability();
|
|
1145
|
+
} else {
|
|
1146
|
+
toast({
|
|
1147
|
+
variant: "error",
|
|
1148
|
+
title: "Booking failed",
|
|
1149
|
+
description: _nullishCoalesce(BOOKING_ERROR_COPY[code], () => ( BOOKING_ERROR_COPY.VALIDATION))
|
|
1150
|
+
});
|
|
1140
1151
|
}
|
|
1141
1152
|
},
|
|
1142
|
-
[book, onBooked, refetchAvailability, resetSignals]
|
|
1153
|
+
[book, onBooked, refetchAvailability, resetSignals, toast]
|
|
1143
1154
|
);
|
|
1144
1155
|
const escapeHatch = fallbackUrl ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkS6LZN66Dcjs.Button, { variant: "outline", size: "small-legacy", href: fallbackUrl, openInNewTab: true, children: "Open in HubSpot" }) : null;
|
|
1145
1156
|
if (isLoadingAvailability && !availability) {
|
|
@@ -1198,10 +1209,6 @@ function HubSpotMeetingScheduler({
|
|
|
1198
1209
|
"\xB7 ",
|
|
1199
1210
|
_chunkSPAAKHQAcjs.formatDurationCompact.call(void 0, durationMs / 1e3)
|
|
1200
1211
|
] }),
|
|
1201
|
-
bookingError && bookingError !== "SLOT_TAKEN" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkQDOBY3TPcjs.Alert, { variant: "error", className: "flex flex-col items-start gap-[var(--spacing-system-xs)]", children: [
|
|
1202
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkQDOBY3TPcjs.AlertDescription, { children: _nullishCoalesce(BOOKING_ERROR_COPY[bookingError], () => ( BOOKING_ERROR_COPY.VALIDATION)) }),
|
|
1203
|
-
escapeHatch
|
|
1204
|
-
] }),
|
|
1205
1212
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1206
1213
|
BookingForm,
|
|
1207
1214
|
{
|