@wix/headless-restaurants-olo 0.0.33 → 0.0.34
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/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.js +2 -13
- 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/olo-settings-service.d.ts +0 -1
- package/cjs/dist/services/olo-settings-service.js +1 -3
- 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/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.js +2 -13
- 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/olo-settings-service.d.ts +0 -1
- package/dist/services/olo-settings-service.js +1 -3
- 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
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { defineService, implementService } from '@wix/services-definitions';
|
|
2
|
+
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
3
|
+
import * as operationsSDK from '@wix/auto_sdk_restaurants_operations';
|
|
4
|
+
import { DispatchType, FulfillmentTypeEnum, } from '../types/fulfillments-types.js';
|
|
5
|
+
import { convertDateToTimezone, createTimeSlotId, getLocale, getTimezone, resolveFulfillmentInfo, } from '../utils/fulfillments-utils.js';
|
|
6
|
+
import { createDateFromYMD } from '../utils/date-utils.js';
|
|
7
|
+
import { FulfillmentsServiceDefinition } from './fulfillments-service.js';
|
|
8
|
+
// ========================================
|
|
9
|
+
// Service Definition
|
|
10
|
+
// ========================================
|
|
11
|
+
export const FulfillmentDetailsServiceDefinition = defineService('fulfillment-details');
|
|
12
|
+
// ========================================
|
|
13
|
+
// Service Implementation
|
|
14
|
+
// ========================================
|
|
15
|
+
export const FulfillmentDetailsService = implementService.withConfig()(FulfillmentDetailsServiceDefinition, ({ getService, config }) => {
|
|
16
|
+
if (!config.operation) {
|
|
17
|
+
throw new Error('Operation is required for FulfillmentDetailsService');
|
|
18
|
+
}
|
|
19
|
+
const fulfillmentsService = getService(FulfillmentsServiceDefinition);
|
|
20
|
+
const signalsService = getService(SignalsServiceDefinition);
|
|
21
|
+
const operation = config.operation;
|
|
22
|
+
const daysAhead = config.daysAhead ?? 30;
|
|
23
|
+
// ========================================
|
|
24
|
+
// Initialize Signals
|
|
25
|
+
// ========================================
|
|
26
|
+
const timeslot = signalsService.signal(null);
|
|
27
|
+
const address = signalsService.signal(config?.initialAddress ??
|
|
28
|
+
fulfillmentsService.selectedDispatchInfoSignal?.get()?.address ??
|
|
29
|
+
null);
|
|
30
|
+
const date = signalsService.signal(null);
|
|
31
|
+
const dispatchType = signalsService.signal(config.initialDispatchType ??
|
|
32
|
+
fulfillmentsService.selectedFulfillment.get()
|
|
33
|
+
?.type ??
|
|
34
|
+
null);
|
|
35
|
+
const asapTimeSlot = signalsService.signal(null);
|
|
36
|
+
const isLoading = signalsService.signal(false);
|
|
37
|
+
const error = signalsService.signal(null);
|
|
38
|
+
const availableDates = signalsService.signal([]);
|
|
39
|
+
const availableTimeSlotsForDate = signalsService.signal([]);
|
|
40
|
+
const schedulingType = signalsService.signal(fulfillmentsService.schedulingType?.get());
|
|
41
|
+
// ========================================
|
|
42
|
+
// Helper Functions
|
|
43
|
+
// ========================================
|
|
44
|
+
/**
|
|
45
|
+
* Process time slots from SDK response to our TimeSlot format
|
|
46
|
+
*/
|
|
47
|
+
const processTimeSlots = (timeSlotsResponse, targetDispatchType) => {
|
|
48
|
+
if (!timeSlotsResponse)
|
|
49
|
+
return [];
|
|
50
|
+
const slots = [];
|
|
51
|
+
for (const fulfillmentSlot of timeSlotsResponse) {
|
|
52
|
+
const { timeSlot, fulfilmentType, fulfillmentInfo } = fulfillmentSlot;
|
|
53
|
+
// Filter by dispatch type if specified
|
|
54
|
+
const slotDispatchType = fulfilmentType === 'DELIVERY'
|
|
55
|
+
? DispatchType.DELIVERY
|
|
56
|
+
: DispatchType.PICKUP;
|
|
57
|
+
if (targetDispatchType && slotDispatchType !== targetDispatchType) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const { startTime, endTime, orderSchedulingType } = timeSlot ?? {};
|
|
61
|
+
if (!startTime || !endTime)
|
|
62
|
+
continue;
|
|
63
|
+
const selectedFulfillmentInfo = resolveFulfillmentInfo(fulfillmentInfo ?? []);
|
|
64
|
+
const fulfillmentDetails = {
|
|
65
|
+
maxTimeOptions: selectedFulfillmentInfo?.maxTime,
|
|
66
|
+
durationRangeOptions: selectedFulfillmentInfo?.durationRange,
|
|
67
|
+
...selectedFulfillmentInfo,
|
|
68
|
+
};
|
|
69
|
+
const timezone = getTimezone();
|
|
70
|
+
const locale = getLocale();
|
|
71
|
+
slots.push({
|
|
72
|
+
id: createTimeSlotId(startTime, endTime),
|
|
73
|
+
startTime: convertDateToTimezone(startTime, timezone, locale),
|
|
74
|
+
endTime: convertDateToTimezone(endTime, timezone, locale),
|
|
75
|
+
dispatchType: slotDispatchType,
|
|
76
|
+
startsNow: orderSchedulingType === operationsSDK.OrderSchedulingType.ASAP,
|
|
77
|
+
fulfillmentDetails,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return slots;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Check if two dates are on the same day
|
|
84
|
+
*/
|
|
85
|
+
const isSameDay = (date1, date2) => {
|
|
86
|
+
return (date1.getFullYear() === date2.getFullYear() &&
|
|
87
|
+
date1.getMonth() === date2.getMonth() &&
|
|
88
|
+
date1.getDate() === date2.getDate());
|
|
89
|
+
};
|
|
90
|
+
// ========================================
|
|
91
|
+
// Action Implementations
|
|
92
|
+
// ========================================
|
|
93
|
+
const getTimeslot = () => {
|
|
94
|
+
return timeslot.get();
|
|
95
|
+
};
|
|
96
|
+
const setTimeslot = (slot) => {
|
|
97
|
+
timeslot.set(slot);
|
|
98
|
+
// Update date if timeslot has a start time
|
|
99
|
+
if (slot?.startTime) {
|
|
100
|
+
date.set(slot.startTime);
|
|
101
|
+
}
|
|
102
|
+
// Update dispatch type based on timeslot
|
|
103
|
+
if (slot?.dispatchType) {
|
|
104
|
+
dispatchType.set(slot.dispatchType);
|
|
105
|
+
}
|
|
106
|
+
// Update asap timeslot if this is an ASAP slot
|
|
107
|
+
if (slot?.startsNow) {
|
|
108
|
+
asapTimeSlot.set(slot);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const setAddress = (addr) => {
|
|
112
|
+
address.set(addr);
|
|
113
|
+
};
|
|
114
|
+
const setDate = async (selectedDate) => {
|
|
115
|
+
date.set(selectedDate);
|
|
116
|
+
// Fetch time slots for the selected date
|
|
117
|
+
if (selectedDate) {
|
|
118
|
+
await calculateAvailableTimeSlotsForDateFn(selectedDate);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
availableTimeSlotsForDate.set([]);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const setDispatchType = (type) => {
|
|
125
|
+
console.log('setDispatchType', type);
|
|
126
|
+
dispatchType.set(type);
|
|
127
|
+
// Re-filter available time slots based on new dispatch type
|
|
128
|
+
const currentDate = date.get();
|
|
129
|
+
if (currentDate) {
|
|
130
|
+
calculateAvailableTimeSlotsForDateFn(currentDate);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const setAsapTimeSlot = (slot) => {
|
|
134
|
+
asapTimeSlot.set(slot);
|
|
135
|
+
if (slot) {
|
|
136
|
+
timeslot.set(slot);
|
|
137
|
+
if (slot.startTime) {
|
|
138
|
+
date.set(slot.startTime);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const setSchedulingType = (type) => {
|
|
143
|
+
schedulingType.set(type);
|
|
144
|
+
// Find appropriate time slot based on type
|
|
145
|
+
const slots = availableTimeSlotsForDate.get();
|
|
146
|
+
const targetSlot = slots.find((slot) => {
|
|
147
|
+
if (type === FulfillmentTypeEnum.ASAP) {
|
|
148
|
+
return slot.startsNow;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
return !slot.startsNow;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (targetSlot) {
|
|
155
|
+
setTimeslot(targetSlot);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
// ========================================
|
|
159
|
+
// Fetch Method Implementations
|
|
160
|
+
// ========================================
|
|
161
|
+
const calculateAvailableDatesInRangeFn = async (startDate, endDate) => {
|
|
162
|
+
isLoading.set(true);
|
|
163
|
+
error.set(null);
|
|
164
|
+
try {
|
|
165
|
+
// Call the SDK to get available dates in the range
|
|
166
|
+
const response = await operationsSDK.calculateAvailableDatesInRange(operation.id, {
|
|
167
|
+
from: {
|
|
168
|
+
year: startDate.getFullYear(),
|
|
169
|
+
month: startDate.getMonth() + 1, // SDK uses 1-based months
|
|
170
|
+
day: startDate.getDate(),
|
|
171
|
+
},
|
|
172
|
+
until: {
|
|
173
|
+
year: endDate.getFullYear(),
|
|
174
|
+
month: endDate.getMonth() + 1,
|
|
175
|
+
day: endDate.getDate(),
|
|
176
|
+
},
|
|
177
|
+
deliveryAddress: address.get() ?? undefined,
|
|
178
|
+
});
|
|
179
|
+
const { availableDatesPerFulfillmentType } = response;
|
|
180
|
+
// Process the response to extract available dates
|
|
181
|
+
const datesMap = new Map();
|
|
182
|
+
const currentDispatchType = dispatchType.get();
|
|
183
|
+
for (const fulfillmentDates of availableDatesPerFulfillmentType ??
|
|
184
|
+
[]) {
|
|
185
|
+
const { fulfilmentType, dates } = fulfillmentDates;
|
|
186
|
+
// Filter by dispatch type if set
|
|
187
|
+
if (currentDispatchType) {
|
|
188
|
+
const slotType = fulfilmentType === 'DELIVERY'
|
|
189
|
+
? DispatchType.DELIVERY
|
|
190
|
+
: DispatchType.PICKUP;
|
|
191
|
+
if (slotType !== currentDispatchType)
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (dates) {
|
|
195
|
+
for (const sdkDate of dates) {
|
|
196
|
+
if (!sdkDate.year || !sdkDate.month || !sdkDate.day)
|
|
197
|
+
continue;
|
|
198
|
+
const dateObj = createDateFromYMD(sdkDate.year, sdkDate.month, sdkDate.day);
|
|
199
|
+
const dateKey = dateObj.toISOString().split('T')[0];
|
|
200
|
+
if (!datesMap.has(dateKey)) {
|
|
201
|
+
datesMap.set(dateKey, {
|
|
202
|
+
date: dateObj,
|
|
203
|
+
hasAvailableSlots: true,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const dates = Array.from(datesMap.values()).sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
210
|
+
availableDates.set(dates);
|
|
211
|
+
return dates;
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
const errorMessage = err instanceof Error
|
|
215
|
+
? err.message
|
|
216
|
+
: 'Failed to calculate available dates';
|
|
217
|
+
error.set(errorMessage);
|
|
218
|
+
console.error('Error calculating available dates:', err);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
isLoading.set(false);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const calculateAvailableTimeSlotsForDateFn = async (targetDate) => {
|
|
226
|
+
isLoading.set(true);
|
|
227
|
+
error.set(null);
|
|
228
|
+
try {
|
|
229
|
+
// Call the SDK to get time slots for the specific date
|
|
230
|
+
const response = await operationsSDK.calculateAvailableTimeSlotsForDate(operation.id, {
|
|
231
|
+
date: {
|
|
232
|
+
year: targetDate.getFullYear(),
|
|
233
|
+
month: targetDate.getMonth() + 1,
|
|
234
|
+
day: targetDate.getDate(),
|
|
235
|
+
},
|
|
236
|
+
deliveryAddress: address.get() ?? undefined,
|
|
237
|
+
});
|
|
238
|
+
const { timeslotsPerFulfillmentType } = response;
|
|
239
|
+
const currentDispatchType = dispatchType.get();
|
|
240
|
+
const slots = processTimeSlots(timeslotsPerFulfillmentType, currentDispatchType).reverse();
|
|
241
|
+
availableTimeSlotsForDate.set(slots);
|
|
242
|
+
// Auto-select first available slot if none selected
|
|
243
|
+
if (slots.length > 0 && !timeslot.get()) {
|
|
244
|
+
const firstAsapSlot = slots.find((s) => s.startsNow);
|
|
245
|
+
setTimeslot(firstAsapSlot ?? slots[0]);
|
|
246
|
+
}
|
|
247
|
+
return slots;
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
const errorMessage = err instanceof Error
|
|
251
|
+
? err.message
|
|
252
|
+
: 'Failed to calculate time slots for date';
|
|
253
|
+
error.set(errorMessage);
|
|
254
|
+
console.error('Error calculating time slots for date:', err);
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
isLoading.set(false);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
// ========================================
|
|
262
|
+
// Initialize with default date range
|
|
263
|
+
// ========================================
|
|
264
|
+
// Auto-load available dates on service initialization
|
|
265
|
+
const initializeDates = async () => {
|
|
266
|
+
const startDate = new Date();
|
|
267
|
+
startDate.setHours(0, 0, 0, 0);
|
|
268
|
+
const endDate = new Date();
|
|
269
|
+
endDate.setDate(endDate.getDate() + daysAhead);
|
|
270
|
+
endDate.setHours(23, 59, 59, 999);
|
|
271
|
+
await calculateAvailableDatesInRangeFn(startDate, endDate);
|
|
272
|
+
// If we have available dates, auto-select today or first available
|
|
273
|
+
const dates = availableDates.get();
|
|
274
|
+
if (dates.length > 0) {
|
|
275
|
+
const today = new Date();
|
|
276
|
+
const todayAvailable = dates.find((d) => isSameDay(d.date, today) && d.hasAvailableSlots);
|
|
277
|
+
const firstAvailable = todayAvailable ?? dates.find((d) => d.hasAvailableSlots);
|
|
278
|
+
if (firstAvailable) {
|
|
279
|
+
await setDate(firstAvailable.date);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
// Initialize asynchronously (don't block service creation)
|
|
284
|
+
initializeDates().catch((err) => {
|
|
285
|
+
console.error('Error initializing fulfillment details:', err);
|
|
286
|
+
error.set('Failed to initialize fulfillment details');
|
|
287
|
+
});
|
|
288
|
+
// ========================================
|
|
289
|
+
// Computed Signals
|
|
290
|
+
// ========================================
|
|
291
|
+
/**
|
|
292
|
+
* Available time slots filtered by current dispatch type
|
|
293
|
+
*/
|
|
294
|
+
const availableTimeSlots = signalsService.computed(() => {
|
|
295
|
+
const currentDispatchType = dispatchType.get();
|
|
296
|
+
console.log('currentDispatchType', currentDispatchType);
|
|
297
|
+
const allTimeSlots = availableTimeSlotsForDate.get();
|
|
298
|
+
if (!currentDispatchType) {
|
|
299
|
+
return allTimeSlots;
|
|
300
|
+
}
|
|
301
|
+
return allTimeSlots.filter((slot) => slot.dispatchType === currentDispatchType);
|
|
302
|
+
});
|
|
303
|
+
// ========================================
|
|
304
|
+
// Return Service API
|
|
305
|
+
// ========================================
|
|
306
|
+
return {
|
|
307
|
+
// Signals
|
|
308
|
+
timeslot,
|
|
309
|
+
address,
|
|
310
|
+
date,
|
|
311
|
+
dispatchType,
|
|
312
|
+
asapTimeSlot,
|
|
313
|
+
isLoading,
|
|
314
|
+
error,
|
|
315
|
+
availableDates,
|
|
316
|
+
availableTimeSlotsForDate,
|
|
317
|
+
availableTimeSlots,
|
|
318
|
+
schedulingType,
|
|
319
|
+
// Actions
|
|
320
|
+
getTimeslot,
|
|
321
|
+
setTimeslot,
|
|
322
|
+
setAddress,
|
|
323
|
+
setDate,
|
|
324
|
+
setDispatchType,
|
|
325
|
+
setAsapTimeSlot,
|
|
326
|
+
setSchedulingType,
|
|
327
|
+
// Fetch methods
|
|
328
|
+
calculateAvailableDatesInRange: calculateAvailableDatesInRangeFn,
|
|
329
|
+
calculateAvailableTimeSlotsForDate: calculateAvailableTimeSlotsForDateFn,
|
|
330
|
+
};
|
|
331
|
+
});
|
|
332
|
+
// ========================================
|
|
333
|
+
// Config Loader
|
|
334
|
+
// ========================================
|
|
335
|
+
/**
|
|
336
|
+
* Load configuration for the FulfillmentDetailsService
|
|
337
|
+
* Used for SSR initialization
|
|
338
|
+
*
|
|
339
|
+
* @param operation - The operation to configure the service with
|
|
340
|
+
* @param options - Additional configuration options
|
|
341
|
+
* @returns The service configuration
|
|
342
|
+
*/
|
|
343
|
+
export function loadFulfillmentDetailsServiceConfig(operation, options) {
|
|
344
|
+
console.log('options', options);
|
|
345
|
+
return {
|
|
346
|
+
operation,
|
|
347
|
+
initialDispatchType: options?.initialDispatchType,
|
|
348
|
+
initialAddress: options?.initialAddress,
|
|
349
|
+
daysAhead: options?.daysAhead ?? 30,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
@@ -2,8 +2,8 @@ import { defineService, implementService } from '@wix/services-definitions';
|
|
|
2
2
|
import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
|
|
3
3
|
import * as operationsSDK from '@wix/auto_sdk_restaurants_operations';
|
|
4
4
|
import * as fulfillemtMethodsSDK from '@wix/auto_sdk_restaurants_fulfillment-methods';
|
|
5
|
-
import { processFulfillments, processFulfillmentTimeSlotByOperationList, } from '../utils/fulfillments-utils.js';
|
|
6
|
-
import { DispatchType, } from '../types/fulfillments-types.js';
|
|
5
|
+
import { convertDateToTimezone, getLocale, getTimezone, processFulfillments, processFulfillmentTimeSlotByOperationList, } from '../utils/fulfillments-utils.js';
|
|
6
|
+
import { DispatchType, FulfillmentTypeEnum, } from '../types/fulfillments-types.js';
|
|
7
7
|
export const FulfillmentsServiceDefinition = defineService('fulfillments');
|
|
8
8
|
export const FulfillmentsService = implementService.withConfig()(FulfillmentsServiceDefinition, ({ getService, config }) => {
|
|
9
9
|
if (!config.operation) {
|
|
@@ -12,42 +12,80 @@ export const FulfillmentsService = implementService.withConfig()(FulfillmentsSer
|
|
|
12
12
|
const signalsService = getService(SignalsServiceDefinition);
|
|
13
13
|
const firstAvailableTimeSlotsByOperationList = new Map(config.firstAvailableTimeSlots?.map(processFulfillmentTimeSlotByOperationList) ?? []);
|
|
14
14
|
const fulfillments = signalsService.signal(config.fulfillments ?? []);
|
|
15
|
-
const firstAvailableTimeSlots = signalsService.signal(firstAvailableTimeSlotsByOperationList.get(
|
|
16
|
-
// @ts-expect-error - operation is not typed
|
|
17
|
-
config.operation?.id ?? '') ?? []);
|
|
15
|
+
const firstAvailableTimeSlots = signalsService.signal(firstAvailableTimeSlotsByOperationList.get(config.operation?.id ?? '') ?? []);
|
|
18
16
|
const isLoading = signalsService.signal(false);
|
|
19
17
|
const error = signalsService.signal(null);
|
|
20
18
|
const dispatchesInfoSignal = signalsService.signal({});
|
|
21
19
|
const selectedDispatchInfoSignal = signalsService.signal(null);
|
|
22
20
|
const asapTimeSignal = signalsService.signal(undefined);
|
|
23
21
|
const asapTimeRangeSignal = signalsService.signal(undefined);
|
|
24
|
-
|
|
25
|
-
const { dispatchesInfo } = result ?? {};
|
|
26
|
-
dispatchesInfoSignal.set(dispatchesInfo ?? {});
|
|
27
|
-
const selectedDispatchInfo = dispatchesInfoSignal.get()[selectedFulfillment.get()?.type];
|
|
28
|
-
selectedDispatchInfoSignal.set(selectedDispatchInfo ?? null);
|
|
29
|
-
const { maxTimeOptions, durationRangeOptions } = selectedDispatchInfo ?? {};
|
|
30
|
-
// const isDelivery = !!address;
|
|
31
|
-
const isAsap = !!selectedTimeSlot.get()?.startsNow;
|
|
32
|
-
// if (isDelivery && isAsap) {
|
|
33
|
-
const { fulfillmentDetails } = selectedTimeSlot.get() ?? {};
|
|
34
|
-
if (isAsap) {
|
|
35
|
-
asapTimeSignal.set(fulfillmentDetails?.maxTimeOptions ?? maxTimeOptions);
|
|
36
|
-
asapTimeRangeSignal.set(fulfillmentDetails?.durationRangeOptions ?? durationRangeOptions);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
22
|
+
const selectedFulfillment = signalsService.signal(null);
|
|
39
23
|
const pickupTimeSlot = firstAvailableTimeSlots
|
|
40
24
|
.get()
|
|
41
25
|
?.find((t) => t.dispatchType === DispatchType.PICKUP) ?? null;
|
|
42
26
|
const initialSelected = pickupTimeSlot ?? firstAvailableTimeSlots.get()?.[0] ?? null;
|
|
43
27
|
const selectedTimeSlot = signalsService.signal(initialSelected);
|
|
44
|
-
const
|
|
28
|
+
const { dispatchesInfo } = processFulfillments(fulfillments.get() ?? [], firstAvailableTimeSlots.get() ?? [], config.operation ?? undefined) ?? {};
|
|
29
|
+
// Initialize selected fulfillment for initial time slot
|
|
30
|
+
if (initialSelected && fulfillments.get()?.length > 0) {
|
|
31
|
+
const timezone = getTimezone();
|
|
32
|
+
const locale = getLocale();
|
|
33
|
+
initialSelected.startTime = convertDateToTimezone(initialSelected.startTime, timezone, locale);
|
|
34
|
+
initialSelected.endTime = convertDateToTimezone(initialSelected.endTime, timezone, locale);
|
|
35
|
+
const fulfillmentList = fulfillments.get();
|
|
36
|
+
const matchingFulfillment = fulfillmentList.find((f) => f.type === initialSelected.dispatchType) ?? null;
|
|
37
|
+
selectedFulfillment.set(matchingFulfillment);
|
|
38
|
+
}
|
|
39
|
+
dispatchesInfoSignal.set(dispatchesInfo ?? {});
|
|
40
|
+
const selectedDispatchInfo = dispatchesInfoSignal.get()[selectedFulfillment.get()?.type];
|
|
41
|
+
selectedDispatchInfoSignal.set(selectedDispatchInfo ?? null);
|
|
42
|
+
const { maxTimeOptions, durationRangeOptions } = selectedDispatchInfo ?? {};
|
|
43
|
+
// const isDelivery = !!address;
|
|
44
|
+
const isAsap = !!selectedTimeSlot.get()?.startsNow;
|
|
45
|
+
// if (isDelivery && isAsap) {
|
|
46
|
+
const { fulfillmentDetails } = selectedTimeSlot.get() ?? {};
|
|
47
|
+
if (isAsap) {
|
|
48
|
+
asapTimeSignal.set(fulfillmentDetails?.maxTimeOptions ?? maxTimeOptions);
|
|
49
|
+
asapTimeRangeSignal.set(fulfillmentDetails?.durationRangeOptions ?? durationRangeOptions);
|
|
50
|
+
}
|
|
51
|
+
// });
|
|
45
52
|
const availableTypes = signalsService.signal([]);
|
|
53
|
+
const getSchedulingType = (timeSlot) => {
|
|
54
|
+
if (!timeSlot)
|
|
55
|
+
return undefined;
|
|
56
|
+
if (config.operation?.operationType === operationsSDK.SchedulingType.ASAP) {
|
|
57
|
+
return timeSlot.startsNow
|
|
58
|
+
? FulfillmentTypeEnum.ASAP
|
|
59
|
+
: FulfillmentTypeEnum.ASAP_AND_FUTURE;
|
|
60
|
+
}
|
|
61
|
+
return FulfillmentTypeEnum.PREORDER;
|
|
62
|
+
};
|
|
63
|
+
const schedulingType = signalsService.signal(getSchedulingType(initialSelected));
|
|
64
|
+
// Compute initial fulfillment type options based on time slots
|
|
65
|
+
const computeFulfillmentTypeOptions = (timeSlots) => {
|
|
66
|
+
const hasAsap = timeSlots
|
|
67
|
+
.filter((slot) => slot.dispatchType === selectedFulfillment.get()?.type)
|
|
68
|
+
.some((slot) => slot.startsNow);
|
|
69
|
+
const hasFuture = !!config.operation?.allowAsapFutureHandling;
|
|
70
|
+
return {
|
|
71
|
+
hasAsapOption: hasAsap,
|
|
72
|
+
hasPreorderOption: !hasAsap && hasFuture,
|
|
73
|
+
hasAsapAndFutureOption: hasAsap && hasFuture,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const initialOptions = computeFulfillmentTypeOptions(firstAvailableTimeSlots.get() ?? []);
|
|
77
|
+
const hasAsapOption = signalsService.signal(initialOptions.hasAsapOption);
|
|
78
|
+
const hasPreorderOption = signalsService.signal(initialOptions.hasPreorderOption);
|
|
79
|
+
const hasAsapAndFutureOption = signalsService.signal(initialOptions.hasAsapAndFutureOption);
|
|
46
80
|
selectedTimeSlot.set(initialSelected);
|
|
47
81
|
const selectFulfillmentType = (type) => {
|
|
48
82
|
const timeSlot = firstAvailableTimeSlots.get()?.find((t) => t.dispatchType === type) ??
|
|
49
83
|
null;
|
|
50
84
|
if (timeSlot) {
|
|
85
|
+
const timezone = getTimezone();
|
|
86
|
+
const locale = getLocale();
|
|
87
|
+
timeSlot.startTime = convertDateToTimezone(timeSlot.startTime, timezone, locale);
|
|
88
|
+
timeSlot.endTime = convertDateToTimezone(timeSlot.endTime, timezone, locale);
|
|
51
89
|
setSelectedTimeSlot(timeSlot);
|
|
52
90
|
}
|
|
53
91
|
};
|
|
@@ -56,39 +94,21 @@ export const FulfillmentsService = implementService.withConfig()(FulfillmentsSer
|
|
|
56
94
|
* This action updates:
|
|
57
95
|
* - selectedTimeSlot signal
|
|
58
96
|
* - selectedFulfillment signal (based on dispatch type)
|
|
97
|
+
* - schedulingType signal (based on order scheduling type)
|
|
59
98
|
* - minOrderPrice signal (based on fulfillment method)
|
|
60
99
|
*/
|
|
61
100
|
const setSelectedTimeSlot = (timeSlot) => {
|
|
101
|
+
console.log('setSelectedTimeSlot', timeSlot);
|
|
62
102
|
// Update the selected time slot
|
|
63
103
|
selectedTimeSlot.set(timeSlot);
|
|
104
|
+
// Update scheduling type based on order scheduling configuration
|
|
105
|
+
schedulingType.set(getSchedulingType(timeSlot));
|
|
64
106
|
// Update selected fulfillment based on dispatch type
|
|
65
107
|
const matchingFulfillment = fulfillments.get().find((f) => f.type === timeSlot.dispatchType) ??
|
|
66
108
|
null;
|
|
67
109
|
selectedFulfillment.set(matchingFulfillment);
|
|
68
110
|
selectedDispatchInfoSignal.set(dispatchesInfoSignal.get()[matchingFulfillment?.type] ?? null);
|
|
69
111
|
};
|
|
70
|
-
// Initialize selected fulfillment for initial time slot
|
|
71
|
-
if (initialSelected && fulfillments.get()?.length > 0) {
|
|
72
|
-
const fulfillmentList = fulfillments.get();
|
|
73
|
-
const matchingFulfillment = fulfillmentList.find((f) => f.type === initialSelected.dispatchType) ?? null;
|
|
74
|
-
selectedFulfillment.set(matchingFulfillment);
|
|
75
|
-
}
|
|
76
|
-
// Load time slots and fulfillments if not provided
|
|
77
|
-
if (!config.firstAvailableTimeSlots && config.operation) {
|
|
78
|
-
loadFulfillmentsServiceConfig(config.operation).then((loadedConfig) => {
|
|
79
|
-
const timeSlotsMap = new Map(loadedConfig.firstAvailableTimeSlots?.map(processFulfillmentTimeSlotByOperationList) ?? []);
|
|
80
|
-
const newTimeSlots =
|
|
81
|
-
// @ts-expect-error - operation is not typed
|
|
82
|
-
timeSlotsMap.get(loadedConfig.operation?.id ?? '') ?? [];
|
|
83
|
-
firstAvailableTimeSlots.set(newTimeSlots);
|
|
84
|
-
fulfillments.set(loadedConfig.fulfillments ?? []);
|
|
85
|
-
// Set initial time slot using the action to ensure all related signals are updated
|
|
86
|
-
const newInitialSelected = newTimeSlots[0];
|
|
87
|
-
if (newInitialSelected) {
|
|
88
|
-
setSelectedTimeSlot(newInitialSelected);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
112
|
availableTypes.set(Array.from(new Set(firstAvailableTimeSlots.get()?.map((t) => t.dispatchType) ?? [])).sort((a, _) => (a === DispatchType.PICKUP ? -1 : 1)));
|
|
93
113
|
return {
|
|
94
114
|
firstAvailableTimeSlots,
|
|
@@ -100,6 +120,10 @@ export const FulfillmentsService = implementService.withConfig()(FulfillmentsSer
|
|
|
100
120
|
asapTimeRangeSignal,
|
|
101
121
|
selectedTimeSlot,
|
|
102
122
|
availableTypes,
|
|
123
|
+
schedulingType,
|
|
124
|
+
hasAsapOption,
|
|
125
|
+
hasPreorderOption,
|
|
126
|
+
hasAsapAndFutureOption,
|
|
103
127
|
isLoading,
|
|
104
128
|
error,
|
|
105
129
|
setSelectedTimeSlot,
|
package/dist/services/index.d.ts
CHANGED
|
@@ -2,3 +2,5 @@ export { ItemService, ItemServiceDefinition, loadItemServiceConfig, ItemServiceC
|
|
|
2
2
|
export { OLOSettingsService, OLOSettingsServiceDefinition, loadOLOSettingsServiceConfig, type OLOSettingsServiceConfig, type OLOSettingsServiceAPI, } from './olo-settings-service.js';
|
|
3
3
|
export { AvailabilityStatus, AvailabilityStatusMap, RuleType, RuleTypeMap, AddToCartButtonState, AddToCartButtonLabelMap, } from './common-types.js';
|
|
4
4
|
export { FulfillmentsService, FulfillmentsServiceDefinition, loadFulfillmentsServiceConfig, } from './fulfillments-service.js';
|
|
5
|
+
export { type FulfillmentsServiceConfig } from '../types/fulfillments-types.js';
|
|
6
|
+
export { FulfillmentDetailsService, FulfillmentDetailsServiceDefinition, loadFulfillmentDetailsServiceConfig, type FulfillmentDetailsServiceAPI, type FulfillmentDetailsServiceConfig, type AvailableDate, } from './fulfillment-details-service.js';
|
package/dist/services/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { ItemService, ItemServiceDefinition, loadItemServiceConfig, } from './it
|
|
|
2
2
|
export { OLOSettingsService, OLOSettingsServiceDefinition, loadOLOSettingsServiceConfig, } from './olo-settings-service.js';
|
|
3
3
|
export { AvailabilityStatus, RuleType, AddToCartButtonState, } from './common-types.js';
|
|
4
4
|
export { FulfillmentsService, FulfillmentsServiceDefinition, loadFulfillmentsServiceConfig, } from './fulfillments-service.js';
|
|
5
|
+
export { FulfillmentDetailsService, FulfillmentDetailsServiceDefinition, loadFulfillmentDetailsServiceConfig, } from './fulfillment-details-service.js';
|
|
@@ -4,7 +4,6 @@ import { MenusServiceConfig } from '@wix/restaurants/services';
|
|
|
4
4
|
export interface OLOSettingsServiceAPI {
|
|
5
5
|
operationGroup: Signal<operationGroupsSDK.OperationGroup | undefined>;
|
|
6
6
|
operation: Signal<operationsSDK.Operation | undefined>;
|
|
7
|
-
selectedItem?: Signal<unknown>;
|
|
8
7
|
isLoading: Signal<boolean>;
|
|
9
8
|
error: Signal<string | null>;
|
|
10
9
|
availabilityDispatchAction?: Signal<(() => void) | undefined>;
|
|
@@ -10,8 +10,7 @@ export const OLOSettingsService = implementService.withConfig()(OLOSettingsServi
|
|
|
10
10
|
const operation = signalsService.signal(config.operation);
|
|
11
11
|
const formatCurrency = config.formattedPrice ??
|
|
12
12
|
((price) => price?.toFixed(2)?.toString() ?? '');
|
|
13
|
-
const
|
|
14
|
-
const isLoading = signalsService.signal(false);
|
|
13
|
+
const isLoading = signalsService.signal(true);
|
|
15
14
|
const error = signalsService.signal(null);
|
|
16
15
|
const currentFulfillment = signalsService.signal('Pickup');
|
|
17
16
|
const currentTimeSlot = signalsService.signal('10-14');
|
|
@@ -24,7 +23,6 @@ export const OLOSettingsService = implementService.withConfig()(OLOSettingsServi
|
|
|
24
23
|
operation,
|
|
25
24
|
isLoading,
|
|
26
25
|
error,
|
|
27
|
-
selectedItem,
|
|
28
26
|
availabilityDispatchAction,
|
|
29
27
|
currentFulfillment,
|
|
30
28
|
currentTimeSlot,
|
|
@@ -2,12 +2,21 @@ import * as operationsSDK from '@wix/auto_sdk_restaurants_operations';
|
|
|
2
2
|
import { Signal } from '@wix/services-definitions/core-services/signals';
|
|
3
3
|
import * as fulfillemtMethodsSDK from '@wix/auto_sdk_restaurants_fulfillment-methods';
|
|
4
4
|
import { Address, DurationRange } from '@wix/auto_sdk_restaurants_operations';
|
|
5
|
+
import { Operation } from './operation.js';
|
|
5
6
|
export declare enum DispatchType {
|
|
6
7
|
/** Pickup fulfillment */
|
|
7
8
|
PICKUP = "PICKUP",
|
|
8
9
|
/** Delivery fulfillment */
|
|
9
10
|
DELIVERY = "DELIVERY"
|
|
10
11
|
}
|
|
12
|
+
export declare enum FulfillmentTypeEnum {
|
|
13
|
+
/** ASAP - Order will be fulfilled as soon as possible */
|
|
14
|
+
ASAP = "ASAP",
|
|
15
|
+
/** PREORDER - Order will be fulfilled at a scheduled time */
|
|
16
|
+
PREORDER = "PREORDER",
|
|
17
|
+
/** ASAP_AND_FUTURE - Both ASAP and future scheduling available */
|
|
18
|
+
ASAP_AND_FUTURE = "ASAP_AND_FUTURE"
|
|
19
|
+
}
|
|
11
20
|
export type TimeSlot = {
|
|
12
21
|
id: string;
|
|
13
22
|
startTime: Date;
|
|
@@ -52,6 +61,14 @@ export interface FulfillmentsServiceAPI {
|
|
|
52
61
|
error: Signal<string | null>;
|
|
53
62
|
/** Available dispatch types */
|
|
54
63
|
availableTypes: Signal<DispatchType[]>;
|
|
64
|
+
/** Current scheduling type (ASAP or PREORDER) based on selected time slot */
|
|
65
|
+
schedulingType: Signal<FulfillmentTypeEnum | undefined>;
|
|
66
|
+
/** Whether ASAP option is available */
|
|
67
|
+
hasAsapOption: Signal<boolean>;
|
|
68
|
+
/** Whether preorder (schedule for later) option is available */
|
|
69
|
+
hasPreorderOption: Signal<boolean>;
|
|
70
|
+
/** Whether both ASAP and future scheduling options are available */
|
|
71
|
+
hasAsapAndFutureOption: Signal<boolean>;
|
|
55
72
|
/**
|
|
56
73
|
* Dispatches info by dispatch type
|
|
57
74
|
*/
|
|
@@ -78,6 +95,6 @@ export interface FulfillmentsServiceAPI {
|
|
|
78
95
|
}
|
|
79
96
|
export interface FulfillmentsServiceConfig {
|
|
80
97
|
firstAvailableTimeSlots?: operationsSDK.TimeSlotForOperation[];
|
|
81
|
-
operation?:
|
|
98
|
+
operation?: Operation;
|
|
82
99
|
fulfillments?: fulfillemtMethodsSDK.FulfillmentMethod[];
|
|
83
100
|
}
|
|
@@ -5,3 +5,12 @@ export var DispatchType;
|
|
|
5
5
|
/** Delivery fulfillment */
|
|
6
6
|
DispatchType["DELIVERY"] = "DELIVERY";
|
|
7
7
|
})(DispatchType || (DispatchType = {}));
|
|
8
|
+
export var FulfillmentTypeEnum;
|
|
9
|
+
(function (FulfillmentTypeEnum) {
|
|
10
|
+
/** ASAP - Order will be fulfilled as soon as possible */
|
|
11
|
+
FulfillmentTypeEnum["ASAP"] = "ASAP";
|
|
12
|
+
/** PREORDER - Order will be fulfilled at a scheduled time */
|
|
13
|
+
FulfillmentTypeEnum["PREORDER"] = "PREORDER";
|
|
14
|
+
/** ASAP_AND_FUTURE - Both ASAP and future scheduling available */
|
|
15
|
+
FulfillmentTypeEnum["ASAP_AND_FUTURE"] = "ASAP_AND_FUTURE";
|
|
16
|
+
})(FulfillmentTypeEnum || (FulfillmentTypeEnum = {}));
|