@cimplify/sdk 0.90.0 → 0.93.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.
- package/dist/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +20 -20
- package/dist/advanced.mjs +1 -1
- package/dist/{chunk-KJP4U6L3.js → chunk-6Y7KC5YZ.js} +23 -0
- package/dist/{chunk-JVWFIFQ7.js → chunk-AWNWP5J3.js} +4 -4
- package/dist/{chunk-2UDEWILL.mjs → chunk-FKARGZK3.mjs} +307 -90
- package/dist/{chunk-T7Z25EY4.js → chunk-HKN3YG76.js} +84 -0
- package/dist/{chunk-BN4V4ONE.js → chunk-QLGZIBRO.js} +348 -128
- package/dist/{chunk-NGE37REW.mjs → chunk-QXWX7CD2.mjs} +2 -2
- package/dist/{chunk-OZDDHSIB.mjs → chunk-RRBY35TJ.mjs} +80 -1
- package/dist/{chunk-GHFDYM5K.js → chunk-VEOAMBEX.js} +11 -4
- package/dist/{chunk-ZU4ZRYUR.mjs → chunk-VUSHG3HA.mjs} +11 -4
- package/dist/{chunk-NNGKC3MM.mjs → chunk-X36FZZT6.mjs} +23 -0
- package/dist/{client-YiSelM7x.d.mts → client-BTL9d_nN.d.mts} +117 -11
- package/dist/{client-CR1f94wY.d.ts → client-BX30FAWZ.d.ts} +117 -11
- package/dist/{client-DMttx1xk.d.mts → client-Bhe2TnFW.d.mts} +2 -2
- package/dist/{client-CqpOcbin.d.ts → client-DrkuEK_I.d.ts} +2 -2
- package/dist/{index-DD9z396M.d.mts → index-BuNjyfaV.d.mts} +3 -3
- package/dist/{index-CxTVu0HZ.d.ts → index-D-w5h82t.d.ts} +3 -3
- package/dist/index.d.mts +49 -7
- package/dist/index.d.ts +49 -7
- package/dist/index.js +103 -83
- package/dist/index.mjs +5 -5
- package/dist/mock/cli.mjs +23 -0
- package/dist/mock/library.js +23 -0
- package/dist/mock/library.mjs +23 -0
- package/dist/mock/msw.js +23 -0
- package/dist/mock/msw.mjs +23 -0
- package/dist/{payment-BX7ccIKY.d.ts → payment-By0JidTe.d.ts} +1 -1
- package/dist/{payment-5d8FTA1q.d.mts → payment-pKPpaORI.d.mts} +1 -1
- package/dist/{price-DtfP_Xfq.d.ts → price-23RNXftz.d.ts} +1 -1
- package/dist/{price-arl_4Rzn.d.mts → price-BvRtp6ak.d.mts} +1 -1
- package/dist/{product-DZX4QuKS.d.ts → product-O8-wcHSe.d.mts} +49 -0
- package/dist/{product-DZX4QuKS.d.mts → product-O8-wcHSe.d.ts} +49 -0
- package/dist/react.d.mts +79 -6
- package/dist/react.d.ts +79 -6
- package/dist/react.js +607 -379
- package/dist/react.mjs +539 -315
- package/dist/{server-BceZMxcK.d.mts → server-BN9ZCAKT.d.mts} +5 -1
- package/dist/{server-DoJGSC7L.d.ts → server-DDGbfW_S.d.ts} +5 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +7 -7
- package/dist/server.mjs +2 -2
- package/dist/styles.css +1 -1
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +2 -2
- package/dist/testing/msw.mjs +1 -1
- package/dist/testing/suite.d.mts +6 -6
- package/dist/testing/suite.d.ts +6 -6
- package/dist/testing/suite.js +23 -23
- package/dist/testing/suite.mjs +4 -4
- package/dist/testing.d.mts +5 -5
- package/dist/testing.d.ts +5 -5
- package/dist/testing.js +79 -79
- package/dist/testing.mjs +5 -5
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/package.json +1 -1
- package/registry/booking-page.json +1 -1
- package/registry/chat-widget.json +1 -1
- package/registry/customer-input-fields.json +1 -1
- package/registry/date-slot-picker.json +1 -1
- package/registry/product-customizer.json +1 -1
- package/registry/rental-service-card.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-
|
|
3
|
-
export { getVariantDisplayName } from './chunk-
|
|
2
|
+
import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayService, serviceBillingMultiplier, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-RRBY35TJ.mjs';
|
|
3
|
+
export { getVariantDisplayName } from './chunk-RRBY35TJ.mjs';
|
|
4
4
|
import { messagePreview } from './chunk-U4MW6PYU.mjs';
|
|
5
5
|
export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
|
|
6
|
-
import { createCimplifyClient } from './chunk-
|
|
7
|
-
import { ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-
|
|
6
|
+
import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, MUTATION_ERROR } from './chunk-FKARGZK3.mjs';
|
|
7
|
+
import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-VUSHG3HA.mjs';
|
|
8
8
|
import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-KFLGQGMC.mjs';
|
|
9
|
-
import { isSupportedCurrency } from './chunk-K5EN2W2Q.mjs';
|
|
9
|
+
import { isSupportedCurrency, CimplifyError } from './chunk-K5EN2W2Q.mjs';
|
|
10
10
|
import './chunk-3G6RQLXK.mjs';
|
|
11
11
|
import React14, { createContext, lazy, memo, Suspense, useContext, useMemo, useRef, useState, useCallback, useEffect, useId, useSyncExternalStore } from 'react';
|
|
12
12
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
@@ -723,6 +723,7 @@ function mapServerCart(serverCart, fallbackCurrency) {
|
|
|
723
723
|
scheduledStart: item.scheduled_start || void 0,
|
|
724
724
|
scheduledEnd: item.scheduled_end || void 0,
|
|
725
725
|
staffId: item.staff_id || void 0,
|
|
726
|
+
units: item.units || void 0,
|
|
726
727
|
lineType: item.line_type,
|
|
727
728
|
confirmationCode: item.confirmation_code || void 0,
|
|
728
729
|
serviceStatus: item.service_status || void 0,
|
|
@@ -781,7 +782,7 @@ async function syncFromServer(entry) {
|
|
|
781
782
|
return next;
|
|
782
783
|
}
|
|
783
784
|
function emitMutationError(entry, operation, opId, idempotencyKey, error) {
|
|
784
|
-
entry.client.events.emit(
|
|
785
|
+
entry.client.events.emit(MUTATION_ERROR, {
|
|
785
786
|
service: "cart",
|
|
786
787
|
operation,
|
|
787
788
|
opId,
|
|
@@ -821,6 +822,12 @@ function getOrCreateStore(params) {
|
|
|
821
822
|
syncTail: Promise.resolve(),
|
|
822
823
|
failedOpRemovers: /* @__PURE__ */ new Set()
|
|
823
824
|
};
|
|
825
|
+
client.events.on(SESSION_CLAIMED, () => {
|
|
826
|
+
if (entry.initialized) void syncFromServer(entry);
|
|
827
|
+
});
|
|
828
|
+
client.events.on(SESSION_CLEARED, () => {
|
|
829
|
+
if (entry.initialized) void syncFromServer(entry);
|
|
830
|
+
});
|
|
824
831
|
cartStores.set(storeKey, entry);
|
|
825
832
|
return entry;
|
|
826
833
|
}
|
|
@@ -879,7 +886,8 @@ async function performAddItem(entry, product, quantity, options) {
|
|
|
879
886
|
specialInstructions: normalizedOptions.specialInstructions,
|
|
880
887
|
scheduledStart: normalizedOptions.scheduledStart,
|
|
881
888
|
scheduledEnd: normalizedOptions.scheduledEnd,
|
|
882
|
-
staffId: normalizedOptions.staffId
|
|
889
|
+
staffId: normalizedOptions.staffId,
|
|
890
|
+
units: normalizedOptions.units
|
|
883
891
|
});
|
|
884
892
|
const idempotencyKey = `idem_${typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`}`;
|
|
885
893
|
const patch = { kind: "add_item", tempItem, lineKey };
|
|
@@ -901,6 +909,7 @@ async function performAddItem(entry, product, quantity, options) {
|
|
|
901
909
|
scheduled_end: normalizedOptions.scheduledEnd,
|
|
902
910
|
staff_id: normalizedOptions.staffId,
|
|
903
911
|
resource_id: normalizedOptions.resourceId,
|
|
912
|
+
units: normalizedOptions.units,
|
|
904
913
|
customer_inputs: normalizedOptions.customerInputs
|
|
905
914
|
},
|
|
906
915
|
{ idempotencyKey }
|
|
@@ -5454,14 +5463,24 @@ function useServices(options = {}) {
|
|
|
5454
5463
|
}
|
|
5455
5464
|
|
|
5456
5465
|
// src/react/hooks/use-available-slots.ts
|
|
5466
|
+
function narrowingUnits(units) {
|
|
5467
|
+
return units != null && units > 1 ? units : void 0;
|
|
5468
|
+
}
|
|
5457
5469
|
var slotsQuery = createCachedQuery({
|
|
5458
5470
|
name: "useAvailableSlots",
|
|
5459
|
-
key: (client, options) => cacheKey(
|
|
5471
|
+
key: (client, options) => cacheKey(
|
|
5472
|
+
client.getPublicKey(),
|
|
5473
|
+
options.serviceId,
|
|
5474
|
+
options.date,
|
|
5475
|
+
options.participantCount,
|
|
5476
|
+
narrowingUnits(options.units)
|
|
5477
|
+
),
|
|
5460
5478
|
fetch: async (client, options) => {
|
|
5461
5479
|
const result = await client.scheduling.getAvailableSlots({
|
|
5462
5480
|
service_id: options.serviceId,
|
|
5463
5481
|
date: options.date,
|
|
5464
|
-
participant_count: options.participantCount
|
|
5482
|
+
participant_count: options.participantCount,
|
|
5483
|
+
units: narrowingUnits(options.units)
|
|
5465
5484
|
});
|
|
5466
5485
|
if (!result.ok) throw result.error;
|
|
5467
5486
|
return result.value;
|
|
@@ -5478,6 +5497,9 @@ function useAvailableSlots(serviceId, date, options = {}) {
|
|
|
5478
5497
|
}
|
|
5479
5498
|
|
|
5480
5499
|
// src/react/hooks/use-service-availability.ts
|
|
5500
|
+
function narrowingUnits2(units) {
|
|
5501
|
+
return units != null && units > 1 ? units : void 0;
|
|
5502
|
+
}
|
|
5481
5503
|
var serviceAvailabilityQuery = createCachedQuery({
|
|
5482
5504
|
name: "useServiceAvailability",
|
|
5483
5505
|
key: (client, options) => cacheKey(
|
|
@@ -5485,7 +5507,8 @@ var serviceAvailabilityQuery = createCachedQuery({
|
|
|
5485
5507
|
options.serviceId,
|
|
5486
5508
|
options.startDate,
|
|
5487
5509
|
options.endDate,
|
|
5488
|
-
options.participantCount
|
|
5510
|
+
options.participantCount,
|
|
5511
|
+
narrowingUnits2(options.units)
|
|
5489
5512
|
),
|
|
5490
5513
|
fetch: async (client, options) => {
|
|
5491
5514
|
const result = await client.scheduling.getServiceAvailability({
|
|
@@ -5493,7 +5516,8 @@ var serviceAvailabilityQuery = createCachedQuery({
|
|
|
5493
5516
|
start_date: options.startDate,
|
|
5494
5517
|
end_date: options.endDate,
|
|
5495
5518
|
location_id: options.locationId,
|
|
5496
|
-
participant_count: options.participantCount
|
|
5519
|
+
participant_count: options.participantCount,
|
|
5520
|
+
units: narrowingUnits2(options.units)
|
|
5497
5521
|
});
|
|
5498
5522
|
if (!result.ok) throw result.error;
|
|
5499
5523
|
return result.value.availability;
|
|
@@ -5865,6 +5889,20 @@ var IDLE_TIMEOUT_MS = 6e4;
|
|
|
5865
5889
|
var WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
5866
5890
|
var WS_PATH = "/api/v1/support/conversation/ws";
|
|
5867
5891
|
var AWAIT_REPLY_TIMEOUT_MS = 3e4;
|
|
5892
|
+
function mergeMessages(prev, incoming) {
|
|
5893
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
5894
|
+
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5895
|
+
if (fresh.length === 0) return prev;
|
|
5896
|
+
const merged = [...prev, ...fresh].sort(
|
|
5897
|
+
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
5898
|
+
);
|
|
5899
|
+
const confirmed = new Set(
|
|
5900
|
+
merged.filter((m) => !m.id.startsWith("opt_") && m.client_id).map((m) => m.client_id)
|
|
5901
|
+
);
|
|
5902
|
+
return merged.filter(
|
|
5903
|
+
(m) => !(m.id.startsWith("opt_") && m.client_id && confirmed.has(m.client_id))
|
|
5904
|
+
);
|
|
5905
|
+
}
|
|
5868
5906
|
function useChat(options = {}) {
|
|
5869
5907
|
const context = useOptionalCimplifyClient();
|
|
5870
5908
|
const client = options.client ?? context?.client;
|
|
@@ -5877,6 +5915,9 @@ function useChat(options = {}) {
|
|
|
5877
5915
|
const [awaitingReply, setAwaitingReply] = useState(false);
|
|
5878
5916
|
const [seenAt, setSeenAt] = useState(null);
|
|
5879
5917
|
const [mayHaveOlder, setMayHaveOlder] = useState(false);
|
|
5918
|
+
const [claimedAs, setClaimedAs] = useState(
|
|
5919
|
+
() => client?.support.claimedCustomerName ?? null
|
|
5920
|
+
);
|
|
5880
5921
|
const pollRef = useRef(null);
|
|
5881
5922
|
const lastActivityRef = useRef(Date.now());
|
|
5882
5923
|
const openedRef = useRef(false);
|
|
@@ -5888,6 +5929,7 @@ function useChat(options = {}) {
|
|
|
5888
5929
|
const aiEnabledRef = useRef(false);
|
|
5889
5930
|
const humanRequestedRef = useRef(false);
|
|
5890
5931
|
const awaitReplyTimerRef = useRef(null);
|
|
5932
|
+
const claimUnsubRef = useRef(null);
|
|
5891
5933
|
messagesRef.current = messages;
|
|
5892
5934
|
clientRef.current = client;
|
|
5893
5935
|
humanRequestedRef.current = humanRequested;
|
|
@@ -5913,18 +5955,22 @@ function useChat(options = {}) {
|
|
|
5913
5955
|
if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
|
|
5914
5956
|
stopAwaitingReply();
|
|
5915
5957
|
}
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5919
|
-
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
5920
|
-
});
|
|
5958
|
+
if (incoming.length > 0) lastActivityRef.current = Date.now();
|
|
5959
|
+
setMessages((prev) => mergeMessages(prev, incoming));
|
|
5921
5960
|
},
|
|
5922
5961
|
[stopAwaitingReply]
|
|
5923
5962
|
);
|
|
5963
|
+
const lastConfirmedMessage = useCallback(() => {
|
|
5964
|
+
const list = messagesRef.current;
|
|
5965
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
5966
|
+
if (!list[i].id.startsWith("opt_")) return list[i];
|
|
5967
|
+
}
|
|
5968
|
+
return void 0;
|
|
5969
|
+
}, []);
|
|
5924
5970
|
const fetchNewMessages = useCallback(async () => {
|
|
5925
5971
|
const c = clientRef.current;
|
|
5926
5972
|
if (!c) return;
|
|
5927
|
-
const last =
|
|
5973
|
+
const last = lastConfirmedMessage();
|
|
5928
5974
|
const result = await c.support.getMessages({
|
|
5929
5975
|
after: last?.created_at,
|
|
5930
5976
|
limit: 50
|
|
@@ -5932,29 +5978,61 @@ function useChat(options = {}) {
|
|
|
5932
5978
|
if (result.ok && result.value.length > 0) {
|
|
5933
5979
|
appendMessages(result.value);
|
|
5934
5980
|
}
|
|
5935
|
-
}, [appendMessages]);
|
|
5936
|
-
const pollMessages = useCallback(async () => {
|
|
5937
|
-
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
|
|
5938
|
-
await fetchNewMessages();
|
|
5939
|
-
}, [fetchNewMessages]);
|
|
5981
|
+
}, [appendMessages, lastConfirmedMessage]);
|
|
5940
5982
|
const startPolling = useCallback(() => {
|
|
5941
5983
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5942
|
-
const
|
|
5943
|
-
|
|
5944
|
-
|
|
5984
|
+
const idle = () => Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
|
|
5985
|
+
let current = idle() ? pollIdleInterval : pollInterval;
|
|
5986
|
+
const tick = () => {
|
|
5987
|
+
void fetchNewMessages().then(() => {
|
|
5988
|
+
const desired = idle() ? pollIdleInterval : pollInterval;
|
|
5989
|
+
if (desired !== current && pollRef.current) {
|
|
5990
|
+
current = desired;
|
|
5991
|
+
clearInterval(pollRef.current);
|
|
5992
|
+
pollRef.current = setInterval(tick, current);
|
|
5993
|
+
}
|
|
5994
|
+
});
|
|
5995
|
+
};
|
|
5996
|
+
pollRef.current = setInterval(tick, current);
|
|
5997
|
+
}, [fetchNewMessages, pollInterval, pollIdleInterval]);
|
|
5945
5998
|
const stopPolling = useCallback(() => {
|
|
5946
5999
|
if (pollRef.current) {
|
|
5947
6000
|
clearInterval(pollRef.current);
|
|
5948
6001
|
pollRef.current = null;
|
|
5949
6002
|
}
|
|
5950
6003
|
}, []);
|
|
6004
|
+
const resyncConversation = useCallback(async () => {
|
|
6005
|
+
const c = clientRef.current;
|
|
6006
|
+
if (!c || !openedRef.current) return;
|
|
6007
|
+
const result = await c.support.openConversation();
|
|
6008
|
+
if (!result.ok) return;
|
|
6009
|
+
const snapshot = result.value.messages;
|
|
6010
|
+
setMessages((prev) => {
|
|
6011
|
+
const snapshotIds = new Set(snapshot.map((m) => m.id));
|
|
6012
|
+
const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
|
|
6013
|
+
const extras = prev.filter(
|
|
6014
|
+
(m) => !snapshotIds.has(m.id) && (m.id.startsWith("opt_") || new Date(m.created_at).getTime() > tail)
|
|
6015
|
+
);
|
|
6016
|
+
return mergeMessages(snapshot, extras);
|
|
6017
|
+
});
|
|
6018
|
+
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6019
|
+
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6020
|
+
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6021
|
+
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6022
|
+
}, []);
|
|
5951
6023
|
const connectWebSocket = useCallback(() => {
|
|
5952
6024
|
const c = clientRef.current;
|
|
5953
6025
|
if (!c || typeof WebSocket === "undefined") {
|
|
5954
6026
|
startPolling();
|
|
5955
6027
|
return;
|
|
5956
6028
|
}
|
|
6029
|
+
if (wsReconnectTimerRef.current) {
|
|
6030
|
+
clearTimeout(wsReconnectTimerRef.current);
|
|
6031
|
+
wsReconnectTimerRef.current = null;
|
|
6032
|
+
}
|
|
5957
6033
|
if (wsRef.current) {
|
|
6034
|
+
wsRef.current.onclose = null;
|
|
6035
|
+
wsRef.current.onmessage = null;
|
|
5958
6036
|
wsRef.current.close();
|
|
5959
6037
|
wsRef.current = null;
|
|
5960
6038
|
}
|
|
@@ -5978,10 +6056,12 @@ function useChat(options = {}) {
|
|
|
5978
6056
|
else stopAwaitingReply();
|
|
5979
6057
|
} else if (envelope.type === "resync") {
|
|
5980
6058
|
const latest = envelope.latest_message_at;
|
|
5981
|
-
const last =
|
|
6059
|
+
const last = lastConfirmedMessage();
|
|
5982
6060
|
if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
|
|
5983
6061
|
fetchNewMessages();
|
|
5984
6062
|
}
|
|
6063
|
+
} else if (envelope.type === "rekey") {
|
|
6064
|
+
void resyncConversation();
|
|
5985
6065
|
}
|
|
5986
6066
|
} catch {
|
|
5987
6067
|
}
|
|
@@ -5993,6 +6073,7 @@ function useChat(options = {}) {
|
|
|
5993
6073
|
if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
|
|
5994
6074
|
const attempt = wsReconnectAttemptRef.current++;
|
|
5995
6075
|
const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
|
|
6076
|
+
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
5996
6077
|
wsReconnectTimerRef.current = setTimeout(connectWebSocket, delay);
|
|
5997
6078
|
} else {
|
|
5998
6079
|
startPolling();
|
|
@@ -6001,14 +6082,91 @@ function useChat(options = {}) {
|
|
|
6001
6082
|
ws.onerror = () => {
|
|
6002
6083
|
};
|
|
6003
6084
|
wsRef.current = ws;
|
|
6004
|
-
}, [appendMessages, startPolling, stopPolling, fetchNewMessages]);
|
|
6085
|
+
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation, lastConfirmedMessage]);
|
|
6086
|
+
const resetConversation = useCallback(() => {
|
|
6087
|
+
stopPolling();
|
|
6088
|
+
if (wsReconnectTimerRef.current) {
|
|
6089
|
+
clearTimeout(wsReconnectTimerRef.current);
|
|
6090
|
+
wsReconnectTimerRef.current = null;
|
|
6091
|
+
}
|
|
6092
|
+
if (wsRef.current) {
|
|
6093
|
+
wsRef.current.onclose = null;
|
|
6094
|
+
wsRef.current.close();
|
|
6095
|
+
wsRef.current = null;
|
|
6096
|
+
}
|
|
6097
|
+
wsReconnectAttemptRef.current = 0;
|
|
6098
|
+
openedRef.current = false;
|
|
6099
|
+
stopAwaitingReply();
|
|
6100
|
+
setMessages([]);
|
|
6101
|
+
setConversationOpened(false);
|
|
6102
|
+
setHumanRequested(false);
|
|
6103
|
+
setSeenAt(null);
|
|
6104
|
+
setMayHaveOlder(false);
|
|
6105
|
+
setError(null);
|
|
6106
|
+
setClaimedAs(null);
|
|
6107
|
+
}, [stopPolling, stopAwaitingReply]);
|
|
6108
|
+
const hydrateConversation = useCallback(
|
|
6109
|
+
(value) => {
|
|
6110
|
+
const c = clientRef.current;
|
|
6111
|
+
setMessages(value.messages);
|
|
6112
|
+
setConversationOpened(true);
|
|
6113
|
+
aiEnabledRef.current = value.conversation.ai_enabled;
|
|
6114
|
+
setHumanRequested(value.conversation.needs_human_review ?? false);
|
|
6115
|
+
setSeenAt(value.conversation.team_read_at ?? null);
|
|
6116
|
+
setMayHaveOlder(value.messages.length >= 50);
|
|
6117
|
+
setClaimedAs(c?.support.claimedCustomerName ?? null);
|
|
6118
|
+
connectWebSocket();
|
|
6119
|
+
},
|
|
6120
|
+
[connectWebSocket]
|
|
6121
|
+
);
|
|
6122
|
+
const openIfExisting = useCallback(async () => {
|
|
6123
|
+
const c = clientRef.current;
|
|
6124
|
+
if (!c || openedRef.current) return;
|
|
6125
|
+
openedRef.current = true;
|
|
6126
|
+
setIsLoading(true);
|
|
6127
|
+
const result = await c.support.peekConversation();
|
|
6128
|
+
if (result.ok && result.value) {
|
|
6129
|
+
hydrateConversation(result.value);
|
|
6130
|
+
} else {
|
|
6131
|
+
openedRef.current = false;
|
|
6132
|
+
}
|
|
6133
|
+
setIsLoading(false);
|
|
6134
|
+
}, [hydrateConversation]);
|
|
6135
|
+
const watchSessionClaims = useCallback(() => {
|
|
6136
|
+
const c = clientRef.current;
|
|
6137
|
+
if (!c || claimUnsubRef.current) return;
|
|
6138
|
+
const unsubClaimed = c.events.on(SESSION_CLAIMED, (event) => {
|
|
6139
|
+
setClaimedAs(event.customerName || c.support.claimedCustomerName || null);
|
|
6140
|
+
if (!openedRef.current) {
|
|
6141
|
+
void openIfExisting();
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
6144
|
+
connectWebSocket();
|
|
6145
|
+
void resyncConversation();
|
|
6146
|
+
});
|
|
6147
|
+
const unsubCleared = c.events.on(SESSION_CLEARED, resetConversation);
|
|
6148
|
+
claimUnsubRef.current = () => {
|
|
6149
|
+
unsubClaimed();
|
|
6150
|
+
unsubCleared();
|
|
6151
|
+
};
|
|
6152
|
+
}, [connectWebSocket, resyncConversation, resetConversation, openIfExisting]);
|
|
6153
|
+
const signOut = useCallback(async () => {
|
|
6154
|
+
const c = clientRef.current;
|
|
6155
|
+
if (!c) return false;
|
|
6156
|
+
const result = await c.link.logout();
|
|
6157
|
+
return result.ok;
|
|
6158
|
+
}, []);
|
|
6005
6159
|
useEffect(() => {
|
|
6160
|
+
watchSessionClaims();
|
|
6006
6161
|
return () => {
|
|
6007
6162
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
6008
6163
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6009
6164
|
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
6165
|
+
claimUnsubRef.current?.();
|
|
6166
|
+
claimUnsubRef.current = null;
|
|
6010
6167
|
if (wsRef.current) {
|
|
6011
6168
|
wsRef.current.onclose = null;
|
|
6169
|
+
wsRef.current.onmessage = null;
|
|
6012
6170
|
wsRef.current.close();
|
|
6013
6171
|
}
|
|
6014
6172
|
};
|
|
@@ -6020,19 +6178,13 @@ function useChat(options = {}) {
|
|
|
6020
6178
|
setError(null);
|
|
6021
6179
|
const result = await client.support.openConversation();
|
|
6022
6180
|
if (result.ok) {
|
|
6023
|
-
|
|
6024
|
-
setConversationOpened(true);
|
|
6025
|
-
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6026
|
-
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6027
|
-
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6028
|
-
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6029
|
-
connectWebSocket();
|
|
6181
|
+
hydrateConversation(result.value);
|
|
6030
6182
|
} else {
|
|
6031
6183
|
openedRef.current = false;
|
|
6032
6184
|
setError(result.error);
|
|
6033
6185
|
}
|
|
6034
6186
|
setIsLoading(false);
|
|
6035
|
-
}, [client,
|
|
6187
|
+
}, [client, hydrateConversation]);
|
|
6036
6188
|
const send = useCallback(
|
|
6037
6189
|
async (content, opts) => {
|
|
6038
6190
|
const attachments = opts?.attachments ?? [];
|
|
@@ -6044,7 +6196,8 @@ function useChat(options = {}) {
|
|
|
6044
6196
|
if (!openedRef.current) return false;
|
|
6045
6197
|
}
|
|
6046
6198
|
const replyTo = opts?.replyTo;
|
|
6047
|
-
const
|
|
6199
|
+
const clientId = generateIdempotencyKey();
|
|
6200
|
+
const optimisticId = `opt_${clientId}`;
|
|
6048
6201
|
const optimistic = {
|
|
6049
6202
|
id: optimisticId,
|
|
6050
6203
|
sender_type: "customer",
|
|
@@ -6052,6 +6205,7 @@ function useChat(options = {}) {
|
|
|
6052
6205
|
content_type: attachments.length > 0 ? "image" : "text",
|
|
6053
6206
|
attachments,
|
|
6054
6207
|
metadata: {},
|
|
6208
|
+
client_id: clientId,
|
|
6055
6209
|
// Same shape the server echoes back, so the bubble doesn't reflow
|
|
6056
6210
|
// when the confirmed message replaces it.
|
|
6057
6211
|
reply_to: replyTo ? {
|
|
@@ -6064,6 +6218,7 @@ function useChat(options = {}) {
|
|
|
6064
6218
|
setMessages((prev) => [...prev, optimistic]);
|
|
6065
6219
|
setIsSending(true);
|
|
6066
6220
|
const result = await client.support.sendMessage(content.trim(), {
|
|
6221
|
+
idempotencyKey: clientId,
|
|
6067
6222
|
replyToMessageId: replyTo?.id,
|
|
6068
6223
|
attachments: attachments.length > 0 ? attachments : void 0
|
|
6069
6224
|
});
|
|
@@ -6134,11 +6289,7 @@ function useChat(options = {}) {
|
|
|
6134
6289
|
const older = result.value;
|
|
6135
6290
|
setMayHaveOlder(older.length >= 50);
|
|
6136
6291
|
if (older.length === 0) return false;
|
|
6137
|
-
setMessages((prev) =>
|
|
6138
|
-
const ids = new Set(prev.map((m) => m.id));
|
|
6139
|
-
const fresh = older.filter((m) => !ids.has(m.id));
|
|
6140
|
-
return fresh.length > 0 ? [...fresh, ...prev] : prev;
|
|
6141
|
-
});
|
|
6292
|
+
setMessages((prev) => mergeMessages(prev, older));
|
|
6142
6293
|
return true;
|
|
6143
6294
|
}, []);
|
|
6144
6295
|
const agentArrived = messages.some((m) => m.sender_type === "agent");
|
|
@@ -6152,11 +6303,14 @@ function useChat(options = {}) {
|
|
|
6152
6303
|
awaitingReply,
|
|
6153
6304
|
seenAt,
|
|
6154
6305
|
mayHaveOlder,
|
|
6306
|
+
claimedAs,
|
|
6307
|
+
signOut,
|
|
6155
6308
|
send,
|
|
6156
6309
|
startConversation,
|
|
6157
6310
|
react,
|
|
6158
6311
|
requestHuman,
|
|
6159
|
-
loadOlder
|
|
6312
|
+
loadOlder,
|
|
6313
|
+
openIfExisting
|
|
6160
6314
|
};
|
|
6161
6315
|
}
|
|
6162
6316
|
var ElementsContext = createContext({
|
|
@@ -7886,6 +8040,177 @@ function VolumePricing({
|
|
|
7886
8040
|
}
|
|
7887
8041
|
);
|
|
7888
8042
|
}
|
|
8043
|
+
function createPlacesSessionToken() {
|
|
8044
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
8045
|
+
return crypto.randomUUID();
|
|
8046
|
+
}
|
|
8047
|
+
return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
8048
|
+
}
|
|
8049
|
+
var MIN_QUERY_LENGTH = 3;
|
|
8050
|
+
var DEBOUNCE_MS = 300;
|
|
8051
|
+
var BLUR_CLOSE_MS = 120;
|
|
8052
|
+
function AddressAutocomplete({
|
|
8053
|
+
value,
|
|
8054
|
+
onSelect,
|
|
8055
|
+
onDirty,
|
|
8056
|
+
placeholder,
|
|
8057
|
+
required,
|
|
8058
|
+
autoFocus,
|
|
8059
|
+
id,
|
|
8060
|
+
className,
|
|
8061
|
+
classNames
|
|
8062
|
+
}) {
|
|
8063
|
+
const clientContext = useOptionalCimplifyClient();
|
|
8064
|
+
const [inputText, setInputText] = React14.useState(value?.formatted_address ?? "");
|
|
8065
|
+
const [suggestions, setSuggestions] = React14.useState([]);
|
|
8066
|
+
const [isOpen, setIsOpen] = React14.useState(false);
|
|
8067
|
+
const [highlightedIndex, setHighlightedIndex] = React14.useState(-1);
|
|
8068
|
+
const debounceRef = React14.useRef(null);
|
|
8069
|
+
const seqRef = React14.useRef(0);
|
|
8070
|
+
const sessionTokenRef = React14.useRef(createPlacesSessionToken());
|
|
8071
|
+
const blurTimeoutRef = React14.useRef(null);
|
|
8072
|
+
React14.useEffect(() => {
|
|
8073
|
+
return () => {
|
|
8074
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8075
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8076
|
+
seqRef.current += 1;
|
|
8077
|
+
};
|
|
8078
|
+
}, []);
|
|
8079
|
+
const fetchSuggestions = React14.useCallback(
|
|
8080
|
+
async (query) => {
|
|
8081
|
+
const client = clientContext?.client;
|
|
8082
|
+
if (!client || query.length < MIN_QUERY_LENGTH) {
|
|
8083
|
+
setSuggestions([]);
|
|
8084
|
+
setIsOpen(false);
|
|
8085
|
+
return;
|
|
8086
|
+
}
|
|
8087
|
+
const seq = ++seqRef.current;
|
|
8088
|
+
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8089
|
+
if (seqRef.current !== seq) return;
|
|
8090
|
+
if (result.ok && result.value.predictions.length > 0) {
|
|
8091
|
+
setSuggestions(result.value.predictions);
|
|
8092
|
+
setHighlightedIndex(0);
|
|
8093
|
+
setIsOpen(true);
|
|
8094
|
+
} else {
|
|
8095
|
+
setSuggestions([]);
|
|
8096
|
+
setIsOpen(false);
|
|
8097
|
+
}
|
|
8098
|
+
},
|
|
8099
|
+
[clientContext]
|
|
8100
|
+
);
|
|
8101
|
+
const handleInputChange = (nextValue) => {
|
|
8102
|
+
setInputText(nextValue);
|
|
8103
|
+
onDirty?.();
|
|
8104
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8105
|
+
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), DEBOUNCE_MS);
|
|
8106
|
+
};
|
|
8107
|
+
const selectSuggestion = React14.useCallback(
|
|
8108
|
+
async (suggestion) => {
|
|
8109
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8110
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8111
|
+
const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8112
|
+
setInputText(fallback);
|
|
8113
|
+
setSuggestions([]);
|
|
8114
|
+
setIsOpen(false);
|
|
8115
|
+
const client = clientContext?.client;
|
|
8116
|
+
if (!client) return;
|
|
8117
|
+
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8118
|
+
sessionTokenRef.current = createPlacesSessionToken();
|
|
8119
|
+
if (result.ok) {
|
|
8120
|
+
const d = result.value;
|
|
8121
|
+
const address = {
|
|
8122
|
+
formatted_address: d.formatted_address || fallback,
|
|
8123
|
+
street_address: d.street_address,
|
|
8124
|
+
apartment: d.apartment,
|
|
8125
|
+
city: d.city,
|
|
8126
|
+
region: d.region,
|
|
8127
|
+
postal_code: d.postal_code,
|
|
8128
|
+
country: d.country,
|
|
8129
|
+
latitude: d.latitude,
|
|
8130
|
+
longitude: d.longitude,
|
|
8131
|
+
place_id: d.place_id
|
|
8132
|
+
};
|
|
8133
|
+
setInputText(address.formatted_address);
|
|
8134
|
+
onSelect(address);
|
|
8135
|
+
}
|
|
8136
|
+
},
|
|
8137
|
+
[clientContext, onSelect]
|
|
8138
|
+
);
|
|
8139
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-address-autocomplete": true, className: cn("relative", className, classNames?.root), children: [
|
|
8140
|
+
/* @__PURE__ */ jsx(
|
|
8141
|
+
"input",
|
|
8142
|
+
{
|
|
8143
|
+
id,
|
|
8144
|
+
type: "text",
|
|
8145
|
+
value: inputText,
|
|
8146
|
+
onChange: (e) => handleInputChange(e.target.value),
|
|
8147
|
+
onFocus: () => {
|
|
8148
|
+
if (suggestions.length > 0) setIsOpen(true);
|
|
8149
|
+
},
|
|
8150
|
+
onBlur: () => {
|
|
8151
|
+
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), BLUR_CLOSE_MS);
|
|
8152
|
+
},
|
|
8153
|
+
onKeyDown: (e) => {
|
|
8154
|
+
if (e.key === "Escape") {
|
|
8155
|
+
setIsOpen(false);
|
|
8156
|
+
return;
|
|
8157
|
+
}
|
|
8158
|
+
if (!isOpen || suggestions.length === 0) return;
|
|
8159
|
+
if (e.key === "ArrowDown") {
|
|
8160
|
+
e.preventDefault();
|
|
8161
|
+
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8162
|
+
} else if (e.key === "ArrowUp") {
|
|
8163
|
+
e.preventDefault();
|
|
8164
|
+
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8165
|
+
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8166
|
+
e.preventDefault();
|
|
8167
|
+
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8168
|
+
}
|
|
8169
|
+
},
|
|
8170
|
+
placeholder: placeholder ?? "Search for an address...",
|
|
8171
|
+
required,
|
|
8172
|
+
autoFocus,
|
|
8173
|
+
autoComplete: "off",
|
|
8174
|
+
"aria-autocomplete": "list",
|
|
8175
|
+
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8176
|
+
"data-cimplify-address-input": true,
|
|
8177
|
+
className: classNames?.input
|
|
8178
|
+
}
|
|
8179
|
+
),
|
|
8180
|
+
isOpen && suggestions.length > 0 && /* @__PURE__ */ jsx(
|
|
8181
|
+
"div",
|
|
8182
|
+
{
|
|
8183
|
+
role: "listbox",
|
|
8184
|
+
"data-cimplify-address-list": true,
|
|
8185
|
+
className: cn(
|
|
8186
|
+
"absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg",
|
|
8187
|
+
classNames?.list
|
|
8188
|
+
),
|
|
8189
|
+
children: suggestions.map((s, i) => /* @__PURE__ */ jsx(
|
|
8190
|
+
"button",
|
|
8191
|
+
{
|
|
8192
|
+
type: "button",
|
|
8193
|
+
role: "option",
|
|
8194
|
+
"aria-selected": i === highlightedIndex,
|
|
8195
|
+
onMouseDown: (e) => {
|
|
8196
|
+
e.preventDefault();
|
|
8197
|
+
void selectSuggestion(s);
|
|
8198
|
+
},
|
|
8199
|
+
"data-cimplify-address-option": true,
|
|
8200
|
+
className: cn(
|
|
8201
|
+
"block w-full px-3 py-2.5 text-left text-sm transition-colors",
|
|
8202
|
+
i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70",
|
|
8203
|
+
classNames?.option,
|
|
8204
|
+
i === highlightedIndex && classNames?.activeOption
|
|
8205
|
+
),
|
|
8206
|
+
children: s.description
|
|
8207
|
+
},
|
|
8208
|
+
s.place_id
|
|
8209
|
+
))
|
|
8210
|
+
}
|
|
8211
|
+
)
|
|
8212
|
+
] });
|
|
8213
|
+
}
|
|
7889
8214
|
function parseValue(value) {
|
|
7890
8215
|
if (!value) return void 0;
|
|
7891
8216
|
const date = parseISO(value);
|
|
@@ -8646,129 +8971,18 @@ function AddressInput({
|
|
|
8646
8971
|
classNames,
|
|
8647
8972
|
inputClass
|
|
8648
8973
|
}) {
|
|
8649
|
-
const clientContext = useOptionalCimplifyClient();
|
|
8650
8974
|
const addr = value && typeof value === "object" ? value : null;
|
|
8651
|
-
|
|
8652
|
-
const [suggestions, setSuggestions] = React14.useState([]);
|
|
8653
|
-
const [isOpen, setIsOpen] = React14.useState(false);
|
|
8654
|
-
const [highlightedIndex, setHighlightedIndex] = React14.useState(-1);
|
|
8655
|
-
const debounceRef = React14.useRef(null);
|
|
8656
|
-
const seqRef = React14.useRef(0);
|
|
8657
|
-
const sessionTokenRef = React14.useRef(createSessionToken());
|
|
8658
|
-
const blurTimeoutRef = React14.useRef(null);
|
|
8659
|
-
React14.useEffect(() => {
|
|
8660
|
-
return () => {
|
|
8661
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8662
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8663
|
-
seqRef.current += 1;
|
|
8664
|
-
};
|
|
8665
|
-
}, []);
|
|
8666
|
-
const fetchSuggestions = React14.useCallback(async (query) => {
|
|
8667
|
-
const client = clientContext?.client;
|
|
8668
|
-
if (!client || query.length < 3) {
|
|
8669
|
-
setSuggestions([]);
|
|
8670
|
-
setIsOpen(false);
|
|
8671
|
-
return;
|
|
8672
|
-
}
|
|
8673
|
-
const seq = ++seqRef.current;
|
|
8674
|
-
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8675
|
-
if (seqRef.current !== seq) return;
|
|
8676
|
-
if (result.ok && result.value.predictions.length > 0) {
|
|
8677
|
-
setSuggestions(result.value.predictions);
|
|
8678
|
-
setHighlightedIndex(0);
|
|
8679
|
-
setIsOpen(true);
|
|
8680
|
-
} else {
|
|
8681
|
-
setSuggestions([]);
|
|
8682
|
-
setIsOpen(false);
|
|
8683
|
-
}
|
|
8684
|
-
}, [clientContext]);
|
|
8685
|
-
const handleInputChange = (nextValue) => {
|
|
8686
|
-
setInputText(nextValue);
|
|
8687
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8688
|
-
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
8689
|
-
};
|
|
8690
|
-
const selectSuggestion = React14.useCallback(async (suggestion) => {
|
|
8691
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8692
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8693
|
-
const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8694
|
-
setInputText(fallback);
|
|
8695
|
-
setSuggestions([]);
|
|
8696
|
-
setIsOpen(false);
|
|
8697
|
-
const client = clientContext?.client;
|
|
8698
|
-
if (!client) return;
|
|
8699
|
-
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8700
|
-
sessionTokenRef.current = createSessionToken();
|
|
8701
|
-
if (result.ok) {
|
|
8702
|
-
const d = result.value;
|
|
8703
|
-
const addressValue = {
|
|
8704
|
-
formatted_address: d.formatted_address || fallback,
|
|
8705
|
-
street_address: d.street_address,
|
|
8706
|
-
apartment: d.apartment,
|
|
8707
|
-
city: d.city,
|
|
8708
|
-
region: d.region,
|
|
8709
|
-
postal_code: d.postal_code,
|
|
8710
|
-
country: d.country,
|
|
8711
|
-
latitude: d.latitude,
|
|
8712
|
-
longitude: d.longitude,
|
|
8713
|
-
place_id: d.place_id
|
|
8714
|
-
};
|
|
8715
|
-
setInputText(addressValue.formatted_address);
|
|
8716
|
-
onValueChange(addressValue);
|
|
8717
|
-
}
|
|
8718
|
-
}, [clientContext, onValueChange]);
|
|
8719
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2 relative", classNames?.addressInput), children: [
|
|
8975
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", classNames?.addressInput), children: [
|
|
8720
8976
|
/* @__PURE__ */ jsx(
|
|
8721
|
-
|
|
8977
|
+
AddressAutocomplete,
|
|
8722
8978
|
{
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
onChange: (e) => handleInputChange(e.target.value),
|
|
8726
|
-
onFocus: () => {
|
|
8727
|
-
if (suggestions.length > 0) setIsOpen(true);
|
|
8728
|
-
},
|
|
8729
|
-
onBlur: () => {
|
|
8730
|
-
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
8731
|
-
},
|
|
8732
|
-
onKeyDown: (e) => {
|
|
8733
|
-
if (e.key === "Escape") {
|
|
8734
|
-
setIsOpen(false);
|
|
8735
|
-
return;
|
|
8736
|
-
}
|
|
8737
|
-
if (!isOpen || suggestions.length === 0) return;
|
|
8738
|
-
if (e.key === "ArrowDown") {
|
|
8739
|
-
e.preventDefault();
|
|
8740
|
-
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8741
|
-
} else if (e.key === "ArrowUp") {
|
|
8742
|
-
e.preventDefault();
|
|
8743
|
-
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8744
|
-
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8745
|
-
e.preventDefault();
|
|
8746
|
-
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8747
|
-
}
|
|
8748
|
-
},
|
|
8979
|
+
value: addr,
|
|
8980
|
+
onSelect: onValueChange,
|
|
8749
8981
|
placeholder: field.placeholder || "Search for an address...",
|
|
8750
8982
|
required: field.is_required,
|
|
8751
|
-
|
|
8752
|
-
"aria-autocomplete": "list",
|
|
8753
|
-
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8754
|
-
className: inputClass
|
|
8983
|
+
classNames: { input: inputClass }
|
|
8755
8984
|
}
|
|
8756
8985
|
),
|
|
8757
|
-
isOpen && suggestions.length > 0 && /* @__PURE__ */ 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__ */ jsx(
|
|
8758
|
-
"button",
|
|
8759
|
-
{
|
|
8760
|
-
type: "button",
|
|
8761
|
-
role: "option",
|
|
8762
|
-
"aria-selected": i === highlightedIndex,
|
|
8763
|
-
onMouseDown: (e) => {
|
|
8764
|
-
e.preventDefault();
|
|
8765
|
-
void selectSuggestion(s);
|
|
8766
|
-
},
|
|
8767
|
-
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"),
|
|
8768
|
-
children: s.description
|
|
8769
|
-
},
|
|
8770
|
-
s.place_id
|
|
8771
|
-
)) }),
|
|
8772
8986
|
addr && /* @__PURE__ */ jsx(
|
|
8773
8987
|
"input",
|
|
8774
8988
|
{
|
|
@@ -8788,121 +9002,22 @@ function LocationInput({
|
|
|
8788
9002
|
classNames,
|
|
8789
9003
|
inputClass
|
|
8790
9004
|
}) {
|
|
8791
|
-
const clientContext = useOptionalCimplifyClient();
|
|
8792
9005
|
const loc = value && typeof value === "object" ? value : null;
|
|
8793
|
-
|
|
8794
|
-
const [suggestions, setSuggestions] = React14.useState([]);
|
|
8795
|
-
const [isOpen, setIsOpen] = React14.useState(false);
|
|
8796
|
-
const [highlightedIndex, setHighlightedIndex] = React14.useState(-1);
|
|
8797
|
-
const debounceRef = React14.useRef(null);
|
|
8798
|
-
const seqRef = React14.useRef(0);
|
|
8799
|
-
const sessionTokenRef = React14.useRef(createSessionToken());
|
|
8800
|
-
const blurTimeoutRef = React14.useRef(null);
|
|
8801
|
-
React14.useEffect(() => {
|
|
8802
|
-
return () => {
|
|
8803
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8804
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8805
|
-
seqRef.current += 1;
|
|
8806
|
-
};
|
|
8807
|
-
}, []);
|
|
8808
|
-
const fetchSuggestions = React14.useCallback(async (query) => {
|
|
8809
|
-
const client = clientContext?.client;
|
|
8810
|
-
if (!client || query.length < 3) {
|
|
8811
|
-
setSuggestions([]);
|
|
8812
|
-
setIsOpen(false);
|
|
8813
|
-
return;
|
|
8814
|
-
}
|
|
8815
|
-
const seq = ++seqRef.current;
|
|
8816
|
-
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8817
|
-
if (seqRef.current !== seq) return;
|
|
8818
|
-
if (result.ok && result.value.predictions.length > 0) {
|
|
8819
|
-
setSuggestions(result.value.predictions);
|
|
8820
|
-
setHighlightedIndex(0);
|
|
8821
|
-
setIsOpen(true);
|
|
8822
|
-
} else {
|
|
8823
|
-
setSuggestions([]);
|
|
8824
|
-
setIsOpen(false);
|
|
8825
|
-
}
|
|
8826
|
-
}, [clientContext]);
|
|
8827
|
-
const handleInputChange = (nextValue) => {
|
|
8828
|
-
setInputText(nextValue);
|
|
8829
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8830
|
-
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
8831
|
-
};
|
|
8832
|
-
const selectSuggestion = React14.useCallback(async (suggestion) => {
|
|
8833
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8834
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8835
|
-
const label = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8836
|
-
setInputText(label);
|
|
8837
|
-
setSuggestions([]);
|
|
8838
|
-
setIsOpen(false);
|
|
8839
|
-
const client = clientContext?.client;
|
|
8840
|
-
if (!client) return;
|
|
8841
|
-
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8842
|
-
sessionTokenRef.current = createSessionToken();
|
|
8843
|
-
if (result.ok) {
|
|
8844
|
-
const d = result.value;
|
|
8845
|
-
onValueChange({
|
|
8846
|
-
latitude: d.latitude,
|
|
8847
|
-
longitude: d.longitude,
|
|
8848
|
-
label: d.formatted_address || label
|
|
8849
|
-
});
|
|
8850
|
-
setInputText(d.formatted_address || label);
|
|
8851
|
-
}
|
|
8852
|
-
}, [clientContext, onValueChange]);
|
|
8853
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("relative", classNames?.locationInput), children: [
|
|
9006
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(classNames?.locationInput), children: [
|
|
8854
9007
|
/* @__PURE__ */ jsx(
|
|
8855
|
-
|
|
9008
|
+
AddressAutocomplete,
|
|
8856
9009
|
{
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
},
|
|
8863
|
-
onBlur: () => {
|
|
8864
|
-
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
8865
|
-
},
|
|
8866
|
-
onKeyDown: (e) => {
|
|
8867
|
-
if (e.key === "Escape") {
|
|
8868
|
-
setIsOpen(false);
|
|
8869
|
-
return;
|
|
8870
|
-
}
|
|
8871
|
-
if (!isOpen || suggestions.length === 0) return;
|
|
8872
|
-
if (e.key === "ArrowDown") {
|
|
8873
|
-
e.preventDefault();
|
|
8874
|
-
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8875
|
-
} else if (e.key === "ArrowUp") {
|
|
8876
|
-
e.preventDefault();
|
|
8877
|
-
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8878
|
-
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8879
|
-
e.preventDefault();
|
|
8880
|
-
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8881
|
-
}
|
|
8882
|
-
},
|
|
9010
|
+
value: loc ? { formatted_address: loc.label ?? "", latitude: loc.latitude, longitude: loc.longitude } : null,
|
|
9011
|
+
onSelect: (address) => onValueChange({
|
|
9012
|
+
latitude: address.latitude,
|
|
9013
|
+
longitude: address.longitude,
|
|
9014
|
+
label: address.formatted_address
|
|
9015
|
+
}),
|
|
8883
9016
|
placeholder: field.placeholder || "Search for a location...",
|
|
8884
9017
|
required: field.is_required,
|
|
8885
|
-
|
|
8886
|
-
"aria-autocomplete": "list",
|
|
8887
|
-
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8888
|
-
className: inputClass
|
|
9018
|
+
classNames: { input: inputClass }
|
|
8889
9019
|
}
|
|
8890
9020
|
),
|
|
8891
|
-
isOpen && suggestions.length > 0 && /* @__PURE__ */ 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__ */ jsx(
|
|
8892
|
-
"button",
|
|
8893
|
-
{
|
|
8894
|
-
type: "button",
|
|
8895
|
-
role: "option",
|
|
8896
|
-
"aria-selected": i === highlightedIndex,
|
|
8897
|
-
onMouseDown: (e) => {
|
|
8898
|
-
e.preventDefault();
|
|
8899
|
-
void selectSuggestion(s);
|
|
8900
|
-
},
|
|
8901
|
-
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"),
|
|
8902
|
-
children: s.description
|
|
8903
|
-
},
|
|
8904
|
-
s.place_id
|
|
8905
|
-
)) }),
|
|
8906
9021
|
loc && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
8907
9022
|
loc.latitude.toFixed(4),
|
|
8908
9023
|
", ",
|
|
@@ -9033,12 +9148,6 @@ function SignatureInput({
|
|
|
9033
9148
|
!sig?.data_url && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: field.placeholder || "Draw your signature above" })
|
|
9034
9149
|
] });
|
|
9035
9150
|
}
|
|
9036
|
-
function createSessionToken() {
|
|
9037
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
9038
|
-
return crypto.randomUUID();
|
|
9039
|
-
}
|
|
9040
|
-
return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
9041
|
-
}
|
|
9042
9151
|
function getTimeOfDay(timeStr) {
|
|
9043
9152
|
const hour = parseInt(timeStr.split("T").pop()?.split(":")[0] ?? timeStr.split(":")[0], 10);
|
|
9044
9153
|
if (hour < 12) return "morning";
|
|
@@ -9284,6 +9393,7 @@ function DateSlotPicker({
|
|
|
9284
9393
|
serviceId,
|
|
9285
9394
|
daysToShow = 7,
|
|
9286
9395
|
participantCount,
|
|
9396
|
+
units,
|
|
9287
9397
|
selectedSlot,
|
|
9288
9398
|
onSlotSelect,
|
|
9289
9399
|
availability: availabilityProp,
|
|
@@ -9318,6 +9428,7 @@ function DateSlotPicker({
|
|
|
9318
9428
|
dateRange.endDate,
|
|
9319
9429
|
{
|
|
9320
9430
|
participantCount,
|
|
9431
|
+
units,
|
|
9321
9432
|
enabled: availabilityProp === void 0
|
|
9322
9433
|
}
|
|
9323
9434
|
);
|
|
@@ -9475,6 +9586,7 @@ function ProductCustomizer({
|
|
|
9475
9586
|
const [specialInstructions, setSpecialInstructions] = useState("");
|
|
9476
9587
|
const [selectedSlot, setSelectedSlot] = useState(null);
|
|
9477
9588
|
const [selectedStaffId, setSelectedStaffId] = useState(null);
|
|
9589
|
+
const [unitsCount, setUnitsCount] = useState(1);
|
|
9478
9590
|
const cart = useCart();
|
|
9479
9591
|
const currency = getProductCurrency(product);
|
|
9480
9592
|
const productType = product.type || "product";
|
|
@@ -9482,6 +9594,7 @@ function ProductCustomizer({
|
|
|
9482
9594
|
const isBundle = productType === "bundle";
|
|
9483
9595
|
const isDigital = productType === "digital";
|
|
9484
9596
|
const isService = productType === "service";
|
|
9597
|
+
const offersUnits = isService && isMultiDayService(product);
|
|
9485
9598
|
const isStandard = !isComposite && !isBundle;
|
|
9486
9599
|
const hasVariants = isStandard && product.variants && product.variants.length > 0;
|
|
9487
9600
|
const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
|
|
@@ -9503,6 +9616,7 @@ function ProductCustomizer({
|
|
|
9503
9616
|
setSpecialInstructions("");
|
|
9504
9617
|
setSelectedSlot(null);
|
|
9505
9618
|
setSelectedStaffId(null);
|
|
9619
|
+
setUnitsCount(1);
|
|
9506
9620
|
}, [product.id, product.min_order_quantity]);
|
|
9507
9621
|
const slotStaff = useMemo(
|
|
9508
9622
|
() => (selectedSlot?.available_staff ?? []).map((member) => ({
|
|
@@ -9592,7 +9706,17 @@ function ProductCustomizer({
|
|
|
9592
9706
|
if (perUnit === void 0 || perUnit === null) return void 0;
|
|
9593
9707
|
return parsePrice(perUnit) * quantity;
|
|
9594
9708
|
}, [quote, quantity]);
|
|
9595
|
-
const displayTotalPrice =
|
|
9709
|
+
const displayTotalPrice = useMemo(() => {
|
|
9710
|
+
const total = quotedTotalPrice ?? localTotalPrice;
|
|
9711
|
+
const multiplier = serviceBillingMultiplier(product, {
|
|
9712
|
+
quantity,
|
|
9713
|
+
units: unitsCount,
|
|
9714
|
+
scheduledStart: selectedSlot?.start_time,
|
|
9715
|
+
scheduledEnd: selectedSlot?.end_time
|
|
9716
|
+
});
|
|
9717
|
+
if (multiplier === null) return total;
|
|
9718
|
+
return total / Math.max(1, quantity) * multiplier;
|
|
9719
|
+
}, [quotedTotalPrice, localTotalPrice, product, quantity, unitsCount, selectedSlot]);
|
|
9596
9720
|
const handleVariantChange = useCallback(
|
|
9597
9721
|
(variantId, variant) => {
|
|
9598
9722
|
setSelectedVariantId(variantId);
|
|
@@ -9624,7 +9748,8 @@ function ProductCustomizer({
|
|
|
9624
9748
|
scheduledStart: selectedSlot.start_time,
|
|
9625
9749
|
scheduledEnd: selectedSlot.end_time,
|
|
9626
9750
|
staffId: selectedStaffId ?? selectedSlot.available_staff?.[0]?.staff_id,
|
|
9627
|
-
resourceId: selectedSlot.available_resources?.[0]?.resource_id
|
|
9751
|
+
resourceId: selectedSlot.available_resources?.[0]?.resource_id,
|
|
9752
|
+
units: unitsCount > 1 ? unitsCount : void 0
|
|
9628
9753
|
} : {},
|
|
9629
9754
|
customerInputs: Object.keys(customerInputValues).length > 0 ? Object.entries(customerInputValues).filter(([, v]) => v !== void 0 && v !== "").map(([fieldId, value]) => ({ field_id: fieldId, value })) : void 0,
|
|
9630
9755
|
specialInstructions: specialInstructions.trim() || void 0
|
|
@@ -9645,7 +9770,7 @@ function ProductCustomizer({
|
|
|
9645
9770
|
} finally {
|
|
9646
9771
|
setIsSubmitting(false);
|
|
9647
9772
|
}
|
|
9648
|
-
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, isSubmitting, onAddToCart, cart]);
|
|
9773
|
+
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, unitsCount, isSubmitting, onAddToCart, cart]);
|
|
9649
9774
|
return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
|
|
9650
9775
|
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsx(
|
|
9651
9776
|
CompositeSelector,
|
|
@@ -9682,9 +9807,17 @@ function ProductCustomizer({
|
|
|
9682
9807
|
setSelectedStaffId(null);
|
|
9683
9808
|
},
|
|
9684
9809
|
participantCount: quantity,
|
|
9685
|
-
|
|
9810
|
+
units: offersUnits ? unitsCount : void 0,
|
|
9811
|
+
currency,
|
|
9812
|
+
schedulingMode: product.scheduling_mode ?? (offersUnits ? "multi_day" : "intraday"),
|
|
9813
|
+
durationUnit: product.duration_unit,
|
|
9814
|
+
durationValue: product.duration_value
|
|
9686
9815
|
}
|
|
9687
9816
|
),
|
|
9817
|
+
offersUnits && /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-units": true, className: classNames?.units, children: [
|
|
9818
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Units" }),
|
|
9819
|
+
/* @__PURE__ */ jsx(QuantitySelector, { value: unitsCount, onChange: setUnitsCount, min: 1 })
|
|
9820
|
+
] }),
|
|
9688
9821
|
isService && slotStaff.length > 0 && /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-staff": true, children: [
|
|
9689
9822
|
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Choose your specialist" }),
|
|
9690
9823
|
/* @__PURE__ */ jsx(
|
|
@@ -11738,18 +11871,18 @@ function RentalServiceCard({
|
|
|
11738
11871
|
const currency = getProductCurrency(product);
|
|
11739
11872
|
const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
|
|
11740
11873
|
const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
|
|
11874
|
+
const priceUnit = product.price_basis === "per_person" ? "guest" : product.price_basis === "flat" ? "" : unit;
|
|
11741
11875
|
const status = product.inventory_status;
|
|
11742
11876
|
return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
|
|
11743
11877
|
/* @__PURE__ */ jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
|
|
11744
11878
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
|
|
11745
|
-
/* @__PURE__ */ 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: [
|
|
11879
|
+
priceUnit && /* @__PURE__ */ 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: [
|
|
11746
11880
|
"Per ",
|
|
11747
|
-
|
|
11881
|
+
priceUnit
|
|
11748
11882
|
] }),
|
|
11749
11883
|
/* @__PURE__ */ jsxs("span", { className: "absolute bottom-3 right-3 text-white font-bold text-lg drop-shadow-sm", children: [
|
|
11750
11884
|
/* @__PURE__ */ jsx(Price, { amount: product.default_price, currency }),
|
|
11751
|
-
|
|
11752
|
-
unit
|
|
11885
|
+
priceUnit && `/${priceUnit}`
|
|
11753
11886
|
] }),
|
|
11754
11887
|
product.general_service_capacity != null && product.general_service_capacity > 1 && /* @__PURE__ */ 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: [
|
|
11755
11888
|
"Up to ",
|
|
@@ -12343,6 +12476,18 @@ function ProductGrid({
|
|
|
12343
12476
|
] });
|
|
12344
12477
|
}
|
|
12345
12478
|
|
|
12479
|
+
// src/react/utils/attachment.ts
|
|
12480
|
+
var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
|
|
12481
|
+
function safeAttachmentUrl(url) {
|
|
12482
|
+
return /^https?:\/\//i.test(url) ? url : null;
|
|
12483
|
+
}
|
|
12484
|
+
function isImageAttachment(attachment) {
|
|
12485
|
+
if (attachment.mime_type?.startsWith("image/")) return true;
|
|
12486
|
+
if (attachment.mime_type) return false;
|
|
12487
|
+
const path = attachment.url.split("?")[0].split("#")[0].toLowerCase();
|
|
12488
|
+
return IMAGE_EXTENSIONS.some((extension) => path.endsWith(extension));
|
|
12489
|
+
}
|
|
12490
|
+
|
|
12346
12491
|
// src/react/utils/boundary-labels.ts
|
|
12347
12492
|
var PRESET_LABELS = {
|
|
12348
12493
|
check_in_out: { start: "Check-in", end: "Check-out" },
|
|
@@ -13524,6 +13669,7 @@ function BookingPage({
|
|
|
13524
13669
|
const [selectedDate, setSelectedDate] = useState(null);
|
|
13525
13670
|
const [selectedStaffId, setSelectedStaffId] = useState(null);
|
|
13526
13671
|
const [selectedResourceId, setSelectedResourceId] = useState(null);
|
|
13672
|
+
const [bookingUnits, setBookingUnits] = useState(1);
|
|
13527
13673
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
13528
13674
|
const [error, setError] = useState(null);
|
|
13529
13675
|
const hasResourceStep = resources && resources.length > 0;
|
|
@@ -13617,7 +13763,8 @@ function BookingPage({
|
|
|
13617
13763
|
scheduledStart: selectedSlot.start_time,
|
|
13618
13764
|
scheduledEnd: selectedSlot.end_time,
|
|
13619
13765
|
staffId: selectedStaffId || void 0,
|
|
13620
|
-
resourceId: selectedResourceId || void 0
|
|
13766
|
+
resourceId: selectedResourceId || void 0,
|
|
13767
|
+
units: bookingUnits > 1 ? bookingUnits : void 0
|
|
13621
13768
|
});
|
|
13622
13769
|
onBooked?.(selectedSlot, selectedStaffId);
|
|
13623
13770
|
}
|
|
@@ -13713,6 +13860,31 @@ function BookingPage({
|
|
|
13713
13860
|
] }),
|
|
13714
13861
|
step === STEP.CONFIRM && selectedSlot && /* @__PURE__ */ jsxs("div", { "data-cimplify-booking-step": "confirm", className: classNames?.step, children: [
|
|
13715
13862
|
/* @__PURE__ */ jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Confirm your booking" }),
|
|
13863
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-booking-units": true, className: classNames?.summaryRow, children: [
|
|
13864
|
+
/* @__PURE__ */ jsx("span", { children: "Units" }),
|
|
13865
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-booking-units-stepper": true, children: [
|
|
13866
|
+
/* @__PURE__ */ jsx(
|
|
13867
|
+
"button",
|
|
13868
|
+
{
|
|
13869
|
+
type: "button",
|
|
13870
|
+
"data-cimplify-booking-units-decrement": true,
|
|
13871
|
+
onClick: () => setBookingUnits((current) => Math.max(1, current - 1)),
|
|
13872
|
+
disabled: bookingUnits <= 1,
|
|
13873
|
+
children: "\u2212"
|
|
13874
|
+
}
|
|
13875
|
+
),
|
|
13876
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-booking-units-value": true, children: bookingUnits }),
|
|
13877
|
+
/* @__PURE__ */ jsx(
|
|
13878
|
+
"button",
|
|
13879
|
+
{
|
|
13880
|
+
type: "button",
|
|
13881
|
+
"data-cimplify-booking-units-increment": true,
|
|
13882
|
+
onClick: () => setBookingUnits((current) => current + 1),
|
|
13883
|
+
children: "+"
|
|
13884
|
+
}
|
|
13885
|
+
)
|
|
13886
|
+
] })
|
|
13887
|
+
] }),
|
|
13716
13888
|
/* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary": true, className: classNames?.summary, children: [
|
|
13717
13889
|
/* @__PURE__ */ jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
|
|
13718
13890
|
/* @__PURE__ */ jsx("span", { children: "Service" }),
|
|
@@ -16955,6 +17127,7 @@ function partitionMessages(messages) {
|
|
|
16955
17127
|
}
|
|
16956
17128
|
return { bubbles, reactionsByTarget };
|
|
16957
17129
|
}
|
|
17130
|
+
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
16958
17131
|
function ChatWidget({
|
|
16959
17132
|
client: clientProp,
|
|
16960
17133
|
businessName = "Support",
|
|
@@ -16971,7 +17144,8 @@ function ChatWidget({
|
|
|
16971
17144
|
const context = useOptionalCimplifyClient();
|
|
16972
17145
|
const client = clientProp ?? context?.client;
|
|
16973
17146
|
const [open, setOpen] = useState(defaultOpen);
|
|
16974
|
-
const [
|
|
17147
|
+
const [readAt, setReadAt] = useState(null);
|
|
17148
|
+
const [attachError, setAttachError] = useState(null);
|
|
16975
17149
|
const {
|
|
16976
17150
|
messages,
|
|
16977
17151
|
isLoading,
|
|
@@ -16980,16 +17154,31 @@ function ChatWidget({
|
|
|
16980
17154
|
seenAt,
|
|
16981
17155
|
error,
|
|
16982
17156
|
isActive,
|
|
17157
|
+
claimedAs,
|
|
17158
|
+
signOut,
|
|
16983
17159
|
send,
|
|
16984
17160
|
react,
|
|
16985
|
-
startConversation
|
|
17161
|
+
startConversation,
|
|
17162
|
+
openIfExisting
|
|
16986
17163
|
} = useChat({ client, pollInterval });
|
|
16987
17164
|
const showTyping = isSending || awaitingReply;
|
|
16988
17165
|
const attachPhoto = useCallback(
|
|
16989
17166
|
async (file) => {
|
|
16990
|
-
if (!client
|
|
17167
|
+
if (!client) return;
|
|
17168
|
+
setAttachError(null);
|
|
17169
|
+
if (!file.type.startsWith("image/")) {
|
|
17170
|
+
setAttachError(new CimplifyError("invalid_attachment", "Only photos can be attached."));
|
|
17171
|
+
return;
|
|
17172
|
+
}
|
|
17173
|
+
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
17174
|
+
setAttachError(new CimplifyError("attachment_too_large", "Photos must be under 5MB."));
|
|
17175
|
+
return;
|
|
17176
|
+
}
|
|
16991
17177
|
const uploaded = await client.uploads.upload(file);
|
|
16992
|
-
if (!uploaded.ok)
|
|
17178
|
+
if (!uploaded.ok) {
|
|
17179
|
+
setAttachError(uploaded.error);
|
|
17180
|
+
return;
|
|
17181
|
+
}
|
|
16993
17182
|
const attachment = {
|
|
16994
17183
|
upload_id: uploaded.value.id,
|
|
16995
17184
|
url: uploaded.value.url,
|
|
@@ -17001,22 +17190,28 @@ function ChatWidget({
|
|
|
17001
17190
|
},
|
|
17002
17191
|
[client, send]
|
|
17003
17192
|
);
|
|
17004
|
-
const
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17193
|
+
const handleSend = useCallback(
|
|
17194
|
+
(content) => {
|
|
17195
|
+
setAttachError(null);
|
|
17196
|
+
return send(content);
|
|
17197
|
+
},
|
|
17198
|
+
[send]
|
|
17199
|
+
);
|
|
17200
|
+
const unread = open ? 0 : messages.filter(
|
|
17201
|
+
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
17202
|
+
).length;
|
|
17203
|
+
useState(() => {
|
|
17204
|
+
if (defaultOpen) void openIfExisting();
|
|
17205
|
+
});
|
|
17015
17206
|
const handleOpen = useCallback(() => {
|
|
17016
17207
|
setOpen(true);
|
|
17017
|
-
|
|
17018
|
-
}, []);
|
|
17019
|
-
const handleClose = useCallback(() =>
|
|
17208
|
+
void openIfExisting();
|
|
17209
|
+
}, [openIfExisting]);
|
|
17210
|
+
const handleClose = useCallback(() => {
|
|
17211
|
+
const newest = messages[messages.length - 1];
|
|
17212
|
+
setReadAt(newest?.created_at ?? null);
|
|
17213
|
+
setOpen(false);
|
|
17214
|
+
}, [messages]);
|
|
17020
17215
|
const isLeft = position === "bottom-left";
|
|
17021
17216
|
return /* @__PURE__ */ jsxs(
|
|
17022
17217
|
"div",
|
|
@@ -17049,11 +17244,13 @@ function ChatWidget({
|
|
|
17049
17244
|
isSending,
|
|
17050
17245
|
isTyping: showTyping,
|
|
17051
17246
|
seenAt,
|
|
17052
|
-
error,
|
|
17247
|
+
error: error ?? attachError,
|
|
17053
17248
|
isActive,
|
|
17054
17249
|
isLeft,
|
|
17250
|
+
claimedAs,
|
|
17251
|
+
onSignOut: () => void signOut(),
|
|
17055
17252
|
onClose: handleClose,
|
|
17056
|
-
onSend:
|
|
17253
|
+
onSend: handleSend,
|
|
17057
17254
|
onReact: react,
|
|
17058
17255
|
onAttach: attachPhoto,
|
|
17059
17256
|
onStartConversation: startConversation,
|
|
@@ -17113,6 +17310,8 @@ function Panel({
|
|
|
17113
17310
|
error,
|
|
17114
17311
|
isActive,
|
|
17115
17312
|
isLeft,
|
|
17313
|
+
claimedAs,
|
|
17314
|
+
onSignOut,
|
|
17116
17315
|
onClose,
|
|
17117
17316
|
onSend,
|
|
17118
17317
|
onReact,
|
|
@@ -17136,6 +17335,8 @@ function Panel({
|
|
|
17136
17335
|
Header,
|
|
17137
17336
|
{
|
|
17138
17337
|
businessName,
|
|
17338
|
+
claimedAs,
|
|
17339
|
+
onSignOut,
|
|
17139
17340
|
onClose,
|
|
17140
17341
|
className: classNames?.header
|
|
17141
17342
|
}
|
|
@@ -17160,7 +17361,7 @@ function Panel({
|
|
|
17160
17361
|
className: classNames?.messages
|
|
17161
17362
|
}
|
|
17162
17363
|
),
|
|
17163
|
-
error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: "Something went wrong. Please try again." }),
|
|
17364
|
+
error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: error.userMessage || "Something went wrong. Please try again." }),
|
|
17164
17365
|
(isActive || isLoading) && /* @__PURE__ */ jsx(
|
|
17165
17366
|
ChatInput,
|
|
17166
17367
|
{
|
|
@@ -17182,6 +17383,8 @@ function Panel({
|
|
|
17182
17383
|
}
|
|
17183
17384
|
function Header({
|
|
17184
17385
|
businessName,
|
|
17386
|
+
claimedAs,
|
|
17387
|
+
onSignOut,
|
|
17185
17388
|
onClose,
|
|
17186
17389
|
className
|
|
17187
17390
|
}) {
|
|
@@ -17196,7 +17399,22 @@ function Header({
|
|
|
17196
17399
|
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-white/15", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
17197
17400
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
17198
17401
|
/* @__PURE__ */ jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
17199
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1
|
|
17402
|
+
claimedAs ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-white/60", children: [
|
|
17403
|
+
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
17404
|
+
"Chatting as ",
|
|
17405
|
+
claimedAs
|
|
17406
|
+
] }),
|
|
17407
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
17408
|
+
/* @__PURE__ */ jsx(
|
|
17409
|
+
"button",
|
|
17410
|
+
{
|
|
17411
|
+
type: "button",
|
|
17412
|
+
onClick: onSignOut,
|
|
17413
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-white",
|
|
17414
|
+
children: "Not you?"
|
|
17415
|
+
}
|
|
17416
|
+
)
|
|
17417
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
|
|
17200
17418
|
/* @__PURE__ */ jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
17201
17419
|
"Online"
|
|
17202
17420
|
] })
|
|
@@ -17280,10 +17498,6 @@ function MessageList({
|
|
|
17280
17498
|
onReact,
|
|
17281
17499
|
className
|
|
17282
17500
|
}) {
|
|
17283
|
-
const bottomRef = useRef(null);
|
|
17284
|
-
useEffect(() => {
|
|
17285
|
-
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
17286
|
-
}, [messages.length, isSending]);
|
|
17287
17501
|
const { bubbles, reactionsByTarget } = partitionMessages(messages);
|
|
17288
17502
|
const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
|
|
17289
17503
|
return /* @__PURE__ */ jsxs(
|
|
@@ -17357,11 +17571,13 @@ function MessageList({
|
|
|
17357
17571
|
]
|
|
17358
17572
|
}
|
|
17359
17573
|
),
|
|
17360
|
-
msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map(
|
|
17361
|
-
|
|
17574
|
+
msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map((attachment, index) => {
|
|
17575
|
+
const url = safeAttachmentUrl(attachment.url);
|
|
17576
|
+
if (!url) return null;
|
|
17577
|
+
return attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
|
|
17362
17578
|
"img",
|
|
17363
17579
|
{
|
|
17364
|
-
src:
|
|
17580
|
+
src: url,
|
|
17365
17581
|
alt: attachment.filename ?? "Photo",
|
|
17366
17582
|
loading: "lazy",
|
|
17367
17583
|
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
@@ -17369,15 +17585,15 @@ function MessageList({
|
|
|
17369
17585
|
) }, index) : /* @__PURE__ */ jsx(
|
|
17370
17586
|
"a",
|
|
17371
17587
|
{
|
|
17372
|
-
href:
|
|
17588
|
+
href: url,
|
|
17373
17589
|
target: "_blank",
|
|
17374
17590
|
rel: "noreferrer",
|
|
17375
17591
|
className: "truncate text-xs underline",
|
|
17376
17592
|
children: attachment.filename ?? "Attachment"
|
|
17377
17593
|
},
|
|
17378
17594
|
index
|
|
17379
|
-
)
|
|
17380
|
-
) }),
|
|
17595
|
+
);
|
|
17596
|
+
}) }),
|
|
17381
17597
|
isCustomer ? msg.content && /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsx(
|
|
17382
17598
|
ChatMarkdown,
|
|
17383
17599
|
{
|
|
@@ -17403,7 +17619,15 @@ function MessageList({
|
|
|
17403
17619
|
] }, msg.id);
|
|
17404
17620
|
}),
|
|
17405
17621
|
isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
17406
|
-
/* @__PURE__ */ jsx(
|
|
17622
|
+
/* @__PURE__ */ jsx(
|
|
17623
|
+
"div",
|
|
17624
|
+
{
|
|
17625
|
+
ref: (node) => {
|
|
17626
|
+
node?.scrollIntoView({ behavior: "smooth" });
|
|
17627
|
+
}
|
|
17628
|
+
},
|
|
17629
|
+
`${messages.length}:${isSending}`
|
|
17630
|
+
)
|
|
17407
17631
|
]
|
|
17408
17632
|
}
|
|
17409
17633
|
);
|
|
@@ -19450,4 +19674,4 @@ async function genUIFingerprint(manifest) {
|
|
|
19450
19674
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
19451
19675
|
}
|
|
19452
19676
|
|
|
19453
|
-
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|
|
19677
|
+
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, createPlacesSessionToken, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isImageAttachment, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|