@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.
Files changed (59) 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/ItemDetails.d.ts +4 -4
  4. package/cjs/dist/react/ItemDetails.js +5 -6
  5. package/cjs/dist/react/Settings.d.ts +8 -0
  6. package/cjs/dist/react/Settings.js +18 -25
  7. package/cjs/dist/react/core/FulfillmentDetails.d.ts +73 -7
  8. package/cjs/dist/react/core/FulfillmentDetails.js +133 -86
  9. package/cjs/dist/react/core/ItemDetails.d.ts +1 -1
  10. package/cjs/dist/react/core/ItemDetails.js +5 -16
  11. package/cjs/dist/react/core/Settings.d.ts +19 -4
  12. package/cjs/dist/react/core/Settings.js +29 -20
  13. package/cjs/dist/services/fulfillment-details-service.d.ts +127 -0
  14. package/cjs/dist/services/fulfillment-details-service.js +351 -0
  15. package/cjs/dist/services/fulfillments-service.js +67 -43
  16. package/cjs/dist/services/index.d.ts +2 -0
  17. package/cjs/dist/services/index.js +1 -0
  18. package/cjs/dist/services/item-details-service.d.ts +1 -1
  19. package/cjs/dist/services/item-details-service.js +10 -16
  20. package/cjs/dist/services/olo-settings-service.d.ts +0 -1
  21. package/cjs/dist/services/olo-settings-service.js +1 -3
  22. package/cjs/dist/services/utils.d.ts +4 -0
  23. package/cjs/dist/services/utils.js +12 -0
  24. package/cjs/dist/types/fulfillments-types.d.ts +18 -1
  25. package/cjs/dist/types/fulfillments-types.js +9 -0
  26. package/cjs/dist/utils/date-utils.d.ts +164 -0
  27. package/cjs/dist/utils/date-utils.js +297 -0
  28. package/cjs/dist/utils/fulfillments-utils.d.ts +3 -2
  29. package/cjs/dist/utils/fulfillments-utils.js +13 -57
  30. package/dist/react/FulfillmentDetails.d.ts +139 -15
  31. package/dist/react/FulfillmentDetails.js +128 -24
  32. package/dist/react/ItemDetails.d.ts +4 -4
  33. package/dist/react/ItemDetails.js +5 -6
  34. package/dist/react/Settings.d.ts +8 -0
  35. package/dist/react/Settings.js +18 -25
  36. package/dist/react/core/FulfillmentDetails.d.ts +73 -7
  37. package/dist/react/core/FulfillmentDetails.js +133 -86
  38. package/dist/react/core/ItemDetails.d.ts +1 -1
  39. package/dist/react/core/ItemDetails.js +5 -16
  40. package/dist/react/core/Settings.d.ts +19 -4
  41. package/dist/react/core/Settings.js +29 -20
  42. package/dist/services/fulfillment-details-service.d.ts +127 -0
  43. package/dist/services/fulfillment-details-service.js +351 -0
  44. package/dist/services/fulfillments-service.js +67 -43
  45. package/dist/services/index.d.ts +2 -0
  46. package/dist/services/index.js +1 -0
  47. package/dist/services/item-details-service.d.ts +1 -1
  48. package/dist/services/item-details-service.js +10 -16
  49. package/dist/services/olo-settings-service.d.ts +0 -1
  50. package/dist/services/olo-settings-service.js +1 -3
  51. package/dist/services/utils.d.ts +4 -0
  52. package/dist/services/utils.js +12 -0
  53. package/dist/types/fulfillments-types.d.ts +18 -1
  54. package/dist/types/fulfillments-types.js +9 -0
  55. package/dist/utils/date-utils.d.ts +164 -0
  56. package/dist/utils/date-utils.js +297 -0
  57. package/dist/utils/fulfillments-utils.d.ts +3 -2
  58. package/dist/utils/fulfillments-utils.js +13 -57
  59. package/package.json +3 -2
@@ -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, endTimeStr);
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('en-US', fullDateOptions)} - ${timeSlot?.endTime.toLocaleString('en-US', fullDateOptions)}`;
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 = new Date();
136
- const isToday = timeSlot?.startTime &&
137
- timeSlot.startTime.toDateString() === now.toDateString();
138
- const tomorrow = new Date(now);
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 hasDetails ? (_jsxs(AsChildSlot, { ref: ref, asChild: asChild,
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] })) : undefined;
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 && _jsx("div", { 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 && _jsx("div", { 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 { DispatchType } from '../../types/fulfillments-types.js';
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 { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
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
- return _jsx(_Fragment, { children: children });
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 fulfillmentsService = useService(FulfillmentsServiceDefinition);
19
- const selectedDispatchInfo = fulfillmentsService.selectedDispatchInfoSignal?.get();
20
- const selectedAddress = selectedDispatchInfo?.address ?? null;
21
- const selectedTimeSlot = fulfillmentsService.selectedTimeSlot?.get();
22
- const dispatchType = selectedTimeSlot?.dispatchType ?? null;
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 selectedTimeSlot = fulfillmentsService.selectedTimeSlot?.get();
53
- const [selectedDate, setSelectedDate] = useState(selectedTimeSlot?.startTime ?? null);
54
- const onDateChange = (date) => {
55
- setSelectedDate(date);
56
- // Find time slots for the selected date and update if needed
57
- const timeSlots = fulfillmentsService.firstAvailableTimeSlots?.get() ?? [];
58
- const slotsForDate = timeSlots.filter((slot) => {
59
- const slotDate = new Date(slot.startTime);
60
- return (slotDate.getFullYear() === date.getFullYear() &&
61
- slotDate.getMonth() === date.getMonth() &&
62
- slotDate.getDate() === date.getDate());
63
- });
64
- // If there are slots for this date, select the first one
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
- // Set min date to today
70
- const minDate = new Date();
71
- minDate.setHours(0, 0, 0, 0);
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 timeSlots = fulfillmentsService.firstAvailableTimeSlots?.get() ?? [];
90
- console.log('timeSlots', timeSlots);
91
- const selectedTimeSlot = fulfillmentsService.selectedTimeSlot?.get();
92
- const formatTimeRange = (startTime, endTime) => {
93
- const options = {
94
- hour: 'numeric',
95
- minute: 'numeric',
96
- hour12: true,
97
- };
98
- const start = startTime.toLocaleString('en-US', options);
99
- const end = endTime.toLocaleString('en-US', options);
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.startsNow
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
- fulfillmentsService.setSelectedTimeSlot(timeSlot);
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 timeSlots = fulfillmentsService.firstAvailableTimeSlots?.get() ?? [];
144
- const selectedTimeSlot = fulfillmentsService.selectedTimeSlot?.get();
145
- const hasAsapOption = timeSlots.some((slot) => slot.startsNow);
146
- const hasPreorderOption = timeSlots.some((slot) => !slot.startsNow);
147
- const currentType = selectedTimeSlot?.startsNow
148
- ? FulfillmentTypeEnum.ASAP
149
- : FulfillmentTypeEnum.PREORDER;
150
- const [selectedType, setSelectedType] = useState(currentType);
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
- console.log('onTypeChange', type);
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: false,
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, loadItemServiceConfig, } from '../../services/item-details-service.js';
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
- const service = useService(OLOSettingsServiceDefinition);
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
- lineItem,
44
+ onHandleAddToCart,
56
45
  buttonState,
57
46
  addToCartButtonDisabled,
58
47
  labelText,
59
48
  formattedPrice,
60
- modifierGroupHasError,
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
- * <CoreSettings.Root fulfillmentsServiceConfig={config}>
18
- * <CoreSettings.CurrentTimeSlot>
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
- * </CoreSettings.CurrentTimeSlot>
23
- * </CoreSettings.Root>
37
+ * </Settings.CurrentTimeSlot>
38
+ * </Settings.Root>
24
39
  * ```
25
40
  */
26
41
  export declare const Root: React.FC<RootProps>;