@wix/headless-bookings 0.0.10 → 0.0.12

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 (69) hide show
  1. package/cjs/dist/api/index.d.ts +10 -1
  2. package/cjs/dist/api/index.js +10 -1
  3. package/cjs/dist/api/query-services/index.d.ts +78 -0
  4. package/cjs/dist/api/query-services/index.js +137 -0
  5. package/cjs/dist/react/core/booking/Booking.d.ts +5 -5
  6. package/cjs/dist/react/core/booking/Booking.js +5 -5
  7. package/cjs/dist/react/core/location/Location.d.ts +44 -0
  8. package/cjs/dist/react/core/location/Location.js +78 -0
  9. package/cjs/dist/react/core/service/Service.d.ts +160 -17
  10. package/cjs/dist/react/core/service/Service.js +211 -13
  11. package/cjs/dist/react/core/service-list/ServiceList.d.ts +75 -0
  12. package/cjs/dist/react/core/service-list/ServiceList.js +58 -0
  13. package/cjs/dist/react/index.d.ts +2 -0
  14. package/cjs/dist/react/index.js +2 -0
  15. package/cjs/dist/react/location/Location.d.ts +77 -0
  16. package/cjs/dist/react/location/Location.js +89 -0
  17. package/cjs/dist/react/service/Service.d.ts +1036 -31
  18. package/cjs/dist/react/service/Service.js +1060 -47
  19. package/cjs/dist/react/service-list/ServiceList.d.ts +185 -0
  20. package/cjs/dist/react/service-list/ServiceList.js +184 -0
  21. package/cjs/dist/services/booking/booking.d.ts +19 -11
  22. package/cjs/dist/services/booking/booking.js +48 -16
  23. package/cjs/dist/services/index.d.ts +3 -2
  24. package/cjs/dist/services/index.js +2 -1
  25. package/cjs/dist/services/service/service.d.ts +71 -18
  26. package/cjs/dist/services/service/service.js +186 -51
  27. package/cjs/dist/services/service-list/service-list.d.ts +157 -0
  28. package/cjs/dist/services/service-list/service-list.js +274 -0
  29. package/cjs/dist/utils/dateAndTime.d.ts +2 -0
  30. package/cjs/dist/utils/dateAndTime.js +18 -0
  31. package/cjs/dist/utils/money.d.ts +21 -0
  32. package/cjs/dist/utils/money.js +34 -0
  33. package/dist/api/index.d.ts +10 -1
  34. package/dist/api/index.js +10 -1
  35. package/dist/api/query-services/index.d.ts +78 -0
  36. package/dist/api/query-services/index.js +137 -0
  37. package/dist/react/core/booking/Booking.d.ts +5 -5
  38. package/dist/react/core/booking/Booking.js +5 -5
  39. package/dist/react/core/location/Location.d.ts +44 -0
  40. package/dist/react/core/location/Location.js +78 -0
  41. package/dist/react/core/service/Service.d.ts +160 -17
  42. package/dist/react/core/service/Service.js +211 -13
  43. package/dist/react/core/service-list/ServiceList.d.ts +75 -0
  44. package/dist/react/core/service-list/ServiceList.js +58 -0
  45. package/dist/react/index.d.ts +2 -0
  46. package/dist/react/index.js +2 -0
  47. package/dist/react/location/Location.d.ts +77 -0
  48. package/dist/react/location/Location.js +89 -0
  49. package/dist/react/service/Service.d.ts +1036 -31
  50. package/dist/react/service/Service.js +1060 -47
  51. package/dist/react/service-list/ServiceList.d.ts +185 -0
  52. package/dist/react/service-list/ServiceList.js +184 -0
  53. package/dist/services/booking/booking.d.ts +19 -11
  54. package/dist/services/booking/booking.js +48 -16
  55. package/dist/services/index.d.ts +3 -2
  56. package/dist/services/index.js +2 -1
  57. package/dist/services/service/service.d.ts +71 -18
  58. package/dist/services/service/service.js +186 -51
  59. package/dist/services/service-list/service-list.d.ts +157 -0
  60. package/dist/services/service-list/service-list.js +274 -0
  61. package/dist/utils/dateAndTime.d.ts +2 -0
  62. package/dist/utils/dateAndTime.js +18 -0
  63. package/dist/utils/money.d.ts +21 -0
  64. package/dist/utils/money.js +34 -0
  65. package/package.json +2 -2
  66. package/cjs/dist/api/query-service/index.d.ts +0 -23
  67. package/cjs/dist/api/query-service/index.js +0 -41
  68. package/dist/api/query-service/index.d.ts +0 -23
  69. package/dist/api/query-service/index.js +0 -41
@@ -0,0 +1,185 @@
1
+ /**
2
+ * ServiceList - High-level component for displaying services list
3
+ * Provides components for displaying services list with pagination
4
+ */
5
+ import React from 'react';
6
+ import { ListVariant, GenericListLoadMoreRenderProps } from '@wix/headless-components/react';
7
+ import { AsChildChildren } from '@wix/headless-utils/react';
8
+ import type { ServiceListServiceConfig } from '../../services/service-list/service-list.js';
9
+ import type { services } from '@wix/bookings';
10
+ /**
11
+ * Props for the ServiceList root component
12
+ */
13
+ export interface ServiceListRootProps {
14
+ children: React.ReactNode;
15
+ serviceListConfig?: ServiceListServiceConfig;
16
+ className?: string;
17
+ variant?: ListVariant;
18
+ }
19
+ /**
20
+ * Root component that provides the ServiceList service context for rendering services lists.
21
+ *
22
+ * @order 1
23
+ * @component
24
+ * @example
25
+ * ```tsx
26
+ * import { ServiceList } from '@wix/bookings/components';
27
+ *
28
+ * function ServicesPage({ serviceListConfig }) {
29
+ * return (
30
+ * <ServiceList.Root serviceListConfig={serviceListConfig}>
31
+ * <ServiceList.Services emptyState={<div>No services available</div>}>
32
+ * <ServiceList.ServiceRepeater>
33
+ * <Service.Name />
34
+ * <Service.Price />
35
+ * </ServiceList.ServiceRepeater>
36
+ * </ServiceList.Services>
37
+ * </ServiceList.Root>
38
+ * );
39
+ * }
40
+ * ```
41
+ */
42
+ export declare const Root: React.ForwardRefExoticComponent<ServiceListRootProps & React.RefAttributes<HTMLElement>>;
43
+ /**
44
+ * Props for ServiceList Services component
45
+ */
46
+ export interface ServicesProps {
47
+ children: React.ReactNode;
48
+ emptyState?: React.ReactNode;
49
+ infiniteScroll?: boolean;
50
+ pageSize?: number;
51
+ className?: string;
52
+ }
53
+ /**
54
+ * Container for the services list with empty state support.
55
+ * Wraps GenericList.Items. Follows List Container Level pattern.
56
+ *
57
+ * @component
58
+ * @example
59
+ * ```tsx
60
+ * <ServiceList.Services emptyState={<div>No services found</div>}>
61
+ * <ServiceList.ServiceRepeater>
62
+ * <Service.Name />
63
+ * <Service.Price />
64
+ * </ServiceList.ServiceRepeater>
65
+ * </ServiceList.Services>
66
+ * ```
67
+ */
68
+ export declare const Services: React.ForwardRefExoticComponent<ServicesProps & React.RefAttributes<HTMLElement>>;
69
+ /**
70
+ * Function children type for Service.Root component
71
+ * Receives { selected, bookable } props from Service.Root
72
+ */
73
+ export type ServiceRootChildren = AsChildChildren<{
74
+ selected: boolean;
75
+ bookable: boolean;
76
+ }>;
77
+ /**
78
+ * Props for the itemWrapper function in asChild pattern
79
+ * Supports both ReactNode and function children that receive { selected, bookable }
80
+ */
81
+ export interface ServiceItemWrapperProps {
82
+ item: services.Service;
83
+ index?: number;
84
+ /** Children can be ReactNode or a function that receives { selected, bookable } from Service.Root */
85
+ children: React.ReactNode | ServiceRootChildren;
86
+ }
87
+ /**
88
+ * Render props for ServiceRepeater asChild pattern
89
+ * Provides items array and itemWrapper function for custom rendering
90
+ */
91
+ export interface ServiceRepeaterRenderProps {
92
+ items: services.Service[];
93
+ variant?: ListVariant;
94
+ /** Wraps each item in Service.Root - children can be ReactNode or function receiving { selected, bookable } */
95
+ itemWrapper: (props: ServiceItemWrapperProps) => React.ReactNode;
96
+ }
97
+ /**
98
+ * Props for ServiceList ServiceRepeater component
99
+ */
100
+ export interface ServiceRepeaterProps {
101
+ /**
102
+ * Children can be:
103
+ * - ReactNode: Static content rendered for each service
104
+ * - ServiceRootChildren: Function receiving { selected, bookable } from Service.Root
105
+ * - Function (with asChild=true): Receives { items, itemWrapper } for custom rendering
106
+ */
107
+ children: React.ReactNode | ((props: ServiceRepeaterRenderProps, ref?: React.Ref<HTMLElement>) => React.ReactNode) | ServiceRootChildren;
108
+ /** Whether to render as child component (asChild pattern) */
109
+ asChild?: boolean;
110
+ }
111
+ /**
112
+ * Repeater component that renders Service.Root for each service.
113
+ * Follows Repeater Level pattern and uses GenericList.Repeater for consistency.
114
+ * Supports asChild pattern for advanced layout components.
115
+ *
116
+ * @component
117
+ * @example
118
+ * ```tsx
119
+ * // Standard usage
120
+ * <ServiceList.ServiceRepeater>
121
+ * <Service.Name />
122
+ * <Service.Price />
123
+ * </ServiceList.ServiceRepeater>
124
+ *
125
+ * // AsChild usage with custom wrapper
126
+ * <ServiceList.ServiceRepeater asChild>
127
+ * {({ items, variant, itemWrapper }) => (
128
+ * <CustomWrapper
129
+ * items={items}
130
+ * variant={variant}
131
+ * itemRenderer={(item, index) =>
132
+ * itemWrapper({ item, index, children: <>
133
+ * <Service.Name />
134
+ * <Service.Price />
135
+ * </> })
136
+ * }
137
+ * />
138
+ * )}
139
+ * </ServiceList.ServiceRepeater>
140
+ * ```
141
+ */
142
+ export declare const ServiceRepeater: React.ForwardRefExoticComponent<ServiceRepeaterProps & React.RefAttributes<HTMLElement>>;
143
+ /**
144
+ * Props for ServiceList LoadMore component
145
+ */
146
+ export interface LoadMoreProps {
147
+ /** Custom render function when using asChild */
148
+ children?: React.ReactNode | React.ForwardRefRenderFunction<HTMLButtonElement, GenericListLoadMoreRenderProps>;
149
+ /**
150
+ * Whether to render as a child component.
151
+ * @default false
152
+ */
153
+ asChild?: boolean;
154
+ /**
155
+ * The CSS classes to apply to the button.
156
+ */
157
+ className?: string;
158
+ /**
159
+ * The label to display inside the button.
160
+ */
161
+ label?: string;
162
+ /**
163
+ * The loading state to display inside the button.
164
+ */
165
+ loadingState?: React.ReactNode;
166
+ }
167
+ /**
168
+ * Displays a button to load more services. Not rendered if infiniteScroll is false or no services are left to load.
169
+ * Follows the architecture rules - does not support asChild as it's a simple trigger component.
170
+ *
171
+ * @component
172
+ * @example
173
+ * ```tsx
174
+ * <ServiceList.Actions.LoadMore asChild>
175
+ * <button>Load More</button>
176
+ * </ServiceList.Actions.LoadMore>
177
+ * ```
178
+ */
179
+ export declare const LoadMore: React.ForwardRefExoticComponent<LoadMoreProps & React.RefAttributes<HTMLButtonElement>>;
180
+ /**
181
+ * Actions namespace for ServiceList
182
+ */
183
+ export declare const Actions: {
184
+ LoadMore: React.ForwardRefExoticComponent<LoadMoreProps & React.RefAttributes<HTMLButtonElement>>;
185
+ };
@@ -0,0 +1,184 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ServiceList - High-level component for displaying services list
4
+ * Provides components for displaying services list with pagination
5
+ */
6
+ import React from 'react';
7
+ import { GenericList, } from '@wix/headless-components/react';
8
+ import { useService } from '@wix/services-manager-react';
9
+ import { ServiceListServiceDefinition } from '../../services/service-list/service-list.js';
10
+ import * as CoreServiceList from '../core/service-list/ServiceList.js';
11
+ import * as Service from '../service/Service.js';
12
+ /**
13
+ * Booking app ID constant
14
+ */
15
+ const BOOKING_APP_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
16
+ var TestIds;
17
+ (function (TestIds) {
18
+ TestIds["serviceListRoot"] = "service-list-root";
19
+ TestIds["serviceListServices"] = "service-list-services";
20
+ TestIds["serviceListService"] = "service-list-service";
21
+ TestIds["serviceListLoadMore"] = "service-list-load-more";
22
+ })(TestIds || (TestIds = {}));
23
+ /**
24
+ * Root component that provides the ServiceList service context for rendering services lists.
25
+ *
26
+ * @order 1
27
+ * @component
28
+ * @example
29
+ * ```tsx
30
+ * import { ServiceList } from '@wix/bookings/components';
31
+ *
32
+ * function ServicesPage({ serviceListConfig }) {
33
+ * return (
34
+ * <ServiceList.Root serviceListConfig={serviceListConfig}>
35
+ * <ServiceList.Services emptyState={<div>No services available</div>}>
36
+ * <ServiceList.ServiceRepeater>
37
+ * <Service.Name />
38
+ * <Service.Price />
39
+ * </ServiceList.ServiceRepeater>
40
+ * </ServiceList.Services>
41
+ * </ServiceList.Root>
42
+ * );
43
+ * }
44
+ * ```
45
+ */
46
+ export const Root = React.forwardRef((props, ref) => {
47
+ const { children, serviceListConfig, className, variant } = props;
48
+ const serviceConfig = serviceListConfig || {
49
+ services: [],
50
+ options: {
51
+ appId: BOOKING_APP_ID,
52
+ pagingMetadata: {
53
+ limit: 0,
54
+ offset: 0,
55
+ hasNext: false,
56
+ },
57
+ },
58
+ };
59
+ return (_jsx(CoreServiceList.Root, { serviceListConfig: serviceConfig, children: _jsx(RootContent, { className: className, ref: ref, variant: variant, children: children }) }));
60
+ });
61
+ Root.displayName = 'ServiceList.Root';
62
+ /**
63
+ * Internal component to handle the Root content with service access
64
+ */
65
+ const RootContent = React.forwardRef((props, ref) => {
66
+ const { children, className, variant } = props;
67
+ const serviceListService = useService(ServiceListServiceDefinition);
68
+ const items = serviceListService.servicesSignal
69
+ .get()
70
+ .map((service) => ({
71
+ ...service,
72
+ id: service._id,
73
+ }));
74
+ return (_jsx(GenericList.Root, { items: items, loadMore: () => serviceListService.actions.loadMore(10), hasMore: serviceListService.hasMoreServicesSignal.get(), isLoading: serviceListService.isLoadingSignal.get(), className: className, ref: ref, "data-testid": TestIds.serviceListRoot, variant: variant, children: children }));
75
+ });
76
+ /**
77
+ * Container for the services list with empty state support.
78
+ * Wraps GenericList.Items. Follows List Container Level pattern.
79
+ *
80
+ * @component
81
+ * @example
82
+ * ```tsx
83
+ * <ServiceList.Services emptyState={<div>No services found</div>}>
84
+ * <ServiceList.ServiceRepeater>
85
+ * <Service.Name />
86
+ * <Service.Price />
87
+ * </ServiceList.ServiceRepeater>
88
+ * </ServiceList.Services>
89
+ * ```
90
+ */
91
+ export const Services = React.forwardRef((props, ref) => {
92
+ const { children, ...otherProps } = props;
93
+ return (_jsx(GenericList.Items, { ref: ref, "data-testid": TestIds.serviceListServices, ...otherProps, children: children }));
94
+ });
95
+ Services.displayName = 'ServiceList.Services';
96
+ /**
97
+ * Repeater component that renders Service.Root for each service.
98
+ * Follows Repeater Level pattern and uses GenericList.Repeater for consistency.
99
+ * Supports asChild pattern for advanced layout components.
100
+ *
101
+ * @component
102
+ * @example
103
+ * ```tsx
104
+ * // Standard usage
105
+ * <ServiceList.ServiceRepeater>
106
+ * <Service.Name />
107
+ * <Service.Price />
108
+ * </ServiceList.ServiceRepeater>
109
+ *
110
+ * // AsChild usage with custom wrapper
111
+ * <ServiceList.ServiceRepeater asChild>
112
+ * {({ items, variant, itemWrapper }) => (
113
+ * <CustomWrapper
114
+ * items={items}
115
+ * variant={variant}
116
+ * itemRenderer={(item, index) =>
117
+ * itemWrapper({ item, index, children: <>
118
+ * <Service.Name />
119
+ * <Service.Price />
120
+ * </> })
121
+ * }
122
+ * />
123
+ * )}
124
+ * </ServiceList.ServiceRepeater>
125
+ * ```
126
+ */
127
+ export const ServiceRepeater = React.forwardRef((props, ref) => {
128
+ const { children, asChild } = props;
129
+ const serviceListService = useService(ServiceListServiceDefinition);
130
+ const appId = serviceListService.queryOptionsSignal.get().appId;
131
+ // Check if children is a function that matches ServiceRootChildren pattern
132
+ // (function with single parameter that receives { selected, bookable })
133
+ const isServiceRootFunction = typeof children === 'function' && children.length === 1 && !asChild;
134
+ // Custom itemWrapper that matches ItemWrapperProps signature
135
+ const serviceItemWrapper = (wrapperProps) => {
136
+ const { item: service, children: itemChildren } = wrapperProps;
137
+ return (_jsx(Service.Root, { service: service, appId: appId, "data-testid": TestIds.serviceListService, "data-service-id": service._id, "data-item-id": service._id, children: isServiceRootFunction
138
+ ? children
139
+ : itemChildren }, service._id));
140
+ };
141
+ // Transform children for asChild pattern to provide custom render props
142
+ const transformedChildren = asChild && typeof children === 'function'
143
+ ? (genericProps) => {
144
+ // Create properly typed render props for the consumer
145
+ const renderProps = {
146
+ items: genericProps.items,
147
+ variant: genericProps.variant,
148
+ itemWrapper: (props) => serviceItemWrapper({
149
+ item: props.item,
150
+ index: props.index ?? 0,
151
+ children: props.children,
152
+ }),
153
+ };
154
+ return children(renderProps, ref);
155
+ }
156
+ : isServiceRootFunction
157
+ ? undefined
158
+ : children;
159
+ return (_jsx(GenericList.Repeater, { ref: ref, asChild: asChild, itemWrapper: serviceItemWrapper, children: transformedChildren }));
160
+ });
161
+ ServiceRepeater.displayName = 'ServiceList.ServiceRepeater';
162
+ /**
163
+ * Displays a button to load more services. Not rendered if infiniteScroll is false or no services are left to load.
164
+ * Follows the architecture rules - does not support asChild as it's a simple trigger component.
165
+ *
166
+ * @component
167
+ * @example
168
+ * ```tsx
169
+ * <ServiceList.Actions.LoadMore asChild>
170
+ * <button>Load More</button>
171
+ * </ServiceList.Actions.LoadMore>
172
+ * ```
173
+ */
174
+ export const LoadMore = React.forwardRef((props, ref) => {
175
+ const { children = _jsx("button", {}), ...otherProps } = props;
176
+ return (_jsx(GenericList.Actions.LoadMore, { ref: ref, "data-testid": TestIds.serviceListLoadMore, ...otherProps, children: children }));
177
+ });
178
+ LoadMore.displayName = 'ServiceList.Actions.LoadMore';
179
+ /**
180
+ * Actions namespace for ServiceList
181
+ */
182
+ export const Actions = {
183
+ LoadMore,
184
+ };
@@ -7,12 +7,16 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
7
7
  import type { services, availabilityTimeSlots } from '@wix/bookings';
8
8
  import type { FormValues } from '@wix/form-public';
9
9
  /**
10
- * ServiceSelection contains the service AND its related selections
10
+ * BookingSelection contains the service AND its related selections
11
11
  */
12
- export interface ServiceSelection {
12
+ export interface BookingSelection {
13
+ /** Unique identifier for this booking instance (allows same service multiple times) */
14
+ instanceId: string;
13
15
  service: services.Service;
14
16
  /** Selected resources (staff members, rooms, etc.) */
15
- resources?: availabilityTimeSlots.Resource[];
17
+ staff?: availabilityTimeSlots.Resource;
18
+ /** Per-service slot data from nested slots (for multi-service bookings) */
19
+ nestedSlotData?: availabilityTimeSlots.NestedTimeSlot;
16
20
  }
17
21
  export interface Location {
18
22
  id: string;
@@ -22,10 +26,14 @@ export interface Location {
22
26
  * BookingService actions - setters only, no API calls
23
27
  */
24
28
  export interface BookingServiceActions {
25
- setServices(services: ServiceSelection[]): void;
26
- addService(service: ServiceSelection): void;
27
- updateService(serviceId: string, updates: Partial<ServiceSelection>): void;
28
- removeService(serviceId: string): void;
29
+ /** Clears all booking selections and sets a single service (instanceId is auto-generated) */
30
+ setService(service: Omit<BookingSelection, 'instanceId'>): string;
31
+ /** Adds a service and returns the generated instanceId */
32
+ addService(service: Omit<BookingSelection, 'instanceId'>): string;
33
+ /** Updates a booking selection by instanceId */
34
+ updateServiceByInstanceId(instanceId: string, updates: Partial<Omit<BookingSelection, 'instanceId'>>): void;
35
+ /** Removes a service selection by instanceId. If no instanceId provided, removes the first service. */
36
+ removeServiceByInstanceId(instanceId?: string): void;
29
37
  clearServices(): void;
30
38
  setTimeSlot(slot: availabilityTimeSlots.TimeSlot | null): void;
31
39
  clearTimeSlot(): void;
@@ -40,8 +48,8 @@ export interface BookingServiceActions {
40
48
  * Pure state management - no API calls
41
49
  */
42
50
  export interface BookingService {
43
- /** Signal containing the currently selected services with their selections */
44
- services: Signal<ServiceSelection[]>;
51
+ /** Signal containing the booking selections (services with their related data) */
52
+ bookingSelections: Signal<BookingSelection[]>;
45
53
  /** Signal containing the currently selected time slot */
46
54
  timeSlot: Signal<availabilityTimeSlots.TimeSlot | null>;
47
55
  /** Signal containing the selected location (derived from timeSlot) */
@@ -64,8 +72,8 @@ export declare const BookingServiceDefinition: string & {
64
72
  * Note: location is NOT in config - it's derived internally from timeSlot
65
73
  */
66
74
  export interface BookingServiceConfig {
67
- /** Optional initial services to pre-populate */
68
- initialServices?: ServiceSelection[];
75
+ /** Optional initial booking selections to pre-populate */
76
+ initialBookingSelections?: BookingSelection[];
69
77
  /** Optional initial time slot to pre-populate */
70
78
  initialTimeSlot?: availabilityTimeSlots.TimeSlot | null;
71
79
  /** Optional initial form values */
@@ -5,6 +5,15 @@
5
5
  */
6
6
  import { defineService, implementService } from '@wix/services-definitions';
7
7
  import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
8
+ // ============================================================================
9
+ // Types
10
+ // ============================================================================
11
+ /**
12
+ * Generates a unique instance ID for service selections
13
+ */
14
+ function generateInstanceId() {
15
+ return crypto.randomUUID();
16
+ }
8
17
  /**
9
18
  * Service definition for Booking
10
19
  */
@@ -39,7 +48,12 @@ export const BookingServiceDefinition = defineService('booking');
39
48
  export const BookingService = implementService.withConfig()(BookingServiceDefinition, ({ getService, config }) => {
40
49
  const signalsService = getService(SignalsServiceDefinition);
41
50
  // Initialize signals with config values
42
- const servicesSignal = signalsService.signal(config.initialServices || []);
51
+ // Ensure all initial booking selections have instanceIds
52
+ const initialSelectionsWithIds = (config.initialBookingSelections || []).map((s) => ({
53
+ ...s,
54
+ instanceId: s.instanceId || generateInstanceId(),
55
+ }));
56
+ const bookingSelectionsSignal = signalsService.signal(initialSelectionsWithIds);
43
57
  const timeSlotSignal = signalsService.signal(config.initialTimeSlot || null);
44
58
  // Location is derived internally - initialize as null
45
59
  const locationSignal = signalsService.signal(null);
@@ -49,25 +63,43 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
49
63
  // ========================================================================
50
64
  const actions = {
51
65
  // Services actions
52
- setServices(newServices) {
53
- servicesSignal.set(newServices);
66
+ setService(service) {
67
+ const instanceId = generateInstanceId();
68
+ const newSelection = {
69
+ ...service,
70
+ instanceId,
71
+ };
72
+ bookingSelectionsSignal.set([newSelection]);
73
+ return instanceId;
54
74
  },
55
75
  addService(service) {
56
- const current = servicesSignal.get();
57
- servicesSignal.set([...current, service]);
76
+ const instanceId = generateInstanceId();
77
+ const current = bookingSelectionsSignal.get();
78
+ const newSelection = {
79
+ ...service,
80
+ instanceId,
81
+ };
82
+ bookingSelectionsSignal.set([...current, newSelection]);
83
+ return instanceId;
58
84
  },
59
- updateService(serviceId, updates) {
60
- const current = servicesSignal.get();
61
- const updated = current.map((s) => s.service._id === serviceId ? { ...s, ...updates } : s);
62
- servicesSignal.set(updated);
85
+ updateServiceByInstanceId(instanceId, updates) {
86
+ const current = bookingSelectionsSignal.get();
87
+ const updated = current.map((s) => s.instanceId === instanceId ? { ...s, ...updates } : s);
88
+ bookingSelectionsSignal.set(updated);
63
89
  },
64
- removeService(serviceId) {
65
- const current = servicesSignal.get();
66
- const filtered = current.filter((s) => s.service._id !== serviceId);
67
- servicesSignal.set(filtered);
90
+ removeServiceByInstanceId(instanceId) {
91
+ const current = bookingSelectionsSignal.get();
92
+ if (!instanceId) {
93
+ // Remove first selection if no instanceId provided
94
+ bookingSelectionsSignal.set(current.slice(1));
95
+ }
96
+ else {
97
+ const filtered = current.filter((s) => s.instanceId !== instanceId);
98
+ bookingSelectionsSignal.set(filtered);
99
+ }
68
100
  },
69
101
  clearServices() {
70
- servicesSignal.set([]);
102
+ bookingSelectionsSignal.set([]);
71
103
  },
72
104
  // Time Slot actions
73
105
  setTimeSlot(slot) {
@@ -92,14 +124,14 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
92
124
  },
93
125
  // Clear all
94
126
  clearAll() {
95
- servicesSignal.set([]);
127
+ bookingSelectionsSignal.set([]);
96
128
  timeSlotSignal.set(null);
97
129
  locationSignal.set(null);
98
130
  formValuesSignal.set(null);
99
131
  },
100
132
  };
101
133
  return {
102
- services: servicesSignal,
134
+ bookingSelections: bookingSelectionsSignal,
103
135
  timeSlot: timeSlotSignal,
104
136
  location: locationSignal,
105
137
  formValues: formValuesSignal,
@@ -2,5 +2,6 @@
2
2
  * Bookings Services
3
3
  * Main export file for all service definitions and implementations
4
4
  */
5
- export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type ServiceServiceConfig, loadServiceServiceConfig, type SuccessServiceServiceConfigResult, type NotFoundServiceServiceConfigResult, } from './service/service.js';
6
- export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type ServiceSelection, type Location, type BookingServiceActions, } from './booking/booking.js';
5
+ export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type ServiceServiceConfig, type ServiceActions, type ServicePaymentData, loadServiceServiceInitialData, type SuccessServiceServiceConfigResult, type NotFoundServiceServiceConfigResult, } from './service/service.js';
6
+ export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type BookingSelection, type Location, type BookingServiceActions, } from './booking/booking.js';
7
+ export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
@@ -2,5 +2,6 @@
2
2
  * Bookings Services
3
3
  * Main export file for all service definitions and implementations
4
4
  */
5
- export { ServiceService, ServiceServiceDefinition, loadServiceServiceConfig, } from './service/service.js';
5
+ export { ServiceService, ServiceServiceDefinition, loadServiceServiceInitialData, } from './service/service.js';
6
6
  export { BookingService, BookingServiceDefinition, } from './booking/booking.js';
7
+ export { ServiceListService, ServiceListServiceDefinition, loadServiceListServiceInitialData, } from './service-list/service-list.js';