payload-reserve 1.0.2 → 1.1.0
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/README.md +61 -1141
- package/dist/collections/Customers.d.ts +3 -0
- package/dist/collections/Customers.js +58 -0
- package/dist/collections/Customers.js.map +1 -0
- package/dist/collections/Reservations.js +129 -35
- package/dist/collections/Reservations.js.map +1 -1
- package/dist/collections/Resources.js +70 -2
- package/dist/collections/Resources.js.map +1 -1
- package/dist/collections/Schedules.js +4 -1
- package/dist/collections/Schedules.js.map +1 -1
- package/dist/collections/Services.js +59 -2
- package/dist/collections/Services.js.map +1 -1
- package/dist/components/AvailabilityOverview/AvailabilityOverview.module.css +31 -0
- package/dist/components/AvailabilityOverview/index.js +59 -8
- package/dist/components/AvailabilityOverview/index.js.map +1 -1
- package/dist/components/CalendarView/CalendarView.module.css +171 -0
- package/dist/components/CalendarView/index.js +547 -38
- package/dist/components/CalendarView/index.js.map +1 -1
- package/dist/components/CustomerField/index.js +24 -10
- package/dist/components/CustomerField/index.js.map +1 -1
- package/dist/components/DashboardWidget/DashboardWidgetServer.js +21 -11
- package/dist/components/DashboardWidget/DashboardWidgetServer.js.map +1 -1
- package/dist/defaults.d.ts +1 -2
- package/dist/defaults.js +22 -4
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/cancelBooking.d.ts +3 -0
- package/dist/endpoints/cancelBooking.js +37 -0
- package/dist/endpoints/cancelBooking.js.map +1 -0
- package/dist/endpoints/checkAvailability.d.ts +3 -0
- package/dist/endpoints/checkAvailability.js +37 -0
- package/dist/endpoints/checkAvailability.js.map +1 -0
- package/dist/endpoints/createBooking.d.ts +3 -0
- package/dist/endpoints/createBooking.js +32 -0
- package/dist/endpoints/createBooking.js.map +1 -0
- package/dist/endpoints/customerSearch.js +76 -58
- package/dist/endpoints/customerSearch.js.map +1 -1
- package/dist/endpoints/getSlots.d.ts +3 -0
- package/dist/endpoints/getSlots.js +51 -0
- package/dist/endpoints/getSlots.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/reservations/calculateEndTime.js +75 -9
- package/dist/hooks/reservations/calculateEndTime.js.map +1 -1
- package/dist/hooks/reservations/checkIdempotency.d.ts +3 -0
- package/dist/hooks/reservations/checkIdempotency.js +32 -0
- package/dist/hooks/reservations/checkIdempotency.js.map +1 -0
- package/dist/hooks/reservations/onStatusChange.d.ts +3 -0
- package/dist/hooks/reservations/onStatusChange.js +41 -0
- package/dist/hooks/reservations/onStatusChange.js.map +1 -0
- package/dist/hooks/reservations/validateCancellation.js +1 -1
- package/dist/hooks/reservations/validateCancellation.js.map +1 -1
- package/dist/hooks/reservations/validateConflicts.js +34 -56
- package/dist/hooks/reservations/validateConflicts.js.map +1 -1
- package/dist/hooks/reservations/validateStatusTransition.d.ts +2 -1
- package/dist/hooks/reservations/validateStatusTransition.js +29 -6
- package/dist/hooks/reservations/validateStatusTransition.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +49 -54
- package/dist/plugin.js.map +1 -1
- package/dist/services/AvailabilityService.d.ts +57 -0
- package/dist/services/AvailabilityService.js +232 -0
- package/dist/services/AvailabilityService.js.map +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +3 -0
- package/dist/services/index.js.map +1 -0
- package/dist/translations/en.json +33 -1
- package/dist/translations/index.d.ts +5 -0
- package/dist/translations/index.js.map +1 -1
- package/dist/types.d.ts +84 -6
- package/dist/types.js +29 -9
- package/dist/types.js.map +1 -1
- package/dist/utilities/ownerAccess.d.ts +24 -0
- package/dist/utilities/ownerAccess.js +128 -0
- package/dist/utilities/ownerAccess.js.map +1 -0
- package/dist/utilities/resolveReservationItems.d.ts +18 -0
- package/dist/utilities/resolveReservationItems.js +45 -0
- package/dist/utilities/resolveReservationItems.js.map +1 -0
- package/package.json +12 -9
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function createCustomersCollection(config) {
|
|
2
|
+
return {
|
|
3
|
+
slug: config.slugs.customers,
|
|
4
|
+
access: {
|
|
5
|
+
admin: ()=>false,
|
|
6
|
+
...config.access.customers
|
|
7
|
+
},
|
|
8
|
+
admin: {
|
|
9
|
+
group: config.adminGroup,
|
|
10
|
+
listSearchableFields: [
|
|
11
|
+
'firstName',
|
|
12
|
+
'lastName',
|
|
13
|
+
'phone',
|
|
14
|
+
'email'
|
|
15
|
+
],
|
|
16
|
+
useAsTitle: 'firstName'
|
|
17
|
+
},
|
|
18
|
+
auth: true,
|
|
19
|
+
fields: [
|
|
20
|
+
{
|
|
21
|
+
name: 'firstName',
|
|
22
|
+
type: 'text',
|
|
23
|
+
label: ({ t })=>t('reservation:fieldFirstName'),
|
|
24
|
+
maxLength: 200,
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'lastName',
|
|
29
|
+
type: 'text',
|
|
30
|
+
label: ({ t })=>t('reservation:fieldLastName'),
|
|
31
|
+
maxLength: 200,
|
|
32
|
+
required: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'phone',
|
|
36
|
+
type: 'text',
|
|
37
|
+
maxLength: 50
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'notes',
|
|
41
|
+
type: 'textarea',
|
|
42
|
+
label: ({ t })=>t('reservation:fieldNotes')
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'bookings',
|
|
46
|
+
type: 'join',
|
|
47
|
+
collection: config.slugs.reservations,
|
|
48
|
+
on: 'customer'
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
labels: {
|
|
52
|
+
plural: ({ t })=>t('reservation:collectionCustomers'),
|
|
53
|
+
singular: ({ t })=>t('reservation:collectionCustomer')
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=Customers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Customers.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createCustomersCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n return {\n slug: config.slugs.customers,\n access: {\n admin: () => false,\n ...config.access.customers,\n },\n admin: {\n group: config.adminGroup,\n listSearchableFields: ['firstName', 'lastName', 'phone', 'email'],\n useAsTitle: 'firstName',\n },\n auth: true,\n fields: [\n {\n name: 'firstName',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldFirstName'),\n maxLength: 200,\n required: true,\n },\n {\n name: 'lastName',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldLastName'),\n maxLength: 200,\n required: true,\n },\n {\n name: 'phone',\n type: 'text',\n maxLength: 50,\n },\n {\n name: 'notes',\n type: 'textarea',\n label: ({ t }) => (t as PluginT)('reservation:fieldNotes'),\n },\n {\n name: 'bookings',\n type: 'join',\n collection: config.slugs.reservations as unknown as CollectionSlug,\n on: 'customer',\n },\n ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionCustomers'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionCustomer'),\n },\n }\n}\n"],"names":["createCustomersCollection","config","slug","slugs","customers","access","admin","group","adminGroup","listSearchableFields","useAsTitle","auth","fields","name","type","label","t","maxLength","required","collection","reservations","on","labels","plural","singular"],"mappings":"AAKA,OAAO,SAASA,0BACdC,MAAuC;IAEvC,OAAO;QACLC,MAAMD,OAAOE,KAAK,CAACC,SAAS;QAC5BC,QAAQ;YACNC,OAAO,IAAM;YACb,GAAGL,OAAOI,MAAM,CAACD,SAAS;QAC5B;QACAE,OAAO;YACLC,OAAON,OAAOO,UAAU;YACxBC,sBAAsB;gBAAC;gBAAa;gBAAY;gBAAS;aAAQ;YACjEC,YAAY;QACd;QACAC,MAAM;QACNC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNG,WAAW;YACb;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;YACA;gBACEH,MAAM;gBACNC,MAAM;gBACNK,YAAYlB,OAAOE,KAAK,CAACiB,YAAY;gBACrCC,IAAI;YACN;SACD;QACDC,QAAQ;YACNC,QAAQ,CAAC,EAAEP,CAAC,EAAE,GAAK,AAACA,EAAc;YAClCQ,UAAU,CAAC,EAAER,CAAC,EAAE,GAAK,AAACA,EAAc;QACtC;IACF;AACF"}
|
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
import { calculateEndTime } from '../hooks/reservations/calculateEndTime.js';
|
|
2
|
+
import { checkIdempotency } from '../hooks/reservations/checkIdempotency.js';
|
|
3
|
+
import { onStatusChange } from '../hooks/reservations/onStatusChange.js';
|
|
2
4
|
import { validateCancellation } from '../hooks/reservations/validateCancellation.js';
|
|
3
5
|
import { validateConflicts } from '../hooks/reservations/validateConflicts.js';
|
|
4
6
|
import { validateStatusTransition } from '../hooks/reservations/validateStatusTransition.js';
|
|
7
|
+
import { makeReservationOwnerAccess } from '../utilities/ownerAccess.js';
|
|
8
|
+
function createPluginHooksBeforeCreate(hooks) {
|
|
9
|
+
return async ({ context, data, operation, req })=>{
|
|
10
|
+
if (context?.skipReservationHooks) {
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
if (operation === 'create' && hooks.beforeBookingCreate) {
|
|
14
|
+
let mutatedData = data;
|
|
15
|
+
for (const hook of hooks.beforeBookingCreate){
|
|
16
|
+
const result = await hook({
|
|
17
|
+
data: mutatedData,
|
|
18
|
+
req
|
|
19
|
+
});
|
|
20
|
+
if (result) {
|
|
21
|
+
mutatedData = result;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return mutatedData;
|
|
25
|
+
}
|
|
26
|
+
return data;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function createPluginHooksAfterCreate(hooks) {
|
|
30
|
+
return async ({ doc, operation, req })=>{
|
|
31
|
+
if (operation === 'create' && hooks.afterBookingCreate) {
|
|
32
|
+
const docRecord = doc;
|
|
33
|
+
for (const hook of hooks.afterBookingCreate){
|
|
34
|
+
await hook({
|
|
35
|
+
doc: docRecord,
|
|
36
|
+
req
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return doc;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
5
43
|
export function createReservationsCollection(config) {
|
|
44
|
+
const { statusMachine } = config;
|
|
45
|
+
const rom = config.resourceOwnerMode;
|
|
46
|
+
const access = config.access.reservations ?? (rom ? makeReservationOwnerAccess(rom) : {});
|
|
6
47
|
return {
|
|
7
48
|
slug: config.slugs.reservations,
|
|
8
|
-
access
|
|
49
|
+
access,
|
|
9
50
|
admin: {
|
|
10
51
|
components: {
|
|
11
52
|
views: {
|
|
@@ -38,23 +79,16 @@ export function createReservationsCollection(config) {
|
|
|
38
79
|
{
|
|
39
80
|
name: 'customer',
|
|
40
81
|
type: 'relationship',
|
|
41
|
-
label: ({ t })=>t('reservation:fieldCustomer'),
|
|
42
|
-
relationTo: config.userCollection,
|
|
43
|
-
required: true,
|
|
44
|
-
...config.customerRole ? {
|
|
45
|
-
filterOptions: ()=>({
|
|
46
|
-
role: {
|
|
47
|
-
equals: config.customerRole
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
} : {},
|
|
51
82
|
admin: {
|
|
52
83
|
allowCreate: true,
|
|
53
84
|
allowEdit: true,
|
|
54
85
|
components: {
|
|
55
86
|
Field: 'payload-reserve/client#CustomerField'
|
|
56
87
|
}
|
|
57
|
-
}
|
|
88
|
+
},
|
|
89
|
+
label: ({ t })=>t('reservation:fieldCustomer'),
|
|
90
|
+
relationTo: config.slugs.customers,
|
|
91
|
+
required: true
|
|
58
92
|
},
|
|
59
93
|
{
|
|
60
94
|
name: 'startTime',
|
|
@@ -81,50 +115,110 @@ export function createReservationsCollection(config) {
|
|
|
81
115
|
{
|
|
82
116
|
name: 'status',
|
|
83
117
|
type: 'select',
|
|
84
|
-
defaultValue:
|
|
118
|
+
defaultValue: statusMachine.defaultStatus,
|
|
85
119
|
label: ({ t })=>t('reservation:fieldStatus'),
|
|
86
|
-
options:
|
|
120
|
+
options: statusMachine.statuses.map((s)=>({
|
|
121
|
+
label: ({ t })=>{
|
|
122
|
+
const key = `reservation:status${s.charAt(0).toUpperCase() + s.slice(1)}`;
|
|
123
|
+
const translated = t(key);
|
|
124
|
+
return translated !== key ? translated : s.charAt(0).toUpperCase() + s.slice(1);
|
|
125
|
+
},
|
|
126
|
+
value: s
|
|
127
|
+
}))
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'cancellationReason',
|
|
131
|
+
type: 'textarea',
|
|
132
|
+
admin: {
|
|
133
|
+
condition: (_, siblingData)=>siblingData?.status === 'cancelled'
|
|
134
|
+
},
|
|
135
|
+
label: ({ t })=>t('reservation:fieldCancellationReason')
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'guestCount',
|
|
139
|
+
type: 'number',
|
|
140
|
+
defaultValue: 1,
|
|
141
|
+
label: ({ t })=>t('reservation:fieldGuestCount'),
|
|
142
|
+
min: 1
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'notes',
|
|
146
|
+
type: 'textarea',
|
|
147
|
+
label: ({ t })=>t('reservation:fieldNotes')
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'items',
|
|
151
|
+
type: 'array',
|
|
152
|
+
admin: {
|
|
153
|
+
description: 'Resources included in this booking. Leave empty for single-resource bookings.'
|
|
154
|
+
},
|
|
155
|
+
fields: [
|
|
87
156
|
{
|
|
88
|
-
|
|
89
|
-
|
|
157
|
+
name: 'resource',
|
|
158
|
+
type: 'relationship',
|
|
159
|
+
label: ({ t })=>t('reservation:fieldResource'),
|
|
160
|
+
relationTo: config.slugs.resources,
|
|
161
|
+
required: true
|
|
90
162
|
},
|
|
91
163
|
{
|
|
92
|
-
|
|
93
|
-
|
|
164
|
+
name: 'service',
|
|
165
|
+
type: 'relationship',
|
|
166
|
+
label: ({ t })=>t('reservation:fieldService'),
|
|
167
|
+
relationTo: config.slugs.services
|
|
94
168
|
},
|
|
95
169
|
{
|
|
96
|
-
|
|
97
|
-
|
|
170
|
+
name: 'startTime',
|
|
171
|
+
type: 'date',
|
|
172
|
+
admin: {
|
|
173
|
+
date: {
|
|
174
|
+
pickerAppearance: 'dayAndTime'
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
label: ({ t })=>t('reservation:fieldStartTime')
|
|
98
178
|
},
|
|
99
179
|
{
|
|
100
|
-
|
|
101
|
-
|
|
180
|
+
name: 'endTime',
|
|
181
|
+
type: 'date',
|
|
182
|
+
admin: {
|
|
183
|
+
date: {
|
|
184
|
+
pickerAppearance: 'dayAndTime'
|
|
185
|
+
},
|
|
186
|
+
readOnly: false
|
|
187
|
+
},
|
|
188
|
+
label: ({ t })=>t('reservation:fieldEndTime')
|
|
102
189
|
},
|
|
103
190
|
{
|
|
104
|
-
|
|
105
|
-
|
|
191
|
+
name: 'guestCount',
|
|
192
|
+
type: 'number',
|
|
193
|
+
label: ({ t })=>t('reservation:fieldGuestCount'),
|
|
194
|
+
min: 1
|
|
106
195
|
}
|
|
107
|
-
]
|
|
196
|
+
],
|
|
197
|
+
label: ({ t })=>t('reservation:fieldItems')
|
|
108
198
|
},
|
|
109
199
|
{
|
|
110
|
-
name: '
|
|
111
|
-
type: '
|
|
200
|
+
name: 'idempotencyKey',
|
|
201
|
+
type: 'text',
|
|
112
202
|
admin: {
|
|
113
|
-
|
|
203
|
+
position: 'sidebar',
|
|
204
|
+
readOnly: true
|
|
114
205
|
},
|
|
115
|
-
|
|
206
|
+
index: true,
|
|
207
|
+
unique: true
|
|
116
208
|
},
|
|
117
|
-
|
|
118
|
-
name: 'notes',
|
|
119
|
-
type: 'textarea',
|
|
120
|
-
label: ({ t })=>t('reservation:fieldNotes')
|
|
121
|
-
}
|
|
209
|
+
...config.extraReservationFields
|
|
122
210
|
],
|
|
123
211
|
hooks: {
|
|
212
|
+
afterChange: [
|
|
213
|
+
createPluginHooksAfterCreate(config.hooks),
|
|
214
|
+
onStatusChange(config)
|
|
215
|
+
],
|
|
124
216
|
beforeChange: [
|
|
217
|
+
createPluginHooksBeforeCreate(config.hooks),
|
|
218
|
+
checkIdempotency(config),
|
|
125
219
|
calculateEndTime(config),
|
|
126
220
|
validateConflicts(config),
|
|
127
|
-
validateStatusTransition(),
|
|
221
|
+
validateStatusTransition(config),
|
|
128
222
|
validateCancellation(config)
|
|
129
223
|
]
|
|
130
224
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Reservations.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { calculateEndTime } from '../hooks/reservations/calculateEndTime.js'\nimport { validateCancellation } from '../hooks/reservations/validateCancellation.js'\nimport { validateConflicts } from '../hooks/reservations/validateConflicts.js'\nimport { validateStatusTransition } from '../hooks/reservations/validateStatusTransition.js'\n\nexport function createReservationsCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n return {\n slug: config.slugs.reservations,\n access: config.access.reservations ?? {},\n admin: {\n components: {\n views: {\n list: {\n Component: 'payload-reserve/client#CalendarView',\n },\n },\n },\n group: config.adminGroup,\n listSearchableFields: ['status'],\n useAsTitle: 'startTime',\n },\n fields: [\n {\n name: 'service',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldService'),\n relationTo: config.slugs.services,\n required: true,\n },\n {\n name: 'resource',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldResource'),\n relationTo: config.slugs.resources,\n required: true,\n },\n {\n name: 'customer',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldCustomer'),\n relationTo: config.userCollection,\n required: true,\n ...(config.customerRole\n ? { filterOptions: () => ({ role: { equals: config.customerRole } }) }\n : {}),\n admin: {\n allowCreate: true,\n allowEdit: true,\n components: {\n Field: 'payload-reserve/client#CustomerField',\n },\n },\n },\n {\n name: 'startTime',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayAndTime',\n },\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldStartTime'),\n required: true,\n },\n {\n name: 'endTime',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayAndTime',\n },\n readOnly: true,\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldEndTime'),\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: 'pending',\n label: ({ t }) => (t as PluginT)('reservation:fieldStatus'),\n options: [\n { label: ({ t }) => (t as PluginT)('reservation:statusPending'), value: 'pending' },\n { label: ({ t }) => (t as PluginT)('reservation:statusConfirmed'), value: 'confirmed' },\n { label: ({ t }) => (t as PluginT)('reservation:statusCompleted'), value: 'completed' },\n { label: ({ t }) => (t as PluginT)('reservation:statusCancelled'), value: 'cancelled' },\n { label: ({ t }) => (t as PluginT)('reservation:statusNoShow'), value: 'no-show' },\n ],\n },\n {\n name: 'cancellationReason',\n type: 'textarea',\n admin: {\n condition: (_, siblingData) => siblingData?.status === 'cancelled',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldCancellationReason'),\n },\n {\n name: 'notes',\n type: 'textarea',\n label: ({ t }) => (t as PluginT)('reservation:fieldNotes'),\n },\n ],\n hooks: {\n beforeChange: [\n calculateEndTime(config),\n validateConflicts(config),\n validateStatusTransition(),\n validateCancellation(config),\n ],\n },\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionReservations'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionReservations'),\n },\n }\n}\n"],"names":["calculateEndTime","validateCancellation","validateConflicts","validateStatusTransition","createReservationsCollection","config","slug","slugs","reservations","access","admin","components","views","list","Component","group","adminGroup","listSearchableFields","useAsTitle","fields","name","type","label","t","relationTo","services","required","resources","userCollection","customerRole","filterOptions","role","equals","allowCreate","allowEdit","Field","date","pickerAppearance","readOnly","defaultValue","options","value","condition","_","siblingData","status","hooks","beforeChange","labels","plural","singular"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Reservations.ts"],"sourcesContent":["import type {\n CollectionAfterChangeHook,\n CollectionBeforeChangeHook,\n CollectionConfig,\n CollectionSlug,\n} from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ReservationPluginHooks, ResolvedReservationPluginConfig } from '../types.js'\n\nimport { calculateEndTime } from '../hooks/reservations/calculateEndTime.js'\nimport { checkIdempotency } from '../hooks/reservations/checkIdempotency.js'\nimport { onStatusChange } from '../hooks/reservations/onStatusChange.js'\nimport { validateCancellation } from '../hooks/reservations/validateCancellation.js'\nimport { validateConflicts } from '../hooks/reservations/validateConflicts.js'\nimport { validateStatusTransition } from '../hooks/reservations/validateStatusTransition.js'\nimport { makeReservationOwnerAccess } from '../utilities/ownerAccess.js'\n\nfunction createPluginHooksBeforeCreate(\n hooks: ReservationPluginHooks,\n): CollectionBeforeChangeHook {\n return async ({ context, data, operation, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n if (operation === 'create' && hooks.beforeBookingCreate) {\n let mutatedData = data\n for (const hook of hooks.beforeBookingCreate) {\n const result = await hook({ data: mutatedData, req })\n if (result) {mutatedData = result}\n }\n return mutatedData\n }\n\n return data\n }\n}\n\nfunction createPluginHooksAfterCreate(\n hooks: ReservationPluginHooks,\n): CollectionAfterChangeHook {\n return async ({ doc, operation, req }) => {\n if (operation === 'create' && hooks.afterBookingCreate) {\n const docRecord = doc as Record<string, unknown>\n for (const hook of hooks.afterBookingCreate) {\n await hook({ doc: docRecord, req })\n }\n }\n return doc\n }\n}\n\nexport function createReservationsCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n const { statusMachine } = config\n const rom = config.resourceOwnerMode\n const access =\n config.access.reservations ?? (rom ? makeReservationOwnerAccess(rom) : {})\n\n return {\n slug: config.slugs.reservations,\n access,\n admin: {\n components: {\n views: {\n list: {\n Component: 'payload-reserve/client#CalendarView',\n },\n },\n },\n group: config.adminGroup,\n listSearchableFields: ['status'],\n useAsTitle: 'startTime',\n },\n fields: [\n {\n name: 'service',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldService'),\n relationTo: config.slugs.services as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'resource',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldResource'),\n relationTo: config.slugs.resources as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'customer',\n type: 'relationship',\n admin: {\n allowCreate: true,\n allowEdit: true,\n components: {\n Field: 'payload-reserve/client#CustomerField',\n },\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldCustomer'),\n relationTo: config.slugs.customers as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'startTime',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayAndTime',\n },\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldStartTime'),\n required: true,\n },\n {\n name: 'endTime',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayAndTime',\n },\n readOnly: true,\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldEndTime'),\n },\n {\n name: 'status',\n type: 'select',\n defaultValue: statusMachine.defaultStatus,\n label: ({ t }) => (t as PluginT)('reservation:fieldStatus'),\n options: statusMachine.statuses.map((s) => ({\n label: ({ t }) => {\n const key = `reservation:status${s.charAt(0).toUpperCase() + s.slice(1)}`\n const translated = (t as PluginT)(key)\n return translated !== key ? translated : s.charAt(0).toUpperCase() + s.slice(1)\n },\n value: s,\n })),\n },\n {\n name: 'cancellationReason',\n type: 'textarea',\n admin: {\n condition: (_, siblingData) => siblingData?.status === 'cancelled',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldCancellationReason'),\n },\n {\n name: 'guestCount',\n type: 'number',\n defaultValue: 1,\n label: ({ t }) => (t as PluginT)('reservation:fieldGuestCount'),\n min: 1,\n },\n {\n name: 'notes',\n type: 'textarea',\n label: ({ t }) => (t as PluginT)('reservation:fieldNotes'),\n },\n {\n name: 'items',\n type: 'array',\n admin: {\n description: 'Resources included in this booking. Leave empty for single-resource bookings.',\n },\n fields: [\n {\n name: 'resource',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldResource'),\n relationTo: config.slugs.resources as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'service',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldService'),\n relationTo: config.slugs.services as unknown as CollectionSlug,\n },\n {\n name: 'startTime',\n type: 'date',\n admin: { date: { pickerAppearance: 'dayAndTime' } },\n label: ({ t }) => (t as PluginT)('reservation:fieldStartTime'),\n },\n {\n name: 'endTime',\n type: 'date',\n admin: { date: { pickerAppearance: 'dayAndTime' }, readOnly: false },\n label: ({ t }) => (t as PluginT)('reservation:fieldEndTime'),\n },\n {\n name: 'guestCount',\n type: 'number',\n label: ({ t }) => (t as PluginT)('reservation:fieldGuestCount'),\n min: 1,\n },\n ],\n label: ({ t }) => (t as PluginT)('reservation:fieldItems'),\n },\n {\n name: 'idempotencyKey',\n type: 'text',\n admin: { position: 'sidebar', readOnly: true },\n index: true,\n unique: true,\n },\n ...config.extraReservationFields,\n ],\n hooks: {\n afterChange: [\n createPluginHooksAfterCreate(config.hooks),\n onStatusChange(config),\n ],\n beforeChange: [\n createPluginHooksBeforeCreate(config.hooks),\n checkIdempotency(config),\n calculateEndTime(config),\n validateConflicts(config),\n validateStatusTransition(config),\n validateCancellation(config),\n ],\n },\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionReservations'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionReservations'),\n },\n }\n}\n"],"names":["calculateEndTime","checkIdempotency","onStatusChange","validateCancellation","validateConflicts","validateStatusTransition","makeReservationOwnerAccess","createPluginHooksBeforeCreate","hooks","context","data","operation","req","skipReservationHooks","beforeBookingCreate","mutatedData","hook","result","createPluginHooksAfterCreate","doc","afterBookingCreate","docRecord","createReservationsCollection","config","statusMachine","rom","resourceOwnerMode","access","reservations","slug","slugs","admin","components","views","list","Component","group","adminGroup","listSearchableFields","useAsTitle","fields","name","type","label","t","relationTo","services","required","resources","allowCreate","allowEdit","Field","customers","date","pickerAppearance","readOnly","defaultValue","defaultStatus","options","statuses","map","s","key","charAt","toUpperCase","slice","translated","value","condition","_","siblingData","status","min","description","position","index","unique","extraReservationFields","afterChange","beforeChange","labels","plural","singular"],"mappings":"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"}
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
+
import { makeResourceOwnerAccess } from '../utilities/ownerAccess.js';
|
|
1
2
|
export function createResourcesCollection(config) {
|
|
3
|
+
const rom = config.resourceOwnerMode;
|
|
4
|
+
const ownerField = rom?.ownerField ?? 'owner';
|
|
5
|
+
// Build the owner field when resourceOwnerMode is enabled
|
|
6
|
+
const ownerFieldDef = rom ? {
|
|
7
|
+
name: ownerField,
|
|
8
|
+
type: 'relationship',
|
|
9
|
+
admin: {
|
|
10
|
+
position: 'sidebar'
|
|
11
|
+
},
|
|
12
|
+
hooks: {
|
|
13
|
+
beforeChange: [
|
|
14
|
+
({ operation, req, value })=>{
|
|
15
|
+
if (operation === 'create' && req.user) {
|
|
16
|
+
return req.user.id;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
label: 'Owner',
|
|
23
|
+
relationTo: config.slugs.customers,
|
|
24
|
+
required: true
|
|
25
|
+
} : null;
|
|
26
|
+
// Determine access: app override → owner-mode auto-wired → unrestricted
|
|
27
|
+
const access = config.access.resources ?? (rom ? makeResourceOwnerAccess(rom) : {});
|
|
2
28
|
return {
|
|
3
29
|
slug: config.slugs.resources,
|
|
4
|
-
access
|
|
30
|
+
access,
|
|
5
31
|
admin: {
|
|
6
32
|
group: config.adminGroup,
|
|
7
33
|
useAsTitle: 'name'
|
|
@@ -47,7 +73,49 @@ export function createResourcesCollection(config) {
|
|
|
47
73
|
},
|
|
48
74
|
defaultValue: true,
|
|
49
75
|
label: ({ t })=>t('reservation:fieldActive')
|
|
50
|
-
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'quantity',
|
|
79
|
+
type: 'number',
|
|
80
|
+
admin: {
|
|
81
|
+
position: 'sidebar'
|
|
82
|
+
},
|
|
83
|
+
defaultValue: 1,
|
|
84
|
+
label: ({ t })=>t('reservation:fieldQuantity'),
|
|
85
|
+
min: 1,
|
|
86
|
+
required: true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'capacityMode',
|
|
90
|
+
type: 'select',
|
|
91
|
+
admin: {
|
|
92
|
+
condition: (data)=>(data?.quantity ?? 1) > 1,
|
|
93
|
+
position: 'sidebar'
|
|
94
|
+
},
|
|
95
|
+
defaultValue: 'per-reservation',
|
|
96
|
+
label: ({ t })=>t('reservation:fieldCapacityMode'),
|
|
97
|
+
options: [
|
|
98
|
+
{
|
|
99
|
+
label: ({ t })=>t('reservation:capacityPerReservation'),
|
|
100
|
+
value: 'per-reservation'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: ({ t })=>t('reservation:capacityPerGuest'),
|
|
104
|
+
value: 'per-guest'
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'timezone',
|
|
110
|
+
type: 'text',
|
|
111
|
+
admin: {
|
|
112
|
+
position: 'sidebar'
|
|
113
|
+
},
|
|
114
|
+
label: ({ t })=>t('reservation:fieldTimezone')
|
|
115
|
+
},
|
|
116
|
+
...ownerFieldDef ? [
|
|
117
|
+
ownerFieldDef
|
|
118
|
+
] : []
|
|
51
119
|
],
|
|
52
120
|
labels: {
|
|
53
121
|
plural: ({ t })=>t('reservation:collectionResources'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Resources.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createResourcesCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Resources.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, Field } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { makeResourceOwnerAccess } from '../utilities/ownerAccess.js'\n\nexport function createResourcesCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n const rom = config.resourceOwnerMode\n const ownerField = rom?.ownerField ?? 'owner'\n\n // Build the owner field when resourceOwnerMode is enabled\n const ownerFieldDef: Field | null = rom\n ? {\n name: ownerField,\n type: 'relationship',\n admin: {\n position: 'sidebar',\n },\n hooks: {\n beforeChange: [\n ({ operation, req, value }) => {\n if (operation === 'create' && req.user) {return req.user.id}\n return value\n },\n ],\n },\n label: 'Owner',\n relationTo: config.slugs.customers as unknown as CollectionSlug,\n required: true,\n }\n : null\n\n // Determine access: app override → owner-mode auto-wired → unrestricted\n const access =\n config.access.resources ?? (rom ? makeResourceOwnerAccess(rom) : {})\n\n return {\n slug: config.slugs.resources,\n access,\n admin: {\n group: config.adminGroup,\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldName'),\n ...(config.localized ? { localized: true } : {}),\n maxLength: 200,\n required: true,\n },\n {\n name: 'image',\n type: 'upload',\n label: ({ t }) => (t as PluginT)('reservation:fieldImage'),\n relationTo: config.slugs.media as unknown as CollectionSlug,\n },\n {\n name: 'description',\n type: 'textarea',\n label: ({ t }) => (t as PluginT)('reservation:fieldDescription'),\n ...(config.localized ? { localized: true } : {}),\n },\n {\n name: 'services',\n type: 'relationship',\n hasMany: true,\n label: ({ t }) => (t as PluginT)('reservation:fieldServices'),\n relationTo: config.slugs.services as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'active',\n type: 'checkbox',\n admin: {\n position: 'sidebar',\n },\n defaultValue: true,\n label: ({ t }) => (t as PluginT)('reservation:fieldActive'),\n },\n {\n name: 'quantity',\n type: 'number',\n admin: {\n position: 'sidebar',\n },\n defaultValue: 1,\n label: ({ t }) => (t as PluginT)('reservation:fieldQuantity'),\n min: 1,\n required: true,\n },\n {\n name: 'capacityMode',\n type: 'select',\n admin: {\n condition: (data) => (data?.quantity ?? 1) > 1,\n position: 'sidebar',\n },\n defaultValue: 'per-reservation',\n label: ({ t }) => (t as PluginT)('reservation:fieldCapacityMode'),\n options: [\n {\n label: ({ t }) => (t as PluginT)('reservation:capacityPerReservation'),\n value: 'per-reservation',\n },\n {\n label: ({ t }) => (t as PluginT)('reservation:capacityPerGuest'),\n value: 'per-guest',\n },\n ],\n },\n {\n name: 'timezone',\n type: 'text',\n admin: {\n position: 'sidebar',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldTimezone'),\n },\n ...(ownerFieldDef ? [ownerFieldDef] : []),\n ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n },\n }\n}\n"],"names":["makeResourceOwnerAccess","createResourcesCollection","config","rom","resourceOwnerMode","ownerField","ownerFieldDef","name","type","admin","position","hooks","beforeChange","operation","req","value","user","id","label","relationTo","slugs","customers","required","access","resources","slug","group","adminGroup","useAsTitle","fields","t","localized","maxLength","media","hasMany","services","defaultValue","min","condition","data","quantity","options","labels","plural","singular"],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { makeScheduleOwnerAccess } from '../utilities/ownerAccess.js';
|
|
1
2
|
export function createSchedulesCollection(config) {
|
|
3
|
+
const rom = config.resourceOwnerMode;
|
|
4
|
+
const access = config.access.schedules ?? (rom ? makeScheduleOwnerAccess(rom) : {});
|
|
2
5
|
return {
|
|
3
6
|
slug: config.slugs.schedules,
|
|
4
|
-
access
|
|
7
|
+
access,
|
|
5
8
|
admin: {
|
|
6
9
|
group: config.adminGroup,
|
|
7
10
|
useAsTitle: 'name'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Schedules.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createSchedulesCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n return {\n slug: config.slugs.schedules,\n access
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Schedules.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { makeScheduleOwnerAccess } from '../utilities/ownerAccess.js'\n\nexport function createSchedulesCollection(\n config: ResolvedReservationPluginConfig,\n): CollectionConfig {\n const rom = config.resourceOwnerMode\n const access =\n config.access.schedules ?? (rom ? makeScheduleOwnerAccess(rom) : {})\n\n return {\n slug: config.slugs.schedules,\n access,\n admin: {\n group: config.adminGroup,\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldName'),\n required: true,\n },\n {\n name: 'resource',\n type: 'relationship',\n label: ({ t }) => (t as PluginT)('reservation:fieldResource'),\n relationTo: config.slugs.resources as unknown as CollectionSlug,\n required: true,\n },\n {\n name: 'scheduleType',\n type: 'select',\n defaultValue: 'recurring',\n label: ({ t }) => (t as PluginT)('reservation:fieldScheduleType'),\n options: [\n {\n label: ({ t }) => (t as PluginT)('reservation:scheduleTypeRecurring'),\n value: 'recurring',\n },\n {\n label: ({ t }) => (t as PluginT)('reservation:scheduleTypeManual'),\n value: 'manual',\n },\n ],\n },\n {\n name: 'recurringSlots',\n type: 'array',\n admin: {\n condition: (_, siblingData) => siblingData?.scheduleType === 'recurring',\n },\n fields: [\n {\n name: 'day',\n type: 'select',\n label: ({ t }) => (t as PluginT)('reservation:fieldDay'),\n options: [\n { label: ({ t }) => (t as PluginT)('reservation:dayMonday'), value: 'mon' },\n { label: ({ t }) => (t as PluginT)('reservation:dayTuesday'), value: 'tue' },\n { label: ({ t }) => (t as PluginT)('reservation:dayWednesday'), value: 'wed' },\n { label: ({ t }) => (t as PluginT)('reservation:dayThursday'), value: 'thu' },\n { label: ({ t }) => (t as PluginT)('reservation:dayFriday'), value: 'fri' },\n { label: ({ t }) => (t as PluginT)('reservation:daySaturday'), value: 'sat' },\n { label: ({ t }) => (t as PluginT)('reservation:daySunday'), value: 'sun' },\n ],\n required: true,\n },\n {\n name: 'startTime',\n type: 'text',\n admin: {\n placeholder: '09:00',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldStartTimeHHmm'),\n required: true,\n },\n {\n name: 'endTime',\n type: 'text',\n admin: {\n placeholder: '17:00',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldEndTimeHHmm'),\n required: true,\n },\n ],\n label: ({ t }) => (t as PluginT)('reservation:fieldRecurringSlots'),\n },\n {\n name: 'manualSlots',\n type: 'array',\n admin: {\n condition: (_, siblingData) => siblingData?.scheduleType === 'manual',\n },\n fields: [\n {\n name: 'date',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayOnly',\n },\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldDate'),\n required: true,\n },\n {\n name: 'startTime',\n type: 'text',\n admin: {\n placeholder: '09:00',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldStartTimeHHmm'),\n required: true,\n },\n {\n name: 'endTime',\n type: 'text',\n admin: {\n placeholder: '17:00',\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldEndTimeHHmm'),\n required: true,\n },\n ],\n label: ({ t }) => (t as PluginT)('reservation:fieldManualSlots'),\n },\n {\n name: 'exceptions',\n type: 'array',\n fields: [\n {\n name: 'date',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayOnly',\n },\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldDate'),\n required: true,\n },\n {\n name: 'reason',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldReason'),\n },\n ],\n label: ({ t }) => (t as PluginT)('reservation:fieldExceptions'),\n },\n {\n name: 'active',\n type: 'checkbox',\n admin: {\n position: 'sidebar',\n },\n defaultValue: true,\n label: ({ t }) => (t as PluginT)('reservation:fieldActive'),\n },\n ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionSchedules'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionSchedules'),\n },\n }\n}\n"],"names":["makeScheduleOwnerAccess","createSchedulesCollection","config","rom","resourceOwnerMode","access","schedules","slug","slugs","admin","group","adminGroup","useAsTitle","fields","name","type","label","t","required","relationTo","resources","defaultValue","options","value","condition","_","siblingData","scheduleType","placeholder","date","pickerAppearance","position","labels","plural","singular"],"mappings":"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"}
|
|
@@ -1,7 +1,34 @@
|
|
|
1
|
+
import { makeServiceOwnerAccess } from '../utilities/ownerAccess.js';
|
|
1
2
|
export function createServicesCollection(config) {
|
|
3
|
+
const rom = config.resourceOwnerMode;
|
|
4
|
+
const ownedServices = rom?.ownedServices ?? false;
|
|
5
|
+
const ownerField = rom?.ownerField ?? 'owner';
|
|
6
|
+
// Owner field on Services (only when ownedServices: true)
|
|
7
|
+
const ownerFieldDef = rom && ownedServices ? {
|
|
8
|
+
name: ownerField,
|
|
9
|
+
type: 'relationship',
|
|
10
|
+
admin: {
|
|
11
|
+
position: 'sidebar'
|
|
12
|
+
},
|
|
13
|
+
hooks: {
|
|
14
|
+
beforeChange: [
|
|
15
|
+
({ operation, req, value })=>{
|
|
16
|
+
if (operation === 'create' && req.user) {
|
|
17
|
+
return req.user.id;
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
label: 'Owner',
|
|
24
|
+
relationTo: config.slugs.customers,
|
|
25
|
+
required: true
|
|
26
|
+
} : null;
|
|
27
|
+
// Determine access: app override → owner-mode auto-wired → unrestricted
|
|
28
|
+
const access = config.access.services ?? (rom && ownedServices ? makeServiceOwnerAccess(rom, ownerField) : {});
|
|
2
29
|
return {
|
|
3
30
|
slug: config.slugs.services,
|
|
4
|
-
access
|
|
31
|
+
access,
|
|
5
32
|
admin: {
|
|
6
33
|
group: config.adminGroup,
|
|
7
34
|
useAsTitle: 'name'
|
|
@@ -17,6 +44,12 @@ export function createServicesCollection(config) {
|
|
|
17
44
|
maxLength: 200,
|
|
18
45
|
required: true
|
|
19
46
|
},
|
|
47
|
+
{
|
|
48
|
+
name: 'image',
|
|
49
|
+
type: 'upload',
|
|
50
|
+
label: ({ t })=>t('reservation:fieldImage'),
|
|
51
|
+
relationTo: config.slugs.media
|
|
52
|
+
},
|
|
20
53
|
{
|
|
21
54
|
name: 'description',
|
|
22
55
|
type: 'textarea',
|
|
@@ -32,6 +65,27 @@ export function createServicesCollection(config) {
|
|
|
32
65
|
min: 1,
|
|
33
66
|
required: true
|
|
34
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: 'durationType',
|
|
70
|
+
type: 'select',
|
|
71
|
+
defaultValue: 'fixed',
|
|
72
|
+
label: ({ t })=>t('reservation:fieldDurationType'),
|
|
73
|
+
options: [
|
|
74
|
+
{
|
|
75
|
+
label: ({ t })=>t('reservation:durationFixed'),
|
|
76
|
+
value: 'fixed'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: ({ t })=>t('reservation:durationFlexible'),
|
|
80
|
+
value: 'flexible'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: ({ t })=>t('reservation:durationFullDay'),
|
|
84
|
+
value: 'full-day'
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
required: true
|
|
88
|
+
},
|
|
35
89
|
{
|
|
36
90
|
name: 'price',
|
|
37
91
|
type: 'number',
|
|
@@ -63,7 +117,10 @@ export function createServicesCollection(config) {
|
|
|
63
117
|
},
|
|
64
118
|
defaultValue: true,
|
|
65
119
|
label: ({ t })=>t('reservation:fieldActive')
|
|
66
|
-
}
|
|
120
|
+
},
|
|
121
|
+
...ownerFieldDef ? [
|
|
122
|
+
ownerFieldDef
|
|
123
|
+
] : []
|
|
67
124
|
],
|
|
68
125
|
labels: {
|
|
69
126
|
plural: ({ t })=>t('reservation:collectionServices'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Services.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createServicesCollection(config: ResolvedReservationPluginConfig): CollectionConfig {\n
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Services.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug, Field } from 'payload'\n\nimport type { PluginT } from '../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { makeServiceOwnerAccess } from '../utilities/ownerAccess.js'\n\nexport function createServicesCollection(config: ResolvedReservationPluginConfig): CollectionConfig {\n const rom = config.resourceOwnerMode\n const ownedServices = rom?.ownedServices ?? false\n const ownerField = rom?.ownerField ?? 'owner'\n\n // Owner field on Services (only when ownedServices: true)\n const ownerFieldDef: Field | null =\n rom && ownedServices\n ? {\n name: ownerField,\n type: 'relationship',\n admin: {\n position: 'sidebar',\n },\n hooks: {\n beforeChange: [\n ({ operation, req, value }) => {\n if (operation === 'create' && req.user) {return req.user.id}\n return value\n },\n ],\n },\n label: 'Owner',\n relationTo: config.slugs.customers as unknown as CollectionSlug,\n required: true,\n }\n : null\n\n // Determine access: app override → owner-mode auto-wired → unrestricted\n const access =\n config.access.services ??\n (rom && ownedServices ? makeServiceOwnerAccess(rom, ownerField) : {})\n\n return {\n slug: config.slugs.services,\n access,\n admin: {\n group: config.adminGroup,\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n label: ({ t }) => (t as PluginT)('reservation:fieldName'),\n ...(config.localized ? { localized: true } : {}),\n maxLength: 200,\n required: true,\n },\n {\n name: 'image',\n type: 'upload',\n label: ({ t }) => (t as PluginT)('reservation:fieldImage'),\n relationTo: config.slugs.media as unknown as CollectionSlug,\n },\n {\n name: 'description',\n type: 'textarea',\n label: ({ t }) => (t as PluginT)('reservation:fieldDescription'),\n ...(config.localized ? { localized: true } : {}),\n },\n {\n name: 'duration',\n type: 'number',\n label: ({ t }) => (t as PluginT)('reservation:fieldDurationMinutes'),\n min: 1,\n required: true,\n },\n {\n name: 'durationType',\n type: 'select',\n defaultValue: 'fixed',\n label: ({ t }) => (t as PluginT)('reservation:fieldDurationType'),\n options: [\n {\n label: ({ t }) => (t as PluginT)('reservation:durationFixed'),\n value: 'fixed',\n },\n {\n label: ({ t }) => (t as PluginT)('reservation:durationFlexible'),\n value: 'flexible',\n },\n {\n label: ({ t }) => (t as PluginT)('reservation:durationFullDay'),\n value: 'full-day',\n },\n ],\n required: true,\n },\n {\n name: 'price',\n type: 'number',\n admin: {\n step: 0.01,\n },\n label: ({ t }) => (t as PluginT)('reservation:fieldPrice'),\n min: 0,\n },\n {\n name: 'bufferTimeBefore',\n type: 'number',\n defaultValue: 0,\n label: ({ t }) => (t as PluginT)('reservation:fieldBufferTimeBefore'),\n min: 0,\n },\n {\n name: 'bufferTimeAfter',\n type: 'number',\n defaultValue: 0,\n label: ({ t }) => (t as PluginT)('reservation:fieldBufferTimeAfter'),\n min: 0,\n },\n {\n name: 'active',\n type: 'checkbox',\n admin: {\n position: 'sidebar',\n },\n defaultValue: true,\n label: ({ t }) => (t as PluginT)('reservation:fieldActive'),\n },\n ...(ownerFieldDef ? [ownerFieldDef] : []),\n ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n },\n }\n}\n"],"names":["makeServiceOwnerAccess","createServicesCollection","config","rom","resourceOwnerMode","ownedServices","ownerField","ownerFieldDef","name","type","admin","position","hooks","beforeChange","operation","req","value","user","id","label","relationTo","slugs","customers","required","access","services","slug","group","adminGroup","useAsTitle","fields","t","localized","maxLength","media","min","defaultValue","options","step","labels","plural","singular"],"mappings":"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"}
|