@flamingo-stack/openframe-frontend-core 0.0.573 → 0.0.574-snapshot.20260827223923
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/{chunk-TIX2SHZX.js → chunk-2EPUD2VR.js} +2 -2
- package/dist/{chunk-2EB5273T.js → chunk-2IK6SB6F.js} +2 -2
- package/dist/{chunk-OUMRHNZW.cjs → chunk-3IUQLB47.cjs} +3 -3
- package/dist/{chunk-OUMRHNZW.cjs.map → chunk-3IUQLB47.cjs.map} +1 -1
- package/dist/{chunk-BWHLWS32.cjs → chunk-3U2P63NQ.cjs} +10 -10
- package/dist/{chunk-BWHLWS32.cjs.map → chunk-3U2P63NQ.cjs.map} +1 -1
- package/dist/{chunk-EC44ZXBH.js → chunk-5QDGP3RI.js} +2 -2
- package/dist/{chunk-YS64Y5MP.cjs → chunk-7G23ANVM.cjs} +49 -49
- package/dist/{chunk-YS64Y5MP.cjs.map → chunk-7G23ANVM.cjs.map} +1 -1
- package/dist/{chunk-R3I2XI33.js → chunk-BGE7CKXC.js} +4 -4
- package/dist/{chunk-6SMQCAPO.cjs → chunk-BGZCZ2JS.cjs} +3 -3
- package/dist/{chunk-6SMQCAPO.cjs.map → chunk-BGZCZ2JS.cjs.map} +1 -1
- package/dist/{chunk-B7S7ETML.cjs → chunk-BKXL7OYT.cjs} +3 -3
- package/dist/{chunk-B7S7ETML.cjs.map → chunk-BKXL7OYT.cjs.map} +1 -1
- package/dist/{chunk-4WCTY326.js → chunk-BTZF3JRS.js} +2 -2
- package/dist/{chunk-J5OC2KYU.js → chunk-DTSRMQIY.js} +5 -5
- package/dist/{chunk-4TGUWWMY.cjs → chunk-GFSJXRX7.cjs} +16 -16
- package/dist/{chunk-4TGUWWMY.cjs.map → chunk-GFSJXRX7.cjs.map} +1 -1
- package/dist/{chunk-P625NSEA.cjs → chunk-IRJQH7RJ.cjs} +17 -17
- package/dist/{chunk-P625NSEA.cjs.map → chunk-IRJQH7RJ.cjs.map} +1 -1
- package/dist/{chunk-DSU7LQMU.js → chunk-ISULYCGY.js} +2 -2
- package/dist/{chunk-SB67TZQU.js → chunk-IWISKP3F.js} +2 -2
- package/dist/{chunk-JLIXPN37.cjs → chunk-K3XONUML.cjs} +4 -4
- package/dist/{chunk-JLIXPN37.cjs.map → chunk-K3XONUML.cjs.map} +1 -1
- package/dist/{chunk-IBZBGRO2.js → chunk-KG7D35RU.js} +5 -5
- package/dist/{chunk-3ENV3IEP.js → chunk-KSKCQ3ZN.js} +2 -2
- package/dist/{chunk-NGXP4EAQ.js → chunk-OE7NV7H3.js} +2 -2
- package/dist/{chunk-66LZDE7U.js → chunk-PBS4F534.js} +3 -2
- package/dist/chunk-PBS4F534.js.map +1 -0
- package/dist/{chunk-AQBWGEL3.cjs → chunk-PUL6X6HY.cjs} +32 -32
- package/dist/{chunk-AQBWGEL3.cjs.map → chunk-PUL6X6HY.cjs.map} +1 -1
- package/dist/{chunk-YK7M7RBF.cjs → chunk-QDOBY3TP.cjs} +3 -2
- package/dist/chunk-QDOBY3TP.cjs.map +1 -0
- package/dist/{chunk-OYWAPTEF.cjs → chunk-QF5B6V4L.cjs} +17 -17
- package/dist/{chunk-OYWAPTEF.cjs.map → chunk-QF5B6V4L.cjs.map} +1 -1
- package/dist/{chunk-NCCWS6YE.cjs → chunk-QTVW5XBL.cjs} +4 -4
- package/dist/{chunk-NCCWS6YE.cjs.map → chunk-QTVW5XBL.cjs.map} +1 -1
- package/dist/{chunk-PDQ7F5AB.cjs → chunk-RXJZXMDZ.cjs} +13 -13
- package/dist/{chunk-PDQ7F5AB.cjs.map → chunk-RXJZXMDZ.cjs.map} +1 -1
- package/dist/{chunk-EIK6KDAE.cjs → chunk-SVUZUFT4.cjs} +34 -34
- package/dist/{chunk-EIK6KDAE.cjs.map → chunk-SVUZUFT4.cjs.map} +1 -1
- package/dist/{chunk-GSSJFXIW.cjs → chunk-UILNZL6J.cjs} +3 -3
- package/dist/{chunk-GSSJFXIW.cjs.map → chunk-UILNZL6J.cjs.map} +1 -1
- package/dist/{chunk-4IAZOAS7.js → chunk-UPEIX7VG.js} +4 -4
- package/dist/{chunk-SY7M6VAJ.js → chunk-WAWMZSCP.js} +3 -3
- package/dist/{chunk-JTDYVW2B.js → chunk-WVKRSC65.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +114 -114
- package/dist/components/index.js +12 -12
- package/dist/components/meeting-scheduler/index.cjs +130 -111
- 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 +113 -94
- package/dist/components/meeting-scheduler/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +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/index.cjs +6 -6
- package/dist/index.js +5 -5
- 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 +15 -2
- 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 +34 -16
- package/src/components/ui/alert.tsx +1 -0
- package/src/hooks/use-meeting-booking.ts +6 -13
- package/src/schemas/meeting-booking-schema.ts +23 -3
- package/dist/chunk-66LZDE7U.js.map +0 -1
- package/dist/chunk-YK7M7RBF.cjs.map +0 -1
- /package/dist/{chunk-TIX2SHZX.js.map → chunk-2EPUD2VR.js.map} +0 -0
- /package/dist/{chunk-2EB5273T.js.map → chunk-2IK6SB6F.js.map} +0 -0
- /package/dist/{chunk-EC44ZXBH.js.map → chunk-5QDGP3RI.js.map} +0 -0
- /package/dist/{chunk-R3I2XI33.js.map → chunk-BGE7CKXC.js.map} +0 -0
- /package/dist/{chunk-4WCTY326.js.map → chunk-BTZF3JRS.js.map} +0 -0
- /package/dist/{chunk-J5OC2KYU.js.map → chunk-DTSRMQIY.js.map} +0 -0
- /package/dist/{chunk-DSU7LQMU.js.map → chunk-ISULYCGY.js.map} +0 -0
- /package/dist/{chunk-SB67TZQU.js.map → chunk-IWISKP3F.js.map} +0 -0
- /package/dist/{chunk-IBZBGRO2.js.map → chunk-KG7D35RU.js.map} +0 -0
- /package/dist/{chunk-3ENV3IEP.js.map → chunk-KSKCQ3ZN.js.map} +0 -0
- /package/dist/{chunk-NGXP4EAQ.js.map → chunk-OE7NV7H3.js.map} +0 -0
- /package/dist/{chunk-4IAZOAS7.js.map → chunk-UPEIX7VG.js.map} +0 -0
- /package/dist/{chunk-SY7M6VAJ.js.map → chunk-WAWMZSCP.js.map} +0 -0
- /package/dist/{chunk-JTDYVW2B.js.map → chunk-WVKRSC65.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/meeting-scheduler/index.tsx"],"names":[],"mappings":"AAyCA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EACjB,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/meeting-scheduler/index.tsx"],"names":[],"mappings":"AAyCA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EACjB,MAAM,sCAAsC,CAAC;AA4B9C,MAAM,WAAW,4BAA4B;IAC3C,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,mBAAmB,qCAAqC,CAAC;AAoEtE,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,UAAe,EACf,mBAAmB,EACnB,KAAK,EACL,WAAW,EACX,KAAK,EACL,eAAe,EACf,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,GACV,EAAE,4BAA4B,+BAmV9B;AAED,OAAO,EACL,yBAAyB,EACzB,oCAAoC,EACpC,KAAK,8BAA8B,GACpC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,WAAW,EAAE,CAAC;AAC/F,YAAY,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
EmptyState
|
|
8
8
|
} from "../../chunk-QQ5ZVCD4.js";
|
|
9
9
|
import {
|
|
10
|
+
Alert,
|
|
11
|
+
AlertDescription,
|
|
10
12
|
Autocomplete,
|
|
11
13
|
BackButton,
|
|
12
14
|
DatePickerCalendar,
|
|
@@ -20,7 +22,7 @@ import {
|
|
|
20
22
|
SelectTrigger,
|
|
21
23
|
SelectValue,
|
|
22
24
|
Textarea
|
|
23
|
-
} from "../../chunk-
|
|
25
|
+
} from "../../chunk-PBS4F534.js";
|
|
24
26
|
import "../../chunk-L2SLFCXP.js";
|
|
25
27
|
import "../../chunk-BJXDHKUX.js";
|
|
26
28
|
import {
|
|
@@ -77,102 +79,17 @@ import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMem
|
|
|
77
79
|
// src/hooks/use-meeting-booking.ts
|
|
78
80
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
79
81
|
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
80
|
-
|
|
82
|
+
|
|
83
|
+
// src/schemas/meeting-booking-schema.ts
|
|
84
|
+
import { z } from "zod";
|
|
85
|
+
var MEETING_BOOKING_ERROR_CODES = [
|
|
81
86
|
"SLOT_TAKEN",
|
|
82
87
|
"VALIDATION",
|
|
88
|
+
"INVALID_EMAIL",
|
|
83
89
|
"LINK_GONE",
|
|
84
90
|
"TEMPORARILY_UNAVAILABLE",
|
|
85
91
|
"MEETING_UNAVAILABLE"
|
|
86
92
|
];
|
|
87
|
-
var AVAILABILITY_STALE_MS = 0;
|
|
88
|
-
function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
89
|
-
return ["meeting-availability", apiBaseUrl, meetingId, monthOffset];
|
|
90
|
-
}
|
|
91
|
-
function useMeetingBooking(options) {
|
|
92
|
-
const { meetingId, apiBaseUrl = "", initialAvailability } = options;
|
|
93
|
-
const queryClient = useQueryClient();
|
|
94
|
-
const seededOffset = initialAvailability?.meetingId === meetingId ? initialAvailability.monthOffset : void 0;
|
|
95
|
-
const [monthOffset, setMonthOffsetState] = useState(seededOffset ?? 0);
|
|
96
|
-
const setMonthOffset = useCallback((offset) => {
|
|
97
|
-
setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)));
|
|
98
|
-
}, []);
|
|
99
|
-
const prevMeetingIdRef = useRef(meetingId);
|
|
100
|
-
useLayoutEffect(() => {
|
|
101
|
-
if (prevMeetingIdRef.current === meetingId) return;
|
|
102
|
-
prevMeetingIdRef.current = meetingId;
|
|
103
|
-
setMonthOffsetState(0);
|
|
104
|
-
}, [meetingId]);
|
|
105
|
-
const seed = initialAvailability?.meetingId === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
|
|
106
|
-
const availabilityQuery = useQuery({
|
|
107
|
-
queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
|
|
108
|
-
queryFn: async ({ signal }) => {
|
|
109
|
-
const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
|
|
110
|
-
const res = await contentFetch(`${apiBaseUrl}/api/meetings/availability?${params}`, { signal });
|
|
111
|
-
if (!res.ok) throw new Error(`availability ${res.status}`);
|
|
112
|
-
return await res.json();
|
|
113
|
-
},
|
|
114
|
-
staleTime: AVAILABILITY_STALE_MS,
|
|
115
|
-
initialData: seed,
|
|
116
|
-
// Stamped as infinitely old on purpose: the seed is first-paint
|
|
117
|
-
// scaffolding for a 60-second-volatile resource, not truth, so it paints
|
|
118
|
-
// immediately AND is refetched immediately.
|
|
119
|
-
initialDataUpdatedAt: seed ? 0 : void 0
|
|
120
|
-
});
|
|
121
|
-
const refetchAvailability = useCallback(async () => {
|
|
122
|
-
await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
|
|
123
|
-
}, [queryClient, apiBaseUrl, meetingId, monthOffset]);
|
|
124
|
-
const bookMutation = useMutation({
|
|
125
|
-
mutationFn: async (payload) => {
|
|
126
|
-
try {
|
|
127
|
-
const res = await contentFetch(`${apiBaseUrl}/api/meetings/book`, {
|
|
128
|
-
method: "POST",
|
|
129
|
-
headers: { "Content-Type": "application/json" },
|
|
130
|
-
body: JSON.stringify(payload)
|
|
131
|
-
});
|
|
132
|
-
const data = await res.json().catch(() => ({}));
|
|
133
|
-
if (res.ok) return { ok: true, confirmation: data };
|
|
134
|
-
const rawCode = typeof data.code === "string" ? data.code : "";
|
|
135
|
-
const code = KNOWN_CODES.includes(rawCode) ? rawCode : "TEMPORARILY_UNAVAILABLE";
|
|
136
|
-
return { ok: false, code, message: typeof data.error === "string" ? data.error : void 0 };
|
|
137
|
-
} catch {
|
|
138
|
-
return { ok: false, code: "TEMPORARILY_UNAVAILABLE" };
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
// A booking is not a read — never retried, at any layer.
|
|
142
|
-
retry: false
|
|
143
|
-
});
|
|
144
|
-
const submittingRef = useRef(null);
|
|
145
|
-
const { mutateAsync } = bookMutation;
|
|
146
|
-
const book = useCallback(
|
|
147
|
-
async (payload) => {
|
|
148
|
-
if (submittingRef.current !== null) return { ok: false, code: "VALIDATION", message: "Already submitting" };
|
|
149
|
-
const submitToken = {};
|
|
150
|
-
submittingRef.current = submitToken;
|
|
151
|
-
try {
|
|
152
|
-
return await mutateAsync(payload);
|
|
153
|
-
} finally {
|
|
154
|
-
if (submittingRef.current === submitToken) submittingRef.current = null;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
[mutateAsync]
|
|
158
|
-
);
|
|
159
|
-
return {
|
|
160
|
-
availability: availabilityQuery.data ?? null,
|
|
161
|
-
/** No availability for this month yet — the times column has nothing to show. */
|
|
162
|
-
isLoadingAvailability: availabilityQuery.isPending,
|
|
163
|
-
/** ANY request in flight, including a silent revalidation of cached data. */
|
|
164
|
-
isFetchingAvailability: availabilityQuery.isFetching,
|
|
165
|
-
availabilityError: availabilityQuery.error ? availabilityQuery.error.message : null,
|
|
166
|
-
monthOffset,
|
|
167
|
-
setMonthOffset,
|
|
168
|
-
refetchAvailability,
|
|
169
|
-
book,
|
|
170
|
-
isSubmitting: bookMutation.isPending
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// src/schemas/meeting-booking-schema.ts
|
|
175
|
-
import { z } from "zod";
|
|
176
93
|
var SUPPORTED_FORM_FIELD_TYPES = ["text", "textarea", "select", "radio", "checkbox"];
|
|
177
94
|
function isSupportedFormField(field) {
|
|
178
95
|
return SUPPORTED_FORM_FIELD_TYPES.includes(field.type);
|
|
@@ -259,6 +176,94 @@ function makeBookingSchema(formFields, legalConsent) {
|
|
|
259
176
|
});
|
|
260
177
|
}
|
|
261
178
|
|
|
179
|
+
// src/hooks/use-meeting-booking.ts
|
|
180
|
+
var AVAILABILITY_STALE_MS = 0;
|
|
181
|
+
function meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) {
|
|
182
|
+
return ["meeting-availability", apiBaseUrl, meetingId, monthOffset];
|
|
183
|
+
}
|
|
184
|
+
function useMeetingBooking(options) {
|
|
185
|
+
const { meetingId, apiBaseUrl = "", initialAvailability } = options;
|
|
186
|
+
const queryClient = useQueryClient();
|
|
187
|
+
const seededOffset = initialAvailability?.meetingId === meetingId ? initialAvailability.monthOffset : void 0;
|
|
188
|
+
const [monthOffset, setMonthOffsetState] = useState(seededOffset ?? 0);
|
|
189
|
+
const setMonthOffset = useCallback((offset) => {
|
|
190
|
+
setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)));
|
|
191
|
+
}, []);
|
|
192
|
+
const prevMeetingIdRef = useRef(meetingId);
|
|
193
|
+
useLayoutEffect(() => {
|
|
194
|
+
if (prevMeetingIdRef.current === meetingId) return;
|
|
195
|
+
prevMeetingIdRef.current = meetingId;
|
|
196
|
+
setMonthOffsetState(0);
|
|
197
|
+
}, [meetingId]);
|
|
198
|
+
const seed = initialAvailability?.meetingId === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
|
|
199
|
+
const availabilityQuery = useQuery({
|
|
200
|
+
queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
|
|
201
|
+
queryFn: async ({ signal }) => {
|
|
202
|
+
const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
|
|
203
|
+
const res = await contentFetch(`${apiBaseUrl}/api/meetings/availability?${params}`, { signal });
|
|
204
|
+
if (!res.ok) throw new Error(`availability ${res.status}`);
|
|
205
|
+
return await res.json();
|
|
206
|
+
},
|
|
207
|
+
staleTime: AVAILABILITY_STALE_MS,
|
|
208
|
+
initialData: seed,
|
|
209
|
+
// Stamped as infinitely old on purpose: the seed is first-paint
|
|
210
|
+
// scaffolding for a 60-second-volatile resource, not truth, so it paints
|
|
211
|
+
// immediately AND is refetched immediately.
|
|
212
|
+
initialDataUpdatedAt: seed ? 0 : void 0
|
|
213
|
+
});
|
|
214
|
+
const refetchAvailability = useCallback(async () => {
|
|
215
|
+
await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
|
|
216
|
+
}, [queryClient, apiBaseUrl, meetingId, monthOffset]);
|
|
217
|
+
const bookMutation = useMutation({
|
|
218
|
+
mutationFn: async (payload) => {
|
|
219
|
+
try {
|
|
220
|
+
const res = await contentFetch(`${apiBaseUrl}/api/meetings/book`, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: { "Content-Type": "application/json" },
|
|
223
|
+
body: JSON.stringify(payload)
|
|
224
|
+
});
|
|
225
|
+
const data = await res.json().catch(() => ({}));
|
|
226
|
+
if (res.ok) return { ok: true, confirmation: data };
|
|
227
|
+
const rawCode = typeof data.code === "string" ? data.code : "";
|
|
228
|
+
const code = MEETING_BOOKING_ERROR_CODES.includes(rawCode) ? rawCode : "TEMPORARILY_UNAVAILABLE";
|
|
229
|
+
return { ok: false, code, message: typeof data.error === "string" ? data.error : void 0 };
|
|
230
|
+
} catch {
|
|
231
|
+
return { ok: false, code: "TEMPORARILY_UNAVAILABLE" };
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
// A booking is not a read — never retried, at any layer.
|
|
235
|
+
retry: false
|
|
236
|
+
});
|
|
237
|
+
const submittingRef = useRef(null);
|
|
238
|
+
const { mutateAsync } = bookMutation;
|
|
239
|
+
const book = useCallback(
|
|
240
|
+
async (payload) => {
|
|
241
|
+
if (submittingRef.current !== null) return { ok: false, code: "VALIDATION", message: "Already submitting" };
|
|
242
|
+
const submitToken = {};
|
|
243
|
+
submittingRef.current = submitToken;
|
|
244
|
+
try {
|
|
245
|
+
return await mutateAsync(payload);
|
|
246
|
+
} finally {
|
|
247
|
+
if (submittingRef.current === submitToken) submittingRef.current = null;
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
[mutateAsync]
|
|
251
|
+
);
|
|
252
|
+
return {
|
|
253
|
+
availability: availabilityQuery.data ?? null,
|
|
254
|
+
/** No availability for this month yet — the times column has nothing to show. */
|
|
255
|
+
isLoadingAvailability: availabilityQuery.isPending,
|
|
256
|
+
/** ANY request in flight, including a silent revalidation of cached data. */
|
|
257
|
+
isFetchingAvailability: availabilityQuery.isFetching,
|
|
258
|
+
availabilityError: availabilityQuery.error ? availabilityQuery.error.message : null,
|
|
259
|
+
monthOffset,
|
|
260
|
+
setMonthOffset,
|
|
261
|
+
refetchAvailability,
|
|
262
|
+
book,
|
|
263
|
+
isSubmitting: bookMutation.isPending
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
262
267
|
// src/components/meeting-scheduler/booking-form.tsx
|
|
263
268
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
264
269
|
import { useMemo } from "react";
|
|
@@ -1001,6 +1006,13 @@ function MeetingSchedulerDirectory({
|
|
|
1001
1006
|
|
|
1002
1007
|
// src/components/meeting-scheduler/index.tsx
|
|
1003
1008
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1009
|
+
var BOOKING_ERROR_COPY = {
|
|
1010
|
+
INVALID_EMAIL: "That email address was rejected by our scheduling system \u2014 please use a real, reachable address (work email works best).",
|
|
1011
|
+
VALIDATION: "Please double-check your details \u2014 especially that the email address is real and reachable \u2014 and try again.",
|
|
1012
|
+
TEMPORARILY_UNAVAILABLE: "Scheduling is briefly unavailable \u2014 please try again in a minute.",
|
|
1013
|
+
MEETING_UNAVAILABLE: "This meeting type has reached its booking limit for today \u2014 try another time or contact us.",
|
|
1014
|
+
LINK_GONE: "This meeting type is no longer available."
|
|
1015
|
+
};
|
|
1004
1016
|
var CONTEXT_PANEL_CLASS = "p-[var(--spacing-system-l)] shrink-0 lg:w-[280px] border-b lg:border-b-0 lg:border-r border-ods-border lg:min-h-0 lg:overflow-y-auto";
|
|
1005
1017
|
var ACTION_PANEL_CLASS = "flex-1 min-w-0 flex flex-col md:min-h-0 lg:p-[var(--spacing-system-l)]";
|
|
1006
1018
|
var PANEL_STEP_CLASS = "p-[var(--spacing-system-l)] md:min-h-0 md:overflow-y-auto lg:p-0";
|
|
@@ -1189,8 +1201,8 @@ function HubSpotMeetingScheduler({
|
|
|
1189
1201
|
"\xB7 ",
|
|
1190
1202
|
formatDurationCompact(durationMs / 1e3)
|
|
1191
1203
|
] }),
|
|
1192
|
-
bookingError && bookingError !== "SLOT_TAKEN" && /* @__PURE__ */ jsxs6("
|
|
1193
|
-
/* @__PURE__ */ jsx6(
|
|
1204
|
+
bookingError && bookingError !== "SLOT_TAKEN" && /* @__PURE__ */ jsxs6(Alert, { variant: "error", className: "flex flex-col items-start gap-[var(--spacing-system-xs)]", children: [
|
|
1205
|
+
/* @__PURE__ */ jsx6(AlertDescription, { children: BOOKING_ERROR_COPY[bookingError] ?? BOOKING_ERROR_COPY.VALIDATION }),
|
|
1194
1206
|
escapeHatch
|
|
1195
1207
|
] }),
|
|
1196
1208
|
/* @__PURE__ */ jsx6(
|
|
@@ -1208,7 +1220,14 @@ function HubSpotMeetingScheduler({
|
|
|
1208
1220
|
}
|
|
1209
1221
|
)
|
|
1210
1222
|
] }) : /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-[var(--spacing-system-m)] md:min-h-0 md:flex-1", children: [
|
|
1211
|
-
bookingError === "SLOT_TAKEN" && /* @__PURE__ */ jsx6(
|
|
1223
|
+
bookingError === "SLOT_TAKEN" && /* @__PURE__ */ jsx6(
|
|
1224
|
+
Alert,
|
|
1225
|
+
{
|
|
1226
|
+
variant: "warning",
|
|
1227
|
+
className: "mx-[var(--spacing-system-l)] mt-[var(--spacing-system-l)] w-auto lg:m-0 lg:w-full",
|
|
1228
|
+
children: /* @__PURE__ */ jsx6(AlertDescription, { children: "That time was just taken \u2014 pick another slot below. (If you already submitted, check your email for a confirmation before rebooking.)" })
|
|
1229
|
+
}
|
|
1230
|
+
),
|
|
1212
1231
|
timezone && durationMs != null ? (
|
|
1213
1232
|
// The picker owns BOTH the loading and the empty month now —
|
|
1214
1233
|
// it keeps the calendar up either way, so there is no branch
|