@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.
- package/cjs/dist/react/FulfillmentDetails.d.ts +139 -15
- package/cjs/dist/react/FulfillmentDetails.js +128 -24
- package/cjs/dist/react/ItemDetails.d.ts +4 -4
- package/cjs/dist/react/ItemDetails.js +5 -6
- package/cjs/dist/react/Settings.d.ts +8 -0
- package/cjs/dist/react/Settings.js +18 -25
- package/cjs/dist/react/core/FulfillmentDetails.d.ts +73 -7
- package/cjs/dist/react/core/FulfillmentDetails.js +133 -86
- package/cjs/dist/react/core/ItemDetails.d.ts +1 -1
- package/cjs/dist/react/core/ItemDetails.js +5 -16
- package/cjs/dist/react/core/Settings.d.ts +19 -4
- package/cjs/dist/react/core/Settings.js +29 -20
- package/cjs/dist/services/fulfillment-details-service.d.ts +127 -0
- package/cjs/dist/services/fulfillment-details-service.js +351 -0
- package/cjs/dist/services/fulfillments-service.js +67 -43
- package/cjs/dist/services/index.d.ts +2 -0
- package/cjs/dist/services/index.js +1 -0
- package/cjs/dist/services/item-details-service.d.ts +1 -1
- package/cjs/dist/services/item-details-service.js +10 -16
- package/cjs/dist/services/olo-settings-service.d.ts +0 -1
- package/cjs/dist/services/olo-settings-service.js +1 -3
- package/cjs/dist/services/utils.d.ts +4 -0
- package/cjs/dist/services/utils.js +12 -0
- package/cjs/dist/types/fulfillments-types.d.ts +18 -1
- package/cjs/dist/types/fulfillments-types.js +9 -0
- package/cjs/dist/utils/date-utils.d.ts +164 -0
- package/cjs/dist/utils/date-utils.js +297 -0
- package/cjs/dist/utils/fulfillments-utils.d.ts +3 -2
- package/cjs/dist/utils/fulfillments-utils.js +13 -57
- package/dist/react/FulfillmentDetails.d.ts +139 -15
- package/dist/react/FulfillmentDetails.js +128 -24
- package/dist/react/ItemDetails.d.ts +4 -4
- package/dist/react/ItemDetails.js +5 -6
- package/dist/react/Settings.d.ts +8 -0
- package/dist/react/Settings.js +18 -25
- package/dist/react/core/FulfillmentDetails.d.ts +73 -7
- package/dist/react/core/FulfillmentDetails.js +133 -86
- package/dist/react/core/ItemDetails.d.ts +1 -1
- package/dist/react/core/ItemDetails.js +5 -16
- package/dist/react/core/Settings.d.ts +19 -4
- package/dist/react/core/Settings.js +29 -20
- package/dist/services/fulfillment-details-service.d.ts +127 -0
- package/dist/services/fulfillment-details-service.js +351 -0
- package/dist/services/fulfillments-service.js +67 -43
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +1 -0
- package/dist/services/item-details-service.d.ts +1 -1
- package/dist/services/item-details-service.js +10 -16
- package/dist/services/olo-settings-service.d.ts +0 -1
- package/dist/services/olo-settings-service.js +1 -3
- package/dist/services/utils.d.ts +4 -0
- package/dist/services/utils.js +12 -0
- package/dist/types/fulfillments-types.d.ts +18 -1
- package/dist/types/fulfillments-types.js +9 -0
- package/dist/utils/date-utils.d.ts +164 -0
- package/dist/utils/date-utils.js +297 -0
- package/dist/utils/fulfillments-utils.d.ts +3 -2
- package/dist/utils/fulfillments-utils.js +13 -57
- package/package.json +3 -2
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
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
|
|
108
|
+
* value={selectedDate ? toISODateString(selectedDate) : ''}
|
|
96
109
|
* onChange={(e) => onDateChange(new Date(e.target.value))}
|
|
97
|
-
* min={minDate
|
|
98
|
-
* max={maxDate
|
|
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
|
-
|
|
195
|
-
|
|
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
|
|
198
|
-
selectedType:
|
|
199
|
-
onTypeChange: (type:
|
|
221
|
+
children?: AsChildChildren<{
|
|
222
|
+
selectedType: FulfillmentTypeEnum;
|
|
223
|
+
onTypeChange: (type: FulfillmentTypeEnum) => void;
|
|
200
224
|
hasAsapOption: boolean;
|
|
201
225
|
hasPreorderOption: boolean;
|
|
202
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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
|
|
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
|
|
104
|
+
* value={selectedDate ? toISODateString(selectedDate) : ''}
|
|
92
105
|
* onChange={(e) => onDateChange(new Date(e.target.value))}
|
|
93
|
-
* min={minDate
|
|
94
|
-
* max={maxDate
|
|
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
|
|
103
|
-
|
|
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,
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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 }) =>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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,
|
|
168
|
+
* {({ buttonState, formattedPrice, addToCartButtonDisabled,onHandleAddToCart }) => (
|
|
170
169
|
* <button
|
|
171
|
-
*
|
|
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,
|
|
121
|
+
* {({ buttonState, formattedPrice, addToCartButtonDisabled,onHandleAddToCart }) => (
|
|
122
122
|
* <button
|
|
123
|
-
*
|
|
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: ({
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
136
|
-
const isToday = timeSlot?.startTime &&
|
|
137
|
-
|
|
138
|
-
const
|
|
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
|
|
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] }))
|
|
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 &&
|
|
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 &&
|
|
366
|
+
}, content: freeDeliveryThreshold ?? '', ...rest, children: freeDeliveryThreshold && (_jsxs("div", { children: [label && _jsxs("span", { children: [label, " "] }), "$", freeDeliveryThreshold] })) })) }));
|
|
374
367
|
};
|