@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
package/dist/react/Settings.js
CHANGED
|
@@ -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
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { type FulfillmentDetailsServiceConfig } from '../../services/fulfillment-details-service.js';
|
|
3
|
+
import { DispatchType, FulfillmentTypeEnum } from '../../types/fulfillments-types.js';
|
|
3
4
|
export interface RootProps {
|
|
4
5
|
children: React.ReactNode;
|
|
6
|
+
fulfillmentDetailsServiceConfig?: FulfillmentDetailsServiceConfig;
|
|
5
7
|
}
|
|
6
8
|
/**
|
|
7
9
|
* Root component for FulfillmentDetails
|
|
8
10
|
* Provides context for all fulfillment detail sub-components
|
|
11
|
+
* Adds FulfillmentDetailsService to the services map after FulfillmentsService is loaded
|
|
9
12
|
*/
|
|
10
13
|
export declare const Root: React.FC<RootProps>;
|
|
11
14
|
export interface AddressNameProps {
|
|
@@ -56,18 +59,18 @@ export interface AvailableTimeSlotsProps {
|
|
|
56
59
|
* Shows time ranges when the order can be fulfilled
|
|
57
60
|
*/
|
|
58
61
|
export declare const AvailableTimeSlots: React.FC<AvailableTimeSlotsProps>;
|
|
59
|
-
export declare enum FulfillmentTypeEnum {
|
|
60
|
-
ASAP = "ASAP",
|
|
61
|
-
PREORDER = "PREORDER",
|
|
62
|
-
ASAP_AND_FUTURE = "ASAP_AND_FUTURE"
|
|
63
|
-
}
|
|
64
62
|
export interface FulfillmentTypeProps {
|
|
65
63
|
children: (props: {
|
|
66
|
-
selectedType: FulfillmentTypeEnum;
|
|
64
|
+
selectedType: FulfillmentTypeEnum | undefined;
|
|
67
65
|
onTypeChange: (type: FulfillmentTypeEnum) => void;
|
|
68
66
|
hasAsapOption: boolean;
|
|
69
67
|
hasPreorderOption: boolean;
|
|
70
68
|
hasAsapAndFutureOption: boolean;
|
|
69
|
+
asapTime: number | undefined;
|
|
70
|
+
asapTimeRange: {
|
|
71
|
+
minDuration?: number;
|
|
72
|
+
maxDuration?: number;
|
|
73
|
+
} | undefined;
|
|
71
74
|
}) => React.ReactNode;
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
@@ -76,3 +79,66 @@ export interface FulfillmentTypeProps {
|
|
|
76
79
|
* Pre-order: Order will be fulfilled at a scheduled time
|
|
77
80
|
*/
|
|
78
81
|
export declare const FulfillmentType: React.FC<FulfillmentTypeProps>;
|
|
82
|
+
/**
|
|
83
|
+
* Address fields that can be updated
|
|
84
|
+
*/
|
|
85
|
+
export interface DeliveryAddressFields {
|
|
86
|
+
/** Street address line 1 */
|
|
87
|
+
addressLine?: string;
|
|
88
|
+
/** Street address line 2 (apartment, suite, etc.) */
|
|
89
|
+
addressLine2?: string;
|
|
90
|
+
/** City */
|
|
91
|
+
city?: string;
|
|
92
|
+
/** State/Province/Subdivision */
|
|
93
|
+
subdivision?: string;
|
|
94
|
+
/** Postal/ZIP code */
|
|
95
|
+
postalCode?: string;
|
|
96
|
+
/** Country */
|
|
97
|
+
country?: string;
|
|
98
|
+
/** Formatted full address string */
|
|
99
|
+
formattedAddress?: string;
|
|
100
|
+
}
|
|
101
|
+
export interface DeliveryAddressProps {
|
|
102
|
+
children: (props: {
|
|
103
|
+
/** Current address fields */
|
|
104
|
+
address: DeliveryAddressFields | null;
|
|
105
|
+
/** Whether there is a current address */
|
|
106
|
+
hasAddress: boolean;
|
|
107
|
+
/** Current dispatch type */
|
|
108
|
+
dispatchType: DispatchType | null;
|
|
109
|
+
/** Whether delivery is selected */
|
|
110
|
+
isDelivery: boolean;
|
|
111
|
+
/** Update the delivery address */
|
|
112
|
+
onAddressChange: (address: DeliveryAddressFields) => void;
|
|
113
|
+
/** Clear the delivery address */
|
|
114
|
+
onAddressClear: () => void;
|
|
115
|
+
}) => React.ReactNode;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Component that provides delivery address editing functionality
|
|
119
|
+
* Only renders content when dispatch type is DELIVERY
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```tsx
|
|
123
|
+
* <CoreFulfillmentDetails.DeliveryAddress>
|
|
124
|
+
* {({ address, hasAddress, isDelivery, onAddressChange, onAddressClear }) => (
|
|
125
|
+
* isDelivery && (
|
|
126
|
+
* <div>
|
|
127
|
+
* <input
|
|
128
|
+
* value={address?.addressLine || ''}
|
|
129
|
+
* onChange={(e) => onAddressChange({ ...address, addressLine: e.target.value })}
|
|
130
|
+
* placeholder="Street Address"
|
|
131
|
+
* />
|
|
132
|
+
* <input
|
|
133
|
+
* value={address?.city || ''}
|
|
134
|
+
* onChange={(e) => onAddressChange({ ...address, city: e.target.value })}
|
|
135
|
+
* placeholder="City"
|
|
136
|
+
* />
|
|
137
|
+
* <button onClick={onAddressClear}>Clear</button>
|
|
138
|
+
* </div>
|
|
139
|
+
* )
|
|
140
|
+
* )}
|
|
141
|
+
* </CoreFulfillmentDetails.DeliveryAddress>
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export declare const DeliveryAddress: React.FC<DeliveryAddressProps>;
|
|
@@ -1,13 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { useService } from '@wix/services-manager-react';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { WixServices, useService } from '@wix/services-manager-react';
|
|
4
4
|
import { FulfillmentsServiceDefinition } from '../../services/fulfillments-service.js';
|
|
5
|
+
import { FulfillmentDetailsService, FulfillmentDetailsServiceDefinition, loadFulfillmentDetailsServiceConfig, } from '../../services/fulfillment-details-service.js';
|
|
6
|
+
import { OLOSettingsServiceDefinition } from '../../services/olo-settings-service.js';
|
|
7
|
+
import { DispatchType, FulfillmentTypeEnum, } from '../../types/fulfillments-types.js';
|
|
8
|
+
import { createServicesMap } from '@wix/services-manager';
|
|
9
|
+
import { formatTimeRange, getToday, getFutureDate, } from '../../utils/date-utils.js';
|
|
5
10
|
/**
|
|
6
11
|
* Root component for FulfillmentDetails
|
|
7
12
|
* Provides context for all fulfillment detail sub-components
|
|
13
|
+
* Adds FulfillmentDetailsService to the services map after FulfillmentsService is loaded
|
|
8
14
|
*/
|
|
9
|
-
export const Root = ({ children }) => {
|
|
10
|
-
|
|
15
|
+
export const Root = ({ children, fulfillmentDetailsServiceConfig, }) => {
|
|
16
|
+
const oloSettingsService = useService(OLOSettingsServiceDefinition);
|
|
17
|
+
const fulfillmentsService = useService(FulfillmentsServiceDefinition);
|
|
18
|
+
const [config, setConfig] = useState(fulfillmentDetailsServiceConfig);
|
|
19
|
+
const isFulfillmentsLoading = fulfillmentsService.isLoading?.get() ?? true;
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!fulfillmentDetailsServiceConfig &&
|
|
22
|
+
oloSettingsService.operation?.get()) {
|
|
23
|
+
const operation = oloSettingsService.operation.get();
|
|
24
|
+
if (operation) {
|
|
25
|
+
const loadedConfig = loadFulfillmentDetailsServiceConfig(operation);
|
|
26
|
+
setConfig(loadedConfig);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}, [fulfillmentDetailsServiceConfig, oloSettingsService.operation]);
|
|
30
|
+
if (!config || isFulfillmentsLoading) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(FulfillmentDetailsServiceDefinition, FulfillmentDetailsService, config), children: children }));
|
|
11
34
|
};
|
|
12
35
|
/**
|
|
13
36
|
* Component that provides the address name for the selected fulfillment
|
|
@@ -15,12 +38,11 @@ export const Root = ({ children }) => {
|
|
|
15
38
|
* For delivery: shows the delivery address
|
|
16
39
|
*/
|
|
17
40
|
export const AddressName = ({ children }) => {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const addressName = selectedAddress?.formatted ?? null;
|
|
41
|
+
const fulfillmentDetailsService = useService(FulfillmentDetailsServiceDefinition);
|
|
42
|
+
const selectedAddress = fulfillmentDetailsService?.address?.get();
|
|
43
|
+
console.log('selectedAddress', selectedAddress);
|
|
44
|
+
const dispatchType = fulfillmentDetailsService.dispatchType?.get();
|
|
45
|
+
const addressName = selectedAddress?.formatted;
|
|
24
46
|
const fullAddress = selectedAddress
|
|
25
47
|
? [
|
|
26
48
|
// @ts-expect-error - selectedAddress is not typed
|
|
@@ -49,30 +71,23 @@ export const AddressName = ({ children }) => {
|
|
|
49
71
|
*/
|
|
50
72
|
export const FulfillmentDate = ({ children, }) => {
|
|
51
73
|
const fulfillmentsService = useService(FulfillmentsServiceDefinition);
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (slotsForDate.length > 0) {
|
|
66
|
-
fulfillmentsService.setSelectedTimeSlot(slotsForDate[0]);
|
|
67
|
-
}
|
|
74
|
+
const hasAsapOption = fulfillmentsService.hasAsapOption?.get();
|
|
75
|
+
const hasAsapAndFutureOption = fulfillmentsService.hasAsapAndFutureOption?.get();
|
|
76
|
+
if (hasAsapOption && !hasAsapAndFutureOption) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const fulfillmentDetailsService = useService(FulfillmentDetailsServiceDefinition);
|
|
80
|
+
if (fulfillmentDetailsService?.schedulingType.get() === FulfillmentTypeEnum.ASAP) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const selectedDate = fulfillmentDetailsService.date?.get();
|
|
84
|
+
const availableDates = fulfillmentDetailsService.availableDates?.get() ?? [];
|
|
85
|
+
const onDateChange = async (date) => {
|
|
86
|
+
await fulfillmentDetailsService.setDate(date);
|
|
68
87
|
};
|
|
69
|
-
//
|
|
70
|
-
const minDate =
|
|
71
|
-
|
|
72
|
-
// Set max date to 30 days from now (reasonable booking window)
|
|
73
|
-
const maxDate = new Date();
|
|
74
|
-
maxDate.setDate(maxDate.getDate() + 30);
|
|
75
|
-
maxDate.setHours(23, 59, 59, 999);
|
|
88
|
+
// Calculate min/max dates from available dates if available, otherwise use defaults
|
|
89
|
+
const minDate = availableDates[0]?.date ?? getToday();
|
|
90
|
+
const maxDate = availableDates[availableDates.length - 1]?.date ?? getFutureDate(30);
|
|
76
91
|
return children({
|
|
77
92
|
selectedDate,
|
|
78
93
|
onDateChange,
|
|
@@ -86,24 +101,20 @@ export const FulfillmentDate = ({ children, }) => {
|
|
|
86
101
|
*/
|
|
87
102
|
export const AvailableTimeSlots = ({ children, }) => {
|
|
88
103
|
const fulfillmentsService = useService(FulfillmentsServiceDefinition);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return `${start} - ${end}`;
|
|
101
|
-
};
|
|
104
|
+
const hasAsapOption = fulfillmentsService.hasAsapOption?.get();
|
|
105
|
+
const hasAsapAndFutureOption = fulfillmentsService.hasAsapAndFutureOption?.get();
|
|
106
|
+
if (hasAsapOption && !hasAsapAndFutureOption) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const fulfillmentDetailsService = useService(FulfillmentDetailsServiceDefinition);
|
|
110
|
+
if (fulfillmentDetailsService?.schedulingType.get() === FulfillmentTypeEnum.ASAP) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const timeSlots = fulfillmentDetailsService.availableTimeSlots?.get() ?? [];
|
|
114
|
+
const selectedTimeSlot = fulfillmentDetailsService.timeslot?.get();
|
|
102
115
|
const timeSlotOptions = timeSlots.map((slot) => ({
|
|
103
116
|
id: slot.id,
|
|
104
|
-
label: slot.
|
|
105
|
-
? 'ASAP'
|
|
106
|
-
: formatTimeRange(slot.startTime, slot.endTime),
|
|
117
|
+
label: formatTimeRange(slot.startTime, slot.endTime),
|
|
107
118
|
startTime: slot.startTime,
|
|
108
119
|
endTime: slot.endTime,
|
|
109
120
|
dispatchType: slot.dispatchType,
|
|
@@ -112,7 +123,7 @@ export const AvailableTimeSlots = ({ children, }) => {
|
|
|
112
123
|
const onTimeSlotChange = (timeSlotId) => {
|
|
113
124
|
const timeSlot = timeSlots.find((slot) => slot.id === timeSlotId);
|
|
114
125
|
if (timeSlot) {
|
|
115
|
-
|
|
126
|
+
fulfillmentDetailsService.setTimeslot(timeSlot);
|
|
116
127
|
}
|
|
117
128
|
};
|
|
118
129
|
const hasTimeSlots = timeSlotOptions.length > 0;
|
|
@@ -124,15 +135,6 @@ export const AvailableTimeSlots = ({ children, }) => {
|
|
|
124
135
|
hasTimeSlots,
|
|
125
136
|
});
|
|
126
137
|
};
|
|
127
|
-
// ========================================
|
|
128
|
-
// FULFILLMENT TYPE COMPONENT
|
|
129
|
-
// ========================================
|
|
130
|
-
export var FulfillmentTypeEnum;
|
|
131
|
-
(function (FulfillmentTypeEnum) {
|
|
132
|
-
FulfillmentTypeEnum["ASAP"] = "ASAP";
|
|
133
|
-
FulfillmentTypeEnum["PREORDER"] = "PREORDER";
|
|
134
|
-
FulfillmentTypeEnum["ASAP_AND_FUTURE"] = "ASAP_AND_FUTURE";
|
|
135
|
-
})(FulfillmentTypeEnum || (FulfillmentTypeEnum = {}));
|
|
136
138
|
/**
|
|
137
139
|
* Component that provides fulfillment type selection (ASAP or Pre-order)
|
|
138
140
|
* ASAP: Order will be fulfilled as soon as possible
|
|
@@ -140,38 +142,83 @@ export var FulfillmentTypeEnum;
|
|
|
140
142
|
*/
|
|
141
143
|
export const FulfillmentType = ({ children, }) => {
|
|
142
144
|
const fulfillmentsService = useService(FulfillmentsServiceDefinition);
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const hasAsapOption =
|
|
146
|
-
const hasPreorderOption =
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
145
|
+
const fulfillmentDetailsService = useService(FulfillmentDetailsServiceDefinition);
|
|
146
|
+
// Get fulfillment type options from service
|
|
147
|
+
const hasAsapOption = fulfillmentsService.hasAsapOption?.get();
|
|
148
|
+
const hasPreorderOption = fulfillmentsService.hasPreorderOption?.get();
|
|
149
|
+
const hasAsapAndFutureOption = fulfillmentsService.hasAsapAndFutureOption?.get();
|
|
150
|
+
const asapTime = fulfillmentsService.asapTimeSignal?.get();
|
|
151
|
+
const asapTimeRange = fulfillmentsService.asapTimeRangeSignal?.get();
|
|
152
|
+
const selectedType = fulfillmentDetailsService.schedulingType?.get();
|
|
151
153
|
const onTypeChange = (type) => {
|
|
152
|
-
|
|
153
|
-
setSelectedType(type);
|
|
154
|
-
// Find appropriate time slot based on type
|
|
155
|
-
const targetSlot = timeSlots.find((slot) => {
|
|
156
|
-
if (type === FulfillmentTypeEnum.ASAP) {
|
|
157
|
-
return slot.startsNow;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
return !slot.startsNow;
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
if (targetSlot) {
|
|
164
|
-
fulfillmentsService.setSelectedTimeSlot(targetSlot);
|
|
165
|
-
}
|
|
154
|
+
fulfillmentDetailsService.setSchedulingType(type);
|
|
166
155
|
};
|
|
167
|
-
console.log('selectedType', selectedType);
|
|
168
|
-
console.log('hasAsapOption', hasAsapOption);
|
|
169
|
-
console.log('hasPreorderOption', hasPreorderOption);
|
|
170
156
|
return children({
|
|
171
157
|
selectedType,
|
|
172
158
|
onTypeChange,
|
|
173
159
|
hasAsapOption,
|
|
174
160
|
hasPreorderOption,
|
|
175
|
-
hasAsapAndFutureOption
|
|
161
|
+
hasAsapAndFutureOption,
|
|
162
|
+
asapTime,
|
|
163
|
+
asapTimeRange,
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Component that provides delivery address editing functionality
|
|
168
|
+
* Only renders content when dispatch type is DELIVERY
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```tsx
|
|
172
|
+
* <CoreFulfillmentDetails.DeliveryAddress>
|
|
173
|
+
* {({ address, hasAddress, isDelivery, onAddressChange, onAddressClear }) => (
|
|
174
|
+
* isDelivery && (
|
|
175
|
+
* <div>
|
|
176
|
+
* <input
|
|
177
|
+
* value={address?.addressLine || ''}
|
|
178
|
+
* onChange={(e) => onAddressChange({ ...address, addressLine: e.target.value })}
|
|
179
|
+
* placeholder="Street Address"
|
|
180
|
+
* />
|
|
181
|
+
* <input
|
|
182
|
+
* value={address?.city || ''}
|
|
183
|
+
* onChange={(e) => onAddressChange({ ...address, city: e.target.value })}
|
|
184
|
+
* placeholder="City"
|
|
185
|
+
* />
|
|
186
|
+
* <button onClick={onAddressClear}>Clear</button>
|
|
187
|
+
* </div>
|
|
188
|
+
* )
|
|
189
|
+
* )}
|
|
190
|
+
* </CoreFulfillmentDetails.DeliveryAddress>
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export const DeliveryAddress = ({ children, }) => {
|
|
194
|
+
const fulfillmentDetailsService = useService(FulfillmentDetailsServiceDefinition);
|
|
195
|
+
const currentAddress = fulfillmentDetailsService?.address?.get();
|
|
196
|
+
const dispatchType = fulfillmentDetailsService.dispatchType?.get();
|
|
197
|
+
const isDelivery = dispatchType === DispatchType.DELIVERY;
|
|
198
|
+
const address = currentAddress
|
|
199
|
+
? {
|
|
200
|
+
addressLine: currentAddress.addressLine,
|
|
201
|
+
addressLine2: currentAddress.addressLine2,
|
|
202
|
+
city: currentAddress.city,
|
|
203
|
+
subdivision: currentAddress.subdivision,
|
|
204
|
+
postalCode: currentAddress.postalCode,
|
|
205
|
+
country: currentAddress.country,
|
|
206
|
+
formattedAddress: currentAddress.formattedAddress,
|
|
207
|
+
}
|
|
208
|
+
: null;
|
|
209
|
+
const hasAddress = Boolean(currentAddress);
|
|
210
|
+
const onAddressChange = (newAddress) => {
|
|
211
|
+
fulfillmentDetailsService.setAddress(newAddress);
|
|
212
|
+
};
|
|
213
|
+
const onAddressClear = () => {
|
|
214
|
+
fulfillmentDetailsService.setAddress(null);
|
|
215
|
+
};
|
|
216
|
+
return children({
|
|
217
|
+
address,
|
|
218
|
+
hasAddress,
|
|
219
|
+
dispatchType,
|
|
220
|
+
isDelivery,
|
|
221
|
+
onAddressChange,
|
|
222
|
+
onAddressClear,
|
|
176
223
|
});
|
|
177
224
|
};
|
|
@@ -21,12 +21,12 @@ interface ItemDetailsLineItemProps {
|
|
|
21
21
|
addToCartLabelMap: Record<AddToCartButtonState, string>;
|
|
22
22
|
children: (props: {
|
|
23
23
|
loadingState: string | React.ReactNode;
|
|
24
|
+
onHandleAddToCart: (onClick: (lineItem: LineItem) => void) => void;
|
|
24
25
|
lineItem: LineItem;
|
|
25
26
|
buttonState: AddToCartButtonState;
|
|
26
27
|
addToCartButtonDisabled: boolean;
|
|
27
28
|
labelText: string;
|
|
28
29
|
formattedPrice: string;
|
|
29
|
-
modifierGroupHasError: boolean;
|
|
30
30
|
}) => React.ReactNode;
|
|
31
31
|
}
|
|
32
32
|
export declare const LineItemComponent: React.FC<ItemDetailsLineItemProps>;
|
|
@@ -2,24 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { useService, WixServices } from '@wix/services-manager-react';
|
|
4
4
|
import { createServicesMap } from '@wix/services-manager';
|
|
5
|
-
import { ItemService, ItemServiceDefinition,
|
|
5
|
+
import { ItemService, ItemServiceDefinition, } from '../../services/item-details-service.js';
|
|
6
6
|
import { OLOSettingsServiceDefinition } from '../../services/olo-settings-service.js';
|
|
7
7
|
import { useItemContext } from '@wix/headless-restaurants-menus/react';
|
|
8
8
|
import { AvailabilityStatus, AddToCartButtonState, } from '../../services/common-types.js';
|
|
9
9
|
import { getAvailabilityText } from '../../services/utils.js';
|
|
10
10
|
export const Root = ({ children, itemDetailsServiceConfig, }) => {
|
|
11
|
-
|
|
12
|
-
const selectedItem = service.selectedItem?.get();
|
|
13
|
-
const config = itemDetailsServiceConfig ??
|
|
14
|
-
loadItemServiceConfig({
|
|
15
|
-
item: selectedItem,
|
|
16
|
-
// @ts-expect-error - operation is not typed
|
|
17
|
-
operationId: service.operation?.get()?.id ?? '',
|
|
18
|
-
});
|
|
19
|
-
if (config.item) {
|
|
20
|
-
service.selectedItem?.set(config.item);
|
|
21
|
-
}
|
|
22
|
-
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ItemServiceDefinition, ItemService, config), children: children({ item: itemDetailsServiceConfig?.item ?? selectedItem }) }));
|
|
11
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ItemServiceDefinition, ItemService, itemDetailsServiceConfig), children: children({ item: itemDetailsServiceConfig?.item }) }));
|
|
23
12
|
};
|
|
24
13
|
export const SpecialRequest = ({ children, }) => {
|
|
25
14
|
const service = useService(ItemServiceDefinition);
|
|
@@ -42,6 +31,7 @@ export const LineItemComponent = ({ addToCartLabelMap, children, }) => {
|
|
|
42
31
|
const service = useService(ItemServiceDefinition);
|
|
43
32
|
const oloSettingsService = useService(OLOSettingsServiceDefinition);
|
|
44
33
|
const lineItem = service.lineItem?.get?.() ?? {};
|
|
34
|
+
const onHandleAddToCart = service.onHandleAddToCart;
|
|
45
35
|
const loadingState = service.isLoading?.get?.() ?? false;
|
|
46
36
|
const buttonState = service.buttonState?.get?.() ?? AddToCartButtonState.VALID_TO_CONTINUE;
|
|
47
37
|
const addToCartButtonDisabled = service.addToCartButtonDisabled?.get?.() ?? false;
|
|
@@ -49,15 +39,14 @@ export const LineItemComponent = ({ addToCartLabelMap, children, }) => {
|
|
|
49
39
|
const formatCurrency = oloSettingsService.formatCurrency;
|
|
50
40
|
const formattedPrice = formatCurrency(price);
|
|
51
41
|
const labelText = addToCartLabelMap[buttonState];
|
|
52
|
-
const modifierGroupHasError = service.doesModifierGroupHaveError?.get?.() ?? false;
|
|
53
42
|
return children({
|
|
54
43
|
loadingState,
|
|
55
|
-
|
|
44
|
+
onHandleAddToCart,
|
|
56
45
|
buttonState,
|
|
57
46
|
addToCartButtonDisabled,
|
|
58
47
|
labelText,
|
|
59
48
|
formattedPrice,
|
|
60
|
-
|
|
49
|
+
lineItem,
|
|
61
50
|
});
|
|
62
51
|
};
|
|
63
52
|
export const QuantityComponent = ({ children, }) => {
|
|
@@ -12,15 +12,30 @@ export interface RootProps {
|
|
|
12
12
|
* Core Settings Root component that provides service context
|
|
13
13
|
* Wraps children with both OLOSettingsService and FulfillmentsService providers
|
|
14
14
|
*
|
|
15
|
+
* Note: Use loadFulfillmentsServiceConfig from '@wix/headless-restaurants-olo/services'
|
|
16
|
+
* to load the config before rendering this component.
|
|
17
|
+
*
|
|
15
18
|
* @example
|
|
16
19
|
* ```tsx
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* import { loadFulfillmentsServiceConfig, OLOSettingsServiceDefinition } from '@wix/headless-restaurants-olo/services';
|
|
21
|
+
*
|
|
22
|
+
* // In your component:
|
|
23
|
+
* const service = useService(OLOSettingsServiceDefinition);
|
|
24
|
+
* const [config, setConfig] = useState(null);
|
|
25
|
+
*
|
|
26
|
+
* useEffect(() => {
|
|
27
|
+
* loadFulfillmentsServiceConfig(service.operation?.get()).then(setConfig);
|
|
28
|
+
* }, []);
|
|
29
|
+
*
|
|
30
|
+
* if (!config) return <div>Loading...</div>;
|
|
31
|
+
*
|
|
32
|
+
* <Settings.Root fulfillmentsServiceConfig={config}>
|
|
33
|
+
* <Settings.CurrentTimeSlot>
|
|
19
34
|
* {({ timeSlot, hasDetails }) => (
|
|
20
35
|
* <div>{timeSlot?.dispatchType}</div>
|
|
21
36
|
* )}
|
|
22
|
-
* </
|
|
23
|
-
* </
|
|
37
|
+
* </Settings.CurrentTimeSlot>
|
|
38
|
+
* </Settings.Root>
|
|
24
39
|
* ```
|
|
25
40
|
*/
|
|
26
41
|
export declare const Root: React.FC<RootProps>;
|