@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.
Files changed (108) hide show
  1. package/dist/{chunk-TIX2SHZX.js → chunk-2EPUD2VR.js} +2 -2
  2. package/dist/{chunk-2EB5273T.js → chunk-2IK6SB6F.js} +2 -2
  3. package/dist/{chunk-OUMRHNZW.cjs → chunk-3IUQLB47.cjs} +3 -3
  4. package/dist/{chunk-OUMRHNZW.cjs.map → chunk-3IUQLB47.cjs.map} +1 -1
  5. package/dist/{chunk-BWHLWS32.cjs → chunk-3U2P63NQ.cjs} +10 -10
  6. package/dist/{chunk-BWHLWS32.cjs.map → chunk-3U2P63NQ.cjs.map} +1 -1
  7. package/dist/{chunk-EC44ZXBH.js → chunk-5QDGP3RI.js} +2 -2
  8. package/dist/{chunk-YS64Y5MP.cjs → chunk-7G23ANVM.cjs} +49 -49
  9. package/dist/{chunk-YS64Y5MP.cjs.map → chunk-7G23ANVM.cjs.map} +1 -1
  10. package/dist/{chunk-R3I2XI33.js → chunk-BGE7CKXC.js} +4 -4
  11. package/dist/{chunk-6SMQCAPO.cjs → chunk-BGZCZ2JS.cjs} +3 -3
  12. package/dist/{chunk-6SMQCAPO.cjs.map → chunk-BGZCZ2JS.cjs.map} +1 -1
  13. package/dist/{chunk-B7S7ETML.cjs → chunk-BKXL7OYT.cjs} +3 -3
  14. package/dist/{chunk-B7S7ETML.cjs.map → chunk-BKXL7OYT.cjs.map} +1 -1
  15. package/dist/{chunk-4WCTY326.js → chunk-BTZF3JRS.js} +2 -2
  16. package/dist/{chunk-J5OC2KYU.js → chunk-DTSRMQIY.js} +5 -5
  17. package/dist/{chunk-4TGUWWMY.cjs → chunk-GFSJXRX7.cjs} +16 -16
  18. package/dist/{chunk-4TGUWWMY.cjs.map → chunk-GFSJXRX7.cjs.map} +1 -1
  19. package/dist/{chunk-P625NSEA.cjs → chunk-IRJQH7RJ.cjs} +17 -17
  20. package/dist/{chunk-P625NSEA.cjs.map → chunk-IRJQH7RJ.cjs.map} +1 -1
  21. package/dist/{chunk-DSU7LQMU.js → chunk-ISULYCGY.js} +2 -2
  22. package/dist/{chunk-SB67TZQU.js → chunk-IWISKP3F.js} +2 -2
  23. package/dist/{chunk-JLIXPN37.cjs → chunk-K3XONUML.cjs} +4 -4
  24. package/dist/{chunk-JLIXPN37.cjs.map → chunk-K3XONUML.cjs.map} +1 -1
  25. package/dist/{chunk-IBZBGRO2.js → chunk-KG7D35RU.js} +5 -5
  26. package/dist/{chunk-3ENV3IEP.js → chunk-KSKCQ3ZN.js} +2 -2
  27. package/dist/{chunk-NGXP4EAQ.js → chunk-OE7NV7H3.js} +2 -2
  28. package/dist/{chunk-66LZDE7U.js → chunk-PBS4F534.js} +3 -2
  29. package/dist/chunk-PBS4F534.js.map +1 -0
  30. package/dist/{chunk-AQBWGEL3.cjs → chunk-PUL6X6HY.cjs} +32 -32
  31. package/dist/{chunk-AQBWGEL3.cjs.map → chunk-PUL6X6HY.cjs.map} +1 -1
  32. package/dist/{chunk-YK7M7RBF.cjs → chunk-QDOBY3TP.cjs} +3 -2
  33. package/dist/chunk-QDOBY3TP.cjs.map +1 -0
  34. package/dist/{chunk-OYWAPTEF.cjs → chunk-QF5B6V4L.cjs} +17 -17
  35. package/dist/{chunk-OYWAPTEF.cjs.map → chunk-QF5B6V4L.cjs.map} +1 -1
  36. package/dist/{chunk-NCCWS6YE.cjs → chunk-QTVW5XBL.cjs} +4 -4
  37. package/dist/{chunk-NCCWS6YE.cjs.map → chunk-QTVW5XBL.cjs.map} +1 -1
  38. package/dist/{chunk-PDQ7F5AB.cjs → chunk-RXJZXMDZ.cjs} +13 -13
  39. package/dist/{chunk-PDQ7F5AB.cjs.map → chunk-RXJZXMDZ.cjs.map} +1 -1
  40. package/dist/{chunk-EIK6KDAE.cjs → chunk-SVUZUFT4.cjs} +34 -34
  41. package/dist/{chunk-EIK6KDAE.cjs.map → chunk-SVUZUFT4.cjs.map} +1 -1
  42. package/dist/{chunk-GSSJFXIW.cjs → chunk-UILNZL6J.cjs} +3 -3
  43. package/dist/{chunk-GSSJFXIW.cjs.map → chunk-UILNZL6J.cjs.map} +1 -1
  44. package/dist/{chunk-4IAZOAS7.js → chunk-UPEIX7VG.js} +4 -4
  45. package/dist/{chunk-SY7M6VAJ.js → chunk-WAWMZSCP.js} +3 -3
  46. package/dist/{chunk-JTDYVW2B.js → chunk-WVKRSC65.js} +2 -2
  47. package/dist/components/case-studies/index.cjs +8 -8
  48. package/dist/components/case-studies/index.js +2 -2
  49. package/dist/components/contact/index.cjs +3 -3
  50. package/dist/components/contact/index.js +2 -2
  51. package/dist/components/docs/index.cjs +6 -6
  52. package/dist/components/docs/index.js +5 -5
  53. package/dist/components/embeds/index.cjs +3 -3
  54. package/dist/components/embeds/index.js +2 -2
  55. package/dist/components/faq/index.cjs +3 -3
  56. package/dist/components/faq/index.js +2 -2
  57. package/dist/components/features/index.cjs +2 -2
  58. package/dist/components/features/index.js +1 -1
  59. package/dist/components/help-center-pages/index.cjs +22 -22
  60. package/dist/components/help-center-pages/index.js +13 -13
  61. package/dist/components/index.cjs +114 -114
  62. package/dist/components/index.js +12 -12
  63. package/dist/components/meeting-scheduler/index.cjs +130 -111
  64. package/dist/components/meeting-scheduler/index.cjs.map +1 -1
  65. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  66. package/dist/components/meeting-scheduler/index.js +113 -94
  67. package/dist/components/meeting-scheduler/index.js.map +1 -1
  68. package/dist/components/navigation/index.cjs +5 -5
  69. package/dist/components/navigation/index.js +4 -4
  70. package/dist/components/onboarding-guides/index.cjs +5 -5
  71. package/dist/components/onboarding-guides/index.js +4 -4
  72. package/dist/components/tickets/index.cjs +6 -6
  73. package/dist/components/tickets/index.js +5 -5
  74. package/dist/components/ui/alert.d.ts +1 -1
  75. package/dist/components/ui/alert.d.ts.map +1 -1
  76. package/dist/components/ui/index.cjs +2 -2
  77. package/dist/components/ui/index.js +1 -1
  78. package/dist/hooks/use-meeting-booking.d.ts +1 -1
  79. package/dist/hooks/use-meeting-booking.d.ts.map +1 -1
  80. package/dist/index.cjs +6 -6
  81. package/dist/index.js +5 -5
  82. package/dist/schemas/meeting-booking-schema.cjs +9 -0
  83. package/dist/schemas/meeting-booking-schema.cjs.map +1 -1
  84. package/dist/schemas/meeting-booking-schema.d.ts +15 -2
  85. package/dist/schemas/meeting-booking-schema.d.ts.map +1 -1
  86. package/dist/schemas/meeting-booking-schema.js +9 -1
  87. package/dist/schemas/meeting-booking-schema.js.map +1 -1
  88. package/package.json +1 -1
  89. package/src/components/meeting-scheduler/index.tsx +34 -16
  90. package/src/components/ui/alert.tsx +1 -0
  91. package/src/hooks/use-meeting-booking.ts +6 -13
  92. package/src/schemas/meeting-booking-schema.ts +23 -3
  93. package/dist/chunk-66LZDE7U.js.map +0 -1
  94. package/dist/chunk-YK7M7RBF.cjs.map +0 -1
  95. /package/dist/{chunk-TIX2SHZX.js.map → chunk-2EPUD2VR.js.map} +0 -0
  96. /package/dist/{chunk-2EB5273T.js.map → chunk-2IK6SB6F.js.map} +0 -0
  97. /package/dist/{chunk-EC44ZXBH.js.map → chunk-5QDGP3RI.js.map} +0 -0
  98. /package/dist/{chunk-R3I2XI33.js.map → chunk-BGE7CKXC.js.map} +0 -0
  99. /package/dist/{chunk-4WCTY326.js.map → chunk-BTZF3JRS.js.map} +0 -0
  100. /package/dist/{chunk-J5OC2KYU.js.map → chunk-DTSRMQIY.js.map} +0 -0
  101. /package/dist/{chunk-DSU7LQMU.js.map → chunk-ISULYCGY.js.map} +0 -0
  102. /package/dist/{chunk-SB67TZQU.js.map → chunk-IWISKP3F.js.map} +0 -0
  103. /package/dist/{chunk-IBZBGRO2.js.map → chunk-KG7D35RU.js.map} +0 -0
  104. /package/dist/{chunk-3ENV3IEP.js.map → chunk-KSKCQ3ZN.js.map} +0 -0
  105. /package/dist/{chunk-NGXP4EAQ.js.map → chunk-OE7NV7H3.js.map} +0 -0
  106. /package/dist/{chunk-4IAZOAS7.js.map → chunk-UPEIX7VG.js.map} +0 -0
  107. /package/dist/{chunk-SY7M6VAJ.js.map → chunk-WAWMZSCP.js.map} +0 -0
  108. /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;AAS9C,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,+BAoV9B;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"}
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-66LZDE7U.js";
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
- var KNOWN_CODES = [
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("div", { className: "flex flex-col items-start gap-[var(--spacing-system-xs)]", children: [
1193
- /* @__PURE__ */ jsx6("p", { className: "text-ods-error text-h6", children: bookingError === "TEMPORARILY_UNAVAILABLE" ? "Scheduling is briefly unavailable \u2014 please try again in a minute." : bookingError === "MEETING_UNAVAILABLE" ? "This meeting type has reached its booking limit for today \u2014 try another time or contact us." : bookingError === "LINK_GONE" ? "This meeting type is no longer available." : "Please double-check your details and try again." }),
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("p", { className: "px-[var(--spacing-system-l)] pt-[var(--spacing-system-l)] text-ods-error text-h6 lg:p-0", children: "That time is no longer available \u2014 if you just submitted, check your email for a confirmation before rebooking." }),
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