@wix/headless-bookings 0.0.36 → 0.0.37

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.
@@ -3,7 +3,7 @@
3
3
  * Manages time slot fetching, selection, and state
4
4
  */
5
5
  import { implementService } from '@wix/services-definitions';
6
- import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
6
+ import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
7
7
  import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
8
8
  import { fetchAvailability } from '../../api/fetch-availability/index.js';
9
9
  import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
@@ -15,31 +15,6 @@ function getClientTimezone() {
15
15
  ? Intl.DateTimeFormat().resolvedOptions().timeZone
16
16
  : undefined;
17
17
  }
18
- /** Compare two string arrays for equality (order-independent) */
19
- function arraysEqual(a, b) {
20
- if (a.length !== b.length)
21
- return false;
22
- const sortedA = [...a].sort();
23
- const sortedB = [...b].sort();
24
- return sortedA.every((val, i) => val === sortedB[i]);
25
- }
26
- /** Create a memoized value that only updates when IDs change */
27
- function createMemoizedByIds({ fallback, getValue, extractId, }) {
28
- let cachedIds = [];
29
- let cachedValue = fallback;
30
- return () => {
31
- const value = getValue();
32
- const items = value ?? [];
33
- const newIds = items.length && typeof items[0] === 'string'
34
- ? items
35
- : items.map(extractId);
36
- if (!arraysEqual(newIds, cachedIds)) {
37
- cachedIds = newIds;
38
- cachedValue = items.length ? value : fallback;
39
- }
40
- return cachedValue;
41
- };
42
- }
43
18
  // ============================================================================
44
19
  // Service Implementation
45
20
  // ============================================================================
@@ -59,30 +34,25 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
59
34
  end: config.endDate,
60
35
  });
61
36
  const cursorSignal = signalsService.signal(undefined);
62
- const servicesSignal = signalsService.computed(createMemoizedByIds({
63
- fallback: config.services ?? [],
64
- getValue: () => bookingService.serviceSelections.get().map((s) => s.service),
65
- extractId: (service) => service._id,
66
- }));
67
- const preSelectedStaffSignal = signalsService.computed(createMemoizedByIds({
68
- fallback: config.filters?.staffMemberIds,
69
- getValue: () => (bookingService.serviceSelections.get()[0]?.resources ?? []).map((resource) => resource._id),
70
- }));
71
- const filtersSignal = signalsService.computed(() => {
37
+ // Helper functions using peek() - no subscriptions created
38
+ const getServices = () => {
39
+ const serviceSelections = bookingService.serviceSelections.peek();
40
+ return serviceSelections.length
41
+ ? serviceSelections.map((s) => s.service)
42
+ : (config.services ?? []);
43
+ };
44
+ const getFilters = () => {
72
45
  const baseFilters = config.filters ?? {};
73
- const selectedLocation = bookingService.location.get();
46
+ const selectedLocation = bookingService.location.peek();
74
47
  const location = selectedLocation
75
- ? {
76
- id: selectedLocation._id,
77
- locationType: selectedLocation.type,
78
- }
48
+ ? { id: selectedLocation._id, locationType: selectedLocation.type }
79
49
  : baseFilters.location;
80
- return {
81
- ...baseFilters,
82
- location,
83
- staffMemberIds: preSelectedStaffSignal.get(),
84
- };
85
- });
50
+ const selectedResources = bookingService.serviceSelections.peek()[0]?.resources ?? [];
51
+ const staffMemberIds = selectedResources.length
52
+ ? selectedResources.map((r) => r._id)
53
+ : baseFilters.staffMemberIds;
54
+ return { ...baseFilters, location, staffMemberIds };
55
+ };
86
56
  const fetchTimeSlots = async () => {
87
57
  isLoadingSignal.set(true);
88
58
  errorSignal.set(null);
@@ -90,12 +60,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
90
60
  timeSlotsSignal.set([]);
91
61
  try {
92
62
  const response = await fetchAvailability({
93
- services: servicesSignal.get(),
94
- timeZone: bookingService.timezone.get(),
95
- viewMode: viewModeSignal.get(),
96
- fromLocalDate: dateRangeSignal.get().start,
97
- toLocalDate: dateRangeSignal.get().end,
98
- ...filtersSignal.get(),
63
+ services: getServices(),
64
+ timeZone: bookingService.timezone.peek(),
65
+ viewMode: viewModeSignal.peek(),
66
+ fromLocalDate: dateRangeSignal.peek().start,
67
+ toLocalDate: dateRangeSignal.peek().end,
68
+ ...getFilters(),
99
69
  });
100
70
  timeSlotsSignal.set(response.timeSlots);
101
71
  const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
@@ -143,13 +113,13 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
143
113
  * Updates the booking service with the selected resource
144
114
  */
145
115
  const selectStaffMember = (staffMemberId) => {
146
- const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
116
+ const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
147
117
  if (!selectedTimeSlot)
148
118
  return;
149
119
  const getStaffName = (id) => {
150
120
  if (id === ANY_STAFF_MEMBER_ID)
151
121
  return 'Any staff member';
152
- const slots = timeSlotsSignal.get();
122
+ const slots = timeSlotsSignal.peek();
153
123
  return (slots
154
124
  .flatMap((slot) => slot.availableResources[0].resources)
155
125
  .find((r) => r._id === id)?.name ?? undefined);
@@ -171,7 +141,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
171
141
  * Action: Clear staff selection while keeping the selected time slot
172
142
  */
173
143
  const clearStaffSelection = () => {
174
- const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
144
+ const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
175
145
  if (!selectedTimeSlot)
176
146
  return;
177
147
  bookingService.actions.updateItem({
@@ -182,7 +152,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
182
152
  * Action: Load more slots (pagination)
183
153
  */
184
154
  const loadMore = async () => {
185
- const cursor = cursorSignal.get();
155
+ const cursor = cursorSignal.peek();
186
156
  if (!cursor) {
187
157
  return;
188
158
  }
@@ -190,16 +160,16 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
190
160
  errorSignal.set(null);
191
161
  try {
192
162
  const response = await fetchAvailability({
193
- services: servicesSignal.get(),
194
- fromLocalDate: dateRangeSignal.get().start,
195
- toLocalDate: dateRangeSignal.get().end,
196
- timeZone: bookingService.timezone.get(),
197
- viewMode: viewModeSignal.get(),
163
+ services: getServices(),
164
+ fromLocalDate: dateRangeSignal.peek().start,
165
+ toLocalDate: dateRangeSignal.peek().end,
166
+ timeZone: bookingService.timezone.peek(),
167
+ viewMode: viewModeSignal.peek(),
198
168
  cursor,
199
- ...filtersSignal.get(),
169
+ ...getFilters(),
200
170
  });
201
171
  const newSlots = response.timeSlots;
202
- timeSlotsSignal.set([...timeSlotsSignal.get(), ...newSlots]);
172
+ timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
203
173
  const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
204
174
  cursorSignal.set(nextCursor);
205
175
  hasMoreSignal.set(!!nextCursor);
@@ -218,7 +188,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
218
188
  * Action: Update date range (effect will handle refetch)
219
189
  */
220
190
  const setDateRange = (newStartDate, newEndDate) => {
221
- const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.get();
191
+ const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
222
192
  if (currentStartDate.getTime() === newStartDate.getTime() &&
223
193
  currentEndDate.getTime() === newEndDate.getTime()) {
224
194
  return;
@@ -227,9 +197,10 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
227
197
  };
228
198
  let isFirstRun = true;
229
199
  signalsService.effect(() => {
230
- filtersSignal.get();
200
+ // Dependencies - trigger refetch when these change
231
201
  dateRangeSignal.get();
232
202
  bookingService.timezone.get();
203
+ bookingService.location.get();
233
204
  if (isFirstRun) {
234
205
  isFirstRun = false;
235
206
  if (config.timeSlots) {
@@ -3,7 +3,7 @@
3
3
  * Manages time slot fetching, selection, and state
4
4
  */
5
5
  import { implementService } from '@wix/services-definitions';
6
- import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
6
+ import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
7
7
  import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
8
8
  import { fetchAvailability } from '../../api/fetch-availability/index.js';
9
9
  import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
@@ -15,31 +15,6 @@ function getClientTimezone() {
15
15
  ? Intl.DateTimeFormat().resolvedOptions().timeZone
16
16
  : undefined;
17
17
  }
18
- /** Compare two string arrays for equality (order-independent) */
19
- function arraysEqual(a, b) {
20
- if (a.length !== b.length)
21
- return false;
22
- const sortedA = [...a].sort();
23
- const sortedB = [...b].sort();
24
- return sortedA.every((val, i) => val === sortedB[i]);
25
- }
26
- /** Create a memoized value that only updates when IDs change */
27
- function createMemoizedByIds({ fallback, getValue, extractId, }) {
28
- let cachedIds = [];
29
- let cachedValue = fallback;
30
- return () => {
31
- const value = getValue();
32
- const items = value ?? [];
33
- const newIds = items.length && typeof items[0] === 'string'
34
- ? items
35
- : items.map(extractId);
36
- if (!arraysEqual(newIds, cachedIds)) {
37
- cachedIds = newIds;
38
- cachedValue = items.length ? value : fallback;
39
- }
40
- return cachedValue;
41
- };
42
- }
43
18
  // ============================================================================
44
19
  // Service Implementation
45
20
  // ============================================================================
@@ -59,30 +34,25 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
59
34
  end: config.endDate,
60
35
  });
61
36
  const cursorSignal = signalsService.signal(undefined);
62
- const servicesSignal = signalsService.computed(createMemoizedByIds({
63
- fallback: config.services ?? [],
64
- getValue: () => bookingService.serviceSelections.get().map((s) => s.service),
65
- extractId: (service) => service._id,
66
- }));
67
- const preSelectedStaffSignal = signalsService.computed(createMemoizedByIds({
68
- fallback: config.filters?.staffMemberIds,
69
- getValue: () => (bookingService.serviceSelections.get()[0]?.resources ?? []).map((resource) => resource._id),
70
- }));
71
- const filtersSignal = signalsService.computed(() => {
37
+ // Helper functions using peek() - no subscriptions created
38
+ const getServices = () => {
39
+ const serviceSelections = bookingService.serviceSelections.peek();
40
+ return serviceSelections.length
41
+ ? serviceSelections.map((s) => s.service)
42
+ : (config.services ?? []);
43
+ };
44
+ const getFilters = () => {
72
45
  const baseFilters = config.filters ?? {};
73
- const selectedLocation = bookingService.location.get();
46
+ const selectedLocation = bookingService.location.peek();
74
47
  const location = selectedLocation
75
- ? {
76
- id: selectedLocation._id,
77
- locationType: selectedLocation.type,
78
- }
48
+ ? { id: selectedLocation._id, locationType: selectedLocation.type }
79
49
  : baseFilters.location;
80
- return {
81
- ...baseFilters,
82
- location,
83
- staffMemberIds: preSelectedStaffSignal.get(),
84
- };
85
- });
50
+ const selectedResources = bookingService.serviceSelections.peek()[0]?.resources ?? [];
51
+ const staffMemberIds = selectedResources.length
52
+ ? selectedResources.map((r) => r._id)
53
+ : baseFilters.staffMemberIds;
54
+ return { ...baseFilters, location, staffMemberIds };
55
+ };
86
56
  const fetchTimeSlots = async () => {
87
57
  isLoadingSignal.set(true);
88
58
  errorSignal.set(null);
@@ -90,12 +60,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
90
60
  timeSlotsSignal.set([]);
91
61
  try {
92
62
  const response = await fetchAvailability({
93
- services: servicesSignal.get(),
94
- timeZone: bookingService.timezone.get(),
95
- viewMode: viewModeSignal.get(),
96
- fromLocalDate: dateRangeSignal.get().start,
97
- toLocalDate: dateRangeSignal.get().end,
98
- ...filtersSignal.get(),
63
+ services: getServices(),
64
+ timeZone: bookingService.timezone.peek(),
65
+ viewMode: viewModeSignal.peek(),
66
+ fromLocalDate: dateRangeSignal.peek().start,
67
+ toLocalDate: dateRangeSignal.peek().end,
68
+ ...getFilters(),
99
69
  });
100
70
  timeSlotsSignal.set(response.timeSlots);
101
71
  const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
@@ -143,13 +113,13 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
143
113
  * Updates the booking service with the selected resource
144
114
  */
145
115
  const selectStaffMember = (staffMemberId) => {
146
- const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
116
+ const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
147
117
  if (!selectedTimeSlot)
148
118
  return;
149
119
  const getStaffName = (id) => {
150
120
  if (id === ANY_STAFF_MEMBER_ID)
151
121
  return 'Any staff member';
152
- const slots = timeSlotsSignal.get();
122
+ const slots = timeSlotsSignal.peek();
153
123
  return (slots
154
124
  .flatMap((slot) => slot.availableResources[0].resources)
155
125
  .find((r) => r._id === id)?.name ?? undefined);
@@ -171,7 +141,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
171
141
  * Action: Clear staff selection while keeping the selected time slot
172
142
  */
173
143
  const clearStaffSelection = () => {
174
- const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
144
+ const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
175
145
  if (!selectedTimeSlot)
176
146
  return;
177
147
  bookingService.actions.updateItem({
@@ -182,7 +152,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
182
152
  * Action: Load more slots (pagination)
183
153
  */
184
154
  const loadMore = async () => {
185
- const cursor = cursorSignal.get();
155
+ const cursor = cursorSignal.peek();
186
156
  if (!cursor) {
187
157
  return;
188
158
  }
@@ -190,16 +160,16 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
190
160
  errorSignal.set(null);
191
161
  try {
192
162
  const response = await fetchAvailability({
193
- services: servicesSignal.get(),
194
- fromLocalDate: dateRangeSignal.get().start,
195
- toLocalDate: dateRangeSignal.get().end,
196
- timeZone: bookingService.timezone.get(),
197
- viewMode: viewModeSignal.get(),
163
+ services: getServices(),
164
+ fromLocalDate: dateRangeSignal.peek().start,
165
+ toLocalDate: dateRangeSignal.peek().end,
166
+ timeZone: bookingService.timezone.peek(),
167
+ viewMode: viewModeSignal.peek(),
198
168
  cursor,
199
- ...filtersSignal.get(),
169
+ ...getFilters(),
200
170
  });
201
171
  const newSlots = response.timeSlots;
202
- timeSlotsSignal.set([...timeSlotsSignal.get(), ...newSlots]);
172
+ timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
203
173
  const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
204
174
  cursorSignal.set(nextCursor);
205
175
  hasMoreSignal.set(!!nextCursor);
@@ -218,7 +188,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
218
188
  * Action: Update date range (effect will handle refetch)
219
189
  */
220
190
  const setDateRange = (newStartDate, newEndDate) => {
221
- const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.get();
191
+ const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
222
192
  if (currentStartDate.getTime() === newStartDate.getTime() &&
223
193
  currentEndDate.getTime() === newEndDate.getTime()) {
224
194
  return;
@@ -227,9 +197,10 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
227
197
  };
228
198
  let isFirstRun = true;
229
199
  signalsService.effect(() => {
230
- filtersSignal.get();
200
+ // Dependencies - trigger refetch when these change
231
201
  dateRangeSignal.get();
232
202
  bookingService.timezone.get();
203
+ bookingService.location.get();
233
204
  if (isFirstRun) {
234
205
  isFirstRun = false;
235
206
  if (config.timeSlots) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -72,5 +72,5 @@
72
72
  "groupId": "com.wixpress.headless-components"
73
73
  }
74
74
  },
75
- "falconPackageHash": "ecae9bfd11fe7e50bf6e5eeb5ad0b3e9763b64d5cfa1aa1477176483"
75
+ "falconPackageHash": "20f4812ace043e766a30c4a89597f6ca06981a999aff716744e5b9fc"
76
76
  }