@wix/headless-restaurants-olo 0.0.32 → 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.
Files changed (45) hide show
  1. package/cjs/dist/react/FulfillmentDetails.d.ts +139 -15
  2. package/cjs/dist/react/FulfillmentDetails.js +128 -24
  3. package/cjs/dist/react/Settings.d.ts +8 -0
  4. package/cjs/dist/react/Settings.js +18 -25
  5. package/cjs/dist/react/core/FulfillmentDetails.d.ts +73 -7
  6. package/cjs/dist/react/core/FulfillmentDetails.js +133 -86
  7. package/cjs/dist/react/core/ItemDetails.js +2 -13
  8. package/cjs/dist/react/core/Settings.d.ts +19 -4
  9. package/cjs/dist/react/core/Settings.js +29 -20
  10. package/cjs/dist/services/fulfillment-details-service.d.ts +127 -0
  11. package/cjs/dist/services/fulfillment-details-service.js +351 -0
  12. package/cjs/dist/services/fulfillments-service.js +67 -43
  13. package/cjs/dist/services/index.d.ts +2 -0
  14. package/cjs/dist/services/index.js +1 -0
  15. package/cjs/dist/services/olo-settings-service.d.ts +0 -1
  16. package/cjs/dist/services/olo-settings-service.js +1 -3
  17. package/cjs/dist/types/fulfillments-types.d.ts +18 -1
  18. package/cjs/dist/types/fulfillments-types.js +9 -0
  19. package/cjs/dist/utils/date-utils.d.ts +164 -0
  20. package/cjs/dist/utils/date-utils.js +297 -0
  21. package/cjs/dist/utils/fulfillments-utils.d.ts +3 -2
  22. package/cjs/dist/utils/fulfillments-utils.js +13 -57
  23. package/dist/react/FulfillmentDetails.d.ts +139 -15
  24. package/dist/react/FulfillmentDetails.js +128 -24
  25. package/dist/react/Settings.d.ts +8 -0
  26. package/dist/react/Settings.js +18 -25
  27. package/dist/react/core/FulfillmentDetails.d.ts +73 -7
  28. package/dist/react/core/FulfillmentDetails.js +133 -86
  29. package/dist/react/core/ItemDetails.js +2 -13
  30. package/dist/react/core/Settings.d.ts +19 -4
  31. package/dist/react/core/Settings.js +29 -20
  32. package/dist/services/fulfillment-details-service.d.ts +127 -0
  33. package/dist/services/fulfillment-details-service.js +351 -0
  34. package/dist/services/fulfillments-service.js +67 -43
  35. package/dist/services/index.d.ts +2 -0
  36. package/dist/services/index.js +1 -0
  37. package/dist/services/olo-settings-service.d.ts +0 -1
  38. package/dist/services/olo-settings-service.js +1 -3
  39. package/dist/types/fulfillments-types.d.ts +18 -1
  40. package/dist/types/fulfillments-types.js +9 -0
  41. package/dist/utils/date-utils.d.ts +164 -0
  42. package/dist/utils/date-utils.js +297 -0
  43. package/dist/utils/fulfillments-utils.d.ts +3 -2
  44. package/dist/utils/fulfillments-utils.js +13 -57
  45. package/package.json +4 -3
@@ -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
- processFulfillments(fulfillments.get() ?? [], firstAvailableTimeSlots.get() ?? [], config.operation ?? undefined).then((result) => {
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 selectedFulfillment = signalsService.signal(null);
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,
@@ -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';
@@ -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 selectedItem = signalsService.signal(null);
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?: operationsSDK.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 = {}));