@cimplify/sdk 0.29.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/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, createCimplifyClient, RENDER_HINT } from './chunk-REXW2PET.mjs';
3
- export { getVariantDisplayName } from './chunk-REXW2PET.mjs';
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
6
  import React10, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore, useId } from 'react';
@@ -3310,6 +3310,122 @@ function useBootstrap(options = {}) {
3310
3310
  const { data, isLoading, error, refetch } = bootstrapQuery(options);
3311
3311
  return { bootstrap: data, isLoading, error, refetch };
3312
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
+ }
3313
3429
  var ElementsContext = createContext({
3314
3430
  elements: null,
3315
3431
  isReady: false
@@ -4661,8 +4777,26 @@ function CompositeSelector({
4661
4777
  )
4662
4778
  ] });
4663
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
+ }
4664
4797
  var FREQUENCY_LABELS = {
4665
4798
  weekly: "Weekly",
4799
+ biweekly: "Biweekly",
4666
4800
  monthly: "Monthly",
4667
4801
  quarterly: "Quarterly",
4668
4802
  annually: "Annually"
@@ -5986,9 +6120,7 @@ function ProductCustomizer({
5986
6120
  )) }),
5987
6121
  isService && product.duration_minutes != null && /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-duration": true, className: cn("text-sm text-muted-foreground", classNames?.duration), children: [
5988
6122
  "Duration: ",
5989
- product.duration_minutes,
5990
- " ",
5991
- product.duration_unit ?? "minutes"
6123
+ formatDuration2(product.duration_minutes, product.duration_unit)
5992
6124
  ] }),
5993
6125
  showSpecialInstructions && !isDigital && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-special-instructions": true, children: /* @__PURE__ */ jsx(
5994
6126
  "textarea",
@@ -6951,7 +7083,7 @@ function CompositeProductCard({
6951
7083
  ] })
6952
7084
  ] });
6953
7085
  }
6954
- function formatDuration2(minutes, unit) {
7086
+ function formatDuration3(minutes, unit) {
6955
7087
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
6956
7088
  if (minutes >= 60) {
6957
7089
  const h = Math.floor(minutes / 60);
@@ -6972,7 +7104,7 @@ function StandardServiceCard({
6972
7104
  return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
6973
7105
  /* @__PURE__ */ jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
6974
7106
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
6975
- 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: formatDuration2(product.duration_minutes, product.duration_unit) }),
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) }),
6976
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 }) })
6977
7109
  ] }),
6978
7110
  /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
@@ -7006,7 +7138,7 @@ function StandardServiceCard({
7006
7138
  ] })
7007
7139
  ] });
7008
7140
  }
7009
- function formatDuration3(minutes, unit) {
7141
+ function formatDuration4(minutes, unit) {
7010
7142
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
7011
7143
  if (minutes >= 60) {
7012
7144
  const h = Math.floor(minutes / 60);
@@ -7031,7 +7163,7 @@ function CompactServiceCard({
7031
7163
  /* @__PURE__ */ jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
7032
7164
  product.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
7033
7165
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2 flex-wrap", children: [
7034
- 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: formatDuration3(product.duration_minutes, product.duration_unit) }),
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) }),
7035
7167
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[10.5px] font-medium text-emerald-600", children: [
7036
7168
  /* @__PURE__ */ jsx("span", { className: "w-[6px] h-[6px] rounded-full bg-emerald-500" }),
7037
7169
  "Available"
@@ -7071,7 +7203,7 @@ function ScheduleServiceCard({
7071
7203
  }) {
7072
7204
  const image = product.image_url || product.images?.[0] || "";
7073
7205
  const href = `/products/${product.slug}`;
7074
- const durationLabel = product.duration_minutes != null ? `${product.duration_minutes} ${product.duration_unit ?? "min"}` : null;
7206
+ const durationLabel = product.duration_minutes != null ? formatDuration2(product.duration_minutes, product.duration_unit) : null;
7075
7207
  const displaySlots = (slots || []).slice(0, 3);
7076
7208
  const shellClass = cn(
7077
7209
  "group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden",
@@ -7124,7 +7256,7 @@ function ScheduleServiceCard({
7124
7256
  }
7125
7257
  return /* @__PURE__ */ jsx("a", { href, className: shellClass, "data-cimplify-card": true, children: inner });
7126
7258
  }
7127
- var UNIT_LABELS = {
7259
+ var UNIT_LABELS2 = {
7128
7260
  minutes: "min",
7129
7261
  hours: "hr",
7130
7262
  days: "day",
@@ -7142,7 +7274,7 @@ function RentalServiceCard({
7142
7274
  }) {
7143
7275
  const image = product.image_url || product.images?.[0] || "";
7144
7276
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
7145
- const unit = product.duration_unit ? UNIT_LABELS[product.duration_unit] || product.duration_unit : "day";
7277
+ const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
7146
7278
  const status = product.inventory_status;
7147
7279
  return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
7148
7280
  /* @__PURE__ */ jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
@@ -7262,7 +7394,7 @@ function AccommodationCard({
7262
7394
  ] })
7263
7395
  ] });
7264
7396
  }
7265
- var UNIT_LABELS2 = {
7397
+ var UNIT_LABELS3 = {
7266
7398
  weeks: "week",
7267
7399
  months: "mo",
7268
7400
  years: "yr"
@@ -7277,7 +7409,7 @@ function LeaseServiceCard({
7277
7409
  const image = product.image_url || product.images?.[0] || "";
7278
7410
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
7279
7411
  const hasTiers = product.quantity_pricing && product.quantity_pricing.length > 1;
7280
- const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "mo";
7412
+ const unit = product.duration_unit ? UNIT_LABELS3[product.duration_unit] || product.duration_unit : "mo";
7281
7413
  const tags = product.tags || [];
7282
7414
  const cancellationHours = product.cancellation_window_minutes ? Math.floor(product.cancellation_window_minutes / 60) : null;
7283
7415
  return /* @__PURE__ */ jsxs(CardShell, { product, renderLink, className, children: [
@@ -7335,6 +7467,7 @@ function LeaseServiceCard({
7335
7467
  }
7336
7468
  var FREQUENCY_LABELS2 = {
7337
7469
  weekly: "/week",
7470
+ biweekly: "/2wk",
7338
7471
  monthly: "/mo",
7339
7472
  quarterly: "/qtr",
7340
7473
  annually: "/yr"
@@ -9837,15 +9970,6 @@ function WholesaleProductLayout({
9837
9970
  )
9838
9971
  ] });
9839
9972
  }
9840
- function formatMinutes(minutes) {
9841
- if (minutes >= 1440) return `${Math.floor(minutes / 1440)} day${minutes >= 2880 ? "s" : ""}`;
9842
- if (minutes >= 60) {
9843
- const h = Math.floor(minutes / 60);
9844
- const m = minutes % 60;
9845
- return m > 0 ? `${h}h ${m}min` : `${h} hour${h > 1 ? "s" : ""}`;
9846
- }
9847
- return `${minutes} min`;
9848
- }
9849
9973
  function ServiceProductLayout({
9850
9974
  product,
9851
9975
  onAddToCart,
@@ -9877,7 +10001,7 @@ function ServiceProductLayout({
9877
10001
  /* @__PURE__ */ jsx(Price, { amount: product.default_price, className: "text-2xl font-bold text-primary mt-2 block" })
9878
10002
  ] }),
9879
10003
  /* @__PURE__ */ jsxs("div", { "data-cimplify-product-layout-service-info": true, className: "flex flex-wrap gap-2", children: [
9880
- 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: formatMinutes(product.duration_minutes) }),
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) }),
9881
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: [
9882
10006
  /* @__PURE__ */ jsx(Price, { amount: depositAmount }),
9883
10007
  " deposit"
@@ -9914,7 +10038,7 @@ function ServiceProductLayout({
9914
10038
  cancellationHours,
9915
10039
  " hours"
9916
10040
  ] }),
9917
- " before the appointment"
10041
+ " before the booking"
9918
10042
  ] }),
9919
10043
  product.no_show_fee && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
9920
10044
  "No-show fee: ",
@@ -11838,5 +11962,491 @@ function ResourcePicker({
11838
11962
  }
11839
11963
  );
11840
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
+ }
11841
12451
 
11842
- 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, 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, 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 };