@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,7 +1,11 @@
|
|
|
1
1
|
import * as operationsSDK from '@wix/auto_sdk_restaurants_operations';
|
|
2
2
|
import { DispatchType, } from '../types/fulfillments-types.js';
|
|
3
|
-
import { EntitiesDayOfWeek as DayOfWeek, } from '@wix/auto_sdk_restaurants_fulfillment-methods';
|
|
4
3
|
import { FulfillmentTimeType, } from '@wix/auto_sdk_restaurants_operations';
|
|
4
|
+
import { DAY_OF_WEEK, isInTimeRange, getDayOfWeekNumber, getLocale, convertDateToTimezone, } from './date-utils.js';
|
|
5
|
+
// Re-export date utilities for convenience
|
|
6
|
+
export { convertTimeOfDayToDateTime, convertDateToTimezone, formatDateInTimezone, formatTime, formatTimeRange, getDatePartsInTimezone, buildDateFromParts, isInTimeRange, DAY_OF_WEEK, DayOfWeek, getDayOfWeekNumber,
|
|
7
|
+
// Locale & timezone utilities
|
|
8
|
+
getLocale, getTimezone, } from './date-utils.js';
|
|
5
9
|
/**
|
|
6
10
|
* Format a string with placeholders like {0}, {1}, etc.
|
|
7
11
|
* Similar to C#'s String.Format
|
|
@@ -150,6 +154,8 @@ export const processFulfillmentTimeSlotByOperationList = (operationTimeSlot) =>
|
|
|
150
154
|
durationRangeOptions: selectedFulfillmentInfo?.durationRange,
|
|
151
155
|
...selectedFulfillmentInfo,
|
|
152
156
|
};
|
|
157
|
+
// const startTime = convertTimeOfDayToDateTime(startTime!, timezone);
|
|
158
|
+
// const endTime = convertTimeOfDayToDateTime(endTime!, timezone);
|
|
153
159
|
return {
|
|
154
160
|
id: createTimeSlotId(startTime, endTime),
|
|
155
161
|
startTime: startTime,
|
|
@@ -163,71 +169,22 @@ export const processFulfillmentTimeSlotByOperationList = (operationTimeSlot) =>
|
|
|
163
169
|
}),
|
|
164
170
|
];
|
|
165
171
|
};
|
|
166
|
-
const DAY_OF_WEEK = {
|
|
167
|
-
[DayOfWeek.MON]: 1,
|
|
168
|
-
[DayOfWeek.TUE]: 2,
|
|
169
|
-
[DayOfWeek.WED]: 3,
|
|
170
|
-
[DayOfWeek.THU]: 4,
|
|
171
|
-
[DayOfWeek.FRI]: 5,
|
|
172
|
-
[DayOfWeek.SAT]: 6,
|
|
173
|
-
[DayOfWeek.SUN]: 7,
|
|
174
|
-
};
|
|
175
|
-
const convertTimeOfDayToDateTime = (time, timezone) => {
|
|
176
|
-
const now = new Date();
|
|
177
|
-
if (timezone) {
|
|
178
|
-
// Get today's date components in the specified timezone
|
|
179
|
-
const options = {
|
|
180
|
-
timeZone: timezone,
|
|
181
|
-
year: 'numeric',
|
|
182
|
-
month: '2-digit',
|
|
183
|
-
day: '2-digit',
|
|
184
|
-
};
|
|
185
|
-
const parts = new Intl.DateTimeFormat('en-US', options).formatToParts(now);
|
|
186
|
-
const year = parts.find((p) => p.type === 'year')?.value;
|
|
187
|
-
const month = parts.find((p) => p.type === 'month')?.value;
|
|
188
|
-
const day = parts.find((p) => p.type === 'day')?.value;
|
|
189
|
-
// Create an ISO string with the date in the timezone and the specified time
|
|
190
|
-
const hours = String(time.hours ?? 0).padStart(2, '0');
|
|
191
|
-
const minutes = String(time.minutes ?? 0).padStart(2, '0');
|
|
192
|
-
const isoString = `${year}-${month}-${day}T${hours}:${minutes}:00.000`;
|
|
193
|
-
// Convert to Date - this will be interpreted as local time
|
|
194
|
-
const localDate = new Date(isoString);
|
|
195
|
-
// Get what this time would be formatted as in the target timezone
|
|
196
|
-
const formattedInTz = new Date(localDate.toLocaleString('en-US', { timeZone: timezone }));
|
|
197
|
-
// Calculate the difference and adjust
|
|
198
|
-
const offset = localDate.getTime() - formattedInTz.getTime();
|
|
199
|
-
return new Date(localDate.getTime() + offset);
|
|
200
|
-
}
|
|
201
|
-
// If no timezone specified, use local time
|
|
202
|
-
const date = new Date();
|
|
203
|
-
date.setHours(time.hours ?? 0, time.minutes ?? 0, 0, 0);
|
|
204
|
-
return date;
|
|
205
|
-
};
|
|
206
|
-
const isInTimeRange = (time, range, timeZone) => {
|
|
207
|
-
const startTime = range.startTime
|
|
208
|
-
? convertTimeOfDayToDateTime(range.startTime, timeZone)
|
|
209
|
-
: time;
|
|
210
|
-
const endTime = range.endTime
|
|
211
|
-
? convertTimeOfDayToDateTime(range.endTime, timeZone)
|
|
212
|
-
: time;
|
|
213
|
-
return (time.getTime() >= startTime.getTime() && time.getTime() <= endTime.getTime());
|
|
214
|
-
};
|
|
215
172
|
const isFulfillmentAvailable = (fulfillment, operation) => {
|
|
216
173
|
const { availability: { timeZone, availableTimes } = {} } = fulfillment;
|
|
217
174
|
const { asapOptions: { maxInMinutes, rangeInMinutes } = {} } = operation;
|
|
218
175
|
const deliveryTime = fulfillment.deliveryOptions?.deliveryTimeInMinutes ?? 0;
|
|
219
|
-
const
|
|
220
|
-
const
|
|
176
|
+
const timezone = timeZone;
|
|
177
|
+
const locale = getLocale();
|
|
178
|
+
const timeNow = convertDateToTimezone(new Date(), timezone, locale);
|
|
221
179
|
timeNow.setMinutes(timeNow.getMinutes() +
|
|
222
180
|
(maxInMinutes ?? rangeInMinutes?.min ?? 0) +
|
|
223
181
|
deliveryTime);
|
|
224
|
-
|
|
225
|
-
const currentDayOfWeek = timeNow.getDay() === 0 ? 7 : timeNow.getDay();
|
|
182
|
+
const currentDayOfWeek = getDayOfWeekNumber(timeNow);
|
|
226
183
|
return !!availableTimes?.some((availableTime) => {
|
|
227
184
|
const { dayOfWeek, timeRanges } = availableTime;
|
|
228
185
|
const dayOfWeekNumber = DAY_OF_WEEK[dayOfWeek];
|
|
229
186
|
const isAvailable = dayOfWeekNumber === currentDayOfWeek &&
|
|
230
|
-
!!timeRanges?.some((range) => isInTimeRange(timeNow, range,
|
|
187
|
+
!!timeRanges?.some((range) => isInTimeRange(timeNow, range, timezone));
|
|
231
188
|
return isAvailable;
|
|
232
189
|
});
|
|
233
190
|
};
|
|
@@ -291,10 +248,9 @@ export const createTimeRange = (arr) => {
|
|
|
291
248
|
return { durationRangeOptions: { minDuration, maxDuration } };
|
|
292
249
|
}
|
|
293
250
|
};
|
|
294
|
-
export const processFulfillments =
|
|
251
|
+
export const processFulfillments = (fulfillments, timeSlots, operation) => {
|
|
295
252
|
const shouldConsiderAvailability = timeSlots.length > 0;
|
|
296
253
|
try {
|
|
297
|
-
console.log('fulfillments :>>', fulfillments);
|
|
298
254
|
const pickupFulfillment = fulfillments.find((fulfillment) => fulfillment.type === 'PICKUP' && fulfillment.enabled);
|
|
299
255
|
let deliveryFulfillments = fulfillments.filter((fulfillment) => fulfillment.type === 'DELIVERY' && fulfillment.enabled);
|
|
300
256
|
const isPickupConfigured = !!pickupFulfillment;
|
|
@@ -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
|
});
|
package/dist/react/Settings.d.ts
CHANGED
|
@@ -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;
|