payload-reserve 1.0.3 → 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 +32 -2
- package/dist/collections/Reservations.js +6 -2
- package/dist/collections/Reservations.js.map +1 -1
- package/dist/collections/Resources.js +31 -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 +32 -2
- package/dist/collections/Services.js.map +1 -1
- package/dist/defaults.js +7 -0
- package/dist/defaults.js.map +1 -1
- package/dist/hooks/reservations/onStatusChange.js +4 -1
- package/dist/hooks/reservations/onStatusChange.js.map +1 -1
- package/dist/types.d.ts +20 -1
- 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/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Designed for salons, clinics, hotels, restaurants, event venues, and any busines
|
|
|
10
10
|
|
|
11
11
|
- **5 Domain Collections** — Services, Resources, Schedules, Reservations, and Customers (standalone or user-collection extension)
|
|
12
12
|
- **User Collection Extension** — Optionally extend your existing auth collection with booking fields; set `userCollection: undefined` (default) to use a standalone Customers collection
|
|
13
|
+
- **Resource Owner Multi-Tenancy** — Opt-in `resourceOwnerMode` wires ownership access control so each resource owner (host) sees only their own listings and reservations
|
|
13
14
|
- **Configurable Status Machine** — Define your own statuses, transitions, blocking states, and terminal states
|
|
14
15
|
- **Double-Booking Prevention** — Server-side conflict detection with configurable buffer times; respects capacity modes
|
|
15
16
|
- **Auto End Time** — Calculates `endTime` from `startTime + service.duration` automatically
|
|
@@ -17,6 +18,7 @@ Designed for salons, clinics, hotels, restaurants, event venues, and any busines
|
|
|
17
18
|
- **Multi-Resource Bookings** — Single reservation that spans multiple resources simultaneously via the `items` array
|
|
18
19
|
- **Capacity and Inventory** — `quantity > 1` allows multiple concurrent bookings per resource; `capacityMode` (`per-reservation` | `per-guest`) controls how capacity is counted
|
|
19
20
|
- **Idempotency** — Optional `idempotencyKey` prevents duplicate submissions
|
|
21
|
+
- **Extra Reservation Fields** — Inject custom fields into the Reservations collection via `extraReservationFields` without forking the plugin
|
|
20
22
|
- **Cancellation Policy** — Configurable minimum notice period enforcement
|
|
21
23
|
- **Plugin Hooks API** — Seven lifecycle hooks (`beforeBookingCreate`, `afterBookingCreate`, `beforeBookingConfirm`, `afterBookingConfirm`, `beforeBookingCancel`, `afterBookingCancel`, `afterStatusChange`) for integrating email, Stripe, and external systems
|
|
22
24
|
- **Availability Service** — Pure functions and DB helpers for slot generation and conflict checking
|
|
@@ -58,6 +60,34 @@ export default buildConfig({
|
|
|
58
60
|
|
|
59
61
|
---
|
|
60
62
|
|
|
63
|
+
## Resource Owner Multi-Tenancy
|
|
64
|
+
|
|
65
|
+
Enable `resourceOwnerMode` to support Airbnb-style platforms where each user manages their own listings (Resources) and sees only the reservations made against them. This is opt-in — single-tenant installs are unaffected.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
payloadReserve({
|
|
69
|
+
userCollection: 'users', // required: which auth collection holds owners
|
|
70
|
+
resourceOwnerMode: {
|
|
71
|
+
adminRoles: ['admin'], // roles that bypass all filters (see all records)
|
|
72
|
+
ownerField: 'owner', // field name added to Resources (default: 'owner')
|
|
73
|
+
ownedServices: false, // set true if Services should also be owner-scoped
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**What this does automatically:**
|
|
79
|
+
|
|
80
|
+
| Collection | Behaviour |
|
|
81
|
+
|------------|-----------|
|
|
82
|
+
| Resources | Adds an `owner` relationship field (auto-populated on create); owners read/update/delete only their own records |
|
|
83
|
+
| Schedules | Owners read/update/delete only schedules whose resource they own (join through `resource.owner`) |
|
|
84
|
+
| Reservations | Owners can read reservations for their resources; mutations are admin-only |
|
|
85
|
+
| Services | Unchanged by default; set `ownedServices: true` to apply the same owner pattern |
|
|
86
|
+
|
|
87
|
+
The `access` override in plugin config always takes precedence over the auto-wired functions, so you can fine-tune any collection without losing the rest.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
61
91
|
## Documentation
|
|
62
92
|
|
|
63
93
|
> The docs below live in the [GitHub repository](https://github.com/elghaied/payload-reserve/tree/main/docs) and are not included in the published npm package.
|
|
@@ -65,13 +95,13 @@ export default buildConfig({
|
|
|
65
95
|
| Topic | Contents |
|
|
66
96
|
|-------|----------|
|
|
67
97
|
| [Getting Started](https://github.com/elghaied/payload-reserve/blob/main/docs/getting-started.md) | Installation, quick start, what gets created |
|
|
68
|
-
| [Configuration](https://github.com/elghaied/payload-reserve/blob/main/docs/configuration.md) | All plugin options with types and defaults |
|
|
98
|
+
| [Configuration](https://github.com/elghaied/payload-reserve/blob/main/docs/configuration.md) | All plugin options with types and defaults, including `resourceOwnerMode` |
|
|
69
99
|
| [Collections](https://github.com/elghaied/payload-reserve/blob/main/docs/collections.md) | Services, Resources, Schedules, Customers, Reservations schemas |
|
|
70
100
|
| [Status Machine](https://github.com/elghaied/payload-reserve/blob/main/docs/status-machine.md) | Default flow, custom machines, business logic hooks, escape hatch |
|
|
71
101
|
| [Booking Features](https://github.com/elghaied/payload-reserve/blob/main/docs/booking-features.md) | Duration types, multi-resource bookings, capacity modes |
|
|
72
102
|
| [Hooks API](https://github.com/elghaied/payload-reserve/blob/main/docs/hooks-api.md) | All 7 plugin hook types with signatures and examples |
|
|
73
103
|
| [REST API](https://github.com/elghaied/payload-reserve/blob/main/docs/rest-api.md) | All 5 public endpoints with params, responses, and fetch examples |
|
|
74
104
|
| [Admin UI](https://github.com/elghaied/payload-reserve/blob/main/docs/admin-ui.md) | Calendar view, dashboard widget, availability overview |
|
|
75
|
-
| [Examples](https://github.com/elghaied/payload-reserve/blob/main/docs/examples.md) | Salon, hotel, restaurant, event venue, Stripe, email, multi-tenant |
|
|
105
|
+
| [Examples](https://github.com/elghaied/payload-reserve/blob/main/docs/examples.md) | Salon, hotel, restaurant, event venue, Stripe, email, multi-tenant (resource owner mode) |
|
|
76
106
|
| [Advanced](https://github.com/elghaied/payload-reserve/blob/main/docs/advanced.md) | DB indexes, reconciliation job for race condition detection |
|
|
77
107
|
| [Development](https://github.com/elghaied/payload-reserve/blob/main/docs/development.md) | Prerequisites, commands, project file tree |
|
|
@@ -4,6 +4,7 @@ import { onStatusChange } from '../hooks/reservations/onStatusChange.js';
|
|
|
4
4
|
import { validateCancellation } from '../hooks/reservations/validateCancellation.js';
|
|
5
5
|
import { validateConflicts } from '../hooks/reservations/validateConflicts.js';
|
|
6
6
|
import { validateStatusTransition } from '../hooks/reservations/validateStatusTransition.js';
|
|
7
|
+
import { makeReservationOwnerAccess } from '../utilities/ownerAccess.js';
|
|
7
8
|
function createPluginHooksBeforeCreate(hooks) {
|
|
8
9
|
return async ({ context, data, operation, req })=>{
|
|
9
10
|
if (context?.skipReservationHooks) {
|
|
@@ -41,9 +42,11 @@ function createPluginHooksAfterCreate(hooks) {
|
|
|
41
42
|
}
|
|
42
43
|
export function createReservationsCollection(config) {
|
|
43
44
|
const { statusMachine } = config;
|
|
45
|
+
const rom = config.resourceOwnerMode;
|
|
46
|
+
const access = config.access.reservations ?? (rom ? makeReservationOwnerAccess(rom) : {});
|
|
44
47
|
return {
|
|
45
48
|
slug: config.slugs.reservations,
|
|
46
|
-
access
|
|
49
|
+
access,
|
|
47
50
|
admin: {
|
|
48
51
|
components: {
|
|
49
52
|
views: {
|
|
@@ -202,7 +205,8 @@ export function createReservationsCollection(config) {
|
|
|
202
205
|
},
|
|
203
206
|
index: true,
|
|
204
207
|
unique: true
|
|
205
|
-
}
|
|
208
|
+
},
|
|
209
|
+
...config.extraReservationFields
|
|
206
210
|
],
|
|
207
211
|
hooks: {
|
|
208
212
|
afterChange: [
|
|
@@ -1 +1 @@
|
|
|
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'\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\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 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 ],\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","createPluginHooksBeforeCreate","hooks","context","data","operation","req","skipReservationHooks","beforeBookingCreate","mutatedData","hook","result","createPluginHooksAfterCreate","doc","afterBookingCreate","docRecord","createReservationsCollection","config","statusMachine","slug","slugs","reservations","access","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","afterChange","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'
|
|
@@ -86,7 +112,10 @@ export function createResourcesCollection(config) {
|
|
|
86
112
|
position: 'sidebar'
|
|
87
113
|
},
|
|
88
114
|
label: ({ t })=>t('reservation:fieldTimezone')
|
|
89
|
-
}
|
|
115
|
+
},
|
|
116
|
+
...ownerFieldDef ? [
|
|
117
|
+
ownerFieldDef
|
|
118
|
+
] : []
|
|
90
119
|
],
|
|
91
120
|
labels: {
|
|
92
121
|
plural: ({ t })=>t('reservation:collectionResources'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Resources.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 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, CollectionSlug } 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'
|
|
@@ -90,7 +117,10 @@ export function createServicesCollection(config) {
|
|
|
90
117
|
},
|
|
91
118
|
defaultValue: true,
|
|
92
119
|
label: ({ t })=>t('reservation:fieldActive')
|
|
93
|
-
}
|
|
120
|
+
},
|
|
121
|
+
...ownerFieldDef ? [
|
|
122
|
+
ownerFieldDef
|
|
123
|
+
] : []
|
|
94
124
|
],
|
|
95
125
|
labels: {
|
|
96
126
|
plural: ({ t })=>t('reservation:collectionServices'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Services.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 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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"}
|
package/dist/defaults.js
CHANGED
|
@@ -12,14 +12,21 @@ export const DEFAULT_BUFFER_TIME = 0;
|
|
|
12
12
|
export const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24;
|
|
13
13
|
export function resolveConfig(pluginOptions) {
|
|
14
14
|
const userStatusMachine = pluginOptions.statusMachine;
|
|
15
|
+
const rom = pluginOptions.resourceOwnerMode;
|
|
15
16
|
return {
|
|
16
17
|
access: pluginOptions.access ?? {},
|
|
17
18
|
adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,
|
|
18
19
|
cancellationNoticePeriod: pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,
|
|
19
20
|
defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,
|
|
20
21
|
disabled: pluginOptions.disabled ?? false,
|
|
22
|
+
extraReservationFields: pluginOptions.extraReservationFields ?? [],
|
|
21
23
|
hooks: pluginOptions.hooks ?? {},
|
|
22
24
|
localized: false,
|
|
25
|
+
resourceOwnerMode: rom ? {
|
|
26
|
+
adminRoles: rom.adminRoles ?? [],
|
|
27
|
+
ownedServices: rom.ownedServices ?? false,
|
|
28
|
+
ownerField: rom.ownerField ?? 'owner'
|
|
29
|
+
} : undefined,
|
|
23
30
|
slugs: {
|
|
24
31
|
customers: pluginOptions.slugs?.customers ?? DEFAULT_SLUGS.customers,
|
|
25
32
|
media: pluginOptions.slugs?.media ?? DEFAULT_SLUGS.media,
|
package/dist/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js'\n\nimport { DEFAULT_STATUS_MACHINE } from './types.js'\n\nexport const DEFAULT_SLUGS = {\n customers: 'customers',\n media: 'media',\n reservations: 'reservations',\n resources: 'resources',\n schedules: 'schedules',\n services: 'services',\n} as const\n\nexport const DEFAULT_ADMIN_GROUP = 'Reservations'\nexport const DEFAULT_BUFFER_TIME = 0\nexport const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24\n\nexport function resolveConfig(\n pluginOptions: ReservationPluginConfig,\n): ResolvedReservationPluginConfig {\n const userStatusMachine = pluginOptions.statusMachine\n return {\n access: pluginOptions.access ?? {},\n adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,\n cancellationNoticePeriod:\n pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,\n defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,\n disabled: pluginOptions.disabled ?? false,\n hooks: pluginOptions.hooks ?? {},\n localized: false,\n slugs: {\n customers: pluginOptions.slugs?.customers ?? DEFAULT_SLUGS.customers,\n media: pluginOptions.slugs?.media ?? DEFAULT_SLUGS.media,\n reservations: pluginOptions.slugs?.reservations ?? DEFAULT_SLUGS.reservations,\n resources: pluginOptions.slugs?.resources ?? DEFAULT_SLUGS.resources,\n schedules: pluginOptions.slugs?.schedules ?? DEFAULT_SLUGS.schedules,\n services: pluginOptions.slugs?.services ?? DEFAULT_SLUGS.services,\n },\n statusMachine: userStatusMachine\n ? {\n blockingStatuses:\n userStatusMachine.blockingStatuses ?? DEFAULT_STATUS_MACHINE.blockingStatuses,\n defaultStatus: userStatusMachine.defaultStatus ?? DEFAULT_STATUS_MACHINE.defaultStatus,\n statuses: userStatusMachine.statuses ?? DEFAULT_STATUS_MACHINE.statuses,\n terminalStatuses:\n userStatusMachine.terminalStatuses ?? DEFAULT_STATUS_MACHINE.terminalStatuses,\n transitions: userStatusMachine.transitions ?? DEFAULT_STATUS_MACHINE.transitions,\n }\n : { ...DEFAULT_STATUS_MACHINE },\n userCollection: pluginOptions.userCollection ?? undefined,\n }\n}\n"],"names":["DEFAULT_STATUS_MACHINE","DEFAULT_SLUGS","customers","media","reservations","resources","schedules","services","DEFAULT_ADMIN_GROUP","DEFAULT_BUFFER_TIME","DEFAULT_CANCELLATION_NOTICE_PERIOD","resolveConfig","pluginOptions","userStatusMachine","statusMachine","access","adminGroup","cancellationNoticePeriod","defaultBufferTime","disabled","hooks","localized","slugs","blockingStatuses","defaultStatus","statuses","terminalStatuses","transitions","userCollection"
|
|
1
|
+
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js'\n\nimport { DEFAULT_STATUS_MACHINE } from './types.js'\n\nexport const DEFAULT_SLUGS = {\n customers: 'customers',\n media: 'media',\n reservations: 'reservations',\n resources: 'resources',\n schedules: 'schedules',\n services: 'services',\n} as const\n\nexport const DEFAULT_ADMIN_GROUP = 'Reservations'\nexport const DEFAULT_BUFFER_TIME = 0\nexport const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24\n\nexport function resolveConfig(\n pluginOptions: ReservationPluginConfig,\n): ResolvedReservationPluginConfig {\n const userStatusMachine = pluginOptions.statusMachine\n const rom = pluginOptions.resourceOwnerMode\n return {\n access: pluginOptions.access ?? {},\n adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,\n cancellationNoticePeriod:\n pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,\n defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,\n disabled: pluginOptions.disabled ?? false,\n extraReservationFields: pluginOptions.extraReservationFields ?? [],\n hooks: pluginOptions.hooks ?? {},\n localized: false,\n resourceOwnerMode: rom\n ? {\n adminRoles: rom.adminRoles ?? [],\n ownedServices: rom.ownedServices ?? false,\n ownerField: rom.ownerField ?? 'owner',\n }\n : undefined,\n slugs: {\n customers: pluginOptions.slugs?.customers ?? DEFAULT_SLUGS.customers,\n media: pluginOptions.slugs?.media ?? DEFAULT_SLUGS.media,\n reservations: pluginOptions.slugs?.reservations ?? DEFAULT_SLUGS.reservations,\n resources: pluginOptions.slugs?.resources ?? DEFAULT_SLUGS.resources,\n schedules: pluginOptions.slugs?.schedules ?? DEFAULT_SLUGS.schedules,\n services: pluginOptions.slugs?.services ?? DEFAULT_SLUGS.services,\n },\n statusMachine: userStatusMachine\n ? {\n blockingStatuses:\n userStatusMachine.blockingStatuses ?? DEFAULT_STATUS_MACHINE.blockingStatuses,\n defaultStatus: userStatusMachine.defaultStatus ?? DEFAULT_STATUS_MACHINE.defaultStatus,\n statuses: userStatusMachine.statuses ?? DEFAULT_STATUS_MACHINE.statuses,\n terminalStatuses:\n userStatusMachine.terminalStatuses ?? DEFAULT_STATUS_MACHINE.terminalStatuses,\n transitions: userStatusMachine.transitions ?? DEFAULT_STATUS_MACHINE.transitions,\n }\n : { ...DEFAULT_STATUS_MACHINE },\n userCollection: pluginOptions.userCollection ?? undefined,\n }\n}\n"],"names":["DEFAULT_STATUS_MACHINE","DEFAULT_SLUGS","customers","media","reservations","resources","schedules","services","DEFAULT_ADMIN_GROUP","DEFAULT_BUFFER_TIME","DEFAULT_CANCELLATION_NOTICE_PERIOD","resolveConfig","pluginOptions","userStatusMachine","statusMachine","rom","resourceOwnerMode","access","adminGroup","cancellationNoticePeriod","defaultBufferTime","disabled","extraReservationFields","hooks","localized","adminRoles","ownedServices","ownerField","undefined","slugs","blockingStatuses","defaultStatus","statuses","terminalStatuses","transitions","userCollection"],"mappings":"AAEA,SAASA,sBAAsB,QAAQ,aAAY;AAEnD,OAAO,MAAMC,gBAAgB;IAC3BC,WAAW;IACXC,OAAO;IACPC,cAAc;IACdC,WAAW;IACXC,WAAW;IACXC,UAAU;AACZ,EAAU;AAEV,OAAO,MAAMC,sBAAsB,eAAc;AACjD,OAAO,MAAMC,sBAAsB,EAAC;AACpC,OAAO,MAAMC,qCAAqC,GAAE;AAEpD,OAAO,SAASC,cACdC,aAAsC;IAEtC,MAAMC,oBAAoBD,cAAcE,aAAa;IACrD,MAAMC,MAAMH,cAAcI,iBAAiB;IAC3C,OAAO;QACLC,QAAQL,cAAcK,MAAM,IAAI,CAAC;QACjCC,YAAYN,cAAcM,UAAU,IAAIV;QACxCW,0BACEP,cAAcO,wBAAwB,IAAIT;QAC5CU,mBAAmBR,cAAcQ,iBAAiB,IAAIX;QACtDY,UAAUT,cAAcS,QAAQ,IAAI;QACpCC,wBAAwBV,cAAcU,sBAAsB,IAAI,EAAE;QAClEC,OAAOX,cAAcW,KAAK,IAAI,CAAC;QAC/BC,WAAW;QACXR,mBAAmBD,MACf;YACEU,YAAYV,IAAIU,UAAU,IAAI,EAAE;YAChCC,eAAeX,IAAIW,aAAa,IAAI;YACpCC,YAAYZ,IAAIY,UAAU,IAAI;QAChC,IACAC;QACJC,OAAO;YACL3B,WAAWU,cAAciB,KAAK,EAAE3B,aAAaD,cAAcC,SAAS;YACpEC,OAAOS,cAAciB,KAAK,EAAE1B,SAASF,cAAcE,KAAK;YACxDC,cAAcQ,cAAciB,KAAK,EAAEzB,gBAAgBH,cAAcG,YAAY;YAC7EC,WAAWO,cAAciB,KAAK,EAAExB,aAAaJ,cAAcI,SAAS;YACpEC,WAAWM,cAAciB,KAAK,EAAEvB,aAAaL,cAAcK,SAAS;YACpEC,UAAUK,cAAciB,KAAK,EAAEtB,YAAYN,cAAcM,QAAQ;QACnE;QACAO,eAAeD,oBACX;YACEiB,kBACEjB,kBAAkBiB,gBAAgB,IAAI9B,uBAAuB8B,gBAAgB;YAC/EC,eAAelB,kBAAkBkB,aAAa,IAAI/B,uBAAuB+B,aAAa;YACtFC,UAAUnB,kBAAkBmB,QAAQ,IAAIhC,uBAAuBgC,QAAQ;YACvEC,kBACEpB,kBAAkBoB,gBAAgB,IAAIjC,uBAAuBiC,gBAAgB;YAC/EC,aAAarB,kBAAkBqB,WAAW,IAAIlC,uBAAuBkC,WAAW;QAClF,IACA;YAAE,GAAGlC,sBAAsB;QAAC;QAChCmC,gBAAgBvB,cAAcuB,cAAc,IAAIP;IAClD;AACF"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export const onStatusChange = (config)=>async ({ doc, previousDoc, req })=>{
|
|
1
|
+
export const onStatusChange = (config)=>async ({ context, doc, previousDoc, req })=>{
|
|
2
|
+
if (context?.skipReservationHooks) {
|
|
3
|
+
return doc;
|
|
4
|
+
}
|
|
2
5
|
if (!previousDoc || previousDoc.status === doc.status) {
|
|
3
6
|
return doc;
|
|
4
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/reservations/onStatusChange.ts"],"sourcesContent":["import type { CollectionAfterChangeHook } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nexport const onStatusChange =\n (config: ResolvedReservationPluginConfig): CollectionAfterChangeHook =>\n async ({ doc, previousDoc, req }) => {\n if (!previousDoc || previousDoc.status === doc.status) {return doc}\n\n const prev = previousDoc.status as string\n const next = doc.status as string\n\n // Call generic afterStatusChange plugin hooks\n if (config.hooks?.afterStatusChange) {\n for (const hook of config.hooks.afterStatusChange) {\n await hook({ doc: doc as Record<string, unknown>, newStatus: next, previousStatus: prev, req })\n }\n }\n\n // Call specific hooks based on transition\n if (next === 'confirmed' && config.hooks?.afterBookingConfirm) {\n for (const hook of config.hooks.afterBookingConfirm) {\n await hook({ doc: doc as Record<string, unknown>, req })\n }\n }\n if (next === 'cancelled' && config.hooks?.afterBookingCancel) {\n for (const hook of config.hooks.afterBookingCancel) {\n await hook({ doc: doc as Record<string, unknown>, req })\n }\n }\n\n return doc\n }\n"],"names":["onStatusChange","config","doc","previousDoc","req","status","prev","next","hooks","afterStatusChange","hook","newStatus","previousStatus","afterBookingConfirm","afterBookingCancel"],"mappings":"AAIA,OAAO,MAAMA,iBACX,CAACC,SACD,OAAO,EAAEC,GAAG,EAAEC,WAAW,EAAEC,GAAG,EAAE;
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/reservations/onStatusChange.ts"],"sourcesContent":["import type { CollectionAfterChangeHook } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nexport const onStatusChange =\n (config: ResolvedReservationPluginConfig): CollectionAfterChangeHook =>\n async ({ context, doc, previousDoc, req }) => {\n if (context?.skipReservationHooks) {return doc}\n if (!previousDoc || previousDoc.status === doc.status) {return doc}\n\n const prev = previousDoc.status as string\n const next = doc.status as string\n\n // Call generic afterStatusChange plugin hooks\n if (config.hooks?.afterStatusChange) {\n for (const hook of config.hooks.afterStatusChange) {\n await hook({ doc: doc as Record<string, unknown>, newStatus: next, previousStatus: prev, req })\n }\n }\n\n // Call specific hooks based on transition\n if (next === 'confirmed' && config.hooks?.afterBookingConfirm) {\n for (const hook of config.hooks.afterBookingConfirm) {\n await hook({ doc: doc as Record<string, unknown>, req })\n }\n }\n if (next === 'cancelled' && config.hooks?.afterBookingCancel) {\n for (const hook of config.hooks.afterBookingCancel) {\n await hook({ doc: doc as Record<string, unknown>, req })\n }\n }\n\n return doc\n }\n"],"names":["onStatusChange","config","context","doc","previousDoc","req","skipReservationHooks","status","prev","next","hooks","afterStatusChange","hook","newStatus","previousStatus","afterBookingConfirm","afterBookingCancel"],"mappings":"AAIA,OAAO,MAAMA,iBACX,CAACC,SACD,OAAO,EAAEC,OAAO,EAAEC,GAAG,EAAEC,WAAW,EAAEC,GAAG,EAAE;QACvC,IAAIH,SAASI,sBAAsB;YAAC,OAAOH;QAAG;QAC9C,IAAI,CAACC,eAAeA,YAAYG,MAAM,KAAKJ,IAAII,MAAM,EAAE;YAAC,OAAOJ;QAAG;QAElE,MAAMK,OAAOJ,YAAYG,MAAM;QAC/B,MAAME,OAAON,IAAII,MAAM;QAEvB,8CAA8C;QAC9C,IAAIN,OAAOS,KAAK,EAAEC,mBAAmB;YACnC,KAAK,MAAMC,QAAQX,OAAOS,KAAK,CAACC,iBAAiB,CAAE;gBACjD,MAAMC,KAAK;oBAAET,KAAKA;oBAAgCU,WAAWJ;oBAAMK,gBAAgBN;oBAAMH;gBAAI;YAC/F;QACF;QAEA,0CAA0C;QAC1C,IAAII,SAAS,eAAeR,OAAOS,KAAK,EAAEK,qBAAqB;YAC7D,KAAK,MAAMH,QAAQX,OAAOS,KAAK,CAACK,mBAAmB,CAAE;gBACnD,MAAMH,KAAK;oBAAET,KAAKA;oBAAgCE;gBAAI;YACxD;QACF;QACA,IAAII,SAAS,eAAeR,OAAOS,KAAK,EAAEM,oBAAoB;YAC5D,KAAK,MAAMJ,QAAQX,OAAOS,KAAK,CAACM,kBAAkB,CAAE;gBAClD,MAAMJ,KAAK;oBAAET,KAAKA;oBAAgCE;gBAAI;YACxD;QACF;QAEA,OAAOF;IACT,EAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CollectionConfig, PayloadRequest } from 'payload';
|
|
1
|
+
import type { CollectionConfig, Field, PayloadRequest } from 'payload';
|
|
2
2
|
export type DurationType = 'fixed' | 'flexible' | 'full-day';
|
|
3
3
|
export type CapacityMode = 'per-guest' | 'per-reservation';
|
|
4
4
|
export type StatusMachineConfig = {
|
|
@@ -50,6 +50,19 @@ export type ReservationPluginHooks = {
|
|
|
50
50
|
req: PayloadRequest;
|
|
51
51
|
}) => Promise<Record<string, unknown>> | Record<string, unknown>>;
|
|
52
52
|
};
|
|
53
|
+
export type ResourceOwnerModeConfig = {
|
|
54
|
+
/** Roles that can see all records (default: check req.user.collection === adminCollection) */
|
|
55
|
+
adminRoles?: string[];
|
|
56
|
+
/** Whether Services also get an owner field (default: false — Services are platform-managed) */
|
|
57
|
+
ownedServices?: boolean;
|
|
58
|
+
/** Field name for the owner relationship on Resources (default: 'owner') */
|
|
59
|
+
ownerField?: string;
|
|
60
|
+
};
|
|
61
|
+
export type ResolvedResourceOwnerModeConfig = {
|
|
62
|
+
adminRoles: string[];
|
|
63
|
+
ownedServices: boolean;
|
|
64
|
+
ownerField: string;
|
|
65
|
+
};
|
|
53
66
|
export type ReservationPluginConfig = {
|
|
54
67
|
/** Override access control per collection */
|
|
55
68
|
access?: {
|
|
@@ -67,8 +80,12 @@ export type ReservationPluginConfig = {
|
|
|
67
80
|
defaultBufferTime?: number;
|
|
68
81
|
/** Disable the plugin entirely */
|
|
69
82
|
disabled?: boolean;
|
|
83
|
+
/** Extra fields to append to the Reservations collection */
|
|
84
|
+
extraReservationFields?: Field[];
|
|
70
85
|
/** Plugin hooks for external integrations */
|
|
71
86
|
hooks?: ReservationPluginHooks;
|
|
87
|
+
/** Enable resource-owner multi-tenancy (opt-in) */
|
|
88
|
+
resourceOwnerMode?: ResourceOwnerModeConfig;
|
|
72
89
|
/** Override collection slugs */
|
|
73
90
|
slugs?: {
|
|
74
91
|
customers?: string;
|
|
@@ -95,8 +112,10 @@ export type ResolvedReservationPluginConfig = {
|
|
|
95
112
|
cancellationNoticePeriod: number;
|
|
96
113
|
defaultBufferTime: number;
|
|
97
114
|
disabled: boolean;
|
|
115
|
+
extraReservationFields: Field[];
|
|
98
116
|
hooks: ReservationPluginHooks;
|
|
99
117
|
localized: boolean;
|
|
118
|
+
resourceOwnerMode: ResolvedResourceOwnerModeConfig | undefined;
|
|
100
119
|
slugs: {
|
|
101
120
|
customers: string;
|
|
102
121
|
media: string;
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionConfig, PayloadRequest } from 'payload'\n\n// --- Duration & Capacity models ---\n\nexport type DurationType = 'fixed' | 'flexible' | 'full-day'\n\nexport type CapacityMode = 'per-guest' | 'per-reservation'\n\n// --- Configurable status machine ---\n\nexport type StatusMachineConfig = {\n blockingStatuses: string[]\n defaultStatus: string\n statuses: string[]\n terminalStatuses: string[]\n transitions: Record<string, string[]>\n}\n\nexport const DEFAULT_STATUS_MACHINE: StatusMachineConfig = {\n blockingStatuses: ['pending', 'confirmed'],\n defaultStatus: 'pending',\n statuses: ['pending', 'confirmed', 'completed', 'cancelled', 'no-show'],\n terminalStatuses: ['completed', 'cancelled', 'no-show'],\n transitions: {\n cancelled: [],\n completed: [],\n confirmed: ['completed', 'cancelled', 'no-show'],\n 'no-show': [],\n pending: ['confirmed', 'cancelled'],\n },\n}\n\n// --- Reservation item (for multi-resource bookings, Phase 3) ---\n\nexport type ReservationItemConfig = {\n endTime?: string\n guestCount?: number\n resource: string\n service?: string\n startTime?: string\n}\n\n// --- Plugin hooks for external integrations ---\n\nexport type ReservationPluginHooks = {\n afterBookingCancel?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterBookingConfirm?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterBookingCreate?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterStatusChange?: Array<\n (args: {\n doc: Record<string, unknown>\n newStatus: string\n previousStatus: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingCancel?: Array<\n (args: {\n doc: Record<string, unknown>\n reason?: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingConfirm?: Array<\n (args: {\n doc: Record<string, unknown>\n newStatus: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingCreate?: Array<\n (args: {\n data: Record<string, unknown>\n req: PayloadRequest\n }) => Promise<Record<string, unknown>> | Record<string, unknown>\n >\n}\n\n// --- Plugin configuration ---\n\nexport type ReservationPluginConfig = {\n /** Override access control per collection */\n access?: {\n customers?: CollectionConfig['access']\n reservations?: CollectionConfig['access']\n resources?: CollectionConfig['access']\n schedules?: CollectionConfig['access']\n services?: CollectionConfig['access']\n }\n /** Admin group name for all reservation collections */\n adminGroup?: string\n /** Hours of notice required before cancellation */\n cancellationNoticePeriod?: number\n /** Default buffer time in minutes between reservations */\n defaultBufferTime?: number\n /** Disable the plugin entirely */\n disabled?: boolean\n /** Plugin hooks for external integrations */\n hooks?: ReservationPluginHooks\n /** Override collection slugs */\n slugs?: {\n customers?: string\n media?: string\n reservations?: string\n resources?: string\n schedules?: string\n services?: string\n }\n /** Configurable status machine (defaults to current behavior) */\n statusMachine?: Partial<StatusMachineConfig>\n /** Which existing auth collection to extend with customer fields */\n userCollection?: string\n}\n\nexport type ResolvedReservationPluginConfig = {\n access: {\n customers?: CollectionConfig['access']\n reservations?: CollectionConfig['access']\n resources?: CollectionConfig['access']\n schedules?: CollectionConfig['access']\n services?: CollectionConfig['access']\n }\n adminGroup: string\n cancellationNoticePeriod: number\n defaultBufferTime: number\n disabled: boolean\n hooks: ReservationPluginHooks\n localized: boolean\n slugs: {\n customers: string\n media: string\n reservations: string\n resources: string\n schedules: string\n services: string\n }\n statusMachine: StatusMachineConfig\n userCollection: string | undefined\n}\n\nexport type ReservationStatus = 'cancelled' | 'completed' | 'confirmed' | 'no-show' | 'pending'\n\nexport type DayOfWeek = 'fri' | 'mon' | 'sat' | 'sun' | 'thu' | 'tue' | 'wed'\n\nexport type ScheduleType = 'manual' | 'recurring'\n\n/** @deprecated Use DEFAULT_STATUS_MACHINE.transitions instead */\nexport const VALID_STATUS_TRANSITIONS: Record<ReservationStatus, ReservationStatus[]> =\n DEFAULT_STATUS_MACHINE.transitions as Record<ReservationStatus, ReservationStatus[]>\n"],"names":["DEFAULT_STATUS_MACHINE","blockingStatuses","defaultStatus","statuses","terminalStatuses","transitions","cancelled","completed","confirmed","pending","VALID_STATUS_TRANSITIONS"],"mappings":"AAkBA,OAAO,MAAMA,yBAA8C;IACzDC,kBAAkB;QAAC;QAAW;KAAY;IAC1CC,eAAe;IACfC,UAAU;QAAC;QAAW;QAAa;QAAa;QAAa;KAAU;IACvEC,kBAAkB;QAAC;QAAa;QAAa;KAAU;IACvDC,aAAa;QACXC,WAAW,EAAE;QACbC,WAAW,EAAE;QACbC,WAAW;YAAC;YAAa;YAAa;SAAU;QAChD,WAAW,EAAE;QACbC,SAAS;YAAC;YAAa;SAAY;IACrC;AACF,EAAC;
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionConfig, Field, PayloadRequest } from 'payload'\n\n// --- Duration & Capacity models ---\n\nexport type DurationType = 'fixed' | 'flexible' | 'full-day'\n\nexport type CapacityMode = 'per-guest' | 'per-reservation'\n\n// --- Configurable status machine ---\n\nexport type StatusMachineConfig = {\n blockingStatuses: string[]\n defaultStatus: string\n statuses: string[]\n terminalStatuses: string[]\n transitions: Record<string, string[]>\n}\n\nexport const DEFAULT_STATUS_MACHINE: StatusMachineConfig = {\n blockingStatuses: ['pending', 'confirmed'],\n defaultStatus: 'pending',\n statuses: ['pending', 'confirmed', 'completed', 'cancelled', 'no-show'],\n terminalStatuses: ['completed', 'cancelled', 'no-show'],\n transitions: {\n cancelled: [],\n completed: [],\n confirmed: ['completed', 'cancelled', 'no-show'],\n 'no-show': [],\n pending: ['confirmed', 'cancelled'],\n },\n}\n\n// --- Reservation item (for multi-resource bookings, Phase 3) ---\n\nexport type ReservationItemConfig = {\n endTime?: string\n guestCount?: number\n resource: string\n service?: string\n startTime?: string\n}\n\n// --- Plugin hooks for external integrations ---\n\nexport type ReservationPluginHooks = {\n afterBookingCancel?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterBookingConfirm?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterBookingCreate?: Array<\n (args: { doc: Record<string, unknown>; req: PayloadRequest }) => Promise<void> | void\n >\n afterStatusChange?: Array<\n (args: {\n doc: Record<string, unknown>\n newStatus: string\n previousStatus: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingCancel?: Array<\n (args: {\n doc: Record<string, unknown>\n reason?: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingConfirm?: Array<\n (args: {\n doc: Record<string, unknown>\n newStatus: string\n req: PayloadRequest\n }) => Promise<void> | void\n >\n beforeBookingCreate?: Array<\n (args: {\n data: Record<string, unknown>\n req: PayloadRequest\n }) => Promise<Record<string, unknown>> | Record<string, unknown>\n >\n}\n\n// --- Resource owner mode ---\n\nexport type ResourceOwnerModeConfig = {\n /** Roles that can see all records (default: check req.user.collection === adminCollection) */\n adminRoles?: string[]\n /** Whether Services also get an owner field (default: false — Services are platform-managed) */\n ownedServices?: boolean\n /** Field name for the owner relationship on Resources (default: 'owner') */\n ownerField?: string\n}\n\nexport type ResolvedResourceOwnerModeConfig = {\n adminRoles: string[]\n ownedServices: boolean\n ownerField: string\n}\n\n// --- Plugin configuration ---\n\nexport type ReservationPluginConfig = {\n /** Override access control per collection */\n access?: {\n customers?: CollectionConfig['access']\n reservations?: CollectionConfig['access']\n resources?: CollectionConfig['access']\n schedules?: CollectionConfig['access']\n services?: CollectionConfig['access']\n }\n /** Admin group name for all reservation collections */\n adminGroup?: string\n /** Hours of notice required before cancellation */\n cancellationNoticePeriod?: number\n /** Default buffer time in minutes between reservations */\n defaultBufferTime?: number\n /** Disable the plugin entirely */\n disabled?: boolean\n /** Extra fields to append to the Reservations collection */\n extraReservationFields?: Field[]\n /** Plugin hooks for external integrations */\n hooks?: ReservationPluginHooks\n /** Enable resource-owner multi-tenancy (opt-in) */\n resourceOwnerMode?: ResourceOwnerModeConfig\n /** Override collection slugs */\n slugs?: {\n customers?: string\n media?: string\n reservations?: string\n resources?: string\n schedules?: string\n services?: string\n }\n /** Configurable status machine (defaults to current behavior) */\n statusMachine?: Partial<StatusMachineConfig>\n /** Which existing auth collection to extend with customer fields */\n userCollection?: string\n}\n\nexport type ResolvedReservationPluginConfig = {\n access: {\n customers?: CollectionConfig['access']\n reservations?: CollectionConfig['access']\n resources?: CollectionConfig['access']\n schedules?: CollectionConfig['access']\n services?: CollectionConfig['access']\n }\n adminGroup: string\n cancellationNoticePeriod: number\n defaultBufferTime: number\n disabled: boolean\n extraReservationFields: Field[]\n hooks: ReservationPluginHooks\n localized: boolean\n resourceOwnerMode: ResolvedResourceOwnerModeConfig | undefined\n slugs: {\n customers: string\n media: string\n reservations: string\n resources: string\n schedules: string\n services: string\n }\n statusMachine: StatusMachineConfig\n userCollection: string | undefined\n}\n\nexport type ReservationStatus = 'cancelled' | 'completed' | 'confirmed' | 'no-show' | 'pending'\n\nexport type DayOfWeek = 'fri' | 'mon' | 'sat' | 'sun' | 'thu' | 'tue' | 'wed'\n\nexport type ScheduleType = 'manual' | 'recurring'\n\n/** @deprecated Use DEFAULT_STATUS_MACHINE.transitions instead */\nexport const VALID_STATUS_TRANSITIONS: Record<ReservationStatus, ReservationStatus[]> =\n DEFAULT_STATUS_MACHINE.transitions as Record<ReservationStatus, ReservationStatus[]>\n"],"names":["DEFAULT_STATUS_MACHINE","blockingStatuses","defaultStatus","statuses","terminalStatuses","transitions","cancelled","completed","confirmed","pending","VALID_STATUS_TRANSITIONS"],"mappings":"AAkBA,OAAO,MAAMA,yBAA8C;IACzDC,kBAAkB;QAAC;QAAW;KAAY;IAC1CC,eAAe;IACfC,UAAU;QAAC;QAAW;QAAa;QAAa;QAAa;KAAU;IACvEC,kBAAkB;QAAC;QAAa;QAAa;KAAU;IACvDC,aAAa;QACXC,WAAW,EAAE;QACbC,WAAW,EAAE;QACbC,WAAW;YAAC;YAAa;YAAa;SAAU;QAChD,WAAW,EAAE;QACbC,SAAS;YAAC;YAAa;SAAY;IACrC;AACF,EAAC;AAiJD,+DAA+D,GAC/D,OAAO,MAAMC,2BACXV,uBAAuBK,WAAW,CAAkD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
import type { ResolvedResourceOwnerModeConfig } from '../types.js';
|
|
3
|
+
type CollectionAccess = NonNullable<CollectionConfig['access']>;
|
|
4
|
+
/**
|
|
5
|
+
* Access factories for Resources collection.
|
|
6
|
+
* Owners may read/update/delete their own resources; anyone authenticated may create.
|
|
7
|
+
*/
|
|
8
|
+
export declare function makeResourceOwnerAccess(rom: ResolvedResourceOwnerModeConfig): CollectionAccess;
|
|
9
|
+
/**
|
|
10
|
+
* Access factories for Schedules collection.
|
|
11
|
+
* A schedule's ownership is determined through its `resource.owner` relationship.
|
|
12
|
+
*/
|
|
13
|
+
export declare function makeScheduleOwnerAccess(rom: ResolvedResourceOwnerModeConfig): CollectionAccess;
|
|
14
|
+
/**
|
|
15
|
+
* Access factories for Reservations collection.
|
|
16
|
+
* Resource owners can see reservations for their resources (read-only);
|
|
17
|
+
* mutations are admin-only to prevent owners from unilaterally cancelling guest bookings.
|
|
18
|
+
*/
|
|
19
|
+
export declare function makeReservationOwnerAccess(rom: ResolvedResourceOwnerModeConfig): CollectionAccess;
|
|
20
|
+
/**
|
|
21
|
+
* Access factories for Services collection when `ownedServices: true`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function makeServiceOwnerAccess(rom: ResolvedResourceOwnerModeConfig, ownerField: string): CollectionAccess;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true if the requesting user is considered an "admin" for resource-owner mode:
|
|
3
|
+
* - No user → deny
|
|
4
|
+
* - adminRoles provided → user.role must be in that list
|
|
5
|
+
* - adminRoles empty → no bypass role; all authenticated users are treated as owners
|
|
6
|
+
*/ function isAdmin(user, adminRoles) {
|
|
7
|
+
if (!adminRoles.length) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const role = user.role;
|
|
11
|
+
if (!role) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return Array.isArray(role) ? role.some((r)=>adminRoles.includes(r)) : adminRoles.includes(role);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Access factories for Resources collection.
|
|
18
|
+
* Owners may read/update/delete their own resources; anyone authenticated may create.
|
|
19
|
+
*/ export function makeResourceOwnerAccess(rom) {
|
|
20
|
+
const { adminRoles, ownerField } = rom;
|
|
21
|
+
const ownerOrAdmin = ({ req })=>{
|
|
22
|
+
if (!req.user) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const user = req.user;
|
|
26
|
+
if (isAdmin(user, adminRoles)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
[ownerField]: {
|
|
31
|
+
equals: user.id
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
create: ({ req })=>Boolean(req.user),
|
|
37
|
+
delete: ownerOrAdmin,
|
|
38
|
+
read: ownerOrAdmin,
|
|
39
|
+
update: ownerOrAdmin
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Access factories for Schedules collection.
|
|
44
|
+
* A schedule's ownership is determined through its `resource.owner` relationship.
|
|
45
|
+
*/ export function makeScheduleOwnerAccess(rom) {
|
|
46
|
+
const { adminRoles, ownerField } = rom;
|
|
47
|
+
const ownerOrAdmin = ({ req })=>{
|
|
48
|
+
if (!req.user) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const user = req.user;
|
|
52
|
+
if (isAdmin(user, adminRoles)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
[`resource.${ownerField}`]: {
|
|
57
|
+
equals: user.id
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
create: ({ req })=>Boolean(req.user),
|
|
63
|
+
delete: ownerOrAdmin,
|
|
64
|
+
read: ownerOrAdmin,
|
|
65
|
+
update: ownerOrAdmin
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Access factories for Reservations collection.
|
|
70
|
+
* Resource owners can see reservations for their resources (read-only);
|
|
71
|
+
* mutations are admin-only to prevent owners from unilaterally cancelling guest bookings.
|
|
72
|
+
*/ export function makeReservationOwnerAccess(rom) {
|
|
73
|
+
const { adminRoles, ownerField } = rom;
|
|
74
|
+
const readAccess = ({ req })=>{
|
|
75
|
+
if (!req.user) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const user = req.user;
|
|
79
|
+
if (isAdmin(user, adminRoles)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
[`resource.${ownerField}`]: {
|
|
84
|
+
equals: user.id
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const adminOnly = ({ req })=>{
|
|
89
|
+
if (!req.user) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const user = req.user;
|
|
93
|
+
return isAdmin(user, adminRoles);
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
create: adminOnly,
|
|
97
|
+
delete: adminOnly,
|
|
98
|
+
read: readAccess,
|
|
99
|
+
update: adminOnly
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Access factories for Services collection when `ownedServices: true`.
|
|
104
|
+
*/ export function makeServiceOwnerAccess(rom, ownerField) {
|
|
105
|
+
const { adminRoles } = rom;
|
|
106
|
+
const ownerOrAdmin = ({ req })=>{
|
|
107
|
+
if (!req.user) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const user = req.user;
|
|
111
|
+
if (isAdmin(user, adminRoles)) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
[ownerField]: {
|
|
116
|
+
equals: user.id
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
create: ({ req })=>Boolean(req.user),
|
|
122
|
+
delete: ownerOrAdmin,
|
|
123
|
+
read: ownerOrAdmin,
|
|
124
|
+
update: ownerOrAdmin
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=ownerAccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/ownerAccess.ts"],"sourcesContent":["import type { Access, CollectionConfig, PayloadRequest } from 'payload'\n\nimport type { ResolvedResourceOwnerModeConfig } from '../types.js'\n\ntype CollectionAccess = NonNullable<CollectionConfig['access']>\n\n/**\n * Returns true if the requesting user is considered an \"admin\" for resource-owner mode:\n * - No user → deny\n * - adminRoles provided → user.role must be in that list\n * - adminRoles empty → no bypass role; all authenticated users are treated as owners\n */\nfunction isAdmin(user: Record<string, unknown>, adminRoles: string[]): boolean {\n if (!adminRoles.length) {return false}\n const role = user.role as string | string[] | undefined\n if (!role) {return false}\n return Array.isArray(role) ? role.some((r) => adminRoles.includes(r)) : adminRoles.includes(role)\n}\n\n/**\n * Access factories for Resources collection.\n * Owners may read/update/delete their own resources; anyone authenticated may create.\n */\nexport function makeResourceOwnerAccess(rom: ResolvedResourceOwnerModeConfig): CollectionAccess {\n const { adminRoles, ownerField } = rom\n\n const ownerOrAdmin: Access = ({ req }: { req: PayloadRequest }) => {\n if (!req.user) {return false}\n const user = req.user as Record<string, unknown>\n if (isAdmin(user, adminRoles)) {return true}\n return { [ownerField]: { equals: user.id } }\n }\n\n return {\n create: ({ req }: { req: PayloadRequest }) => Boolean(req.user),\n delete: ownerOrAdmin,\n read: ownerOrAdmin,\n update: ownerOrAdmin,\n }\n}\n\n/**\n * Access factories for Schedules collection.\n * A schedule's ownership is determined through its `resource.owner` relationship.\n */\nexport function makeScheduleOwnerAccess(rom: ResolvedResourceOwnerModeConfig): CollectionAccess {\n const { adminRoles, ownerField } = rom\n\n const ownerOrAdmin: Access = ({ req }: { req: PayloadRequest }) => {\n if (!req.user) {return false}\n const user = req.user as Record<string, unknown>\n if (isAdmin(user, adminRoles)) {return true}\n return { [`resource.${ownerField}`]: { equals: user.id } }\n }\n\n return {\n create: ({ req }: { req: PayloadRequest }) => Boolean(req.user),\n delete: ownerOrAdmin,\n read: ownerOrAdmin,\n update: ownerOrAdmin,\n }\n}\n\n/**\n * Access factories for Reservations collection.\n * Resource owners can see reservations for their resources (read-only);\n * mutations are admin-only to prevent owners from unilaterally cancelling guest bookings.\n */\nexport function makeReservationOwnerAccess(\n rom: ResolvedResourceOwnerModeConfig,\n): CollectionAccess {\n const { adminRoles, ownerField } = rom\n\n const readAccess: Access = ({ req }: { req: PayloadRequest }) => {\n if (!req.user) {return false}\n const user = req.user as Record<string, unknown>\n if (isAdmin(user, adminRoles)) {return true}\n return { [`resource.${ownerField}`]: { equals: user.id } }\n }\n\n const adminOnly: Access = ({ req }: { req: PayloadRequest }) => {\n if (!req.user) {return false}\n const user = req.user as Record<string, unknown>\n return isAdmin(user, adminRoles)\n }\n\n return {\n create: adminOnly,\n delete: adminOnly,\n read: readAccess,\n update: adminOnly,\n }\n}\n\n/**\n * Access factories for Services collection when `ownedServices: true`.\n */\nexport function makeServiceOwnerAccess(\n rom: ResolvedResourceOwnerModeConfig,\n ownerField: string,\n): CollectionAccess {\n const { adminRoles } = rom\n\n const ownerOrAdmin: Access = ({ req }: { req: PayloadRequest }) => {\n if (!req.user) {return false}\n const user = req.user as Record<string, unknown>\n if (isAdmin(user, adminRoles)) {return true}\n return { [ownerField]: { equals: user.id } }\n }\n\n return {\n create: ({ req }: { req: PayloadRequest }) => Boolean(req.user),\n delete: ownerOrAdmin,\n read: ownerOrAdmin,\n update: ownerOrAdmin,\n }\n}\n"],"names":["isAdmin","user","adminRoles","length","role","Array","isArray","some","r","includes","makeResourceOwnerAccess","rom","ownerField","ownerOrAdmin","req","equals","id","create","Boolean","delete","read","update","makeScheduleOwnerAccess","makeReservationOwnerAccess","readAccess","adminOnly","makeServiceOwnerAccess"],"mappings":"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"}
|
package/package.json
CHANGED