@cimplify/sdk 0.28.0 → 0.30.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 +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/{chunk-GS7AX6B3.js → chunk-F47XFKYP.js} +152 -0
- package/dist/{chunk-FFUCCUG4.mjs → chunk-WGAVNFEQ.mjs} +149 -1
- package/dist/{index-EGhIxwfI.d.mts → index-BHVJgyqM.d.ts} +2 -2
- package/dist/{index-C2U1gHyP.d.ts → index-D4oNCc37.d.mts} +2 -2
- package/dist/{index-iHKILmG9.d.ts → index-DTdtu4OO.d.ts} +141 -15
- package/dist/{index-ClrQ0yTH.d.mts → index-DaXyvlxk.d.mts} +141 -15
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +26 -10
- package/dist/index.mjs +1 -1
- package/dist/{payment-k8feV7r8.d.ts → payment-DnU_eLuT.d.mts} +119 -3
- package/dist/{payment-k8feV7r8.d.mts → payment-DnU_eLuT.d.ts} +119 -3
- package/dist/{price-Den0zv61.d.mts → price-D-hso3np.d.mts} +1 -1
- package/dist/{price-4EK-0zF5.d.ts → price-DUVmRTaz.d.ts} +1 -1
- package/dist/react.d.mts +99 -4
- package/dist/react.d.ts +99 -4
- package/dist/react.js +1893 -381
- package/dist/react.mjs +1556 -47
- package/dist/styles.css +1 -1
- package/dist/utils.d.mts +3 -3
- package/dist/utils.d.ts +3 -3
- package/package.json +1 -1
- package/registry/cart-summary.json +1 -1
- package/registry/chat-widget.json +15 -0
- package/registry/index.json +9 -0
- package/registry/product-customizer.json +1 -1
- package/registry/schedule-service-card.json +1 -1
- package/registry/service-product-layout.json +1 -1
- package/registry/subscription-card.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { DURATION_UNIT, getVariantDisplayName, PRODUCT_TYPE, createCimplifyClient, RENDER_HINT } from './chunk-
|
|
3
|
-
export { getVariantDisplayName } from './chunk-
|
|
2
|
+
import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, createCimplifyClient, RENDER_HINT } from './chunk-WGAVNFEQ.mjs';
|
|
3
|
+
export { getVariantDisplayName } from './chunk-WGAVNFEQ.mjs';
|
|
4
4
|
import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-3J4PPSMI.mjs';
|
|
5
5
|
import { isSupportedCurrency, formatPrice, parsePrice, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-E3HC2YAE.mjs';
|
|
6
|
-
import
|
|
6
|
+
import React10, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore, useId } from 'react';
|
|
7
7
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
8
8
|
import { clsx } from 'clsx';
|
|
9
9
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -565,7 +565,8 @@ function mapServerCart(serverCart) {
|
|
|
565
565
|
confirmationCode: item.confirmation_code || void 0,
|
|
566
566
|
serviceStatus: item.service_status || void 0,
|
|
567
567
|
bundleResolved: item.bundle_resolved || void 0,
|
|
568
|
-
compositeResolved: item.composite_resolved || void 0
|
|
568
|
+
compositeResolved: item.composite_resolved || void 0,
|
|
569
|
+
customerInputs: item.customer_inputs || void 0
|
|
569
570
|
});
|
|
570
571
|
});
|
|
571
572
|
return {
|
|
@@ -785,7 +786,8 @@ function createCartStore(params) {
|
|
|
785
786
|
scheduled_start: normalizedOptions.scheduledStart,
|
|
786
787
|
scheduled_end: normalizedOptions.scheduledEnd,
|
|
787
788
|
staff_id: normalizedOptions.staffId,
|
|
788
|
-
resource_id: normalizedOptions.resourceId
|
|
789
|
+
resource_id: normalizedOptions.resourceId,
|
|
790
|
+
customer_inputs: normalizedOptions.customerInputs
|
|
789
791
|
});
|
|
790
792
|
if (!result.ok) {
|
|
791
793
|
throw result.error;
|
|
@@ -1423,14 +1425,14 @@ function CimplifyCheckout({
|
|
|
1423
1425
|
resolvedCartRef.current = resolvedCart;
|
|
1424
1426
|
const primaryColor = appearance?.variables?.primaryColor || "#0a2540";
|
|
1425
1427
|
const isDark = appearance?.theme === "dark";
|
|
1426
|
-
const emitStatus =
|
|
1428
|
+
const emitStatus = React10.useEffectEvent(
|
|
1427
1429
|
(nextStatus, context = {}) => {
|
|
1428
1430
|
setStatus(nextStatus);
|
|
1429
1431
|
setStatusText(context.display_text || "");
|
|
1430
1432
|
onStatusChange?.(nextStatus, context);
|
|
1431
1433
|
}
|
|
1432
1434
|
);
|
|
1433
|
-
const fireError =
|
|
1435
|
+
const fireError = React10.useEffectEvent(
|
|
1434
1436
|
(error) => {
|
|
1435
1437
|
onError?.(error);
|
|
1436
1438
|
}
|
|
@@ -1536,7 +1538,7 @@ function CimplifyCheckout({
|
|
|
1536
1538
|
activeCheckoutRef.current = null;
|
|
1537
1539
|
};
|
|
1538
1540
|
}, []);
|
|
1539
|
-
const handleSubmit =
|
|
1541
|
+
const handleSubmit = React10.useEffectEvent(async () => {
|
|
1540
1542
|
if (isSubmitting || isInitializing || !resolvedCartId) {
|
|
1541
1543
|
if (!resolvedCartId && !isInitializing) {
|
|
1542
1544
|
const message = "Your cart is empty. Add items before checkout.";
|
|
@@ -3308,6 +3310,122 @@ function useBootstrap(options = {}) {
|
|
|
3308
3310
|
const { data, isLoading, error, refetch } = bootstrapQuery(options);
|
|
3309
3311
|
return { bootstrap: data, isLoading, error, refetch };
|
|
3310
3312
|
}
|
|
3313
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
3314
|
+
var POLL_IDLE_INTERVAL_MS = 15e3;
|
|
3315
|
+
var IDLE_TIMEOUT_MS = 6e4;
|
|
3316
|
+
function useChat(options = {}) {
|
|
3317
|
+
const context = useOptionalCimplifyClient();
|
|
3318
|
+
const client = options.client ?? context?.client;
|
|
3319
|
+
const [messages, setMessages] = useState([]);
|
|
3320
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
3321
|
+
const [isSending, setIsSending] = useState(false);
|
|
3322
|
+
const [error, setError] = useState(null);
|
|
3323
|
+
const [conversationOpened, setConversationOpened] = useState(false);
|
|
3324
|
+
const pollRef = useRef(null);
|
|
3325
|
+
const lastActivityRef = useRef(Date.now());
|
|
3326
|
+
const openedRef = useRef(false);
|
|
3327
|
+
const messagesRef = useRef([]);
|
|
3328
|
+
messagesRef.current = messages;
|
|
3329
|
+
const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
|
|
3330
|
+
const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
|
|
3331
|
+
const pollMessages = useCallback(async () => {
|
|
3332
|
+
if (!client) return;
|
|
3333
|
+
const isIdle = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
|
|
3334
|
+
if (isIdle) return;
|
|
3335
|
+
const last = messagesRef.current[messagesRef.current.length - 1];
|
|
3336
|
+
const result = await client.support.getMessages({
|
|
3337
|
+
after: last?.created_at,
|
|
3338
|
+
limit: 50
|
|
3339
|
+
});
|
|
3340
|
+
if (result.ok && result.value.length > 0) {
|
|
3341
|
+
setMessages((prev) => {
|
|
3342
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
3343
|
+
const fresh = result.value.filter((m) => !ids.has(m.id));
|
|
3344
|
+
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
}, [client]);
|
|
3348
|
+
const startPolling = useCallback(() => {
|
|
3349
|
+
if (pollRef.current) clearInterval(pollRef.current);
|
|
3350
|
+
const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
|
|
3351
|
+
pollRef.current = setInterval(pollMessages, interval);
|
|
3352
|
+
}, [pollMessages, pollInterval, pollIdleInterval]);
|
|
3353
|
+
const stopPolling = useCallback(() => {
|
|
3354
|
+
if (pollRef.current) {
|
|
3355
|
+
clearInterval(pollRef.current);
|
|
3356
|
+
pollRef.current = null;
|
|
3357
|
+
}
|
|
3358
|
+
}, []);
|
|
3359
|
+
useEffect(() => stopPolling, [stopPolling]);
|
|
3360
|
+
const openConversation = useCallback(async () => {
|
|
3361
|
+
if (openedRef.current || !client) return;
|
|
3362
|
+
openedRef.current = true;
|
|
3363
|
+
setIsLoading(true);
|
|
3364
|
+
setError(null);
|
|
3365
|
+
const result = await client.support.openConversation();
|
|
3366
|
+
if (result.ok) {
|
|
3367
|
+
setMessages(result.value.messages);
|
|
3368
|
+
setConversationOpened(true);
|
|
3369
|
+
startPolling();
|
|
3370
|
+
} else {
|
|
3371
|
+
openedRef.current = false;
|
|
3372
|
+
setError(result.error);
|
|
3373
|
+
}
|
|
3374
|
+
setIsLoading(false);
|
|
3375
|
+
}, [client, startPolling]);
|
|
3376
|
+
const send = useCallback(
|
|
3377
|
+
async (content) => {
|
|
3378
|
+
if (!client || !content.trim()) return;
|
|
3379
|
+
setError(null);
|
|
3380
|
+
lastActivityRef.current = Date.now();
|
|
3381
|
+
if (!openedRef.current) {
|
|
3382
|
+
await openConversation();
|
|
3383
|
+
if (!openedRef.current) return;
|
|
3384
|
+
}
|
|
3385
|
+
const optimisticId = `opt_${Date.now()}`;
|
|
3386
|
+
const optimistic = {
|
|
3387
|
+
id: optimisticId,
|
|
3388
|
+
sender_type: "customer",
|
|
3389
|
+
content: content.trim(),
|
|
3390
|
+
content_type: "text",
|
|
3391
|
+
attachments: [],
|
|
3392
|
+
metadata: {},
|
|
3393
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3394
|
+
};
|
|
3395
|
+
setMessages((prev) => [...prev, optimistic]);
|
|
3396
|
+
setIsSending(true);
|
|
3397
|
+
const result = await client.support.sendMessage(content.trim());
|
|
3398
|
+
if (result.ok) {
|
|
3399
|
+
setMessages(
|
|
3400
|
+
(prev) => prev.map((m) => m.id === optimisticId ? result.value : m)
|
|
3401
|
+
);
|
|
3402
|
+
} else {
|
|
3403
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
|
|
3404
|
+
setError(result.error);
|
|
3405
|
+
}
|
|
3406
|
+
setIsSending(false);
|
|
3407
|
+
},
|
|
3408
|
+
[client, openConversation]
|
|
3409
|
+
);
|
|
3410
|
+
const startConversation = useCallback(
|
|
3411
|
+
async (starterText) => {
|
|
3412
|
+
await openConversation();
|
|
3413
|
+
if (starterText) {
|
|
3414
|
+
await send(starterText);
|
|
3415
|
+
}
|
|
3416
|
+
},
|
|
3417
|
+
[openConversation, send]
|
|
3418
|
+
);
|
|
3419
|
+
return {
|
|
3420
|
+
messages,
|
|
3421
|
+
isLoading,
|
|
3422
|
+
isSending,
|
|
3423
|
+
error,
|
|
3424
|
+
isActive: conversationOpened && messages.length > 0,
|
|
3425
|
+
send,
|
|
3426
|
+
startConversation
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3311
3429
|
var ElementsContext = createContext({
|
|
3312
3430
|
elements: null,
|
|
3313
3431
|
isReady: false
|
|
@@ -4659,8 +4777,26 @@ function CompositeSelector({
|
|
|
4659
4777
|
)
|
|
4660
4778
|
] });
|
|
4661
4779
|
}
|
|
4780
|
+
|
|
4781
|
+
// src/react/utils/format-duration.ts
|
|
4782
|
+
var UNIT_LABELS = {
|
|
4783
|
+
minutes: { singular: "min", plural: "min" },
|
|
4784
|
+
hours: { singular: "hr", plural: "hrs" },
|
|
4785
|
+
days: { singular: "day", plural: "days" },
|
|
4786
|
+
nights: { singular: "night", plural: "nights" },
|
|
4787
|
+
weeks: { singular: "week", plural: "weeks" },
|
|
4788
|
+
months: { singular: "month", plural: "months" },
|
|
4789
|
+
years: { singular: "year", plural: "years" }
|
|
4790
|
+
};
|
|
4791
|
+
function formatDuration2(value, unit) {
|
|
4792
|
+
const resolved = unit ?? "minutes";
|
|
4793
|
+
const labels = UNIT_LABELS[resolved];
|
|
4794
|
+
const label = value === 1 ? labels.singular : labels.plural;
|
|
4795
|
+
return `${value} ${label}`;
|
|
4796
|
+
}
|
|
4662
4797
|
var FREQUENCY_LABELS = {
|
|
4663
4798
|
weekly: "Weekly",
|
|
4799
|
+
biweekly: "Biweekly",
|
|
4664
4800
|
monthly: "Monthly",
|
|
4665
4801
|
quarterly: "Quarterly",
|
|
4666
4802
|
annually: "Annually"
|
|
@@ -4848,6 +4984,880 @@ function VolumePricing({
|
|
|
4848
4984
|
}
|
|
4849
4985
|
);
|
|
4850
4986
|
}
|
|
4987
|
+
function CustomerInputFields({
|
|
4988
|
+
fields,
|
|
4989
|
+
values,
|
|
4990
|
+
onChange,
|
|
4991
|
+
onFileUpload,
|
|
4992
|
+
className,
|
|
4993
|
+
classNames
|
|
4994
|
+
}) {
|
|
4995
|
+
const clientContext = useOptionalCimplifyClient();
|
|
4996
|
+
if (fields.length === 0) return null;
|
|
4997
|
+
const sorted = [...fields].sort((a, b) => a.display_order - b.display_order);
|
|
4998
|
+
const setValue = useCallback(
|
|
4999
|
+
(fieldId, value) => {
|
|
5000
|
+
onChange({ ...values, [fieldId]: value });
|
|
5001
|
+
},
|
|
5002
|
+
[values, onChange]
|
|
5003
|
+
);
|
|
5004
|
+
const defaultFileUpload = useCallback(
|
|
5005
|
+
async (file) => {
|
|
5006
|
+
if (onFileUpload) return onFileUpload(file, {});
|
|
5007
|
+
if (!clientContext?.client) throw new Error("No upload provider available");
|
|
5008
|
+
const result = await clientContext.client.uploads.upload(file);
|
|
5009
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
5010
|
+
return result.value.url;
|
|
5011
|
+
},
|
|
5012
|
+
[onFileUpload, clientContext]
|
|
5013
|
+
);
|
|
5014
|
+
return /* @__PURE__ */ jsx("div", { "data-cimplify-customer-inputs": true, className: cn("space-y-4", className, classNames?.root), children: sorted.map((field) => /* @__PURE__ */ jsx(
|
|
5015
|
+
InputField,
|
|
5016
|
+
{
|
|
5017
|
+
field,
|
|
5018
|
+
value: values[field.id],
|
|
5019
|
+
onValueChange: (value) => setValue(field.id, value),
|
|
5020
|
+
onFileUpload: onFileUpload ?? (clientContext?.client ? (_file, _field) => defaultFileUpload(_file) : void 0),
|
|
5021
|
+
classNames
|
|
5022
|
+
},
|
|
5023
|
+
field.id
|
|
5024
|
+
)) });
|
|
5025
|
+
}
|
|
5026
|
+
function InputField({
|
|
5027
|
+
field,
|
|
5028
|
+
value,
|
|
5029
|
+
onValueChange,
|
|
5030
|
+
onFileUpload,
|
|
5031
|
+
classNames
|
|
5032
|
+
}) {
|
|
5033
|
+
const hasPriceAdjustment = field.price_adjustment != null && parsePrice(field.price_adjustment) > 0;
|
|
5034
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-customer-input": true, "data-field-type": field.field_type, className: classNames?.field, children: [
|
|
5035
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
|
|
5036
|
+
/* @__PURE__ */ jsx("label", { className: cn("text-sm font-semibold", classNames?.label), children: field.name }),
|
|
5037
|
+
field.is_required && /* @__PURE__ */ jsx("span", { className: cn("text-xs text-destructive", classNames?.required), children: "Required" }),
|
|
5038
|
+
hasPriceAdjustment && /* @__PURE__ */ jsxs("span", { className: cn("text-xs text-muted-foreground", classNames?.priceAdjustment), children: [
|
|
5039
|
+
"+",
|
|
5040
|
+
/* @__PURE__ */ jsx(Price, { amount: field.price_adjustment })
|
|
5041
|
+
] })
|
|
5042
|
+
] }),
|
|
5043
|
+
field.help_text && /* @__PURE__ */ jsx("p", { className: cn("text-xs text-muted-foreground mb-1.5", classNames?.helpText), children: field.help_text }),
|
|
5044
|
+
/* @__PURE__ */ jsx(
|
|
5045
|
+
FieldInput,
|
|
5046
|
+
{
|
|
5047
|
+
field,
|
|
5048
|
+
value,
|
|
5049
|
+
onValueChange,
|
|
5050
|
+
onFileUpload,
|
|
5051
|
+
classNames
|
|
5052
|
+
}
|
|
5053
|
+
)
|
|
5054
|
+
] });
|
|
5055
|
+
}
|
|
5056
|
+
function FieldInput({
|
|
5057
|
+
field,
|
|
5058
|
+
value,
|
|
5059
|
+
onValueChange,
|
|
5060
|
+
onFileUpload,
|
|
5061
|
+
classNames
|
|
5062
|
+
}) {
|
|
5063
|
+
const inputClass = cn(
|
|
5064
|
+
"w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
|
|
5065
|
+
classNames?.input
|
|
5066
|
+
);
|
|
5067
|
+
switch (field.field_type) {
|
|
5068
|
+
case INPUT_FIELD_TYPE.Text:
|
|
5069
|
+
case INPUT_FIELD_TYPE.Url:
|
|
5070
|
+
return /* @__PURE__ */ jsx(
|
|
5071
|
+
"input",
|
|
5072
|
+
{
|
|
5073
|
+
type: field.field_type === INPUT_FIELD_TYPE.Url ? "url" : "text",
|
|
5074
|
+
value: typeof value === "string" ? value : "",
|
|
5075
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
5076
|
+
placeholder: field.placeholder,
|
|
5077
|
+
required: field.is_required,
|
|
5078
|
+
maxLength: field.validation?.max_length ?? void 0,
|
|
5079
|
+
className: inputClass
|
|
5080
|
+
}
|
|
5081
|
+
);
|
|
5082
|
+
case INPUT_FIELD_TYPE.Textarea:
|
|
5083
|
+
return /* @__PURE__ */ jsx(
|
|
5084
|
+
"textarea",
|
|
5085
|
+
{
|
|
5086
|
+
value: typeof value === "string" ? value : "",
|
|
5087
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
5088
|
+
placeholder: field.placeholder,
|
|
5089
|
+
required: field.is_required,
|
|
5090
|
+
maxLength: field.validation?.max_length ?? void 0,
|
|
5091
|
+
rows: 3,
|
|
5092
|
+
className: cn(inputClass, "resize-none", classNames?.textarea)
|
|
5093
|
+
}
|
|
5094
|
+
);
|
|
5095
|
+
case INPUT_FIELD_TYPE.Number:
|
|
5096
|
+
return /* @__PURE__ */ jsx(
|
|
5097
|
+
"input",
|
|
5098
|
+
{
|
|
5099
|
+
type: "number",
|
|
5100
|
+
value: typeof value === "number" ? value : "",
|
|
5101
|
+
onChange: (e) => onValueChange(e.target.value ? parseFloat(e.target.value) : void 0),
|
|
5102
|
+
placeholder: field.placeholder,
|
|
5103
|
+
required: field.is_required,
|
|
5104
|
+
min: field.validation?.min_value ?? void 0,
|
|
5105
|
+
max: field.validation?.max_value ?? void 0,
|
|
5106
|
+
className: inputClass
|
|
5107
|
+
}
|
|
5108
|
+
);
|
|
5109
|
+
case INPUT_FIELD_TYPE.Select:
|
|
5110
|
+
return /* @__PURE__ */ jsxs(
|
|
5111
|
+
"select",
|
|
5112
|
+
{
|
|
5113
|
+
value: typeof value === "string" ? value : "",
|
|
5114
|
+
onChange: (e) => onValueChange(e.target.value || void 0),
|
|
5115
|
+
required: field.is_required,
|
|
5116
|
+
className: cn(inputClass, classNames?.select),
|
|
5117
|
+
children: [
|
|
5118
|
+
/* @__PURE__ */ jsx("option", { value: "", children: field.placeholder || "Select..." }),
|
|
5119
|
+
(field.options || []).map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
|
|
5120
|
+
]
|
|
5121
|
+
}
|
|
5122
|
+
);
|
|
5123
|
+
case INPUT_FIELD_TYPE.Radio:
|
|
5124
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxs(
|
|
5125
|
+
"label",
|
|
5126
|
+
{
|
|
5127
|
+
className: cn(
|
|
5128
|
+
"flex items-center gap-2 text-sm cursor-pointer",
|
|
5129
|
+
classNames?.radioOption
|
|
5130
|
+
),
|
|
5131
|
+
children: [
|
|
5132
|
+
/* @__PURE__ */ jsx(
|
|
5133
|
+
"input",
|
|
5134
|
+
{
|
|
5135
|
+
type: "radio",
|
|
5136
|
+
name: field.id,
|
|
5137
|
+
value: opt,
|
|
5138
|
+
checked: value === opt,
|
|
5139
|
+
onChange: () => onValueChange(opt),
|
|
5140
|
+
className: "accent-primary"
|
|
5141
|
+
}
|
|
5142
|
+
),
|
|
5143
|
+
opt
|
|
5144
|
+
]
|
|
5145
|
+
},
|
|
5146
|
+
opt
|
|
5147
|
+
)) });
|
|
5148
|
+
case INPUT_FIELD_TYPE.Checkbox:
|
|
5149
|
+
return /* @__PURE__ */ jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
|
|
5150
|
+
/* @__PURE__ */ jsx(
|
|
5151
|
+
"input",
|
|
5152
|
+
{
|
|
5153
|
+
type: "checkbox",
|
|
5154
|
+
checked: value === true,
|
|
5155
|
+
onChange: (e) => onValueChange(e.target.checked),
|
|
5156
|
+
className: "accent-primary w-4 h-4"
|
|
5157
|
+
}
|
|
5158
|
+
),
|
|
5159
|
+
field.placeholder || field.name
|
|
5160
|
+
] });
|
|
5161
|
+
case INPUT_FIELD_TYPE.Color:
|
|
5162
|
+
return /* @__PURE__ */ jsx(
|
|
5163
|
+
"input",
|
|
5164
|
+
{
|
|
5165
|
+
type: "color",
|
|
5166
|
+
value: typeof value === "string" ? value : "#000000",
|
|
5167
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
5168
|
+
className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
|
|
5169
|
+
}
|
|
5170
|
+
);
|
|
5171
|
+
case INPUT_FIELD_TYPE.Date:
|
|
5172
|
+
return /* @__PURE__ */ jsx(
|
|
5173
|
+
"input",
|
|
5174
|
+
{
|
|
5175
|
+
type: "date",
|
|
5176
|
+
value: typeof value === "string" ? value : "",
|
|
5177
|
+
onChange: (e) => onValueChange(e.target.value || void 0),
|
|
5178
|
+
required: field.is_required,
|
|
5179
|
+
className: inputClass
|
|
5180
|
+
}
|
|
5181
|
+
);
|
|
5182
|
+
case INPUT_FIELD_TYPE.File:
|
|
5183
|
+
case INPUT_FIELD_TYPE.Image:
|
|
5184
|
+
return /* @__PURE__ */ jsx(
|
|
5185
|
+
FileUploadInput,
|
|
5186
|
+
{
|
|
5187
|
+
field,
|
|
5188
|
+
value,
|
|
5189
|
+
onValueChange,
|
|
5190
|
+
onFileUpload,
|
|
5191
|
+
classNames
|
|
5192
|
+
}
|
|
5193
|
+
);
|
|
5194
|
+
case INPUT_FIELD_TYPE.Email:
|
|
5195
|
+
return /* @__PURE__ */ jsx(
|
|
5196
|
+
"input",
|
|
5197
|
+
{
|
|
5198
|
+
type: "email",
|
|
5199
|
+
value: typeof value === "string" ? value : "",
|
|
5200
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
5201
|
+
placeholder: field.placeholder || "email@example.com",
|
|
5202
|
+
required: field.is_required,
|
|
5203
|
+
className: inputClass
|
|
5204
|
+
}
|
|
5205
|
+
);
|
|
5206
|
+
case INPUT_FIELD_TYPE.DateTime: {
|
|
5207
|
+
const dtLocalValue = typeof value === "string" && value.includes("T") ? value.slice(0, 16) : typeof value === "string" ? value : "";
|
|
5208
|
+
return /* @__PURE__ */ jsx(
|
|
5209
|
+
"input",
|
|
5210
|
+
{
|
|
5211
|
+
type: "datetime-local",
|
|
5212
|
+
value: dtLocalValue,
|
|
5213
|
+
onChange: (e) => {
|
|
5214
|
+
if (!e.target.value) {
|
|
5215
|
+
onValueChange(void 0);
|
|
5216
|
+
return;
|
|
5217
|
+
}
|
|
5218
|
+
const date = new Date(e.target.value);
|
|
5219
|
+
onValueChange(Number.isNaN(date.getTime()) ? e.target.value : date.toISOString());
|
|
5220
|
+
},
|
|
5221
|
+
required: field.is_required,
|
|
5222
|
+
className: inputClass
|
|
5223
|
+
}
|
|
5224
|
+
);
|
|
5225
|
+
}
|
|
5226
|
+
case INPUT_FIELD_TYPE.Time:
|
|
5227
|
+
return /* @__PURE__ */ jsx(
|
|
5228
|
+
"input",
|
|
5229
|
+
{
|
|
5230
|
+
type: "time",
|
|
5231
|
+
value: typeof value === "string" ? value : "",
|
|
5232
|
+
onChange: (e) => onValueChange(e.target.value || void 0),
|
|
5233
|
+
required: field.is_required,
|
|
5234
|
+
className: inputClass
|
|
5235
|
+
}
|
|
5236
|
+
);
|
|
5237
|
+
case INPUT_FIELD_TYPE.MultiSelect:
|
|
5238
|
+
return /* @__PURE__ */ jsx(
|
|
5239
|
+
MultiSelectInput,
|
|
5240
|
+
{
|
|
5241
|
+
field,
|
|
5242
|
+
value,
|
|
5243
|
+
onValueChange,
|
|
5244
|
+
classNames
|
|
5245
|
+
}
|
|
5246
|
+
);
|
|
5247
|
+
case INPUT_FIELD_TYPE.DateRange:
|
|
5248
|
+
return /* @__PURE__ */ jsx(
|
|
5249
|
+
DateRangeInput,
|
|
5250
|
+
{
|
|
5251
|
+
field,
|
|
5252
|
+
value,
|
|
5253
|
+
onValueChange,
|
|
5254
|
+
classNames,
|
|
5255
|
+
inputClass
|
|
5256
|
+
}
|
|
5257
|
+
);
|
|
5258
|
+
case INPUT_FIELD_TYPE.Address:
|
|
5259
|
+
return /* @__PURE__ */ jsx(
|
|
5260
|
+
AddressInput,
|
|
5261
|
+
{
|
|
5262
|
+
field,
|
|
5263
|
+
value,
|
|
5264
|
+
onValueChange,
|
|
5265
|
+
classNames,
|
|
5266
|
+
inputClass
|
|
5267
|
+
}
|
|
5268
|
+
);
|
|
5269
|
+
case INPUT_FIELD_TYPE.Location:
|
|
5270
|
+
return /* @__PURE__ */ jsx(
|
|
5271
|
+
LocationInput,
|
|
5272
|
+
{
|
|
5273
|
+
field,
|
|
5274
|
+
value,
|
|
5275
|
+
onValueChange,
|
|
5276
|
+
classNames,
|
|
5277
|
+
inputClass
|
|
5278
|
+
}
|
|
5279
|
+
);
|
|
5280
|
+
case INPUT_FIELD_TYPE.Phone:
|
|
5281
|
+
return /* @__PURE__ */ jsx(
|
|
5282
|
+
PhoneInput,
|
|
5283
|
+
{
|
|
5284
|
+
field,
|
|
5285
|
+
value,
|
|
5286
|
+
onValueChange,
|
|
5287
|
+
classNames,
|
|
5288
|
+
inputClass
|
|
5289
|
+
}
|
|
5290
|
+
);
|
|
5291
|
+
case INPUT_FIELD_TYPE.Signature:
|
|
5292
|
+
return /* @__PURE__ */ jsx(
|
|
5293
|
+
SignatureInput,
|
|
5294
|
+
{
|
|
5295
|
+
field,
|
|
5296
|
+
value,
|
|
5297
|
+
onValueChange,
|
|
5298
|
+
classNames
|
|
5299
|
+
}
|
|
5300
|
+
);
|
|
5301
|
+
default:
|
|
5302
|
+
return /* @__PURE__ */ jsx(
|
|
5303
|
+
"input",
|
|
5304
|
+
{
|
|
5305
|
+
type: "text",
|
|
5306
|
+
value: typeof value === "string" ? value : "",
|
|
5307
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
5308
|
+
placeholder: field.placeholder,
|
|
5309
|
+
className: inputClass
|
|
5310
|
+
}
|
|
5311
|
+
);
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
function FileUploadInput({
|
|
5315
|
+
field,
|
|
5316
|
+
value,
|
|
5317
|
+
onValueChange,
|
|
5318
|
+
onFileUpload,
|
|
5319
|
+
classNames
|
|
5320
|
+
}) {
|
|
5321
|
+
const [isUploading, setIsUploading] = React10.useState(false);
|
|
5322
|
+
const fileUrl = typeof value === "string" ? value : void 0;
|
|
5323
|
+
const acceptedFormats = field.validation?.accepted_formats;
|
|
5324
|
+
const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
|
|
5325
|
+
const handleFile = async (file) => {
|
|
5326
|
+
if (!onFileUpload) return;
|
|
5327
|
+
if (field.validation?.max_size_mb) {
|
|
5328
|
+
const maxBytes = field.validation.max_size_mb * 1024 * 1024;
|
|
5329
|
+
if (file.size > maxBytes) {
|
|
5330
|
+
return;
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
setIsUploading(true);
|
|
5334
|
+
try {
|
|
5335
|
+
const url = await onFileUpload(file, field);
|
|
5336
|
+
onValueChange(url);
|
|
5337
|
+
} finally {
|
|
5338
|
+
setIsUploading(false);
|
|
5339
|
+
}
|
|
5340
|
+
};
|
|
5341
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-2", classNames?.fileInput), children: fileUrl ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5342
|
+
field.field_type === INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
|
|
5343
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground truncate", children: fileUrl.split("/").pop() }) }),
|
|
5344
|
+
/* @__PURE__ */ jsx(
|
|
5345
|
+
"button",
|
|
5346
|
+
{
|
|
5347
|
+
type: "button",
|
|
5348
|
+
onClick: () => onValueChange(void 0),
|
|
5349
|
+
className: "text-xs text-muted-foreground hover:text-destructive transition-colors",
|
|
5350
|
+
children: "Remove"
|
|
5351
|
+
}
|
|
5352
|
+
)
|
|
5353
|
+
] }) : /* @__PURE__ */ jsxs("label", { className: "flex flex-col items-center justify-center w-full h-24 border-2 border-dashed border-border rounded-lg cursor-pointer hover:border-primary/40 transition-colors", children: [
|
|
5354
|
+
/* @__PURE__ */ jsx(
|
|
5355
|
+
"input",
|
|
5356
|
+
{
|
|
5357
|
+
type: "file",
|
|
5358
|
+
accept,
|
|
5359
|
+
onChange: (e) => {
|
|
5360
|
+
const file = e.target.files?.[0];
|
|
5361
|
+
if (file) void handleFile(file);
|
|
5362
|
+
},
|
|
5363
|
+
className: "hidden",
|
|
5364
|
+
disabled: isUploading
|
|
5365
|
+
}
|
|
5366
|
+
),
|
|
5367
|
+
isUploading ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Uploading..." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5368
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
|
|
5369
|
+
acceptedFormats && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
|
|
5370
|
+
acceptedFormats.map((f) => f.toUpperCase()).join(", "),
|
|
5371
|
+
field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
|
|
5372
|
+
] })
|
|
5373
|
+
] })
|
|
5374
|
+
] }) });
|
|
5375
|
+
}
|
|
5376
|
+
function MultiSelectInput({
|
|
5377
|
+
field,
|
|
5378
|
+
value,
|
|
5379
|
+
onValueChange,
|
|
5380
|
+
classNames
|
|
5381
|
+
}) {
|
|
5382
|
+
const selected = Array.isArray(value) ? value : [];
|
|
5383
|
+
const options = field.options || [];
|
|
5384
|
+
const maxSelections = field.validation?.max_selections;
|
|
5385
|
+
const toggle = (opt) => {
|
|
5386
|
+
const next = selected.includes(opt) ? selected.filter((s) => s !== opt) : maxSelections && selected.length >= maxSelections ? selected : [...selected, opt];
|
|
5387
|
+
onValueChange(next.length > 0 ? next : void 0);
|
|
5388
|
+
};
|
|
5389
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", classNames?.multiSelectGroup), children: [
|
|
5390
|
+
options.map((opt) => /* @__PURE__ */ jsxs(
|
|
5391
|
+
"label",
|
|
5392
|
+
{
|
|
5393
|
+
className: cn(
|
|
5394
|
+
"flex items-center gap-2 text-sm cursor-pointer",
|
|
5395
|
+
classNames?.multiSelectOption
|
|
5396
|
+
),
|
|
5397
|
+
children: [
|
|
5398
|
+
/* @__PURE__ */ jsx(
|
|
5399
|
+
"input",
|
|
5400
|
+
{
|
|
5401
|
+
type: "checkbox",
|
|
5402
|
+
checked: selected.includes(opt),
|
|
5403
|
+
onChange: () => toggle(opt),
|
|
5404
|
+
className: "accent-primary w-4 h-4"
|
|
5405
|
+
}
|
|
5406
|
+
),
|
|
5407
|
+
opt
|
|
5408
|
+
]
|
|
5409
|
+
},
|
|
5410
|
+
opt
|
|
5411
|
+
)),
|
|
5412
|
+
maxSelections && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5413
|
+
selected.length,
|
|
5414
|
+
"/",
|
|
5415
|
+
maxSelections,
|
|
5416
|
+
" selected"
|
|
5417
|
+
] })
|
|
5418
|
+
] });
|
|
5419
|
+
}
|
|
5420
|
+
function DateRangeInput({
|
|
5421
|
+
field,
|
|
5422
|
+
value,
|
|
5423
|
+
onValueChange,
|
|
5424
|
+
classNames,
|
|
5425
|
+
inputClass
|
|
5426
|
+
}) {
|
|
5427
|
+
const range = value && typeof value === "object" ? value : {};
|
|
5428
|
+
const update = (key, v) => {
|
|
5429
|
+
const next = { ...range, [key]: v };
|
|
5430
|
+
onValueChange(next.start || next.end ? next : void 0);
|
|
5431
|
+
};
|
|
5432
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-2 gap-3", classNames?.dateRangeInput), children: [
|
|
5433
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5434
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "Start" }),
|
|
5435
|
+
/* @__PURE__ */ jsx(
|
|
5436
|
+
"input",
|
|
5437
|
+
{
|
|
5438
|
+
type: "date",
|
|
5439
|
+
value: range.start || "",
|
|
5440
|
+
onChange: (e) => update("start", e.target.value),
|
|
5441
|
+
required: field.is_required,
|
|
5442
|
+
className: inputClass
|
|
5443
|
+
}
|
|
5444
|
+
)
|
|
5445
|
+
] }),
|
|
5446
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5447
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "End" }),
|
|
5448
|
+
/* @__PURE__ */ jsx(
|
|
5449
|
+
"input",
|
|
5450
|
+
{
|
|
5451
|
+
type: "date",
|
|
5452
|
+
value: range.end || "",
|
|
5453
|
+
onChange: (e) => update("end", e.target.value),
|
|
5454
|
+
required: field.is_required,
|
|
5455
|
+
className: inputClass
|
|
5456
|
+
}
|
|
5457
|
+
)
|
|
5458
|
+
] })
|
|
5459
|
+
] });
|
|
5460
|
+
}
|
|
5461
|
+
function AddressInput({
|
|
5462
|
+
field,
|
|
5463
|
+
value,
|
|
5464
|
+
onValueChange,
|
|
5465
|
+
classNames,
|
|
5466
|
+
inputClass
|
|
5467
|
+
}) {
|
|
5468
|
+
const clientContext = useOptionalCimplifyClient();
|
|
5469
|
+
const addr = value && typeof value === "object" ? value : null;
|
|
5470
|
+
const [inputText, setInputText] = React10.useState(addr?.formatted_address ?? "");
|
|
5471
|
+
const [suggestions, setSuggestions] = React10.useState([]);
|
|
5472
|
+
const [isOpen, setIsOpen] = React10.useState(false);
|
|
5473
|
+
const [highlightedIndex, setHighlightedIndex] = React10.useState(-1);
|
|
5474
|
+
const debounceRef = React10.useRef(null);
|
|
5475
|
+
const seqRef = React10.useRef(0);
|
|
5476
|
+
const sessionTokenRef = React10.useRef(createSessionToken());
|
|
5477
|
+
const blurTimeoutRef = React10.useRef(null);
|
|
5478
|
+
React10.useEffect(() => {
|
|
5479
|
+
return () => {
|
|
5480
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5481
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
5482
|
+
seqRef.current += 1;
|
|
5483
|
+
};
|
|
5484
|
+
}, []);
|
|
5485
|
+
const fetchSuggestions = React10.useCallback(async (query) => {
|
|
5486
|
+
const client = clientContext?.client;
|
|
5487
|
+
if (!client || query.length < 3) {
|
|
5488
|
+
setSuggestions([]);
|
|
5489
|
+
setIsOpen(false);
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
const seq = ++seqRef.current;
|
|
5493
|
+
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
5494
|
+
if (seqRef.current !== seq) return;
|
|
5495
|
+
if (result.ok && result.value.predictions.length > 0) {
|
|
5496
|
+
setSuggestions(result.value.predictions);
|
|
5497
|
+
setHighlightedIndex(0);
|
|
5498
|
+
setIsOpen(true);
|
|
5499
|
+
} else {
|
|
5500
|
+
setSuggestions([]);
|
|
5501
|
+
setIsOpen(false);
|
|
5502
|
+
}
|
|
5503
|
+
}, [clientContext]);
|
|
5504
|
+
const handleInputChange = (nextValue) => {
|
|
5505
|
+
setInputText(nextValue);
|
|
5506
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5507
|
+
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
5508
|
+
};
|
|
5509
|
+
const selectSuggestion = React10.useCallback(async (suggestion) => {
|
|
5510
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5511
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
5512
|
+
const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
5513
|
+
setInputText(fallback);
|
|
5514
|
+
setSuggestions([]);
|
|
5515
|
+
setIsOpen(false);
|
|
5516
|
+
const client = clientContext?.client;
|
|
5517
|
+
if (!client) return;
|
|
5518
|
+
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
5519
|
+
sessionTokenRef.current = createSessionToken();
|
|
5520
|
+
if (result.ok) {
|
|
5521
|
+
const d = result.value;
|
|
5522
|
+
const addressValue = {
|
|
5523
|
+
formatted_address: d.formatted_address || fallback,
|
|
5524
|
+
street_address: d.street_address,
|
|
5525
|
+
apartment: d.apartment,
|
|
5526
|
+
city: d.city,
|
|
5527
|
+
region: d.region,
|
|
5528
|
+
postal_code: d.postal_code,
|
|
5529
|
+
country: d.country,
|
|
5530
|
+
latitude: d.latitude,
|
|
5531
|
+
longitude: d.longitude,
|
|
5532
|
+
place_id: d.place_id
|
|
5533
|
+
};
|
|
5534
|
+
setInputText(addressValue.formatted_address);
|
|
5535
|
+
onValueChange(addressValue);
|
|
5536
|
+
}
|
|
5537
|
+
}, [clientContext, onValueChange]);
|
|
5538
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2 relative", classNames?.addressInput), children: [
|
|
5539
|
+
/* @__PURE__ */ jsx(
|
|
5540
|
+
"input",
|
|
5541
|
+
{
|
|
5542
|
+
type: "text",
|
|
5543
|
+
value: inputText,
|
|
5544
|
+
onChange: (e) => handleInputChange(e.target.value),
|
|
5545
|
+
onFocus: () => {
|
|
5546
|
+
if (suggestions.length > 0) setIsOpen(true);
|
|
5547
|
+
},
|
|
5548
|
+
onBlur: () => {
|
|
5549
|
+
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
5550
|
+
},
|
|
5551
|
+
onKeyDown: (e) => {
|
|
5552
|
+
if (e.key === "Escape") {
|
|
5553
|
+
setIsOpen(false);
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
if (!isOpen || suggestions.length === 0) return;
|
|
5557
|
+
if (e.key === "ArrowDown") {
|
|
5558
|
+
e.preventDefault();
|
|
5559
|
+
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
5560
|
+
} else if (e.key === "ArrowUp") {
|
|
5561
|
+
e.preventDefault();
|
|
5562
|
+
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
5563
|
+
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
5564
|
+
e.preventDefault();
|
|
5565
|
+
void selectSuggestion(suggestions[highlightedIndex]);
|
|
5566
|
+
}
|
|
5567
|
+
},
|
|
5568
|
+
placeholder: field.placeholder || "Search for an address...",
|
|
5569
|
+
required: field.is_required,
|
|
5570
|
+
autoComplete: "off",
|
|
5571
|
+
"aria-autocomplete": "list",
|
|
5572
|
+
"aria-expanded": isOpen && suggestions.length > 0,
|
|
5573
|
+
className: inputClass
|
|
5574
|
+
}
|
|
5575
|
+
),
|
|
5576
|
+
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(
|
|
5577
|
+
"button",
|
|
5578
|
+
{
|
|
5579
|
+
type: "button",
|
|
5580
|
+
role: "option",
|
|
5581
|
+
"aria-selected": i === highlightedIndex,
|
|
5582
|
+
onMouseDown: (e) => {
|
|
5583
|
+
e.preventDefault();
|
|
5584
|
+
void selectSuggestion(s);
|
|
5585
|
+
},
|
|
5586
|
+
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"),
|
|
5587
|
+
children: s.description
|
|
5588
|
+
},
|
|
5589
|
+
s.place_id
|
|
5590
|
+
)) }),
|
|
5591
|
+
addr && /* @__PURE__ */ jsx(
|
|
5592
|
+
"input",
|
|
5593
|
+
{
|
|
5594
|
+
type: "text",
|
|
5595
|
+
value: addr.apartment || "",
|
|
5596
|
+
onChange: (e) => onValueChange({ ...addr, apartment: e.target.value || void 0 }),
|
|
5597
|
+
placeholder: "Apt, suite, unit (optional)",
|
|
5598
|
+
className: inputClass
|
|
5599
|
+
}
|
|
5600
|
+
)
|
|
5601
|
+
] });
|
|
5602
|
+
}
|
|
5603
|
+
function LocationInput({
|
|
5604
|
+
field,
|
|
5605
|
+
value,
|
|
5606
|
+
onValueChange,
|
|
5607
|
+
classNames,
|
|
5608
|
+
inputClass
|
|
5609
|
+
}) {
|
|
5610
|
+
const clientContext = useOptionalCimplifyClient();
|
|
5611
|
+
const loc = value && typeof value === "object" ? value : null;
|
|
5612
|
+
const [inputText, setInputText] = React10.useState(loc?.label ?? "");
|
|
5613
|
+
const [suggestions, setSuggestions] = React10.useState([]);
|
|
5614
|
+
const [isOpen, setIsOpen] = React10.useState(false);
|
|
5615
|
+
const [highlightedIndex, setHighlightedIndex] = React10.useState(-1);
|
|
5616
|
+
const debounceRef = React10.useRef(null);
|
|
5617
|
+
const seqRef = React10.useRef(0);
|
|
5618
|
+
const sessionTokenRef = React10.useRef(createSessionToken());
|
|
5619
|
+
const blurTimeoutRef = React10.useRef(null);
|
|
5620
|
+
React10.useEffect(() => {
|
|
5621
|
+
return () => {
|
|
5622
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5623
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
5624
|
+
seqRef.current += 1;
|
|
5625
|
+
};
|
|
5626
|
+
}, []);
|
|
5627
|
+
const fetchSuggestions = React10.useCallback(async (query) => {
|
|
5628
|
+
const client = clientContext?.client;
|
|
5629
|
+
if (!client || query.length < 3) {
|
|
5630
|
+
setSuggestions([]);
|
|
5631
|
+
setIsOpen(false);
|
|
5632
|
+
return;
|
|
5633
|
+
}
|
|
5634
|
+
const seq = ++seqRef.current;
|
|
5635
|
+
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
5636
|
+
if (seqRef.current !== seq) return;
|
|
5637
|
+
if (result.ok && result.value.predictions.length > 0) {
|
|
5638
|
+
setSuggestions(result.value.predictions);
|
|
5639
|
+
setHighlightedIndex(0);
|
|
5640
|
+
setIsOpen(true);
|
|
5641
|
+
} else {
|
|
5642
|
+
setSuggestions([]);
|
|
5643
|
+
setIsOpen(false);
|
|
5644
|
+
}
|
|
5645
|
+
}, [clientContext]);
|
|
5646
|
+
const handleInputChange = (nextValue) => {
|
|
5647
|
+
setInputText(nextValue);
|
|
5648
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5649
|
+
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
5650
|
+
};
|
|
5651
|
+
const selectSuggestion = React10.useCallback(async (suggestion) => {
|
|
5652
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
5653
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
5654
|
+
const label = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
5655
|
+
setInputText(label);
|
|
5656
|
+
setSuggestions([]);
|
|
5657
|
+
setIsOpen(false);
|
|
5658
|
+
const client = clientContext?.client;
|
|
5659
|
+
if (!client) return;
|
|
5660
|
+
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
5661
|
+
sessionTokenRef.current = createSessionToken();
|
|
5662
|
+
if (result.ok) {
|
|
5663
|
+
const d = result.value;
|
|
5664
|
+
onValueChange({
|
|
5665
|
+
latitude: d.latitude,
|
|
5666
|
+
longitude: d.longitude,
|
|
5667
|
+
label: d.formatted_address || label
|
|
5668
|
+
});
|
|
5669
|
+
setInputText(d.formatted_address || label);
|
|
5670
|
+
}
|
|
5671
|
+
}, [clientContext, onValueChange]);
|
|
5672
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", classNames?.locationInput), children: [
|
|
5673
|
+
/* @__PURE__ */ jsx(
|
|
5674
|
+
"input",
|
|
5675
|
+
{
|
|
5676
|
+
type: "text",
|
|
5677
|
+
value: inputText,
|
|
5678
|
+
onChange: (e) => handleInputChange(e.target.value),
|
|
5679
|
+
onFocus: () => {
|
|
5680
|
+
if (suggestions.length > 0) setIsOpen(true);
|
|
5681
|
+
},
|
|
5682
|
+
onBlur: () => {
|
|
5683
|
+
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
5684
|
+
},
|
|
5685
|
+
onKeyDown: (e) => {
|
|
5686
|
+
if (e.key === "Escape") {
|
|
5687
|
+
setIsOpen(false);
|
|
5688
|
+
return;
|
|
5689
|
+
}
|
|
5690
|
+
if (!isOpen || suggestions.length === 0) return;
|
|
5691
|
+
if (e.key === "ArrowDown") {
|
|
5692
|
+
e.preventDefault();
|
|
5693
|
+
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
5694
|
+
} else if (e.key === "ArrowUp") {
|
|
5695
|
+
e.preventDefault();
|
|
5696
|
+
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
5697
|
+
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
5698
|
+
e.preventDefault();
|
|
5699
|
+
void selectSuggestion(suggestions[highlightedIndex]);
|
|
5700
|
+
}
|
|
5701
|
+
},
|
|
5702
|
+
placeholder: field.placeholder || "Search for a location...",
|
|
5703
|
+
required: field.is_required,
|
|
5704
|
+
autoComplete: "off",
|
|
5705
|
+
"aria-autocomplete": "list",
|
|
5706
|
+
"aria-expanded": isOpen && suggestions.length > 0,
|
|
5707
|
+
className: inputClass
|
|
5708
|
+
}
|
|
5709
|
+
),
|
|
5710
|
+
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(
|
|
5711
|
+
"button",
|
|
5712
|
+
{
|
|
5713
|
+
type: "button",
|
|
5714
|
+
role: "option",
|
|
5715
|
+
"aria-selected": i === highlightedIndex,
|
|
5716
|
+
onMouseDown: (e) => {
|
|
5717
|
+
e.preventDefault();
|
|
5718
|
+
void selectSuggestion(s);
|
|
5719
|
+
},
|
|
5720
|
+
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"),
|
|
5721
|
+
children: s.description
|
|
5722
|
+
},
|
|
5723
|
+
s.place_id
|
|
5724
|
+
)) }),
|
|
5725
|
+
loc && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
5726
|
+
loc.latitude.toFixed(4),
|
|
5727
|
+
", ",
|
|
5728
|
+
loc.longitude.toFixed(4)
|
|
5729
|
+
] })
|
|
5730
|
+
] });
|
|
5731
|
+
}
|
|
5732
|
+
function PhoneInput({
|
|
5733
|
+
field,
|
|
5734
|
+
value,
|
|
5735
|
+
onValueChange,
|
|
5736
|
+
classNames,
|
|
5737
|
+
inputClass
|
|
5738
|
+
}) {
|
|
5739
|
+
const phone = value && typeof value === "object" ? value : null;
|
|
5740
|
+
const [code, setCode] = React10.useState(phone?.country_code ?? "+1");
|
|
5741
|
+
const [number, setNumber] = React10.useState(phone?.number ?? "");
|
|
5742
|
+
const update = (nextCode, nextNumber) => {
|
|
5743
|
+
setCode(nextCode);
|
|
5744
|
+
setNumber(nextNumber);
|
|
5745
|
+
if (nextNumber) {
|
|
5746
|
+
onValueChange({
|
|
5747
|
+
country_code: nextCode,
|
|
5748
|
+
number: nextNumber,
|
|
5749
|
+
formatted: `${nextCode} ${nextNumber}`
|
|
5750
|
+
});
|
|
5751
|
+
} else {
|
|
5752
|
+
onValueChange(void 0);
|
|
5753
|
+
}
|
|
5754
|
+
};
|
|
5755
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-2", classNames?.phoneInput), children: [
|
|
5756
|
+
/* @__PURE__ */ jsx(
|
|
5757
|
+
"input",
|
|
5758
|
+
{
|
|
5759
|
+
type: "text",
|
|
5760
|
+
value: code,
|
|
5761
|
+
onChange: (e) => update(e.target.value, number),
|
|
5762
|
+
placeholder: "+1",
|
|
5763
|
+
className: cn(inputClass, "w-20 shrink-0")
|
|
5764
|
+
}
|
|
5765
|
+
),
|
|
5766
|
+
/* @__PURE__ */ jsx(
|
|
5767
|
+
"input",
|
|
5768
|
+
{
|
|
5769
|
+
type: "tel",
|
|
5770
|
+
value: number,
|
|
5771
|
+
onChange: (e) => update(code, e.target.value),
|
|
5772
|
+
placeholder: field.placeholder || "Phone number",
|
|
5773
|
+
required: field.is_required,
|
|
5774
|
+
className: inputClass
|
|
5775
|
+
}
|
|
5776
|
+
)
|
|
5777
|
+
] });
|
|
5778
|
+
}
|
|
5779
|
+
function SignatureInput({
|
|
5780
|
+
field,
|
|
5781
|
+
value,
|
|
5782
|
+
onValueChange,
|
|
5783
|
+
classNames
|
|
5784
|
+
}) {
|
|
5785
|
+
const sig = value && typeof value === "object" ? value : null;
|
|
5786
|
+
const canvasRef = React10.useRef(null);
|
|
5787
|
+
const isDrawing = React10.useRef(false);
|
|
5788
|
+
const getCtx = () => canvasRef.current?.getContext("2d") ?? null;
|
|
5789
|
+
const startDraw = (e) => {
|
|
5790
|
+
const ctx = getCtx();
|
|
5791
|
+
if (!ctx) return;
|
|
5792
|
+
isDrawing.current = true;
|
|
5793
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
5794
|
+
ctx.beginPath();
|
|
5795
|
+
ctx.moveTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
5796
|
+
canvasRef.current.setPointerCapture(e.pointerId);
|
|
5797
|
+
};
|
|
5798
|
+
const draw = (e) => {
|
|
5799
|
+
if (!isDrawing.current) return;
|
|
5800
|
+
const ctx = getCtx();
|
|
5801
|
+
if (!ctx) return;
|
|
5802
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
5803
|
+
ctx.lineWidth = 2;
|
|
5804
|
+
ctx.lineCap = "round";
|
|
5805
|
+
const style = getComputedStyle(canvasRef.current);
|
|
5806
|
+
ctx.strokeStyle = style.color || "#000";
|
|
5807
|
+
ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
5808
|
+
ctx.stroke();
|
|
5809
|
+
};
|
|
5810
|
+
const endDraw = () => {
|
|
5811
|
+
if (!isDrawing.current) return;
|
|
5812
|
+
isDrawing.current = false;
|
|
5813
|
+
const canvas = canvasRef.current;
|
|
5814
|
+
if (!canvas) return;
|
|
5815
|
+
onValueChange({
|
|
5816
|
+
data_url: canvas.toDataURL("image/png"),
|
|
5817
|
+
signer_name: sig?.signer_name
|
|
5818
|
+
});
|
|
5819
|
+
};
|
|
5820
|
+
const clear = () => {
|
|
5821
|
+
const canvas = canvasRef.current;
|
|
5822
|
+
if (!canvas) return;
|
|
5823
|
+
const ctx = canvas.getContext("2d");
|
|
5824
|
+
if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
5825
|
+
onValueChange(void 0);
|
|
5826
|
+
};
|
|
5827
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", classNames?.signatureCanvas), children: [
|
|
5828
|
+
/* @__PURE__ */ jsxs("div", { className: "relative rounded-md border border-input overflow-hidden", children: [
|
|
5829
|
+
/* @__PURE__ */ jsx(
|
|
5830
|
+
"canvas",
|
|
5831
|
+
{
|
|
5832
|
+
ref: canvasRef,
|
|
5833
|
+
width: 400,
|
|
5834
|
+
height: 150,
|
|
5835
|
+
className: "w-full touch-none cursor-crosshair bg-background text-foreground",
|
|
5836
|
+
onPointerDown: startDraw,
|
|
5837
|
+
onPointerMove: draw,
|
|
5838
|
+
onPointerUp: endDraw,
|
|
5839
|
+
onPointerLeave: endDraw
|
|
5840
|
+
}
|
|
5841
|
+
),
|
|
5842
|
+
sig?.data_url && /* @__PURE__ */ jsx(
|
|
5843
|
+
"button",
|
|
5844
|
+
{
|
|
5845
|
+
type: "button",
|
|
5846
|
+
onClick: clear,
|
|
5847
|
+
className: "absolute top-1.5 right-1.5 text-xs text-muted-foreground hover:text-destructive transition-colors",
|
|
5848
|
+
children: "Clear"
|
|
5849
|
+
}
|
|
5850
|
+
)
|
|
5851
|
+
] }),
|
|
5852
|
+
!sig?.data_url && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: field.placeholder || "Draw your signature above" })
|
|
5853
|
+
] });
|
|
5854
|
+
}
|
|
5855
|
+
function createSessionToken() {
|
|
5856
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
5857
|
+
return crypto.randomUUID();
|
|
5858
|
+
}
|
|
5859
|
+
return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
5860
|
+
}
|
|
4851
5861
|
function ProductCustomizer({
|
|
4852
5862
|
product,
|
|
4853
5863
|
onAddToCart,
|
|
@@ -4868,6 +5878,7 @@ function ProductCustomizer({
|
|
|
4868
5878
|
const [bundleTotalPrice, setBundleTotalPrice] = useState(null);
|
|
4869
5879
|
const [bundleReady, setBundleReady] = useState(false);
|
|
4870
5880
|
const [selectedBillingPlan, setSelectedBillingPlan] = useState(null);
|
|
5881
|
+
const [customerInputValues, setCustomerInputValues] = useState({});
|
|
4871
5882
|
const [specialInstructions, setSpecialInstructions] = useState("");
|
|
4872
5883
|
const cart = useCart();
|
|
4873
5884
|
const productType = product.type || "product";
|
|
@@ -4892,6 +5903,7 @@ function ProductCustomizer({
|
|
|
4892
5903
|
setBundleTotalPrice(null);
|
|
4893
5904
|
setBundleReady(false);
|
|
4894
5905
|
setSelectedBillingPlan(null);
|
|
5906
|
+
setCustomerInputValues({});
|
|
4895
5907
|
setSpecialInstructions("");
|
|
4896
5908
|
}, [product.id, product.min_order_quantity]);
|
|
4897
5909
|
const selectedAddOnOptions = useMemo(() => {
|
|
@@ -4954,7 +5966,15 @@ function ProductCustomizer({
|
|
|
4954
5966
|
}),
|
|
4955
5967
|
[product.id, quantity, selectedVariantId, normalizedAddOnOptionIds, isBundle, bundleSelections, isComposite, compositeSelections]
|
|
4956
5968
|
);
|
|
4957
|
-
const
|
|
5969
|
+
const requiredInputsSatisfied = useMemo(() => {
|
|
5970
|
+
if (!product.input_fields || product.input_fields.length === 0) return true;
|
|
5971
|
+
return product.input_fields.every((field) => {
|
|
5972
|
+
if (!field.is_required) return true;
|
|
5973
|
+
const val = customerInputValues[field.id];
|
|
5974
|
+
return val !== void 0 && val !== "" && val !== null;
|
|
5975
|
+
});
|
|
5976
|
+
}, [product.input_fields, customerInputValues]);
|
|
5977
|
+
const quoteEnabled = isComposite ? compositeReady && requiredInputsSatisfied : isBundle ? bundleReady && requiredInputsSatisfied : requiredAddOnsSatisfied && requiredInputsSatisfied;
|
|
4958
5978
|
const { quote } = useQuote(quoteInput, {
|
|
4959
5979
|
enabled: quoteEnabled
|
|
4960
5980
|
});
|
|
@@ -4993,6 +6013,7 @@ function ProductCustomizer({
|
|
|
4993
6013
|
compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0,
|
|
4994
6014
|
bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0,
|
|
4995
6015
|
billingPlanId: selectedBillingPlan?.id,
|
|
6016
|
+
customerInputs: Object.keys(customerInputValues).length > 0 ? Object.entries(customerInputValues).filter(([, v]) => v !== void 0 && v !== "").map(([fieldId, value]) => ({ field_id: fieldId, value })) : void 0,
|
|
4996
6017
|
specialInstructions: specialInstructions.trim() || void 0
|
|
4997
6018
|
};
|
|
4998
6019
|
try {
|
|
@@ -5011,7 +6032,7 @@ function ProductCustomizer({
|
|
|
5011
6032
|
} finally {
|
|
5012
6033
|
setIsSubmitting(false);
|
|
5013
6034
|
}
|
|
5014
|
-
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, specialInstructions, isSubmitting, onAddToCart, cart]);
|
|
6035
|
+
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isSubmitting, onAddToCart, cart]);
|
|
5015
6036
|
return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
|
|
5016
6037
|
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsx(
|
|
5017
6038
|
CompositeSelector,
|
|
@@ -5072,6 +6093,14 @@ function ProductCustomizer({
|
|
|
5072
6093
|
currentQuantity: quantity
|
|
5073
6094
|
}
|
|
5074
6095
|
),
|
|
6096
|
+
product.input_fields && product.input_fields.length > 0 && /* @__PURE__ */ jsx(
|
|
6097
|
+
CustomerInputFields,
|
|
6098
|
+
{
|
|
6099
|
+
fields: product.input_fields,
|
|
6100
|
+
values: customerInputValues,
|
|
6101
|
+
onChange: setCustomerInputValues
|
|
6102
|
+
}
|
|
6103
|
+
),
|
|
5075
6104
|
product.deposit_type && product.deposit_type !== "none" && product.deposit_amount && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-deposit": true, className: cn("text-sm text-muted-foreground", classNames?.depositInfo), children: product.deposit_type === "fixed" ? /* @__PURE__ */ jsxs("span", { children: [
|
|
5076
6105
|
"Deposit required: ",
|
|
5077
6106
|
/* @__PURE__ */ jsx(Price, { amount: product.deposit_amount })
|
|
@@ -5091,9 +6120,7 @@ function ProductCustomizer({
|
|
|
5091
6120
|
)) }),
|
|
5092
6121
|
isService && product.duration_minutes != null && /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-duration": true, className: cn("text-sm text-muted-foreground", classNames?.duration), children: [
|
|
5093
6122
|
"Duration: ",
|
|
5094
|
-
product.duration_minutes,
|
|
5095
|
-
" ",
|
|
5096
|
-
product.duration_unit ?? "minutes"
|
|
6123
|
+
formatDuration2(product.duration_minutes, product.duration_unit)
|
|
5097
6124
|
] }),
|
|
5098
6125
|
showSpecialInstructions && !isDigital && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-special-instructions": true, children: /* @__PURE__ */ jsx(
|
|
5099
6126
|
"textarea",
|
|
@@ -5273,6 +6300,10 @@ function CartLineItemRow({
|
|
|
5273
6300
|
] }),
|
|
5274
6301
|
(hasComposite || hasBundle) && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-badge": true, children: hasComposite ? "Custom" : "Bundle" }),
|
|
5275
6302
|
item.specialInstructions && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-instructions": true, className: "text-xs text-muted-foreground", children: item.specialInstructions }),
|
|
6303
|
+
item.customerInputs && item.customerInputs.length > 0 && /* @__PURE__ */ jsx("div", { "data-cimplify-cart-customer-inputs": true, className: classNames?.customerInputs, children: item.customerInputs.map((input) => /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-input": true, children: [
|
|
6304
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-cart-input-label": true, children: input.field_name }),
|
|
6305
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-cart-input-value": true, children: input.field_type === INPUT_FIELD_TYPE.File || input.field_type === INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
|
|
6306
|
+
] }, input.field_id)) }),
|
|
5276
6307
|
(item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-service-info": true, className: classNames?.serviceInfo, children: [
|
|
5277
6308
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
5278
6309
|
"\u{1F4C5}",
|
|
@@ -6052,7 +7083,7 @@ function CompositeProductCard({
|
|
|
6052
7083
|
] })
|
|
6053
7084
|
] });
|
|
6054
7085
|
}
|
|
6055
|
-
function
|
|
7086
|
+
function formatDuration3(minutes, unit) {
|
|
6056
7087
|
if (unit && unit !== "minutes") return `${minutes} ${unit}`;
|
|
6057
7088
|
if (minutes >= 60) {
|
|
6058
7089
|
const h = Math.floor(minutes / 60);
|
|
@@ -6073,7 +7104,7 @@ function StandardServiceCard({
|
|
|
6073
7104
|
return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
|
|
6074
7105
|
/* @__PURE__ */ jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
|
|
6075
7106
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
|
|
6076
|
-
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "absolute bottom-3 left-3 inline-flex items-center gap-1 text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children:
|
|
7107
|
+
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "absolute bottom-3 left-3 inline-flex items-center gap-1 text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children: formatDuration3(product.duration_minutes, product.duration_unit) }),
|
|
6077
7108
|
/* @__PURE__ */ jsx("span", { className: "absolute bottom-3 right-3 text-white font-bold text-lg drop-shadow-sm", children: /* @__PURE__ */ jsx(Price, { amount: product.default_price }) })
|
|
6078
7109
|
] }),
|
|
6079
7110
|
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
@@ -6107,7 +7138,7 @@ function StandardServiceCard({
|
|
|
6107
7138
|
] })
|
|
6108
7139
|
] });
|
|
6109
7140
|
}
|
|
6110
|
-
function
|
|
7141
|
+
function formatDuration4(minutes, unit) {
|
|
6111
7142
|
if (unit && unit !== "minutes") return `${minutes} ${unit}`;
|
|
6112
7143
|
if (minutes >= 60) {
|
|
6113
7144
|
const h = Math.floor(minutes / 60);
|
|
@@ -6132,7 +7163,7 @@ function CompactServiceCard({
|
|
|
6132
7163
|
/* @__PURE__ */ jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
|
|
6133
7164
|
product.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
|
|
6134
7165
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2 flex-wrap", children: [
|
|
6135
|
-
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "text-[10.5px] font-medium text-muted-foreground px-1.5 py-0.5 bg-muted rounded", children:
|
|
7166
|
+
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "text-[10.5px] font-medium text-muted-foreground px-1.5 py-0.5 bg-muted rounded", children: formatDuration4(product.duration_minutes, product.duration_unit) }),
|
|
6136
7167
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[10.5px] font-medium text-emerald-600", children: [
|
|
6137
7168
|
/* @__PURE__ */ jsx("span", { className: "w-[6px] h-[6px] rounded-full bg-emerald-500" }),
|
|
6138
7169
|
"Available"
|
|
@@ -6172,7 +7203,7 @@ function ScheduleServiceCard({
|
|
|
6172
7203
|
}) {
|
|
6173
7204
|
const image = product.image_url || product.images?.[0] || "";
|
|
6174
7205
|
const href = `/products/${product.slug}`;
|
|
6175
|
-
const durationLabel = product.duration_minutes != null ?
|
|
7206
|
+
const durationLabel = product.duration_minutes != null ? formatDuration2(product.duration_minutes, product.duration_unit) : null;
|
|
6176
7207
|
const displaySlots = (slots || []).slice(0, 3);
|
|
6177
7208
|
const shellClass = cn(
|
|
6178
7209
|
"group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden",
|
|
@@ -6225,7 +7256,7 @@ function ScheduleServiceCard({
|
|
|
6225
7256
|
}
|
|
6226
7257
|
return /* @__PURE__ */ jsx("a", { href, className: shellClass, "data-cimplify-card": true, children: inner });
|
|
6227
7258
|
}
|
|
6228
|
-
var
|
|
7259
|
+
var UNIT_LABELS2 = {
|
|
6229
7260
|
minutes: "min",
|
|
6230
7261
|
hours: "hr",
|
|
6231
7262
|
days: "day",
|
|
@@ -6243,7 +7274,7 @@ function RentalServiceCard({
|
|
|
6243
7274
|
}) {
|
|
6244
7275
|
const image = product.image_url || product.images?.[0] || "";
|
|
6245
7276
|
const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
|
|
6246
|
-
const unit = product.duration_unit ?
|
|
7277
|
+
const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
|
|
6247
7278
|
const status = product.inventory_status;
|
|
6248
7279
|
return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
|
|
6249
7280
|
/* @__PURE__ */ jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
|
|
@@ -6363,7 +7394,7 @@ function AccommodationCard({
|
|
|
6363
7394
|
] })
|
|
6364
7395
|
] });
|
|
6365
7396
|
}
|
|
6366
|
-
var
|
|
7397
|
+
var UNIT_LABELS3 = {
|
|
6367
7398
|
weeks: "week",
|
|
6368
7399
|
months: "mo",
|
|
6369
7400
|
years: "yr"
|
|
@@ -6378,7 +7409,7 @@ function LeaseServiceCard({
|
|
|
6378
7409
|
const image = product.image_url || product.images?.[0] || "";
|
|
6379
7410
|
const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
|
|
6380
7411
|
const hasTiers = product.quantity_pricing && product.quantity_pricing.length > 1;
|
|
6381
|
-
const unit = product.duration_unit ?
|
|
7412
|
+
const unit = product.duration_unit ? UNIT_LABELS3[product.duration_unit] || product.duration_unit : "mo";
|
|
6382
7413
|
const tags = product.tags || [];
|
|
6383
7414
|
const cancellationHours = product.cancellation_window_minutes ? Math.floor(product.cancellation_window_minutes / 60) : null;
|
|
6384
7415
|
return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
|
|
@@ -6436,6 +7467,7 @@ function LeaseServiceCard({
|
|
|
6436
7467
|
}
|
|
6437
7468
|
var FREQUENCY_LABELS2 = {
|
|
6438
7469
|
weekly: "/week",
|
|
7470
|
+
biweekly: "/2wk",
|
|
6439
7471
|
monthly: "/mo",
|
|
6440
7472
|
quarterly: "/qtr",
|
|
6441
7473
|
annually: "/yr"
|
|
@@ -6741,13 +7773,13 @@ function ProductGrid({
|
|
|
6741
7773
|
className,
|
|
6742
7774
|
classNames
|
|
6743
7775
|
}) {
|
|
6744
|
-
const rawId =
|
|
7776
|
+
const rawId = React10.useId();
|
|
6745
7777
|
const gridId = `cimplify-grid-${rawId.replace(/:/g, "")}`;
|
|
6746
7778
|
const sm = columns?.sm ?? 1;
|
|
6747
7779
|
const md = columns?.md ?? 2;
|
|
6748
7780
|
const lg = columns?.lg ?? 3;
|
|
6749
7781
|
const xl = columns?.xl ?? 4;
|
|
6750
|
-
const css =
|
|
7782
|
+
const css = React10.useMemo(
|
|
6751
7783
|
() => [
|
|
6752
7784
|
`#${gridId}{display:grid;grid-template-columns:repeat(${sm},1fr);gap:1rem}`,
|
|
6753
7785
|
`@media(min-width:768px){#${gridId}{grid-template-columns:repeat(${md},1fr)}}`,
|
|
@@ -7108,7 +8140,7 @@ function CategoryGrid({
|
|
|
7108
8140
|
enabled: categoriesProp === void 0
|
|
7109
8141
|
});
|
|
7110
8142
|
const categories = categoriesProp ?? fetched;
|
|
7111
|
-
const rawId =
|
|
8143
|
+
const rawId = React10.useId();
|
|
7112
8144
|
const gridId = `cimplify-cat-grid-${rawId.replace(/:/g, "")}`;
|
|
7113
8145
|
const sm = columns?.sm ?? 2;
|
|
7114
8146
|
const md = columns?.md ?? 3;
|
|
@@ -7334,7 +8366,7 @@ function OrderSummary({
|
|
|
7334
8366
|
/* @__PURE__ */ jsx("p", { children: order.delivery_address })
|
|
7335
8367
|
] }),
|
|
7336
8368
|
/* @__PURE__ */ jsx("div", { "data-cimplify-order-items": true, className: classNames?.items, children: order.items.map(
|
|
7337
|
-
(item) => renderLineItem ? /* @__PURE__ */ jsx(
|
|
8369
|
+
(item) => renderLineItem ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderLineItem(item) }, item.id) : /* @__PURE__ */ jsxs("div", { "data-cimplify-order-line-item": true, className: classNames?.lineItem, children: [
|
|
7338
8370
|
/* @__PURE__ */ jsxs("div", { "data-cimplify-order-line-info": true, children: [
|
|
7339
8371
|
/* @__PURE__ */ jsxs("span", { "data-cimplify-order-line-qty": true, children: [
|
|
7340
8372
|
item.quantity,
|
|
@@ -8315,7 +9347,7 @@ function DefaultCatalogueLayout({
|
|
|
8315
9347
|
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium", children: "No products found" }),
|
|
8316
9348
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try adjusting your filters" })
|
|
8317
9349
|
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-4", children: products.map(
|
|
8318
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
9350
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
8319
9351
|
ProductCard,
|
|
8320
9352
|
{
|
|
8321
9353
|
product,
|
|
@@ -8422,7 +9454,7 @@ function CompactCatalogueLayout({
|
|
|
8422
9454
|
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium", children: "No items found" }),
|
|
8423
9455
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different category or search" })
|
|
8424
9456
|
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-5", children: products.map(
|
|
8425
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
9457
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
8426
9458
|
ProductCard,
|
|
8427
9459
|
{
|
|
8428
9460
|
product,
|
|
@@ -8445,8 +9477,8 @@ var BUILT_IN_LAYOUTS = {
|
|
|
8445
9477
|
["compact" /* Compact */]: CompactCatalogueLayout
|
|
8446
9478
|
};
|
|
8447
9479
|
function useDebounce(value, delay) {
|
|
8448
|
-
const [debounced, setDebounced] =
|
|
8449
|
-
|
|
9480
|
+
const [debounced, setDebounced] = React10.useState(value);
|
|
9481
|
+
React10.useEffect(() => {
|
|
8450
9482
|
const timer = setTimeout(() => setDebounced(value), delay);
|
|
8451
9483
|
return () => clearTimeout(timer);
|
|
8452
9484
|
}, [value, delay]);
|
|
@@ -8938,15 +9970,6 @@ function WholesaleProductLayout({
|
|
|
8938
9970
|
)
|
|
8939
9971
|
] });
|
|
8940
9972
|
}
|
|
8941
|
-
function formatMinutes(minutes) {
|
|
8942
|
-
if (minutes >= 1440) return `${Math.floor(minutes / 1440)} day${minutes >= 2880 ? "s" : ""}`;
|
|
8943
|
-
if (minutes >= 60) {
|
|
8944
|
-
const h = Math.floor(minutes / 60);
|
|
8945
|
-
const m = minutes % 60;
|
|
8946
|
-
return m > 0 ? `${h}h ${m}min` : `${h} hour${h > 1 ? "s" : ""}`;
|
|
8947
|
-
}
|
|
8948
|
-
return `${minutes} min`;
|
|
8949
|
-
}
|
|
8950
9973
|
function ServiceProductLayout({
|
|
8951
9974
|
product,
|
|
8952
9975
|
onAddToCart,
|
|
@@ -8978,7 +10001,7 @@ function ServiceProductLayout({
|
|
|
8978
10001
|
/* @__PURE__ */ jsx(Price, { amount: product.default_price, className: "text-2xl font-bold text-primary mt-2 block" })
|
|
8979
10002
|
] }),
|
|
8980
10003
|
/* @__PURE__ */ jsxs("div", { "data-cimplify-product-layout-service-info": true, className: "flex flex-wrap gap-2", children: [
|
|
8981
|
-
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5 px-3 py-1.5 bg-muted text-sm text-muted-foreground rounded-full", children:
|
|
10004
|
+
product.duration_minutes != null && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5 px-3 py-1.5 bg-muted text-sm text-muted-foreground rounded-full", children: formatDuration2(product.duration_minutes, product.duration_unit) }),
|
|
8982
10005
|
hasDeposit && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-3 py-1.5 bg-muted text-sm text-muted-foreground rounded-full", children: [
|
|
8983
10006
|
/* @__PURE__ */ jsx(Price, { amount: depositAmount }),
|
|
8984
10007
|
" deposit"
|
|
@@ -9015,7 +10038,7 @@ function ServiceProductLayout({
|
|
|
9015
10038
|
cancellationHours,
|
|
9016
10039
|
" hours"
|
|
9017
10040
|
] }),
|
|
9018
|
-
" before the
|
|
10041
|
+
" before the booking"
|
|
9019
10042
|
] }),
|
|
9020
10043
|
product.no_show_fee && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
9021
10044
|
"No-show fee: ",
|
|
@@ -9716,7 +10739,7 @@ function DefaultCollectionLayout({
|
|
|
9716
10739
|
] })
|
|
9717
10740
|
] }),
|
|
9718
10741
|
filtered.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4", children: filtered.map(
|
|
9719
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
10742
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
9720
10743
|
ProductCard,
|
|
9721
10744
|
{
|
|
9722
10745
|
product,
|
|
@@ -9769,7 +10792,7 @@ function FeaturedCollectionLayout({
|
|
|
9769
10792
|
] })
|
|
9770
10793
|
] }),
|
|
9771
10794
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: products.map(
|
|
9772
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
10795
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
9773
10796
|
ProductCard,
|
|
9774
10797
|
{
|
|
9775
10798
|
product,
|
|
@@ -9893,7 +10916,7 @@ function CatalogueCollectionLayout({
|
|
|
9893
10916
|
] })
|
|
9894
10917
|
] }),
|
|
9895
10918
|
filtered.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4", children: filtered.map(
|
|
9896
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
10919
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
9897
10920
|
ProductCard,
|
|
9898
10921
|
{
|
|
9899
10922
|
product,
|
|
@@ -10273,7 +11296,7 @@ function DefaultSearchLayout({
|
|
|
10273
11296
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different search term" }),
|
|
10274
11297
|
/* @__PURE__ */ jsx("button", { onClick: clear, className: "text-sm font-medium text-primary mt-4 hover:underline", children: "Clear search" })
|
|
10275
11298
|
] }) : results.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-4", children: results.map(
|
|
10276
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
11299
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
10277
11300
|
ProductCard,
|
|
10278
11301
|
{
|
|
10279
11302
|
product,
|
|
@@ -10375,7 +11398,7 @@ function CompactSearchLayout({
|
|
|
10375
11398
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different search term" }),
|
|
10376
11399
|
/* @__PURE__ */ jsx("button", { onClick: clear, className: "text-sm font-medium text-primary mt-4 hover:underline", children: "Clear search" })
|
|
10377
11400
|
] }) : results.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-5", children: results.map(
|
|
10378
|
-
(product) => renderCard ? /* @__PURE__ */ jsx(
|
|
11401
|
+
(product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
|
|
10379
11402
|
ProductCard,
|
|
10380
11403
|
{
|
|
10381
11404
|
product,
|
|
@@ -10939,5 +11962,491 @@ function ResourcePicker({
|
|
|
10939
11962
|
}
|
|
10940
11963
|
);
|
|
10941
11964
|
}
|
|
11965
|
+
function ChatWidget({
|
|
11966
|
+
client: clientProp,
|
|
11967
|
+
businessName = "Support",
|
|
11968
|
+
greeting = "Hi there!",
|
|
11969
|
+
subtitle = "Ask us anything \u2014 we typically reply in a few seconds.",
|
|
11970
|
+
starters,
|
|
11971
|
+
placeholder = "Type a message\u2026",
|
|
11972
|
+
position = "bottom-right",
|
|
11973
|
+
defaultOpen = false,
|
|
11974
|
+
pollInterval,
|
|
11975
|
+
className,
|
|
11976
|
+
classNames
|
|
11977
|
+
}) {
|
|
11978
|
+
const context = useOptionalCimplifyClient();
|
|
11979
|
+
const client = clientProp ?? context?.client;
|
|
11980
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
11981
|
+
const [unread, setUnread] = useState(0);
|
|
11982
|
+
const { messages, isLoading, isSending, error, isActive, send, startConversation } = useChat({ client, pollInterval });
|
|
11983
|
+
const prevCountRef = useRef(messages.length);
|
|
11984
|
+
useEffect(() => {
|
|
11985
|
+
if (!open && messages.length > prevCountRef.current) {
|
|
11986
|
+
const newMessages = messages.slice(prevCountRef.current);
|
|
11987
|
+
const incomingCount = newMessages.filter(
|
|
11988
|
+
(m) => m.sender_type !== "customer"
|
|
11989
|
+
).length;
|
|
11990
|
+
if (incomingCount > 0) setUnread((n) => n + incomingCount);
|
|
11991
|
+
}
|
|
11992
|
+
prevCountRef.current = messages.length;
|
|
11993
|
+
}, [messages, open]);
|
|
11994
|
+
const handleOpen = useCallback(() => {
|
|
11995
|
+
setOpen(true);
|
|
11996
|
+
setUnread(0);
|
|
11997
|
+
}, []);
|
|
11998
|
+
const handleClose = useCallback(() => setOpen(false), []);
|
|
11999
|
+
const isLeft = position === "bottom-left";
|
|
12000
|
+
return /* @__PURE__ */ jsxs(
|
|
12001
|
+
"div",
|
|
12002
|
+
{
|
|
12003
|
+
className: cn(
|
|
12004
|
+
"fixed bottom-6 z-[9999]",
|
|
12005
|
+
isLeft ? "left-6" : "right-6",
|
|
12006
|
+
className,
|
|
12007
|
+
classNames?.root
|
|
12008
|
+
),
|
|
12009
|
+
children: [
|
|
12010
|
+
!open && /* @__PURE__ */ jsx(
|
|
12011
|
+
Bubble,
|
|
12012
|
+
{
|
|
12013
|
+
unread,
|
|
12014
|
+
onClick: handleOpen,
|
|
12015
|
+
className: classNames?.bubble
|
|
12016
|
+
}
|
|
12017
|
+
),
|
|
12018
|
+
open && /* @__PURE__ */ jsx(
|
|
12019
|
+
Panel,
|
|
12020
|
+
{
|
|
12021
|
+
businessName,
|
|
12022
|
+
greeting,
|
|
12023
|
+
subtitle,
|
|
12024
|
+
starters,
|
|
12025
|
+
placeholder,
|
|
12026
|
+
messages,
|
|
12027
|
+
isLoading,
|
|
12028
|
+
isSending,
|
|
12029
|
+
error,
|
|
12030
|
+
isActive,
|
|
12031
|
+
isLeft,
|
|
12032
|
+
onClose: handleClose,
|
|
12033
|
+
onSend: send,
|
|
12034
|
+
onStartConversation: startConversation,
|
|
12035
|
+
classNames
|
|
12036
|
+
}
|
|
12037
|
+
)
|
|
12038
|
+
]
|
|
12039
|
+
}
|
|
12040
|
+
);
|
|
12041
|
+
}
|
|
12042
|
+
function Bubble({
|
|
12043
|
+
unread,
|
|
12044
|
+
onClick,
|
|
12045
|
+
className
|
|
12046
|
+
}) {
|
|
12047
|
+
return /* @__PURE__ */ jsxs(
|
|
12048
|
+
"button",
|
|
12049
|
+
{
|
|
12050
|
+
type: "button",
|
|
12051
|
+
onClick,
|
|
12052
|
+
"aria-label": "Open chat",
|
|
12053
|
+
className: cn(
|
|
12054
|
+
"relative flex h-14 w-14 items-center justify-center rounded-full",
|
|
12055
|
+
"bg-foreground text-background shadow-lg",
|
|
12056
|
+
"transition-transform duration-300 ease-out",
|
|
12057
|
+
"hover:scale-[1.08] active:scale-95",
|
|
12058
|
+
className
|
|
12059
|
+
),
|
|
12060
|
+
children: [
|
|
12061
|
+
/* @__PURE__ */ jsx(ChatIcon, { className: "h-6 w-6" }),
|
|
12062
|
+
unread > 0 && /* @__PURE__ */ jsx(
|
|
12063
|
+
"span",
|
|
12064
|
+
{
|
|
12065
|
+
className: cn(
|
|
12066
|
+
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
12067
|
+
"rounded-full bg-red-500 text-[10px] font-bold text-white",
|
|
12068
|
+
"ring-2 ring-background"
|
|
12069
|
+
),
|
|
12070
|
+
children: unread > 9 ? "9+" : unread
|
|
12071
|
+
}
|
|
12072
|
+
)
|
|
12073
|
+
]
|
|
12074
|
+
}
|
|
12075
|
+
);
|
|
12076
|
+
}
|
|
12077
|
+
function Panel({
|
|
12078
|
+
businessName,
|
|
12079
|
+
greeting,
|
|
12080
|
+
subtitle,
|
|
12081
|
+
starters,
|
|
12082
|
+
placeholder,
|
|
12083
|
+
messages,
|
|
12084
|
+
isLoading,
|
|
12085
|
+
isSending,
|
|
12086
|
+
error,
|
|
12087
|
+
isActive,
|
|
12088
|
+
isLeft,
|
|
12089
|
+
onClose,
|
|
12090
|
+
onSend,
|
|
12091
|
+
onStartConversation,
|
|
12092
|
+
classNames
|
|
12093
|
+
}) {
|
|
12094
|
+
return /* @__PURE__ */ jsxs(
|
|
12095
|
+
"div",
|
|
12096
|
+
{
|
|
12097
|
+
className: cn(
|
|
12098
|
+
"flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
|
|
12099
|
+
"w-[400px] max-w-[calc(100vw-3rem)]",
|
|
12100
|
+
"h-[min(600px,calc(100vh-6rem))]",
|
|
12101
|
+
"animate-in fade-in slide-in-from-bottom-4 duration-300",
|
|
12102
|
+
isLeft ? "origin-bottom-left" : "origin-bottom-right",
|
|
12103
|
+
classNames?.panel
|
|
12104
|
+
),
|
|
12105
|
+
children: [
|
|
12106
|
+
/* @__PURE__ */ jsx(
|
|
12107
|
+
Header,
|
|
12108
|
+
{
|
|
12109
|
+
businessName,
|
|
12110
|
+
onClose,
|
|
12111
|
+
className: classNames?.header
|
|
12112
|
+
}
|
|
12113
|
+
),
|
|
12114
|
+
!isActive && !isLoading ? /* @__PURE__ */ jsx(
|
|
12115
|
+
Welcome,
|
|
12116
|
+
{
|
|
12117
|
+
greeting,
|
|
12118
|
+
subtitle,
|
|
12119
|
+
starters,
|
|
12120
|
+
onStarter: onStartConversation,
|
|
12121
|
+
className: classNames?.welcome
|
|
12122
|
+
}
|
|
12123
|
+
) : /* @__PURE__ */ jsx(
|
|
12124
|
+
MessageList,
|
|
12125
|
+
{
|
|
12126
|
+
messages,
|
|
12127
|
+
isLoading,
|
|
12128
|
+
isSending,
|
|
12129
|
+
className: classNames?.messages
|
|
12130
|
+
}
|
|
12131
|
+
),
|
|
12132
|
+
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." }),
|
|
12133
|
+
(isActive || isLoading) && /* @__PURE__ */ jsx(
|
|
12134
|
+
ChatInput,
|
|
12135
|
+
{
|
|
12136
|
+
placeholder,
|
|
12137
|
+
isSending,
|
|
12138
|
+
onSend,
|
|
12139
|
+
className: classNames?.input
|
|
12140
|
+
}
|
|
12141
|
+
),
|
|
12142
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-background px-4 py-1.5 text-center text-[10px] text-muted-foreground", children: [
|
|
12143
|
+
"Powered by",
|
|
12144
|
+
" ",
|
|
12145
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
|
|
12146
|
+
] })
|
|
12147
|
+
]
|
|
12148
|
+
}
|
|
12149
|
+
);
|
|
12150
|
+
}
|
|
12151
|
+
function Header({
|
|
12152
|
+
businessName,
|
|
12153
|
+
onClose,
|
|
12154
|
+
className
|
|
12155
|
+
}) {
|
|
12156
|
+
return /* @__PURE__ */ jsxs(
|
|
12157
|
+
"div",
|
|
12158
|
+
{
|
|
12159
|
+
className: cn(
|
|
12160
|
+
"flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
|
|
12161
|
+
className
|
|
12162
|
+
),
|
|
12163
|
+
children: [
|
|
12164
|
+
/* @__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" }) }),
|
|
12165
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
12166
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
12167
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
|
|
12168
|
+
/* @__PURE__ */ jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
12169
|
+
"Online"
|
|
12170
|
+
] })
|
|
12171
|
+
] }),
|
|
12172
|
+
/* @__PURE__ */ jsx(
|
|
12173
|
+
"button",
|
|
12174
|
+
{
|
|
12175
|
+
type: "button",
|
|
12176
|
+
onClick: onClose,
|
|
12177
|
+
"aria-label": "Close chat",
|
|
12178
|
+
className: cn(
|
|
12179
|
+
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
12180
|
+
"bg-white/10 text-white/70 transition-colors hover:bg-white/20 hover:text-white"
|
|
12181
|
+
),
|
|
12182
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
12183
|
+
}
|
|
12184
|
+
)
|
|
12185
|
+
]
|
|
12186
|
+
}
|
|
12187
|
+
);
|
|
12188
|
+
}
|
|
12189
|
+
function Welcome({
|
|
12190
|
+
greeting,
|
|
12191
|
+
subtitle,
|
|
12192
|
+
starters,
|
|
12193
|
+
onStarter,
|
|
12194
|
+
className
|
|
12195
|
+
}) {
|
|
12196
|
+
const defaultStarters = [
|
|
12197
|
+
{ icon: "\u{1F50D}", text: "Help me find a product" },
|
|
12198
|
+
{ icon: "\u{1F6D2}", text: "Check my cart" },
|
|
12199
|
+
{ icon: "\u{1F4E6}", text: "Track my order" }
|
|
12200
|
+
];
|
|
12201
|
+
const items = starters ?? defaultStarters;
|
|
12202
|
+
return /* @__PURE__ */ jsxs(
|
|
12203
|
+
"div",
|
|
12204
|
+
{
|
|
12205
|
+
className: cn(
|
|
12206
|
+
"flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
|
|
12207
|
+
className
|
|
12208
|
+
),
|
|
12209
|
+
children: [
|
|
12210
|
+
/* @__PURE__ */ jsx("div", { className: "mb-5 flex h-16 w-16 items-center justify-center rounded-2xl bg-muted", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-7 w-7 text-muted-foreground" }) }),
|
|
12211
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
|
|
12212
|
+
/* @__PURE__ */ jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
|
|
12213
|
+
/* @__PURE__ */ jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxs(
|
|
12214
|
+
"button",
|
|
12215
|
+
{
|
|
12216
|
+
type: "button",
|
|
12217
|
+
onClick: () => onStarter(s.text),
|
|
12218
|
+
className: cn(
|
|
12219
|
+
"flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
|
|
12220
|
+
"text-left text-[13px] font-medium text-foreground",
|
|
12221
|
+
"transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
|
|
12222
|
+
),
|
|
12223
|
+
children: [
|
|
12224
|
+
s.icon && /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-muted text-[15px]", children: s.icon }),
|
|
12225
|
+
/* @__PURE__ */ jsx("span", { children: s.text })
|
|
12226
|
+
]
|
|
12227
|
+
},
|
|
12228
|
+
s.text
|
|
12229
|
+
)) })
|
|
12230
|
+
]
|
|
12231
|
+
}
|
|
12232
|
+
);
|
|
12233
|
+
}
|
|
12234
|
+
function MessageList({
|
|
12235
|
+
messages,
|
|
12236
|
+
isLoading,
|
|
12237
|
+
isSending,
|
|
12238
|
+
className
|
|
12239
|
+
}) {
|
|
12240
|
+
const bottomRef = useRef(null);
|
|
12241
|
+
useEffect(() => {
|
|
12242
|
+
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
12243
|
+
}, [messages.length, isSending]);
|
|
12244
|
+
return /* @__PURE__ */ jsxs(
|
|
12245
|
+
"div",
|
|
12246
|
+
{
|
|
12247
|
+
className: cn(
|
|
12248
|
+
"flex flex-1 flex-col gap-1 overflow-y-auto bg-muted/50 px-5 py-5",
|
|
12249
|
+
className
|
|
12250
|
+
),
|
|
12251
|
+
children: [
|
|
12252
|
+
isLoading && messages.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
12253
|
+
messages.map((msg, i) => {
|
|
12254
|
+
const isCustomer = msg.sender_type === "customer";
|
|
12255
|
+
const prev = messages[i - 1];
|
|
12256
|
+
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
12257
|
+
const isOptimistic = msg.id.startsWith("opt_");
|
|
12258
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
12259
|
+
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsx("div", { className: "h-3" }),
|
|
12260
|
+
/* @__PURE__ */ jsx(
|
|
12261
|
+
"div",
|
|
12262
|
+
{
|
|
12263
|
+
className: cn(
|
|
12264
|
+
"flex max-w-[85%]",
|
|
12265
|
+
isCustomer ? "ml-auto" : "mr-auto"
|
|
12266
|
+
),
|
|
12267
|
+
children: /* @__PURE__ */ jsxs(
|
|
12268
|
+
"div",
|
|
12269
|
+
{
|
|
12270
|
+
className: cn(
|
|
12271
|
+
"rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
|
|
12272
|
+
isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
|
|
12273
|
+
isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
|
|
12274
|
+
isOptimistic && "opacity-70"
|
|
12275
|
+
),
|
|
12276
|
+
children: [
|
|
12277
|
+
!isCustomer && !sameSenderAsPrev && /* @__PURE__ */ jsxs("span", { className: "mb-1 inline-flex items-center gap-1 rounded bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold text-violet-600", children: [
|
|
12278
|
+
/* @__PURE__ */ jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
12279
|
+
"AI"
|
|
12280
|
+
] }),
|
|
12281
|
+
/* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content })
|
|
12282
|
+
]
|
|
12283
|
+
}
|
|
12284
|
+
)
|
|
12285
|
+
}
|
|
12286
|
+
)
|
|
12287
|
+
] }, msg.id);
|
|
12288
|
+
}),
|
|
12289
|
+
isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
12290
|
+
/* @__PURE__ */ jsx("div", { ref: bottomRef })
|
|
12291
|
+
]
|
|
12292
|
+
}
|
|
12293
|
+
);
|
|
12294
|
+
}
|
|
12295
|
+
function ChatInput({
|
|
12296
|
+
placeholder,
|
|
12297
|
+
isSending,
|
|
12298
|
+
onSend,
|
|
12299
|
+
className
|
|
12300
|
+
}) {
|
|
12301
|
+
const [value, setValue] = useState("");
|
|
12302
|
+
const textareaRef = useRef(null);
|
|
12303
|
+
const handleSend = useCallback(() => {
|
|
12304
|
+
const trimmed = value.trim();
|
|
12305
|
+
if (!trimmed || isSending) return;
|
|
12306
|
+
setValue("");
|
|
12307
|
+
if (textareaRef.current) {
|
|
12308
|
+
textareaRef.current.style.height = "auto";
|
|
12309
|
+
}
|
|
12310
|
+
onSend(trimmed);
|
|
12311
|
+
}, [value, isSending, onSend]);
|
|
12312
|
+
const handleKeyDown = useCallback(
|
|
12313
|
+
(e) => {
|
|
12314
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
12315
|
+
e.preventDefault();
|
|
12316
|
+
handleSend();
|
|
12317
|
+
}
|
|
12318
|
+
},
|
|
12319
|
+
[handleSend]
|
|
12320
|
+
);
|
|
12321
|
+
const handleInput = useCallback(() => {
|
|
12322
|
+
const el = textareaRef.current;
|
|
12323
|
+
if (!el) return;
|
|
12324
|
+
el.style.height = "auto";
|
|
12325
|
+
el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
|
|
12326
|
+
}, []);
|
|
12327
|
+
const hasContent = value.trim().length > 0;
|
|
12328
|
+
return /* @__PURE__ */ jsxs(
|
|
12329
|
+
"div",
|
|
12330
|
+
{
|
|
12331
|
+
className: cn(
|
|
12332
|
+
"flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
|
|
12333
|
+
className
|
|
12334
|
+
),
|
|
12335
|
+
children: [
|
|
12336
|
+
/* @__PURE__ */ jsx(
|
|
12337
|
+
"div",
|
|
12338
|
+
{
|
|
12339
|
+
className: cn(
|
|
12340
|
+
"flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
|
|
12341
|
+
"transition-colors focus-within:border-foreground focus-within:bg-background"
|
|
12342
|
+
),
|
|
12343
|
+
children: /* @__PURE__ */ jsx(
|
|
12344
|
+
"textarea",
|
|
12345
|
+
{
|
|
12346
|
+
ref: textareaRef,
|
|
12347
|
+
value,
|
|
12348
|
+
onChange: (e) => setValue(e.target.value),
|
|
12349
|
+
onKeyDown: handleKeyDown,
|
|
12350
|
+
onInput: handleInput,
|
|
12351
|
+
placeholder,
|
|
12352
|
+
rows: 1,
|
|
12353
|
+
className: cn(
|
|
12354
|
+
"max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
|
|
12355
|
+
"text-sm text-foreground outline-none placeholder:text-muted-foreground"
|
|
12356
|
+
)
|
|
12357
|
+
}
|
|
12358
|
+
)
|
|
12359
|
+
}
|
|
12360
|
+
),
|
|
12361
|
+
/* @__PURE__ */ jsx(
|
|
12362
|
+
"button",
|
|
12363
|
+
{
|
|
12364
|
+
type: "button",
|
|
12365
|
+
onClick: handleSend,
|
|
12366
|
+
disabled: !hasContent || isSending,
|
|
12367
|
+
"aria-label": "Send message",
|
|
12368
|
+
className: cn(
|
|
12369
|
+
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
12370
|
+
"transition-all duration-200",
|
|
12371
|
+
hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
|
|
12372
|
+
),
|
|
12373
|
+
children: /* @__PURE__ */ jsx(SendIcon, { className: "h-[18px] w-[18px]" })
|
|
12374
|
+
}
|
|
12375
|
+
)
|
|
12376
|
+
]
|
|
12377
|
+
}
|
|
12378
|
+
);
|
|
12379
|
+
}
|
|
12380
|
+
function TypingDots() {
|
|
12381
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
|
|
12382
|
+
"span",
|
|
12383
|
+
{
|
|
12384
|
+
className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
|
|
12385
|
+
style: { animationDelay: `${i * 160}ms` }
|
|
12386
|
+
},
|
|
12387
|
+
i
|
|
12388
|
+
)) });
|
|
12389
|
+
}
|
|
12390
|
+
function ChatIcon({ className }) {
|
|
12391
|
+
return /* @__PURE__ */ jsx(
|
|
12392
|
+
"svg",
|
|
12393
|
+
{
|
|
12394
|
+
className,
|
|
12395
|
+
viewBox: "0 0 24 24",
|
|
12396
|
+
fill: "none",
|
|
12397
|
+
stroke: "currentColor",
|
|
12398
|
+
strokeWidth: 2,
|
|
12399
|
+
strokeLinecap: "round",
|
|
12400
|
+
strokeLinejoin: "round",
|
|
12401
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" })
|
|
12402
|
+
}
|
|
12403
|
+
);
|
|
12404
|
+
}
|
|
12405
|
+
function CloseIcon({ className }) {
|
|
12406
|
+
return /* @__PURE__ */ jsx(
|
|
12407
|
+
"svg",
|
|
12408
|
+
{
|
|
12409
|
+
className,
|
|
12410
|
+
viewBox: "0 0 24 24",
|
|
12411
|
+
fill: "none",
|
|
12412
|
+
stroke: "currentColor",
|
|
12413
|
+
strokeWidth: 2,
|
|
12414
|
+
strokeLinecap: "round",
|
|
12415
|
+
strokeLinejoin: "round",
|
|
12416
|
+
children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
|
|
12417
|
+
}
|
|
12418
|
+
);
|
|
12419
|
+
}
|
|
12420
|
+
function SendIcon({ className }) {
|
|
12421
|
+
return /* @__PURE__ */ jsxs(
|
|
12422
|
+
"svg",
|
|
12423
|
+
{
|
|
12424
|
+
className,
|
|
12425
|
+
viewBox: "0 0 24 24",
|
|
12426
|
+
fill: "none",
|
|
12427
|
+
stroke: "currentColor",
|
|
12428
|
+
strokeWidth: 2,
|
|
12429
|
+
strokeLinecap: "round",
|
|
12430
|
+
strokeLinejoin: "round",
|
|
12431
|
+
children: [
|
|
12432
|
+
/* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
12433
|
+
/* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
12434
|
+
]
|
|
12435
|
+
}
|
|
12436
|
+
);
|
|
12437
|
+
}
|
|
12438
|
+
function SparkleIcon({ className }) {
|
|
12439
|
+
return /* @__PURE__ */ jsx(
|
|
12440
|
+
"svg",
|
|
12441
|
+
{
|
|
12442
|
+
className,
|
|
12443
|
+
viewBox: "0 0 24 24",
|
|
12444
|
+
fill: "none",
|
|
12445
|
+
stroke: "currentColor",
|
|
12446
|
+
strokeWidth: 2.5,
|
|
12447
|
+
children: /* @__PURE__ */ jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" })
|
|
12448
|
+
}
|
|
12449
|
+
);
|
|
12450
|
+
}
|
|
10942
12451
|
|
|
10943
|
-
export { AccommodationCard, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BundleProductCard, BundleProductLayout, BundleSelector, CardImage, CardShell, CardVariant, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, CheckoutPage, CimplifyAccount, CimplifyCheckout, CimplifyProvider, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, CurrencySelector, CustomAttributesTable, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, FeaturedCollectionLayout, FoodProductCard, FoodProductLayout, InventoryBadge, LeaseServiceCard, LocationPicker, MetadataStringList, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, ServiceProductLayout, SessionMessageBanner, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, StoreNav, SubscriptionCard, TagPills, TwoColumnGrid, VariantSelector, VolumePricing, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, roomToResource, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|
|
12452
|
+
export { AccommodationCard, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BundleProductCard, BundleProductLayout, BundleSelector, CardImage, CardShell, CardVariant, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatWidget, CheckoutPage, CimplifyAccount, CimplifyCheckout, CimplifyProvider, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, CurrencySelector, CustomAttributesTable, CustomerInputFields, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, FeaturedCollectionLayout, FoodProductCard, FoodProductLayout, InventoryBadge, LeaseServiceCard, LocationPicker, MetadataStringList, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, ServiceProductLayout, SessionMessageBanner, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, StoreNav, SubscriptionCard, TagPills, TwoColumnGrid, VariantSelector, VolumePricing, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, roomToResource, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|