@wix/headless-restaurants-olo 0.0.33 → 0.0.35

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.
Files changed (59) hide show
  1. package/cjs/dist/react/FulfillmentDetails.d.ts +139 -15
  2. package/cjs/dist/react/FulfillmentDetails.js +128 -24
  3. package/cjs/dist/react/ItemDetails.d.ts +4 -4
  4. package/cjs/dist/react/ItemDetails.js +5 -6
  5. package/cjs/dist/react/Settings.d.ts +8 -0
  6. package/cjs/dist/react/Settings.js +18 -25
  7. package/cjs/dist/react/core/FulfillmentDetails.d.ts +73 -7
  8. package/cjs/dist/react/core/FulfillmentDetails.js +133 -86
  9. package/cjs/dist/react/core/ItemDetails.d.ts +1 -1
  10. package/cjs/dist/react/core/ItemDetails.js +5 -16
  11. package/cjs/dist/react/core/Settings.d.ts +19 -4
  12. package/cjs/dist/react/core/Settings.js +29 -20
  13. package/cjs/dist/services/fulfillment-details-service.d.ts +127 -0
  14. package/cjs/dist/services/fulfillment-details-service.js +351 -0
  15. package/cjs/dist/services/fulfillments-service.js +67 -43
  16. package/cjs/dist/services/index.d.ts +2 -0
  17. package/cjs/dist/services/index.js +1 -0
  18. package/cjs/dist/services/item-details-service.d.ts +1 -1
  19. package/cjs/dist/services/item-details-service.js +10 -16
  20. package/cjs/dist/services/olo-settings-service.d.ts +0 -1
  21. package/cjs/dist/services/olo-settings-service.js +1 -3
  22. package/cjs/dist/services/utils.d.ts +4 -0
  23. package/cjs/dist/services/utils.js +12 -0
  24. package/cjs/dist/types/fulfillments-types.d.ts +18 -1
  25. package/cjs/dist/types/fulfillments-types.js +9 -0
  26. package/cjs/dist/utils/date-utils.d.ts +164 -0
  27. package/cjs/dist/utils/date-utils.js +297 -0
  28. package/cjs/dist/utils/fulfillments-utils.d.ts +3 -2
  29. package/cjs/dist/utils/fulfillments-utils.js +13 -57
  30. package/dist/react/FulfillmentDetails.d.ts +139 -15
  31. package/dist/react/FulfillmentDetails.js +128 -24
  32. package/dist/react/ItemDetails.d.ts +4 -4
  33. package/dist/react/ItemDetails.js +5 -6
  34. package/dist/react/Settings.d.ts +8 -0
  35. package/dist/react/Settings.js +18 -25
  36. package/dist/react/core/FulfillmentDetails.d.ts +73 -7
  37. package/dist/react/core/FulfillmentDetails.js +133 -86
  38. package/dist/react/core/ItemDetails.d.ts +1 -1
  39. package/dist/react/core/ItemDetails.js +5 -16
  40. package/dist/react/core/Settings.d.ts +19 -4
  41. package/dist/react/core/Settings.js +29 -20
  42. package/dist/services/fulfillment-details-service.d.ts +127 -0
  43. package/dist/services/fulfillment-details-service.js +351 -0
  44. package/dist/services/fulfillments-service.js +67 -43
  45. package/dist/services/index.d.ts +2 -0
  46. package/dist/services/index.js +1 -0
  47. package/dist/services/item-details-service.d.ts +1 -1
  48. package/dist/services/item-details-service.js +10 -16
  49. package/dist/services/olo-settings-service.d.ts +0 -1
  50. package/dist/services/olo-settings-service.js +1 -3
  51. package/dist/services/utils.d.ts +4 -0
  52. package/dist/services/utils.js +12 -0
  53. package/dist/types/fulfillments-types.d.ts +18 -1
  54. package/dist/types/fulfillments-types.js +9 -0
  55. package/dist/utils/date-utils.d.ts +164 -0
  56. package/dist/utils/date-utils.js +297 -0
  57. package/dist/utils/fulfillments-utils.d.ts +3 -2
  58. package/dist/utils/fulfillments-utils.js +13 -57
  59. package/package.json +3 -2
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
- import { CoreFulfillmentDetails } from './core/index.js';
3
- import { DispatchType } from '../types/fulfillments-types.js';
2
+ import { DispatchType, FulfillmentTypeEnum } from '../types/fulfillments-types.js';
4
3
  import { AsChildChildren } from '@wix/headless-utils/react';
5
4
  interface TimeSlotContextValue {
6
5
  timeSlots: Array<{
@@ -67,6 +66,12 @@ export declare const AddressName: React.ForwardRefExoticComponent<AddressNamePro
67
66
  interface FulfillmentDateProps extends Omit<React.ComponentPropsWithoutRef<'input'>, 'children' | 'type' | 'value' | 'onChange' | 'min' | 'max'> {
68
67
  /** Whether to render as a child component */
69
68
  asChild?: boolean;
69
+ /** Optional label text or element for the date picker */
70
+ label?: React.ReactNode;
71
+ /** Optional class name for the label element */
72
+ labelClassName?: string;
73
+ /** Optional class name for the wrapper div (only rendered when label is provided) */
74
+ wrapperClassName?: string;
70
75
  /** Optional children render prop that receives the date picker props */
71
76
  children?: AsChildChildren<{
72
77
  selectedDate: Date | null;
@@ -87,15 +92,23 @@ interface FulfillmentDateProps extends Omit<React.ComponentPropsWithoutRef<'inpu
87
92
  * // With custom class name
88
93
  * <FulfillmentDetails.FulfillmentDate className="my-date-input" />
89
94
  *
95
+ * // With label
96
+ * <FulfillmentDetails.FulfillmentDate
97
+ * label="Date"
98
+ * labelClassName="block mb-1 text-sm"
99
+ * wrapperClassName="flex-1"
100
+ * className="my-date-input"
101
+ * />
102
+ *
90
103
  * // Using asChild pattern
91
104
  * <FulfillmentDetails.FulfillmentDate asChild>
92
105
  * {({ selectedDate, onDateChange, minDate, maxDate }) => (
93
106
  * <input
94
107
  * type="date"
95
- * value={selectedDate?.toISOString().split('T')[0]}
108
+ * value={selectedDate ? toISODateString(selectedDate) : ''}
96
109
  * onChange={(e) => onDateChange(new Date(e.target.value))}
97
- * min={minDate.toISOString().split('T')[0]}
98
- * max={maxDate.toISOString().split('T')[0]}
110
+ * min={toISODateString(minDate)}
111
+ * max={toISODateString(maxDate)}
99
112
  * />
100
113
  * )}
101
114
  * </FulfillmentDetails.FulfillmentDate>
@@ -191,22 +204,50 @@ interface TimeSlotOptionRepeaterProps {
191
204
  * ```
192
205
  */
193
206
  export declare const TimeSlotOptionRepeater: React.FC<TimeSlotOptionRepeaterProps>;
194
- export { FulfillmentTypeEnum } from './core/FulfillmentDetails.js';
195
- interface FulfillmentTypeProps {
207
+ interface FulfillmentTypeProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
208
+ /** Whether to render as a child component */
209
+ asChild?: boolean;
210
+ /** Label text for the ASAP option */
211
+ asapLabel?: string;
212
+ /** Label text for the schedule for later (preorder) option */
213
+ scheduleLabel?: string;
214
+ /** Aria label for the radio group */
215
+ groupAriaLabel?: string;
216
+ /** Template text for ASAP time display (e.g., "Ready in {0} min"). Use {0} for asapTime placeholder */
217
+ asapTimeText?: string;
218
+ /** Optional class name for the radio input elements */
219
+ inputClassName?: string;
196
220
  /** Children render prop that receives the fulfillment type selection props */
197
- children: (props: {
198
- selectedType: CoreFulfillmentDetails.FulfillmentTypeEnum;
199
- onTypeChange: (type: CoreFulfillmentDetails.FulfillmentTypeEnum) => void;
221
+ children?: AsChildChildren<{
222
+ selectedType: FulfillmentTypeEnum;
223
+ onTypeChange: (type: FulfillmentTypeEnum) => void;
200
224
  hasAsapOption: boolean;
201
225
  hasPreorderOption: boolean;
202
- }) => React.ReactNode;
226
+ asapTime: number | undefined;
227
+ asapTimeRange: {
228
+ minDuration?: number;
229
+ maxDuration?: number;
230
+ } | undefined;
231
+ }> | React.ReactNode;
203
232
  }
204
233
  /**
205
- * Headless component for fulfillment type selection (ASAP or Pre-order)
234
+ * Headless component for fulfillment type selection (ASAP or Schedule for later)
235
+ * Provides radio button functionality with default rendering
206
236
  *
207
237
  * @example
208
238
  * ```tsx
209
- * <FulfillmentDetails.FulfillmentType>
239
+ * // Default usage - renders radio buttons
240
+ * <FulfillmentDetails.FulfillmentType />
241
+ *
242
+ * // With custom labels
243
+ * <FulfillmentDetails.FulfillmentType
244
+ * asapLabel="Now"
245
+ * scheduleLabel="Later"
246
+ * groupAriaLabel="When do you want your order?"
247
+ * />
248
+ *
249
+ * // Using asChild pattern
250
+ * <FulfillmentDetails.FulfillmentType asChild>
210
251
  * {({ selectedType, onTypeChange, hasAsapOption, hasPreorderOption }) => (
211
252
  * <div>
212
253
  * {hasAsapOption && (
@@ -222,7 +263,7 @@ interface FulfillmentTypeProps {
222
263
  * onClick={() => onTypeChange(FulfillmentTypeEnum.PREORDER)}
223
264
  * data-selected={selectedType === FulfillmentTypeEnum.PREORDER}
224
265
  * >
225
- * Pre-order
266
+ * Schedule for later
226
267
  * </button>
227
268
  * )}
228
269
  * </div>
@@ -230,4 +271,87 @@ interface FulfillmentTypeProps {
230
271
  * </FulfillmentDetails.FulfillmentType>
231
272
  * ```
232
273
  */
233
- export declare const FulfillmentType: React.FC<FulfillmentTypeProps>;
274
+ export declare const FulfillmentType: React.ForwardRefExoticComponent<FulfillmentTypeProps & React.RefAttributes<HTMLDivElement>>;
275
+ /**
276
+ * Address fields that can be updated
277
+ */
278
+ export interface DeliveryAddressFields {
279
+ /** Street address line 1 */
280
+ addressLine?: string;
281
+ /** Street address line 2 (apartment, suite, etc.) */
282
+ addressLine2?: string;
283
+ /** City */
284
+ city?: string;
285
+ /** State/Province/Subdivision */
286
+ subdivision?: string;
287
+ /** Postal/ZIP code */
288
+ postalCode?: string;
289
+ /** Country */
290
+ country?: string;
291
+ /** Formatted full address string */
292
+ formattedAddress?: string;
293
+ }
294
+ interface DeliveryAddressProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
295
+ /** Whether to render as a child component */
296
+ asChild?: boolean;
297
+ /** Label text for the address input */
298
+ label?: React.ReactNode;
299
+ /** Optional class name for the label element */
300
+ labelClassName?: string;
301
+ /** Optional class name for the wrapper div (only rendered when label is provided) */
302
+ wrapperClassName?: string;
303
+ /** Placeholder text for the address input */
304
+ placeholder?: string;
305
+ /** Optional class name for the input element */
306
+ inputClassName?: string;
307
+ /** Children render prop that receives the address data */
308
+ children?: AsChildChildren<{
309
+ address: DeliveryAddressFields | null;
310
+ hasAddress: boolean;
311
+ isDelivery: boolean;
312
+ dispatchType: DispatchType | null;
313
+ onAddressChange: (address: DeliveryAddressFields) => void;
314
+ onAddressClear: () => void;
315
+ }> | React.ReactNode;
316
+ }
317
+ /**
318
+ * Headless component for editing the delivery address
319
+ * Only renders when dispatch type is DELIVERY
320
+ *
321
+ * @example
322
+ * ```tsx
323
+ * // Default usage - renders input for address line
324
+ * <FulfillmentDetails.DeliveryAddress
325
+ * label="Delivery Address"
326
+ * placeholder="Enter your address"
327
+ * />
328
+ *
329
+ * // Using asChild pattern for custom rendering
330
+ * <FulfillmentDetails.DeliveryAddress asChild>
331
+ * {({ address, hasAddress, isDelivery, onAddressChange, onAddressClear }) => (
332
+ * isDelivery && (
333
+ * <div>
334
+ * <input
335
+ * value={address?.addressLine || ''}
336
+ * onChange={(e) => onAddressChange({ ...address, addressLine: e.target.value })}
337
+ * placeholder="Street Address"
338
+ * />
339
+ * <input
340
+ * value={address?.city || ''}
341
+ * onChange={(e) => onAddressChange({ ...address, city: e.target.value })}
342
+ * placeholder="City"
343
+ * />
344
+ * <input
345
+ * value={address?.postalCode || ''}
346
+ * onChange={(e) => onAddressChange({ ...address, postalCode: e.target.value })}
347
+ * placeholder="ZIP Code"
348
+ * />
349
+ * {hasAddress && <button onClick={onAddressClear}>Clear</button>}
350
+ * </div>
351
+ * )
352
+ * )}
353
+ * </FulfillmentDetails.DeliveryAddress>
354
+ * ```
355
+ */
356
+ export declare const DeliveryAddress: React.ForwardRefExoticComponent<DeliveryAddressProps & React.RefAttributes<HTMLDivElement>>;
357
+ export {};
@@ -1,7 +1,10 @@
1
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { CoreFulfillmentDetails } from './core/index.js';
4
+ import { FulfillmentTypeEnum, } from '../types/fulfillments-types.js';
4
5
  import { AsChildSlot } from '@wix/headless-utils/react';
6
+ import { stringFormat } from '../utils/fulfillments-utils.js';
7
+ import { toISODateString } from '../utils/date-utils.js';
5
8
  const TimeSlotContext = React.createContext(null);
6
9
  function useTimeSlotContext() {
7
10
  const context = React.useContext(TimeSlotContext);
@@ -22,6 +25,8 @@ var TestIds;
22
25
  TestIds["timeSlotOptions"] = "fulfillment-time-slot-options";
23
26
  TestIds["timeSlotOption"] = "fulfillment-time-slot-option";
24
27
  TestIds["fulfillmentType"] = "fulfillment-type";
28
+ TestIds["deliveryAddress"] = "fulfillment-delivery-address";
29
+ TestIds["deliveryAddressInput"] = "fulfillment-delivery-address-input";
25
30
  })(TestIds || (TestIds = {}));
26
31
  /**
27
32
  * Root headless component for Fulfillment Details
@@ -61,7 +66,7 @@ Root.displayName = 'FulfillmentDetails.Root';
61
66
  */
62
67
  export const AddressName = React.forwardRef(({ children, asChild, className, ...rest }, ref) => {
63
68
  return (_jsx(CoreFulfillmentDetails.AddressName, { children: ({ addressName, fullAddress, hasAddress, dispatchType }) => {
64
- const displayText = addressName || fullAddress || 'No address selected';
69
+ const displayText = addressName || fullAddress;
65
70
  return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: {
66
71
  addressName,
67
72
  fullAddress,
@@ -83,29 +88,38 @@ AddressName.displayName = 'FulfillmentDetails.AddressName';
83
88
  * // With custom class name
84
89
  * <FulfillmentDetails.FulfillmentDate className="my-date-input" />
85
90
  *
91
+ * // With label
92
+ * <FulfillmentDetails.FulfillmentDate
93
+ * label="Date"
94
+ * labelClassName="block mb-1 text-sm"
95
+ * wrapperClassName="flex-1"
96
+ * className="my-date-input"
97
+ * />
98
+ *
86
99
  * // Using asChild pattern
87
100
  * <FulfillmentDetails.FulfillmentDate asChild>
88
101
  * {({ selectedDate, onDateChange, minDate, maxDate }) => (
89
102
  * <input
90
103
  * type="date"
91
- * value={selectedDate?.toISOString().split('T')[0]}
104
+ * value={selectedDate ? toISODateString(selectedDate) : ''}
92
105
  * onChange={(e) => onDateChange(new Date(e.target.value))}
93
- * min={minDate.toISOString().split('T')[0]}
94
- * max={maxDate.toISOString().split('T')[0]}
106
+ * min={toISODateString(minDate)}
107
+ * max={toISODateString(maxDate)}
95
108
  * />
96
109
  * )}
97
110
  * </FulfillmentDetails.FulfillmentDate>
98
111
  * ```
99
112
  */
100
- export const FulfillmentDate = React.forwardRef(({ children, asChild, className, ...rest }, ref) => {
113
+ export const FulfillmentDate = React.forwardRef(({ children, asChild, className, label, labelClassName, wrapperClassName, ...rest }, ref) => {
101
114
  return (_jsx(CoreFulfillmentDetails.FulfillmentDate, { children: ({ selectedDate, onDateChange, minDate, maxDate }) => {
102
- const defaultContent = (_jsx("input", { type: "date", value: selectedDate?.toISOString().split('T')[0] || '', onChange: (e) => onDateChange(new Date(e.target.value)), min: minDate.toISOString().split('T')[0], max: maxDate.toISOString().split('T')[0] }));
103
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: {
115
+ const inputElement = (_jsx("input", { ref: ref, type: "date", className: className, value: selectedDate ? toISODateString(selectedDate) : '', onChange: (e) => onDateChange(new Date(e.target.value)), min: toISODateString(minDate), max: toISODateString(maxDate), "data-testid": TestIds.fulfillmentDate, ...rest }));
116
+ const defaultContent = label ? (_jsxs("div", { className: wrapperClassName, children: [_jsx("label", { className: labelClassName, children: label }), inputElement] })) : (inputElement);
117
+ return (_jsx(AsChildSlot, { asChild: asChild, customElement: children, customElementProps: {
104
118
  selectedDate,
105
119
  onDateChange,
106
120
  minDate,
107
121
  maxDate,
108
- }, content: defaultContent, "data-testid": TestIds.fulfillmentDate, ...rest, children: defaultContent }));
122
+ }, content: defaultContent, children: defaultContent }));
109
123
  } }));
110
124
  });
111
125
  FulfillmentDate.displayName = 'FulfillmentDetails.FulfillmentDate';
@@ -211,16 +225,24 @@ export const TimeSlotOptionRepeater = ({ children, }) => {
211
225
  };
212
226
  TimeSlotOptionRepeater.displayName =
213
227
  'FulfillmentDetails.TimeSlotOptionRepeater';
214
- // ========================================
215
- // FULFILLMENT TYPE COMPONENT
216
- // ========================================
217
- export { FulfillmentTypeEnum } from './core/FulfillmentDetails.js';
218
228
  /**
219
- * Headless component for fulfillment type selection (ASAP or Pre-order)
229
+ * Headless component for fulfillment type selection (ASAP or Schedule for later)
230
+ * Provides radio button functionality with default rendering
220
231
  *
221
232
  * @example
222
233
  * ```tsx
223
- * <FulfillmentDetails.FulfillmentType>
234
+ * // Default usage - renders radio buttons
235
+ * <FulfillmentDetails.FulfillmentType />
236
+ *
237
+ * // With custom labels
238
+ * <FulfillmentDetails.FulfillmentType
239
+ * asapLabel="Now"
240
+ * scheduleLabel="Later"
241
+ * groupAriaLabel="When do you want your order?"
242
+ * />
243
+ *
244
+ * // Using asChild pattern
245
+ * <FulfillmentDetails.FulfillmentType asChild>
224
246
  * {({ selectedType, onTypeChange, hasAsapOption, hasPreorderOption }) => (
225
247
  * <div>
226
248
  * {hasAsapOption && (
@@ -236,7 +258,7 @@ export { FulfillmentTypeEnum } from './core/FulfillmentDetails.js';
236
258
  * onClick={() => onTypeChange(FulfillmentTypeEnum.PREORDER)}
237
259
  * data-selected={selectedType === FulfillmentTypeEnum.PREORDER}
238
260
  * >
239
- * Pre-order
261
+ * Schedule for later
240
262
  * </button>
241
263
  * )}
242
264
  * </div>
@@ -244,12 +266,94 @@ export { FulfillmentTypeEnum } from './core/FulfillmentDetails.js';
244
266
  * </FulfillmentDetails.FulfillmentType>
245
267
  * ```
246
268
  */
247
- export const FulfillmentType = ({ children, }) => {
248
- return (_jsx(CoreFulfillmentDetails.FulfillmentType, { children: ({ selectedType, onTypeChange, hasAsapOption, hasPreorderOption }) => children({
249
- selectedType,
250
- onTypeChange,
251
- hasAsapOption,
252
- hasPreorderOption,
253
- }) }));
254
- };
269
+ export const FulfillmentType = React.forwardRef(({ children, asChild, className, asapLabel, scheduleLabel, groupAriaLabel, asapTimeText, inputClassName, ...rest }, ref) => {
270
+ return (_jsx(CoreFulfillmentDetails.FulfillmentType, { children: ({ selectedType, onTypeChange, hasAsapOption, hasPreorderOption, hasAsapAndFutureOption, asapTime, asapTimeRange, }) => {
271
+ const radioGroupName = React.useId();
272
+ const isOnlyAsap = hasAsapOption && !hasAsapAndFutureOption;
273
+ // Format ASAP label with time if asapTimeText template is provided
274
+ const formattedAsapLabel = asapTimeText
275
+ ? stringFormat(asapTimeText, asapTime)
276
+ : asapTime;
277
+ const onlyAsapContent = isOnlyAsap && formattedAsapLabel;
278
+ const renderRadioOption = (value, label) => (_jsxs("label", { children: [_jsx("input", { type: "radio", name: radioGroupName, value: value, checked: selectedType === value, onChange: () => onTypeChange(value), className: inputClassName }), label] }));
279
+ const defaultContent = hasAsapAndFutureOption && (_jsxs("div", { role: "radiogroup", "aria-label": groupAriaLabel, children: [hasAsapOption &&
280
+ renderRadioOption(FulfillmentTypeEnum.ASAP, asapLabel), hasAsapAndFutureOption &&
281
+ renderRadioOption(FulfillmentTypeEnum.ASAP_AND_FUTURE, scheduleLabel)] }));
282
+ // Use onlyAsapContent when only ASAP is available, otherwise use defaultContent
283
+ const contentToRender = isOnlyAsap ? onlyAsapContent : defaultContent;
284
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: {
285
+ selectedType,
286
+ onTypeChange,
287
+ hasAsapOption,
288
+ hasAsapAndFutureOption,
289
+ hasPreorderOption,
290
+ asapTime,
291
+ asapTimeRange,
292
+ }, content: contentToRender, "data-testid": TestIds.fulfillmentType, ...rest, children: contentToRender }));
293
+ } }));
294
+ });
255
295
  FulfillmentType.displayName = 'FulfillmentDetails.FulfillmentType';
296
+ /**
297
+ * Headless component for editing the delivery address
298
+ * Only renders when dispatch type is DELIVERY
299
+ *
300
+ * @example
301
+ * ```tsx
302
+ * // Default usage - renders input for address line
303
+ * <FulfillmentDetails.DeliveryAddress
304
+ * label="Delivery Address"
305
+ * placeholder="Enter your address"
306
+ * />
307
+ *
308
+ * // Using asChild pattern for custom rendering
309
+ * <FulfillmentDetails.DeliveryAddress asChild>
310
+ * {({ address, hasAddress, isDelivery, onAddressChange, onAddressClear }) => (
311
+ * isDelivery && (
312
+ * <div>
313
+ * <input
314
+ * value={address?.addressLine || ''}
315
+ * onChange={(e) => onAddressChange({ ...address, addressLine: e.target.value })}
316
+ * placeholder="Street Address"
317
+ * />
318
+ * <input
319
+ * value={address?.city || ''}
320
+ * onChange={(e) => onAddressChange({ ...address, city: e.target.value })}
321
+ * placeholder="City"
322
+ * />
323
+ * <input
324
+ * value={address?.postalCode || ''}
325
+ * onChange={(e) => onAddressChange({ ...address, postalCode: e.target.value })}
326
+ * placeholder="ZIP Code"
327
+ * />
328
+ * {hasAddress && <button onClick={onAddressClear}>Clear</button>}
329
+ * </div>
330
+ * )
331
+ * )}
332
+ * </FulfillmentDetails.DeliveryAddress>
333
+ * ```
334
+ */
335
+ export const DeliveryAddress = React.forwardRef(({ children, asChild, className, label, labelClassName, wrapperClassName, placeholder = 'Enter delivery address', inputClassName, ...rest }, ref) => {
336
+ return (_jsx(CoreFulfillmentDetails.DeliveryAddress, { children: ({ address, hasAddress, isDelivery, dispatchType, onAddressChange, onAddressClear, }) => {
337
+ // Don't render anything if not delivery
338
+ if (!isDelivery) {
339
+ return null;
340
+ }
341
+ const handleAddressLineChange = (e) => {
342
+ onAddressChange({
343
+ ...address,
344
+ addressLine: e.target.value,
345
+ });
346
+ };
347
+ const inputElement = (_jsx("input", { type: "text", className: inputClassName, value: address?.addressLine || '', onChange: handleAddressLineChange, placeholder: placeholder, "data-testid": TestIds.deliveryAddressInput }));
348
+ const defaultContent = label ? (_jsxs("div", { className: wrapperClassName, children: [_jsx("label", { className: labelClassName, children: label }), inputElement] })) : (inputElement);
349
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: {
350
+ address,
351
+ hasAddress,
352
+ isDelivery,
353
+ dispatchType,
354
+ onAddressChange,
355
+ onAddressClear,
356
+ }, content: defaultContent, "data-testid": TestIds.deliveryAddress, ...rest, children: defaultContent }));
357
+ } }));
358
+ });
359
+ DeliveryAddress.displayName = 'FulfillmentDetails.DeliveryAddress';
@@ -131,7 +131,7 @@ export interface AddToCartButtonProps {
131
131
  addToCartLabelMap: Record<AddToCartButtonState, string>;
132
132
  /** Children render prop that receives button state and line item data */
133
133
  children?: AsChildChildren<{
134
- lineItem: LineItem;
134
+ onHandleAddToCart: (onClick: (lineItem: LineItem) => void) => void;
135
135
  buttonState: AddToCartButtonState;
136
136
  addToCartButtonDisabled?: boolean;
137
137
  /** Content to display when loading */
@@ -139,7 +139,6 @@ export interface AddToCartButtonProps {
139
139
  /** Text label for the button */
140
140
  label: React.ReactNode;
141
141
  formattedPrice: string;
142
- modifierGroupHasError: boolean;
143
142
  }>;
144
143
  }
145
144
  /**
@@ -166,9 +165,10 @@ export interface AddToCartButtonProps {
166
165
  * addToCartLabelMap={labelMap}
167
166
  * asChild
168
167
  * >
169
- * {({ buttonState, formattedPrice, modifierGroupHasError, addToCartButtonDisabled }) => (
168
+ * {({ buttonState, formattedPrice, addToCartButtonDisabled,onHandleAddToCart }) => (
170
169
  * <button
171
- * disabled={addToCartButtonDisabled || modifierGroupHasError}
170
+ * onClick={() => onHandleAddToCart(onClickFunction)}
171
+ * disabled={addToCartButtonDisabled}
172
172
  * className="bg-primary text-primary-foreground"
173
173
  * >
174
174
  * <span>{labelMap[buttonState]}</span>
@@ -118,9 +118,10 @@ Variants.displayName = 'ItemDetails.Variants';
118
118
  * addToCartLabelMap={labelMap}
119
119
  * asChild
120
120
  * >
121
- * {({ buttonState, formattedPrice, modifierGroupHasError, addToCartButtonDisabled }) => (
121
+ * {({ buttonState, formattedPrice, addToCartButtonDisabled,onHandleAddToCart }) => (
122
122
  * <button
123
- * disabled={addToCartButtonDisabled || modifierGroupHasError}
123
+ * onClick={() => onHandleAddToCart(onClickFunction)}
124
+ * disabled={addToCartButtonDisabled}
124
125
  * className="bg-primary text-primary-foreground"
125
126
  * >
126
127
  * <span>{labelMap[buttonState]}</span>
@@ -131,17 +132,15 @@ Variants.displayName = 'ItemDetails.Variants';
131
132
  * ```
132
133
  */
133
134
  export const AddToCartButton = React.forwardRef(({ asChild, children, className, addToCartLabelMap, ...props }, ref) => {
134
- return (_jsx(CoreItemDetails.LineItemComponent, { addToCartLabelMap: addToCartLabelMap, children: ({ lineItem, buttonState, addToCartButtonDisabled, loadingState, labelText, formattedPrice, modifierGroupHasError, }) => {
135
+ return (_jsx(CoreItemDetails.LineItemComponent, { addToCartLabelMap: addToCartLabelMap, children: ({ onHandleAddToCart, buttonState, addToCartButtonDisabled, loadingState, labelText, formattedPrice, lineItem, }) => {
135
136
  const label = (_jsxs(_Fragment, { children: [_jsx("span", { children: labelText }), " ", _jsx("span", { children: " | " }), _jsx("span", { children: formattedPrice })] }));
136
137
  return (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: {
137
138
  buttonState,
138
139
  addToCartButtonDisabled,
139
140
  loadingState,
140
- lineItem,
141
- lineItems: [lineItem],
141
+ onHandleAddToCart,
142
142
  label,
143
143
  formattedPrice,
144
- modifierGroupHasError,
145
144
  }, ref: ref, ...props, children: _jsx(Commerce.Actions.AddToCart, { asChild: false, label: label, className: className, lineItems: [lineItem], ...props }) }));
146
145
  } }));
147
146
  });
@@ -37,6 +37,8 @@ interface CurrentTimeSlotProps {
37
37
  className?: string;
38
38
  /** Whether to render as a child component */
39
39
  asChild?: boolean;
40
+ /** prefixElement to display before the time slot text */
41
+ prefixElement?: React.ReactNode;
40
42
  deliveryTypeText: string;
41
43
  pickupTypeText: string;
42
44
  asapTimeText: string;
@@ -172,6 +174,8 @@ interface CurrentLocationProps {
172
174
  export declare const CurrentLocation: React.FC<CurrentLocationProps>;
173
175
  interface MinOrderAmountProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
174
176
  asChild?: boolean;
177
+ /** Label text to display before the minimum order amount (e.g., "Min Order:") */
178
+ label?: string;
175
179
  /** Children render prop that receives the minimum order amount data */
176
180
  children?: AsChildChildren<{
177
181
  minOrderAmount: number | undefined;
@@ -242,6 +246,8 @@ interface SelectedAddressProps extends Omit<React.ComponentPropsWithoutRef<'div'
242
246
  export declare const SelectedAddress: React.FC<SelectedAddressProps>;
243
247
  interface DeliveryFeeProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
244
248
  asChild?: boolean;
249
+ /** Label text to display before the delivery fee (e.g., "Delivery Fee:") */
250
+ label?: string;
245
251
  /** Children render prop that receives the delivery fee data */
246
252
  children?: AsChildChildren<{
247
253
  deliveryFee: string | undefined;
@@ -267,6 +273,8 @@ interface DeliveryFeeProps extends Omit<React.ComponentPropsWithoutRef<'div'>, '
267
273
  export declare const DeliveryFee: React.FC<DeliveryFeeProps>;
268
274
  interface FreeDeliveryThresholdProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
269
275
  asChild?: boolean;
276
+ /** Label text to display before the free delivery threshold (e.g., "Free delivery above:") */
277
+ label?: string;
270
278
  /** Children render prop that receives the free delivery threshold data */
271
279
  children?: AsChildChildren<{
272
280
  freeDeliveryThreshold: string | undefined;
@@ -4,6 +4,7 @@ import { CoreSettings } from './core/index.js';
4
4
  import { DispatchType, } from '../types/fulfillments-types.js';
5
5
  import { AsChildSlot } from '@wix/headless-utils/react';
6
6
  import { stringFormat } from '../utils/fulfillments-utils.js';
7
+ import { formatTime, getFutureDate, getLocale, getToday, isSameDay, } from '../utils/date-utils.js';
7
8
  const DispatchTypeContext = React.createContext(null);
8
9
  function useDispatchTypeContext() {
9
10
  const context = React.useContext(DispatchTypeContext);
@@ -80,13 +81,6 @@ var TimeSlotDisplayType;
80
81
  TimeSlotDisplayType["PREORDER_TOMORROW"] = "PREORDER_TOMORROW";
81
82
  TimeSlotDisplayType["SCHEDULED"] = "SCHEDULED";
82
83
  })(TimeSlotDisplayType || (TimeSlotDisplayType = {}));
83
- // Time format options
84
- const TIME_ONLY_FORMAT = {
85
- hour: 'numeric',
86
- minute: 'numeric',
87
- };
88
- // Helper to format a date as time only (e.g., "10:30 AM")
89
- const formatTime = (date) => date?.toLocaleString('en-US', TIME_ONLY_FORMAT) ?? '';
90
84
  const getTimeSlotDisplayType = (params) => {
91
85
  const { asapTime, asapTimeRange, isToday, isTomorrow } = params;
92
86
  if (asapTime)
@@ -112,15 +106,15 @@ const formatTimeSlotText = (params, templates) => {
112
106
  case TimeSlotDisplayType.ASAP_TIME_RANGE:
113
107
  return stringFormat(asapTimeRangeText, asapTimeRange?.minDuration ?? 0, asapTimeRange?.maxDuration ?? 0);
114
108
  case TimeSlotDisplayType.PREORDER_TODAY:
115
- return stringFormat(preorderTodayText, startTimeStr, endTimeStr);
109
+ return stringFormat(preorderTodayText, startTimeStr);
116
110
  case TimeSlotDisplayType.PREORDER_TOMORROW:
117
111
  return stringFormat(preorderTomorrowText, startTimeStr, endTimeStr);
118
112
  case TimeSlotDisplayType.SCHEDULED:
119
113
  default:
120
- return `${timeSlot?.startTime.toLocaleString('en-US', fullDateOptions)} - ${timeSlot?.endTime.toLocaleString('en-US', fullDateOptions)}`;
114
+ return `${timeSlot?.startTime.toLocaleString(getLocale(), fullDateOptions)} - ${timeSlot?.endTime.toLocaleString(getLocale(), fullDateOptions)}`;
121
115
  }
122
116
  };
123
- export const CurrentTimeSlot = React.forwardRef(({ asChild, children, className, deliveryTypeText, pickupTypeText, asapTimeText = '', asapTimeRangeText = '', preorderTodayText = '', preorderTomorrowText = '', ...rest }, ref) => {
117
+ export const CurrentTimeSlot = React.forwardRef(({ asChild, children, className, prefixElement, deliveryTypeText, pickupTypeText, asapTimeText = '', asapTimeRangeText = '', preorderTodayText = '', preorderTomorrowText = '', ...rest }, ref) => {
124
118
  const fullDateOptions = {
125
119
  month: 'short',
126
120
  day: 'numeric',
@@ -128,17 +122,16 @@ export const CurrentTimeSlot = React.forwardRef(({ asChild, children, className,
128
122
  minute: 'numeric',
129
123
  };
130
124
  return (_jsx(CoreSettings.CurrentTimeSlot, { children: ({ timeSlot, hasDetails, asapTime, asapTimeRange }) => {
125
+ if (!hasDetails)
126
+ return null;
131
127
  const dispatchTypeText = timeSlot?.dispatchType === DispatchType.DELIVERY
132
128
  ? deliveryTypeText
133
129
  : pickupTypeText;
134
130
  // Determine if the time slot is today or tomorrow
135
- const now = new Date();
136
- const isToday = timeSlot?.startTime &&
137
- timeSlot.startTime.toDateString() === now.toDateString();
138
- const tomorrow = new Date(now);
139
- tomorrow.setDate(tomorrow.getDate() + 1);
140
- const isTomorrow = timeSlot?.startTime &&
141
- timeSlot.startTime.toDateString() === tomorrow.toDateString();
131
+ const now = getToday();
132
+ const isToday = timeSlot?.startTime && isSameDay(timeSlot.startTime, now);
133
+ const tomorrow = getFutureDate(1);
134
+ const isTomorrow = timeSlot?.startTime && isSameDay(timeSlot.startTime, tomorrow);
142
135
  const timeSlotText = formatTimeSlotText({ asapTime, asapTimeRange, timeSlot, isToday, isTomorrow }, {
143
136
  asapTimeText,
144
137
  asapTimeRangeText,
@@ -146,14 +139,14 @@ export const CurrentTimeSlot = React.forwardRef(({ asChild, children, className,
146
139
  preorderTomorrowText,
147
140
  fullDateOptions,
148
141
  });
149
- return hasDetails ? (_jsxs(AsChildSlot, { ref: ref, asChild: asChild,
142
+ return (_jsxs(AsChildSlot, { ref: ref, asChild: asChild,
150
143
  // testId={TestIds.currentTimeSlot}
151
144
  className: className, customElement: children, customElementProps: {
152
145
  timeSlot,
153
146
  hasDetails,
154
147
  asapTime,
155
148
  asapTimeRange,
156
- }, content: timeSlot, ...rest, children: [dispatchTypeText, " ", timeSlotText] })) : undefined;
149
+ }, content: timeSlot, ...rest, children: [prefixElement, dispatchTypeText, " ", timeSlotText] }));
157
150
  } }));
158
151
  });
159
152
  CurrentTimeSlot.displayName = 'Settings.CurrentTimeSlot';
@@ -283,8 +276,8 @@ export const CurrentLocation = ({ children, className, asChild, }) => {
283
276
  * </Settings.MinOrderAmount>
284
277
  * ```
285
278
  */
286
- export const MinOrderAmount = ({ children, asChild, className, ...rest }) => {
287
- return (_jsx(CoreSettings.MinOrderAmount, { children: ({ minOrderAmount, hasMinOrderAmount }) => (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: { minOrderAmount, hasMinOrderAmount }, content: minOrderAmount?.toString() ?? '', ...rest, children: minOrderAmount && _jsxs("div", { children: ["$", minOrderAmount] }) })) }));
279
+ export const MinOrderAmount = ({ children, asChild, className, label, ...rest }) => {
280
+ return (_jsx(CoreSettings.MinOrderAmount, { children: ({ minOrderAmount, hasMinOrderAmount }) => (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: { minOrderAmount, hasMinOrderAmount }, content: minOrderAmount?.toString() ?? '', ...rest, children: minOrderAmount && (_jsxs("div", { children: [label && _jsxs("span", { children: [label, " "] }), "$", minOrderAmount] })) })) }));
288
281
  };
289
282
  /**
290
283
  * Headless component for accepting orders status
@@ -349,8 +342,8 @@ export const SelectedAddress = ({ children, asChild, className, ...rest }) => {
349
342
  * </Settings.DeliveryFee>
350
343
  * ```
351
344
  */
352
- export const DeliveryFee = ({ children, asChild, className, ...rest }) => {
353
- return (_jsx(CoreSettings.DeliveryFee, { children: ({ deliveryFee, hasDeliveryFee }) => (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: { deliveryFee, hasDeliveryFee }, content: deliveryFee ?? '', ...rest, children: deliveryFee && _jsx("div", { children: deliveryFee }) })) }));
345
+ export const DeliveryFee = ({ children, asChild, className, label, ...rest }) => {
346
+ return (_jsx(CoreSettings.DeliveryFee, { children: ({ deliveryFee, hasDeliveryFee }) => (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: { deliveryFee, hasDeliveryFee }, content: deliveryFee ?? '', ...rest, children: deliveryFee && (_jsxs("div", { children: [label && _jsxs("span", { children: [label, " "] }), deliveryFee] })) })) }));
354
347
  };
355
348
  /**
356
349
  * Headless component for free delivery threshold information
@@ -366,9 +359,9 @@ export const DeliveryFee = ({ children, asChild, className, ...rest }) => {
366
359
  * </Settings.FreeDeliveryThreshold>
367
360
  * ```
368
361
  */
369
- export const FreeDeliveryThreshold = ({ children, asChild, className, ...rest }) => {
362
+ export const FreeDeliveryThreshold = ({ children, asChild, className, label, ...rest }) => {
370
363
  return (_jsx(CoreSettings.FreeDeliveryThreshold, { children: ({ freeDeliveryThreshold, hasFreeDeliveryThreshold }) => (_jsx(AsChildSlot, { asChild: asChild, className: className, customElement: children, customElementProps: {
371
364
  freeDeliveryThreshold,
372
365
  hasFreeDeliveryThreshold,
373
- }, content: freeDeliveryThreshold ?? '', ...rest, children: freeDeliveryThreshold && _jsx("div", { children: freeDeliveryThreshold }) })) }));
366
+ }, content: freeDeliveryThreshold ?? '', ...rest, children: freeDeliveryThreshold && (_jsxs("div", { children: [label && _jsxs("span", { children: [label, " "] }), "$", freeDeliveryThreshold] })) })) }));
374
367
  };