@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.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 chunkF47XFKYP_js = require('./chunk-F47XFKYP.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 chunkF47XFKYP_js.createCimplifyClient({ publicKey: envPublicKey });
1067
1067
  }
1068
1068
  function getStoredLocationId() {
1069
1069
  if (typeof window === "undefined" || !window.localStorage) {
@@ -3315,6 +3315,122 @@ 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
+ function useChat(options = {}) {
3322
+ const context = useOptionalCimplifyClient();
3323
+ const client = options.client ?? context?.client;
3324
+ const [messages, setMessages] = React10.useState([]);
3325
+ const [isLoading, setIsLoading] = React10.useState(false);
3326
+ const [isSending, setIsSending] = React10.useState(false);
3327
+ const [error, setError] = React10.useState(null);
3328
+ const [conversationOpened, setConversationOpened] = React10.useState(false);
3329
+ const pollRef = React10.useRef(null);
3330
+ const lastActivityRef = React10.useRef(Date.now());
3331
+ const openedRef = React10.useRef(false);
3332
+ const messagesRef = React10.useRef([]);
3333
+ messagesRef.current = messages;
3334
+ const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
3335
+ const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
3336
+ const pollMessages = React10.useCallback(async () => {
3337
+ if (!client) return;
3338
+ const isIdle = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
3339
+ if (isIdle) return;
3340
+ const last = messagesRef.current[messagesRef.current.length - 1];
3341
+ const result = await client.support.getMessages({
3342
+ after: last?.created_at,
3343
+ limit: 50
3344
+ });
3345
+ if (result.ok && result.value.length > 0) {
3346
+ setMessages((prev) => {
3347
+ const ids = new Set(prev.map((m) => m.id));
3348
+ const fresh = result.value.filter((m) => !ids.has(m.id));
3349
+ return fresh.length > 0 ? [...prev, ...fresh] : prev;
3350
+ });
3351
+ }
3352
+ }, [client]);
3353
+ const startPolling = React10.useCallback(() => {
3354
+ if (pollRef.current) clearInterval(pollRef.current);
3355
+ const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
3356
+ pollRef.current = setInterval(pollMessages, interval);
3357
+ }, [pollMessages, pollInterval, pollIdleInterval]);
3358
+ const stopPolling = React10.useCallback(() => {
3359
+ if (pollRef.current) {
3360
+ clearInterval(pollRef.current);
3361
+ pollRef.current = null;
3362
+ }
3363
+ }, []);
3364
+ React10.useEffect(() => stopPolling, [stopPolling]);
3365
+ const openConversation = React10.useCallback(async () => {
3366
+ if (openedRef.current || !client) return;
3367
+ openedRef.current = true;
3368
+ setIsLoading(true);
3369
+ setError(null);
3370
+ const result = await client.support.openConversation();
3371
+ if (result.ok) {
3372
+ setMessages(result.value.messages);
3373
+ setConversationOpened(true);
3374
+ startPolling();
3375
+ } else {
3376
+ openedRef.current = false;
3377
+ setError(result.error);
3378
+ }
3379
+ setIsLoading(false);
3380
+ }, [client, startPolling]);
3381
+ const send = React10.useCallback(
3382
+ async (content) => {
3383
+ if (!client || !content.trim()) return;
3384
+ setError(null);
3385
+ lastActivityRef.current = Date.now();
3386
+ if (!openedRef.current) {
3387
+ await openConversation();
3388
+ if (!openedRef.current) return;
3389
+ }
3390
+ const optimisticId = `opt_${Date.now()}`;
3391
+ const optimistic = {
3392
+ id: optimisticId,
3393
+ sender_type: "customer",
3394
+ content: content.trim(),
3395
+ content_type: "text",
3396
+ attachments: [],
3397
+ metadata: {},
3398
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
3399
+ };
3400
+ setMessages((prev) => [...prev, optimistic]);
3401
+ setIsSending(true);
3402
+ const result = await client.support.sendMessage(content.trim());
3403
+ if (result.ok) {
3404
+ setMessages(
3405
+ (prev) => prev.map((m) => m.id === optimisticId ? result.value : m)
3406
+ );
3407
+ } else {
3408
+ setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
3409
+ setError(result.error);
3410
+ }
3411
+ setIsSending(false);
3412
+ },
3413
+ [client, openConversation]
3414
+ );
3415
+ const startConversation = React10.useCallback(
3416
+ async (starterText) => {
3417
+ await openConversation();
3418
+ if (starterText) {
3419
+ await send(starterText);
3420
+ }
3421
+ },
3422
+ [openConversation, send]
3423
+ );
3424
+ return {
3425
+ messages,
3426
+ isLoading,
3427
+ isSending,
3428
+ error,
3429
+ isActive: conversationOpened && messages.length > 0,
3430
+ send,
3431
+ startConversation
3432
+ };
3433
+ }
3318
3434
  var ElementsContext = React10.createContext({
3319
3435
  elements: null,
3320
3436
  isReady: false
@@ -3737,7 +3853,7 @@ function VariantSelector({
3737
3853
  {
3738
3854
  "data-cimplify-variant-name": true,
3739
3855
  className: cn("flex-1 min-w-0 text-sm", classNames?.name),
3740
- children: chunkR4AKAGZQ_js.getVariantDisplayName(variant, productName)
3856
+ children: chunkF47XFKYP_js.getVariantDisplayName(variant, productName)
3741
3857
  }
3742
3858
  ),
3743
3859
  /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-variant-pricing": true, className: cn("text-sm text-muted-foreground", classNames?.pricing), children: [
@@ -4666,8 +4782,26 @@ function CompositeSelector({
4666
4782
  )
4667
4783
  ] });
4668
4784
  }
4785
+
4786
+ // src/react/utils/format-duration.ts
4787
+ var UNIT_LABELS = {
4788
+ minutes: { singular: "min", plural: "min" },
4789
+ hours: { singular: "hr", plural: "hrs" },
4790
+ days: { singular: "day", plural: "days" },
4791
+ nights: { singular: "night", plural: "nights" },
4792
+ weeks: { singular: "week", plural: "weeks" },
4793
+ months: { singular: "month", plural: "months" },
4794
+ years: { singular: "year", plural: "years" }
4795
+ };
4796
+ function formatDuration2(value, unit) {
4797
+ const resolved = unit ?? "minutes";
4798
+ const labels = UNIT_LABELS[resolved];
4799
+ const label = value === 1 ? labels.singular : labels.plural;
4800
+ return `${value} ${label}`;
4801
+ }
4669
4802
  var FREQUENCY_LABELS = {
4670
4803
  weekly: "Weekly",
4804
+ biweekly: "Biweekly",
4671
4805
  monthly: "Monthly",
4672
4806
  quarterly: "Quarterly",
4673
4807
  annually: "Annually"
@@ -4936,12 +5070,12 @@ function FieldInput({
4936
5070
  classNames?.input
4937
5071
  );
4938
5072
  switch (field.field_type) {
4939
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Text:
4940
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Url:
5073
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Text:
5074
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Url:
4941
5075
  return /* @__PURE__ */ jsxRuntime.jsx(
4942
5076
  "input",
4943
5077
  {
4944
- type: field.field_type === chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
5078
+ type: field.field_type === chunkF47XFKYP_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
4945
5079
  value: typeof value === "string" ? value : "",
4946
5080
  onChange: (e) => onValueChange(e.target.value),
4947
5081
  placeholder: field.placeholder,
@@ -4950,7 +5084,7 @@ function FieldInput({
4950
5084
  className: inputClass
4951
5085
  }
4952
5086
  );
4953
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Textarea:
5087
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Textarea:
4954
5088
  return /* @__PURE__ */ jsxRuntime.jsx(
4955
5089
  "textarea",
4956
5090
  {
@@ -4963,7 +5097,7 @@ function FieldInput({
4963
5097
  className: cn(inputClass, "resize-none", classNames?.textarea)
4964
5098
  }
4965
5099
  );
4966
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Number:
5100
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Number:
4967
5101
  return /* @__PURE__ */ jsxRuntime.jsx(
4968
5102
  "input",
4969
5103
  {
@@ -4977,7 +5111,7 @@ function FieldInput({
4977
5111
  className: inputClass
4978
5112
  }
4979
5113
  );
4980
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Select:
5114
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Select:
4981
5115
  return /* @__PURE__ */ jsxRuntime.jsxs(
4982
5116
  "select",
4983
5117
  {
@@ -4991,7 +5125,7 @@ function FieldInput({
4991
5125
  ]
4992
5126
  }
4993
5127
  );
4994
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Radio:
5128
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Radio:
4995
5129
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
4996
5130
  "label",
4997
5131
  {
@@ -5016,7 +5150,7 @@ function FieldInput({
5016
5150
  },
5017
5151
  opt
5018
5152
  )) });
5019
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Checkbox:
5153
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Checkbox:
5020
5154
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
5021
5155
  /* @__PURE__ */ jsxRuntime.jsx(
5022
5156
  "input",
@@ -5029,7 +5163,7 @@ function FieldInput({
5029
5163
  ),
5030
5164
  field.placeholder || field.name
5031
5165
  ] });
5032
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Color:
5166
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Color:
5033
5167
  return /* @__PURE__ */ jsxRuntime.jsx(
5034
5168
  "input",
5035
5169
  {
@@ -5039,7 +5173,7 @@ function FieldInput({
5039
5173
  className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
5040
5174
  }
5041
5175
  );
5042
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Date:
5176
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Date:
5043
5177
  return /* @__PURE__ */ jsxRuntime.jsx(
5044
5178
  "input",
5045
5179
  {
@@ -5050,8 +5184,8 @@ function FieldInput({
5050
5184
  className: inputClass
5051
5185
  }
5052
5186
  );
5053
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.File:
5054
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Image:
5187
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.File:
5188
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Image:
5055
5189
  return /* @__PURE__ */ jsxRuntime.jsx(
5056
5190
  FileUploadInput,
5057
5191
  {
@@ -5062,7 +5196,7 @@ function FieldInput({
5062
5196
  classNames
5063
5197
  }
5064
5198
  );
5065
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Email:
5199
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Email:
5066
5200
  return /* @__PURE__ */ jsxRuntime.jsx(
5067
5201
  "input",
5068
5202
  {
@@ -5074,7 +5208,7 @@ function FieldInput({
5074
5208
  className: inputClass
5075
5209
  }
5076
5210
  );
5077
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.DateTime: {
5211
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.DateTime: {
5078
5212
  const dtLocalValue = typeof value === "string" && value.includes("T") ? value.slice(0, 16) : typeof value === "string" ? value : "";
5079
5213
  return /* @__PURE__ */ jsxRuntime.jsx(
5080
5214
  "input",
@@ -5094,7 +5228,7 @@ function FieldInput({
5094
5228
  }
5095
5229
  );
5096
5230
  }
5097
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Time:
5231
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Time:
5098
5232
  return /* @__PURE__ */ jsxRuntime.jsx(
5099
5233
  "input",
5100
5234
  {
@@ -5105,7 +5239,7 @@ function FieldInput({
5105
5239
  className: inputClass
5106
5240
  }
5107
5241
  );
5108
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.MultiSelect:
5242
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.MultiSelect:
5109
5243
  return /* @__PURE__ */ jsxRuntime.jsx(
5110
5244
  MultiSelectInput,
5111
5245
  {
@@ -5115,7 +5249,7 @@ function FieldInput({
5115
5249
  classNames
5116
5250
  }
5117
5251
  );
5118
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.DateRange:
5252
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.DateRange:
5119
5253
  return /* @__PURE__ */ jsxRuntime.jsx(
5120
5254
  DateRangeInput,
5121
5255
  {
@@ -5126,7 +5260,7 @@ function FieldInput({
5126
5260
  inputClass
5127
5261
  }
5128
5262
  );
5129
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Address:
5263
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Address:
5130
5264
  return /* @__PURE__ */ jsxRuntime.jsx(
5131
5265
  AddressInput,
5132
5266
  {
@@ -5137,7 +5271,7 @@ function FieldInput({
5137
5271
  inputClass
5138
5272
  }
5139
5273
  );
5140
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Location:
5274
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Location:
5141
5275
  return /* @__PURE__ */ jsxRuntime.jsx(
5142
5276
  LocationInput,
5143
5277
  {
@@ -5148,7 +5282,7 @@ function FieldInput({
5148
5282
  inputClass
5149
5283
  }
5150
5284
  );
5151
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Phone:
5285
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Phone:
5152
5286
  return /* @__PURE__ */ jsxRuntime.jsx(
5153
5287
  PhoneInput,
5154
5288
  {
@@ -5159,7 +5293,7 @@ function FieldInput({
5159
5293
  inputClass
5160
5294
  }
5161
5295
  );
5162
- case chunkR4AKAGZQ_js.INPUT_FIELD_TYPE.Signature:
5296
+ case chunkF47XFKYP_js.INPUT_FIELD_TYPE.Signature:
5163
5297
  return /* @__PURE__ */ jsxRuntime.jsx(
5164
5298
  SignatureInput,
5165
5299
  {
@@ -5192,7 +5326,7 @@ function FileUploadInput({
5192
5326
  const [isUploading, setIsUploading] = React10__default.default.useState(false);
5193
5327
  const fileUrl = typeof value === "string" ? value : void 0;
5194
5328
  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;
5329
+ const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunkF47XFKYP_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
5196
5330
  const handleFile = async (file) => {
5197
5331
  if (!onFileUpload) return;
5198
5332
  if (field.validation?.max_size_mb) {
@@ -5210,7 +5344,7 @@ function FileUploadInput({
5210
5344
  }
5211
5345
  };
5212
5346
  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" }),
5347
+ field.field_type === chunkF47XFKYP_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
5348
  /* @__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
5349
  /* @__PURE__ */ jsxRuntime.jsx(
5216
5350
  "button",
@@ -5236,7 +5370,7 @@ function FileUploadInput({
5236
5370
  }
5237
5371
  ),
5238
5372
  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"}` }),
5373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunkF47XFKYP_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
5240
5374
  acceptedFormats && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
5241
5375
  acceptedFormats.map((f) => f.toUpperCase()).join(", "),
5242
5376
  field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
@@ -5991,9 +6125,7 @@ function ProductCustomizer({
5991
6125
  )) }),
5992
6126
  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
6127
  "Duration: ",
5994
- product.duration_minutes,
5995
- " ",
5996
- product.duration_unit ?? "minutes"
6128
+ formatDuration2(product.duration_minutes, product.duration_unit)
5997
6129
  ] }),
5998
6130
  showSpecialInstructions && !isDigital && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-customizer-special-instructions": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5999
6131
  "textarea",
@@ -6166,7 +6298,7 @@ function CartLineItemRow({
6166
6298
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item": true, className: classNames?.item, children: [
6167
6299
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item-info": true, children: [
6168
6300
  /* @__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) }),
6301
+ item.variant && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-variant": true, children: chunkF47XFKYP_js.getVariantDisplayName(item.variant, item.product.name) }),
6170
6302
  item.addOnOptions && item.addOnOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-cart-item-addons": true, children: [
6171
6303
  "+ ",
6172
6304
  item.addOnOptions.map((opt) => opt.name).join(", ")
@@ -6175,7 +6307,7 @@ function CartLineItemRow({
6175
6307
  item.specialInstructions && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-instructions": true, className: "text-xs text-muted-foreground", children: item.specialInstructions }),
6176
6308
  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
6309
  /* @__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) })
6310
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-input-value": true, children: input.field_type === chunkF47XFKYP_js.INPUT_FIELD_TYPE.File || input.field_type === chunkF47XFKYP_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
6179
6311
  ] }, input.field_id)) }),
6180
6312
  (item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-info": true, className: classNames?.serviceInfo, children: [
6181
6313
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
@@ -6695,7 +6827,7 @@ function FoodProductCard({
6695
6827
  const tags = product.tags || [];
6696
6828
  const isSignature = product.metadata?.is_signature === true;
6697
6829
  const isNew = product.metadata?.is_new === true;
6698
- const isComposite = product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite;
6830
+ const isComposite = product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Composite;
6699
6831
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
6700
6832
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "square", renderImage, children: [
6701
6833
  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 +7088,7 @@ function CompositeProductCard({
6956
7088
  ] })
6957
7089
  ] });
6958
7090
  }
6959
- function formatDuration2(minutes, unit) {
7091
+ function formatDuration3(minutes, unit) {
6960
7092
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
6961
7093
  if (minutes >= 60) {
6962
7094
  const h = Math.floor(minutes / 60);
@@ -6977,7 +7109,7 @@ function StandardServiceCard({
6977
7109
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
6978
7110
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
6979
7111
  /* @__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) }),
7112
+ 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
7113
  /* @__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
7114
  ] }),
6983
7115
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
@@ -7011,7 +7143,7 @@ function StandardServiceCard({
7011
7143
  ] })
7012
7144
  ] });
7013
7145
  }
7014
- function formatDuration3(minutes, unit) {
7146
+ function formatDuration4(minutes, unit) {
7015
7147
  if (unit && unit !== "minutes") return `${minutes} ${unit}`;
7016
7148
  if (minutes >= 60) {
7017
7149
  const h = Math.floor(minutes / 60);
@@ -7036,7 +7168,7 @@ function CompactServiceCard({
7036
7168
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
7037
7169
  product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
7038
7170
  /* @__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) }),
7171
+ 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
7172
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10.5px] font-medium text-emerald-600", children: [
7041
7173
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-[6px] h-[6px] rounded-full bg-emerald-500" }),
7042
7174
  "Available"
@@ -7076,7 +7208,7 @@ function ScheduleServiceCard({
7076
7208
  }) {
7077
7209
  const image = product.image_url || product.images?.[0] || "";
7078
7210
  const href = `/products/${product.slug}`;
7079
- const durationLabel = product.duration_minutes != null ? `${product.duration_minutes} ${product.duration_unit ?? "min"}` : null;
7211
+ const durationLabel = product.duration_minutes != null ? formatDuration2(product.duration_minutes, product.duration_unit) : null;
7080
7212
  const displaySlots = (slots || []).slice(0, 3);
7081
7213
  const shellClass = cn(
7082
7214
  "group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden",
@@ -7129,7 +7261,7 @@ function ScheduleServiceCard({
7129
7261
  }
7130
7262
  return /* @__PURE__ */ jsxRuntime.jsx("a", { href, className: shellClass, "data-cimplify-card": true, children: inner });
7131
7263
  }
7132
- var UNIT_LABELS = {
7264
+ var UNIT_LABELS2 = {
7133
7265
  minutes: "min",
7134
7266
  hours: "hr",
7135
7267
  days: "day",
@@ -7147,7 +7279,7 @@ function RentalServiceCard({
7147
7279
  }) {
7148
7280
  const image = product.image_url || product.images?.[0] || "";
7149
7281
  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";
7282
+ const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
7151
7283
  const status = product.inventory_status;
7152
7284
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
7153
7285
  /* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
@@ -7267,7 +7399,7 @@ function AccommodationCard({
7267
7399
  ] })
7268
7400
  ] });
7269
7401
  }
7270
- var UNIT_LABELS2 = {
7402
+ var UNIT_LABELS3 = {
7271
7403
  weeks: "week",
7272
7404
  months: "mo",
7273
7405
  years: "yr"
@@ -7282,7 +7414,7 @@ function LeaseServiceCard({
7282
7414
  const image = product.image_url || product.images?.[0] || "";
7283
7415
  const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
7284
7416
  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";
7417
+ const unit = product.duration_unit ? UNIT_LABELS3[product.duration_unit] || product.duration_unit : "mo";
7286
7418
  const tags = product.tags || [];
7287
7419
  const cancellationHours = product.cancellation_window_minutes ? Math.floor(product.cancellation_window_minutes / 60) : null;
7288
7420
  return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
@@ -7340,6 +7472,7 @@ function LeaseServiceCard({
7340
7472
  }
7341
7473
  var FREQUENCY_LABELS2 = {
7342
7474
  weekly: "/week",
7475
+ biweekly: "/2wk",
7343
7476
  monthly: "/mo",
7344
7477
  quarterly: "/qtr",
7345
7478
  annually: "/yr"
@@ -7447,26 +7580,26 @@ var BUILT_IN_CARDS = {
7447
7580
  ["composite" /* Composite */]: CompositeProductCard,
7448
7581
  ["subscription" /* Subscription */]: SubscriptionCard
7449
7582
  };
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]);
7583
+ var RENTAL_UNITS = /* @__PURE__ */ new Set([chunkF47XFKYP_js.DURATION_UNIT.Days, chunkF47XFKYP_js.DURATION_UNIT.Hours]);
7584
+ var LEASE_UNITS = /* @__PURE__ */ new Set([chunkF47XFKYP_js.DURATION_UNIT.Weeks, chunkF47XFKYP_js.DURATION_UNIT.Months, chunkF47XFKYP_js.DURATION_UNIT.Years]);
7452
7585
  function resolveCardVariant(product) {
7453
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Bundle) {
7586
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Bundle) {
7454
7587
  return "bundle" /* Bundle */;
7455
7588
  }
7456
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite) {
7589
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Composite) {
7457
7590
  return "composite" /* Composite */;
7458
7591
  }
7459
7592
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
7460
7593
  return "wholesale" /* Wholesale */;
7461
7594
  }
7462
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Digital) {
7595
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Digital) {
7463
7596
  return "digital" /* Digital */;
7464
7597
  }
7465
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Service) {
7598
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Service) {
7466
7599
  if (product.duration_unit && RENTAL_UNITS.has(product.duration_unit)) {
7467
7600
  return "rental" /* Rental */;
7468
7601
  }
7469
- if (product.duration_unit === chunkR4AKAGZQ_js.DURATION_UNIT.Nights) {
7602
+ if (product.duration_unit === chunkF47XFKYP_js.DURATION_UNIT.Nights) {
7470
7603
  return "accommodation" /* Accommodation */;
7471
7604
  }
7472
7605
  if (product.duration_unit && LEASE_UNITS.has(product.duration_unit)) {
@@ -7477,10 +7610,10 @@ function resolveCardVariant(product) {
7477
7610
  }
7478
7611
  return "standard" /* Standard */;
7479
7612
  }
7480
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Food) {
7613
+ if (product.render_hint === chunkF47XFKYP_js.RENDER_HINT.Food) {
7481
7614
  return "food" /* Food */;
7482
7615
  }
7483
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Physical) {
7616
+ if (product.render_hint === chunkF47XFKYP_js.RENDER_HINT.Physical) {
7484
7617
  return "retail" /* Retail */;
7485
7618
  }
7486
7619
  return "retail" /* Retail */;
@@ -9842,15 +9975,6 @@ function WholesaleProductLayout({
9842
9975
  )
9843
9976
  ] });
9844
9977
  }
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
9978
  function ServiceProductLayout({
9855
9979
  product,
9856
9980
  onAddToCart,
@@ -9882,7 +10006,7 @@ function ServiceProductLayout({
9882
10006
  /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: product.default_price, className: "text-2xl font-bold text-primary mt-2 block" })
9883
10007
  ] }),
9884
10008
  /* @__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) }),
10009
+ 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
10010
  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
10011
  /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: depositAmount }),
9888
10012
  " deposit"
@@ -9919,7 +10043,7 @@ function ServiceProductLayout({
9919
10043
  cancellationHours,
9920
10044
  " hours"
9921
10045
  ] }),
9922
- " before the appointment"
10046
+ " before the booking"
9923
10047
  ] }),
9924
10048
  product.no_show_fee && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
9925
10049
  "No-show fee: ",
@@ -10250,25 +10374,25 @@ function resolveTemplateKey(product) {
10250
10374
  if (typeof metaTemplate === "string" && metaTemplate.trim()) {
10251
10375
  return metaTemplate.trim();
10252
10376
  }
10253
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Bundle) {
10377
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Bundle) {
10254
10378
  return "bundle" /* Bundle */;
10255
10379
  }
10256
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Composite) {
10380
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Composite) {
10257
10381
  return "composite" /* Composite */;
10258
10382
  }
10259
10383
  if (product.quantity_pricing && product.quantity_pricing.length > 1) {
10260
10384
  return "wholesale" /* Wholesale */;
10261
10385
  }
10262
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Service) {
10386
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Service) {
10263
10387
  return "service" /* Service */;
10264
10388
  }
10265
- if (product.type === chunkR4AKAGZQ_js.PRODUCT_TYPE.Digital) {
10389
+ if (product.type === chunkF47XFKYP_js.PRODUCT_TYPE.Digital) {
10266
10390
  return "digital" /* Digital */;
10267
10391
  }
10268
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Food) {
10392
+ if (product.render_hint === chunkF47XFKYP_js.RENDER_HINT.Food) {
10269
10393
  return "food" /* Food */;
10270
10394
  }
10271
- if (product.render_hint === chunkR4AKAGZQ_js.RENDER_HINT.Physical) {
10395
+ if (product.render_hint === chunkF47XFKYP_js.RENDER_HINT.Physical) {
10272
10396
  return "physical" /* Physical */;
10273
10397
  }
10274
10398
  return "default" /* Default */;
@@ -11843,10 +11967,496 @@ function ResourcePicker({
11843
11967
  }
11844
11968
  );
11845
11969
  }
11970
+ function ChatWidget({
11971
+ client: clientProp,
11972
+ businessName = "Support",
11973
+ greeting = "Hi there!",
11974
+ subtitle = "Ask us anything \u2014 we typically reply in a few seconds.",
11975
+ starters,
11976
+ placeholder = "Type a message\u2026",
11977
+ position = "bottom-right",
11978
+ defaultOpen = false,
11979
+ pollInterval,
11980
+ className,
11981
+ classNames
11982
+ }) {
11983
+ const context = useOptionalCimplifyClient();
11984
+ const client = clientProp ?? context?.client;
11985
+ const [open, setOpen] = React10.useState(defaultOpen);
11986
+ const [unread, setUnread] = React10.useState(0);
11987
+ const { messages, isLoading, isSending, error, isActive, send, startConversation } = useChat({ client, pollInterval });
11988
+ const prevCountRef = React10.useRef(messages.length);
11989
+ React10.useEffect(() => {
11990
+ if (!open && messages.length > prevCountRef.current) {
11991
+ const newMessages = messages.slice(prevCountRef.current);
11992
+ const incomingCount = newMessages.filter(
11993
+ (m) => m.sender_type !== "customer"
11994
+ ).length;
11995
+ if (incomingCount > 0) setUnread((n) => n + incomingCount);
11996
+ }
11997
+ prevCountRef.current = messages.length;
11998
+ }, [messages, open]);
11999
+ const handleOpen = React10.useCallback(() => {
12000
+ setOpen(true);
12001
+ setUnread(0);
12002
+ }, []);
12003
+ const handleClose = React10.useCallback(() => setOpen(false), []);
12004
+ const isLeft = position === "bottom-left";
12005
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12006
+ "div",
12007
+ {
12008
+ className: cn(
12009
+ "fixed bottom-6 z-[9999]",
12010
+ isLeft ? "left-6" : "right-6",
12011
+ className,
12012
+ classNames?.root
12013
+ ),
12014
+ children: [
12015
+ !open && /* @__PURE__ */ jsxRuntime.jsx(
12016
+ Bubble,
12017
+ {
12018
+ unread,
12019
+ onClick: handleOpen,
12020
+ className: classNames?.bubble
12021
+ }
12022
+ ),
12023
+ open && /* @__PURE__ */ jsxRuntime.jsx(
12024
+ Panel,
12025
+ {
12026
+ businessName,
12027
+ greeting,
12028
+ subtitle,
12029
+ starters,
12030
+ placeholder,
12031
+ messages,
12032
+ isLoading,
12033
+ isSending,
12034
+ error,
12035
+ isActive,
12036
+ isLeft,
12037
+ onClose: handleClose,
12038
+ onSend: send,
12039
+ onStartConversation: startConversation,
12040
+ classNames
12041
+ }
12042
+ )
12043
+ ]
12044
+ }
12045
+ );
12046
+ }
12047
+ function Bubble({
12048
+ unread,
12049
+ onClick,
12050
+ className
12051
+ }) {
12052
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12053
+ "button",
12054
+ {
12055
+ type: "button",
12056
+ onClick,
12057
+ "aria-label": "Open chat",
12058
+ className: cn(
12059
+ "relative flex h-14 w-14 items-center justify-center rounded-full",
12060
+ "bg-foreground text-background shadow-lg",
12061
+ "transition-transform duration-300 ease-out",
12062
+ "hover:scale-[1.08] active:scale-95",
12063
+ className
12064
+ ),
12065
+ children: [
12066
+ /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-6 w-6" }),
12067
+ unread > 0 && /* @__PURE__ */ jsxRuntime.jsx(
12068
+ "span",
12069
+ {
12070
+ className: cn(
12071
+ "absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
12072
+ "rounded-full bg-red-500 text-[10px] font-bold text-white",
12073
+ "ring-2 ring-background"
12074
+ ),
12075
+ children: unread > 9 ? "9+" : unread
12076
+ }
12077
+ )
12078
+ ]
12079
+ }
12080
+ );
12081
+ }
12082
+ function Panel({
12083
+ businessName,
12084
+ greeting,
12085
+ subtitle,
12086
+ starters,
12087
+ placeholder,
12088
+ messages,
12089
+ isLoading,
12090
+ isSending,
12091
+ error,
12092
+ isActive,
12093
+ isLeft,
12094
+ onClose,
12095
+ onSend,
12096
+ onStartConversation,
12097
+ classNames
12098
+ }) {
12099
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12100
+ "div",
12101
+ {
12102
+ className: cn(
12103
+ "flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
12104
+ "w-[400px] max-w-[calc(100vw-3rem)]",
12105
+ "h-[min(600px,calc(100vh-6rem))]",
12106
+ "animate-in fade-in slide-in-from-bottom-4 duration-300",
12107
+ isLeft ? "origin-bottom-left" : "origin-bottom-right",
12108
+ classNames?.panel
12109
+ ),
12110
+ children: [
12111
+ /* @__PURE__ */ jsxRuntime.jsx(
12112
+ Header,
12113
+ {
12114
+ businessName,
12115
+ onClose,
12116
+ className: classNames?.header
12117
+ }
12118
+ ),
12119
+ !isActive && !isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
12120
+ Welcome,
12121
+ {
12122
+ greeting,
12123
+ subtitle,
12124
+ starters,
12125
+ onStarter: onStartConversation,
12126
+ className: classNames?.welcome
12127
+ }
12128
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
12129
+ MessageList,
12130
+ {
12131
+ messages,
12132
+ isLoading,
12133
+ isSending,
12134
+ className: classNames?.messages
12135
+ }
12136
+ ),
12137
+ 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." }),
12138
+ (isActive || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(
12139
+ ChatInput,
12140
+ {
12141
+ placeholder,
12142
+ isSending,
12143
+ onSend,
12144
+ className: classNames?.input
12145
+ }
12146
+ ),
12147
+ /* @__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: [
12148
+ "Powered by",
12149
+ " ",
12150
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
12151
+ ] })
12152
+ ]
12153
+ }
12154
+ );
12155
+ }
12156
+ function Header({
12157
+ businessName,
12158
+ onClose,
12159
+ className
12160
+ }) {
12161
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12162
+ "div",
12163
+ {
12164
+ className: cn(
12165
+ "flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
12166
+ className
12167
+ ),
12168
+ children: [
12169
+ /* @__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" }) }),
12170
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
12171
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
12172
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
12173
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
12174
+ "Online"
12175
+ ] })
12176
+ ] }),
12177
+ /* @__PURE__ */ jsxRuntime.jsx(
12178
+ "button",
12179
+ {
12180
+ type: "button",
12181
+ onClick: onClose,
12182
+ "aria-label": "Close chat",
12183
+ className: cn(
12184
+ "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
12185
+ "bg-white/10 text-white/70 transition-colors hover:bg-white/20 hover:text-white"
12186
+ ),
12187
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
12188
+ }
12189
+ )
12190
+ ]
12191
+ }
12192
+ );
12193
+ }
12194
+ function Welcome({
12195
+ greeting,
12196
+ subtitle,
12197
+ starters,
12198
+ onStarter,
12199
+ className
12200
+ }) {
12201
+ const defaultStarters = [
12202
+ { icon: "\u{1F50D}", text: "Help me find a product" },
12203
+ { icon: "\u{1F6D2}", text: "Check my cart" },
12204
+ { icon: "\u{1F4E6}", text: "Track my order" }
12205
+ ];
12206
+ const items = starters ?? defaultStarters;
12207
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12208
+ "div",
12209
+ {
12210
+ className: cn(
12211
+ "flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
12212
+ className
12213
+ ),
12214
+ children: [
12215
+ /* @__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" }) }),
12216
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
12217
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
12218
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
12219
+ "button",
12220
+ {
12221
+ type: "button",
12222
+ onClick: () => onStarter(s.text),
12223
+ className: cn(
12224
+ "flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
12225
+ "text-left text-[13px] font-medium text-foreground",
12226
+ "transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
12227
+ ),
12228
+ children: [
12229
+ 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 }),
12230
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: s.text })
12231
+ ]
12232
+ },
12233
+ s.text
12234
+ )) })
12235
+ ]
12236
+ }
12237
+ );
12238
+ }
12239
+ function MessageList({
12240
+ messages,
12241
+ isLoading,
12242
+ isSending,
12243
+ className
12244
+ }) {
12245
+ const bottomRef = React10.useRef(null);
12246
+ React10.useEffect(() => {
12247
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
12248
+ }, [messages.length, isSending]);
12249
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12250
+ "div",
12251
+ {
12252
+ className: cn(
12253
+ "flex flex-1 flex-col gap-1 overflow-y-auto bg-muted/50 px-5 py-5",
12254
+ className
12255
+ ),
12256
+ children: [
12257
+ isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
12258
+ messages.map((msg, i) => {
12259
+ const isCustomer = msg.sender_type === "customer";
12260
+ const prev = messages[i - 1];
12261
+ const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
12262
+ const isOptimistic = msg.id.startsWith("opt_");
12263
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
12264
+ !sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
12265
+ /* @__PURE__ */ jsxRuntime.jsx(
12266
+ "div",
12267
+ {
12268
+ className: cn(
12269
+ "flex max-w-[85%]",
12270
+ isCustomer ? "ml-auto" : "mr-auto"
12271
+ ),
12272
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
12273
+ "div",
12274
+ {
12275
+ className: cn(
12276
+ "rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
12277
+ isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
12278
+ isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
12279
+ isOptimistic && "opacity-70"
12280
+ ),
12281
+ children: [
12282
+ !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: [
12283
+ /* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
12284
+ "AI"
12285
+ ] }),
12286
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content })
12287
+ ]
12288
+ }
12289
+ )
12290
+ }
12291
+ )
12292
+ ] }, msg.id);
12293
+ }),
12294
+ isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
12295
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
12296
+ ]
12297
+ }
12298
+ );
12299
+ }
12300
+ function ChatInput({
12301
+ placeholder,
12302
+ isSending,
12303
+ onSend,
12304
+ className
12305
+ }) {
12306
+ const [value, setValue] = React10.useState("");
12307
+ const textareaRef = React10.useRef(null);
12308
+ const handleSend = React10.useCallback(() => {
12309
+ const trimmed = value.trim();
12310
+ if (!trimmed || isSending) return;
12311
+ setValue("");
12312
+ if (textareaRef.current) {
12313
+ textareaRef.current.style.height = "auto";
12314
+ }
12315
+ onSend(trimmed);
12316
+ }, [value, isSending, onSend]);
12317
+ const handleKeyDown = React10.useCallback(
12318
+ (e) => {
12319
+ if (e.key === "Enter" && !e.shiftKey) {
12320
+ e.preventDefault();
12321
+ handleSend();
12322
+ }
12323
+ },
12324
+ [handleSend]
12325
+ );
12326
+ const handleInput = React10.useCallback(() => {
12327
+ const el = textareaRef.current;
12328
+ if (!el) return;
12329
+ el.style.height = "auto";
12330
+ el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
12331
+ }, []);
12332
+ const hasContent = value.trim().length > 0;
12333
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12334
+ "div",
12335
+ {
12336
+ className: cn(
12337
+ "flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
12338
+ className
12339
+ ),
12340
+ children: [
12341
+ /* @__PURE__ */ jsxRuntime.jsx(
12342
+ "div",
12343
+ {
12344
+ className: cn(
12345
+ "flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
12346
+ "transition-colors focus-within:border-foreground focus-within:bg-background"
12347
+ ),
12348
+ children: /* @__PURE__ */ jsxRuntime.jsx(
12349
+ "textarea",
12350
+ {
12351
+ ref: textareaRef,
12352
+ value,
12353
+ onChange: (e) => setValue(e.target.value),
12354
+ onKeyDown: handleKeyDown,
12355
+ onInput: handleInput,
12356
+ placeholder,
12357
+ rows: 1,
12358
+ className: cn(
12359
+ "max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
12360
+ "text-sm text-foreground outline-none placeholder:text-muted-foreground"
12361
+ )
12362
+ }
12363
+ )
12364
+ }
12365
+ ),
12366
+ /* @__PURE__ */ jsxRuntime.jsx(
12367
+ "button",
12368
+ {
12369
+ type: "button",
12370
+ onClick: handleSend,
12371
+ disabled: !hasContent || isSending,
12372
+ "aria-label": "Send message",
12373
+ className: cn(
12374
+ "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
12375
+ "transition-all duration-200",
12376
+ hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
12377
+ ),
12378
+ children: /* @__PURE__ */ jsxRuntime.jsx(SendIcon, { className: "h-[18px] w-[18px]" })
12379
+ }
12380
+ )
12381
+ ]
12382
+ }
12383
+ );
12384
+ }
12385
+ function TypingDots() {
12386
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
12387
+ "span",
12388
+ {
12389
+ className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
12390
+ style: { animationDelay: `${i * 160}ms` }
12391
+ },
12392
+ i
12393
+ )) });
12394
+ }
12395
+ function ChatIcon({ className }) {
12396
+ return /* @__PURE__ */ jsxRuntime.jsx(
12397
+ "svg",
12398
+ {
12399
+ className,
12400
+ viewBox: "0 0 24 24",
12401
+ fill: "none",
12402
+ stroke: "currentColor",
12403
+ strokeWidth: 2,
12404
+ strokeLinecap: "round",
12405
+ strokeLinejoin: "round",
12406
+ 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" })
12407
+ }
12408
+ );
12409
+ }
12410
+ function CloseIcon({ className }) {
12411
+ return /* @__PURE__ */ jsxRuntime.jsx(
12412
+ "svg",
12413
+ {
12414
+ className,
12415
+ viewBox: "0 0 24 24",
12416
+ fill: "none",
12417
+ stroke: "currentColor",
12418
+ strokeWidth: 2,
12419
+ strokeLinecap: "round",
12420
+ strokeLinejoin: "round",
12421
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
12422
+ }
12423
+ );
12424
+ }
12425
+ function SendIcon({ className }) {
12426
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12427
+ "svg",
12428
+ {
12429
+ className,
12430
+ viewBox: "0 0 24 24",
12431
+ fill: "none",
12432
+ stroke: "currentColor",
12433
+ strokeWidth: 2,
12434
+ strokeLinecap: "round",
12435
+ strokeLinejoin: "round",
12436
+ children: [
12437
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
12438
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
12439
+ ]
12440
+ }
12441
+ );
12442
+ }
12443
+ function SparkleIcon({ className }) {
12444
+ return /* @__PURE__ */ jsxRuntime.jsx(
12445
+ "svg",
12446
+ {
12447
+ className,
12448
+ viewBox: "0 0 24 24",
12449
+ fill: "none",
12450
+ stroke: "currentColor",
12451
+ strokeWidth: 2.5,
12452
+ 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" })
12453
+ }
12454
+ );
12455
+ }
11846
12456
 
11847
12457
  Object.defineProperty(exports, "getVariantDisplayName", {
11848
12458
  enumerable: true,
11849
- get: function () { return chunkR4AKAGZQ_js.getVariantDisplayName; }
12459
+ get: function () { return chunkF47XFKYP_js.getVariantDisplayName; }
11850
12460
  });
11851
12461
  exports.AccommodationCard = AccommodationCard;
11852
12462
  exports.Ad = Ad;
@@ -11872,6 +12482,7 @@ exports.CataloguePage = CataloguePage;
11872
12482
  exports.CatalogueTemplate = CatalogueTemplate;
11873
12483
  exports.CategoryFilter = CategoryFilter;
11874
12484
  exports.CategoryGrid = CategoryGrid;
12485
+ exports.ChatWidget = ChatWidget;
11875
12486
  exports.CheckoutPage = CheckoutPage;
11876
12487
  exports.CimplifyAccount = CimplifyAccount;
11877
12488
  exports.CimplifyCheckout = CimplifyCheckout;
@@ -11963,6 +12574,7 @@ exports.useBootstrap = useBootstrap;
11963
12574
  exports.useBundles = useBundles;
11964
12575
  exports.useCart = useCart;
11965
12576
  exports.useCategories = useCategories;
12577
+ exports.useChat = useChat;
11966
12578
  exports.useCheckout = useCheckout;
11967
12579
  exports.useCimplify = useCimplify;
11968
12580
  exports.useCimplifyClient = useCimplifyClient;