@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.
@@ -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;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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/meeting-scheduler/index.tsx"],"names":[],"mappings":"AA0CA,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,+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"}
@@ -26,7 +26,8 @@ import {
26
26
  import "../../chunk-L2SLFCXP.js";
27
27
  import "../../chunk-BJXDHKUX.js";
28
28
  import {
29
- useHumanitySignals
29
+ useHumanitySignals,
30
+ useToast
30
31
  } from "../../chunk-HM2R5MDR.js";
31
32
  import "../../chunk-ZRIVCLPR.js";
32
33
  import "../../chunk-T5DAFFK7.js";
@@ -79,102 +80,17 @@ import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMem
79
80
  // src/hooks/use-meeting-booking.ts
80
81
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
81
82
  import { useCallback, useLayoutEffect, useRef, useState } from "react";
82
- var KNOWN_CODES = [
83
+
84
+ // src/schemas/meeting-booking-schema.ts
85
+ import { z } from "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 = useQueryClient();
96
- const seededOffset = initialAvailability?.meetingId === meetingId ? initialAvailability.monthOffset : void 0;
97
- const [monthOffset, setMonthOffsetState] = useState(seededOffset ?? 0);
98
- const setMonthOffset = useCallback((offset) => {
99
- setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)));
100
- }, []);
101
- const prevMeetingIdRef = useRef(meetingId);
102
- useLayoutEffect(() => {
103
- if (prevMeetingIdRef.current === meetingId) return;
104
- prevMeetingIdRef.current = meetingId;
105
- setMonthOffsetState(0);
106
- }, [meetingId]);
107
- const seed = initialAvailability?.meetingId === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
108
- const availabilityQuery = useQuery({
109
- queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
110
- queryFn: async ({ signal }) => {
111
- const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
112
- const res = await contentFetch(`${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 = useCallback(async () => {
124
- await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
125
- }, [queryClient, apiBaseUrl, meetingId, monthOffset]);
126
- const bookMutation = useMutation({
127
- mutationFn: async (payload) => {
128
- try {
129
- const res = await contentFetch(`${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 {
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 = useRef(null);
147
- const { mutateAsync } = bookMutation;
148
- const book = useCallback(
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: 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
- import { z } from "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);
@@ -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 = useQueryClient();
188
+ const seededOffset = initialAvailability?.meetingId === meetingId ? initialAvailability.monthOffset : void 0;
189
+ const [monthOffset, setMonthOffsetState] = useState(seededOffset ?? 0);
190
+ const setMonthOffset = useCallback((offset) => {
191
+ setMonthOffsetState(Math.max(0, Math.min(MAX_MONTH_OFFSET, offset)));
192
+ }, []);
193
+ const prevMeetingIdRef = useRef(meetingId);
194
+ useLayoutEffect(() => {
195
+ if (prevMeetingIdRef.current === meetingId) return;
196
+ prevMeetingIdRef.current = meetingId;
197
+ setMonthOffsetState(0);
198
+ }, [meetingId]);
199
+ const seed = initialAvailability?.meetingId === meetingId && initialAvailability.monthOffset === monthOffset ? initialAvailability : void 0;
200
+ const availabilityQuery = useQuery({
201
+ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset),
202
+ queryFn: async ({ signal }) => {
203
+ const params = new URLSearchParams({ meeting: meetingId, monthOffset: String(monthOffset) });
204
+ const res = await contentFetch(`${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 = useCallback(async () => {
216
+ await queryClient.invalidateQueries({ queryKey: meetingAvailabilityKey(apiBaseUrl, meetingId, monthOffset) }).catch(() => void 0);
217
+ }, [queryClient, apiBaseUrl, meetingId, monthOffset]);
218
+ const bookMutation = useMutation({
219
+ mutationFn: async (payload) => {
220
+ try {
221
+ const res = await contentFetch(`${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 {
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 = useRef(null);
239
+ const { mutateAsync } = bookMutation;
240
+ const book = useCallback(
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: 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
  import { zodResolver } from "@hookform/resolvers/zod";
266
270
  import { useMemo } from "react";
@@ -1076,6 +1080,7 @@ function HubSpotMeetingScheduler({
1076
1080
  const [confirmation, setConfirmation] = useState3(null);
1077
1081
  const [bookingError, setBookingError] = useState3(null);
1078
1082
  const { honeypotInputProps, getSignals, resetSignals } = useHumanitySignals();
1083
+ const { toast } = useToast();
1079
1084
  const autoAdvanceCount = useRef2(0);
1080
1085
  const [machineFor, setMachineFor] = useState3(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: 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__ */ jsx6(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
  formatDurationCompact(durationMs / 1e3)
1200
1211
  ] }),
1201
- bookingError && bookingError !== "SLOT_TAKEN" && /* @__PURE__ */ jsxs6(Alert, { variant: "error", className: "flex flex-col items-start gap-[var(--spacing-system-xs)]", children: [
1202
- /* @__PURE__ */ jsx6(AlertDescription, { children: BOOKING_ERROR_COPY[bookingError] ?? BOOKING_ERROR_COPY.VALIDATION }),
1203
- escapeHatch
1204
- ] }),
1205
1212
  /* @__PURE__ */ jsx6(
1206
1213
  BookingForm,
1207
1214
  {