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
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js'\n\nexport const DEFAULT_SLUGS = {\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\nexport const DEFAULT_USER_COLLECTION = 'users'\nexport const DEFAULT_CUSTOMER_ROLE: false | string = false\n\nexport function resolveConfig(\n pluginOptions: ReservationPluginConfig,\n): ResolvedReservationPluginConfig {\n return {\n access: pluginOptions.access ?? {},\n adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,\n cancellationNoticePeriod:\n pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,\n customerRole: pluginOptions.customerRole ?? DEFAULT_CUSTOMER_ROLE,\n defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,\n disabled: pluginOptions.disabled ?? false,\n localized: false,\n slugs: {\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 userCollection: pluginOptions.userCollection ?? DEFAULT_USER_COLLECTION,\n }\n}\n"],"names":["DEFAULT_SLUGS","media","reservations","resources","schedules","services","DEFAULT_ADMIN_GROUP","DEFAULT_BUFFER_TIME","DEFAULT_CANCELLATION_NOTICE_PERIOD","DEFAULT_USER_COLLECTION","DEFAULT_CUSTOMER_ROLE","resolveConfig","pluginOptions","access","adminGroup","cancellationNoticePeriod","customerRole","defaultBufferTime","disabled","localized","slugs","userCollection"],"mappings":"AAEA,OAAO,MAAMA,gBAAgB;IAC3BC,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;AACpD,OAAO,MAAMC,0BAA0B,QAAO;AAC9C,OAAO,MAAMC,wBAAwC,MAAK;AAE1D,OAAO,SAASC,cACdC,aAAsC;IAEtC,OAAO;QACLC,QAAQD,cAAcC,MAAM,IAAI,CAAC;QACjCC,YAAYF,cAAcE,UAAU,IAAIR;QACxCS,0BACEH,cAAcG,wBAAwB,IAAIP;QAC5CQ,cAAcJ,cAAcI,YAAY,IAAIN;QAC5CO,mBAAmBL,cAAcK,iBAAiB,IAAIV;QACtDW,UAAUN,cAAcM,QAAQ,IAAI;QACpCC,WAAW;QACXC,OAAO;YACLnB,OAAOW,cAAcQ,KAAK,EAAEnB,SAASD,cAAcC,KAAK;YACxDC,cAAcU,cAAcQ,KAAK,EAAElB,gBAAgBF,cAAcE,YAAY;YAC7EC,WAAWS,cAAcQ,KAAK,EAAEjB,aAAaH,cAAcG,SAAS;YACpEC,WAAWQ,cAAcQ,KAAK,EAAEhB,aAAaJ,cAAcI,SAAS;YACpEC,UAAUO,cAAcQ,KAAK,EAAEf,YAAYL,cAAcK,QAAQ;QACnE;QACAgB,gBAAgBT,cAAcS,cAAc,IAAIZ;IAClD;AACF"}
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","undefined"],"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,OAAO;QACLC,QAAQH,cAAcG,MAAM,IAAI,CAAC;QACjCC,YAAYJ,cAAcI,UAAU,IAAIR;QACxCS,0BACEL,cAAcK,wBAAwB,IAAIP;QAC5CQ,mBAAmBN,cAAcM,iBAAiB,IAAIT;QACtDU,UAAUP,cAAcO,QAAQ,IAAI;QACpCC,OAAOR,cAAcQ,KAAK,IAAI,CAAC;QAC/BC,WAAW;QACXC,OAAO;YACLpB,WAAWU,cAAcU,KAAK,EAAEpB,aAAaD,cAAcC,SAAS;YACpEC,OAAOS,cAAcU,KAAK,EAAEnB,SAASF,cAAcE,KAAK;YACxDC,cAAcQ,cAAcU,KAAK,EAAElB,gBAAgBH,cAAcG,YAAY;YAC7EC,WAAWO,cAAcU,KAAK,EAAEjB,aAAaJ,cAAcI,SAAS;YACpEC,WAAWM,cAAcU,KAAK,EAAEhB,aAAaL,cAAcK,SAAS;YACpEC,UAAUK,cAAcU,KAAK,EAAEf,YAAYN,cAAcM,QAAQ;QACnE;QACAO,eAAeD,oBACX;YACEU,kBACEV,kBAAkBU,gBAAgB,IAAIvB,uBAAuBuB,gBAAgB;YAC/EC,eAAeX,kBAAkBW,aAAa,IAAIxB,uBAAuBwB,aAAa;YACtFC,UAAUZ,kBAAkBY,QAAQ,IAAIzB,uBAAuByB,QAAQ;YACvEC,kBACEb,kBAAkBa,gBAAgB,IAAI1B,uBAAuB0B,gBAAgB;YAC/EC,aAAad,kBAAkBc,WAAW,IAAI3B,uBAAuB2B,WAAW;QAClF,IACA;YAAE,GAAG3B,sBAAsB;QAAC;QAChC4B,gBAAgBhB,cAAcgB,cAAc,IAAIC;IAClD;AACF"}
@@ -0,0 +1,3 @@
1
+ import type { Endpoint } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../types.js';
3
+ export declare function createCancelBookingEndpoint(config: ResolvedReservationPluginConfig): Endpoint;
@@ -0,0 +1,37 @@
1
+ export function createCancelBookingEndpoint(config) {
2
+ return {
3
+ handler: async (req)=>{
4
+ if (!req.user) {
5
+ return Response.json({
6
+ message: 'Unauthorized'
7
+ }, {
8
+ status: 401
9
+ });
10
+ }
11
+ const body = await req.json?.();
12
+ const { reason, reservationId } = body ?? {};
13
+ if (!reservationId) {
14
+ return Response.json({
15
+ message: 'reservationId is required'
16
+ }, {
17
+ status: 400
18
+ });
19
+ }
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ const reservation = await req.payload.update({
22
+ id: reservationId,
23
+ collection: config.slugs.reservations,
24
+ data: {
25
+ cancellationReason: reason,
26
+ status: 'cancelled'
27
+ },
28
+ req
29
+ });
30
+ return Response.json(reservation);
31
+ },
32
+ method: 'post',
33
+ path: '/reserve/cancel'
34
+ };
35
+ }
36
+
37
+ //# sourceMappingURL=cancelBooking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/cancelBooking.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createCancelBookingEndpoint(config: ResolvedReservationPluginConfig): Endpoint {\n return {\n handler: async (req) => {\n if (!req.user) {\n return Response.json({ message: 'Unauthorized' }, { status: 401 })\n }\n\n const body = await req.json?.()\n const { reason, reservationId } = (body ?? {}) as {\n reason?: string\n reservationId?: string\n }\n\n if (!reservationId) {\n return Response.json({ message: 'reservationId is required' }, { status: 400 })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const reservation = await (req.payload.update as any)({\n id: reservationId,\n collection: config.slugs.reservations,\n data: {\n cancellationReason: reason,\n status: 'cancelled',\n },\n req,\n })\n\n return Response.json(reservation)\n },\n method: 'post',\n path: '/reserve/cancel',\n }\n}\n"],"names":["createCancelBookingEndpoint","config","handler","req","user","Response","json","message","status","body","reason","reservationId","reservation","payload","update","id","collection","slugs","reservations","data","cancellationReason","method","path"],"mappings":"AAIA,OAAO,SAASA,4BAA4BC,MAAuC;IACjF,OAAO;QACLC,SAAS,OAAOC;YACd,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAAe,GAAG;oBAAEC,QAAQ;gBAAI;YAClE;YAEA,MAAMC,OAAO,MAAMN,IAAIG,IAAI;YAC3B,MAAM,EAAEI,MAAM,EAAEC,aAAa,EAAE,GAAIF,QAAQ,CAAC;YAK5C,IAAI,CAACE,eAAe;gBAClB,OAAON,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAA4B,GAAG;oBAAEC,QAAQ;gBAAI;YAC/E;YAEA,8DAA8D;YAC9D,MAAMI,cAAc,MAAM,AAACT,IAAIU,OAAO,CAACC,MAAM,CAAS;gBACpDC,IAAIJ;gBACJK,YAAYf,OAAOgB,KAAK,CAACC,YAAY;gBACrCC,MAAM;oBACJC,oBAAoBV;oBACpBF,QAAQ;gBACV;gBACAL;YACF;YAEA,OAAOE,SAASC,IAAI,CAACM;QACvB;QACAS,QAAQ;QACRC,MAAM;IACR;AACF"}
@@ -0,0 +1,3 @@
1
+ import type { Endpoint } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../types.js';
3
+ export declare function createCheckAvailabilityEndpoint(config: ResolvedReservationPluginConfig): Endpoint;
@@ -0,0 +1,37 @@
1
+ import { getAvailableSlots } from '../services/AvailabilityService.js';
2
+ export function createCheckAvailabilityEndpoint(config) {
3
+ return {
4
+ handler: async (req)=>{
5
+ const url = new URL(req.url);
6
+ const date = url.searchParams.get('date');
7
+ const resource = url.searchParams.get('resource');
8
+ const service = url.searchParams.get('service');
9
+ if (!date || !resource || !service) {
10
+ return Response.json({
11
+ message: 'Missing required query params: resource, date, service'
12
+ }, {
13
+ status: 400
14
+ });
15
+ }
16
+ const slots = await getAvailableSlots({
17
+ blockingStatuses: config.statusMachine.blockingStatuses,
18
+ date: new Date(date),
19
+ payload: req.payload,
20
+ req,
21
+ reservationSlug: config.slugs.reservations,
22
+ resourceId: resource,
23
+ resourceSlug: config.slugs.resources,
24
+ scheduleSlug: config.slugs.schedules,
25
+ serviceId: service,
26
+ serviceSlug: config.slugs.services
27
+ });
28
+ return Response.json({
29
+ slots
30
+ });
31
+ },
32
+ method: 'get',
33
+ path: '/reserve/availability'
34
+ };
35
+ }
36
+
37
+ //# sourceMappingURL=checkAvailability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/checkAvailability.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { getAvailableSlots } from '../services/AvailabilityService.js'\n\nexport function createCheckAvailabilityEndpoint(\n config: ResolvedReservationPluginConfig,\n): Endpoint {\n return {\n handler: async (req) => {\n const url = new URL(req.url!)\n const date = url.searchParams.get('date')\n const resource = url.searchParams.get('resource')\n const service = url.searchParams.get('service')\n\n if (!date || !resource || !service) {\n return Response.json(\n { message: 'Missing required query params: resource, date, service' },\n { status: 400 },\n )\n }\n\n const slots = await getAvailableSlots({\n blockingStatuses: config.statusMachine.blockingStatuses,\n date: new Date(date),\n payload: req.payload,\n req,\n reservationSlug: config.slugs.reservations,\n resourceId: resource,\n resourceSlug: config.slugs.resources,\n scheduleSlug: config.slugs.schedules,\n serviceId: service,\n serviceSlug: config.slugs.services,\n })\n\n return Response.json({ slots })\n },\n method: 'get',\n path: '/reserve/availability',\n }\n}\n"],"names":["getAvailableSlots","createCheckAvailabilityEndpoint","config","handler","req","url","URL","date","searchParams","get","resource","service","Response","json","message","status","slots","blockingStatuses","statusMachine","Date","payload","reservationSlug","slugs","reservations","resourceId","resourceSlug","resources","scheduleSlug","schedules","serviceId","serviceSlug","services","method","path"],"mappings":"AAIA,SAASA,iBAAiB,QAAQ,qCAAoC;AAEtE,OAAO,SAASC,gCACdC,MAAuC;IAEvC,OAAO;QACLC,SAAS,OAAOC;YACd,MAAMC,MAAM,IAAIC,IAAIF,IAAIC,GAAG;YAC3B,MAAME,OAAOF,IAAIG,YAAY,CAACC,GAAG,CAAC;YAClC,MAAMC,WAAWL,IAAIG,YAAY,CAACC,GAAG,CAAC;YACtC,MAAME,UAAUN,IAAIG,YAAY,CAACC,GAAG,CAAC;YAErC,IAAI,CAACF,QAAQ,CAACG,YAAY,CAACC,SAAS;gBAClC,OAAOC,SAASC,IAAI,CAClB;oBAAEC,SAAS;gBAAyD,GACpE;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,MAAMC,QAAQ,MAAMhB,kBAAkB;gBACpCiB,kBAAkBf,OAAOgB,aAAa,CAACD,gBAAgB;gBACvDV,MAAM,IAAIY,KAAKZ;gBACfa,SAAShB,IAAIgB,OAAO;gBACpBhB;gBACAiB,iBAAiBnB,OAAOoB,KAAK,CAACC,YAAY;gBAC1CC,YAAYd;gBACZe,cAAcvB,OAAOoB,KAAK,CAACI,SAAS;gBACpCC,cAAczB,OAAOoB,KAAK,CAACM,SAAS;gBACpCC,WAAWlB;gBACXmB,aAAa5B,OAAOoB,KAAK,CAACS,QAAQ;YACpC;YAEA,OAAOnB,SAASC,IAAI,CAAC;gBAAEG;YAAM;QAC/B;QACAgB,QAAQ;QACRC,MAAM;IACR;AACF"}
@@ -0,0 +1,3 @@
1
+ import type { Endpoint } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../types.js';
3
+ export declare function createBookingEndpoint(config: ResolvedReservationPluginConfig): Endpoint;
@@ -0,0 +1,32 @@
1
+ export function createBookingEndpoint(config) {
2
+ return {
3
+ handler: async (req)=>{
4
+ const data = await req.json?.();
5
+ // Call beforeBookingCreate plugin hooks before creating the reservation
6
+ let bookingData = data;
7
+ if (config.hooks?.beforeBookingCreate) {
8
+ for (const hook of config.hooks.beforeBookingCreate){
9
+ bookingData = await hook({
10
+ data: bookingData,
11
+ req
12
+ }) ?? bookingData;
13
+ }
14
+ }
15
+ // Create via Payload Local API — collection hooks handle conflict detection,
16
+ // endTime calculation, and status transitions
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ const reservation = await req.payload.create({
19
+ collection: config.slugs.reservations,
20
+ data: bookingData,
21
+ req
22
+ });
23
+ return Response.json(reservation, {
24
+ status: 201
25
+ });
26
+ },
27
+ method: 'post',
28
+ path: '/reserve/book'
29
+ };
30
+ }
31
+
32
+ //# sourceMappingURL=createBooking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/createBooking.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createBookingEndpoint(config: ResolvedReservationPluginConfig): Endpoint {\n return {\n handler: async (req) => {\n const data = (await req.json?.()) as Record<string, unknown>\n\n // Call beforeBookingCreate plugin hooks before creating the reservation\n let bookingData = data\n if (config.hooks?.beforeBookingCreate) {\n for (const hook of config.hooks.beforeBookingCreate) {\n bookingData = (await hook({ data: bookingData, req })) ?? bookingData\n }\n }\n\n // Create via Payload Local API — collection hooks handle conflict detection,\n // endTime calculation, and status transitions\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const reservation = await (req.payload.create as any)({\n collection: config.slugs.reservations,\n data: bookingData,\n req,\n })\n\n return Response.json(reservation, { status: 201 })\n },\n method: 'post',\n path: '/reserve/book',\n }\n}\n"],"names":["createBookingEndpoint","config","handler","req","data","json","bookingData","hooks","beforeBookingCreate","hook","reservation","payload","create","collection","slugs","reservations","Response","status","method","path"],"mappings":"AAIA,OAAO,SAASA,sBAAsBC,MAAuC;IAC3E,OAAO;QACLC,SAAS,OAAOC;YACd,MAAMC,OAAQ,MAAMD,IAAIE,IAAI;YAE5B,wEAAwE;YACxE,IAAIC,cAAcF;YAClB,IAAIH,OAAOM,KAAK,EAAEC,qBAAqB;gBACrC,KAAK,MAAMC,QAAQR,OAAOM,KAAK,CAACC,mBAAmB,CAAE;oBACnDF,cAAc,AAAC,MAAMG,KAAK;wBAAEL,MAAME;wBAAaH;oBAAI,MAAOG;gBAC5D;YACF;YAEA,6EAA6E;YAC7E,8CAA8C;YAC9C,8DAA8D;YAC9D,MAAMI,cAAc,MAAM,AAACP,IAAIQ,OAAO,CAACC,MAAM,CAAS;gBACpDC,YAAYZ,OAAOa,KAAK,CAACC,YAAY;gBACrCX,MAAME;gBACNH;YACF;YAEA,OAAOa,SAASX,IAAI,CAACK,aAAa;gBAAEO,QAAQ;YAAI;QAClD;QACAC,QAAQ;QACRC,MAAM;IACR;AACF"}
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Inspect a collection's field list and return the set of top-level named
3
+ * fields as a plain Set<string>. Unnamed fields (rows, groups without a name,
4
+ * etc.) are skipped.
5
+ */ function getNamedFields(fields) {
6
+ const names = new Set();
7
+ for (const field of fields){
8
+ if ('name' in field) {
9
+ names.add(field.name);
10
+ }
11
+ }
12
+ return names;
13
+ }
1
14
  export function createCustomerSearchEndpoint(config) {
2
15
  return {
3
16
  handler: async (req)=>{
@@ -12,76 +25,81 @@ export function createCustomerSearchEndpoint(config) {
12
25
  const search = url.searchParams.get('search') ?? '';
13
26
  const limit = Math.min(Number(url.searchParams.get('limit') ?? '10'), 50);
14
27
  const page = Math.max(Number(url.searchParams.get('page') ?? '1'), 1);
28
+ // Detect which fields exist on the target collection at runtime
29
+ const collectionConfig = req.payload.collections[config.slugs.customers]?.config;
30
+ const availableFields = collectionConfig ? getNamedFields(collectionConfig.fields) : new Set();
31
+ const hasName = availableFields.has('name');
32
+ const hasFirstName = availableFields.has('firstName');
33
+ const hasLastName = availableFields.has('lastName');
34
+ const hasPhone = availableFields.has('phone');
15
35
  let where = {};
16
- if (search && config.customerRole) {
17
- where = {
18
- and: [
19
- {
20
- or: [
21
- {
22
- name: {
23
- contains: search
24
- }
25
- },
26
- {
27
- phone: {
28
- contains: search
29
- }
30
- },
31
- {
32
- email: {
33
- contains: search
34
- }
35
- }
36
- ]
37
- },
38
- {
39
- role: {
40
- equals: config.customerRole
41
- }
36
+ if (search) {
37
+ const orClauses = [];
38
+ if (hasName) {
39
+ orClauses.push({
40
+ name: {
41
+ contains: search
42
42
  }
43
- ]
44
- };
45
- } else if (search) {
46
- where = {
47
- or: [
48
- {
49
- name: {
50
- contains: search
51
- }
52
- },
53
- {
54
- phone: {
55
- contains: search
56
- }
57
- },
58
- {
59
- email: {
60
- contains: search
61
- }
43
+ });
44
+ }
45
+ if (hasFirstName) {
46
+ orClauses.push({
47
+ firstName: {
48
+ contains: search
62
49
  }
63
- ]
64
- };
65
- } else if (config.customerRole) {
66
- where = {
67
- role: {
68
- equals: config.customerRole
50
+ });
51
+ }
52
+ if (hasLastName) {
53
+ orClauses.push({
54
+ lastName: {
55
+ contains: search
56
+ }
57
+ });
58
+ }
59
+ // email is always present on auth collections
60
+ orClauses.push({
61
+ email: {
62
+ contains: search
69
63
  }
64
+ });
65
+ if (hasPhone) {
66
+ orClauses.push({
67
+ phone: {
68
+ contains: search
69
+ }
70
+ });
71
+ }
72
+ where = {
73
+ or: orClauses
70
74
  };
71
75
  }
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
77
  const result = await req.payload.find({
73
- collection: config.userCollection,
78
+ collection: config.slugs.customers,
74
79
  limit,
75
80
  page,
76
81
  where
77
82
  });
78
83
  return Response.json({
79
- docs: result.docs.map((doc)=>({
80
- id: doc.id,
81
- name: doc.name ?? '',
82
- email: doc.email ?? '',
83
- phone: doc.phone ?? ''
84
- })),
84
+ docs: result.docs.map((doc)=>{
85
+ const entry = {
86
+ id: doc['id'],
87
+ email: doc['email'] ?? ''
88
+ };
89
+ if (hasName) {
90
+ entry['name'] = doc['name'] ?? '';
91
+ }
92
+ if (hasFirstName) {
93
+ entry['firstName'] = doc['firstName'] ?? '';
94
+ }
95
+ if (hasLastName) {
96
+ entry['lastName'] = doc['lastName'] ?? '';
97
+ }
98
+ if (hasPhone) {
99
+ entry['phone'] = doc['phone'] ?? '';
100
+ }
101
+ return entry;
102
+ }),
85
103
  hasNextPage: result.hasNextPage,
86
104
  totalDocs: result.totalDocs
87
105
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/customerSearch.ts"],"sourcesContent":["import type { Endpoint, Where } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nexport function createCustomerSearchEndpoint(\n config: ResolvedReservationPluginConfig,\n): Endpoint {\n return {\n handler: async (req) => {\n if (!req.user) {\n return Response.json({ message: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url!)\n const search = url.searchParams.get('search') ?? ''\n const limit = Math.min(Number(url.searchParams.get('limit') ?? '10'), 50)\n const page = Math.max(Number(url.searchParams.get('page') ?? '1'), 1)\n\n let where: Where = {}\n\n if (search && config.customerRole) {\n where = {\n and: [\n {\n or: [\n { name: { contains: search } },\n { phone: { contains: search } },\n { email: { contains: search } },\n ],\n },\n { role: { equals: config.customerRole } },\n ],\n }\n } else if (search) {\n where = {\n or: [\n { name: { contains: search } },\n { phone: { contains: search } },\n { email: { contains: search } },\n ],\n }\n } else if (config.customerRole) {\n where = { role: { equals: config.customerRole } }\n }\n\n const result = await req.payload.find({\n collection: config.userCollection,\n limit,\n page,\n where,\n })\n\n return Response.json({\n docs: result.docs.map((doc: Record<string, unknown>) => ({\n id: doc.id,\n name: doc.name ?? '',\n email: doc.email ?? '',\n phone: doc.phone ?? '',\n })),\n hasNextPage: result.hasNextPage,\n totalDocs: result.totalDocs,\n })\n },\n method: 'get',\n path: '/reservation-customer-search',\n }\n}\n"],"names":["createCustomerSearchEndpoint","config","handler","req","user","Response","json","message","status","url","URL","search","searchParams","get","limit","Math","min","Number","page","max","where","customerRole","and","or","name","contains","phone","email","role","equals","result","payload","find","collection","userCollection","docs","map","doc","id","hasNextPage","totalDocs","method","path"],"mappings":"AAIA,OAAO,SAASA,6BACdC,MAAuC;IAEvC,OAAO;QACLC,SAAS,OAAOC;YACd,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAAe,GAAG;oBAAEC,QAAQ;gBAAI;YAClE;YAEA,MAAMC,MAAM,IAAIC,IAAIP,IAAIM,GAAG;YAC3B,MAAME,SAASF,IAAIG,YAAY,CAACC,GAAG,CAAC,aAAa;YACjD,MAAMC,QAAQC,KAAKC,GAAG,CAACC,OAAOR,IAAIG,YAAY,CAACC,GAAG,CAAC,YAAY,OAAO;YACtE,MAAMK,OAAOH,KAAKI,GAAG,CAACF,OAAOR,IAAIG,YAAY,CAACC,GAAG,CAAC,WAAW,MAAM;YAEnE,IAAIO,QAAe,CAAC;YAEpB,IAAIT,UAAUV,OAAOoB,YAAY,EAAE;gBACjCD,QAAQ;oBACNE,KAAK;wBACH;4BACEC,IAAI;gCACF;oCAAEC,MAAM;wCAAEC,UAAUd;oCAAO;gCAAE;gCAC7B;oCAAEe,OAAO;wCAAED,UAAUd;oCAAO;gCAAE;gCAC9B;oCAAEgB,OAAO;wCAAEF,UAAUd;oCAAO;gCAAE;6BAC/B;wBACH;wBACA;4BAAEiB,MAAM;gCAAEC,QAAQ5B,OAAOoB,YAAY;4BAAC;wBAAE;qBACzC;gBACH;YACF,OAAO,IAAIV,QAAQ;gBACjBS,QAAQ;oBACNG,IAAI;wBACF;4BAAEC,MAAM;gCAAEC,UAAUd;4BAAO;wBAAE;wBAC7B;4BAAEe,OAAO;gCAAED,UAAUd;4BAAO;wBAAE;wBAC9B;4BAAEgB,OAAO;gCAAEF,UAAUd;4BAAO;wBAAE;qBAC/B;gBACH;YACF,OAAO,IAAIV,OAAOoB,YAAY,EAAE;gBAC9BD,QAAQ;oBAAEQ,MAAM;wBAAEC,QAAQ5B,OAAOoB,YAAY;oBAAC;gBAAE;YAClD;YAEA,MAAMS,SAAS,MAAM3B,IAAI4B,OAAO,CAACC,IAAI,CAAC;gBACpCC,YAAYhC,OAAOiC,cAAc;gBACjCpB;gBACAI;gBACAE;YACF;YAEA,OAAOf,SAASC,IAAI,CAAC;gBACnB6B,MAAML,OAAOK,IAAI,CAACC,GAAG,CAAC,CAACC,MAAkC,CAAA;wBACvDC,IAAID,IAAIC,EAAE;wBACVd,MAAMa,IAAIb,IAAI,IAAI;wBAClBG,OAAOU,IAAIV,KAAK,IAAI;wBACpBD,OAAOW,IAAIX,KAAK,IAAI;oBACtB,CAAA;gBACAa,aAAaT,OAAOS,WAAW;gBAC/BC,WAAWV,OAAOU,SAAS;YAC7B;QACF;QACAC,QAAQ;QACRC,MAAM;IACR;AACF"}
1
+ {"version":3,"sources":["../../src/endpoints/customerSearch.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, Field, Where } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\n/**\n * Inspect a collection's field list and return the set of top-level named\n * fields as a plain Set<string>. Unnamed fields (rows, groups without a name,\n * etc.) are skipped.\n */\nfunction getNamedFields(fields: Field[]): Set<string> {\n const names = new Set<string>()\n for (const field of fields) {\n if ('name' in field) {\n names.add(field.name)\n }\n }\n return names\n}\n\nexport function createCustomerSearchEndpoint(\n config: ResolvedReservationPluginConfig,\n): Endpoint {\n return {\n handler: async (req) => {\n if (!req.user) {\n return Response.json({ message: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url!)\n const search = url.searchParams.get('search') ?? ''\n const limit = Math.min(Number(url.searchParams.get('limit') ?? '10'), 50)\n const page = Math.max(Number(url.searchParams.get('page') ?? '1'), 1)\n\n // Detect which fields exist on the target collection at runtime\n const collectionConfig = req.payload.collections[config.slugs.customers as unknown as CollectionSlug]?.config\n const availableFields: Set<string> = collectionConfig\n ? getNamedFields(collectionConfig.fields)\n : new Set()\n\n const hasName = availableFields.has('name')\n const hasFirstName = availableFields.has('firstName')\n const hasLastName = availableFields.has('lastName')\n const hasPhone = availableFields.has('phone')\n\n let where: Where = {}\n\n if (search) {\n const orClauses: Where[] = []\n\n if (hasName) {\n orClauses.push({ name: { contains: search } })\n }\n if (hasFirstName) {\n orClauses.push({ firstName: { contains: search } })\n }\n if (hasLastName) {\n orClauses.push({ lastName: { contains: search } })\n }\n // email is always present on auth collections\n orClauses.push({ email: { contains: search } })\n if (hasPhone) {\n orClauses.push({ phone: { contains: search } })\n }\n\n where = { or: orClauses }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = await (req.payload.find as any)({\n collection: config.slugs.customers,\n limit,\n page,\n where,\n })\n\n return Response.json({\n docs: (result.docs as Record<string, unknown>[]).map((doc) => {\n const entry: Record<string, unknown> = {\n id: doc['id'],\n email: doc['email'] ?? '',\n }\n\n if (hasName) {\n entry['name'] = doc['name'] ?? ''\n }\n if (hasFirstName) {\n entry['firstName'] = doc['firstName'] ?? ''\n }\n if (hasLastName) {\n entry['lastName'] = doc['lastName'] ?? ''\n }\n if (hasPhone) {\n entry['phone'] = doc['phone'] ?? ''\n }\n\n return entry\n }),\n hasNextPage: result.hasNextPage,\n totalDocs: result.totalDocs,\n })\n },\n method: 'get',\n path: '/reservation-customer-search',\n }\n}\n"],"names":["getNamedFields","fields","names","Set","field","add","name","createCustomerSearchEndpoint","config","handler","req","user","Response","json","message","status","url","URL","search","searchParams","get","limit","Math","min","Number","page","max","collectionConfig","payload","collections","slugs","customers","availableFields","hasName","has","hasFirstName","hasLastName","hasPhone","where","orClauses","push","contains","firstName","lastName","email","phone","or","result","find","collection","docs","map","doc","entry","id","hasNextPage","totalDocs","method","path"],"mappings":"AAIA;;;;CAIC,GACD,SAASA,eAAeC,MAAe;IACrC,MAAMC,QAAQ,IAAIC;IAClB,KAAK,MAAMC,SAASH,OAAQ;QAC1B,IAAI,UAAUG,OAAO;YACnBF,MAAMG,GAAG,CAACD,MAAME,IAAI;QACtB;IACF;IACA,OAAOJ;AACT;AAEA,OAAO,SAASK,6BACdC,MAAuC;IAEvC,OAAO;QACLC,SAAS,OAAOC;YACd,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAAe,GAAG;oBAAEC,QAAQ;gBAAI;YAClE;YAEA,MAAMC,MAAM,IAAIC,IAAIP,IAAIM,GAAG;YAC3B,MAAME,SAASF,IAAIG,YAAY,CAACC,GAAG,CAAC,aAAa;YACjD,MAAMC,QAAQC,KAAKC,GAAG,CAACC,OAAOR,IAAIG,YAAY,CAACC,GAAG,CAAC,YAAY,OAAO;YACtE,MAAMK,OAAOH,KAAKI,GAAG,CAACF,OAAOR,IAAIG,YAAY,CAACC,GAAG,CAAC,WAAW,MAAM;YAEnE,gEAAgE;YAChE,MAAMO,mBAAmBjB,IAAIkB,OAAO,CAACC,WAAW,CAACrB,OAAOsB,KAAK,CAACC,SAAS,CAA8B,EAAEvB;YACvG,MAAMwB,kBAA+BL,mBACjC3B,eAAe2B,iBAAiB1B,MAAM,IACtC,IAAIE;YAER,MAAM8B,UAAUD,gBAAgBE,GAAG,CAAC;YACpC,MAAMC,eAAeH,gBAAgBE,GAAG,CAAC;YACzC,MAAME,cAAcJ,gBAAgBE,GAAG,CAAC;YACxC,MAAMG,WAAWL,gBAAgBE,GAAG,CAAC;YAErC,IAAII,QAAe,CAAC;YAEpB,IAAIpB,QAAQ;gBACV,MAAMqB,YAAqB,EAAE;gBAE7B,IAAIN,SAAS;oBACXM,UAAUC,IAAI,CAAC;wBAAElC,MAAM;4BAAEmC,UAAUvB;wBAAO;oBAAE;gBAC9C;gBACA,IAAIiB,cAAc;oBAChBI,UAAUC,IAAI,CAAC;wBAAEE,WAAW;4BAAED,UAAUvB;wBAAO;oBAAE;gBACnD;gBACA,IAAIkB,aAAa;oBACfG,UAAUC,IAAI,CAAC;wBAAEG,UAAU;4BAAEF,UAAUvB;wBAAO;oBAAE;gBAClD;gBACA,8CAA8C;gBAC9CqB,UAAUC,IAAI,CAAC;oBAAEI,OAAO;wBAAEH,UAAUvB;oBAAO;gBAAE;gBAC7C,IAAImB,UAAU;oBACZE,UAAUC,IAAI,CAAC;wBAAEK,OAAO;4BAAEJ,UAAUvB;wBAAO;oBAAE;gBAC/C;gBAEAoB,QAAQ;oBAAEQ,IAAIP;gBAAU;YAC1B;YAEA,8DAA8D;YAC9D,MAAMQ,SAAS,MAAM,AAACrC,IAAIkB,OAAO,CAACoB,IAAI,CAAS;gBAC7CC,YAAYzC,OAAOsB,KAAK,CAACC,SAAS;gBAClCV;gBACAI;gBACAa;YACF;YAEA,OAAO1B,SAASC,IAAI,CAAC;gBACnBqC,MAAM,AAACH,OAAOG,IAAI,CAA+BC,GAAG,CAAC,CAACC;oBACpD,MAAMC,QAAiC;wBACrCC,IAAIF,GAAG,CAAC,KAAK;wBACbR,OAAOQ,GAAG,CAAC,QAAQ,IAAI;oBACzB;oBAEA,IAAInB,SAAS;wBACXoB,KAAK,CAAC,OAAO,GAAGD,GAAG,CAAC,OAAO,IAAI;oBACjC;oBACA,IAAIjB,cAAc;wBAChBkB,KAAK,CAAC,YAAY,GAAGD,GAAG,CAAC,YAAY,IAAI;oBAC3C;oBACA,IAAIhB,aAAa;wBACfiB,KAAK,CAAC,WAAW,GAAGD,GAAG,CAAC,WAAW,IAAI;oBACzC;oBACA,IAAIf,UAAU;wBACZgB,KAAK,CAAC,QAAQ,GAAGD,GAAG,CAAC,QAAQ,IAAI;oBACnC;oBAEA,OAAOC;gBACT;gBACAE,aAAaR,OAAOQ,WAAW;gBAC/BC,WAAWT,OAAOS,SAAS;YAC7B;QACF;QACAC,QAAQ;QACRC,MAAM;IACR;AACF"}
@@ -0,0 +1,3 @@
1
+ import type { Endpoint } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../types.js';
3
+ export declare function createGetSlotsEndpoint(config: ResolvedReservationPluginConfig): Endpoint;
@@ -0,0 +1,51 @@
1
+ import { getAvailableSlots } from '../services/AvailabilityService.js';
2
+ export function createGetSlotsEndpoint(config) {
3
+ return {
4
+ handler: async (req)=>{
5
+ const url = new URL(req.url);
6
+ const date = url.searchParams.get('date');
7
+ const resource = url.searchParams.get('resource');
8
+ const service = url.searchParams.get('service');
9
+ if (!date || !resource || !service) {
10
+ return Response.json({
11
+ error: 'Missing required query params: resource, date, service'
12
+ }, {
13
+ status: 400
14
+ });
15
+ }
16
+ const parsedDate = new Date(date);
17
+ if (isNaN(parsedDate.getTime())) {
18
+ return Response.json({
19
+ error: 'Invalid date format. Expected YYYY-MM-DD'
20
+ }, {
21
+ status: 400
22
+ });
23
+ }
24
+ const guestCount = Math.max(Number(url.searchParams.get('guestCount') ?? '1'), 1);
25
+ const slots = await getAvailableSlots({
26
+ blockingStatuses: config.statusMachine.blockingStatuses,
27
+ date: parsedDate,
28
+ payload: req.payload,
29
+ req,
30
+ reservationSlug: config.slugs.reservations,
31
+ resourceId: resource,
32
+ resourceSlug: config.slugs.resources,
33
+ scheduleSlug: config.slugs.schedules,
34
+ serviceId: service,
35
+ serviceSlug: config.slugs.services
36
+ });
37
+ return Response.json({
38
+ date,
39
+ guestCount,
40
+ slots: slots.map((s)=>({
41
+ end: s.end.toISOString(),
42
+ start: s.start.toISOString()
43
+ }))
44
+ });
45
+ },
46
+ method: 'get',
47
+ path: '/reserve/slots'
48
+ };
49
+ }
50
+
51
+ //# sourceMappingURL=getSlots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/getSlots.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../types.js'\n\nimport { getAvailableSlots } from '../services/AvailabilityService.js'\n\nexport function createGetSlotsEndpoint(config: ResolvedReservationPluginConfig): Endpoint {\n return {\n handler: async (req) => {\n const url = new URL(req.url!)\n const date = url.searchParams.get('date')\n const resource = url.searchParams.get('resource')\n const service = url.searchParams.get('service')\n\n if (!date || !resource || !service) {\n return Response.json(\n { error: 'Missing required query params: resource, date, service' },\n { status: 400 },\n )\n }\n\n const parsedDate = new Date(date)\n if (isNaN(parsedDate.getTime())) {\n return Response.json(\n { error: 'Invalid date format. Expected YYYY-MM-DD' },\n { status: 400 },\n )\n }\n\n const guestCount = Math.max(Number(url.searchParams.get('guestCount') ?? '1'), 1)\n\n const slots = await getAvailableSlots({\n blockingStatuses: config.statusMachine.blockingStatuses,\n date: parsedDate,\n payload: req.payload,\n req,\n reservationSlug: config.slugs.reservations,\n resourceId: resource,\n resourceSlug: config.slugs.resources,\n scheduleSlug: config.slugs.schedules,\n serviceId: service,\n serviceSlug: config.slugs.services,\n })\n\n return Response.json({\n date,\n guestCount,\n slots: slots.map((s) => ({ end: s.end.toISOString(), start: s.start.toISOString() })),\n })\n },\n method: 'get',\n path: '/reserve/slots',\n }\n}\n"],"names":["getAvailableSlots","createGetSlotsEndpoint","config","handler","req","url","URL","date","searchParams","get","resource","service","Response","json","error","status","parsedDate","Date","isNaN","getTime","guestCount","Math","max","Number","slots","blockingStatuses","statusMachine","payload","reservationSlug","slugs","reservations","resourceId","resourceSlug","resources","scheduleSlug","schedules","serviceId","serviceSlug","services","map","s","end","toISOString","start","method","path"],"mappings":"AAIA,SAASA,iBAAiB,QAAQ,qCAAoC;AAEtE,OAAO,SAASC,uBAAuBC,MAAuC;IAC5E,OAAO;QACLC,SAAS,OAAOC;YACd,MAAMC,MAAM,IAAIC,IAAIF,IAAIC,GAAG;YAC3B,MAAME,OAAOF,IAAIG,YAAY,CAACC,GAAG,CAAC;YAClC,MAAMC,WAAWL,IAAIG,YAAY,CAACC,GAAG,CAAC;YACtC,MAAME,UAAUN,IAAIG,YAAY,CAACC,GAAG,CAAC;YAErC,IAAI,CAACF,QAAQ,CAACG,YAAY,CAACC,SAAS;gBAClC,OAAOC,SAASC,IAAI,CAClB;oBAAEC,OAAO;gBAAyD,GAClE;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,MAAMC,aAAa,IAAIC,KAAKV;YAC5B,IAAIW,MAAMF,WAAWG,OAAO,KAAK;gBAC/B,OAAOP,SAASC,IAAI,CAClB;oBAAEC,OAAO;gBAA2C,GACpD;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,MAAMK,aAAaC,KAAKC,GAAG,CAACC,OAAOlB,IAAIG,YAAY,CAACC,GAAG,CAAC,iBAAiB,MAAM;YAE/E,MAAMe,QAAQ,MAAMxB,kBAAkB;gBACpCyB,kBAAkBvB,OAAOwB,aAAa,CAACD,gBAAgB;gBACvDlB,MAAMS;gBACNW,SAASvB,IAAIuB,OAAO;gBACpBvB;gBACAwB,iBAAiB1B,OAAO2B,KAAK,CAACC,YAAY;gBAC1CC,YAAYrB;gBACZsB,cAAc9B,OAAO2B,KAAK,CAACI,SAAS;gBACpCC,cAAchC,OAAO2B,KAAK,CAACM,SAAS;gBACpCC,WAAWzB;gBACX0B,aAAanC,OAAO2B,KAAK,CAACS,QAAQ;YACpC;YAEA,OAAO1B,SAASC,IAAI,CAAC;gBACnBN;gBACAa;gBACAI,OAAOA,MAAMe,GAAG,CAAC,CAACC,IAAO,CAAA;wBAAEC,KAAKD,EAAEC,GAAG,CAACC,WAAW;wBAAIC,OAAOH,EAAEG,KAAK,CAACD,WAAW;oBAAG,CAAA;YACpF;QACF;QACAE,QAAQ;QACRC,MAAM;IACR;AACF"}
@@ -1,4 +1,6 @@
1
1
  export { calculateEndTime } from './reservations/calculateEndTime.js';
2
+ export { checkIdempotency } from './reservations/checkIdempotency.js';
3
+ export { onStatusChange } from './reservations/onStatusChange.js';
2
4
  export { validateCancellation } from './reservations/validateCancellation.js';
3
5
  export { validateConflicts } from './reservations/validateConflicts.js';
4
6
  export { validateStatusTransition } from './reservations/validateStatusTransition.js';
@@ -1,4 +1,6 @@
1
1
  export { calculateEndTime } from './reservations/calculateEndTime.js';
2
+ export { checkIdempotency } from './reservations/checkIdempotency.js';
3
+ export { onStatusChange } from './reservations/onStatusChange.js';
2
4
  export { validateCancellation } from './reservations/validateCancellation.js';
3
5
  export { validateConflicts } from './reservations/validateConflicts.js';
4
6
  export { validateStatusTransition } from './reservations/validateStatusTransition.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/index.ts"],"sourcesContent":["export { calculateEndTime } from './reservations/calculateEndTime.js'\nexport { validateCancellation } from './reservations/validateCancellation.js'\nexport { validateConflicts } from './reservations/validateConflicts.js'\nexport { validateStatusTransition } from './reservations/validateStatusTransition.js'\n"],"names":["calculateEndTime","validateCancellation","validateConflicts","validateStatusTransition"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SAASC,oBAAoB,QAAQ,yCAAwC;AAC7E,SAASC,iBAAiB,QAAQ,sCAAqC;AACvE,SAASC,wBAAwB,QAAQ,6CAA4C"}
1
+ {"version":3,"sources":["../../src/hooks/index.ts"],"sourcesContent":["export { calculateEndTime } from './reservations/calculateEndTime.js'\nexport { checkIdempotency } from './reservations/checkIdempotency.js'\nexport { onStatusChange } from './reservations/onStatusChange.js'\nexport { validateCancellation } from './reservations/validateCancellation.js'\nexport { validateConflicts } from './reservations/validateConflicts.js'\nexport { validateStatusTransition } from './reservations/validateStatusTransition.js'\n"],"names":["calculateEndTime","checkIdempotency","onStatusChange","validateCancellation","validateConflicts","validateStatusTransition"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SAASC,gBAAgB,QAAQ,qCAAoC;AACrE,SAASC,cAAc,QAAQ,mCAAkC;AACjE,SAASC,oBAAoB,QAAQ,yCAAwC;AAC7E,SAASC,iBAAiB,QAAQ,sCAAqC;AACvE,SAASC,wBAAwB,QAAQ,6CAA4C"}
@@ -1,4 +1,6 @@
1
- import { addMinutes } from '../../utilities/slotUtils.js';
1
+ import { ValidationError } from 'payload';
2
+ import { computeEndTime } from '../../services/AvailabilityService.js';
3
+ import { resolveReservationItems } from '../../utilities/resolveReservationItems.js';
2
4
  export const calculateEndTime = (config)=>async ({ context, data, req })=>{
3
5
  if (context?.skipReservationHooks) {
4
6
  return data;
@@ -6,15 +8,79 @@ export const calculateEndTime = (config)=>async ({ context, data, req })=>{
6
8
  if (!data?.startTime || !data?.service) {
7
9
  return data;
8
10
  }
9
- const serviceId = typeof data.service === 'object' ? data.service.id : data.service;
10
- const service = await req.payload.findByID({
11
- id: serviceId,
12
- collection: config.slugs.services,
13
- req
14
- });
15
- if (service?.duration) {
11
+ const items = resolveReservationItems(data);
12
+ if (items.length <= 1) {
13
+ // Single-resource: compute top-level endTime
14
+ const serviceId = typeof data.service === 'object' ? data.service.id : data.service;
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const service = await req.payload.findByID({
17
+ id: serviceId,
18
+ collection: config.slugs.services,
19
+ req
20
+ });
21
+ if (!service?.duration && service?.durationType !== 'full-day') {
22
+ return data;
23
+ }
24
+ const durationType = service.durationType ?? 'fixed';
16
25
  const startDate = new Date(data.startTime);
17
- data.endTime = addMinutes(startDate, service.duration).toISOString();
26
+ if (durationType === 'flexible') {
27
+ if (!data.endTime) {
28
+ throw new ValidationError({
29
+ errors: [
30
+ {
31
+ message: 'endTime is required for flexible duration services',
32
+ path: 'endTime'
33
+ }
34
+ ]
35
+ });
36
+ }
37
+ // Validate customer-provided endTime (computeEndTime returns it back)
38
+ computeEndTime({
39
+ durationType: 'flexible',
40
+ endTime: new Date(data.endTime),
41
+ serviceDuration: service.duration,
42
+ startTime: startDate
43
+ });
44
+ } else {
45
+ const result = computeEndTime({
46
+ durationType,
47
+ serviceDuration: service.duration ?? 0,
48
+ startTime: startDate
49
+ });
50
+ data.endTime = result.endTime.toISOString();
51
+ }
52
+ } else {
53
+ // Multi-resource: compute endTime per item
54
+ for (const item of data.items){
55
+ if (!item.startTime) {
56
+ continue;
57
+ }
58
+ const itemServiceId = typeof item.service === 'object' ? item.service.id : item.service ?? (typeof data.service === 'object' ? data.service.id : data.service);
59
+ if (!itemServiceId) {
60
+ continue;
61
+ }
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const service = await req.payload.findByID({
64
+ id: itemServiceId,
65
+ collection: config.slugs.services,
66
+ req
67
+ });
68
+ if (!service?.duration && service?.durationType !== 'full-day') {
69
+ continue;
70
+ }
71
+ const durationType = service.durationType ?? 'fixed';
72
+ if (durationType === 'flexible' && !item.endTime) {
73
+ continue;
74
+ }
75
+ if (durationType !== 'flexible') {
76
+ const result = computeEndTime({
77
+ durationType,
78
+ serviceDuration: service.duration ?? 0,
79
+ startTime: new Date(item.startTime)
80
+ });
81
+ item.endTime = result.endTime.toISOString();
82
+ }
83
+ }
18
84
  }
19
85
  return data;
20
86
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/reservations/calculateEndTime.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nimport { addMinutes } from '../../utilities/slotUtils.js'\n\nexport const calculateEndTime =\n (config: ResolvedReservationPluginConfig): CollectionBeforeChangeHook =>\n async ({ context, data, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n if (!data?.startTime || !data?.service) {return data}\n\n const serviceId = typeof data.service === 'object' ? data.service.id : data.service\n\n const service = await req.payload.findByID({\n id: serviceId,\n collection: config.slugs.services as 'services',\n req,\n })\n\n if (service?.duration) {\n const startDate = new Date(data.startTime)\n data.endTime = addMinutes(startDate, service.duration).toISOString()\n }\n\n return data\n }\n"],"names":["addMinutes","calculateEndTime","config","context","data","req","skipReservationHooks","startTime","service","serviceId","id","payload","findByID","collection","slugs","services","duration","startDate","Date","endTime","toISOString"],"mappings":"AAIA,SAASA,UAAU,QAAQ,+BAA8B;AAEzD,OAAO,MAAMC,mBACX,CAACC,SACD,OAAO,EAAEC,OAAO,EAAEC,IAAI,EAAEC,GAAG,EAAE;QAC3B,IAAIF,SAASG,sBAAsB;YAAC,OAAOF;QAAI;QAE/C,IAAI,CAACA,MAAMG,aAAa,CAACH,MAAMI,SAAS;YAAC,OAAOJ;QAAI;QAEpD,MAAMK,YAAY,OAAOL,KAAKI,OAAO,KAAK,WAAWJ,KAAKI,OAAO,CAACE,EAAE,GAAGN,KAAKI,OAAO;QAEnF,MAAMA,UAAU,MAAMH,IAAIM,OAAO,CAACC,QAAQ,CAAC;YACzCF,IAAID;YACJI,YAAYX,OAAOY,KAAK,CAACC,QAAQ;YACjCV;QACF;QAEA,IAAIG,SAASQ,UAAU;YACrB,MAAMC,YAAY,IAAIC,KAAKd,KAAKG,SAAS;YACzCH,KAAKe,OAAO,GAAGnB,WAAWiB,WAAWT,QAAQQ,QAAQ,EAAEI,WAAW;QACpE;QAEA,OAAOhB;IACT,EAAC"}
1
+ {"version":3,"sources":["../../../src/hooks/reservations/calculateEndTime.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { DurationType, ResolvedReservationPluginConfig } from '../../types.js'\n\nimport { computeEndTime } from '../../services/AvailabilityService.js'\nimport { resolveReservationItems } from '../../utilities/resolveReservationItems.js'\n\nexport const calculateEndTime =\n (config: ResolvedReservationPluginConfig): CollectionBeforeChangeHook =>\n async ({ context, data, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n if (!data?.startTime || !data?.service) {return data}\n\n const items = resolveReservationItems(data)\n\n if (items.length <= 1) {\n // Single-resource: compute top-level endTime\n const serviceId = typeof data.service === 'object' ? data.service.id : data.service\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const service = await (req.payload.findByID as any)({\n id: serviceId,\n collection: config.slugs.services,\n req,\n })\n\n if (!service?.duration && service?.durationType !== 'full-day') {return data}\n\n const durationType = ((service.durationType as string) ?? 'fixed') as DurationType\n const startDate = new Date(data.startTime)\n\n if (durationType === 'flexible') {\n if (!data.endTime) {\n throw new ValidationError({\n errors: [{ message: 'endTime is required for flexible duration services', path: 'endTime' }],\n })\n }\n // Validate customer-provided endTime (computeEndTime returns it back)\n computeEndTime({\n durationType: 'flexible',\n endTime: new Date(data.endTime),\n serviceDuration: service.duration as number,\n startTime: startDate,\n })\n } else {\n const result = computeEndTime({\n durationType,\n serviceDuration: (service.duration as number) ?? 0,\n startTime: startDate,\n })\n data.endTime = result.endTime.toISOString()\n }\n } else {\n // Multi-resource: compute endTime per item\n for (const item of data.items as Array<Record<string, unknown>>) {\n if (!item.startTime) {continue}\n\n const itemServiceId = typeof item.service === 'object'\n ? (item.service as { id: string }).id\n : (item.service as string) ?? (typeof data.service === 'object' ? data.service.id : data.service)\n\n if (!itemServiceId) {continue}\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const service = await (req.payload.findByID as any)({\n id: itemServiceId,\n collection: config.slugs.services,\n req,\n })\n\n if (!service?.duration && service?.durationType !== 'full-day') {continue}\n\n const durationType = ((service.durationType as string) ?? 'fixed') as DurationType\n\n if (durationType === 'flexible' && !item.endTime) {continue}\n\n if (durationType !== 'flexible') {\n const result = computeEndTime({\n durationType,\n serviceDuration: (service.duration as number) ?? 0,\n startTime: new Date(item.startTime as string),\n })\n item.endTime = result.endTime.toISOString()\n }\n }\n }\n\n return data\n }\n"],"names":["ValidationError","computeEndTime","resolveReservationItems","calculateEndTime","config","context","data","req","skipReservationHooks","startTime","service","items","length","serviceId","id","payload","findByID","collection","slugs","services","duration","durationType","startDate","Date","endTime","errors","message","path","serviceDuration","result","toISOString","item","itemServiceId"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAIzC,SAASC,cAAc,QAAQ,wCAAuC;AACtE,SAASC,uBAAuB,QAAQ,6CAA4C;AAEpF,OAAO,MAAMC,mBACX,CAACC,SACD,OAAO,EAAEC,OAAO,EAAEC,IAAI,EAAEC,GAAG,EAAE;QAC3B,IAAIF,SAASG,sBAAsB;YAAC,OAAOF;QAAI;QAE/C,IAAI,CAACA,MAAMG,aAAa,CAACH,MAAMI,SAAS;YAAC,OAAOJ;QAAI;QAEpD,MAAMK,QAAQT,wBAAwBI;QAEtC,IAAIK,MAAMC,MAAM,IAAI,GAAG;YACrB,6CAA6C;YAC7C,MAAMC,YAAY,OAAOP,KAAKI,OAAO,KAAK,WAAWJ,KAAKI,OAAO,CAACI,EAAE,GAAGR,KAAKI,OAAO;YAEnF,8DAA8D;YAC9D,MAAMA,UAAU,MAAM,AAACH,IAAIQ,OAAO,CAACC,QAAQ,CAAS;gBAClDF,IAAID;gBACJI,YAAYb,OAAOc,KAAK,CAACC,QAAQ;gBACjCZ;YACF;YAEA,IAAI,CAACG,SAASU,YAAYV,SAASW,iBAAiB,YAAY;gBAAC,OAAOf;YAAI;YAE5E,MAAMe,eAAgB,AAACX,QAAQW,YAAY,IAAe;YAC1D,MAAMC,YAAY,IAAIC,KAAKjB,KAAKG,SAAS;YAEzC,IAAIY,iBAAiB,YAAY;gBAC/B,IAAI,CAACf,KAAKkB,OAAO,EAAE;oBACjB,MAAM,IAAIxB,gBAAgB;wBACxByB,QAAQ;4BAAC;gCAAEC,SAAS;gCAAsDC,MAAM;4BAAU;yBAAE;oBAC9F;gBACF;gBACA,sEAAsE;gBACtE1B,eAAe;oBACboB,cAAc;oBACdG,SAAS,IAAID,KAAKjB,KAAKkB,OAAO;oBAC9BI,iBAAiBlB,QAAQU,QAAQ;oBACjCX,WAAWa;gBACb;YACF,OAAO;gBACL,MAAMO,SAAS5B,eAAe;oBAC5BoB;oBACAO,iBAAiB,AAAClB,QAAQU,QAAQ,IAAe;oBACjDX,WAAWa;gBACb;gBACAhB,KAAKkB,OAAO,GAAGK,OAAOL,OAAO,CAACM,WAAW;YAC3C;QACF,OAAO;YACL,2CAA2C;YAC3C,KAAK,MAAMC,QAAQzB,KAAKK,KAAK,CAAoC;gBAC/D,IAAI,CAACoB,KAAKtB,SAAS,EAAE;oBAAC;gBAAQ;gBAE9B,MAAMuB,gBAAgB,OAAOD,KAAKrB,OAAO,KAAK,WAC1C,AAACqB,KAAKrB,OAAO,CAAoBI,EAAE,GACnC,AAACiB,KAAKrB,OAAO,IAAgB,CAAA,OAAOJ,KAAKI,OAAO,KAAK,WAAWJ,KAAKI,OAAO,CAACI,EAAE,GAAGR,KAAKI,OAAO,AAAD;gBAEjG,IAAI,CAACsB,eAAe;oBAAC;gBAAQ;gBAE7B,8DAA8D;gBAC9D,MAAMtB,UAAU,MAAM,AAACH,IAAIQ,OAAO,CAACC,QAAQ,CAAS;oBAClDF,IAAIkB;oBACJf,YAAYb,OAAOc,KAAK,CAACC,QAAQ;oBACjCZ;gBACF;gBAEA,IAAI,CAACG,SAASU,YAAYV,SAASW,iBAAiB,YAAY;oBAAC;gBAAQ;gBAEzE,MAAMA,eAAgB,AAACX,QAAQW,YAAY,IAAe;gBAE1D,IAAIA,iBAAiB,cAAc,CAACU,KAAKP,OAAO,EAAE;oBAAC;gBAAQ;gBAE3D,IAAIH,iBAAiB,YAAY;oBAC/B,MAAMQ,SAAS5B,eAAe;wBAC5BoB;wBACAO,iBAAiB,AAAClB,QAAQU,QAAQ,IAAe;wBACjDX,WAAW,IAAIc,KAAKQ,KAAKtB,SAAS;oBACpC;oBACAsB,KAAKP,OAAO,GAAGK,OAAOL,OAAO,CAACM,WAAW;gBAC3C;YACF;QACF;QAEA,OAAOxB;IACT,EAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../../types.js';
3
+ export declare const checkIdempotency: (config: ResolvedReservationPluginConfig) => CollectionBeforeChangeHook;
@@ -0,0 +1,32 @@
1
+ import { ValidationError } from 'payload';
2
+ export const checkIdempotency = (config)=>async ({ context, data, operation, req })=>{
3
+ if (context?.skipReservationHooks) {
4
+ return data;
5
+ }
6
+ if (operation !== 'create' || !data?.idempotencyKey) {
7
+ return data;
8
+ }
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ const { totalDocs } = await req.payload.count({
11
+ collection: config.slugs.reservations,
12
+ req,
13
+ where: {
14
+ idempotencyKey: {
15
+ equals: data.idempotencyKey
16
+ }
17
+ }
18
+ });
19
+ if (totalDocs > 0) {
20
+ throw new ValidationError({
21
+ errors: [
22
+ {
23
+ message: 'Duplicate reservation',
24
+ path: 'idempotencyKey'
25
+ }
26
+ ]
27
+ });
28
+ }
29
+ return data;
30
+ };
31
+
32
+ //# sourceMappingURL=checkIdempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/reservations/checkIdempotency.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nexport const checkIdempotency =\n (config: ResolvedReservationPluginConfig): CollectionBeforeChangeHook =>\n async ({ context, data, operation, req }) => {\n if (context?.skipReservationHooks) {return data}\n if (operation !== 'create' || !data?.idempotencyKey) {return data}\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { totalDocs } = await (req.payload.count as any)({\n collection: config.slugs.reservations,\n req,\n where: { idempotencyKey: { equals: data.idempotencyKey } },\n })\n\n if (totalDocs > 0) {\n throw new ValidationError({\n errors: [{ message: 'Duplicate reservation', path: 'idempotencyKey' }],\n })\n }\n return data\n }\n"],"names":["ValidationError","checkIdempotency","config","context","data","operation","req","skipReservationHooks","idempotencyKey","totalDocs","payload","count","collection","slugs","reservations","where","equals","errors","message","path"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAIzC,OAAO,MAAMC,mBACX,CAACC,SACD,OAAO,EAAEC,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,GAAG,EAAE;QACtC,IAAIH,SAASI,sBAAsB;YAAC,OAAOH;QAAI;QAC/C,IAAIC,cAAc,YAAY,CAACD,MAAMI,gBAAgB;YAAC,OAAOJ;QAAI;QAEjE,8DAA8D;QAC9D,MAAM,EAAEK,SAAS,EAAE,GAAG,MAAM,AAACH,IAAII,OAAO,CAACC,KAAK,CAAS;YACrDC,YAAYV,OAAOW,KAAK,CAACC,YAAY;YACrCR;YACAS,OAAO;gBAAEP,gBAAgB;oBAAEQ,QAAQZ,KAAKI,cAAc;gBAAC;YAAE;QAC3D;QAEA,IAAIC,YAAY,GAAG;YACjB,MAAM,IAAIT,gBAAgB;gBACxBiB,QAAQ;oBAAC;wBAAEC,SAAS;wBAAyBC,MAAM;oBAAiB;iBAAE;YACxE;QACF;QACA,OAAOf;IACT,EAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionAfterChangeHook } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../../types.js';
3
+ export declare const onStatusChange: (config: ResolvedReservationPluginConfig) => CollectionAfterChangeHook;