@cimplify/sdk 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
- import { DURATION_UNIT, getVariantDisplayName, PRODUCT_TYPE, createCimplifyClient, RENDER_HINT } from './chunk-FFUCCUG4.mjs';
3
- export { getVariantDisplayName } from './chunk-FFUCCUG4.mjs';
2
+ import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, createCimplifyClient, RENDER_HINT } from './chunk-REXW2PET.mjs';
3
+ export { getVariantDisplayName } from './chunk-REXW2PET.mjs';
4
4
  import { ELEMENT_TYPES, EVENT_TYPES } from './chunk-3J4PPSMI.mjs';
5
5
  import { isSupportedCurrency, formatPrice, parsePrice, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-E3HC2YAE.mjs';
6
- import React3, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore, useId } from 'react';
6
+ import React10, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore, useId } from 'react';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
8
  import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
@@ -565,7 +565,8 @@ function mapServerCart(serverCart) {
565
565
  confirmationCode: item.confirmation_code || void 0,
566
566
  serviceStatus: item.service_status || void 0,
567
567
  bundleResolved: item.bundle_resolved || void 0,
568
- compositeResolved: item.composite_resolved || void 0
568
+ compositeResolved: item.composite_resolved || void 0,
569
+ customerInputs: item.customer_inputs || void 0
569
570
  });
570
571
  });
571
572
  return {
@@ -785,7 +786,8 @@ function createCartStore(params) {
785
786
  scheduled_start: normalizedOptions.scheduledStart,
786
787
  scheduled_end: normalizedOptions.scheduledEnd,
787
788
  staff_id: normalizedOptions.staffId,
788
- resource_id: normalizedOptions.resourceId
789
+ resource_id: normalizedOptions.resourceId,
790
+ customer_inputs: normalizedOptions.customerInputs
789
791
  });
790
792
  if (!result.ok) {
791
793
  throw result.error;
@@ -1423,14 +1425,14 @@ function CimplifyCheckout({
1423
1425
  resolvedCartRef.current = resolvedCart;
1424
1426
  const primaryColor = appearance?.variables?.primaryColor || "#0a2540";
1425
1427
  const isDark = appearance?.theme === "dark";
1426
- const emitStatus = React3.useEffectEvent(
1428
+ const emitStatus = React10.useEffectEvent(
1427
1429
  (nextStatus, context = {}) => {
1428
1430
  setStatus(nextStatus);
1429
1431
  setStatusText(context.display_text || "");
1430
1432
  onStatusChange?.(nextStatus, context);
1431
1433
  }
1432
1434
  );
1433
- const fireError = React3.useEffectEvent(
1435
+ const fireError = React10.useEffectEvent(
1434
1436
  (error) => {
1435
1437
  onError?.(error);
1436
1438
  }
@@ -1536,7 +1538,7 @@ function CimplifyCheckout({
1536
1538
  activeCheckoutRef.current = null;
1537
1539
  };
1538
1540
  }, []);
1539
- const handleSubmit = React3.useEffectEvent(async () => {
1541
+ const handleSubmit = React10.useEffectEvent(async () => {
1540
1542
  if (isSubmitting || isInitializing || !resolvedCartId) {
1541
1543
  if (!resolvedCartId && !isInitializing) {
1542
1544
  const message = "Your cart is empty. Add items before checkout.";
@@ -4848,6 +4850,880 @@ function VolumePricing({
4848
4850
  }
4849
4851
  );
4850
4852
  }
4853
+ function CustomerInputFields({
4854
+ fields,
4855
+ values,
4856
+ onChange,
4857
+ onFileUpload,
4858
+ className,
4859
+ classNames
4860
+ }) {
4861
+ const clientContext = useOptionalCimplifyClient();
4862
+ if (fields.length === 0) return null;
4863
+ const sorted = [...fields].sort((a, b) => a.display_order - b.display_order);
4864
+ const setValue = useCallback(
4865
+ (fieldId, value) => {
4866
+ onChange({ ...values, [fieldId]: value });
4867
+ },
4868
+ [values, onChange]
4869
+ );
4870
+ const defaultFileUpload = useCallback(
4871
+ async (file) => {
4872
+ if (onFileUpload) return onFileUpload(file, {});
4873
+ if (!clientContext?.client) throw new Error("No upload provider available");
4874
+ const result = await clientContext.client.uploads.upload(file);
4875
+ if (!result.ok) throw new Error(result.error.message);
4876
+ return result.value.url;
4877
+ },
4878
+ [onFileUpload, clientContext]
4879
+ );
4880
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-customer-inputs": true, className: cn("space-y-4", className, classNames?.root), children: sorted.map((field) => /* @__PURE__ */ jsx(
4881
+ InputField,
4882
+ {
4883
+ field,
4884
+ value: values[field.id],
4885
+ onValueChange: (value) => setValue(field.id, value),
4886
+ onFileUpload: onFileUpload ?? (clientContext?.client ? (_file, _field) => defaultFileUpload(_file) : void 0),
4887
+ classNames
4888
+ },
4889
+ field.id
4890
+ )) });
4891
+ }
4892
+ function InputField({
4893
+ field,
4894
+ value,
4895
+ onValueChange,
4896
+ onFileUpload,
4897
+ classNames
4898
+ }) {
4899
+ const hasPriceAdjustment = field.price_adjustment != null && parsePrice(field.price_adjustment) > 0;
4900
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-customer-input": true, "data-field-type": field.field_type, className: classNames?.field, children: [
4901
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
4902
+ /* @__PURE__ */ jsx("label", { className: cn("text-sm font-semibold", classNames?.label), children: field.name }),
4903
+ field.is_required && /* @__PURE__ */ jsx("span", { className: cn("text-xs text-destructive", classNames?.required), children: "Required" }),
4904
+ hasPriceAdjustment && /* @__PURE__ */ jsxs("span", { className: cn("text-xs text-muted-foreground", classNames?.priceAdjustment), children: [
4905
+ "+",
4906
+ /* @__PURE__ */ jsx(Price, { amount: field.price_adjustment })
4907
+ ] })
4908
+ ] }),
4909
+ field.help_text && /* @__PURE__ */ jsx("p", { className: cn("text-xs text-muted-foreground mb-1.5", classNames?.helpText), children: field.help_text }),
4910
+ /* @__PURE__ */ jsx(
4911
+ FieldInput,
4912
+ {
4913
+ field,
4914
+ value,
4915
+ onValueChange,
4916
+ onFileUpload,
4917
+ classNames
4918
+ }
4919
+ )
4920
+ ] });
4921
+ }
4922
+ function FieldInput({
4923
+ field,
4924
+ value,
4925
+ onValueChange,
4926
+ onFileUpload,
4927
+ classNames
4928
+ }) {
4929
+ const inputClass = cn(
4930
+ "w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
4931
+ classNames?.input
4932
+ );
4933
+ switch (field.field_type) {
4934
+ case INPUT_FIELD_TYPE.Text:
4935
+ case INPUT_FIELD_TYPE.Url:
4936
+ return /* @__PURE__ */ jsx(
4937
+ "input",
4938
+ {
4939
+ type: field.field_type === INPUT_FIELD_TYPE.Url ? "url" : "text",
4940
+ value: typeof value === "string" ? value : "",
4941
+ onChange: (e) => onValueChange(e.target.value),
4942
+ placeholder: field.placeholder,
4943
+ required: field.is_required,
4944
+ maxLength: field.validation?.max_length ?? void 0,
4945
+ className: inputClass
4946
+ }
4947
+ );
4948
+ case INPUT_FIELD_TYPE.Textarea:
4949
+ return /* @__PURE__ */ jsx(
4950
+ "textarea",
4951
+ {
4952
+ value: typeof value === "string" ? value : "",
4953
+ onChange: (e) => onValueChange(e.target.value),
4954
+ placeholder: field.placeholder,
4955
+ required: field.is_required,
4956
+ maxLength: field.validation?.max_length ?? void 0,
4957
+ rows: 3,
4958
+ className: cn(inputClass, "resize-none", classNames?.textarea)
4959
+ }
4960
+ );
4961
+ case INPUT_FIELD_TYPE.Number:
4962
+ return /* @__PURE__ */ jsx(
4963
+ "input",
4964
+ {
4965
+ type: "number",
4966
+ value: typeof value === "number" ? value : "",
4967
+ onChange: (e) => onValueChange(e.target.value ? parseFloat(e.target.value) : void 0),
4968
+ placeholder: field.placeholder,
4969
+ required: field.is_required,
4970
+ min: field.validation?.min_value ?? void 0,
4971
+ max: field.validation?.max_value ?? void 0,
4972
+ className: inputClass
4973
+ }
4974
+ );
4975
+ case INPUT_FIELD_TYPE.Select:
4976
+ return /* @__PURE__ */ jsxs(
4977
+ "select",
4978
+ {
4979
+ value: typeof value === "string" ? value : "",
4980
+ onChange: (e) => onValueChange(e.target.value || void 0),
4981
+ required: field.is_required,
4982
+ className: cn(inputClass, classNames?.select),
4983
+ children: [
4984
+ /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder || "Select..." }),
4985
+ (field.options || []).map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
4986
+ ]
4987
+ }
4988
+ );
4989
+ case INPUT_FIELD_TYPE.Radio:
4990
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxs(
4991
+ "label",
4992
+ {
4993
+ className: cn(
4994
+ "flex items-center gap-2 text-sm cursor-pointer",
4995
+ classNames?.radioOption
4996
+ ),
4997
+ children: [
4998
+ /* @__PURE__ */ jsx(
4999
+ "input",
5000
+ {
5001
+ type: "radio",
5002
+ name: field.id,
5003
+ value: opt,
5004
+ checked: value === opt,
5005
+ onChange: () => onValueChange(opt),
5006
+ className: "accent-primary"
5007
+ }
5008
+ ),
5009
+ opt
5010
+ ]
5011
+ },
5012
+ opt
5013
+ )) });
5014
+ case INPUT_FIELD_TYPE.Checkbox:
5015
+ return /* @__PURE__ */ jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
5016
+ /* @__PURE__ */ jsx(
5017
+ "input",
5018
+ {
5019
+ type: "checkbox",
5020
+ checked: value === true,
5021
+ onChange: (e) => onValueChange(e.target.checked),
5022
+ className: "accent-primary w-4 h-4"
5023
+ }
5024
+ ),
5025
+ field.placeholder || field.name
5026
+ ] });
5027
+ case INPUT_FIELD_TYPE.Color:
5028
+ return /* @__PURE__ */ jsx(
5029
+ "input",
5030
+ {
5031
+ type: "color",
5032
+ value: typeof value === "string" ? value : "#000000",
5033
+ onChange: (e) => onValueChange(e.target.value),
5034
+ className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
5035
+ }
5036
+ );
5037
+ case INPUT_FIELD_TYPE.Date:
5038
+ return /* @__PURE__ */ jsx(
5039
+ "input",
5040
+ {
5041
+ type: "date",
5042
+ value: typeof value === "string" ? value : "",
5043
+ onChange: (e) => onValueChange(e.target.value || void 0),
5044
+ required: field.is_required,
5045
+ className: inputClass
5046
+ }
5047
+ );
5048
+ case INPUT_FIELD_TYPE.File:
5049
+ case INPUT_FIELD_TYPE.Image:
5050
+ return /* @__PURE__ */ jsx(
5051
+ FileUploadInput,
5052
+ {
5053
+ field,
5054
+ value,
5055
+ onValueChange,
5056
+ onFileUpload,
5057
+ classNames
5058
+ }
5059
+ );
5060
+ case INPUT_FIELD_TYPE.Email:
5061
+ return /* @__PURE__ */ jsx(
5062
+ "input",
5063
+ {
5064
+ type: "email",
5065
+ value: typeof value === "string" ? value : "",
5066
+ onChange: (e) => onValueChange(e.target.value),
5067
+ placeholder: field.placeholder || "email@example.com",
5068
+ required: field.is_required,
5069
+ className: inputClass
5070
+ }
5071
+ );
5072
+ case INPUT_FIELD_TYPE.DateTime: {
5073
+ const dtLocalValue = typeof value === "string" && value.includes("T") ? value.slice(0, 16) : typeof value === "string" ? value : "";
5074
+ return /* @__PURE__ */ jsx(
5075
+ "input",
5076
+ {
5077
+ type: "datetime-local",
5078
+ value: dtLocalValue,
5079
+ onChange: (e) => {
5080
+ if (!e.target.value) {
5081
+ onValueChange(void 0);
5082
+ return;
5083
+ }
5084
+ const date = new Date(e.target.value);
5085
+ onValueChange(Number.isNaN(date.getTime()) ? e.target.value : date.toISOString());
5086
+ },
5087
+ required: field.is_required,
5088
+ className: inputClass
5089
+ }
5090
+ );
5091
+ }
5092
+ case INPUT_FIELD_TYPE.Time:
5093
+ return /* @__PURE__ */ jsx(
5094
+ "input",
5095
+ {
5096
+ type: "time",
5097
+ value: typeof value === "string" ? value : "",
5098
+ onChange: (e) => onValueChange(e.target.value || void 0),
5099
+ required: field.is_required,
5100
+ className: inputClass
5101
+ }
5102
+ );
5103
+ case INPUT_FIELD_TYPE.MultiSelect:
5104
+ return /* @__PURE__ */ jsx(
5105
+ MultiSelectInput,
5106
+ {
5107
+ field,
5108
+ value,
5109
+ onValueChange,
5110
+ classNames
5111
+ }
5112
+ );
5113
+ case INPUT_FIELD_TYPE.DateRange:
5114
+ return /* @__PURE__ */ jsx(
5115
+ DateRangeInput,
5116
+ {
5117
+ field,
5118
+ value,
5119
+ onValueChange,
5120
+ classNames,
5121
+ inputClass
5122
+ }
5123
+ );
5124
+ case INPUT_FIELD_TYPE.Address:
5125
+ return /* @__PURE__ */ jsx(
5126
+ AddressInput,
5127
+ {
5128
+ field,
5129
+ value,
5130
+ onValueChange,
5131
+ classNames,
5132
+ inputClass
5133
+ }
5134
+ );
5135
+ case INPUT_FIELD_TYPE.Location:
5136
+ return /* @__PURE__ */ jsx(
5137
+ LocationInput,
5138
+ {
5139
+ field,
5140
+ value,
5141
+ onValueChange,
5142
+ classNames,
5143
+ inputClass
5144
+ }
5145
+ );
5146
+ case INPUT_FIELD_TYPE.Phone:
5147
+ return /* @__PURE__ */ jsx(
5148
+ PhoneInput,
5149
+ {
5150
+ field,
5151
+ value,
5152
+ onValueChange,
5153
+ classNames,
5154
+ inputClass
5155
+ }
5156
+ );
5157
+ case INPUT_FIELD_TYPE.Signature:
5158
+ return /* @__PURE__ */ jsx(
5159
+ SignatureInput,
5160
+ {
5161
+ field,
5162
+ value,
5163
+ onValueChange,
5164
+ classNames
5165
+ }
5166
+ );
5167
+ default:
5168
+ return /* @__PURE__ */ jsx(
5169
+ "input",
5170
+ {
5171
+ type: "text",
5172
+ value: typeof value === "string" ? value : "",
5173
+ onChange: (e) => onValueChange(e.target.value),
5174
+ placeholder: field.placeholder,
5175
+ className: inputClass
5176
+ }
5177
+ );
5178
+ }
5179
+ }
5180
+ function FileUploadInput({
5181
+ field,
5182
+ value,
5183
+ onValueChange,
5184
+ onFileUpload,
5185
+ classNames
5186
+ }) {
5187
+ const [isUploading, setIsUploading] = React10.useState(false);
5188
+ const fileUrl = typeof value === "string" ? value : void 0;
5189
+ const acceptedFormats = field.validation?.accepted_formats;
5190
+ const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
5191
+ const handleFile = async (file) => {
5192
+ if (!onFileUpload) return;
5193
+ if (field.validation?.max_size_mb) {
5194
+ const maxBytes = field.validation.max_size_mb * 1024 * 1024;
5195
+ if (file.size > maxBytes) {
5196
+ return;
5197
+ }
5198
+ }
5199
+ setIsUploading(true);
5200
+ try {
5201
+ const url = await onFileUpload(file, field);
5202
+ onValueChange(url);
5203
+ } finally {
5204
+ setIsUploading(false);
5205
+ }
5206
+ };
5207
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-2", classNames?.fileInput), children: fileUrl ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5208
+ field.field_type === INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
5209
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground truncate", children: fileUrl.split("/").pop() }) }),
5210
+ /* @__PURE__ */ jsx(
5211
+ "button",
5212
+ {
5213
+ type: "button",
5214
+ onClick: () => onValueChange(void 0),
5215
+ className: "text-xs text-muted-foreground hover:text-destructive transition-colors",
5216
+ children: "Remove"
5217
+ }
5218
+ )
5219
+ ] }) : /* @__PURE__ */ jsxs("label", { className: "flex flex-col items-center justify-center w-full h-24 border-2 border-dashed border-border rounded-lg cursor-pointer hover:border-primary/40 transition-colors", children: [
5220
+ /* @__PURE__ */ jsx(
5221
+ "input",
5222
+ {
5223
+ type: "file",
5224
+ accept,
5225
+ onChange: (e) => {
5226
+ const file = e.target.files?.[0];
5227
+ if (file) void handleFile(file);
5228
+ },
5229
+ className: "hidden",
5230
+ disabled: isUploading
5231
+ }
5232
+ ),
5233
+ isUploading ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Uploading..." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5234
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
5235
+ acceptedFormats && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
5236
+ acceptedFormats.map((f) => f.toUpperCase()).join(", "),
5237
+ field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
5238
+ ] })
5239
+ ] })
5240
+ ] }) });
5241
+ }
5242
+ function MultiSelectInput({
5243
+ field,
5244
+ value,
5245
+ onValueChange,
5246
+ classNames
5247
+ }) {
5248
+ const selected = Array.isArray(value) ? value : [];
5249
+ const options = field.options || [];
5250
+ const maxSelections = field.validation?.max_selections;
5251
+ const toggle = (opt) => {
5252
+ const next = selected.includes(opt) ? selected.filter((s) => s !== opt) : maxSelections && selected.length >= maxSelections ? selected : [...selected, opt];
5253
+ onValueChange(next.length > 0 ? next : void 0);
5254
+ };
5255
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", classNames?.multiSelectGroup), children: [
5256
+ options.map((opt) => /* @__PURE__ */ jsxs(
5257
+ "label",
5258
+ {
5259
+ className: cn(
5260
+ "flex items-center gap-2 text-sm cursor-pointer",
5261
+ classNames?.multiSelectOption
5262
+ ),
5263
+ children: [
5264
+ /* @__PURE__ */ jsx(
5265
+ "input",
5266
+ {
5267
+ type: "checkbox",
5268
+ checked: selected.includes(opt),
5269
+ onChange: () => toggle(opt),
5270
+ className: "accent-primary w-4 h-4"
5271
+ }
5272
+ ),
5273
+ opt
5274
+ ]
5275
+ },
5276
+ opt
5277
+ )),
5278
+ maxSelections && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5279
+ selected.length,
5280
+ "/",
5281
+ maxSelections,
5282
+ " selected"
5283
+ ] })
5284
+ ] });
5285
+ }
5286
+ function DateRangeInput({
5287
+ field,
5288
+ value,
5289
+ onValueChange,
5290
+ classNames,
5291
+ inputClass
5292
+ }) {
5293
+ const range = value && typeof value === "object" ? value : {};
5294
+ const update = (key, v) => {
5295
+ const next = { ...range, [key]: v };
5296
+ onValueChange(next.start || next.end ? next : void 0);
5297
+ };
5298
+ return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-2 gap-3", classNames?.dateRangeInput), children: [
5299
+ /* @__PURE__ */ jsxs("div", { children: [
5300
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "Start" }),
5301
+ /* @__PURE__ */ jsx(
5302
+ "input",
5303
+ {
5304
+ type: "date",
5305
+ value: range.start || "",
5306
+ onChange: (e) => update("start", e.target.value),
5307
+ required: field.is_required,
5308
+ className: inputClass
5309
+ }
5310
+ )
5311
+ ] }),
5312
+ /* @__PURE__ */ jsxs("div", { children: [
5313
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "End" }),
5314
+ /* @__PURE__ */ jsx(
5315
+ "input",
5316
+ {
5317
+ type: "date",
5318
+ value: range.end || "",
5319
+ onChange: (e) => update("end", e.target.value),
5320
+ required: field.is_required,
5321
+ className: inputClass
5322
+ }
5323
+ )
5324
+ ] })
5325
+ ] });
5326
+ }
5327
+ function AddressInput({
5328
+ field,
5329
+ value,
5330
+ onValueChange,
5331
+ classNames,
5332
+ inputClass
5333
+ }) {
5334
+ const clientContext = useOptionalCimplifyClient();
5335
+ const addr = value && typeof value === "object" ? value : null;
5336
+ const [inputText, setInputText] = React10.useState(addr?.formatted_address ?? "");
5337
+ const [suggestions, setSuggestions] = React10.useState([]);
5338
+ const [isOpen, setIsOpen] = React10.useState(false);
5339
+ const [highlightedIndex, setHighlightedIndex] = React10.useState(-1);
5340
+ const debounceRef = React10.useRef(null);
5341
+ const seqRef = React10.useRef(0);
5342
+ const sessionTokenRef = React10.useRef(createSessionToken());
5343
+ const blurTimeoutRef = React10.useRef(null);
5344
+ React10.useEffect(() => {
5345
+ return () => {
5346
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5347
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
5348
+ seqRef.current += 1;
5349
+ };
5350
+ }, []);
5351
+ const fetchSuggestions = React10.useCallback(async (query) => {
5352
+ const client = clientContext?.client;
5353
+ if (!client || query.length < 3) {
5354
+ setSuggestions([]);
5355
+ setIsOpen(false);
5356
+ return;
5357
+ }
5358
+ const seq = ++seqRef.current;
5359
+ const result = await client.places.autocomplete(query, sessionTokenRef.current);
5360
+ if (seqRef.current !== seq) return;
5361
+ if (result.ok && result.value.predictions.length > 0) {
5362
+ setSuggestions(result.value.predictions);
5363
+ setHighlightedIndex(0);
5364
+ setIsOpen(true);
5365
+ } else {
5366
+ setSuggestions([]);
5367
+ setIsOpen(false);
5368
+ }
5369
+ }, [clientContext]);
5370
+ const handleInputChange = (nextValue) => {
5371
+ setInputText(nextValue);
5372
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5373
+ debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
5374
+ };
5375
+ const selectSuggestion = React10.useCallback(async (suggestion) => {
5376
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5377
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
5378
+ const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
5379
+ setInputText(fallback);
5380
+ setSuggestions([]);
5381
+ setIsOpen(false);
5382
+ const client = clientContext?.client;
5383
+ if (!client) return;
5384
+ const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
5385
+ sessionTokenRef.current = createSessionToken();
5386
+ if (result.ok) {
5387
+ const d = result.value;
5388
+ const addressValue = {
5389
+ formatted_address: d.formatted_address || fallback,
5390
+ street_address: d.street_address,
5391
+ apartment: d.apartment,
5392
+ city: d.city,
5393
+ region: d.region,
5394
+ postal_code: d.postal_code,
5395
+ country: d.country,
5396
+ latitude: d.latitude,
5397
+ longitude: d.longitude,
5398
+ place_id: d.place_id
5399
+ };
5400
+ setInputText(addressValue.formatted_address);
5401
+ onValueChange(addressValue);
5402
+ }
5403
+ }, [clientContext, onValueChange]);
5404
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2 relative", classNames?.addressInput), children: [
5405
+ /* @__PURE__ */ jsx(
5406
+ "input",
5407
+ {
5408
+ type: "text",
5409
+ value: inputText,
5410
+ onChange: (e) => handleInputChange(e.target.value),
5411
+ onFocus: () => {
5412
+ if (suggestions.length > 0) setIsOpen(true);
5413
+ },
5414
+ onBlur: () => {
5415
+ blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
5416
+ },
5417
+ onKeyDown: (e) => {
5418
+ if (e.key === "Escape") {
5419
+ setIsOpen(false);
5420
+ return;
5421
+ }
5422
+ if (!isOpen || suggestions.length === 0) return;
5423
+ if (e.key === "ArrowDown") {
5424
+ e.preventDefault();
5425
+ setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
5426
+ } else if (e.key === "ArrowUp") {
5427
+ e.preventDefault();
5428
+ setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
5429
+ } else if (e.key === "Enter" && highlightedIndex >= 0) {
5430
+ e.preventDefault();
5431
+ void selectSuggestion(suggestions[highlightedIndex]);
5432
+ }
5433
+ },
5434
+ placeholder: field.placeholder || "Search for an address...",
5435
+ required: field.is_required,
5436
+ autoComplete: "off",
5437
+ "aria-autocomplete": "list",
5438
+ "aria-expanded": isOpen && suggestions.length > 0,
5439
+ className: inputClass
5440
+ }
5441
+ ),
5442
+ isOpen && suggestions.length > 0 && /* @__PURE__ */ jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsx(
5443
+ "button",
5444
+ {
5445
+ type: "button",
5446
+ role: "option",
5447
+ "aria-selected": i === highlightedIndex,
5448
+ onMouseDown: (e) => {
5449
+ e.preventDefault();
5450
+ void selectSuggestion(s);
5451
+ },
5452
+ className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
5453
+ children: s.description
5454
+ },
5455
+ s.place_id
5456
+ )) }),
5457
+ addr && /* @__PURE__ */ jsx(
5458
+ "input",
5459
+ {
5460
+ type: "text",
5461
+ value: addr.apartment || "",
5462
+ onChange: (e) => onValueChange({ ...addr, apartment: e.target.value || void 0 }),
5463
+ placeholder: "Apt, suite, unit (optional)",
5464
+ className: inputClass
5465
+ }
5466
+ )
5467
+ ] });
5468
+ }
5469
+ function LocationInput({
5470
+ field,
5471
+ value,
5472
+ onValueChange,
5473
+ classNames,
5474
+ inputClass
5475
+ }) {
5476
+ const clientContext = useOptionalCimplifyClient();
5477
+ const loc = value && typeof value === "object" ? value : null;
5478
+ const [inputText, setInputText] = React10.useState(loc?.label ?? "");
5479
+ const [suggestions, setSuggestions] = React10.useState([]);
5480
+ const [isOpen, setIsOpen] = React10.useState(false);
5481
+ const [highlightedIndex, setHighlightedIndex] = React10.useState(-1);
5482
+ const debounceRef = React10.useRef(null);
5483
+ const seqRef = React10.useRef(0);
5484
+ const sessionTokenRef = React10.useRef(createSessionToken());
5485
+ const blurTimeoutRef = React10.useRef(null);
5486
+ React10.useEffect(() => {
5487
+ return () => {
5488
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5489
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
5490
+ seqRef.current += 1;
5491
+ };
5492
+ }, []);
5493
+ const fetchSuggestions = React10.useCallback(async (query) => {
5494
+ const client = clientContext?.client;
5495
+ if (!client || query.length < 3) {
5496
+ setSuggestions([]);
5497
+ setIsOpen(false);
5498
+ return;
5499
+ }
5500
+ const seq = ++seqRef.current;
5501
+ const result = await client.places.autocomplete(query, sessionTokenRef.current);
5502
+ if (seqRef.current !== seq) return;
5503
+ if (result.ok && result.value.predictions.length > 0) {
5504
+ setSuggestions(result.value.predictions);
5505
+ setHighlightedIndex(0);
5506
+ setIsOpen(true);
5507
+ } else {
5508
+ setSuggestions([]);
5509
+ setIsOpen(false);
5510
+ }
5511
+ }, [clientContext]);
5512
+ const handleInputChange = (nextValue) => {
5513
+ setInputText(nextValue);
5514
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5515
+ debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
5516
+ };
5517
+ const selectSuggestion = React10.useCallback(async (suggestion) => {
5518
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5519
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
5520
+ const label = suggestion.description.split(",")[0]?.trim() || suggestion.description;
5521
+ setInputText(label);
5522
+ setSuggestions([]);
5523
+ setIsOpen(false);
5524
+ const client = clientContext?.client;
5525
+ if (!client) return;
5526
+ const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
5527
+ sessionTokenRef.current = createSessionToken();
5528
+ if (result.ok) {
5529
+ const d = result.value;
5530
+ onValueChange({
5531
+ latitude: d.latitude,
5532
+ longitude: d.longitude,
5533
+ label: d.formatted_address || label
5534
+ });
5535
+ setInputText(d.formatted_address || label);
5536
+ }
5537
+ }, [clientContext, onValueChange]);
5538
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", classNames?.locationInput), children: [
5539
+ /* @__PURE__ */ jsx(
5540
+ "input",
5541
+ {
5542
+ type: "text",
5543
+ value: inputText,
5544
+ onChange: (e) => handleInputChange(e.target.value),
5545
+ onFocus: () => {
5546
+ if (suggestions.length > 0) setIsOpen(true);
5547
+ },
5548
+ onBlur: () => {
5549
+ blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
5550
+ },
5551
+ onKeyDown: (e) => {
5552
+ if (e.key === "Escape") {
5553
+ setIsOpen(false);
5554
+ return;
5555
+ }
5556
+ if (!isOpen || suggestions.length === 0) return;
5557
+ if (e.key === "ArrowDown") {
5558
+ e.preventDefault();
5559
+ setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
5560
+ } else if (e.key === "ArrowUp") {
5561
+ e.preventDefault();
5562
+ setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
5563
+ } else if (e.key === "Enter" && highlightedIndex >= 0) {
5564
+ e.preventDefault();
5565
+ void selectSuggestion(suggestions[highlightedIndex]);
5566
+ }
5567
+ },
5568
+ placeholder: field.placeholder || "Search for a location...",
5569
+ required: field.is_required,
5570
+ autoComplete: "off",
5571
+ "aria-autocomplete": "list",
5572
+ "aria-expanded": isOpen && suggestions.length > 0,
5573
+ className: inputClass
5574
+ }
5575
+ ),
5576
+ isOpen && suggestions.length > 0 && /* @__PURE__ */ jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsx(
5577
+ "button",
5578
+ {
5579
+ type: "button",
5580
+ role: "option",
5581
+ "aria-selected": i === highlightedIndex,
5582
+ onMouseDown: (e) => {
5583
+ e.preventDefault();
5584
+ void selectSuggestion(s);
5585
+ },
5586
+ className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
5587
+ children: s.description
5588
+ },
5589
+ s.place_id
5590
+ )) }),
5591
+ loc && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
5592
+ loc.latitude.toFixed(4),
5593
+ ", ",
5594
+ loc.longitude.toFixed(4)
5595
+ ] })
5596
+ ] });
5597
+ }
5598
+ function PhoneInput({
5599
+ field,
5600
+ value,
5601
+ onValueChange,
5602
+ classNames,
5603
+ inputClass
5604
+ }) {
5605
+ const phone = value && typeof value === "object" ? value : null;
5606
+ const [code, setCode] = React10.useState(phone?.country_code ?? "+1");
5607
+ const [number, setNumber] = React10.useState(phone?.number ?? "");
5608
+ const update = (nextCode, nextNumber) => {
5609
+ setCode(nextCode);
5610
+ setNumber(nextNumber);
5611
+ if (nextNumber) {
5612
+ onValueChange({
5613
+ country_code: nextCode,
5614
+ number: nextNumber,
5615
+ formatted: `${nextCode} ${nextNumber}`
5616
+ });
5617
+ } else {
5618
+ onValueChange(void 0);
5619
+ }
5620
+ };
5621
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-2", classNames?.phoneInput), children: [
5622
+ /* @__PURE__ */ jsx(
5623
+ "input",
5624
+ {
5625
+ type: "text",
5626
+ value: code,
5627
+ onChange: (e) => update(e.target.value, number),
5628
+ placeholder: "+1",
5629
+ className: cn(inputClass, "w-20 shrink-0")
5630
+ }
5631
+ ),
5632
+ /* @__PURE__ */ jsx(
5633
+ "input",
5634
+ {
5635
+ type: "tel",
5636
+ value: number,
5637
+ onChange: (e) => update(code, e.target.value),
5638
+ placeholder: field.placeholder || "Phone number",
5639
+ required: field.is_required,
5640
+ className: inputClass
5641
+ }
5642
+ )
5643
+ ] });
5644
+ }
5645
+ function SignatureInput({
5646
+ field,
5647
+ value,
5648
+ onValueChange,
5649
+ classNames
5650
+ }) {
5651
+ const sig = value && typeof value === "object" ? value : null;
5652
+ const canvasRef = React10.useRef(null);
5653
+ const isDrawing = React10.useRef(false);
5654
+ const getCtx = () => canvasRef.current?.getContext("2d") ?? null;
5655
+ const startDraw = (e) => {
5656
+ const ctx = getCtx();
5657
+ if (!ctx) return;
5658
+ isDrawing.current = true;
5659
+ const rect = canvasRef.current.getBoundingClientRect();
5660
+ ctx.beginPath();
5661
+ ctx.moveTo(e.clientX - rect.left, e.clientY - rect.top);
5662
+ canvasRef.current.setPointerCapture(e.pointerId);
5663
+ };
5664
+ const draw = (e) => {
5665
+ if (!isDrawing.current) return;
5666
+ const ctx = getCtx();
5667
+ if (!ctx) return;
5668
+ const rect = canvasRef.current.getBoundingClientRect();
5669
+ ctx.lineWidth = 2;
5670
+ ctx.lineCap = "round";
5671
+ const style = getComputedStyle(canvasRef.current);
5672
+ ctx.strokeStyle = style.color || "#000";
5673
+ ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top);
5674
+ ctx.stroke();
5675
+ };
5676
+ const endDraw = () => {
5677
+ if (!isDrawing.current) return;
5678
+ isDrawing.current = false;
5679
+ const canvas = canvasRef.current;
5680
+ if (!canvas) return;
5681
+ onValueChange({
5682
+ data_url: canvas.toDataURL("image/png"),
5683
+ signer_name: sig?.signer_name
5684
+ });
5685
+ };
5686
+ const clear = () => {
5687
+ const canvas = canvasRef.current;
5688
+ if (!canvas) return;
5689
+ const ctx = canvas.getContext("2d");
5690
+ if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);
5691
+ onValueChange(void 0);
5692
+ };
5693
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", classNames?.signatureCanvas), children: [
5694
+ /* @__PURE__ */ jsxs("div", { className: "relative rounded-md border border-input overflow-hidden", children: [
5695
+ /* @__PURE__ */ jsx(
5696
+ "canvas",
5697
+ {
5698
+ ref: canvasRef,
5699
+ width: 400,
5700
+ height: 150,
5701
+ className: "w-full touch-none cursor-crosshair bg-background text-foreground",
5702
+ onPointerDown: startDraw,
5703
+ onPointerMove: draw,
5704
+ onPointerUp: endDraw,
5705
+ onPointerLeave: endDraw
5706
+ }
5707
+ ),
5708
+ sig?.data_url && /* @__PURE__ */ jsx(
5709
+ "button",
5710
+ {
5711
+ type: "button",
5712
+ onClick: clear,
5713
+ className: "absolute top-1.5 right-1.5 text-xs text-muted-foreground hover:text-destructive transition-colors",
5714
+ children: "Clear"
5715
+ }
5716
+ )
5717
+ ] }),
5718
+ !sig?.data_url && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: field.placeholder || "Draw your signature above" })
5719
+ ] });
5720
+ }
5721
+ function createSessionToken() {
5722
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
5723
+ return crypto.randomUUID();
5724
+ }
5725
+ return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
5726
+ }
4851
5727
  function ProductCustomizer({
4852
5728
  product,
4853
5729
  onAddToCart,
@@ -4868,6 +5744,7 @@ function ProductCustomizer({
4868
5744
  const [bundleTotalPrice, setBundleTotalPrice] = useState(null);
4869
5745
  const [bundleReady, setBundleReady] = useState(false);
4870
5746
  const [selectedBillingPlan, setSelectedBillingPlan] = useState(null);
5747
+ const [customerInputValues, setCustomerInputValues] = useState({});
4871
5748
  const [specialInstructions, setSpecialInstructions] = useState("");
4872
5749
  const cart = useCart();
4873
5750
  const productType = product.type || "product";
@@ -4892,6 +5769,7 @@ function ProductCustomizer({
4892
5769
  setBundleTotalPrice(null);
4893
5770
  setBundleReady(false);
4894
5771
  setSelectedBillingPlan(null);
5772
+ setCustomerInputValues({});
4895
5773
  setSpecialInstructions("");
4896
5774
  }, [product.id, product.min_order_quantity]);
4897
5775
  const selectedAddOnOptions = useMemo(() => {
@@ -4954,7 +5832,15 @@ function ProductCustomizer({
4954
5832
  }),
4955
5833
  [product.id, quantity, selectedVariantId, normalizedAddOnOptionIds, isBundle, bundleSelections, isComposite, compositeSelections]
4956
5834
  );
4957
- const quoteEnabled = isComposite ? compositeReady : isBundle ? bundleReady : requiredAddOnsSatisfied;
5835
+ const requiredInputsSatisfied = useMemo(() => {
5836
+ if (!product.input_fields || product.input_fields.length === 0) return true;
5837
+ return product.input_fields.every((field) => {
5838
+ if (!field.is_required) return true;
5839
+ const val = customerInputValues[field.id];
5840
+ return val !== void 0 && val !== "" && val !== null;
5841
+ });
5842
+ }, [product.input_fields, customerInputValues]);
5843
+ const quoteEnabled = isComposite ? compositeReady && requiredInputsSatisfied : isBundle ? bundleReady && requiredInputsSatisfied : requiredAddOnsSatisfied && requiredInputsSatisfied;
4958
5844
  const { quote } = useQuote(quoteInput, {
4959
5845
  enabled: quoteEnabled
4960
5846
  });
@@ -4993,6 +5879,7 @@ function ProductCustomizer({
4993
5879
  compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0,
4994
5880
  bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0,
4995
5881
  billingPlanId: selectedBillingPlan?.id,
5882
+ customerInputs: Object.keys(customerInputValues).length > 0 ? Object.entries(customerInputValues).filter(([, v]) => v !== void 0 && v !== "").map(([fieldId, value]) => ({ field_id: fieldId, value })) : void 0,
4996
5883
  specialInstructions: specialInstructions.trim() || void 0
4997
5884
  };
4998
5885
  try {
@@ -5011,7 +5898,7 @@ function ProductCustomizer({
5011
5898
  } finally {
5012
5899
  setIsSubmitting(false);
5013
5900
  }
5014
- }, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, specialInstructions, isSubmitting, onAddToCart, cart]);
5901
+ }, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isSubmitting, onAddToCart, cart]);
5015
5902
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
5016
5903
  isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsx(
5017
5904
  CompositeSelector,
@@ -5072,6 +5959,14 @@ function ProductCustomizer({
5072
5959
  currentQuantity: quantity
5073
5960
  }
5074
5961
  ),
5962
+ product.input_fields && product.input_fields.length > 0 && /* @__PURE__ */ jsx(
5963
+ CustomerInputFields,
5964
+ {
5965
+ fields: product.input_fields,
5966
+ values: customerInputValues,
5967
+ onChange: setCustomerInputValues
5968
+ }
5969
+ ),
5075
5970
  product.deposit_type && product.deposit_type !== "none" && product.deposit_amount && /* @__PURE__ */ jsx("div", { "data-cimplify-customizer-deposit": true, className: cn("text-sm text-muted-foreground", classNames?.depositInfo), children: product.deposit_type === "fixed" ? /* @__PURE__ */ jsxs("span", { children: [
5076
5971
  "Deposit required: ",
5077
5972
  /* @__PURE__ */ jsx(Price, { amount: product.deposit_amount })
@@ -5273,6 +6168,10 @@ function CartLineItemRow({
5273
6168
  ] }),
5274
6169
  (hasComposite || hasBundle) && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-badge": true, children: hasComposite ? "Custom" : "Bundle" }),
5275
6170
  item.specialInstructions && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-instructions": true, className: "text-xs text-muted-foreground", children: item.specialInstructions }),
6171
+ item.customerInputs && item.customerInputs.length > 0 && /* @__PURE__ */ jsx("div", { "data-cimplify-cart-customer-inputs": true, className: classNames?.customerInputs, children: item.customerInputs.map((input) => /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-input": true, children: [
6172
+ /* @__PURE__ */ jsx("span", { "data-cimplify-cart-input-label": true, children: input.field_name }),
6173
+ /* @__PURE__ */ jsx("span", { "data-cimplify-cart-input-value": true, children: input.field_type === INPUT_FIELD_TYPE.File || input.field_type === INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
6174
+ ] }, input.field_id)) }),
5276
6175
  (item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-service-info": true, className: classNames?.serviceInfo, children: [
5277
6176
  /* @__PURE__ */ jsxs("span", { children: [
5278
6177
  "\u{1F4C5}",
@@ -6741,13 +7640,13 @@ function ProductGrid({
6741
7640
  className,
6742
7641
  classNames
6743
7642
  }) {
6744
- const rawId = React3.useId();
7643
+ const rawId = React10.useId();
6745
7644
  const gridId = `cimplify-grid-${rawId.replace(/:/g, "")}`;
6746
7645
  const sm = columns?.sm ?? 1;
6747
7646
  const md = columns?.md ?? 2;
6748
7647
  const lg = columns?.lg ?? 3;
6749
7648
  const xl = columns?.xl ?? 4;
6750
- const css = React3.useMemo(
7649
+ const css = React10.useMemo(
6751
7650
  () => [
6752
7651
  `#${gridId}{display:grid;grid-template-columns:repeat(${sm},1fr);gap:1rem}`,
6753
7652
  `@media(min-width:768px){#${gridId}{grid-template-columns:repeat(${md},1fr)}}`,
@@ -7108,7 +8007,7 @@ function CategoryGrid({
7108
8007
  enabled: categoriesProp === void 0
7109
8008
  });
7110
8009
  const categories = categoriesProp ?? fetched;
7111
- const rawId = React3.useId();
8010
+ const rawId = React10.useId();
7112
8011
  const gridId = `cimplify-cat-grid-${rawId.replace(/:/g, "")}`;
7113
8012
  const sm = columns?.sm ?? 2;
7114
8013
  const md = columns?.md ?? 3;
@@ -7334,7 +8233,7 @@ function OrderSummary({
7334
8233
  /* @__PURE__ */ jsx("p", { children: order.delivery_address })
7335
8234
  ] }),
7336
8235
  /* @__PURE__ */ jsx("div", { "data-cimplify-order-items": true, className: classNames?.items, children: order.items.map(
7337
- (item) => renderLineItem ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderLineItem(item) }, item.id) : /* @__PURE__ */ jsxs("div", { "data-cimplify-order-line-item": true, className: classNames?.lineItem, children: [
8236
+ (item) => renderLineItem ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderLineItem(item) }, item.id) : /* @__PURE__ */ jsxs("div", { "data-cimplify-order-line-item": true, className: classNames?.lineItem, children: [
7338
8237
  /* @__PURE__ */ jsxs("div", { "data-cimplify-order-line-info": true, children: [
7339
8238
  /* @__PURE__ */ jsxs("span", { "data-cimplify-order-line-qty": true, children: [
7340
8239
  item.quantity,
@@ -8315,7 +9214,7 @@ function DefaultCatalogueLayout({
8315
9214
  /* @__PURE__ */ jsx("p", { className: "text-lg font-medium", children: "No products found" }),
8316
9215
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try adjusting your filters" })
8317
9216
  ] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-4", children: products.map(
8318
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
9217
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
8319
9218
  ProductCard,
8320
9219
  {
8321
9220
  product,
@@ -8422,7 +9321,7 @@ function CompactCatalogueLayout({
8422
9321
  /* @__PURE__ */ jsx("p", { className: "text-lg font-medium", children: "No items found" }),
8423
9322
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different category or search" })
8424
9323
  ] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-5", children: products.map(
8425
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
9324
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
8426
9325
  ProductCard,
8427
9326
  {
8428
9327
  product,
@@ -8445,8 +9344,8 @@ var BUILT_IN_LAYOUTS = {
8445
9344
  ["compact" /* Compact */]: CompactCatalogueLayout
8446
9345
  };
8447
9346
  function useDebounce(value, delay) {
8448
- const [debounced, setDebounced] = React3.useState(value);
8449
- React3.useEffect(() => {
9347
+ const [debounced, setDebounced] = React10.useState(value);
9348
+ React10.useEffect(() => {
8450
9349
  const timer = setTimeout(() => setDebounced(value), delay);
8451
9350
  return () => clearTimeout(timer);
8452
9351
  }, [value, delay]);
@@ -9716,7 +10615,7 @@ function DefaultCollectionLayout({
9716
10615
  ] })
9717
10616
  ] }),
9718
10617
  filtered.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4", children: filtered.map(
9719
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
10618
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
9720
10619
  ProductCard,
9721
10620
  {
9722
10621
  product,
@@ -9769,7 +10668,7 @@ function FeaturedCollectionLayout({
9769
10668
  ] })
9770
10669
  ] }),
9771
10670
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: products.map(
9772
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
10671
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
9773
10672
  ProductCard,
9774
10673
  {
9775
10674
  product,
@@ -9893,7 +10792,7 @@ function CatalogueCollectionLayout({
9893
10792
  ] })
9894
10793
  ] }),
9895
10794
  filtered.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4", children: filtered.map(
9896
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
10795
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
9897
10796
  ProductCard,
9898
10797
  {
9899
10798
  product,
@@ -10273,7 +11172,7 @@ function DefaultSearchLayout({
10273
11172
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different search term" }),
10274
11173
  /* @__PURE__ */ jsx("button", { onClick: clear, className: "text-sm font-medium text-primary mt-4 hover:underline", children: "Clear search" })
10275
11174
  ] }) : results.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-4", children: results.map(
10276
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
11175
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
10277
11176
  ProductCard,
10278
11177
  {
10279
11178
  product,
@@ -10375,7 +11274,7 @@ function CompactSearchLayout({
10375
11274
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Try a different search term" }),
10376
11275
  /* @__PURE__ */ jsx("button", { onClick: clear, className: "text-sm font-medium text-primary mt-4 hover:underline", children: "Clear search" })
10377
11276
  ] }) : results.length > 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-5", children: results.map(
10378
- (product) => renderCard ? /* @__PURE__ */ jsx(React3.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
11277
+ (product) => renderCard ? /* @__PURE__ */ jsx(React10.Fragment, { children: renderCard(product) }, product.id) : /* @__PURE__ */ jsx(
10379
11278
  ProductCard,
10380
11279
  {
10381
11280
  product,
@@ -10940,4 +11839,4 @@ function ResourcePicker({
10940
11839
  );
10941
11840
  }
10942
11841
 
10943
- export { AccommodationCard, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BundleProductCard, BundleProductLayout, BundleSelector, CardImage, CardShell, CardVariant, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, CheckoutPage, CimplifyAccount, CimplifyCheckout, CimplifyProvider, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, CurrencySelector, CustomAttributesTable, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, FeaturedCollectionLayout, FoodProductCard, FoodProductLayout, InventoryBadge, LeaseServiceCard, LocationPicker, MetadataStringList, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, ServiceProductLayout, SessionMessageBanner, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, StoreNav, SubscriptionCard, TagPills, TwoColumnGrid, VariantSelector, VolumePricing, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, roomToResource, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
11842
+ export { AccommodationCard, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BillingPlanSelector, BookingCard, BookingList, BundleProductCard, BundleProductLayout, BundleSelector, CardImage, CardShell, CardVariant, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, CheckoutPage, CimplifyAccount, CimplifyCheckout, CimplifyProvider, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, CurrencySelector, CustomAttributesTable, CustomerInputFields, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, FeaturedCollectionLayout, FoodProductCard, FoodProductLayout, InventoryBadge, LeaseServiceCard, LocationPicker, MetadataStringList, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderSummary, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, ServiceProductLayout, SessionMessageBanner, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, StoreNav, SubscriptionCard, TagPills, TwoColumnGrid, VariantSelector, VolumePricing, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, roomToResource, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCategories, useCheckout, useCimplify, useCimplifyClient, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };