@wix/headless-bookings 0.0.53 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/dist/__mocks__/booking.js +1 -0
- package/cjs/dist/__mocks__/services.d.ts +38 -1
- package/cjs/dist/__mocks__/services.js +82 -0
- package/cjs/dist/api/query-services/index.js +33 -3
- package/cjs/dist/react/core/location/Location.d.ts +2 -2
- package/cjs/dist/react/core/location/Location.js +1 -1
- package/cjs/dist/react/core/staff-member/StaffMember.d.ts +104 -0
- package/cjs/dist/react/core/staff-member/StaffMember.js +143 -0
- package/cjs/dist/react/core/staff-member-list/StaffMemberList.d.ts +96 -0
- package/cjs/dist/react/core/staff-member-list/StaffMemberList.js +66 -0
- package/cjs/dist/react/index.d.ts +2 -0
- package/cjs/dist/react/index.js +2 -0
- package/cjs/dist/react/location/LocationList.d.ts +19 -0
- package/cjs/dist/react/location/LocationList.js +52 -1
- package/cjs/dist/react/service/Service.d.ts +51 -44
- package/cjs/dist/react/service/Service.js +56 -37
- package/cjs/dist/react/staff-member/StaffMember.d.ts +119 -0
- package/cjs/dist/react/staff-member/StaffMember.js +147 -0
- package/cjs/dist/react/staff-member/StaffMemberList.d.ts +123 -0
- package/cjs/dist/react/staff-member/StaffMemberList.js +168 -0
- package/cjs/dist/services/booking/booking.d.ts +2 -0
- package/cjs/dist/services/booking/booking.js +15 -0
- package/cjs/dist/services/index.d.ts +1 -0
- package/cjs/dist/services/index.js +1 -0
- package/cjs/dist/services/service-list/service-list.js +41 -9
- package/cjs/dist/services/staff-member-list/staff-member-list.d.ts +85 -0
- package/cjs/dist/services/staff-member-list/staff-member-list.def.d.ts +46 -0
- package/cjs/dist/services/staff-member-list/staff-member-list.def.js +14 -0
- package/cjs/dist/services/staff-member-list/staff-member-list.js +105 -0
- package/dist/__mocks__/booking.js +1 -0
- package/dist/__mocks__/services.d.ts +38 -1
- package/dist/__mocks__/services.js +82 -0
- package/dist/api/query-services/index.js +33 -3
- package/dist/react/core/location/Location.d.ts +2 -2
- package/dist/react/core/location/Location.js +1 -1
- package/dist/react/core/staff-member/StaffMember.d.ts +104 -0
- package/dist/react/core/staff-member/StaffMember.js +143 -0
- package/dist/react/core/staff-member-list/StaffMemberList.d.ts +96 -0
- package/dist/react/core/staff-member-list/StaffMemberList.js +66 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/react/location/LocationList.d.ts +19 -0
- package/dist/react/location/LocationList.js +52 -1
- package/dist/react/service/Service.d.ts +51 -44
- package/dist/react/service/Service.js +56 -37
- package/dist/react/staff-member/StaffMember.d.ts +119 -0
- package/dist/react/staff-member/StaffMember.js +147 -0
- package/dist/react/staff-member/StaffMemberList.d.ts +123 -0
- package/dist/react/staff-member/StaffMemberList.js +168 -0
- package/dist/services/booking/booking.d.ts +2 -0
- package/dist/services/booking/booking.js +15 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/service-list/service-list.js +41 -9
- package/dist/services/staff-member-list/staff-member-list.d.ts +85 -0
- package/dist/services/staff-member-list/staff-member-list.def.d.ts +46 -0
- package/dist/services/staff-member-list/staff-member-list.def.js +14 -0
- package/dist/services/staff-member-list/staff-member-list.js +105 -0
- package/package.json +2 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StaffMemberList - High-level component for displaying staff member lists
|
|
3
|
+
* Follows the pattern from LocationList.tsx
|
|
4
|
+
*
|
|
5
|
+
* @module React/StaffMemberList
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { GenericListRepeaterProps, type ListVariant } from '@wix/headless-components/react';
|
|
9
|
+
import { type AsChildChildren } from '@wix/headless-utils/react';
|
|
10
|
+
import type { StaffMemberListServiceConfig } from '../../services/staff-member-list/staff-member-list.js';
|
|
11
|
+
import type { StaffMemberData } from '../../services/staff-member-list/staff-member-list.def.js';
|
|
12
|
+
/**
|
|
13
|
+
* Staff member with id for list rendering
|
|
14
|
+
* Uses staffMemberId as the id (which equals resource id)
|
|
15
|
+
*/
|
|
16
|
+
interface StaffMemberWithId extends StaffMemberData {
|
|
17
|
+
id: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Props for the StaffMemberList root component
|
|
21
|
+
*/
|
|
22
|
+
export interface RootProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
staffMemberListConfig?: StaffMemberListServiceConfig;
|
|
25
|
+
className?: string;
|
|
26
|
+
variant?: ListVariant;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Root component that provides the StaffMemberList service context for rendering staff member lists.
|
|
30
|
+
*
|
|
31
|
+
* @component
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import { StaffMemberList, StaffMember } from '@wix/bookings/react';
|
|
35
|
+
*
|
|
36
|
+
* function StaffMembersPage({ staffMemberListConfig }) {
|
|
37
|
+
* return (
|
|
38
|
+
* <StaffMemberList.Root staffMemberListConfig={staffMemberListConfig}>
|
|
39
|
+
* <StaffMemberList.StaffMembers emptyState={<div>No staff members available</div>}>
|
|
40
|
+
* <StaffMemberList.StaffMemberRepeater>
|
|
41
|
+
* <StaffMember.Name />
|
|
42
|
+
* </StaffMemberList.StaffMemberRepeater>
|
|
43
|
+
* </StaffMemberList.StaffMembers>
|
|
44
|
+
* </StaffMemberList.Root>
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare const Root: React.ForwardRefExoticComponent<RootProps & React.RefAttributes<HTMLElement>>;
|
|
50
|
+
/**
|
|
51
|
+
* Props for StaffMemberList.StaffMembers component
|
|
52
|
+
*/
|
|
53
|
+
export interface StaffMembersProps {
|
|
54
|
+
children: React.ReactNode;
|
|
55
|
+
emptyState?: React.ReactNode;
|
|
56
|
+
className?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Container for the staff members list with empty state support.
|
|
60
|
+
* Wraps GenericList.Items internally (no separate Items export).
|
|
61
|
+
*
|
|
62
|
+
* @component
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* <StaffMemberList.StaffMembers emptyState={<div>No staff members available</div>}>
|
|
66
|
+
* <StaffMemberList.StaffMemberRepeater>
|
|
67
|
+
* <StaffMember.Name />
|
|
68
|
+
* </StaffMemberList.StaffMemberRepeater>
|
|
69
|
+
* </StaffMemberList.StaffMembers>
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare const StaffMembers: React.ForwardRefExoticComponent<StaffMembersProps & React.RefAttributes<HTMLElement>>;
|
|
73
|
+
/**
|
|
74
|
+
* Props for StaffMemberList.StaffMemberRepeater component
|
|
75
|
+
*/
|
|
76
|
+
export type StaffMemberRepeaterProps = GenericListRepeaterProps<StaffMemberWithId>;
|
|
77
|
+
export declare const StaffMemberRepeater: React.ForwardRefExoticComponent<StaffMemberRepeaterProps & React.RefAttributes<HTMLElement>>;
|
|
78
|
+
/**
|
|
79
|
+
* Props for StaffMemberList.Totals component
|
|
80
|
+
*/
|
|
81
|
+
export interface TotalsProps {
|
|
82
|
+
asChild?: boolean;
|
|
83
|
+
children?: React.ReactNode | AsChildChildren<{
|
|
84
|
+
count: number;
|
|
85
|
+
hasStaffMembers: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
className?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Displays the total count of staff members.
|
|
91
|
+
*
|
|
92
|
+
* @component
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* <StaffMemberList.Totals />
|
|
96
|
+
*
|
|
97
|
+
* // With asChild
|
|
98
|
+
* <StaffMemberList.Totals asChild>
|
|
99
|
+
* {({ count }) => <span>{count} staff members available</span>}
|
|
100
|
+
* </StaffMemberList.Totals>
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare const Totals: React.ForwardRefExoticComponent<TotalsProps & React.RefAttributes<HTMLElement>>;
|
|
104
|
+
/**
|
|
105
|
+
* Props for StaffMemberList.Actions.Clear component
|
|
106
|
+
*/
|
|
107
|
+
export interface ClearProps {
|
|
108
|
+
asChild?: boolean;
|
|
109
|
+
children?: React.ReactNode | ((props: {
|
|
110
|
+
onClick: () => void;
|
|
111
|
+
}) => React.ReactNode);
|
|
112
|
+
className?: string;
|
|
113
|
+
label?: string;
|
|
114
|
+
/** Callback fired when the clear button is clicked */
|
|
115
|
+
onClicked?: () => void;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Actions namespace for staff member list-level actions
|
|
119
|
+
*/
|
|
120
|
+
export declare const Actions: {
|
|
121
|
+
Clear: React.ForwardRefExoticComponent<ClearProps & React.RefAttributes<HTMLButtonElement>>;
|
|
122
|
+
};
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* StaffMemberList - High-level component for displaying staff member lists
|
|
4
|
+
* Follows the pattern from LocationList.tsx
|
|
5
|
+
*
|
|
6
|
+
* @module React/StaffMemberList
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { GenericList, } from '@wix/headless-components/react';
|
|
10
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
11
|
+
import { useService } from '@wix/services-manager-react';
|
|
12
|
+
import { BookingServiceDefinition } from '../../services/booking/booking.js';
|
|
13
|
+
import * as CoreStaffMemberList from '../core/staff-member-list/StaffMemberList.js';
|
|
14
|
+
import * as StaffMember from './StaffMember.js';
|
|
15
|
+
/** Resource type ID for staff members */
|
|
16
|
+
const STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// TestIds
|
|
19
|
+
// ============================================================================
|
|
20
|
+
var TestIds;
|
|
21
|
+
(function (TestIds) {
|
|
22
|
+
TestIds["staffMemberListRoot"] = "staff-member-list-root";
|
|
23
|
+
TestIds["staffMemberListStaffMembers"] = "staff-member-list-staff-members";
|
|
24
|
+
TestIds["staffMemberListStaffMember"] = "staff-member-list-staff-member";
|
|
25
|
+
TestIds["staffMemberListActionClear"] = "staff-member-list-action-clear";
|
|
26
|
+
})(TestIds || (TestIds = {}));
|
|
27
|
+
/**
|
|
28
|
+
* Root component that provides the StaffMemberList service context for rendering staff member lists.
|
|
29
|
+
*
|
|
30
|
+
* @component
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* import { StaffMemberList, StaffMember } from '@wix/bookings/react';
|
|
34
|
+
*
|
|
35
|
+
* function StaffMembersPage({ staffMemberListConfig }) {
|
|
36
|
+
* return (
|
|
37
|
+
* <StaffMemberList.Root staffMemberListConfig={staffMemberListConfig}>
|
|
38
|
+
* <StaffMemberList.StaffMembers emptyState={<div>No staff members available</div>}>
|
|
39
|
+
* <StaffMemberList.StaffMemberRepeater>
|
|
40
|
+
* <StaffMember.Name />
|
|
41
|
+
* </StaffMemberList.StaffMemberRepeater>
|
|
42
|
+
* </StaffMemberList.StaffMembers>
|
|
43
|
+
* </StaffMemberList.Root>
|
|
44
|
+
* );
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const Root = React.forwardRef((props, ref) => {
|
|
49
|
+
const { children, staffMemberListConfig, className, variant } = props;
|
|
50
|
+
const config = staffMemberListConfig || {
|
|
51
|
+
staffMembers: [],
|
|
52
|
+
};
|
|
53
|
+
return (_jsx(CoreStaffMemberList.Root, { staffMemberListServiceConfig: config, children: _jsx(RootContent, { className: className, ref: ref, variant: variant, children: children }) }));
|
|
54
|
+
});
|
|
55
|
+
Root.displayName = 'StaffMemberList.Root';
|
|
56
|
+
/**
|
|
57
|
+
* Internal component to handle the Root content with service access
|
|
58
|
+
*/
|
|
59
|
+
const RootContent = React.forwardRef((props, ref) => {
|
|
60
|
+
const { children, className, variant } = props;
|
|
61
|
+
return (_jsx(CoreStaffMemberList.StaffMembers, { children: ({ staffMembers, isLoading }) => {
|
|
62
|
+
// Add id to each staff member for GenericList (staffMemberId equals resource id)
|
|
63
|
+
const itemsWithId = staffMembers.map((staffMember, index) => ({
|
|
64
|
+
...staffMember,
|
|
65
|
+
id: staffMember.staffMemberId || String(index),
|
|
66
|
+
}));
|
|
67
|
+
return (_jsx(GenericList.Root, { items: itemsWithId, hasMore: false, isLoading: isLoading, className: className, ref: ref, "data-testid": TestIds.staffMemberListRoot, variant: variant, children: children }));
|
|
68
|
+
} }));
|
|
69
|
+
});
|
|
70
|
+
RootContent.displayName = 'StaffMemberList.RootContent';
|
|
71
|
+
/**
|
|
72
|
+
* Container for the staff members list with empty state support.
|
|
73
|
+
* Wraps GenericList.Items internally (no separate Items export).
|
|
74
|
+
*
|
|
75
|
+
* @component
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <StaffMemberList.StaffMembers emptyState={<div>No staff members available</div>}>
|
|
79
|
+
* <StaffMemberList.StaffMemberRepeater>
|
|
80
|
+
* <StaffMember.Name />
|
|
81
|
+
* </StaffMemberList.StaffMemberRepeater>
|
|
82
|
+
* </StaffMemberList.StaffMembers>
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export const StaffMembers = React.forwardRef((props, ref) => {
|
|
86
|
+
const { children, ...otherProps } = props;
|
|
87
|
+
return (_jsx(GenericList.Items, { ref: ref, "data-testid": TestIds.staffMemberListStaffMembers, ...otherProps, children: children }));
|
|
88
|
+
});
|
|
89
|
+
StaffMembers.displayName = 'StaffMemberList.StaffMembers';
|
|
90
|
+
export const StaffMemberRepeater = React.forwardRef((props, ref) => {
|
|
91
|
+
const { children } = props;
|
|
92
|
+
// Custom itemWrapper that wraps each item in StaffMember.Root
|
|
93
|
+
const staffMemberItemWrapper = ({ item: staffMemberWithId, children: itemChildren, }) => {
|
|
94
|
+
return (_jsx(StaffMember.Root, { staffMember: staffMemberWithId, "data-testid": TestIds.staffMemberListStaffMember, "data-staff-member-id": staffMemberWithId.id, "data-item-id": staffMemberWithId.id, children: itemChildren }, staffMemberWithId.id));
|
|
95
|
+
};
|
|
96
|
+
return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: staffMemberItemWrapper, children: children }));
|
|
97
|
+
});
|
|
98
|
+
StaffMemberRepeater.displayName = 'StaffMemberList.StaffMemberRepeater';
|
|
99
|
+
/**
|
|
100
|
+
* Displays the total count of staff members.
|
|
101
|
+
*
|
|
102
|
+
* @component
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* <StaffMemberList.Totals />
|
|
106
|
+
*
|
|
107
|
+
* // With asChild
|
|
108
|
+
* <StaffMemberList.Totals asChild>
|
|
109
|
+
* {({ count }) => <span>{count} staff members available</span>}
|
|
110
|
+
* </StaffMemberList.Totals>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export const Totals = React.forwardRef((props, ref) => {
|
|
114
|
+
const { asChild, children, className, ...attrs } = props;
|
|
115
|
+
return (_jsx(CoreStaffMemberList.StaffMembers, { children: ({ staffMembers, hasStaffMembers }) => {
|
|
116
|
+
const count = staffMembers.length;
|
|
117
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: { count, hasStaffMembers }, ...attrs, children: _jsx("span", { children: count }) }));
|
|
118
|
+
} }));
|
|
119
|
+
});
|
|
120
|
+
Totals.displayName = 'StaffMemberList.Totals';
|
|
121
|
+
/**
|
|
122
|
+
* Button to clear the staff member selection.
|
|
123
|
+
* Clears the staff member resource from the BookingService.
|
|
124
|
+
*
|
|
125
|
+
* @component
|
|
126
|
+
* @example
|
|
127
|
+
* ```tsx
|
|
128
|
+
* // Default button with "Clear" label
|
|
129
|
+
* <StaffMemberList.Actions.Clear />
|
|
130
|
+
*
|
|
131
|
+
* // With custom label
|
|
132
|
+
* <StaffMemberList.Actions.Clear label="Clear selection" />
|
|
133
|
+
*
|
|
134
|
+
* // With asChild
|
|
135
|
+
* <StaffMemberList.Actions.Clear asChild>
|
|
136
|
+
* <button className="btn-secondary">Clear staff</button>
|
|
137
|
+
* </StaffMemberList.Actions.Clear>
|
|
138
|
+
*
|
|
139
|
+
* // Using render prop pattern
|
|
140
|
+
* <StaffMemberList.Actions.Clear>
|
|
141
|
+
* {({ onClick }) => <button onClick={onClick}>Clear selection</button>}
|
|
142
|
+
* </StaffMemberList.Actions.Clear>
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
const Clear = React.forwardRef((props, ref) => {
|
|
146
|
+
const { asChild, children, className, label = 'Clear', onClicked } = props;
|
|
147
|
+
let bookingService = null;
|
|
148
|
+
try {
|
|
149
|
+
bookingService = useService(BookingServiceDefinition);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// BookingService not available - likely no ServiceManagerProvider
|
|
153
|
+
}
|
|
154
|
+
const handleClick = () => {
|
|
155
|
+
bookingService?.actions.clearResourceById(STAFF_MEMBER_RESOURCE_TYPE_ID);
|
|
156
|
+
onClicked?.();
|
|
157
|
+
};
|
|
158
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.staffMemberListActionClear, customElement: children, customElementProps: {
|
|
159
|
+
onClick: handleClick,
|
|
160
|
+
}, children: _jsx("button", { type: "button", onClick: handleClick, children: label }) }));
|
|
161
|
+
});
|
|
162
|
+
Clear.displayName = 'StaffMemberList.Actions.Clear';
|
|
163
|
+
/**
|
|
164
|
+
* Actions namespace for staff member list-level actions
|
|
165
|
+
*/
|
|
166
|
+
export const Actions = {
|
|
167
|
+
Clear,
|
|
168
|
+
};
|
|
@@ -57,6 +57,8 @@ export interface BookingServiceActions {
|
|
|
57
57
|
setResourceById(resourceId: string, resource: Resource, instanceId?: string): void;
|
|
58
58
|
/** Gets a resource by ID from a service selection. If no instanceId provided, uses the first item. */
|
|
59
59
|
getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
|
|
60
|
+
/** Clears a resource by ID from a service selection. If no instanceId provided, uses the first item. */
|
|
61
|
+
clearResourceById(resourceId: string, instanceId?: string): void;
|
|
60
62
|
setLocation(location: ServiceLocationType | null): void;
|
|
61
63
|
clearLocation(): void;
|
|
62
64
|
setTimezone(timezone: string): void;
|
|
@@ -149,6 +149,21 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
149
149
|
return undefined;
|
|
150
150
|
return targetItem.resources.find((r) => r._id === resourceId);
|
|
151
151
|
},
|
|
152
|
+
clearResourceById(resourceId, instanceId) {
|
|
153
|
+
const current = serviceSelectionsSignal.get();
|
|
154
|
+
// If no instanceId provided, use the first item
|
|
155
|
+
const targetId = instanceId ?? current[0]?.instanceId;
|
|
156
|
+
if (!targetId)
|
|
157
|
+
return;
|
|
158
|
+
const updated = current.map((item) => {
|
|
159
|
+
if (item.instanceId !== targetId)
|
|
160
|
+
return item;
|
|
161
|
+
// Filter out the resource with matching ID
|
|
162
|
+
const newResources = item.resources.filter((r) => r._id !== resourceId);
|
|
163
|
+
return { ...item, resources: newResources };
|
|
164
|
+
});
|
|
165
|
+
serviceSelectionsSignal.set(updated);
|
|
166
|
+
},
|
|
152
167
|
// Location actions
|
|
153
168
|
setLocation(location) {
|
|
154
169
|
locationSignal.set(location);
|
|
@@ -10,3 +10,4 @@ export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest,
|
|
|
10
10
|
export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, type BookingFormServiceInternalAPI, type ValidationResult, } from './booking-form/booking-form.js';
|
|
11
11
|
export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type SlotService, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
|
|
12
12
|
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type LocationData, type DisplayLocation, type LoadLocationListServiceResult, } from './location-list/location-list.js';
|
|
13
|
+
export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, type StaffMemberListServiceAPI, type StaffMemberListServiceConfig, type StaffMemberListActions, type StaffMemberData, type LoadStaffMemberListServiceResult, } from './staff-member-list/staff-member-list.js';
|
|
@@ -10,3 +10,4 @@ export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest,
|
|
|
10
10
|
export { BookingFormService, BookingFormServiceDefinition, } from './booking-form/booking-form.js';
|
|
11
11
|
export { PaymentService, PaymentServiceDefinition, loadPaymentConfig, } from './payment/payment.js';
|
|
12
12
|
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, } from './location-list/location-list.js';
|
|
13
|
+
export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, } from './staff-member-list/staff-member-list.js';
|
|
@@ -73,13 +73,14 @@ export const ServiceListServiceDefinition = defineService('service-list');
|
|
|
73
73
|
* ```
|
|
74
74
|
*/
|
|
75
75
|
export const ServiceListService = implementService.withConfig()(ServiceListServiceDefinition, ({ getService, config }) => {
|
|
76
|
-
let firstRun = true;
|
|
77
76
|
const signalsService = getService(SignalsServiceDefinition);
|
|
78
77
|
const bookingService = getService(BookingServiceDefinition);
|
|
79
78
|
// Default appId to BOOKING_APP_ID if not provided
|
|
80
79
|
const appId = config.options?.appId ?? BOOKING_APP_ID;
|
|
81
80
|
// Check if services are provided (showcase mode vs query mode)
|
|
82
81
|
const hasInitialServices = config.services && config.services.length > 0;
|
|
82
|
+
// Check if categories are provided (from SSR prefetch)
|
|
83
|
+
const hasInitialCategories = config.categories && config.categories.length > 0;
|
|
83
84
|
// Initialize services signal - use empty array if not provided
|
|
84
85
|
const initialServices = config.services || [];
|
|
85
86
|
const servicesSignal = signalsService.signal(initialServices);
|
|
@@ -157,9 +158,43 @@ export const ServiceListService = implementService.withConfig()(ServiceListServi
|
|
|
157
158
|
...(locationId ? { locationIds: [locationId] } : {}),
|
|
158
159
|
};
|
|
159
160
|
};
|
|
160
|
-
//
|
|
161
|
+
// Initial fetch - runs once on client when no prefetched data
|
|
162
|
+
// Fetches both services AND categories in parallel
|
|
163
|
+
if (typeof window !== 'undefined' && !hasInitialServices) {
|
|
164
|
+
const initialQueryOptions = queryOptionsSignal.peek();
|
|
165
|
+
const currentPaging = pagingMetadataSignal.peek();
|
|
166
|
+
const effectiveFilters = getEffectiveFilters(initialQueryOptions.filters);
|
|
167
|
+
isLoadingSignal.set(true);
|
|
168
|
+
Promise.all([
|
|
169
|
+
queryServices({
|
|
170
|
+
appId: initialQueryOptions.appId,
|
|
171
|
+
filters: effectiveFilters,
|
|
172
|
+
pagingMetadata: {
|
|
173
|
+
limit: currentPaging.limit,
|
|
174
|
+
offset: 0,
|
|
175
|
+
},
|
|
176
|
+
sort: initialQueryOptions.sort,
|
|
177
|
+
}),
|
|
178
|
+
!hasInitialCategories ? queryCategories() : Promise.resolve(null),
|
|
179
|
+
])
|
|
180
|
+
.then(([servicesResult, categoriesResult]) => {
|
|
181
|
+
servicesSignal.set(servicesResult.services);
|
|
182
|
+
pagingMetadataSignal.set(servicesResult.pagingMetadata);
|
|
183
|
+
if (categoriesResult) {
|
|
184
|
+
categoriesSignal.set(categoriesResult.categories);
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
.catch((error) => {
|
|
188
|
+
errorSignal.set(error instanceof Error ? error.message : 'Unknown error');
|
|
189
|
+
})
|
|
190
|
+
.finally(() => {
|
|
191
|
+
isLoadingSignal.set(false);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// Reactive effect - only handles query/filter changes (not initial fetch)
|
|
161
195
|
// IMPORTANT: Only subscribes to queryOptionsSignal and bookingService.location
|
|
162
196
|
if (typeof window !== 'undefined') {
|
|
197
|
+
let isFirstEffectRun = true;
|
|
163
198
|
signalsService.effect(async () => {
|
|
164
199
|
// CRITICAL: Read queryOptionsSignal to establish dependency
|
|
165
200
|
// Do NOT read pagingMetadataSignal here to avoid infinite loop
|
|
@@ -167,12 +202,10 @@ export const ServiceListService = implementService.withConfig()(ServiceListServi
|
|
|
167
202
|
// Also subscribe to location changes from BookingService
|
|
168
203
|
// Reading the signal here establishes the dependency
|
|
169
204
|
bookingService.location.get();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
205
|
+
// Skip first run - initial fetch is handled separately above
|
|
206
|
+
if (isFirstEffectRun) {
|
|
207
|
+
isFirstEffectRun = false;
|
|
208
|
+
return;
|
|
176
209
|
}
|
|
177
210
|
// Read paging metadata outside of dependency tracking for the fetch
|
|
178
211
|
const currentPaging = pagingMetadataSignal.peek();
|
|
@@ -202,7 +235,6 @@ export const ServiceListService = implementService.withConfig()(ServiceListServi
|
|
|
202
235
|
}
|
|
203
236
|
});
|
|
204
237
|
}
|
|
205
|
-
firstRun = false;
|
|
206
238
|
const loadMoreCursor = async (count) => {
|
|
207
239
|
const currentQueryOptions = queryOptionsSignal.get();
|
|
208
240
|
// Calculate next page offset based on current services count
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staff Member List Service Implementation
|
|
3
|
+
* Manages staff member state and BookingService integration
|
|
4
|
+
*
|
|
5
|
+
* @module Services/StaffMemberList
|
|
6
|
+
*/
|
|
7
|
+
import type { ServiceFactoryConfig } from '@wix/services-definitions';
|
|
8
|
+
import { type StaffMemberData } from './staff-member-list.def.js';
|
|
9
|
+
/**
|
|
10
|
+
* Configuration interface for StaffMemberListService
|
|
11
|
+
*/
|
|
12
|
+
export interface StaffMemberListServiceConfig {
|
|
13
|
+
/** Staff members array */
|
|
14
|
+
staffMembers?: StaffMemberData[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Implementation of the StaffMemberList service
|
|
18
|
+
* Manages staff member state for display purposes
|
|
19
|
+
*/
|
|
20
|
+
export declare const StaffMemberListService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
21
|
+
__api: import("./staff-member-list.def.js").StaffMemberListServiceAPI;
|
|
22
|
+
__config: {};
|
|
23
|
+
isServiceDefinition?: boolean;
|
|
24
|
+
} & import("./staff-member-list.def.js").StaffMemberListServiceAPI, StaffMemberListServiceConfig>;
|
|
25
|
+
/**
|
|
26
|
+
* Result type for SSR loader
|
|
27
|
+
*/
|
|
28
|
+
export type LoadStaffMemberListServiceResult = {
|
|
29
|
+
type: 'success';
|
|
30
|
+
config: StaffMemberListServiceConfig;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'error';
|
|
33
|
+
error: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* SSR Loader: Creates configuration for server-side rendering.
|
|
37
|
+
*
|
|
38
|
+
* @param options - Optional configuration
|
|
39
|
+
* @returns Configuration result for StaffMemberListService
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // Pass staff members directly (e.g., from service.staffMemberDetails.staffMembers)
|
|
44
|
+
* const result = await loadStaffMemberListServiceInitialData({
|
|
45
|
+
* staffMembers: service.staffMemberDetails?.staffMembers,
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* if (result.type === 'error') {
|
|
49
|
+
* return Astro.redirect('/500');
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* const staffMemberListConfig = result.config;
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function loadStaffMemberListServiceInitialData(options?: {
|
|
56
|
+
/** Pass staff members directly */
|
|
57
|
+
staffMembers?: StaffMemberData[];
|
|
58
|
+
}): Promise<LoadStaffMemberListServiceResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Service binding helper for StaffMemberListService.
|
|
61
|
+
* Bundles definition, implementation, and config for ServicesManager.
|
|
62
|
+
*
|
|
63
|
+
* @param servicesConfigs - Object containing StaffMemberListServiceDefinition config
|
|
64
|
+
* @returns Tuple of [Definition, Implementation, Config] for addService()
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const servicesManager = createServicesManager(
|
|
69
|
+
* createServicesMap()
|
|
70
|
+
* .addService(...staffMemberListServiceBinding(configs))
|
|
71
|
+
* );
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const staffMemberListServiceBinding: <T extends {
|
|
75
|
+
[key: string]: StaffMemberListServiceConfig;
|
|
76
|
+
}>(servicesConfigs: T) => readonly [string & {
|
|
77
|
+
__api: import("./staff-member-list.def.js").StaffMemberListServiceAPI;
|
|
78
|
+
__config: {};
|
|
79
|
+
isServiceDefinition?: boolean;
|
|
80
|
+
} & import("./staff-member-list.def.js").StaffMemberListServiceAPI, import("@wix/services-definitions").ServiceFactory<string & {
|
|
81
|
+
__api: import("./staff-member-list.def.js").StaffMemberListServiceAPI;
|
|
82
|
+
__config: {};
|
|
83
|
+
isServiceDefinition?: boolean;
|
|
84
|
+
} & import("./staff-member-list.def.js").StaffMemberListServiceAPI, StaffMemberListServiceConfig>, StaffMemberListServiceConfig];
|
|
85
|
+
export { StaffMemberListServiceDefinition, type StaffMemberData, type StaffMemberListActions, type StaffMemberListServiceAPI, } from './staff-member-list.def.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staff Member List Service Definition
|
|
3
|
+
* Defines types and service interface for managing staff member lists
|
|
4
|
+
*
|
|
5
|
+
* @module Services/StaffMemberList/Definition
|
|
6
|
+
*/
|
|
7
|
+
import type { Signal, ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
|
|
8
|
+
import type { StaffMember } from '@wix/auto_sdk_bookings_services';
|
|
9
|
+
/**
|
|
10
|
+
* StaffMemberData type - from Service['staffMemberDetails']['staffMembers'][number]
|
|
11
|
+
* Contains staffMemberId (which equals resource id), name, and mainMedia
|
|
12
|
+
*/
|
|
13
|
+
export type StaffMemberData = StaffMember;
|
|
14
|
+
/**
|
|
15
|
+
* Actions interface for staff member list management
|
|
16
|
+
*/
|
|
17
|
+
export interface StaffMemberListActions {
|
|
18
|
+
/** Select a staff member */
|
|
19
|
+
select: (staffMember: StaffMemberData) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* API interface for the StaffMemberList service
|
|
23
|
+
*/
|
|
24
|
+
export interface StaffMemberListServiceAPI {
|
|
25
|
+
/** Reactive signal containing the staff members array */
|
|
26
|
+
staffMembers: Signal<StaffMemberData[]>;
|
|
27
|
+
/** Whether there are any staff members */
|
|
28
|
+
hasStaffMembers: ReadOnlySignal<boolean>;
|
|
29
|
+
/** Reactive signal indicating if staff members are loading */
|
|
30
|
+
isLoading: Signal<boolean>;
|
|
31
|
+
/** Reactive signal containing any error message */
|
|
32
|
+
error: Signal<string | null>;
|
|
33
|
+
/** Computed signal for selected staff member */
|
|
34
|
+
selectedStaffMember: ReadOnlySignal<StaffMemberData | null>;
|
|
35
|
+
/** Actions for staff member management */
|
|
36
|
+
actions: StaffMemberListActions;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Service definition for StaffMemberList
|
|
40
|
+
*/
|
|
41
|
+
export declare const StaffMemberListServiceDefinition: string & {
|
|
42
|
+
__api: StaffMemberListServiceAPI;
|
|
43
|
+
__config: {};
|
|
44
|
+
isServiceDefinition?: boolean;
|
|
45
|
+
} & StaffMemberListServiceAPI;
|
|
46
|
+
export type { StaffMember };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staff Member List Service Definition
|
|
3
|
+
* Defines types and service interface for managing staff member lists
|
|
4
|
+
*
|
|
5
|
+
* @module Services/StaffMemberList/Definition
|
|
6
|
+
*/
|
|
7
|
+
import { defineService } from '@wix/services-definitions';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Service Definition
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Service definition for StaffMemberList
|
|
13
|
+
*/
|
|
14
|
+
export const StaffMemberListServiceDefinition = defineService('staff-member-list');
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staff Member List Service Implementation
|
|
3
|
+
* Manages staff member state and BookingService integration
|
|
4
|
+
*
|
|
5
|
+
* @module Services/StaffMemberList
|
|
6
|
+
*/
|
|
7
|
+
import { implementService } from '@wix/services-definitions';
|
|
8
|
+
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
9
|
+
import { StaffMemberListServiceDefinition, } from './staff-member-list.def.js';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Service Implementation
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Implementation of the StaffMemberList service
|
|
15
|
+
* Manages staff member state for display purposes
|
|
16
|
+
*/
|
|
17
|
+
export const StaffMemberListService = implementService.withConfig()(StaffMemberListServiceDefinition, ({ getService, config }) => {
|
|
18
|
+
const signalsService = getService(SignalsServiceDefinition);
|
|
19
|
+
// Initialize staff members signal
|
|
20
|
+
const staffMembersSignal = signalsService.signal(config.staffMembers || []);
|
|
21
|
+
// Has staff members signal
|
|
22
|
+
const hasStaffMembersSignal = signalsService.computed(() => staffMembersSignal.get().length > 0);
|
|
23
|
+
const isLoadingSignal = signalsService.signal(false);
|
|
24
|
+
const errorSignal = signalsService.signal(null);
|
|
25
|
+
// Selected staff member - managed locally
|
|
26
|
+
const selectedStaffMemberSignal = signalsService.signal(null);
|
|
27
|
+
// Actions
|
|
28
|
+
const actions = {
|
|
29
|
+
select: (staffMember) => {
|
|
30
|
+
selectedStaffMemberSignal.set(staffMember);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
staffMembers: staffMembersSignal,
|
|
35
|
+
hasStaffMembers: hasStaffMembersSignal,
|
|
36
|
+
isLoading: isLoadingSignal,
|
|
37
|
+
error: errorSignal,
|
|
38
|
+
selectedStaffMember: selectedStaffMemberSignal,
|
|
39
|
+
actions,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* SSR Loader: Creates configuration for server-side rendering.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Optional configuration
|
|
46
|
+
* @returns Configuration result for StaffMemberListService
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // Pass staff members directly (e.g., from service.staffMemberDetails.staffMembers)
|
|
51
|
+
* const result = await loadStaffMemberListServiceInitialData({
|
|
52
|
+
* staffMembers: service.staffMemberDetails?.staffMembers,
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* if (result.type === 'error') {
|
|
56
|
+
* return Astro.redirect('/500');
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* const staffMemberListConfig = result.config;
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export async function loadStaffMemberListServiceInitialData(options) {
|
|
63
|
+
try {
|
|
64
|
+
return {
|
|
65
|
+
type: 'success',
|
|
66
|
+
config: {
|
|
67
|
+
staffMembers: options?.staffMembers || [],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : 'Failed to load staff members';
|
|
73
|
+
console.error('loadStaffMemberListServiceInitialData error:', error);
|
|
74
|
+
return { type: 'error', error: message };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Service Binding Helper
|
|
79
|
+
// ============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Service binding helper for StaffMemberListService.
|
|
82
|
+
* Bundles definition, implementation, and config for ServicesManager.
|
|
83
|
+
*
|
|
84
|
+
* @param servicesConfigs - Object containing StaffMemberListServiceDefinition config
|
|
85
|
+
* @returns Tuple of [Definition, Implementation, Config] for addService()
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const servicesManager = createServicesManager(
|
|
90
|
+
* createServicesMap()
|
|
91
|
+
* .addService(...staffMemberListServiceBinding(configs))
|
|
92
|
+
* );
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export const staffMemberListServiceBinding = (servicesConfigs) => {
|
|
96
|
+
return [
|
|
97
|
+
StaffMemberListServiceDefinition,
|
|
98
|
+
StaffMemberListService,
|
|
99
|
+
servicesConfigs['staff-member-list'],
|
|
100
|
+
];
|
|
101
|
+
};
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Re-exports
|
|
104
|
+
// ============================================================================
|
|
105
|
+
export { StaffMemberListServiceDefinition, } from './staff-member-list.def.js';
|