@cimplify/sdk 0.29.0 → 0.31.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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkR4AKAGZQ_js = require('./chunk-R4AKAGZQ.js');
4
+ var chunk6HCBOIZ4_js = require('./chunk-6HCBOIZ4.js');
5
5
  var chunk2CGA3MUW_js = require('./chunk-2CGA3MUW.js');
6
6
  var chunkPEDLCL72_js = require('./chunk-PEDLCL72.js');
7
7
  var React10 = require('react');
@@ -1063,7 +1063,7 @@ var DEFAULT_COUNTRY = "US";
1063
1063
  function createDefaultClient() {
1064
1064
  const processRef = globalThis.process;
1065
1065
  const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
1066
- return chunkR4AKAGZQ_js.createCimplifyClient({ publicKey: envPublicKey });
1066
+ return chunk6HCBOIZ4_js.createCimplifyClient({ publicKey: envPublicKey });
1067
1067
  }
1068
1068
  function getStoredLocationId() {
1069
1069
  if (typeof window === "undefined" || !window.localStorage) {
@@ -3315,6 +3315,182 @@ function useBootstrap(options = {}) {
3315
3315
  const { data, isLoading, error, refetch } = bootstrapQuery(options);
3316
3316
  return { bootstrap: data, isLoading, error, refetch };
3317
3317
  }
3318
+ var POLL_INTERVAL_MS = 3e3;
3319
+ var POLL_IDLE_INTERVAL_MS = 15e3;
3320
+ var IDLE_TIMEOUT_MS = 6e4;
3321
+ var WS_MAX_RECONNECT_ATTEMPTS = 10;
3322
+ var WS_PATH = "/api/v1/support/conversation/ws";
3323
+ function useChat(options = {}) {
3324
+ const context = useOptionalCimplifyClient();
3325
+ const client = options.client ?? context?.client;
3326
+ const [messages, setMessages] = React10.useState([]);
3327
+ const [isLoading, setIsLoading] = React10.useState(false);
3328
+ const [isSending, setIsSending] = React10.useState(false);
3329
+ const [error, setError] = React10.useState(null);
3330
+ const [conversationOpened, setConversationOpened] = React10.useState(false);
3331
+ const pollRef = React10.useRef(null);
3332
+ const lastActivityRef = React10.useRef(Date.now());
3333
+ const openedRef = React10.useRef(false);
3334
+ const messagesRef = React10.useRef([]);
3335
+ const clientRef = React10.useRef(client);
3336
+ const wsRef = React10.useRef(null);
3337
+ const wsReconnectAttemptRef = React10.useRef(0);
3338
+ const wsReconnectTimerRef = React10.useRef(null);
3339
+ messagesRef.current = messages;
3340
+ clientRef.current = client;
3341
+ const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
3342
+ const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
3343
+ const appendMessages = React10.useCallback((incoming) => {
3344
+ setMessages((prev) => {
3345
+ const ids = new Set(prev.map((m) => m.id));
3346
+ const fresh = incoming.filter((m) => !ids.has(m.id));
3347
+ return fresh.length > 0 ? [...prev, ...fresh] : prev;
3348
+ });
3349
+ }, []);
3350
+ const pollMessages = React10.useCallback(async () => {
3351
+ const c = clientRef.current;
3352
+ if (!c) return;
3353
+ if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
3354
+ const last = messagesRef.current[messagesRef.current.length - 1];
3355
+ const result = await c.support.getMessages({
3356
+ after: last?.created_at,
3357
+ limit: 50
3358
+ });
3359
+ if (result.ok && result.value.length > 0) {
3360
+ appendMessages(result.value);
3361
+ }
3362
+ }, [appendMessages]);
3363
+ const startPolling = React10.useCallback(() => {
3364
+ if (pollRef.current) clearInterval(pollRef.current);
3365
+ const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
3366
+ pollRef.current = setInterval(pollMessages, interval);
3367
+ }, [pollMessages, pollInterval, pollIdleInterval]);
3368
+ const stopPolling = React10.useCallback(() => {
3369
+ if (pollRef.current) {
3370
+ clearInterval(pollRef.current);
3371
+ pollRef.current = null;
3372
+ }
3373
+ }, []);
3374
+ const connectWebSocket = React10.useCallback(() => {
3375
+ const c = clientRef.current;
3376
+ if (!c || typeof WebSocket === "undefined") {
3377
+ startPolling();
3378
+ return;
3379
+ }
3380
+ if (wsRef.current) {
3381
+ wsRef.current.close();
3382
+ wsRef.current = null;
3383
+ }
3384
+ const url = c.buildWsUrl(WS_PATH);
3385
+ const ws = new WebSocket(url);
3386
+ ws.onopen = () => {
3387
+ wsReconnectAttemptRef.current = 0;
3388
+ stopPolling();
3389
+ };
3390
+ ws.onmessage = (event) => {
3391
+ try {
3392
+ const envelope = JSON.parse(event.data);
3393
+ if (envelope.type === "message.created" && envelope.data) {
3394
+ appendMessages([envelope.data]);
3395
+ }
3396
+ } catch {
3397
+ }
3398
+ };
3399
+ ws.onclose = () => {
3400
+ wsRef.current = null;
3401
+ if (!openedRef.current) return;
3402
+ pollMessages();
3403
+ if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
3404
+ const attempt = wsReconnectAttemptRef.current++;
3405
+ const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
3406
+ wsReconnectTimerRef.current = setTimeout(connectWebSocket, delay);
3407
+ } else {
3408
+ startPolling();
3409
+ }
3410
+ };
3411
+ ws.onerror = () => {
3412
+ };
3413
+ wsRef.current = ws;
3414
+ }, [appendMessages, startPolling, stopPolling, pollMessages]);
3415
+ React10.useEffect(() => {
3416
+ return () => {
3417
+ if (pollRef.current) clearInterval(pollRef.current);
3418
+ if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
3419
+ if (wsRef.current) {
3420
+ wsRef.current.onclose = null;
3421
+ wsRef.current.close();
3422
+ }
3423
+ };
3424
+ }, []);
3425
+ const openConversation = React10.useCallback(async () => {
3426
+ if (openedRef.current || !client) return;
3427
+ openedRef.current = true;
3428
+ setIsLoading(true);
3429
+ setError(null);
3430
+ const result = await client.support.openConversation();
3431
+ if (result.ok) {
3432
+ setMessages(result.value.messages);
3433
+ setConversationOpened(true);
3434
+ connectWebSocket();
3435
+ } else {
3436
+ openedRef.current = false;
3437
+ setError(result.error);
3438
+ }
3439
+ setIsLoading(false);
3440
+ }, [client, connectWebSocket]);
3441
+ const send = React10.useCallback(
3442
+ async (content) => {
3443
+ if (!client || !content.trim()) return;
3444
+ setError(null);
3445
+ lastActivityRef.current = Date.now();
3446
+ if (!openedRef.current) {
3447
+ await openConversation();
3448
+ if (!openedRef.current) return;
3449
+ }
3450
+ const optimisticId = `opt_${Date.now()}`;
3451
+ const optimistic = {
3452
+ id: optimisticId,
3453
+ sender_type: "customer",
3454
+ content: content.trim(),
3455
+ content_type: "text",
3456
+ attachments: [],
3457
+ metadata: {},
3458
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
3459
+ };
3460
+ setMessages((prev) => [...prev, optimistic]);
3461
+ setIsSending(true);
3462
+ const result = await client.support.sendMessage(content.trim());
3463
+ if (result.ok) {
3464
+ setMessages(
3465
+ (prev) => prev.map((m) => m.id === optimisticId ? result.value : m)
3466
+ );
3467
+ } else {
3468
+ setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
3469
+ setError(result.error);
3470
+ }
3471
+ setIsSending(false);
3472
+ },
3473
+ [client, openConversation]
3474
+ );
3475
+ const startConversation = React10.useCallback(
3476
+ async (starterText) => {
3477
+ await openConversation();
3478
+ if (starterText) {
3479
+ await send(starterText);
3480
+ }
3481
+ },
3482
+ [openConversation, send]
3483
+ );
3484
+ return {
3485
+ messages,
3486
+ isLoading,
3487
+ isSending,
3488
+ error,
3489
+ isActive: conversationOpened && messages.length > 0,
3490
+ send,
3491
+ startConversation
3492
+ };
3493
+ }
3318
3494
  var ElementsContext = React10.createContext({
3319
3495
  elements: null,
3320
3496
  isReady: false
@@ -3737,7 +3913,7 @@ function VariantSelector({
3737
3913
  {
3738
3914
  "data-cimplify-variant-name": true,
3739
3915
  className: cn("flex-1 min-w-0 text-sm", classNames?.name),
3740
- children: chunkR4AKAGZQ_js.getVariantDisplayName(variant, productName)
3916
+ children: chunk6HCBOIZ4_js.getVariantDisplayName(variant, productName)
3741
3917
  }
3742
3918
  ),
3743
3919
  /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-variant-pricing": true, className: cn("text-sm text-muted-foreground", classNames?.pricing), children: [
@@ -4666,8 +4842,26 @@ function CompositeSelector({
4666
4842
  )
4667
4843
  ] });
4668
4844
  }
4845
+
4846
+ // src/react/utils/format-duration.ts
4847
+ var UNIT_LABELS = {
4848
+ minutes: { singular: "min", plural: "min" },
4849
+ hours: { singular: "hr", plural: "hrs" },
4850
+ days: { singular: "day", plural: "days" },
4851
+ nights: { singular: "night", plural: "nights" },
4852
+ weeks: { singular: "week", plural: "weeks" },
4853
+ months: { singular: "month", plural: "months" },
4854
+ years: { singular: "year", plural: "years" }
4855
+ };
4856
+ function formatDuration2(value, unit) {
4857
+ const resolved = unit ?? "minutes";
4858
+ const labels = UNIT_LABELS[resolved];
4859
+ const label = value === 1 ? labels.singular : labels.plural;
4860
+ return `${value} ${label}`;
4861
+ }
4669
4862
  var FREQUENCY_LABELS = {
4670
4863
  weekly: "Weekly",
4864
+ biweekly: "Biweekly",
4671
4865
  monthly: "Monthly",
4672
4866
  quarterly: "Quarterly",
4673
4867
  annually: "Annually"
@@ -4936,12 +5130,12 @@ function FieldInput({
4936
5130
  classNames?.input
4937
5131
  );
4938
5132
  switch (field.field_type) {
4939
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Text:
4940
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Url:
5133
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Text:
5134
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Url:
4941
5135
  return /* @__PURE__ */ jsxRuntime.jsx(
4942
5136
  "input",
4943
5137
  {
4944
- type: field.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
5138
+ type: field.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
4945
5139
  value: typeof value === "string" ? value : "",
4946
5140
  onChange: (e) => onValueChange(e.target.value),
4947
5141
  placeholder: field.placeholder,
@@ -4950,7 +5144,7 @@ function FieldInput({
4950
5144
  className: inputClass
4951
5145
  }
4952
5146
  );
4953
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Textarea:
5147
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Textarea:
4954
5148
  return /* @__PURE__ */ jsxRuntime.jsx(
4955
5149
  "textarea",
4956
5150
  {
@@ -4963,7 +5157,7 @@ function FieldInput({
4963
5157
  className: cn(inputClass, "resize-none", classNames?.textarea)
4964
5158
  }
4965
5159
  );
4966
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Number:
5160
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Number:
4967
5161
  return /* @__PURE__ */ jsxRuntime.jsx(
4968
5162
  "input",
4969
5163
  {
@@ -4977,7 +5171,7 @@ function FieldInput({
4977
5171
  className: inputClass
4978
5172
  }
4979
5173
  );
4980
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Select:
5174
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Select:
4981
5175
  return /* @__PURE__ */ jsxRuntime.jsxs(
4982
5176
  "select",
4983
5177
  {
@@ -4991,7 +5185,7 @@ function FieldInput({
4991
5185
  ]
4992
5186
  }
4993
5187
  );
4994
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Radio:
5188
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Radio:
4995
5189
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
4996
5190
  "label",
4997
5191
  {
@@ -5016,7 +5210,7 @@ function FieldInput({
5016
5210
  },
5017
5211
  opt
5018
5212
  )) });
5019
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Checkbox:
5213
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Checkbox:
5020
5214
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
5021
5215
  /* @__PURE__ */ jsxRuntime.jsx(
5022
5216
  "input",
@@ -5029,7 +5223,7 @@ function FieldInput({
5029
5223
  ),
5030
5224
  field.placeholder || field.name
5031
5225
  ] });
5032
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Color:
5226
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Color:
5033
5227
  return /* @__PURE__ */ jsxRuntime.jsx(
5034
5228
  "input",
5035
5229
  {
@@ -5039,7 +5233,7 @@ function FieldInput({
5039
5233
  className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
5040
5234
  }
5041
5235
  );
5042
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Date:
5236
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Date:
5043
5237
  return /* @__PURE__ */ jsxRuntime.jsx(
5044
5238
  "input",
5045
5239
  {
@@ -5050,8 +5244,8 @@ function FieldInput({
5050
5244
  className: inputClass
5051
5245
  }
5052
5246
  );
5053
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.File:
5054
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image:
5247
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.File:
5248
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Image:
5055
5249
  return /* @__PURE__ */ jsxRuntime.jsx(
5056
5250
  FileUploadInput,
5057
5251
  {
@@ -5062,7 +5256,7 @@ function FieldInput({
5062
5256
  classNames
5063
5257
  }
5064
5258
  );
5065
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Email:
5259
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Email:
5066
5260
  return /* @__PURE__ */ jsxRuntime.jsx(
5067
5261
  "input",
5068
5262
  {
@@ -5074,7 +5268,7 @@ function FieldInput({
5074
5268
  className: inputClass
5075
5269
  }
5076
5270
  );
5077
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.DateTime: {
5271
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.DateTime: {
5078
5272
  const dtLocalValue = typeof value === "string" && value.includes("T") ? value.slice(0, 16) : typeof value === "string" ? value : "";
5079
5273
  return /* @__PURE__ */ jsxRuntime.jsx(
5080
5274
  "input",
@@ -5094,7 +5288,7 @@ function FieldInput({
5094
5288
  }
5095
5289
  );
5096
5290
  }
5097
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Time:
5291
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Time:
5098
5292
  return /* @__PURE__ */ jsxRuntime.jsx(
5099
5293
  "input",
5100
5294
  {
@@ -5105,7 +5299,7 @@ function FieldInput({
5105
5299
  className: inputClass
5106
5300
  }
5107
5301
  );
5108
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.MultiSelect:
5302
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.MultiSelect:
5109
5303
  return /* @__PURE__ */ jsxRuntime.jsx(
5110
5304
  MultiSelectInput,
5111
5305
  {
@@ -5115,7 +5309,7 @@ function FieldInput({
5115
5309
  classNames
5116
5310
  }
5117
5311
  );
5118
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.DateRange:
5312
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.DateRange:
5119
5313
  return /* @__PURE__ */ jsxRuntime.jsx(
5120
5314
  DateRangeInput,
5121
5315
  {
@@ -5126,7 +5320,7 @@ function FieldInput({
5126
5320
  inputClass
5127
5321
  }
5128
5322
  );
5129
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Address:
5323
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Address:
5130
5324
  return /* @__PURE__ */ jsxRuntime.jsx(
5131
5325
  AddressInput,
5132
5326
  {
@@ -5137,7 +5331,7 @@ function FieldInput({
5137
5331
  inputClass
5138
5332
  }
5139
5333
  );
5140
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Location:
5334
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Location:
5141
5335
  return /* @__PURE__ */ jsxRuntime.jsx(
5142
5336
  LocationInput,
5143
5337
  {
@@ -5148,7 +5342,7 @@ function FieldInput({
5148
5342
  inputClass
5149
5343
  }
5150
5344
  );
5151
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Phone:
5345
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Phone:
5152
5346
  return /* @__PURE__ */ jsxRuntime.jsx(
5153
5347
  PhoneInput,
5154
5348
  {
@@ -5159,7 +5353,7 @@ function FieldInput({
5159
5353
  inputClass
5160
5354
  }
5161
5355
  );
5162
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Signature:
5356
+ case chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Signature:
5163
5357
  return /* @__PURE__ */ jsxRuntime.jsx(
5164
5358
  SignatureInput,
5165
5359
  {
@@ -5192,7 +5386,7 @@ function FileUploadInput({
5192
5386
  const [isUploading, setIsUploading] = React10__default.default.useState(false);
5193
5387
  const fileUrl = typeof value === "string" ? value : void 0;
5194
5388
  const acceptedFormats = field.validation?.accepted_formats;
5195
- const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
5389
+ const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
5196
5390
  const handleFile = async (file) => {
5197
5391
  if (!onFileUpload) return;
5198
5392
  if (field.validation?.max_size_mb) {
@@ -5210,7 +5404,7 @@ function FileUploadInput({
5210
5404
  }
5211
5405
  };
5212
5406
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.fileInput), children: fileUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
5213
- field.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsxRuntime.jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
5407
+ field.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsxRuntime.jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
5214
5408
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground truncate", children: fileUrl.split("/").pop() }) }),
5215
5409
  /* @__PURE__ */ jsxRuntime.jsx(
5216
5410
  "button",
@@ -5236,7 +5430,7 @@ function FileUploadInput({
5236
5430
  }
5237
5431
  ),
5238
5432
  isUploading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Uploading..." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5239
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
5433
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
5240
5434
  acceptedFormats && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
5241
5435
  acceptedFormats.map((f) => f.toUpperCase()).join(", "),
5242
5436
  field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
@@ -5991,9 +6185,7 @@ function ProductCustomizer({
5991
6185
  )) }),
5992
6186
  isService && product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-duration": true, className: cn("text-sm text-muted-foreground", classNames?.duration), children: [
5993
6187
  "Duration: ",
5994
- product.duration_minutes,
5995
- " ",
5996
- product.duration_unit ?? "minutes"
6188
+ formatDuration2(product.duration_minutes, product.duration_unit)
5997
6189
  ] }),
5998
6190
  showSpecialInstructions && !isDigital && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-customizer-special-instructions": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5999
6191
  "textarea",
@@ -6166,7 +6358,7 @@ function CartLineItemRow({
6166
6358
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item": true, className: classNames?.item, children: [
6167
6359
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item-info": true, children: [
6168
6360
  /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-name": true, className: classNames?.itemName, children: item.product.name }),
6169
- item.variant && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-variant": true, children: chunkR4AKAGZQ_js.getVariantDisplayName(item.variant, item.product.name) }),
6361
+ item.variant && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-variant": true, children: chunk6HCBOIZ4_js.getVariantDisplayName(item.variant, item.product.name) }),
6170
6362
  item.addOnOptions && item.addOnOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-cart-item-addons": true, children: [
6171
6363
  "+ ",
6172
6364
  item.addOnOptions.map((opt) => opt.name).join(", ")
@@ -6175,7 +6367,7 @@ function CartLineItemRow({
6175
6367
  item.specialInstructions && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-instructions": true, className: "text-xs text-muted-foreground", children: item.specialInstructions }),
6176
6368
  item.customerInputs && item.customerInputs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-cart-customer-inputs": true, className: classNames?.customerInputs, children: item.customerInputs.map((input) => /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-input": true, children: [
6177
6369
  /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-input-label": true, children: input.field_name }),
6178
- /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-input-value": true, children: input.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.File || input.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
6370
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-input-value": true, children: input.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.File || input.field_type === chunk6HCBOIZ4_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
6179
6371
  ] }, input.field_id)) }),
6180
6372
  (item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-info": true, className: classNames?.serviceInfo, children: [
6181
6373
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
@@ -6695,7 +6887,7 @@ function FoodProductCard({
6695
6887
  const tags = product.tags || [];
6696
6888
  const isSignature = product.metadata?.is_signature === true;
6697
6889
  const isNew = product.metadata?.is_new === true;
6698
- const isComposite = product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite;
6890
+ const isComposite = product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Composite;
6699
6891
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
6700
6892
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "square", renderImage, children: [
6701
6893
  isSignature && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-3 left-3 inline-flex items-center gap-1 text-[11px] font-semibold tracking-wide bg-amber-50 text-amber-700 border border-amber-200/60 px-2 py-0.5 rounded-md", children: [
@@ -6956,7 +7148,7 @@ function CompositeProductCard({
6956
7148
  ] })
6957
7149
  ] });
6958
7150
  }
6959
- function formatDuration2(minutes, unit) {
7151
+ function formatDuration3(minutes, unit) {
6960
7152
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
6961
7153
  if (minutes >= 60) {
6962
7154
  const h = Math.floor(minutes / 60);
@@ -6977,7 +7169,7 @@ function StandardServiceCard({
6977
7169
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
6978
7170
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
6979
7171
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
6980
- product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
7172
+ product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
6981
7173
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-3 right-3 text-white font-bold text-lg drop-shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: product.default_price }) })
6982
7174
  ] }),
6983
7175
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
@@ -7011,7 +7203,7 @@ function StandardServiceCard({
7011
7203
  ] })
7012
7204
  ] });
7013
7205
  }
7014
- function formatDuration3(minutes, unit) {
7206
+ function formatDuration4(minutes, unit) {
7015
7207
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
7016
7208
  if (minutes >= 60) {
7017
7209
  const h = Math.floor(minutes / 60);
@@ -7036,7 +7228,7 @@ function CompactServiceCard({
7036
7228
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
7037
7229
  product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
7038
7230
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2 flex-wrap", children: [
7039
- product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
7231
+ product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
7040
7232
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10.5px] font-medium text-emerald-600", children: [
7041
7233
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-[6px] h-[6px] rounded-full bg-emerald-500" }),
7042
7234
  "Available"
@@ -7076,7 +7268,7 @@ function ScheduleServiceCard({
7076
7268
  }) {
7077
7269
  const image = product.image_url || product.images?.[0] || "";
7078
7270
  const href = `/products/${product.slug}`;
7079
- const durationLabel = product.duration_minutes != null ? `${product.duration_minutes} ${product.duration_unit ?? "min"}` : null;
7271
+ const durationLabel = product.duration_minutes != null ? formatDuration2(product.duration_minutes, product.duration_unit) : null;
7080
7272
  const displaySlots = (slots || []).slice(0, 3);
7081
7273
  const shellClass = cn(
7082
7274
  "group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden",
@@ -7129,7 +7321,7 @@ function ScheduleServiceCard({
7129
7321
  }
7130
7322
  return /* @__PURE__ */ jsxRuntime.jsx("a", { href, className: shellClass, "data-cimplify-card": true, children: inner });
7131
7323
  }
7132
- var UNIT_LABELS = {
7324
+ var UNIT_LABELS2 = {
7133
7325
  minutes: "min",
7134
7326
  hours: "hr",
7135
7327
  days: "day",
@@ -7147,7 +7339,7 @@ function RentalServiceCard({
7147
7339
  }) {
7148
7340
  const image = product.image_url || product.images?.[0] || "";
7149
7341
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
7150
- const unit = product.duration_unit ? UNIT_LABELS[product.duration_unit] || product.duration_unit : "day";
7342
+ const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
7151
7343
  const status = product.inventory_status;
7152
7344
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
7153
7345
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
@@ -7267,7 +7459,7 @@ function AccommodationCard({
7267
7459
  ] })
7268
7460
  ] });
7269
7461
  }
7270
- var UNIT_LABELS2 = {
7462
+ var UNIT_LABELS3 = {
7271
7463
  weeks: "week",
7272
7464
  months: "mo",
7273
7465
  years: "yr"
@@ -7282,7 +7474,7 @@ function LeaseServiceCard({
7282
7474
  const image = product.image_url || product.images?.[0] || "";
7283
7475
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
7284
7476
  const hasTiers = product.quantity_pricing && product.quantity_pricing.length > 1;
7285
- const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "mo";
7477
+ const unit = product.duration_unit ? UNIT_LABELS3[product.duration_unit] || product.duration_unit : "mo";
7286
7478
  const tags = product.tags || [];
7287
7479
  const cancellationHours = product.cancellation_window_minutes ? Math.floor(product.cancellation_window_minutes / 60) : null;
7288
7480
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
@@ -7340,6 +7532,7 @@ function LeaseServiceCard({
7340
7532
  }
7341
7533
  var FREQUENCY_LABELS2 = {
7342
7534
  weekly: "/week",
7535
+ biweekly: "/2wk",
7343
7536
  monthly: "/mo",
7344
7537
  quarterly: "/qtr",
7345
7538
  annually: "/yr"
@@ -7447,26 +7640,26 @@ var BUILT_IN_CARDS = {
7447
7640
  ["composite" /* Composite */]: CompositeProductCard,
7448
7641
  ["subscription" /* Subscription */]: SubscriptionCard
7449
7642
  };
7450
- var RENTAL_UNITS = /* @__PURE__ */ new Set([chunkR4AKAGZQ_js.DURATION_UNIT.Days, chunkR4AKAGZQ_js.DURATION_UNIT.Hours]);
7451
- var LEASE_UNITS = /* @__PURE__ */ new Set([chunkR4AKAGZQ_js.DURATION_UNIT.Weeks, chunkR4AKAGZQ_js.DURATION_UNIT.Months, chunkR4AKAGZQ_js.DURATION_UNIT.Years]);
7643
+ var RENTAL_UNITS = /* @__PURE__ */ new Set([chunk6HCBOIZ4_js.DURATION_UNIT.Days, chunk6HCBOIZ4_js.DURATION_UNIT.Hours]);
7644
+ var LEASE_UNITS = /* @__PURE__ */ new Set([chunk6HCBOIZ4_js.DURATION_UNIT.Weeks, chunk6HCBOIZ4_js.DURATION_UNIT.Months, chunk6HCBOIZ4_js.DURATION_UNIT.Years]);
7452
7645
  function resolveCardVariant(product) {
7453
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Bundle) {
7646
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Bundle) {
7454
7647
  return "bundle" /* Bundle */;
7455
7648
  }
7456
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite) {
7649
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Composite) {
7457
7650
  return "composite" /* Composite */;
7458
7651
  }
7459
7652
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
7460
7653
  return "wholesale" /* Wholesale */;
7461
7654
  }
7462
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Digital) {
7655
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Digital) {
7463
7656
  return "digital" /* Digital */;
7464
7657
  }
7465
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Service) {
7658
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Service) {
7466
7659
  if (product.duration_unit && RENTAL_UNITS.has(product.duration_unit)) {
7467
7660
  return "rental" /* Rental */;
7468
7661
  }
7469
- if (product.duration_unit === chunkR4AKAGZQ_js.DURATION_UNIT.Nights) {
7662
+ if (product.duration_unit === chunk6HCBOIZ4_js.DURATION_UNIT.Nights) {
7470
7663
  return "accommodation" /* Accommodation */;
7471
7664
  }
7472
7665
  if (product.duration_unit && LEASE_UNITS.has(product.duration_unit)) {
@@ -7477,10 +7670,10 @@ function resolveCardVariant(product) {
7477
7670
  }
7478
7671
  return "standard" /* Standard */;
7479
7672
  }
7480
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Food) {
7673
+ if (product.render_hint === chunk6HCBOIZ4_js.RENDER_HINT.Food) {
7481
7674
  return "food" /* Food */;
7482
7675
  }
7483
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Physical) {
7676
+ if (product.render_hint === chunk6HCBOIZ4_js.RENDER_HINT.Physical) {
7484
7677
  return "retail" /* Retail */;
7485
7678
  }
7486
7679
  return "retail" /* Retail */;
@@ -9842,15 +10035,6 @@ function WholesaleProductLayout({
9842
10035
  )
9843
10036
  ] });
9844
10037
  }
9845
- function formatMinutes(minutes) {
9846
- if (minutes >= 1440) return `${Math.floor(minutes / 1440)} day${minutes >= 2880 ? "s" : ""}`;
9847
- if (minutes >= 60) {
9848
- const h = Math.floor(minutes / 60);
9849
- const m = minutes % 60;
9850
- return m > 0 ? `${h}h ${m}min` : `${h} hour${h > 1 ? "s" : ""}`;
9851
- }
9852
- return `${minutes} min`;
9853
- }
9854
10038
  function ServiceProductLayout({
9855
10039
  product,
9856
10040
  onAddToCart,
@@ -9882,7 +10066,7 @@ function ServiceProductLayout({
9882
10066
  /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: product.default_price, className: "text-2xl font-bold text-primary mt-2 block" })
9883
10067
  ] }),
9884
10068
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-product-layout-service-info": true, className: "flex flex-wrap gap-2", children: [
9885
- product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
10069
+ product.duration_minutes != null && /* @__PURE__ */ jsxRuntime.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) }),
9886
10070
  hasDeposit && /* @__PURE__ */ jsxRuntime.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: [
9887
10071
  /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: depositAmount }),
9888
10072
  " deposit"
@@ -9919,7 +10103,7 @@ function ServiceProductLayout({
9919
10103
  cancellationHours,
9920
10104
  " hours"
9921
10105
  ] }),
9922
- " before the appointment"
10106
+ " before the booking"
9923
10107
  ] }),
9924
10108
  product.no_show_fee && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
9925
10109
  "No-show fee: ",
@@ -10250,25 +10434,25 @@ function resolveTemplateKey(product) {
10250
10434
  if (typeof metaTemplate === "string" && metaTemplate.trim()) {
10251
10435
  return metaTemplate.trim();
10252
10436
  }
10253
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Bundle) {
10437
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Bundle) {
10254
10438
  return "bundle" /* Bundle */;
10255
10439
  }
10256
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite) {
10440
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Composite) {
10257
10441
  return "composite" /* Composite */;
10258
10442
  }
10259
10443
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
10260
10444
  return "wholesale" /* Wholesale */;
10261
10445
  }
10262
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Service) {
10446
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Service) {
10263
10447
  return "service" /* Service */;
10264
10448
  }
10265
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Digital) {
10449
+ if (product.type === chunk6HCBOIZ4_js.PRODUCT_TYPE.Digital) {
10266
10450
  return "digital" /* Digital */;
10267
10451
  }
10268
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Food) {
10452
+ if (product.render_hint === chunk6HCBOIZ4_js.RENDER_HINT.Food) {
10269
10453
  return "food" /* Food */;
10270
10454
  }
10271
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Physical) {
10455
+ if (product.render_hint === chunk6HCBOIZ4_js.RENDER_HINT.Physical) {
10272
10456
  return "physical" /* Physical */;
10273
10457
  }
10274
10458
  return "default" /* Default */;
@@ -11843,10 +12027,496 @@ function ResourcePicker({
11843
12027
  }
11844
12028
  );
11845
12029
  }
12030
+ function ChatWidget({
12031
+ client: clientProp,
12032
+ businessName = "Support",
12033
+ greeting = "Hi there!",
12034
+ subtitle = "Ask us anything \u2014 we typically reply in a few seconds.",
12035
+ starters,
12036
+ placeholder = "Type a message\u2026",
12037
+ position = "bottom-right",
12038
+ defaultOpen = false,
12039
+ pollInterval,
12040
+ className,
12041
+ classNames
12042
+ }) {
12043
+ const context = useOptionalCimplifyClient();
12044
+ const client = clientProp ?? context?.client;
12045
+ const [open, setOpen] = React10.useState(defaultOpen);
12046
+ const [unread, setUnread] = React10.useState(0);
12047
+ const { messages, isLoading, isSending, error, isActive, send, startConversation } = useChat({ client, pollInterval });
12048
+ const prevCountRef = React10.useRef(messages.length);
12049
+ React10.useEffect(() => {
12050
+ if (!open && messages.length > prevCountRef.current) {
12051
+ const newMessages = messages.slice(prevCountRef.current);
12052
+ const incomingCount = newMessages.filter(
12053
+ (m) => m.sender_type !== "customer"
12054
+ ).length;
12055
+ if (incomingCount > 0) setUnread((n) => n + incomingCount);
12056
+ }
12057
+ prevCountRef.current = messages.length;
12058
+ }, [messages, open]);
12059
+ const handleOpen = React10.useCallback(() => {
12060
+ setOpen(true);
12061
+ setUnread(0);
12062
+ }, []);
12063
+ const handleClose = React10.useCallback(() => setOpen(false), []);
12064
+ const isLeft = position === "bottom-left";
12065
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12066
+ "div",
12067
+ {
12068
+ className: cn(
12069
+ "fixed bottom-6 z-[9999]",
12070
+ isLeft ? "left-6" : "right-6",
12071
+ className,
12072
+ classNames?.root
12073
+ ),
12074
+ children: [
12075
+ !open && /* @__PURE__ */ jsxRuntime.jsx(
12076
+ Bubble,
12077
+ {
12078
+ unread,
12079
+ onClick: handleOpen,
12080
+ className: classNames?.bubble
12081
+ }
12082
+ ),
12083
+ open && /* @__PURE__ */ jsxRuntime.jsx(
12084
+ Panel,
12085
+ {
12086
+ businessName,
12087
+ greeting,
12088
+ subtitle,
12089
+ starters,
12090
+ placeholder,
12091
+ messages,
12092
+ isLoading,
12093
+ isSending,
12094
+ error,
12095
+ isActive,
12096
+ isLeft,
12097
+ onClose: handleClose,
12098
+ onSend: send,
12099
+ onStartConversation: startConversation,
12100
+ classNames
12101
+ }
12102
+ )
12103
+ ]
12104
+ }
12105
+ );
12106
+ }
12107
+ function Bubble({
12108
+ unread,
12109
+ onClick,
12110
+ className
12111
+ }) {
12112
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12113
+ "button",
12114
+ {
12115
+ type: "button",
12116
+ onClick,
12117
+ "aria-label": "Open chat",
12118
+ className: cn(
12119
+ "relative flex h-14 w-14 items-center justify-center rounded-full",
12120
+ "bg-foreground text-background shadow-lg",
12121
+ "transition-transform duration-300 ease-out",
12122
+ "hover:scale-[1.08] active:scale-95",
12123
+ className
12124
+ ),
12125
+ children: [
12126
+ /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-6 w-6" }),
12127
+ unread > 0 && /* @__PURE__ */ jsxRuntime.jsx(
12128
+ "span",
12129
+ {
12130
+ className: cn(
12131
+ "absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
12132
+ "rounded-full bg-red-500 text-[10px] font-bold text-white",
12133
+ "ring-2 ring-background"
12134
+ ),
12135
+ children: unread > 9 ? "9+" : unread
12136
+ }
12137
+ )
12138
+ ]
12139
+ }
12140
+ );
12141
+ }
12142
+ function Panel({
12143
+ businessName,
12144
+ greeting,
12145
+ subtitle,
12146
+ starters,
12147
+ placeholder,
12148
+ messages,
12149
+ isLoading,
12150
+ isSending,
12151
+ error,
12152
+ isActive,
12153
+ isLeft,
12154
+ onClose,
12155
+ onSend,
12156
+ onStartConversation,
12157
+ classNames
12158
+ }) {
12159
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12160
+ "div",
12161
+ {
12162
+ className: cn(
12163
+ "flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
12164
+ "w-[400px] max-w-[calc(100vw-3rem)]",
12165
+ "h-[min(600px,calc(100vh-6rem))]",
12166
+ "animate-in fade-in slide-in-from-bottom-4 duration-300",
12167
+ isLeft ? "origin-bottom-left" : "origin-bottom-right",
12168
+ classNames?.panel
12169
+ ),
12170
+ children: [
12171
+ /* @__PURE__ */ jsxRuntime.jsx(
12172
+ Header,
12173
+ {
12174
+ businessName,
12175
+ onClose,
12176
+ className: classNames?.header
12177
+ }
12178
+ ),
12179
+ !isActive && !isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
12180
+ Welcome,
12181
+ {
12182
+ greeting,
12183
+ subtitle,
12184
+ starters,
12185
+ onStarter: onStartConversation,
12186
+ className: classNames?.welcome
12187
+ }
12188
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
12189
+ MessageList,
12190
+ {
12191
+ messages,
12192
+ isLoading,
12193
+ isSending,
12194
+ className: classNames?.messages
12195
+ }
12196
+ ),
12197
+ error != null && /* @__PURE__ */ jsxRuntime.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." }),
12198
+ (isActive || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(
12199
+ ChatInput,
12200
+ {
12201
+ placeholder,
12202
+ isSending,
12203
+ onSend,
12204
+ className: classNames?.input
12205
+ }
12206
+ ),
12207
+ /* @__PURE__ */ jsxRuntime.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: [
12208
+ "Powered by",
12209
+ " ",
12210
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
12211
+ ] })
12212
+ ]
12213
+ }
12214
+ );
12215
+ }
12216
+ function Header({
12217
+ businessName,
12218
+ onClose,
12219
+ className
12220
+ }) {
12221
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12222
+ "div",
12223
+ {
12224
+ className: cn(
12225
+ "flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
12226
+ className
12227
+ ),
12228
+ children: [
12229
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-white/15", children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-5 w-5" }) }),
12230
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
12231
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
12232
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
12233
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
12234
+ "Online"
12235
+ ] })
12236
+ ] }),
12237
+ /* @__PURE__ */ jsxRuntime.jsx(
12238
+ "button",
12239
+ {
12240
+ type: "button",
12241
+ onClick: onClose,
12242
+ "aria-label": "Close chat",
12243
+ className: cn(
12244
+ "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
12245
+ "bg-white/10 text-white/70 transition-colors hover:bg-white/20 hover:text-white"
12246
+ ),
12247
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
12248
+ }
12249
+ )
12250
+ ]
12251
+ }
12252
+ );
12253
+ }
12254
+ function Welcome({
12255
+ greeting,
12256
+ subtitle,
12257
+ starters,
12258
+ onStarter,
12259
+ className
12260
+ }) {
12261
+ const defaultStarters = [
12262
+ { icon: "\u{1F50D}", text: "Help me find a product" },
12263
+ { icon: "\u{1F6D2}", text: "Check my cart" },
12264
+ { icon: "\u{1F4E6}", text: "Track my order" }
12265
+ ];
12266
+ const items = starters ?? defaultStarters;
12267
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12268
+ "div",
12269
+ {
12270
+ className: cn(
12271
+ "flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
12272
+ className
12273
+ ),
12274
+ children: [
12275
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-5 flex h-16 w-16 items-center justify-center rounded-2xl bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-7 w-7 text-muted-foreground" }) }),
12276
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
12277
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
12278
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
12279
+ "button",
12280
+ {
12281
+ type: "button",
12282
+ onClick: () => onStarter(s.text),
12283
+ className: cn(
12284
+ "flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
12285
+ "text-left text-[13px] font-medium text-foreground",
12286
+ "transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
12287
+ ),
12288
+ children: [
12289
+ s.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-muted text-[15px]", children: s.icon }),
12290
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: s.text })
12291
+ ]
12292
+ },
12293
+ s.text
12294
+ )) })
12295
+ ]
12296
+ }
12297
+ );
12298
+ }
12299
+ function MessageList({
12300
+ messages,
12301
+ isLoading,
12302
+ isSending,
12303
+ className
12304
+ }) {
12305
+ const bottomRef = React10.useRef(null);
12306
+ React10.useEffect(() => {
12307
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
12308
+ }, [messages.length, isSending]);
12309
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12310
+ "div",
12311
+ {
12312
+ className: cn(
12313
+ "flex flex-1 flex-col gap-1 overflow-y-auto bg-muted/50 px-5 py-5",
12314
+ className
12315
+ ),
12316
+ children: [
12317
+ isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
12318
+ messages.map((msg, i) => {
12319
+ const isCustomer = msg.sender_type === "customer";
12320
+ const prev = messages[i - 1];
12321
+ const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
12322
+ const isOptimistic = msg.id.startsWith("opt_");
12323
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
12324
+ !sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
12325
+ /* @__PURE__ */ jsxRuntime.jsx(
12326
+ "div",
12327
+ {
12328
+ className: cn(
12329
+ "flex max-w-[85%]",
12330
+ isCustomer ? "ml-auto" : "mr-auto"
12331
+ ),
12332
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
12333
+ "div",
12334
+ {
12335
+ className: cn(
12336
+ "rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
12337
+ isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
12338
+ isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
12339
+ isOptimistic && "opacity-70"
12340
+ ),
12341
+ children: [
12342
+ !isCustomer && !sameSenderAsPrev && /* @__PURE__ */ jsxRuntime.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: [
12343
+ /* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
12344
+ "AI"
12345
+ ] }),
12346
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content })
12347
+ ]
12348
+ }
12349
+ )
12350
+ }
12351
+ )
12352
+ ] }, msg.id);
12353
+ }),
12354
+ isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
12355
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
12356
+ ]
12357
+ }
12358
+ );
12359
+ }
12360
+ function ChatInput({
12361
+ placeholder,
12362
+ isSending,
12363
+ onSend,
12364
+ className
12365
+ }) {
12366
+ const [value, setValue] = React10.useState("");
12367
+ const textareaRef = React10.useRef(null);
12368
+ const handleSend = React10.useCallback(() => {
12369
+ const trimmed = value.trim();
12370
+ if (!trimmed || isSending) return;
12371
+ setValue("");
12372
+ if (textareaRef.current) {
12373
+ textareaRef.current.style.height = "auto";
12374
+ }
12375
+ onSend(trimmed);
12376
+ }, [value, isSending, onSend]);
12377
+ const handleKeyDown = React10.useCallback(
12378
+ (e) => {
12379
+ if (e.key === "Enter" && !e.shiftKey) {
12380
+ e.preventDefault();
12381
+ handleSend();
12382
+ }
12383
+ },
12384
+ [handleSend]
12385
+ );
12386
+ const handleInput = React10.useCallback(() => {
12387
+ const el = textareaRef.current;
12388
+ if (!el) return;
12389
+ el.style.height = "auto";
12390
+ el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
12391
+ }, []);
12392
+ const hasContent = value.trim().length > 0;
12393
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12394
+ "div",
12395
+ {
12396
+ className: cn(
12397
+ "flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
12398
+ className
12399
+ ),
12400
+ children: [
12401
+ /* @__PURE__ */ jsxRuntime.jsx(
12402
+ "div",
12403
+ {
12404
+ className: cn(
12405
+ "flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
12406
+ "transition-colors focus-within:border-foreground focus-within:bg-background"
12407
+ ),
12408
+ children: /* @__PURE__ */ jsxRuntime.jsx(
12409
+ "textarea",
12410
+ {
12411
+ ref: textareaRef,
12412
+ value,
12413
+ onChange: (e) => setValue(e.target.value),
12414
+ onKeyDown: handleKeyDown,
12415
+ onInput: handleInput,
12416
+ placeholder,
12417
+ rows: 1,
12418
+ className: cn(
12419
+ "max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
12420
+ "text-sm text-foreground outline-none placeholder:text-muted-foreground"
12421
+ )
12422
+ }
12423
+ )
12424
+ }
12425
+ ),
12426
+ /* @__PURE__ */ jsxRuntime.jsx(
12427
+ "button",
12428
+ {
12429
+ type: "button",
12430
+ onClick: handleSend,
12431
+ disabled: !hasContent || isSending,
12432
+ "aria-label": "Send message",
12433
+ className: cn(
12434
+ "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
12435
+ "transition-all duration-200",
12436
+ hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
12437
+ ),
12438
+ children: /* @__PURE__ */ jsxRuntime.jsx(SendIcon, { className: "h-[18px] w-[18px]" })
12439
+ }
12440
+ )
12441
+ ]
12442
+ }
12443
+ );
12444
+ }
12445
+ function TypingDots() {
12446
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
12447
+ "span",
12448
+ {
12449
+ className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
12450
+ style: { animationDelay: `${i * 160}ms` }
12451
+ },
12452
+ i
12453
+ )) });
12454
+ }
12455
+ function ChatIcon({ className }) {
12456
+ return /* @__PURE__ */ jsxRuntime.jsx(
12457
+ "svg",
12458
+ {
12459
+ className,
12460
+ viewBox: "0 0 24 24",
12461
+ fill: "none",
12462
+ stroke: "currentColor",
12463
+ strokeWidth: 2,
12464
+ strokeLinecap: "round",
12465
+ strokeLinejoin: "round",
12466
+ children: /* @__PURE__ */ jsxRuntime.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" })
12467
+ }
12468
+ );
12469
+ }
12470
+ function CloseIcon({ className }) {
12471
+ return /* @__PURE__ */ jsxRuntime.jsx(
12472
+ "svg",
12473
+ {
12474
+ className,
12475
+ viewBox: "0 0 24 24",
12476
+ fill: "none",
12477
+ stroke: "currentColor",
12478
+ strokeWidth: 2,
12479
+ strokeLinecap: "round",
12480
+ strokeLinejoin: "round",
12481
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
12482
+ }
12483
+ );
12484
+ }
12485
+ function SendIcon({ className }) {
12486
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12487
+ "svg",
12488
+ {
12489
+ className,
12490
+ viewBox: "0 0 24 24",
12491
+ fill: "none",
12492
+ stroke: "currentColor",
12493
+ strokeWidth: 2,
12494
+ strokeLinecap: "round",
12495
+ strokeLinejoin: "round",
12496
+ children: [
12497
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
12498
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
12499
+ ]
12500
+ }
12501
+ );
12502
+ }
12503
+ function SparkleIcon({ className }) {
12504
+ return /* @__PURE__ */ jsxRuntime.jsx(
12505
+ "svg",
12506
+ {
12507
+ className,
12508
+ viewBox: "0 0 24 24",
12509
+ fill: "none",
12510
+ stroke: "currentColor",
12511
+ strokeWidth: 2.5,
12512
+ children: /* @__PURE__ */ jsxRuntime.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" })
12513
+ }
12514
+ );
12515
+ }
11846
12516
 
11847
12517
  Object.defineProperty(exports, "getVariantDisplayName", {
11848
12518
  enumerable: true,
11849
- get: function () { return chunkR4AKAGZQ_js.getVariantDisplayName; }
12519
+ get: function () { return chunk6HCBOIZ4_js.getVariantDisplayName; }
11850
12520
  });
11851
12521
  exports.AccommodationCard = AccommodationCard;
11852
12522
  exports.Ad = Ad;
@@ -11872,6 +12542,7 @@ exports.CataloguePage = CataloguePage;
11872
12542
  exports.CatalogueTemplate = CatalogueTemplate;
11873
12543
  exports.CategoryFilter = CategoryFilter;
11874
12544
  exports.CategoryGrid = CategoryGrid;
12545
+ exports.ChatWidget = ChatWidget;
11875
12546
  exports.CheckoutPage = CheckoutPage;
11876
12547
  exports.CimplifyAccount = CimplifyAccount;
11877
12548
  exports.CimplifyCheckout = CimplifyCheckout;
@@ -11963,6 +12634,7 @@ exports.useBootstrap = useBootstrap;
11963
12634
  exports.useBundles = useBundles;
11964
12635
  exports.useCart = useCart;
11965
12636
  exports.useCategories = useCategories;
12637
+ exports.useChat = useChat;
11966
12638
  exports.useCheckout = useCheckout;
11967
12639
  exports.useCimplify = useCimplify;
11968
12640
  exports.useCimplifyClient = useCimplifyClient;