@cimplify/sdk 0.47.0 → 0.48.1

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
@@ -15,6 +15,9 @@ var radioGroup = require('@base-ui/react/radio-group');
15
15
  var radio = require('@base-ui/react/radio');
16
16
  var checkbox = require('@base-ui/react/checkbox');
17
17
  var button = require('@base-ui/react/button');
18
+ var popover = require('@base-ui/react/popover');
19
+ var reactDayPicker = require('react-day-picker');
20
+ var dateFns = require('date-fns');
18
21
  var tabs = require('@base-ui/react/tabs');
19
22
  var dialog = require('@base-ui/react/dialog');
20
23
  var field = require('@base-ui/react/field');
@@ -1338,16 +1341,10 @@ function useCimplifyClient() {
1338
1341
  function useOptionalCimplifyClient() {
1339
1342
  return React10.useContext(CimplifyClientContext);
1340
1343
  }
1341
- var SPACE = { sm: 8};
1342
- function shellColors(isDark, primaryColor) {
1344
+ function shellColors(isDark) {
1343
1345
  return {
1344
- text: isDark ? "#f4f4f5" : "#1a1a1a",
1345
1346
  textSecondary: isDark ? "#a1a1aa" : "#52525b",
1346
- textMuted: isDark ? "#71717a" : "#a1a1aa",
1347
- border: isDark ? "#27272a" : "#e4e4e7",
1348
- surface: isDark ? "#18181b" : "#fafafa",
1349
- error: "#dc2626",
1350
- primary: primaryColor
1347
+ error: "#dc2626"
1351
1348
  };
1352
1349
  }
1353
1350
  function statusToLabel(status) {
@@ -1432,7 +1429,6 @@ function CimplifyCheckout({
1432
1429
  fxOptionsRef.current = fxOptions;
1433
1430
  const resolvedCartRef = React10.useRef(resolvedCart);
1434
1431
  resolvedCartRef.current = resolvedCart;
1435
- const primaryColor = appearance?.variables?.primaryColor || "#0a2540";
1436
1432
  const isDark = appearance?.theme === "dark";
1437
1433
  const emitStatus = React10__default.default.useEffectEvent(
1438
1434
  (nextStatus, context = {}) => {
@@ -1635,7 +1631,7 @@ function CimplifyCheckout({
1635
1631
  checkoutElement.setCart(transformToCheckoutCart(resolvedCart, fxOptions));
1636
1632
  }
1637
1633
  }, [resolvedCart, fxOptions]);
1638
- const colors = shellColors(isDark ?? false, primaryColor);
1634
+ const colors = shellColors(isDark ?? false);
1639
1635
  if (isInitializing) {
1640
1636
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-cimplify-checkout": "", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-status": "", style: { fontSize: 13, color: colors.textSecondary }, children: "Preparing checkout..." }) });
1641
1637
  }
@@ -1657,8 +1653,8 @@ function CimplifyCheckout({
1657
1653
  }
1658
1654
  ),
1659
1655
  /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-section": "checkout", children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: checkoutMountRef }) }),
1660
- status && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-status": "", style: { marginTop: SPACE.sm, fontSize: 13, color: colors.textSecondary }, children: statusText || statusToLabel(status) }),
1661
- errorMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-error": "", style: { marginTop: SPACE.sm, fontSize: 13, color: colors.error }, children: errorMessage })
1656
+ status && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-status": "", style: { marginTop: 8, fontSize: 13, color: colors.textSecondary }, children: statusText || statusToLabel(status) }),
1657
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-error": "", style: { marginTop: 8, fontSize: 13, color: colors.error }, children: errorMessage })
1662
1658
  ] });
1663
1659
  }
1664
1660
  function cn(...inputs) {
@@ -5006,6 +5002,262 @@ function VolumePricing({
5006
5002
  }
5007
5003
  );
5008
5004
  }
5005
+ function parseValue(value) {
5006
+ if (!value) return void 0;
5007
+ const date = dateFns.parseISO(value);
5008
+ return dateFns.isValid(date) ? date : void 0;
5009
+ }
5010
+ function formatValue(date) {
5011
+ return dateFns.format(date, "yyyy-MM-dd");
5012
+ }
5013
+ function DatePicker({
5014
+ value,
5015
+ onChange,
5016
+ placeholder = "Select a date",
5017
+ minDate,
5018
+ maxDate,
5019
+ disabled,
5020
+ triggerDisabled,
5021
+ name,
5022
+ required,
5023
+ className,
5024
+ classNames,
5025
+ "aria-label": ariaLabel
5026
+ }) {
5027
+ const [open, setOpen] = React10.useState(false);
5028
+ const selected = parseValue(value);
5029
+ const disabledMatchers = [];
5030
+ if (minDate) disabledMatchers.push({ before: minDate });
5031
+ if (maxDate) disabledMatchers.push({ after: maxDate });
5032
+ if (Array.isArray(disabled)) {
5033
+ disabledMatchers.push(...disabled);
5034
+ } else if (disabled) {
5035
+ disabledMatchers.push(disabled);
5036
+ }
5037
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-date-picker": true, className: cn(className, classNames?.root), children: [
5038
+ /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover.Root, { open, onOpenChange: setOpen, children: [
5039
+ /* @__PURE__ */ jsxRuntime.jsxs(
5040
+ popover.Popover.Trigger,
5041
+ {
5042
+ type: "button",
5043
+ "aria-label": ariaLabel ?? placeholder,
5044
+ disabled: triggerDisabled,
5045
+ className: cn(
5046
+ "w-full inline-flex items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-left text-sm transition-colors hover:bg-muted/40 focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
5047
+ classNames?.trigger
5048
+ ),
5049
+ children: [
5050
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(!selected && "text-muted-foreground"), children: selected ? dateFns.format(selected, "EEE, MMM d, yyyy") : placeholder }),
5051
+ /* @__PURE__ */ jsxRuntime.jsxs(
5052
+ "svg",
5053
+ {
5054
+ width: "16",
5055
+ height: "16",
5056
+ viewBox: "0 0 24 24",
5057
+ fill: "none",
5058
+ stroke: "currentColor",
5059
+ strokeWidth: "2",
5060
+ strokeLinecap: "round",
5061
+ strokeLinejoin: "round",
5062
+ "aria-hidden": "true",
5063
+ className: "shrink-0 text-muted-foreground",
5064
+ children: [
5065
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
5066
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
5067
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
5068
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
5069
+ ]
5070
+ }
5071
+ )
5072
+ ]
5073
+ }
5074
+ ),
5075
+ /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Positioner, { sideOffset: 6, align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
5076
+ popover.Popover.Popup,
5077
+ {
5078
+ className: cn(
5079
+ "z-50 rounded-lg border border-border bg-background p-3 shadow-lg outline-none",
5080
+ classNames?.popup
5081
+ ),
5082
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5083
+ reactDayPicker.DayPicker,
5084
+ {
5085
+ mode: "single",
5086
+ selected,
5087
+ onSelect: (date) => {
5088
+ onChange?.(date ? formatValue(date) : "");
5089
+ if (date) setOpen(false);
5090
+ },
5091
+ disabled: disabledMatchers.length > 0 ? disabledMatchers : void 0,
5092
+ showOutsideDays: true,
5093
+ weekStartsOn: 0,
5094
+ className: cn("p-0", classNames?.calendar),
5095
+ classNames: {
5096
+ months: "flex flex-col gap-3",
5097
+ month: "flex flex-col gap-3",
5098
+ caption: "flex items-center justify-between px-1",
5099
+ caption_label: "text-sm font-semibold",
5100
+ nav: "flex items-center gap-1",
5101
+ nav_button: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted transition-colors",
5102
+ table: "w-full border-collapse",
5103
+ head_row: "flex",
5104
+ head_cell: "w-9 text-center text-[10px] font-medium uppercase tracking-wider text-muted-foreground",
5105
+ row: "flex w-full mt-1",
5106
+ cell: "w-9 h-9 text-center text-sm p-0 relative",
5107
+ day: cn(
5108
+ "inline-flex h-9 w-9 items-center justify-center rounded-md text-sm font-normal text-foreground hover:bg-muted transition-colors",
5109
+ "aria-selected:bg-foreground aria-selected:text-background aria-selected:hover:bg-foreground/90"
5110
+ ),
5111
+ day_selected: cn(
5112
+ "bg-foreground text-background hover:bg-foreground/90 focus:bg-foreground",
5113
+ classNames?.daySelected
5114
+ ),
5115
+ day_today: cn(
5116
+ "ring-1 ring-inset ring-primary/40",
5117
+ classNames?.dayToday
5118
+ ),
5119
+ day_outside: "text-muted-foreground/50",
5120
+ day_disabled: cn(
5121
+ "text-muted-foreground/40 cursor-not-allowed line-through",
5122
+ classNames?.dayDisabled
5123
+ )
5124
+ }
5125
+ }
5126
+ )
5127
+ }
5128
+ ) }) })
5129
+ ] }),
5130
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: value ?? "", required }) : null
5131
+ ] });
5132
+ }
5133
+ function parseHHmm(value) {
5134
+ if (!value) return null;
5135
+ const match = /^(\d{1,2}):(\d{2})$/.exec(value);
5136
+ if (!match) return null;
5137
+ const hour = Number.parseInt(match[1] ?? "", 10);
5138
+ const minute = Number.parseInt(match[2] ?? "", 10);
5139
+ if (Number.isNaN(hour) || Number.isNaN(minute)) return null;
5140
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;
5141
+ return { hour, minute };
5142
+ }
5143
+ function formatHHmm(hour, minute) {
5144
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
5145
+ }
5146
+ function formatDisplay(hour, minute, displayFormat) {
5147
+ if (displayFormat === "24h") {
5148
+ return formatHHmm(hour, minute);
5149
+ }
5150
+ const period = hour >= 12 ? "PM" : "AM";
5151
+ const displayHour = hour % 12 || 12;
5152
+ return `${displayHour}:${String(minute).padStart(2, "0")} ${period}`;
5153
+ }
5154
+ function buildOptions(minTime, maxTime, stepMinutes) {
5155
+ const min = parseHHmm(minTime) ?? { hour: 0, minute: 0 };
5156
+ const max = parseHHmm(maxTime) ?? { hour: 23, minute: 59 };
5157
+ const minTotal = min.hour * 60 + min.minute;
5158
+ const maxTotal = max.hour * 60 + max.minute;
5159
+ const step = Math.max(1, stepMinutes);
5160
+ const out = [];
5161
+ for (let total = minTotal; total <= maxTotal; total += step) {
5162
+ out.push(formatHHmm(Math.floor(total / 60), total % 60));
5163
+ }
5164
+ return out;
5165
+ }
5166
+ function TimePicker({
5167
+ value,
5168
+ onChange,
5169
+ placeholder = "Select a time",
5170
+ minTime = "00:00",
5171
+ maxTime = "23:30",
5172
+ stepMinutes = 30,
5173
+ displayFormat = "12h",
5174
+ triggerDisabled,
5175
+ name,
5176
+ required,
5177
+ className,
5178
+ classNames,
5179
+ "aria-label": ariaLabel
5180
+ }) {
5181
+ const [open, setOpen] = React10.useState(false);
5182
+ const parsed = parseHHmm(value);
5183
+ const options = React10.useMemo(
5184
+ () => buildOptions(minTime, maxTime, stepMinutes),
5185
+ [minTime, maxTime, stepMinutes]
5186
+ );
5187
+ const triggerLabel = parsed ? formatDisplay(parsed.hour, parsed.minute, displayFormat) : placeholder;
5188
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-time-picker": true, className: cn(className, classNames?.root), children: [
5189
+ /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover.Root, { open, onOpenChange: setOpen, children: [
5190
+ /* @__PURE__ */ jsxRuntime.jsxs(
5191
+ popover.Popover.Trigger,
5192
+ {
5193
+ type: "button",
5194
+ "aria-label": ariaLabel ?? placeholder,
5195
+ disabled: triggerDisabled,
5196
+ className: cn(
5197
+ "w-full inline-flex items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-left text-sm transition-colors hover:bg-muted/40 focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
5198
+ classNames?.trigger
5199
+ ),
5200
+ children: [
5201
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(!parsed && "text-muted-foreground"), children: triggerLabel }),
5202
+ /* @__PURE__ */ jsxRuntime.jsxs(
5203
+ "svg",
5204
+ {
5205
+ width: "16",
5206
+ height: "16",
5207
+ viewBox: "0 0 24 24",
5208
+ fill: "none",
5209
+ stroke: "currentColor",
5210
+ strokeWidth: "2",
5211
+ strokeLinecap: "round",
5212
+ strokeLinejoin: "round",
5213
+ "aria-hidden": "true",
5214
+ className: "shrink-0 text-muted-foreground",
5215
+ children: [
5216
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
5217
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12 7 12 12 15 14" })
5218
+ ]
5219
+ }
5220
+ )
5221
+ ]
5222
+ }
5223
+ ),
5224
+ /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(popover.Popover.Positioner, { sideOffset: 6, align: "start", children: /* @__PURE__ */ jsxRuntime.jsx(
5225
+ popover.Popover.Popup,
5226
+ {
5227
+ className: cn(
5228
+ "z-50 max-h-64 overflow-y-auto rounded-lg border border-border bg-background p-1 shadow-lg outline-none",
5229
+ classNames?.popup
5230
+ ),
5231
+ children: /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "listbox", "aria-label": ariaLabel ?? placeholder, className: "flex flex-col", children: options.map((option) => {
5232
+ const isSelected = option === value;
5233
+ const opt = parseHHmm(option);
5234
+ if (!opt) return null;
5235
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
5236
+ "button",
5237
+ {
5238
+ type: "button",
5239
+ role: "option",
5240
+ "aria-selected": isSelected,
5241
+ onClick: () => {
5242
+ onChange?.(option);
5243
+ setOpen(false);
5244
+ },
5245
+ className: cn(
5246
+ "w-full rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-muted",
5247
+ isSelected && "bg-foreground text-background hover:bg-foreground/90",
5248
+ classNames?.option,
5249
+ isSelected && classNames?.optionSelected
5250
+ ),
5251
+ children: formatDisplay(opt.hour, opt.minute, displayFormat)
5252
+ }
5253
+ ) }, option);
5254
+ }) })
5255
+ }
5256
+ ) }) })
5257
+ ] }),
5258
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: value ?? "", required }) : null
5259
+ ] });
5260
+ }
5009
5261
  function CustomerInputFields({
5010
5262
  fields,
5011
5263
  values,
@@ -5192,13 +5444,13 @@ function FieldInput({
5192
5444
  );
5193
5445
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.Date:
5194
5446
  return /* @__PURE__ */ jsxRuntime.jsx(
5195
- "input",
5447
+ DatePicker,
5196
5448
  {
5197
- type: "date",
5198
5449
  value: typeof value === "string" ? value : "",
5199
- onChange: (e) => onValueChange(e.target.value || void 0),
5450
+ onChange: (next) => onValueChange(next || void 0),
5200
5451
  required: field.is_required,
5201
- className: inputClass
5452
+ placeholder: field.placeholder ?? "Select a date",
5453
+ "aria-label": field.name
5202
5454
  }
5203
5455
  );
5204
5456
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.File:
@@ -5226,34 +5478,53 @@ function FieldInput({
5226
5478
  }
5227
5479
  );
5228
5480
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.DateTime: {
5229
- const dtLocalValue = typeof value === "string" && value.includes("T") ? value.slice(0, 16) : typeof value === "string" ? value : "";
5230
- return /* @__PURE__ */ jsxRuntime.jsx(
5231
- "input",
5232
- {
5233
- type: "datetime-local",
5234
- value: dtLocalValue,
5235
- onChange: (e) => {
5236
- if (!e.target.value) {
5237
- onValueChange(void 0);
5238
- return;
5239
- }
5240
- const date = new Date(e.target.value);
5241
- onValueChange(Number.isNaN(date.getTime()) ? e.target.value : date.toISOString());
5242
- },
5243
- required: field.is_required,
5244
- className: inputClass
5481
+ const stringValue = typeof value === "string" ? value : "";
5482
+ const [datePart, timePartRaw] = stringValue.includes("T") ? stringValue.split("T", 2) : [stringValue, ""];
5483
+ const timePart = (timePartRaw ?? "").slice(0, 5);
5484
+ const commit = (nextDate, nextTime) => {
5485
+ if (!nextDate && !nextTime) {
5486
+ onValueChange(void 0);
5487
+ return;
5245
5488
  }
5246
- );
5489
+ if (!nextDate) {
5490
+ onValueChange(`${nextTime}`);
5491
+ return;
5492
+ }
5493
+ const combined = `${nextDate}T${nextTime || "00:00"}`;
5494
+ const parsed = new Date(combined);
5495
+ onValueChange(Number.isNaN(parsed.getTime()) ? combined : parsed.toISOString());
5496
+ };
5497
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-[1fr_auto] gap-2", children: [
5498
+ /* @__PURE__ */ jsxRuntime.jsx(
5499
+ DatePicker,
5500
+ {
5501
+ value: datePart ?? "",
5502
+ onChange: (next) => commit(next, timePart),
5503
+ placeholder: field.placeholder ?? "Select a date",
5504
+ "aria-label": `${field.name} date`,
5505
+ required: field.is_required
5506
+ }
5507
+ ),
5508
+ /* @__PURE__ */ jsxRuntime.jsx(
5509
+ TimePicker,
5510
+ {
5511
+ value: timePart,
5512
+ onChange: (next) => commit(datePart ?? "", next),
5513
+ placeholder: "Time",
5514
+ "aria-label": `${field.name} time`
5515
+ }
5516
+ )
5517
+ ] });
5247
5518
  }
5248
5519
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.Time:
5249
5520
  return /* @__PURE__ */ jsxRuntime.jsx(
5250
- "input",
5521
+ TimePicker,
5251
5522
  {
5252
- type: "time",
5253
5523
  value: typeof value === "string" ? value : "",
5254
- onChange: (e) => onValueChange(e.target.value || void 0),
5524
+ onChange: (next) => onValueChange(next || void 0),
5255
5525
  required: field.is_required,
5256
- className: inputClass
5526
+ placeholder: field.placeholder ?? "Select a time",
5527
+ "aria-label": field.name
5257
5528
  }
5258
5529
  );
5259
5530
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.MultiSelect:
@@ -5273,8 +5544,7 @@ function FieldInput({
5273
5544
  field,
5274
5545
  value,
5275
5546
  onValueChange,
5276
- classNames,
5277
- inputClass
5547
+ classNames
5278
5548
  }
5279
5549
  );
5280
5550
  case chunkMN4PNKJA_js.INPUT_FIELD_TYPE.Address:
@@ -5443,38 +5713,39 @@ function DateRangeInput({
5443
5713
  field,
5444
5714
  value,
5445
5715
  onValueChange,
5446
- classNames,
5447
- inputClass
5716
+ classNames
5448
5717
  }) {
5449
5718
  const range = value && typeof value === "object" ? value : {};
5450
5719
  const update = (key, v) => {
5451
5720
  const next = { ...range, [key]: v };
5452
5721
  onValueChange(next.start || next.end ? next : void 0);
5453
5722
  };
5454
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid grid-cols-2 gap-3", classNames?.dateRangeInput), children: [
5723
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid grid-cols-1 sm:grid-cols-2 gap-3", classNames?.dateRangeInput), children: [
5455
5724
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5456
5725
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "Start" }),
5457
5726
  /* @__PURE__ */ jsxRuntime.jsx(
5458
- "input",
5727
+ DatePicker,
5459
5728
  {
5460
- type: "date",
5461
5729
  value: range.start || "",
5462
- onChange: (e) => update("start", e.target.value),
5730
+ onChange: (next) => update("start", next),
5463
5731
  required: field.is_required,
5464
- className: inputClass
5732
+ placeholder: "Start date",
5733
+ "aria-label": `${field.name} start date`,
5734
+ maxDate: range.end ? /* @__PURE__ */ new Date(`${range.end}T00:00`) : void 0
5465
5735
  }
5466
5736
  )
5467
5737
  ] }),
5468
5738
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5469
5739
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "End" }),
5470
5740
  /* @__PURE__ */ jsxRuntime.jsx(
5471
- "input",
5741
+ DatePicker,
5472
5742
  {
5473
- type: "date",
5474
5743
  value: range.end || "",
5475
- onChange: (e) => update("end", e.target.value),
5744
+ onChange: (next) => update("end", next),
5476
5745
  required: field.is_required,
5477
- className: inputClass
5746
+ placeholder: "End date",
5747
+ "aria-label": `${field.name} end date`,
5748
+ minDate: range.start ? /* @__PURE__ */ new Date(`${range.start}T00:00`) : void 0
5478
5749
  }
5479
5750
  )
5480
5751
  ] })
@@ -5918,6 +6189,39 @@ function formatTime(timeStr) {
5918
6189
  }
5919
6190
  return timeStr;
5920
6191
  }
6192
+ function pluralizeUnit(unit, value) {
6193
+ if (!unit) return value === 1 ? "day" : "days";
6194
+ const v = value ?? 1;
6195
+ if (unit === "minutes") return v === 1 ? "minute" : "minutes";
6196
+ if (unit === "hours") return v === 1 ? "hour" : "hours";
6197
+ if (unit === "days") return v === 1 ? "day" : "days";
6198
+ if (unit === "weeks") return v === 1 ? "week" : "weeks";
6199
+ if (unit === "months") return v === 1 ? "month" : "months";
6200
+ return unit;
6201
+ }
6202
+ function formatStaySummary(slot, durationUnit, durationValue) {
6203
+ const start = new Date(slot.start_time);
6204
+ const end = new Date(slot.end_time);
6205
+ const startLabel = start.toLocaleString(void 0, {
6206
+ weekday: "short",
6207
+ month: "short",
6208
+ day: "numeric",
6209
+ hour: "numeric",
6210
+ minute: "2-digit"
6211
+ });
6212
+ const endLabel = end.toLocaleString(void 0, {
6213
+ weekday: "short",
6214
+ month: "short",
6215
+ day: "numeric",
6216
+ hour: "numeric",
6217
+ minute: "2-digit"
6218
+ });
6219
+ const unitLabel = pluralizeUnit(durationUnit, durationValue);
6220
+ if (durationValue !== void 0) {
6221
+ return `${durationValue} ${unitLabel}: ${startLabel} \u2192 ${endLabel}`;
6222
+ }
6223
+ return `${startLabel} \u2192 ${endLabel}`;
6224
+ }
5921
6225
  function slotToValue(slot) {
5922
6226
  return `${slot.start_time}|${slot.end_time}`;
5923
6227
  }
@@ -5930,10 +6234,14 @@ function SlotPicker({
5930
6234
  onSlotSelect,
5931
6235
  groupByTimeOfDay = true,
5932
6236
  showPrice = true,
6237
+ schedulingMode = "intraday",
6238
+ durationUnit,
6239
+ durationValue,
5933
6240
  emptyMessage = "No available slots",
5934
6241
  className,
5935
6242
  classNames
5936
6243
  }) {
6244
+ const isMultiDay = schedulingMode === "multi_day";
5937
6245
  const { slots: fetched, isLoading } = useAvailableSlots(
5938
6246
  serviceId ?? null,
5939
6247
  date ?? null,
@@ -5964,7 +6272,7 @@ function SlotPicker({
5964
6272
  }
5965
6273
  );
5966
6274
  }
5967
- const groups = groupByTimeOfDay ? groupSlots(slots) : [{ label: "", slots }];
6275
+ const groups = groupByTimeOfDay && !isMultiDay ? groupSlots(slots) : [{ label: "", slots }];
5968
6276
  const slotsByValue = /* @__PURE__ */ new Map();
5969
6277
  for (const slot of slots) {
5970
6278
  slotsByValue.set(slotToValue(slot), slot);
@@ -5974,37 +6282,74 @@ function SlotPicker({
5974
6282
  radioGroup.RadioGroup,
5975
6283
  {
5976
6284
  "data-cimplify-slot-picker": true,
5977
- className: cn(className, classNames?.root),
6285
+ className: cn("flex flex-col gap-4", className, classNames?.root),
5978
6286
  value: selectedValue,
5979
6287
  onValueChange: (value) => {
5980
6288
  const slot = slotsByValue.get(value);
5981
- if (slot?.is_available) {
6289
+ if (slot && slot.is_available !== false) {
5982
6290
  onSlotSelect?.(slot);
5983
6291
  }
5984
6292
  },
5985
- children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-slot-group": true, className: classNames?.group, children: [
5986
- group.label && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-slot-group-label": true, className: classNames?.groupLabel, children: group.label }),
5987
- group.slots.map((slot) => {
5988
- const value = slotToValue(slot);
5989
- const isSelected = selectedSlot?.start_time === slot.start_time && selectedSlot?.end_time === slot.end_time;
5990
- return /* @__PURE__ */ jsxRuntime.jsxs(
5991
- radio.Radio.Root,
5992
- {
5993
- value,
5994
- disabled: !slot.is_available,
5995
- "data-cimplify-slot": true,
5996
- "data-selected": isSelected || void 0,
5997
- "data-unavailable": !slot.is_available || void 0,
5998
- className: classNames?.slot,
5999
- children: [
6000
- /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-slot-time": true, className: classNames?.slotTime, children: formatTime(slot.start_time) }),
6001
- showPrice && slot.price && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-slot-price": true, className: classNames?.slotPrice, children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: slot.price }) })
6002
- ]
6003
- },
6004
- value
6005
- );
6006
- })
6007
- ] }, group.label || "all"))
6293
+ children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
6294
+ "div",
6295
+ {
6296
+ "data-cimplify-slot-group": true,
6297
+ className: cn("flex flex-col gap-2", classNames?.group),
6298
+ children: [
6299
+ group.label && /* @__PURE__ */ jsxRuntime.jsx(
6300
+ "div",
6301
+ {
6302
+ "data-cimplify-slot-group-label": true,
6303
+ className: cn(
6304
+ "text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground",
6305
+ classNames?.groupLabel
6306
+ ),
6307
+ children: group.label
6308
+ }
6309
+ ),
6310
+ /* @__PURE__ */ jsxRuntime.jsx(
6311
+ "div",
6312
+ {
6313
+ className: cn(
6314
+ isMultiDay ? "flex flex-col gap-2" : "grid grid-cols-3 sm:grid-cols-4 gap-2"
6315
+ ),
6316
+ children: group.slots.map((slot) => {
6317
+ const value = slotToValue(slot);
6318
+ const isSelected = selectedSlot?.start_time === slot.start_time && selectedSlot?.end_time === slot.end_time;
6319
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6320
+ radio.Radio.Root,
6321
+ {
6322
+ value,
6323
+ disabled: slot.is_available === false,
6324
+ "data-cimplify-slot": true,
6325
+ "data-selected": isSelected || void 0,
6326
+ "data-unavailable": slot.is_available === false || void 0,
6327
+ className: cn(
6328
+ "inline-flex items-center justify-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[unavailable]:cursor-not-allowed data-[unavailable]:opacity-40 data-[unavailable]:line-through",
6329
+ isMultiDay && "justify-between text-left",
6330
+ classNames?.slot
6331
+ ),
6332
+ children: [
6333
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-slot-time": true, className: classNames?.slotTime, children: isMultiDay ? formatStaySummary(slot, durationUnit, durationValue) : formatTime(slot.start_time) }),
6334
+ showPrice && slot.price && /* @__PURE__ */ jsxRuntime.jsx(
6335
+ "span",
6336
+ {
6337
+ "data-cimplify-slot-price": true,
6338
+ className: cn("text-xs opacity-70", classNames?.slotPrice),
6339
+ children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: slot.price })
6340
+ }
6341
+ )
6342
+ ]
6343
+ },
6344
+ value
6345
+ );
6346
+ })
6347
+ }
6348
+ )
6349
+ ]
6350
+ },
6351
+ group.label || "all"
6352
+ ))
6008
6353
  }
6009
6354
  );
6010
6355
  }
@@ -6028,6 +6373,9 @@ function DateSlotPicker({
6028
6373
  onSlotSelect,
6029
6374
  availability: availabilityProp,
6030
6375
  showPrice = true,
6376
+ schedulingMode,
6377
+ durationUnit,
6378
+ durationValue,
6031
6379
  className,
6032
6380
  classNames
6033
6381
  }) {
@@ -6086,55 +6434,80 @@ function DateSlotPicker({
6086
6434
  value: selectedDate,
6087
6435
  onValueChange: handleDateChange,
6088
6436
  "data-cimplify-date-slot-picker": true,
6089
- className: cn(className, classNames?.root),
6437
+ className: cn("flex flex-col gap-4", className, classNames?.root),
6090
6438
  children: [
6091
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-date-nav": true, className: classNames?.nav, children: [
6092
- /* @__PURE__ */ jsxRuntime.jsx(
6093
- "button",
6094
- {
6095
- type: "button",
6096
- onClick: handlePrev,
6097
- disabled: offset === 0,
6098
- "data-cimplify-date-nav-prev": true,
6099
- className: classNames?.navButton,
6100
- children: "\u2190"
6101
- }
6102
- ),
6103
- /* @__PURE__ */ jsxRuntime.jsx(
6104
- "button",
6105
- {
6106
- type: "button",
6107
- onClick: handleNext,
6108
- "data-cimplify-date-nav-next": true,
6109
- className: classNames?.navButton,
6110
- children: "\u2192"
6111
- }
6112
- )
6113
- ] }),
6114
- /* @__PURE__ */ jsxRuntime.jsx(tabs.Tabs.List, { "data-cimplify-date-strip": true, className: classNames?.dateStrip, children: dateRange.dates.map((date) => {
6115
- const dayInfo = availabilityMap.get(date);
6116
- const hasAvailability = dayInfo?.has_availability !== false;
6117
- const isSelected = selectedDate === date;
6118
- return /* @__PURE__ */ jsxRuntime.jsx(
6119
- tabs.Tabs.Tab,
6120
- {
6121
- value: date,
6122
- "data-cimplify-date-button": true,
6123
- "data-selected": isSelected || void 0,
6124
- "data-available": hasAvailability || void 0,
6125
- "data-fully-booked": !hasAvailability || void 0,
6126
- className: classNames?.dateButton,
6127
- children: formatDate(date)
6128
- },
6129
- date
6130
- );
6131
- }) }),
6439
+ /* @__PURE__ */ jsxRuntime.jsxs(
6440
+ "div",
6441
+ {
6442
+ "data-cimplify-date-nav": true,
6443
+ className: cn("flex items-center justify-end gap-2", classNames?.nav),
6444
+ children: [
6445
+ /* @__PURE__ */ jsxRuntime.jsx(
6446
+ "button",
6447
+ {
6448
+ type: "button",
6449
+ onClick: handlePrev,
6450
+ disabled: offset === 0,
6451
+ "aria-label": "Previous dates",
6452
+ "data-cimplify-date-nav-prev": true,
6453
+ className: cn(
6454
+ "grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40",
6455
+ classNames?.navButton
6456
+ ),
6457
+ children: "\u2190"
6458
+ }
6459
+ ),
6460
+ /* @__PURE__ */ jsxRuntime.jsx(
6461
+ "button",
6462
+ {
6463
+ type: "button",
6464
+ onClick: handleNext,
6465
+ "aria-label": "Next dates",
6466
+ "data-cimplify-date-nav-next": true,
6467
+ className: cn(
6468
+ "grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
6469
+ classNames?.navButton
6470
+ ),
6471
+ children: "\u2192"
6472
+ }
6473
+ )
6474
+ ]
6475
+ }
6476
+ ),
6477
+ /* @__PURE__ */ jsxRuntime.jsx(
6478
+ tabs.Tabs.List,
6479
+ {
6480
+ "data-cimplify-date-strip": true,
6481
+ className: cn("grid grid-cols-7 gap-1 sm:gap-2", classNames?.dateStrip),
6482
+ children: dateRange.dates.map((date) => {
6483
+ const dayInfo = availabilityMap.get(date);
6484
+ const hasAvailability = dayInfo?.has_availability !== false;
6485
+ const isSelected = selectedDate === date;
6486
+ return /* @__PURE__ */ jsxRuntime.jsx(
6487
+ tabs.Tabs.Tab,
6488
+ {
6489
+ value: date,
6490
+ "data-cimplify-date-button": true,
6491
+ "data-selected": isSelected || void 0,
6492
+ "data-available": hasAvailability || void 0,
6493
+ "data-fully-booked": !hasAvailability || void 0,
6494
+ className: cn(
6495
+ "flex flex-col items-center justify-center rounded-md border border-border bg-background px-1 py-2 text-center text-xs font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[fully-booked]:cursor-not-allowed data-[fully-booked]:opacity-40",
6496
+ classNames?.dateButton
6497
+ ),
6498
+ children: formatDate(date)
6499
+ },
6500
+ date
6501
+ );
6502
+ })
6503
+ }
6504
+ ),
6132
6505
  availabilityLoading && /* @__PURE__ */ jsxRuntime.jsx(
6133
6506
  "div",
6134
6507
  {
6135
6508
  "data-cimplify-date-slot-loading": true,
6136
6509
  "aria-busy": "true",
6137
- className: classNames?.loading
6510
+ className: cn("h-32 rounded-md bg-muted/40 animate-pulse", classNames?.loading)
6138
6511
  }
6139
6512
  ),
6140
6513
  /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-date-slots": true, className: classNames?.slots, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -6145,7 +6518,10 @@ function DateSlotPicker({
6145
6518
  participantCount,
6146
6519
  selectedSlot,
6147
6520
  onSlotSelect: handleSlotSelect,
6148
- showPrice
6521
+ showPrice,
6522
+ schedulingMode,
6523
+ durationUnit,
6524
+ durationValue
6149
6525
  }
6150
6526
  ) })
6151
6527
  ]
@@ -7104,12 +7480,11 @@ function ProductSheet({
7104
7480
  )
7105
7481
  ] }),
7106
7482
  fullProduct.description && /* @__PURE__ */ jsxRuntime.jsx(
7107
- "p",
7483
+ "div",
7108
7484
  {
7109
7485
  "data-cimplify-product-sheet-description": true,
7110
- className: classNames?.description,
7111
- style: { margin: 0 },
7112
- children: fullProduct.description
7486
+ className: cn("text-sm leading-relaxed text-muted-foreground [&_p]:m-0 [&_p+p]:mt-2", classNames?.description),
7487
+ dangerouslySetInnerHTML: { __html: fullProduct.description }
7113
7488
  }
7114
7489
  ),
7115
7490
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -7140,14 +7515,14 @@ function CardImage({
7140
7515
  "16/9": "aspect-[16/9]"
7141
7516
  }[aspectRatio];
7142
7517
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-card-image": true, className: cn("relative overflow-hidden bg-muted", aspectClass, className), children: [
7143
- renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : /* @__PURE__ */ jsxRuntime.jsx(
7518
+ renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : src ? /* @__PURE__ */ jsxRuntime.jsx(
7144
7519
  "img",
7145
7520
  {
7146
7521
  src,
7147
7522
  alt,
7148
7523
  className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
7149
7524
  }
7150
- ),
7525
+ ) : null,
7151
7526
  children
7152
7527
  ] });
7153
7528
  }
@@ -7599,7 +7974,7 @@ function CompactServiceCard({
7599
7974
  const hasBillingPlans = product.billing_plans && product.billing_plans.length > 0;
7600
7975
  const href = `/products/${product.slug}`;
7601
7976
  const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 p-3", children: [
7602
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) }),
7977
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : image ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) : null }),
7603
7978
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
7604
7979
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
7605
7980
  product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
@@ -13280,6 +13655,7 @@ exports.CompositeSelector = CompositeSelector;
13280
13655
  exports.CurrencySelector = CurrencySelector;
13281
13656
  exports.CustomAttributesTable = CustomAttributesTable;
13282
13657
  exports.CustomerInputFields = CustomerInputFields;
13658
+ exports.DatePicker = DatePicker;
13283
13659
  exports.DateSlotPicker = DateSlotPicker;
13284
13660
  exports.DealBanner = DealBanner;
13285
13661
  exports.DealsPage = DealsPage;
@@ -13337,6 +13713,7 @@ exports.StandardServiceCard = StandardServiceCard;
13337
13713
  exports.StoreNav = StoreNav;
13338
13714
  exports.SubscriptionCard = SubscriptionCard;
13339
13715
  exports.TagPills = TagPills;
13716
+ exports.TimePicker = TimePicker;
13340
13717
  exports.TwoColumnGrid = TwoColumnGrid;
13341
13718
  exports.VariantSelector = VariantSelector;
13342
13719
  exports.VolumePricing = VolumePricing;