@cimplify/sdk 0.90.0 → 0.92.0

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 (67) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/advanced.js +20 -20
  4. package/dist/advanced.mjs +1 -1
  5. package/dist/{chunk-JVWFIFQ7.js → chunk-3MBMIEWT.js} +4 -4
  6. package/dist/{chunk-2UDEWILL.mjs → chunk-3XD2FVP4.mjs} +117 -35
  7. package/dist/{chunk-NGE37REW.mjs → chunk-54H2YYIW.mjs} +2 -2
  8. package/dist/{chunk-BN4V4ONE.js → chunk-DCRN67NO.js} +158 -73
  9. package/dist/{chunk-T7Z25EY4.js → chunk-HKN3YG76.js} +84 -0
  10. package/dist/{chunk-GHFDYM5K.js → chunk-IVJEUGWF.js} +10 -1
  11. package/dist/{chunk-KJP4U6L3.js → chunk-OAHSF542.js} +12 -0
  12. package/dist/{chunk-NNGKC3MM.mjs → chunk-PYBFTMHI.mjs} +12 -0
  13. package/dist/{chunk-OZDDHSIB.mjs → chunk-RRBY35TJ.mjs} +80 -1
  14. package/dist/{chunk-ZU4ZRYUR.mjs → chunk-T7LDBZZZ.mjs} +10 -1
  15. package/dist/{client-YiSelM7x.d.mts → client-BQbSKlHS.d.mts} +90 -11
  16. package/dist/{client-DMttx1xk.d.mts → client-Bw4HtLP1.d.mts} +2 -2
  17. package/dist/{client-CR1f94wY.d.ts → client-CeW3acve.d.ts} +90 -11
  18. package/dist/{client-CqpOcbin.d.ts → client-Otzv_rgP.d.ts} +2 -2
  19. package/dist/{index-DD9z396M.d.mts → index-CmIboQjl.d.mts} +3 -3
  20. package/dist/{index-CxTVu0HZ.d.ts → index-DgoRwz8l.d.ts} +3 -3
  21. package/dist/index.d.mts +49 -7
  22. package/dist/index.d.ts +49 -7
  23. package/dist/index.js +103 -83
  24. package/dist/index.mjs +5 -5
  25. package/dist/mock/cli.mjs +12 -0
  26. package/dist/mock/library.js +12 -0
  27. package/dist/mock/library.mjs +12 -0
  28. package/dist/mock/msw.js +12 -0
  29. package/dist/mock/msw.mjs +12 -0
  30. package/dist/{payment-5d8FTA1q.d.mts → payment-BFtLqiG2.d.mts} +1 -1
  31. package/dist/{payment-BX7ccIKY.d.ts → payment-Cq8FT1B6.d.ts} +1 -1
  32. package/dist/{price-DtfP_Xfq.d.ts → price-DaFXT-Lr.d.ts} +1 -1
  33. package/dist/{price-arl_4Rzn.d.mts → price-DleDWk_s.d.mts} +1 -1
  34. package/dist/{product-DZX4QuKS.d.ts → product-bZ8G5Z8D.d.mts} +38 -0
  35. package/dist/{product-DZX4QuKS.d.mts → product-bZ8G5Z8D.d.ts} +38 -0
  36. package/dist/react.d.mts +71 -6
  37. package/dist/react.d.ts +71 -6
  38. package/dist/react.js +466 -337
  39. package/dist/react.mjs +398 -272
  40. package/dist/{server-BceZMxcK.d.mts → server-D-iK-bNt.d.mts} +1 -1
  41. package/dist/{server-DoJGSC7L.d.ts → server-DwsY9hlX.d.ts} +1 -1
  42. package/dist/server.d.mts +4 -4
  43. package/dist/server.d.ts +4 -4
  44. package/dist/server.js +7 -7
  45. package/dist/server.mjs +2 -2
  46. package/dist/styles.css +1 -1
  47. package/dist/testing/msw.d.mts +2 -2
  48. package/dist/testing/msw.d.ts +2 -2
  49. package/dist/testing/msw.js +2 -2
  50. package/dist/testing/msw.mjs +1 -1
  51. package/dist/testing/suite.d.mts +6 -6
  52. package/dist/testing/suite.d.ts +6 -6
  53. package/dist/testing/suite.js +23 -23
  54. package/dist/testing/suite.mjs +4 -4
  55. package/dist/testing.d.mts +5 -5
  56. package/dist/testing.d.ts +5 -5
  57. package/dist/testing.js +79 -79
  58. package/dist/testing.mjs +5 -5
  59. package/dist/utils.d.mts +4 -4
  60. package/dist/utils.d.ts +4 -4
  61. package/package.json +1 -1
  62. package/registry/booking-page.json +1 -1
  63. package/registry/chat-widget.json +1 -1
  64. package/registry/customer-input-fields.json +1 -1
  65. package/registry/date-slot-picker.json +1 -1
  66. package/registry/product-customizer.json +1 -1
  67. package/registry/rental-service-card.json +1 -1
package/dist/react.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkT7Z25EY4_js = require('./chunk-T7Z25EY4.js');
4
+ var chunkHKN3YG76_js = require('./chunk-HKN3YG76.js');
5
5
  var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
6
- var chunkBN4V4ONE_js = require('./chunk-BN4V4ONE.js');
7
- var chunkGHFDYM5K_js = require('./chunk-GHFDYM5K.js');
6
+ var chunkDCRN67NO_js = require('./chunk-DCRN67NO.js');
7
+ var chunkIVJEUGWF_js = require('./chunk-IVJEUGWF.js');
8
8
  var chunkPTPOQVXS_js = require('./chunk-PTPOQVXS.js');
9
9
  var chunkAETVRBKI_js = require('./chunk-AETVRBKI.js');
10
10
  require('./chunk-OWW5GUSB.js');
@@ -747,6 +747,7 @@ function mapServerCart(serverCart, fallbackCurrency) {
747
747
  scheduledStart: item.scheduled_start || void 0,
748
748
  scheduledEnd: item.scheduled_end || void 0,
749
749
  staffId: item.staff_id || void 0,
750
+ units: item.units || void 0,
750
751
  lineType: item.line_type,
751
752
  confirmationCode: item.confirmation_code || void 0,
752
753
  serviceStatus: item.service_status || void 0,
@@ -805,7 +806,7 @@ async function syncFromServer(entry) {
805
806
  return next;
806
807
  }
807
808
  function emitMutationError(entry, operation, opId, idempotencyKey, error) {
808
- entry.client.events.emit("mutation:error", {
809
+ entry.client.events.emit(chunkDCRN67NO_js.MUTATION_ERROR, {
809
810
  service: "cart",
810
811
  operation,
811
812
  opId,
@@ -845,6 +846,12 @@ function getOrCreateStore(params) {
845
846
  syncTail: Promise.resolve(),
846
847
  failedOpRemovers: /* @__PURE__ */ new Set()
847
848
  };
849
+ client.events.on(chunkDCRN67NO_js.SESSION_CLAIMED, () => {
850
+ if (entry.initialized) void syncFromServer(entry);
851
+ });
852
+ client.events.on(chunkDCRN67NO_js.SESSION_CLEARED, () => {
853
+ if (entry.initialized) void syncFromServer(entry);
854
+ });
848
855
  cartStores.set(storeKey, entry);
849
856
  return entry;
850
857
  }
@@ -903,7 +910,8 @@ async function performAddItem(entry, product, quantity, options) {
903
910
  specialInstructions: normalizedOptions.specialInstructions,
904
911
  scheduledStart: normalizedOptions.scheduledStart,
905
912
  scheduledEnd: normalizedOptions.scheduledEnd,
906
- staffId: normalizedOptions.staffId
913
+ staffId: normalizedOptions.staffId,
914
+ units: normalizedOptions.units
907
915
  });
908
916
  const idempotencyKey = `idem_${typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`}`;
909
917
  const patch = { kind: "add_item", tempItem, lineKey };
@@ -925,6 +933,7 @@ async function performAddItem(entry, product, quantity, options) {
925
933
  scheduled_end: normalizedOptions.scheduledEnd,
926
934
  staff_id: normalizedOptions.staffId,
927
935
  resource_id: normalizedOptions.resourceId,
936
+ units: normalizedOptions.units,
928
937
  customer_inputs: normalizedOptions.customerInputs
929
938
  },
930
939
  { idempotencyKey }
@@ -1136,7 +1145,7 @@ var DEFAULT_COUNTRY = "US";
1136
1145
  function createDefaultClient() {
1137
1146
  const processRef = globalThis.process;
1138
1147
  const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
1139
- return chunkBN4V4ONE_js.createCimplifyClient({ publicKey: envPublicKey });
1148
+ return chunkDCRN67NO_js.createCimplifyClient({ publicKey: envPublicKey });
1140
1149
  }
1141
1150
  function getStoredLocationId() {
1142
1151
  if (typeof window === "undefined" || !window.localStorage) {
@@ -1289,7 +1298,7 @@ function CimplifyProvider({
1289
1298
  const apiCredentials = resolvedClient.getCredentialsMode() === "include" ? "use-credentials" : "anonymous";
1290
1299
  preconnectOrigin(resolvedClient.getBaseUrl(), apiCredentials);
1291
1300
  preconnectOrigin(resolvedClient.getLinkApiUrl(), apiCredentials);
1292
- preconnectOrigin(chunkT7Z25EY4_js.DEFAULT_CDN_BASE_URL);
1301
+ preconnectOrigin(chunkHKN3YG76_js.DEFAULT_CDN_BASE_URL);
1293
1302
  }
1294
1303
  const setCurrentLocation = React14.useCallback(
1295
1304
  (location) => {
@@ -3448,7 +3457,7 @@ function CimplifySignInButton({
3448
3457
  const onClick = async () => {
3449
3458
  if (busy || disabled || !signInOpts.clientId) return;
3450
3459
  setBusy(true);
3451
- await chunkGHFDYM5K_js.startSignIn({
3460
+ await chunkIVJEUGWF_js.startSignIn({
3452
3461
  ...signInOpts,
3453
3462
  onSuccess: () => {
3454
3463
  setBusy(false);
@@ -5478,14 +5487,24 @@ function useServices(options = {}) {
5478
5487
  }
5479
5488
 
5480
5489
  // src/react/hooks/use-available-slots.ts
5490
+ function narrowingUnits(units) {
5491
+ return units != null && units > 1 ? units : void 0;
5492
+ }
5481
5493
  var slotsQuery = createCachedQuery({
5482
5494
  name: "useAvailableSlots",
5483
- key: (client, options) => cacheKey(client.getPublicKey(), options.serviceId, options.date, options.participantCount),
5495
+ key: (client, options) => cacheKey(
5496
+ client.getPublicKey(),
5497
+ options.serviceId,
5498
+ options.date,
5499
+ options.participantCount,
5500
+ narrowingUnits(options.units)
5501
+ ),
5484
5502
  fetch: async (client, options) => {
5485
5503
  const result = await client.scheduling.getAvailableSlots({
5486
5504
  service_id: options.serviceId,
5487
5505
  date: options.date,
5488
- participant_count: options.participantCount
5506
+ participant_count: options.participantCount,
5507
+ units: narrowingUnits(options.units)
5489
5508
  });
5490
5509
  if (!result.ok) throw result.error;
5491
5510
  return result.value;
@@ -5502,6 +5521,9 @@ function useAvailableSlots(serviceId, date, options = {}) {
5502
5521
  }
5503
5522
 
5504
5523
  // src/react/hooks/use-service-availability.ts
5524
+ function narrowingUnits2(units) {
5525
+ return units != null && units > 1 ? units : void 0;
5526
+ }
5505
5527
  var serviceAvailabilityQuery = createCachedQuery({
5506
5528
  name: "useServiceAvailability",
5507
5529
  key: (client, options) => cacheKey(
@@ -5509,7 +5531,8 @@ var serviceAvailabilityQuery = createCachedQuery({
5509
5531
  options.serviceId,
5510
5532
  options.startDate,
5511
5533
  options.endDate,
5512
- options.participantCount
5534
+ options.participantCount,
5535
+ narrowingUnits2(options.units)
5513
5536
  ),
5514
5537
  fetch: async (client, options) => {
5515
5538
  const result = await client.scheduling.getServiceAvailability({
@@ -5517,7 +5540,8 @@ var serviceAvailabilityQuery = createCachedQuery({
5517
5540
  start_date: options.startDate,
5518
5541
  end_date: options.endDate,
5519
5542
  location_id: options.locationId,
5520
- participant_count: options.participantCount
5543
+ participant_count: options.participantCount,
5544
+ units: narrowingUnits2(options.units)
5521
5545
  });
5522
5546
  if (!result.ok) throw result.error;
5523
5547
  return result.value.availability;
@@ -5901,6 +5925,9 @@ function useChat(options = {}) {
5901
5925
  const [awaitingReply, setAwaitingReply] = React14.useState(false);
5902
5926
  const [seenAt, setSeenAt] = React14.useState(null);
5903
5927
  const [mayHaveOlder, setMayHaveOlder] = React14.useState(false);
5928
+ const [claimedAs, setClaimedAs] = React14.useState(
5929
+ () => client?.support.claimedCustomerName ?? null
5930
+ );
5904
5931
  const pollRef = React14.useRef(null);
5905
5932
  const lastActivityRef = React14.useRef(Date.now());
5906
5933
  const openedRef = React14.useRef(false);
@@ -5912,6 +5939,7 @@ function useChat(options = {}) {
5912
5939
  const aiEnabledRef = React14.useRef(false);
5913
5940
  const humanRequestedRef = React14.useRef(false);
5914
5941
  const awaitReplyTimerRef = React14.useRef(null);
5942
+ const claimUnsubRef = React14.useRef(null);
5915
5943
  messagesRef.current = messages;
5916
5944
  clientRef.current = client;
5917
5945
  humanRequestedRef.current = humanRequested;
@@ -5972,6 +6000,17 @@ function useChat(options = {}) {
5972
6000
  pollRef.current = null;
5973
6001
  }
5974
6002
  }, []);
6003
+ const resyncConversation = React14.useCallback(async () => {
6004
+ const c = clientRef.current;
6005
+ if (!c || !openedRef.current) return;
6006
+ const result = await c.support.openConversation();
6007
+ if (!result.ok) return;
6008
+ setMessages(result.value.messages);
6009
+ aiEnabledRef.current = result.value.conversation.ai_enabled;
6010
+ setHumanRequested(result.value.conversation.needs_human_review ?? false);
6011
+ setSeenAt(result.value.conversation.team_read_at ?? null);
6012
+ setMayHaveOlder(result.value.messages.length >= 50);
6013
+ }, []);
5975
6014
  const connectWebSocket = React14.useCallback(() => {
5976
6015
  const c = clientRef.current;
5977
6016
  if (!c || typeof WebSocket === "undefined") {
@@ -6006,6 +6045,8 @@ function useChat(options = {}) {
6006
6045
  if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
6007
6046
  fetchNewMessages();
6008
6047
  }
6048
+ } else if (envelope.type === "rekey") {
6049
+ void resyncConversation();
6009
6050
  }
6010
6051
  } catch {
6011
6052
  }
@@ -6025,12 +6066,57 @@ function useChat(options = {}) {
6025
6066
  ws.onerror = () => {
6026
6067
  };
6027
6068
  wsRef.current = ws;
6028
- }, [appendMessages, startPolling, stopPolling, fetchNewMessages]);
6069
+ }, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation]);
6070
+ const resetConversation = React14.useCallback(() => {
6071
+ stopPolling();
6072
+ if (wsReconnectTimerRef.current) {
6073
+ clearTimeout(wsReconnectTimerRef.current);
6074
+ wsReconnectTimerRef.current = null;
6075
+ }
6076
+ if (wsRef.current) {
6077
+ wsRef.current.onclose = null;
6078
+ wsRef.current.close();
6079
+ wsRef.current = null;
6080
+ }
6081
+ wsReconnectAttemptRef.current = 0;
6082
+ openedRef.current = false;
6083
+ stopAwaitingReply();
6084
+ setMessages([]);
6085
+ setConversationOpened(false);
6086
+ setHumanRequested(false);
6087
+ setSeenAt(null);
6088
+ setMayHaveOlder(false);
6089
+ setError(null);
6090
+ setClaimedAs(null);
6091
+ }, [stopPolling, stopAwaitingReply]);
6092
+ const watchSessionClaims = React14.useCallback(() => {
6093
+ const c = clientRef.current;
6094
+ if (!c || claimUnsubRef.current) return;
6095
+ const unsubClaimed = c.events.on(chunkDCRN67NO_js.SESSION_CLAIMED, (event) => {
6096
+ setClaimedAs(event.customerName || null);
6097
+ if (!openedRef.current) return;
6098
+ connectWebSocket();
6099
+ void resyncConversation();
6100
+ });
6101
+ const unsubCleared = c.events.on(chunkDCRN67NO_js.SESSION_CLEARED, resetConversation);
6102
+ claimUnsubRef.current = () => {
6103
+ unsubClaimed();
6104
+ unsubCleared();
6105
+ };
6106
+ }, [connectWebSocket, resyncConversation, resetConversation]);
6107
+ const signOut = React14.useCallback(async () => {
6108
+ const c = clientRef.current;
6109
+ if (!c) return false;
6110
+ const result = await c.link.logout();
6111
+ return result.ok;
6112
+ }, []);
6029
6113
  React14.useEffect(() => {
6030
6114
  return () => {
6031
6115
  if (pollRef.current) clearInterval(pollRef.current);
6032
6116
  if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
6033
6117
  if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
6118
+ claimUnsubRef.current?.();
6119
+ claimUnsubRef.current = null;
6034
6120
  if (wsRef.current) {
6035
6121
  wsRef.current.onclose = null;
6036
6122
  wsRef.current.close();
@@ -6050,13 +6136,15 @@ function useChat(options = {}) {
6050
6136
  setHumanRequested(result.value.conversation.needs_human_review ?? false);
6051
6137
  setSeenAt(result.value.conversation.team_read_at ?? null);
6052
6138
  setMayHaveOlder(result.value.messages.length >= 50);
6139
+ setClaimedAs(client.support.claimedCustomerName);
6053
6140
  connectWebSocket();
6141
+ watchSessionClaims();
6054
6142
  } else {
6055
6143
  openedRef.current = false;
6056
6144
  setError(result.error);
6057
6145
  }
6058
6146
  setIsLoading(false);
6059
- }, [client, connectWebSocket]);
6147
+ }, [client, connectWebSocket, watchSessionClaims]);
6060
6148
  const send = React14.useCallback(
6061
6149
  async (content, opts) => {
6062
6150
  const attachments = opts?.attachments ?? [];
@@ -6176,6 +6264,8 @@ function useChat(options = {}) {
6176
6264
  awaitingReply,
6177
6265
  seenAt,
6178
6266
  mayHaveOlder,
6267
+ claimedAs,
6268
+ signOut,
6179
6269
  send,
6180
6270
  startConversation,
6181
6271
  react,
@@ -6255,15 +6345,15 @@ function AuthElement({
6255
6345
  }, [onReady, onAuthenticated, onError]);
6256
6346
  React14.useEffect(() => {
6257
6347
  if (!elements || !containerRef.current) return;
6258
- const element = elements.create(chunkGHFDYM5K_js.ELEMENT_TYPES.AUTH, { prefillEmail });
6348
+ const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.AUTH, { prefillEmail });
6259
6349
  elementRef.current = element;
6260
- element.on(chunkGHFDYM5K_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6350
+ element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6261
6351
  element.on(
6262
- chunkGHFDYM5K_js.EVENT_TYPES.AUTHENTICATED,
6352
+ chunkIVJEUGWF_js.EVENT_TYPES.AUTHENTICATED,
6263
6353
  (data) => onAuthenticatedRef.current?.(data)
6264
6354
  );
6265
6355
  element.on(
6266
- chunkGHFDYM5K_js.EVENT_TYPES.ERROR,
6356
+ chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
6267
6357
  (data) => onErrorRef.current?.(data)
6268
6358
  );
6269
6359
  element.mount(containerRef.current);
@@ -6292,15 +6382,15 @@ function AddressElement({
6292
6382
  }, [onReady, onChange, onError]);
6293
6383
  React14.useEffect(() => {
6294
6384
  if (!elements || !containerRef.current) return;
6295
- const element = elements.create(chunkGHFDYM5K_js.ELEMENT_TYPES.ADDRESS, { mode });
6385
+ const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.ADDRESS, { mode });
6296
6386
  elementRef.current = element;
6297
- element.on(chunkGHFDYM5K_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6387
+ element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6298
6388
  element.on(
6299
- chunkGHFDYM5K_js.EVENT_TYPES.CHANGE,
6389
+ chunkIVJEUGWF_js.EVENT_TYPES.CHANGE,
6300
6390
  (data) => onChangeRef.current?.(data)
6301
6391
  );
6302
6392
  element.on(
6303
- chunkGHFDYM5K_js.EVENT_TYPES.ERROR,
6393
+ chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
6304
6394
  (data) => onErrorRef.current?.(data)
6305
6395
  );
6306
6396
  element.mount(containerRef.current);
@@ -6330,15 +6420,15 @@ function PaymentElement({
6330
6420
  }, [onReady, onChange, onError]);
6331
6421
  React14.useEffect(() => {
6332
6422
  if (!elements || !containerRef.current) return;
6333
- const element = elements.create(chunkGHFDYM5K_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
6423
+ const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
6334
6424
  elementRef.current = element;
6335
- element.on(chunkGHFDYM5K_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6425
+ element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
6336
6426
  element.on(
6337
- chunkGHFDYM5K_js.EVENT_TYPES.CHANGE,
6427
+ chunkIVJEUGWF_js.EVENT_TYPES.CHANGE,
6338
6428
  (data) => onChangeRef.current?.(data)
6339
6429
  );
6340
6430
  element.on(
6341
- chunkGHFDYM5K_js.EVENT_TYPES.ERROR,
6431
+ chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
6342
6432
  (data) => onErrorRef.current?.(data)
6343
6433
  );
6344
6434
  element.mount(containerRef.current);
@@ -6629,7 +6719,7 @@ function VariantSelector({
6629
6719
  {
6630
6720
  "data-cimplify-variant-name": true,
6631
6721
  className: cn("flex-1 min-w-0 text-sm", classNames?.name),
6632
- children: chunkT7Z25EY4_js.getVariantDisplayName(variant, productName)
6722
+ children: chunkHKN3YG76_js.getVariantDisplayName(variant, productName)
6633
6723
  }
6634
6724
  ),
6635
6725
  /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-variant-pricing": true, className: cn("text-sm text-muted-foreground", classNames?.pricing), children: [
@@ -7910,6 +8000,177 @@ function VolumePricing({
7910
8000
  }
7911
8001
  );
7912
8002
  }
8003
+ function createPlacesSessionToken() {
8004
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
8005
+ return crypto.randomUUID();
8006
+ }
8007
+ return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
8008
+ }
8009
+ var MIN_QUERY_LENGTH = 3;
8010
+ var DEBOUNCE_MS = 300;
8011
+ var BLUR_CLOSE_MS = 120;
8012
+ function AddressAutocomplete({
8013
+ value,
8014
+ onSelect,
8015
+ onDirty,
8016
+ placeholder,
8017
+ required,
8018
+ autoFocus,
8019
+ id,
8020
+ className,
8021
+ classNames
8022
+ }) {
8023
+ const clientContext = useOptionalCimplifyClient();
8024
+ const [inputText, setInputText] = React14__default.default.useState(value?.formatted_address ?? "");
8025
+ const [suggestions, setSuggestions] = React14__default.default.useState([]);
8026
+ const [isOpen, setIsOpen] = React14__default.default.useState(false);
8027
+ const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
8028
+ const debounceRef = React14__default.default.useRef(null);
8029
+ const seqRef = React14__default.default.useRef(0);
8030
+ const sessionTokenRef = React14__default.default.useRef(createPlacesSessionToken());
8031
+ const blurTimeoutRef = React14__default.default.useRef(null);
8032
+ React14__default.default.useEffect(() => {
8033
+ return () => {
8034
+ if (debounceRef.current) clearTimeout(debounceRef.current);
8035
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8036
+ seqRef.current += 1;
8037
+ };
8038
+ }, []);
8039
+ const fetchSuggestions = React14__default.default.useCallback(
8040
+ async (query) => {
8041
+ const client = clientContext?.client;
8042
+ if (!client || query.length < MIN_QUERY_LENGTH) {
8043
+ setSuggestions([]);
8044
+ setIsOpen(false);
8045
+ return;
8046
+ }
8047
+ const seq = ++seqRef.current;
8048
+ const result = await client.places.autocomplete(query, sessionTokenRef.current);
8049
+ if (seqRef.current !== seq) return;
8050
+ if (result.ok && result.value.predictions.length > 0) {
8051
+ setSuggestions(result.value.predictions);
8052
+ setHighlightedIndex(0);
8053
+ setIsOpen(true);
8054
+ } else {
8055
+ setSuggestions([]);
8056
+ setIsOpen(false);
8057
+ }
8058
+ },
8059
+ [clientContext]
8060
+ );
8061
+ const handleInputChange = (nextValue) => {
8062
+ setInputText(nextValue);
8063
+ onDirty?.();
8064
+ if (debounceRef.current) clearTimeout(debounceRef.current);
8065
+ debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), DEBOUNCE_MS);
8066
+ };
8067
+ const selectSuggestion = React14__default.default.useCallback(
8068
+ async (suggestion) => {
8069
+ if (debounceRef.current) clearTimeout(debounceRef.current);
8070
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8071
+ const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
8072
+ setInputText(fallback);
8073
+ setSuggestions([]);
8074
+ setIsOpen(false);
8075
+ const client = clientContext?.client;
8076
+ if (!client) return;
8077
+ const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
8078
+ sessionTokenRef.current = createPlacesSessionToken();
8079
+ if (result.ok) {
8080
+ const d = result.value;
8081
+ const address = {
8082
+ formatted_address: d.formatted_address || fallback,
8083
+ street_address: d.street_address,
8084
+ apartment: d.apartment,
8085
+ city: d.city,
8086
+ region: d.region,
8087
+ postal_code: d.postal_code,
8088
+ country: d.country,
8089
+ latitude: d.latitude,
8090
+ longitude: d.longitude,
8091
+ place_id: d.place_id
8092
+ };
8093
+ setInputText(address.formatted_address);
8094
+ onSelect(address);
8095
+ }
8096
+ },
8097
+ [clientContext, onSelect]
8098
+ );
8099
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-address-autocomplete": true, className: cn("relative", className, classNames?.root), children: [
8100
+ /* @__PURE__ */ jsxRuntime.jsx(
8101
+ "input",
8102
+ {
8103
+ id,
8104
+ type: "text",
8105
+ value: inputText,
8106
+ onChange: (e) => handleInputChange(e.target.value),
8107
+ onFocus: () => {
8108
+ if (suggestions.length > 0) setIsOpen(true);
8109
+ },
8110
+ onBlur: () => {
8111
+ blurTimeoutRef.current = setTimeout(() => setIsOpen(false), BLUR_CLOSE_MS);
8112
+ },
8113
+ onKeyDown: (e) => {
8114
+ if (e.key === "Escape") {
8115
+ setIsOpen(false);
8116
+ return;
8117
+ }
8118
+ if (!isOpen || suggestions.length === 0) return;
8119
+ if (e.key === "ArrowDown") {
8120
+ e.preventDefault();
8121
+ setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
8122
+ } else if (e.key === "ArrowUp") {
8123
+ e.preventDefault();
8124
+ setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
8125
+ } else if (e.key === "Enter" && highlightedIndex >= 0) {
8126
+ e.preventDefault();
8127
+ void selectSuggestion(suggestions[highlightedIndex]);
8128
+ }
8129
+ },
8130
+ placeholder: placeholder ?? "Search for an address...",
8131
+ required,
8132
+ autoFocus,
8133
+ autoComplete: "off",
8134
+ "aria-autocomplete": "list",
8135
+ "aria-expanded": isOpen && suggestions.length > 0,
8136
+ "data-cimplify-address-input": true,
8137
+ className: classNames?.input
8138
+ }
8139
+ ),
8140
+ isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
8141
+ "div",
8142
+ {
8143
+ role: "listbox",
8144
+ "data-cimplify-address-list": true,
8145
+ className: cn(
8146
+ "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg",
8147
+ classNames?.list
8148
+ ),
8149
+ children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
8150
+ "button",
8151
+ {
8152
+ type: "button",
8153
+ role: "option",
8154
+ "aria-selected": i === highlightedIndex,
8155
+ onMouseDown: (e) => {
8156
+ e.preventDefault();
8157
+ void selectSuggestion(s);
8158
+ },
8159
+ "data-cimplify-address-option": true,
8160
+ className: cn(
8161
+ "block w-full px-3 py-2.5 text-left text-sm transition-colors",
8162
+ i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70",
8163
+ classNames?.option,
8164
+ i === highlightedIndex && classNames?.activeOption
8165
+ ),
8166
+ children: s.description
8167
+ },
8168
+ s.place_id
8169
+ ))
8170
+ }
8171
+ )
8172
+ ] });
8173
+ }
7913
8174
  function parseValue(value) {
7914
8175
  if (!value) return void 0;
7915
8176
  const date = dateFns.parseISO(value);
@@ -8251,12 +8512,12 @@ function FieldInput({
8251
8512
  classNames?.input
8252
8513
  );
8253
8514
  switch (field.field_type) {
8254
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Text:
8255
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Url:
8515
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Text:
8516
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Url:
8256
8517
  return /* @__PURE__ */ jsxRuntime.jsx(
8257
8518
  "input",
8258
8519
  {
8259
- type: field.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
8520
+ type: field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
8260
8521
  value: typeof value === "string" ? value : "",
8261
8522
  onChange: (e) => onValueChange(e.target.value),
8262
8523
  placeholder: field.placeholder,
@@ -8265,7 +8526,7 @@ function FieldInput({
8265
8526
  className: inputClass
8266
8527
  }
8267
8528
  );
8268
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Textarea:
8529
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Textarea:
8269
8530
  return /* @__PURE__ */ jsxRuntime.jsx(
8270
8531
  "textarea",
8271
8532
  {
@@ -8278,7 +8539,7 @@ function FieldInput({
8278
8539
  className: cn(inputClass, "resize-none", classNames?.textarea)
8279
8540
  }
8280
8541
  );
8281
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Number:
8542
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Number:
8282
8543
  return /* @__PURE__ */ jsxRuntime.jsx(
8283
8544
  "input",
8284
8545
  {
@@ -8292,7 +8553,7 @@ function FieldInput({
8292
8553
  className: inputClass
8293
8554
  }
8294
8555
  );
8295
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Select:
8556
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Select:
8296
8557
  return /* @__PURE__ */ jsxRuntime.jsxs(
8297
8558
  "select",
8298
8559
  {
@@ -8306,7 +8567,7 @@ function FieldInput({
8306
8567
  ]
8307
8568
  }
8308
8569
  );
8309
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Radio:
8570
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Radio:
8310
8571
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
8311
8572
  "label",
8312
8573
  {
@@ -8331,7 +8592,7 @@ function FieldInput({
8331
8592
  },
8332
8593
  opt
8333
8594
  )) });
8334
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Checkbox:
8595
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Checkbox:
8335
8596
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
8336
8597
  /* @__PURE__ */ jsxRuntime.jsx(
8337
8598
  "input",
@@ -8344,7 +8605,7 @@ function FieldInput({
8344
8605
  ),
8345
8606
  field.placeholder || field.name
8346
8607
  ] });
8347
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Color:
8608
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Color:
8348
8609
  return /* @__PURE__ */ jsxRuntime.jsx(
8349
8610
  "input",
8350
8611
  {
@@ -8354,7 +8615,7 @@ function FieldInput({
8354
8615
  className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
8355
8616
  }
8356
8617
  );
8357
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Date:
8618
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Date:
8358
8619
  return /* @__PURE__ */ jsxRuntime.jsx(
8359
8620
  DatePicker,
8360
8621
  {
@@ -8365,8 +8626,8 @@ function FieldInput({
8365
8626
  "aria-label": field.name
8366
8627
  }
8367
8628
  );
8368
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.File:
8369
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Image:
8629
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.File:
8630
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image:
8370
8631
  return /* @__PURE__ */ jsxRuntime.jsx(
8371
8632
  FileUploadInput,
8372
8633
  {
@@ -8377,7 +8638,7 @@ function FieldInput({
8377
8638
  classNames
8378
8639
  }
8379
8640
  );
8380
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Email:
8641
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Email:
8381
8642
  return /* @__PURE__ */ jsxRuntime.jsx(
8382
8643
  "input",
8383
8644
  {
@@ -8389,7 +8650,7 @@ function FieldInput({
8389
8650
  className: inputClass
8390
8651
  }
8391
8652
  );
8392
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.DateTime: {
8653
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.DateTime: {
8393
8654
  const stringValue = typeof value === "string" ? value : "";
8394
8655
  const [datePart, timePartRaw] = stringValue.includes("T") ? stringValue.split("T", 2) : [stringValue, ""];
8395
8656
  const timePart = (timePartRaw ?? "").slice(0, 5);
@@ -8428,7 +8689,7 @@ function FieldInput({
8428
8689
  )
8429
8690
  ] });
8430
8691
  }
8431
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Time:
8692
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Time:
8432
8693
  return /* @__PURE__ */ jsxRuntime.jsx(
8433
8694
  TimePicker,
8434
8695
  {
@@ -8439,7 +8700,7 @@ function FieldInput({
8439
8700
  "aria-label": field.name
8440
8701
  }
8441
8702
  );
8442
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.MultiSelect:
8703
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.MultiSelect:
8443
8704
  return /* @__PURE__ */ jsxRuntime.jsx(
8444
8705
  MultiSelectInput,
8445
8706
  {
@@ -8449,7 +8710,7 @@ function FieldInput({
8449
8710
  classNames
8450
8711
  }
8451
8712
  );
8452
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.DateRange:
8713
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.DateRange:
8453
8714
  return /* @__PURE__ */ jsxRuntime.jsx(
8454
8715
  DateRangeInput,
8455
8716
  {
@@ -8459,7 +8720,7 @@ function FieldInput({
8459
8720
  classNames
8460
8721
  }
8461
8722
  );
8462
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Address:
8723
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Address:
8463
8724
  return /* @__PURE__ */ jsxRuntime.jsx(
8464
8725
  AddressInput,
8465
8726
  {
@@ -8470,7 +8731,7 @@ function FieldInput({
8470
8731
  inputClass
8471
8732
  }
8472
8733
  );
8473
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Location:
8734
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Location:
8474
8735
  return /* @__PURE__ */ jsxRuntime.jsx(
8475
8736
  LocationInput,
8476
8737
  {
@@ -8481,7 +8742,7 @@ function FieldInput({
8481
8742
  inputClass
8482
8743
  }
8483
8744
  );
8484
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Phone:
8745
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Phone:
8485
8746
  return /* @__PURE__ */ jsxRuntime.jsx(
8486
8747
  PhoneInput,
8487
8748
  {
@@ -8492,7 +8753,7 @@ function FieldInput({
8492
8753
  inputClass
8493
8754
  }
8494
8755
  );
8495
- case chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Signature:
8756
+ case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Signature:
8496
8757
  return /* @__PURE__ */ jsxRuntime.jsx(
8497
8758
  SignatureInput,
8498
8759
  {
@@ -8525,7 +8786,7 @@ function FileUploadInput({
8525
8786
  const [isUploading, setIsUploading] = React14__default.default.useState(false);
8526
8787
  const fileUrl = typeof value === "string" ? value : void 0;
8527
8788
  const acceptedFormats = field.validation?.accepted_formats;
8528
- const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
8789
+ const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
8529
8790
  const handleFile = async (file) => {
8530
8791
  if (!onFileUpload) return;
8531
8792
  if (field.validation?.max_size_mb) {
@@ -8543,7 +8804,7 @@ function FileUploadInput({
8543
8804
  }
8544
8805
  };
8545
8806
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.fileInput), children: fileUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
8546
- field.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsxRuntime.jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
8807
+ field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsxRuntime.jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
8547
8808
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground truncate", children: fileUrl.split("/").pop() }) }),
8548
8809
  /* @__PURE__ */ jsxRuntime.jsx(
8549
8810
  "button",
@@ -8569,7 +8830,7 @@ function FileUploadInput({
8569
8830
  }
8570
8831
  ),
8571
8832
  isUploading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Uploading..." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8572
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
8833
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
8573
8834
  acceptedFormats && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
8574
8835
  acceptedFormats.map((f) => f.toUpperCase()).join(", "),
8575
8836
  field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
@@ -8670,129 +8931,18 @@ function AddressInput({
8670
8931
  classNames,
8671
8932
  inputClass
8672
8933
  }) {
8673
- const clientContext = useOptionalCimplifyClient();
8674
8934
  const addr = value && typeof value === "object" ? value : null;
8675
- const [inputText, setInputText] = React14__default.default.useState(addr?.formatted_address ?? "");
8676
- const [suggestions, setSuggestions] = React14__default.default.useState([]);
8677
- const [isOpen, setIsOpen] = React14__default.default.useState(false);
8678
- const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
8679
- const debounceRef = React14__default.default.useRef(null);
8680
- const seqRef = React14__default.default.useRef(0);
8681
- const sessionTokenRef = React14__default.default.useRef(createSessionToken());
8682
- const blurTimeoutRef = React14__default.default.useRef(null);
8683
- React14__default.default.useEffect(() => {
8684
- return () => {
8685
- if (debounceRef.current) clearTimeout(debounceRef.current);
8686
- if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8687
- seqRef.current += 1;
8688
- };
8689
- }, []);
8690
- const fetchSuggestions = React14__default.default.useCallback(async (query) => {
8691
- const client = clientContext?.client;
8692
- if (!client || query.length < 3) {
8693
- setSuggestions([]);
8694
- setIsOpen(false);
8695
- return;
8696
- }
8697
- const seq = ++seqRef.current;
8698
- const result = await client.places.autocomplete(query, sessionTokenRef.current);
8699
- if (seqRef.current !== seq) return;
8700
- if (result.ok && result.value.predictions.length > 0) {
8701
- setSuggestions(result.value.predictions);
8702
- setHighlightedIndex(0);
8703
- setIsOpen(true);
8704
- } else {
8705
- setSuggestions([]);
8706
- setIsOpen(false);
8707
- }
8708
- }, [clientContext]);
8709
- const handleInputChange = (nextValue) => {
8710
- setInputText(nextValue);
8711
- if (debounceRef.current) clearTimeout(debounceRef.current);
8712
- debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
8713
- };
8714
- const selectSuggestion = React14__default.default.useCallback(async (suggestion) => {
8715
- if (debounceRef.current) clearTimeout(debounceRef.current);
8716
- if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8717
- const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
8718
- setInputText(fallback);
8719
- setSuggestions([]);
8720
- setIsOpen(false);
8721
- const client = clientContext?.client;
8722
- if (!client) return;
8723
- const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
8724
- sessionTokenRef.current = createSessionToken();
8725
- if (result.ok) {
8726
- const d = result.value;
8727
- const addressValue = {
8728
- formatted_address: d.formatted_address || fallback,
8729
- street_address: d.street_address,
8730
- apartment: d.apartment,
8731
- city: d.city,
8732
- region: d.region,
8733
- postal_code: d.postal_code,
8734
- country: d.country,
8735
- latitude: d.latitude,
8736
- longitude: d.longitude,
8737
- place_id: d.place_id
8738
- };
8739
- setInputText(addressValue.formatted_address);
8740
- onValueChange(addressValue);
8741
- }
8742
- }, [clientContext, onValueChange]);
8743
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2 relative", classNames?.addressInput), children: [
8935
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2", classNames?.addressInput), children: [
8744
8936
  /* @__PURE__ */ jsxRuntime.jsx(
8745
- "input",
8937
+ AddressAutocomplete,
8746
8938
  {
8747
- type: "text",
8748
- value: inputText,
8749
- onChange: (e) => handleInputChange(e.target.value),
8750
- onFocus: () => {
8751
- if (suggestions.length > 0) setIsOpen(true);
8752
- },
8753
- onBlur: () => {
8754
- blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
8755
- },
8756
- onKeyDown: (e) => {
8757
- if (e.key === "Escape") {
8758
- setIsOpen(false);
8759
- return;
8760
- }
8761
- if (!isOpen || suggestions.length === 0) return;
8762
- if (e.key === "ArrowDown") {
8763
- e.preventDefault();
8764
- setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
8765
- } else if (e.key === "ArrowUp") {
8766
- e.preventDefault();
8767
- setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
8768
- } else if (e.key === "Enter" && highlightedIndex >= 0) {
8769
- e.preventDefault();
8770
- void selectSuggestion(suggestions[highlightedIndex]);
8771
- }
8772
- },
8939
+ value: addr,
8940
+ onSelect: onValueChange,
8773
8941
  placeholder: field.placeholder || "Search for an address...",
8774
8942
  required: field.is_required,
8775
- autoComplete: "off",
8776
- "aria-autocomplete": "list",
8777
- "aria-expanded": isOpen && suggestions.length > 0,
8778
- className: inputClass
8943
+ classNames: { input: inputClass }
8779
8944
  }
8780
8945
  ),
8781
- isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
8782
- "button",
8783
- {
8784
- type: "button",
8785
- role: "option",
8786
- "aria-selected": i === highlightedIndex,
8787
- onMouseDown: (e) => {
8788
- e.preventDefault();
8789
- void selectSuggestion(s);
8790
- },
8791
- className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
8792
- children: s.description
8793
- },
8794
- s.place_id
8795
- )) }),
8796
8946
  addr && /* @__PURE__ */ jsxRuntime.jsx(
8797
8947
  "input",
8798
8948
  {
@@ -8812,121 +8962,22 @@ function LocationInput({
8812
8962
  classNames,
8813
8963
  inputClass
8814
8964
  }) {
8815
- const clientContext = useOptionalCimplifyClient();
8816
8965
  const loc = value && typeof value === "object" ? value : null;
8817
- const [inputText, setInputText] = React14__default.default.useState(loc?.label ?? "");
8818
- const [suggestions, setSuggestions] = React14__default.default.useState([]);
8819
- const [isOpen, setIsOpen] = React14__default.default.useState(false);
8820
- const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
8821
- const debounceRef = React14__default.default.useRef(null);
8822
- const seqRef = React14__default.default.useRef(0);
8823
- const sessionTokenRef = React14__default.default.useRef(createSessionToken());
8824
- const blurTimeoutRef = React14__default.default.useRef(null);
8825
- React14__default.default.useEffect(() => {
8826
- return () => {
8827
- if (debounceRef.current) clearTimeout(debounceRef.current);
8828
- if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8829
- seqRef.current += 1;
8830
- };
8831
- }, []);
8832
- const fetchSuggestions = React14__default.default.useCallback(async (query) => {
8833
- const client = clientContext?.client;
8834
- if (!client || query.length < 3) {
8835
- setSuggestions([]);
8836
- setIsOpen(false);
8837
- return;
8838
- }
8839
- const seq = ++seqRef.current;
8840
- const result = await client.places.autocomplete(query, sessionTokenRef.current);
8841
- if (seqRef.current !== seq) return;
8842
- if (result.ok && result.value.predictions.length > 0) {
8843
- setSuggestions(result.value.predictions);
8844
- setHighlightedIndex(0);
8845
- setIsOpen(true);
8846
- } else {
8847
- setSuggestions([]);
8848
- setIsOpen(false);
8849
- }
8850
- }, [clientContext]);
8851
- const handleInputChange = (nextValue) => {
8852
- setInputText(nextValue);
8853
- if (debounceRef.current) clearTimeout(debounceRef.current);
8854
- debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
8855
- };
8856
- const selectSuggestion = React14__default.default.useCallback(async (suggestion) => {
8857
- if (debounceRef.current) clearTimeout(debounceRef.current);
8858
- if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
8859
- const label = suggestion.description.split(",")[0]?.trim() || suggestion.description;
8860
- setInputText(label);
8861
- setSuggestions([]);
8862
- setIsOpen(false);
8863
- const client = clientContext?.client;
8864
- if (!client) return;
8865
- const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
8866
- sessionTokenRef.current = createSessionToken();
8867
- if (result.ok) {
8868
- const d = result.value;
8869
- onValueChange({
8870
- latitude: d.latitude,
8871
- longitude: d.longitude,
8872
- label: d.formatted_address || label
8873
- });
8874
- setInputText(d.formatted_address || label);
8875
- }
8876
- }, [clientContext, onValueChange]);
8877
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", classNames?.locationInput), children: [
8966
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(classNames?.locationInput), children: [
8878
8967
  /* @__PURE__ */ jsxRuntime.jsx(
8879
- "input",
8968
+ AddressAutocomplete,
8880
8969
  {
8881
- type: "text",
8882
- value: inputText,
8883
- onChange: (e) => handleInputChange(e.target.value),
8884
- onFocus: () => {
8885
- if (suggestions.length > 0) setIsOpen(true);
8886
- },
8887
- onBlur: () => {
8888
- blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
8889
- },
8890
- onKeyDown: (e) => {
8891
- if (e.key === "Escape") {
8892
- setIsOpen(false);
8893
- return;
8894
- }
8895
- if (!isOpen || suggestions.length === 0) return;
8896
- if (e.key === "ArrowDown") {
8897
- e.preventDefault();
8898
- setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
8899
- } else if (e.key === "ArrowUp") {
8900
- e.preventDefault();
8901
- setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
8902
- } else if (e.key === "Enter" && highlightedIndex >= 0) {
8903
- e.preventDefault();
8904
- void selectSuggestion(suggestions[highlightedIndex]);
8905
- }
8906
- },
8970
+ value: loc ? { formatted_address: loc.label ?? "", latitude: loc.latitude, longitude: loc.longitude } : null,
8971
+ onSelect: (address) => onValueChange({
8972
+ latitude: address.latitude,
8973
+ longitude: address.longitude,
8974
+ label: address.formatted_address
8975
+ }),
8907
8976
  placeholder: field.placeholder || "Search for a location...",
8908
8977
  required: field.is_required,
8909
- autoComplete: "off",
8910
- "aria-autocomplete": "list",
8911
- "aria-expanded": isOpen && suggestions.length > 0,
8912
- className: inputClass
8978
+ classNames: { input: inputClass }
8913
8979
  }
8914
8980
  ),
8915
- isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
8916
- "button",
8917
- {
8918
- type: "button",
8919
- role: "option",
8920
- "aria-selected": i === highlightedIndex,
8921
- onMouseDown: (e) => {
8922
- e.preventDefault();
8923
- void selectSuggestion(s);
8924
- },
8925
- className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
8926
- children: s.description
8927
- },
8928
- s.place_id
8929
- )) }),
8930
8981
  loc && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
8931
8982
  loc.latitude.toFixed(4),
8932
8983
  ", ",
@@ -9057,12 +9108,6 @@ function SignatureInput({
9057
9108
  !sig?.data_url && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: field.placeholder || "Draw your signature above" })
9058
9109
  ] });
9059
9110
  }
9060
- function createSessionToken() {
9061
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
9062
- return crypto.randomUUID();
9063
- }
9064
- return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
9065
- }
9066
9111
  function getTimeOfDay(timeStr) {
9067
9112
  const hour = parseInt(timeStr.split("T").pop()?.split(":")[0] ?? timeStr.split(":")[0], 10);
9068
9113
  if (hour < 12) return "morning";
@@ -9308,6 +9353,7 @@ function DateSlotPicker({
9308
9353
  serviceId,
9309
9354
  daysToShow = 7,
9310
9355
  participantCount,
9356
+ units,
9311
9357
  selectedSlot,
9312
9358
  onSlotSelect,
9313
9359
  availability: availabilityProp,
@@ -9342,6 +9388,7 @@ function DateSlotPicker({
9342
9388
  dateRange.endDate,
9343
9389
  {
9344
9390
  participantCount,
9391
+ units,
9345
9392
  enabled: availabilityProp === void 0
9346
9393
  }
9347
9394
  );
@@ -9499,6 +9546,7 @@ function ProductCustomizer({
9499
9546
  const [specialInstructions, setSpecialInstructions] = React14.useState("");
9500
9547
  const [selectedSlot, setSelectedSlot] = React14.useState(null);
9501
9548
  const [selectedStaffId, setSelectedStaffId] = React14.useState(null);
9549
+ const [unitsCount, setUnitsCount] = React14.useState(1);
9502
9550
  const cart = useCart();
9503
9551
  const currency = chunkPTPOQVXS_js.getProductCurrency(product);
9504
9552
  const productType = product.type || "product";
@@ -9506,6 +9554,7 @@ function ProductCustomizer({
9506
9554
  const isBundle = productType === "bundle";
9507
9555
  const isDigital = productType === "digital";
9508
9556
  const isService = productType === "service";
9557
+ const offersUnits = isService && chunkHKN3YG76_js.isMultiDayService(product);
9509
9558
  const isStandard = !isComposite && !isBundle;
9510
9559
  const hasVariants = isStandard && product.variants && product.variants.length > 0;
9511
9560
  const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
@@ -9527,6 +9576,7 @@ function ProductCustomizer({
9527
9576
  setSpecialInstructions("");
9528
9577
  setSelectedSlot(null);
9529
9578
  setSelectedStaffId(null);
9579
+ setUnitsCount(1);
9530
9580
  }, [product.id, product.min_order_quantity]);
9531
9581
  const slotStaff = React14.useMemo(
9532
9582
  () => (selectedSlot?.available_staff ?? []).map((member) => ({
@@ -9616,7 +9666,17 @@ function ProductCustomizer({
9616
9666
  if (perUnit === void 0 || perUnit === null) return void 0;
9617
9667
  return chunkPTPOQVXS_js.parsePrice(perUnit) * quantity;
9618
9668
  }, [quote, quantity]);
9619
- const displayTotalPrice = quotedTotalPrice ?? localTotalPrice;
9669
+ const displayTotalPrice = React14.useMemo(() => {
9670
+ const total = quotedTotalPrice ?? localTotalPrice;
9671
+ const multiplier = chunkHKN3YG76_js.serviceBillingMultiplier(product, {
9672
+ quantity,
9673
+ units: unitsCount,
9674
+ scheduledStart: selectedSlot?.start_time,
9675
+ scheduledEnd: selectedSlot?.end_time
9676
+ });
9677
+ if (multiplier === null) return total;
9678
+ return total / Math.max(1, quantity) * multiplier;
9679
+ }, [quotedTotalPrice, localTotalPrice, product, quantity, unitsCount, selectedSlot]);
9620
9680
  const handleVariantChange = React14.useCallback(
9621
9681
  (variantId, variant) => {
9622
9682
  setSelectedVariantId(variantId);
@@ -9648,7 +9708,8 @@ function ProductCustomizer({
9648
9708
  scheduledStart: selectedSlot.start_time,
9649
9709
  scheduledEnd: selectedSlot.end_time,
9650
9710
  staffId: selectedStaffId ?? selectedSlot.available_staff?.[0]?.staff_id,
9651
- resourceId: selectedSlot.available_resources?.[0]?.resource_id
9711
+ resourceId: selectedSlot.available_resources?.[0]?.resource_id,
9712
+ units: unitsCount > 1 ? unitsCount : void 0
9652
9713
  } : {},
9653
9714
  customerInputs: Object.keys(customerInputValues).length > 0 ? Object.entries(customerInputValues).filter(([, v]) => v !== void 0 && v !== "").map(([fieldId, value]) => ({ field_id: fieldId, value })) : void 0,
9654
9715
  specialInstructions: specialInstructions.trim() || void 0
@@ -9669,7 +9730,7 @@ function ProductCustomizer({
9669
9730
  } finally {
9670
9731
  setIsSubmitting(false);
9671
9732
  }
9672
- }, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, isSubmitting, onAddToCart, cart]);
9733
+ }, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, unitsCount, isSubmitting, onAddToCart, cart]);
9673
9734
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
9674
9735
  isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsxRuntime.jsx(
9675
9736
  CompositeSelector,
@@ -9706,9 +9767,17 @@ function ProductCustomizer({
9706
9767
  setSelectedStaffId(null);
9707
9768
  },
9708
9769
  participantCount: quantity,
9709
- currency
9770
+ units: offersUnits ? unitsCount : void 0,
9771
+ currency,
9772
+ schedulingMode: product.scheduling_mode ?? (offersUnits ? "multi_day" : "intraday"),
9773
+ durationUnit: product.duration_unit,
9774
+ durationValue: product.duration_value
9710
9775
  }
9711
9776
  ),
9777
+ offersUnits && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-units": true, className: classNames?.units, children: [
9778
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Units" }),
9779
+ /* @__PURE__ */ jsxRuntime.jsx(QuantitySelector, { value: unitsCount, onChange: setUnitsCount, min: 1 })
9780
+ ] }),
9712
9781
  isService && slotStaff.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-staff": true, children: [
9713
9782
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Choose your specialist" }),
9714
9783
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -10313,7 +10382,7 @@ function CartLineItemRow({
10313
10382
  const isOptimistic = item.isOptimistic === true;
10314
10383
  const lineTotal = chunkPTPOQVXS_js.parsePrice(unitPrice) * item.quantity;
10315
10384
  const imageUrl = item.product.image_url;
10316
- const variantLabel2 = item.variant ? chunkT7Z25EY4_js.getVariantDisplayName(item.variant, item.product.name) : null;
10385
+ const variantLabel2 = item.variant ? chunkHKN3YG76_js.getVariantDisplayName(item.variant, item.product.name) : null;
10317
10386
  return /* @__PURE__ */ jsxRuntime.jsxs(
10318
10387
  "div",
10319
10388
  {
@@ -10413,7 +10482,7 @@ function CartLineItemRow({
10413
10482
  input.field_name,
10414
10483
  ":"
10415
10484
  ] }),
10416
- /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cimplify-cart-input-value": true, className: "text-foreground m-0 truncate", children: input.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.File || input.field_type === chunkT7Z25EY4_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
10485
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cimplify-cart-input-value": true, className: "text-foreground m-0 truncate", children: input.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.File || input.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
10417
10486
  ] }, input.field_id)) }),
10418
10487
  (item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-info": true, className: cn("mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground", classNames?.serviceInfo), children: [
10419
10488
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3.5 h-3.5 shrink-0", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
@@ -11298,7 +11367,7 @@ function FoodProductCard({
11298
11367
  const tags = product.tags || [];
11299
11368
  const isSignature = product.metadata?.is_signature === true;
11300
11369
  const isNew = product.metadata?.is_new === true;
11301
- const isComposite = product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Composite;
11370
+ const isComposite = product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite;
11302
11371
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
11303
11372
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "square", renderImage, children: [
11304
11373
  isSignature && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-3 left-3 inline-flex items-center gap-1 text-[11px] font-semibold tracking-wide bg-amber-50 text-amber-700 border border-amber-200/60 px-2 py-0.5 rounded-md", children: [
@@ -11762,18 +11831,18 @@ function RentalServiceCard({
11762
11831
  const currency = chunkPTPOQVXS_js.getProductCurrency(product);
11763
11832
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
11764
11833
  const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
11834
+ const priceUnit = product.price_basis === "per_person" ? "guest" : product.price_basis === "flat" ? "" : unit;
11765
11835
  const status = product.inventory_status;
11766
11836
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
11767
11837
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
11768
11838
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
11769
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 left-3 inline-flex items-center text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children: [
11839
+ priceUnit && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 left-3 inline-flex items-center text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children: [
11770
11840
  "Per ",
11771
- unit
11841
+ priceUnit
11772
11842
  ] }),
11773
11843
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 right-3 text-white font-bold text-lg drop-shadow-sm", children: [
11774
11844
  /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: product.default_price, currency }),
11775
- "/",
11776
- unit
11845
+ priceUnit && `/${priceUnit}`
11777
11846
  ] }),
11778
11847
  product.general_service_capacity != null && product.general_service_capacity > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-3 left-3 text-[11px] font-semibold tracking-wide bg-blue-50/90 text-blue-700 border border-blue-200/50 backdrop-blur px-2 py-0.5 rounded-md", children: [
11779
11848
  "Up to ",
@@ -12065,36 +12134,36 @@ var BUILT_IN_CARDS = {
12065
12134
  ["composite" /* Composite */]: CompositeProductCard,
12066
12135
  ["subscription" /* Subscription */]: SubscriptionCard
12067
12136
  };
12068
- var RENTAL_UNITS = /* @__PURE__ */ new Set([chunkT7Z25EY4_js.DURATION_UNIT.Days, chunkT7Z25EY4_js.DURATION_UNIT.Hours]);
12069
- var LEASE_UNITS = /* @__PURE__ */ new Set([chunkT7Z25EY4_js.DURATION_UNIT.Weeks, chunkT7Z25EY4_js.DURATION_UNIT.Months, chunkT7Z25EY4_js.DURATION_UNIT.Years]);
12137
+ var RENTAL_UNITS = /* @__PURE__ */ new Set([chunkHKN3YG76_js.DURATION_UNIT.Days, chunkHKN3YG76_js.DURATION_UNIT.Hours]);
12138
+ var LEASE_UNITS = /* @__PURE__ */ new Set([chunkHKN3YG76_js.DURATION_UNIT.Weeks, chunkHKN3YG76_js.DURATION_UNIT.Months, chunkHKN3YG76_js.DURATION_UNIT.Years]);
12070
12139
  function resolveCardVariant(product) {
12071
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Bundle) {
12140
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Bundle) {
12072
12141
  return "bundle" /* Bundle */;
12073
12142
  }
12074
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Composite) {
12143
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite) {
12075
12144
  return "composite" /* Composite */;
12076
12145
  }
12077
12146
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
12078
12147
  return "wholesale" /* Wholesale */;
12079
12148
  }
12080
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Digital) {
12149
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Digital) {
12081
12150
  return "digital" /* Digital */;
12082
12151
  }
12083
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Service) {
12152
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Service) {
12084
12153
  switch (product.boundary_kind) {
12085
- case chunkT7Z25EY4_js.BOUNDARY_KIND.CheckInOut:
12154
+ case chunkHKN3YG76_js.BOUNDARY_KIND.CheckInOut:
12086
12155
  return "accommodation" /* Accommodation */;
12087
- case chunkT7Z25EY4_js.BOUNDARY_KIND.MoveInOut:
12156
+ case chunkHKN3YG76_js.BOUNDARY_KIND.MoveInOut:
12088
12157
  return "lease" /* Lease */;
12089
- case chunkT7Z25EY4_js.BOUNDARY_KIND.PickupReturn:
12090
- case chunkT7Z25EY4_js.BOUNDARY_KIND.DeliveryCollection:
12091
- case chunkT7Z25EY4_js.BOUNDARY_KIND.AccessVacate:
12158
+ case chunkHKN3YG76_js.BOUNDARY_KIND.PickupReturn:
12159
+ case chunkHKN3YG76_js.BOUNDARY_KIND.DeliveryCollection:
12160
+ case chunkHKN3YG76_js.BOUNDARY_KIND.AccessVacate:
12092
12161
  return "rental" /* Rental */;
12093
12162
  }
12094
12163
  if (product.duration_unit && RENTAL_UNITS.has(product.duration_unit)) {
12095
12164
  return "rental" /* Rental */;
12096
12165
  }
12097
- if (product.duration_unit === chunkT7Z25EY4_js.DURATION_UNIT.Nights) {
12166
+ if (product.duration_unit === chunkHKN3YG76_js.DURATION_UNIT.Nights) {
12098
12167
  return "accommodation" /* Accommodation */;
12099
12168
  }
12100
12169
  if (product.duration_unit && LEASE_UNITS.has(product.duration_unit)) {
@@ -12105,10 +12174,10 @@ function resolveCardVariant(product) {
12105
12174
  }
12106
12175
  return "standard" /* Standard */;
12107
12176
  }
12108
- if (product.render_hint === chunkT7Z25EY4_js.RENDER_HINT.Food) {
12177
+ if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Food) {
12109
12178
  return "food" /* Food */;
12110
12179
  }
12111
- if (product.render_hint === chunkT7Z25EY4_js.RENDER_HINT.Physical) {
12180
+ if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Physical) {
12112
12181
  return "retail" /* Retail */;
12113
12182
  }
12114
12183
  return "retail" /* Retail */;
@@ -12367,6 +12436,15 @@ function ProductGrid({
12367
12436
  ] });
12368
12437
  }
12369
12438
 
12439
+ // src/react/utils/attachment.ts
12440
+ var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
12441
+ function isImageAttachment(attachment) {
12442
+ if (attachment.mime_type?.startsWith("image/")) return true;
12443
+ if (attachment.mime_type) return false;
12444
+ const path = attachment.url.split("?")[0].split("#")[0].toLowerCase();
12445
+ return IMAGE_EXTENSIONS.some((extension) => path.endsWith(extension));
12446
+ }
12447
+
12370
12448
  // src/react/utils/boundary-labels.ts
12371
12449
  var PRESET_LABELS = {
12372
12450
  check_in_out: { start: "Check-in", end: "Check-out" },
@@ -12376,14 +12454,14 @@ var PRESET_LABELS = {
12376
12454
  move_in_out: { start: "Move-in", end: "Move-out" }
12377
12455
  };
12378
12456
  function defaultBoundaryKind(unit) {
12379
- return unit === chunkT7Z25EY4_js.DURATION_UNIT.Nights ? chunkT7Z25EY4_js.BOUNDARY_KIND.CheckInOut : chunkT7Z25EY4_js.BOUNDARY_KIND.PickupReturn;
12457
+ return unit === chunkHKN3YG76_js.DURATION_UNIT.Nights ? chunkHKN3YG76_js.BOUNDARY_KIND.CheckInOut : chunkHKN3YG76_js.BOUNDARY_KIND.PickupReturn;
12380
12458
  }
12381
12459
  function resolveBoundaryKind(product) {
12382
12460
  return product.boundary_kind ?? defaultBoundaryKind(product.duration_unit);
12383
12461
  }
12384
12462
  function getBoundaryLabels(product) {
12385
12463
  const kind = resolveBoundaryKind(product);
12386
- if (kind === chunkT7Z25EY4_js.BOUNDARY_KIND.Custom) {
12464
+ if (kind === chunkHKN3YG76_js.BOUNDARY_KIND.Custom) {
12387
12465
  return {
12388
12466
  start: product.boundary_start_label?.trim() || "Start",
12389
12467
  end: product.boundary_end_label?.trim() || "End"
@@ -13548,6 +13626,7 @@ function BookingPage({
13548
13626
  const [selectedDate, setSelectedDate] = React14.useState(null);
13549
13627
  const [selectedStaffId, setSelectedStaffId] = React14.useState(null);
13550
13628
  const [selectedResourceId, setSelectedResourceId] = React14.useState(null);
13629
+ const [bookingUnits, setBookingUnits] = React14.useState(1);
13551
13630
  const [isSubmitting, setIsSubmitting] = React14.useState(false);
13552
13631
  const [error, setError] = React14.useState(null);
13553
13632
  const hasResourceStep = resources && resources.length > 0;
@@ -13641,7 +13720,8 @@ function BookingPage({
13641
13720
  scheduledStart: selectedSlot.start_time,
13642
13721
  scheduledEnd: selectedSlot.end_time,
13643
13722
  staffId: selectedStaffId || void 0,
13644
- resourceId: selectedResourceId || void 0
13723
+ resourceId: selectedResourceId || void 0,
13724
+ units: bookingUnits > 1 ? bookingUnits : void 0
13645
13725
  });
13646
13726
  onBooked?.(selectedSlot, selectedStaffId);
13647
13727
  }
@@ -13737,6 +13817,31 @@ function BookingPage({
13737
13817
  ] }),
13738
13818
  step === STEP.CONFIRM && selectedSlot && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-step": "confirm", className: classNames?.step, children: [
13739
13819
  /* @__PURE__ */ jsxRuntime.jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Confirm your booking" }),
13820
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-units": true, className: classNames?.summaryRow, children: [
13821
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Units" }),
13822
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-units-stepper": true, children: [
13823
+ /* @__PURE__ */ jsxRuntime.jsx(
13824
+ "button",
13825
+ {
13826
+ type: "button",
13827
+ "data-cimplify-booking-units-decrement": true,
13828
+ onClick: () => setBookingUnits((current) => Math.max(1, current - 1)),
13829
+ disabled: bookingUnits <= 1,
13830
+ children: "\u2212"
13831
+ }
13832
+ ),
13833
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-booking-units-value": true, children: bookingUnits }),
13834
+ /* @__PURE__ */ jsxRuntime.jsx(
13835
+ "button",
13836
+ {
13837
+ type: "button",
13838
+ "data-cimplify-booking-units-increment": true,
13839
+ onClick: () => setBookingUnits((current) => current + 1),
13840
+ children: "+"
13841
+ }
13842
+ )
13843
+ ] })
13844
+ ] }),
13740
13845
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-summary": true, className: classNames?.summary, children: [
13741
13846
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
13742
13847
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Service" }),
@@ -14444,7 +14549,7 @@ function DefaultProductLayout({
14444
14549
  className
14445
14550
  }) {
14446
14551
  const [selectedVariantId, setSelectedVariantId] = React14__default.default.useState();
14447
- const images = chunkT7Z25EY4_js.resolveGallery(product, selectedVariantId);
14552
+ const images = chunkHKN3YG76_js.resolveGallery(product, selectedVariantId);
14448
14553
  const currency = chunkPTPOQVXS_js.getProductCurrency(product);
14449
14554
  const hasRange = product.quantity_pricing && product.quantity_pricing.length > 1 || product.variants && product.variants.length > 1;
14450
14555
  const onSale = chunkPTPOQVXS_js.isOnSale(product);
@@ -15091,25 +15196,25 @@ function resolveTemplateKey(product) {
15091
15196
  if (typeof metaTemplate === "string" && metaTemplate.trim()) {
15092
15197
  return metaTemplate.trim();
15093
15198
  }
15094
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Bundle) {
15199
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Bundle) {
15095
15200
  return "bundle" /* Bundle */;
15096
15201
  }
15097
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Composite) {
15202
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite) {
15098
15203
  return "composite" /* Composite */;
15099
15204
  }
15100
15205
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
15101
15206
  return "wholesale" /* Wholesale */;
15102
15207
  }
15103
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Service) {
15208
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Service) {
15104
15209
  return "service" /* Service */;
15105
15210
  }
15106
- if (product.type === chunkT7Z25EY4_js.PRODUCT_TYPE.Digital) {
15211
+ if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Digital) {
15107
15212
  return "digital" /* Digital */;
15108
15213
  }
15109
- if (product.render_hint === chunkT7Z25EY4_js.RENDER_HINT.Food) {
15214
+ if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Food) {
15110
15215
  return "food" /* Food */;
15111
15216
  }
15112
- if (product.render_hint === chunkT7Z25EY4_js.RENDER_HINT.Physical) {
15217
+ if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Physical) {
15113
15218
  return "physical" /* Physical */;
15114
15219
  }
15115
15220
  return "default" /* Default */;
@@ -16995,7 +17100,7 @@ function ChatWidget({
16995
17100
  const context = useOptionalCimplifyClient();
16996
17101
  const client = clientProp ?? context?.client;
16997
17102
  const [open, setOpen] = React14.useState(defaultOpen);
16998
- const [unread, setUnread] = React14.useState(0);
17103
+ const [readCount, setReadCount] = React14.useState(0);
16999
17104
  const {
17000
17105
  messages,
17001
17106
  isLoading,
@@ -17004,6 +17109,8 @@ function ChatWidget({
17004
17109
  seenAt,
17005
17110
  error,
17006
17111
  isActive,
17112
+ claimedAs,
17113
+ signOut,
17007
17114
  send,
17008
17115
  react,
17009
17116
  startConversation
@@ -17025,22 +17132,14 @@ function ChatWidget({
17025
17132
  },
17026
17133
  [client, send]
17027
17134
  );
17028
- const prevCountRef = React14.useRef(messages.length);
17029
- React14.useEffect(() => {
17030
- if (!open && messages.length > prevCountRef.current) {
17031
- const newMessages = messages.slice(prevCountRef.current);
17032
- const incomingCount = newMessages.filter(
17033
- (m) => m.sender_type !== "customer"
17034
- ).length;
17035
- if (incomingCount > 0) setUnread((n) => n + incomingCount);
17036
- }
17037
- prevCountRef.current = messages.length;
17038
- }, [messages, open]);
17135
+ const unread = open ? 0 : messages.slice(readCount).filter((m) => m.sender_type !== "customer").length;
17039
17136
  const handleOpen = React14.useCallback(() => {
17040
17137
  setOpen(true);
17041
- setUnread(0);
17042
17138
  }, []);
17043
- const handleClose = React14.useCallback(() => setOpen(false), []);
17139
+ const handleClose = React14.useCallback(() => {
17140
+ setReadCount(messages.length);
17141
+ setOpen(false);
17142
+ }, [messages.length]);
17044
17143
  const isLeft = position === "bottom-left";
17045
17144
  return /* @__PURE__ */ jsxRuntime.jsxs(
17046
17145
  "div",
@@ -17076,6 +17175,8 @@ function ChatWidget({
17076
17175
  error,
17077
17176
  isActive,
17078
17177
  isLeft,
17178
+ claimedAs,
17179
+ onSignOut: () => void signOut(),
17079
17180
  onClose: handleClose,
17080
17181
  onSend: send,
17081
17182
  onReact: react,
@@ -17137,6 +17238,8 @@ function Panel({
17137
17238
  error,
17138
17239
  isActive,
17139
17240
  isLeft,
17241
+ claimedAs,
17242
+ onSignOut,
17140
17243
  onClose,
17141
17244
  onSend,
17142
17245
  onReact,
@@ -17160,6 +17263,8 @@ function Panel({
17160
17263
  Header,
17161
17264
  {
17162
17265
  businessName,
17266
+ claimedAs,
17267
+ onSignOut,
17163
17268
  onClose,
17164
17269
  className: classNames?.header
17165
17270
  }
@@ -17206,6 +17311,8 @@ function Panel({
17206
17311
  }
17207
17312
  function Header({
17208
17313
  businessName,
17314
+ claimedAs,
17315
+ onSignOut,
17209
17316
  onClose,
17210
17317
  className
17211
17318
  }) {
@@ -17220,7 +17327,22 @@ function Header({
17220
17327
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-white/15", children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-5 w-5" }) }),
17221
17328
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
17222
17329
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
17223
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
17330
+ claimedAs ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-white/60", children: [
17331
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
17332
+ "Chatting as ",
17333
+ claimedAs
17334
+ ] }),
17335
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }),
17336
+ /* @__PURE__ */ jsxRuntime.jsx(
17337
+ "button",
17338
+ {
17339
+ type: "button",
17340
+ onClick: onSignOut,
17341
+ className: "flex-shrink-0 underline underline-offset-2 hover:text-white",
17342
+ children: "Not you?"
17343
+ }
17344
+ )
17345
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
17224
17346
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
17225
17347
  "Online"
17226
17348
  ] })
@@ -17304,10 +17426,6 @@ function MessageList({
17304
17426
  onReact,
17305
17427
  className
17306
17428
  }) {
17307
- const bottomRef = React14.useRef(null);
17308
- React14.useEffect(() => {
17309
- bottomRef.current?.scrollIntoView({ behavior: "smooth" });
17310
- }, [messages.length, isSending]);
17311
17429
  const { bubbles, reactionsByTarget } = partitionMessages(messages);
17312
17430
  const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
17313
17431
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -17427,7 +17545,15 @@ function MessageList({
17427
17545
  ] }, msg.id);
17428
17546
  }),
17429
17547
  isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
17430
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
17548
+ /* @__PURE__ */ jsxRuntime.jsx(
17549
+ "div",
17550
+ {
17551
+ ref: (node) => {
17552
+ node?.scrollIntoView({ behavior: "smooth" });
17553
+ }
17554
+ },
17555
+ `${messages.length}:${isSending}`
17556
+ )
17431
17557
  ]
17432
17558
  }
17433
17559
  );
@@ -19476,7 +19602,7 @@ async function genUIFingerprint(manifest) {
19476
19602
 
19477
19603
  Object.defineProperty(exports, "getVariantDisplayName", {
19478
19604
  enumerable: true,
19479
- get: function () { return chunkT7Z25EY4_js.getVariantDisplayName; }
19605
+ get: function () { return chunkHKN3YG76_js.getVariantDisplayName; }
19480
19606
  });
19481
19607
  Object.defineProperty(exports, "messagePreview", {
19482
19608
  enumerable: true,
@@ -19505,6 +19631,7 @@ exports.AccountSubscriptionsPage = AccountSubscriptionsPage;
19505
19631
  exports.Ad = Ad;
19506
19632
  exports.AdProvider = AdProvider;
19507
19633
  exports.AddOnSelector = AddOnSelector;
19634
+ exports.AddressAutocomplete = AddressAutocomplete;
19508
19635
  exports.AddressElement = AddressElement;
19509
19636
  exports.AuthElement = AuthElement;
19510
19637
  exports.AvailabilityBadge = AvailabilityBadge;
@@ -19668,12 +19795,14 @@ exports.WholesaleProductLayout = WholesaleProductLayout;
19668
19795
  exports.WishlistButton = WishlistButton;
19669
19796
  exports.clearOrdersCache = clearOrdersCache;
19670
19797
  exports.cn = cn;
19798
+ exports.createPlacesSessionToken = createPlacesSessionToken;
19671
19799
  exports.defaultBoundaryKind = defaultBoundaryKind;
19672
19800
  exports.genUICatalog = genUICatalog;
19673
19801
  exports.genUIFingerprint = genUIFingerprint;
19674
19802
  exports.genUIManifest = genUIManifest;
19675
19803
  exports.genUIManifestJson = genUIManifestJson;
19676
19804
  exports.getBoundaryLabels = getBoundaryLabels;
19805
+ exports.isImageAttachment = isImageAttachment;
19677
19806
  exports.isTerminalOrderStatus = isTerminalOrderStatus;
19678
19807
  exports.parseGenUIProgram = parse;
19679
19808
  exports.partitionMessages = partitionMessages;