payload-reserve 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +44 -1154
  2. package/dist/collections/Customers.d.ts +3 -0
  3. package/dist/collections/Customers.js +58 -0
  4. package/dist/collections/Customers.js.map +1 -0
  5. package/dist/collections/Reservations.js +124 -34
  6. package/dist/collections/Reservations.js.map +1 -1
  7. package/dist/collections/Resources.js +39 -0
  8. package/dist/collections/Resources.js.map +1 -1
  9. package/dist/collections/Schedules.js.map +1 -1
  10. package/dist/collections/Services.js +27 -0
  11. package/dist/collections/Services.js.map +1 -1
  12. package/dist/components/AvailabilityOverview/AvailabilityOverview.module.css +31 -0
  13. package/dist/components/AvailabilityOverview/index.js +59 -8
  14. package/dist/components/AvailabilityOverview/index.js.map +1 -1
  15. package/dist/components/CalendarView/CalendarView.module.css +171 -0
  16. package/dist/components/CalendarView/index.js +547 -38
  17. package/dist/components/CalendarView/index.js.map +1 -1
  18. package/dist/components/CustomerField/index.js +24 -10
  19. package/dist/components/CustomerField/index.js.map +1 -1
  20. package/dist/components/DashboardWidget/DashboardWidgetServer.js +21 -11
  21. package/dist/components/DashboardWidget/DashboardWidgetServer.js.map +1 -1
  22. package/dist/defaults.d.ts +1 -2
  23. package/dist/defaults.js +15 -4
  24. package/dist/defaults.js.map +1 -1
  25. package/dist/endpoints/cancelBooking.d.ts +3 -0
  26. package/dist/endpoints/cancelBooking.js +37 -0
  27. package/dist/endpoints/cancelBooking.js.map +1 -0
  28. package/dist/endpoints/checkAvailability.d.ts +3 -0
  29. package/dist/endpoints/checkAvailability.js +37 -0
  30. package/dist/endpoints/checkAvailability.js.map +1 -0
  31. package/dist/endpoints/createBooking.d.ts +3 -0
  32. package/dist/endpoints/createBooking.js +32 -0
  33. package/dist/endpoints/createBooking.js.map +1 -0
  34. package/dist/endpoints/customerSearch.js +76 -58
  35. package/dist/endpoints/customerSearch.js.map +1 -1
  36. package/dist/endpoints/getSlots.d.ts +3 -0
  37. package/dist/endpoints/getSlots.js +51 -0
  38. package/dist/endpoints/getSlots.js.map +1 -0
  39. package/dist/hooks/index.d.ts +2 -0
  40. package/dist/hooks/index.js +2 -0
  41. package/dist/hooks/index.js.map +1 -1
  42. package/dist/hooks/reservations/calculateEndTime.js +75 -9
  43. package/dist/hooks/reservations/calculateEndTime.js.map +1 -1
  44. package/dist/hooks/reservations/checkIdempotency.d.ts +3 -0
  45. package/dist/hooks/reservations/checkIdempotency.js +32 -0
  46. package/dist/hooks/reservations/checkIdempotency.js.map +1 -0
  47. package/dist/hooks/reservations/onStatusChange.d.ts +3 -0
  48. package/dist/hooks/reservations/onStatusChange.js +38 -0
  49. package/dist/hooks/reservations/onStatusChange.js.map +1 -0
  50. package/dist/hooks/reservations/validateCancellation.js +1 -1
  51. package/dist/hooks/reservations/validateCancellation.js.map +1 -1
  52. package/dist/hooks/reservations/validateConflicts.js +34 -56
  53. package/dist/hooks/reservations/validateConflicts.js.map +1 -1
  54. package/dist/hooks/reservations/validateStatusTransition.d.ts +2 -1
  55. package/dist/hooks/reservations/validateStatusTransition.js +29 -6
  56. package/dist/hooks/reservations/validateStatusTransition.js.map +1 -1
  57. package/dist/index.d.ts +5 -1
  58. package/dist/index.js +3 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/plugin.js +49 -54
  61. package/dist/plugin.js.map +1 -1
  62. package/dist/services/AvailabilityService.d.ts +57 -0
  63. package/dist/services/AvailabilityService.js +232 -0
  64. package/dist/services/AvailabilityService.js.map +1 -0
  65. package/dist/services/index.d.ts +1 -0
  66. package/dist/services/index.js +3 -0
  67. package/dist/services/index.js.map +1 -0
  68. package/dist/translations/en.json +33 -1
  69. package/dist/translations/index.d.ts +5 -0
  70. package/dist/translations/index.js.map +1 -1
  71. package/dist/types.d.ts +65 -6
  72. package/dist/types.js +29 -9
  73. package/dist/types.js.map +1 -1
  74. package/dist/utilities/resolveReservationItems.d.ts +18 -0
  75. package/dist/utilities/resolveReservationItems.js +45 -0
  76. package/dist/utilities/resolveReservationItems.js.map +1 -0
  77. package/package.json +12 -9
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../types.js';
3
+ export declare function createCustomersCollection(config: ResolvedReservationPluginConfig): CollectionConfig;
@@ -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,8 +1,46 @@
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
+ function createPluginHooksBeforeCreate(hooks) {
8
+ return async ({ context, data, operation, req })=>{
9
+ if (context?.skipReservationHooks) {
10
+ return data;
11
+ }
12
+ if (operation === 'create' && hooks.beforeBookingCreate) {
13
+ let mutatedData = data;
14
+ for (const hook of hooks.beforeBookingCreate){
15
+ const result = await hook({
16
+ data: mutatedData,
17
+ req
18
+ });
19
+ if (result) {
20
+ mutatedData = result;
21
+ }
22
+ }
23
+ return mutatedData;
24
+ }
25
+ return data;
26
+ };
27
+ }
28
+ function createPluginHooksAfterCreate(hooks) {
29
+ return async ({ doc, operation, req })=>{
30
+ if (operation === 'create' && hooks.afterBookingCreate) {
31
+ const docRecord = doc;
32
+ for (const hook of hooks.afterBookingCreate){
33
+ await hook({
34
+ doc: docRecord,
35
+ req
36
+ });
37
+ }
38
+ }
39
+ return doc;
40
+ };
41
+ }
5
42
  export function createReservationsCollection(config) {
43
+ const { statusMachine } = config;
6
44
  return {
7
45
  slug: config.slugs.reservations,
8
46
  access: config.access.reservations ?? {},
@@ -38,23 +76,16 @@ export function createReservationsCollection(config) {
38
76
  {
39
77
  name: 'customer',
40
78
  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
79
  admin: {
52
80
  allowCreate: true,
53
81
  allowEdit: true,
54
82
  components: {
55
83
  Field: 'payload-reserve/client#CustomerField'
56
84
  }
57
- }
85
+ },
86
+ label: ({ t })=>t('reservation:fieldCustomer'),
87
+ relationTo: config.slugs.customers,
88
+ required: true
58
89
  },
59
90
  {
60
91
  name: 'startTime',
@@ -81,50 +112,109 @@ export function createReservationsCollection(config) {
81
112
  {
82
113
  name: 'status',
83
114
  type: 'select',
84
- defaultValue: 'pending',
115
+ defaultValue: statusMachine.defaultStatus,
85
116
  label: ({ t })=>t('reservation:fieldStatus'),
86
- options: [
117
+ options: statusMachine.statuses.map((s)=>({
118
+ label: ({ t })=>{
119
+ const key = `reservation:status${s.charAt(0).toUpperCase() + s.slice(1)}`;
120
+ const translated = t(key);
121
+ return translated !== key ? translated : s.charAt(0).toUpperCase() + s.slice(1);
122
+ },
123
+ value: s
124
+ }))
125
+ },
126
+ {
127
+ name: 'cancellationReason',
128
+ type: 'textarea',
129
+ admin: {
130
+ condition: (_, siblingData)=>siblingData?.status === 'cancelled'
131
+ },
132
+ label: ({ t })=>t('reservation:fieldCancellationReason')
133
+ },
134
+ {
135
+ name: 'guestCount',
136
+ type: 'number',
137
+ defaultValue: 1,
138
+ label: ({ t })=>t('reservation:fieldGuestCount'),
139
+ min: 1
140
+ },
141
+ {
142
+ name: 'notes',
143
+ type: 'textarea',
144
+ label: ({ t })=>t('reservation:fieldNotes')
145
+ },
146
+ {
147
+ name: 'items',
148
+ type: 'array',
149
+ admin: {
150
+ description: 'Resources included in this booking. Leave empty for single-resource bookings.'
151
+ },
152
+ fields: [
87
153
  {
88
- label: ({ t })=>t('reservation:statusPending'),
89
- value: 'pending'
154
+ name: 'resource',
155
+ type: 'relationship',
156
+ label: ({ t })=>t('reservation:fieldResource'),
157
+ relationTo: config.slugs.resources,
158
+ required: true
90
159
  },
91
160
  {
92
- label: ({ t })=>t('reservation:statusConfirmed'),
93
- value: 'confirmed'
161
+ name: 'service',
162
+ type: 'relationship',
163
+ label: ({ t })=>t('reservation:fieldService'),
164
+ relationTo: config.slugs.services
94
165
  },
95
166
  {
96
- label: ({ t })=>t('reservation:statusCompleted'),
97
- value: 'completed'
167
+ name: 'startTime',
168
+ type: 'date',
169
+ admin: {
170
+ date: {
171
+ pickerAppearance: 'dayAndTime'
172
+ }
173
+ },
174
+ label: ({ t })=>t('reservation:fieldStartTime')
98
175
  },
99
176
  {
100
- label: ({ t })=>t('reservation:statusCancelled'),
101
- value: 'cancelled'
177
+ name: 'endTime',
178
+ type: 'date',
179
+ admin: {
180
+ date: {
181
+ pickerAppearance: 'dayAndTime'
182
+ },
183
+ readOnly: false
184
+ },
185
+ label: ({ t })=>t('reservation:fieldEndTime')
102
186
  },
103
187
  {
104
- label: ({ t })=>t('reservation:statusNoShow'),
105
- value: 'no-show'
188
+ name: 'guestCount',
189
+ type: 'number',
190
+ label: ({ t })=>t('reservation:fieldGuestCount'),
191
+ min: 1
106
192
  }
107
- ]
193
+ ],
194
+ label: ({ t })=>t('reservation:fieldItems')
108
195
  },
109
196
  {
110
- name: 'cancellationReason',
111
- type: 'textarea',
197
+ name: 'idempotencyKey',
198
+ type: 'text',
112
199
  admin: {
113
- condition: (_, siblingData)=>siblingData?.status === 'cancelled'
200
+ position: 'sidebar',
201
+ readOnly: true
114
202
  },
115
- label: ({ t })=>t('reservation:fieldCancellationReason')
116
- },
117
- {
118
- name: 'notes',
119
- type: 'textarea',
120
- label: ({ t })=>t('reservation:fieldNotes')
203
+ index: true,
204
+ unique: true
121
205
  }
122
206
  ],
123
207
  hooks: {
208
+ afterChange: [
209
+ createPluginHooksAfterCreate(config.hooks),
210
+ onStatusChange(config)
211
+ ],
124
212
  beforeChange: [
213
+ createPluginHooksBeforeCreate(config.hooks),
214
+ checkIdempotency(config),
125
215
  calculateEndTime(config),
126
216
  validateConflicts(config),
127
- validateStatusTransition(),
217
+ validateStatusTransition(config),
128
218
  validateCancellation(config)
129
219
  ]
130
220
  },
@@ -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'\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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"}
@@ -47,6 +47,45 @@ export function createResourcesCollection(config) {
47
47
  },
48
48
  defaultValue: true,
49
49
  label: ({ t })=>t('reservation:fieldActive')
50
+ },
51
+ {
52
+ name: 'quantity',
53
+ type: 'number',
54
+ admin: {
55
+ position: 'sidebar'
56
+ },
57
+ defaultValue: 1,
58
+ label: ({ t })=>t('reservation:fieldQuantity'),
59
+ min: 1,
60
+ required: true
61
+ },
62
+ {
63
+ name: 'capacityMode',
64
+ type: 'select',
65
+ admin: {
66
+ condition: (data)=>(data?.quantity ?? 1) > 1,
67
+ position: 'sidebar'
68
+ },
69
+ defaultValue: 'per-reservation',
70
+ label: ({ t })=>t('reservation:fieldCapacityMode'),
71
+ options: [
72
+ {
73
+ label: ({ t })=>t('reservation:capacityPerReservation'),
74
+ value: 'per-reservation'
75
+ },
76
+ {
77
+ label: ({ t })=>t('reservation:capacityPerGuest'),
78
+ value: 'per-guest'
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ name: 'timezone',
84
+ type: 'text',
85
+ admin: {
86
+ position: 'sidebar'
87
+ },
88
+ label: ({ t })=>t('reservation:fieldTimezone')
50
89
  }
51
90
  ],
52
91
  labels: {
@@ -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 return {\n slug: config.slugs.resources,\n access: config.access.resources ?? {},\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,\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,\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 labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n },\n }\n}\n"],"names":["createResourcesCollection","config","slug","slugs","resources","access","admin","group","adminGroup","useAsTitle","fields","name","type","label","t","localized","maxLength","required","relationTo","media","hasMany","services","position","defaultValue","labels","plural","singular"],"mappings":"AAKA,OAAO,SAASA,0BACdC,MAAuC;IAEvC,OAAO;QACLC,MAAMD,OAAOE,KAAK,CAACC,SAAS;QAC5BC,QAAQJ,OAAOI,MAAM,CAACD,SAAS,IAAI,CAAC;QACpCE,OAAO;YACLC,OAAON,OAAOO,UAAU;YACxBC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;gBAC/CC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,YAAYjB,OAAOE,KAAK,CAACgB,KAAK;YAChC;YACA;gBACER,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;YACjD;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNQ,SAAS;gBACTP,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,YAAYjB,OAAOE,KAAK,CAACkB,QAAQ;gBACjCJ,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLgB,UAAU;gBACZ;gBACAC,cAAc;gBACdV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;SACD;QACDU,QAAQ;YACNC,QAAQ,CAAC,EAAEX,CAAC,EAAE,GAAK,AAACA,EAAc;YAClCY,UAAU,CAAC,EAAEZ,CAAC,EAAE,GAAK,AAACA,EAAc;QACtC;IACF;AACF"}
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 return {\n slug: config.slugs.resources,\n access: config.access.resources ?? {},\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 ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionResources'),\n },\n }\n}\n"],"names":["createResourcesCollection","config","slug","slugs","resources","access","admin","group","adminGroup","useAsTitle","fields","name","type","label","t","localized","maxLength","required","relationTo","media","hasMany","services","position","defaultValue","min","condition","data","quantity","options","value","labels","plural","singular"],"mappings":"AAKA,OAAO,SAASA,0BACdC,MAAuC;IAEvC,OAAO;QACLC,MAAMD,OAAOE,KAAK,CAACC,SAAS;QAC5BC,QAAQJ,OAAOI,MAAM,CAACD,SAAS,IAAI,CAAC;QACpCE,OAAO;YACLC,OAAON,OAAOO,UAAU;YACxBC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;gBAC/CC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,YAAYjB,OAAOE,KAAK,CAACgB,KAAK;YAChC;YACA;gBACER,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;YACjD;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNQ,SAAS;gBACTP,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,YAAYjB,OAAOE,KAAK,CAACkB,QAAQ;gBACjCJ,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLgB,UAAU;gBACZ;gBACAC,cAAc;gBACdV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;YACA;gBACEH,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLgB,UAAU;gBACZ;gBACAC,cAAc;gBACdV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCU,KAAK;gBACLP,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLmB,WAAW,CAACC,OAAS,AAACA,CAAAA,MAAMC,YAAY,CAAA,IAAK;oBAC7CL,UAAU;gBACZ;gBACAC,cAAc;gBACdV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCc,SAAS;oBACP;wBACEf,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;wBACjCe,OAAO;oBACT;oBACA;wBACEhB,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;wBACjCe,OAAO;oBACT;iBACD;YACH;YACA;gBACElB,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLgB,UAAU;gBACZ;gBACAT,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;SACD;QACDgB,QAAQ;YACNC,QAAQ,CAAC,EAAEjB,CAAC,EAAE,GAAK,AAACA,EAAc;YAClCkB,UAAU,CAAC,EAAElB,CAAC,EAAE,GAAK,AAACA,EAAc;QACtC;IACF;AACF"}
@@ -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: config.access.schedules ?? {},\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,\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":["createSchedulesCollection","config","slug","slugs","schedules","access","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
+ {"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: config.access.schedules ?? {},\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":["createSchedulesCollection","config","slug","slugs","schedules","access","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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"}
@@ -17,6 +17,12 @@ export function createServicesCollection(config) {
17
17
  maxLength: 200,
18
18
  required: true
19
19
  },
20
+ {
21
+ name: 'image',
22
+ type: 'upload',
23
+ label: ({ t })=>t('reservation:fieldImage'),
24
+ relationTo: config.slugs.media
25
+ },
20
26
  {
21
27
  name: 'description',
22
28
  type: 'textarea',
@@ -32,6 +38,27 @@ export function createServicesCollection(config) {
32
38
  min: 1,
33
39
  required: true
34
40
  },
41
+ {
42
+ name: 'durationType',
43
+ type: 'select',
44
+ defaultValue: 'fixed',
45
+ label: ({ t })=>t('reservation:fieldDurationType'),
46
+ options: [
47
+ {
48
+ label: ({ t })=>t('reservation:durationFixed'),
49
+ value: 'fixed'
50
+ },
51
+ {
52
+ label: ({ t })=>t('reservation:durationFlexible'),
53
+ value: 'flexible'
54
+ },
55
+ {
56
+ label: ({ t })=>t('reservation:durationFullDay'),
57
+ value: 'full-day'
58
+ }
59
+ ],
60
+ required: true
61
+ },
35
62
  {
36
63
  name: 'price',
37
64
  type: 'number',
@@ -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 return {\n slug: config.slugs.services,\n access: config.access.services ?? {},\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: '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: '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 ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n },\n }\n}\n"],"names":["createServicesCollection","config","slug","slugs","services","access","admin","group","adminGroup","useAsTitle","fields","name","type","label","t","localized","maxLength","required","min","step","defaultValue","position","labels","plural","singular"],"mappings":"AAKA,OAAO,SAASA,yBAAyBC,MAAuC;IAC9E,OAAO;QACLC,MAAMD,OAAOE,KAAK,CAACC,QAAQ;QAC3BC,QAAQJ,OAAOI,MAAM,CAACD,QAAQ,IAAI,CAAC;QACnCE,OAAO;YACLC,OAAON,OAAOO,UAAU;YACxBC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;gBAC/CC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;YACjD;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,KAAK;gBACLD,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLa,MAAM;gBACR;gBACAN,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,KAAK;YACP;YACA;gBACEP,MAAM;gBACNC,MAAM;gBACNQ,cAAc;gBACdP,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,KAAK;YACP;YACA;gBACEP,MAAM;gBACNC,MAAM;gBACNQ,cAAc;gBACdP,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,KAAK;YACP;YACA;gBACEP,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLe,UAAU;gBACZ;gBACAD,cAAc;gBACdP,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;SACD;QACDQ,QAAQ;YACNC,QAAQ,CAAC,EAAET,CAAC,EAAE,GAAK,AAACA,EAAc;YAClCU,UAAU,CAAC,EAAEV,CAAC,EAAE,GAAK,AAACA,EAAc;QACtC;IACF;AACF"}
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 return {\n slug: config.slugs.services,\n access: config.access.services ?? {},\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 ],\n labels: {\n plural: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n singular: ({ t }) => (t as PluginT)('reservation:collectionServices'),\n },\n }\n}\n"],"names":["createServicesCollection","config","slug","slugs","services","access","admin","group","adminGroup","useAsTitle","fields","name","type","label","t","localized","maxLength","required","relationTo","media","min","defaultValue","options","value","step","position","labels","plural","singular"],"mappings":"AAKA,OAAO,SAASA,yBAAyBC,MAAuC;IAC9E,OAAO;QACLC,MAAMD,OAAOE,KAAK,CAACC,QAAQ;QAC3BC,QAAQJ,OAAOI,MAAM,CAACD,QAAQ,IAAI,CAAC;QACnCE,OAAO;YACLC,OAAON,OAAOO,UAAU;YACxBC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;gBAC/CC,WAAW;gBACXC,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCI,YAAYjB,OAAOE,KAAK,CAACgB,KAAK;YAChC;YACA;gBACER,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjC,GAAIb,OAAOc,SAAS,GAAG;oBAAEA,WAAW;gBAAK,IAAI,CAAC,CAAC;YACjD;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNC,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCM,KAAK;gBACLH,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNS,cAAc;gBACdR,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCQ,SAAS;oBACP;wBACET,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;wBACjCS,OAAO;oBACT;oBACA;wBACEV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;wBACjCS,OAAO;oBACT;oBACA;wBACEV,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;wBACjCS,OAAO;oBACT;iBACD;gBACDN,UAAU;YACZ;YACA;gBACEN,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLkB,MAAM;gBACR;gBACAX,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCM,KAAK;YACP;YACA;gBACET,MAAM;gBACNC,MAAM;gBACNS,cAAc;gBACdR,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCM,KAAK;YACP;YACA;gBACET,MAAM;gBACNC,MAAM;gBACNS,cAAc;gBACdR,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;gBACjCM,KAAK;YACP;YACA;gBACET,MAAM;gBACNC,MAAM;gBACNN,OAAO;oBACLmB,UAAU;gBACZ;gBACAJ,cAAc;gBACdR,OAAO,CAAC,EAAEC,CAAC,EAAE,GAAK,AAACA,EAAc;YACnC;SACD;QACDY,QAAQ;YACNC,QAAQ,CAAC,EAAEb,CAAC,EAAE,GAAK,AAACA,EAAc;YAClCc,UAAU,CAAC,EAAEd,CAAC,EAAE,GAAK,AAACA,EAAc;QACtC;IACF;AACF"}
@@ -79,6 +79,37 @@
79
79
  color: #1e40af;
80
80
  }
81
81
 
82
+ /* Graduated capacity colors for multi-unit resources */
83
+ /* Green: utilization < 50% */
84
+ .slotCapacityLow {
85
+ font-size: 0.625rem;
86
+ padding: 2px 4px;
87
+ margin-bottom: 1px;
88
+ border-radius: 2px;
89
+ background: #d1fae5;
90
+ color: #065f46;
91
+ }
92
+
93
+ /* Yellow: utilization 50–99% */
94
+ .slotCapacityMid {
95
+ font-size: 0.625rem;
96
+ padding: 2px 4px;
97
+ margin-bottom: 1px;
98
+ border-radius: 2px;
99
+ background: #fef3c7;
100
+ color: #92400e;
101
+ }
102
+
103
+ /* Red: utilization 100% (fully booked) */
104
+ .slotCapacityFull {
105
+ font-size: 0.625rem;
106
+ padding: 2px 4px;
107
+ margin-bottom: 1px;
108
+ border-radius: 2px;
109
+ background: #fee2e2;
110
+ color: #991b1b;
111
+ }
112
+
82
113
  .slotException {
83
114
  font-size: 0.625rem;
84
115
  padding: 2px 4px;