payload-reserve 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1145 -0
  3. package/dist/collections/Reservations.d.ts +3 -0
  4. package/dist/collections/Reservations.js +124 -0
  5. package/dist/collections/Reservations.js.map +1 -0
  6. package/dist/collections/Resources.d.ts +3 -0
  7. package/dist/collections/Resources.js +53 -0
  8. package/dist/collections/Resources.js.map +1 -0
  9. package/dist/collections/Schedules.d.ts +3 -0
  10. package/dist/collections/Schedules.js +182 -0
  11. package/dist/collections/Schedules.js.map +1 -0
  12. package/dist/collections/Services.d.ts +3 -0
  13. package/dist/collections/Services.js +75 -0
  14. package/dist/collections/Services.js.map +1 -0
  15. package/dist/components/AvailabilityOverview/AvailabilityOverview.module.css +103 -0
  16. package/dist/components/AvailabilityOverview/index.d.ts +2 -0
  17. package/dist/components/AvailabilityOverview/index.js +277 -0
  18. package/dist/components/AvailabilityOverview/index.js.map +1 -0
  19. package/dist/components/CalendarView/CalendarView.module.css +283 -0
  20. package/dist/components/CalendarView/index.d.ts +3 -0
  21. package/dist/components/CalendarView/index.js +508 -0
  22. package/dist/components/CalendarView/index.js.map +1 -0
  23. package/dist/components/DashboardWidget/DashboardWidget.module.css +53 -0
  24. package/dist/components/DashboardWidget/DashboardWidgetServer.d.ts +2 -0
  25. package/dist/components/DashboardWidget/DashboardWidgetServer.js +126 -0
  26. package/dist/components/DashboardWidget/DashboardWidgetServer.js.map +1 -0
  27. package/dist/defaults.d.ts +12 -0
  28. package/dist/defaults.js +29 -0
  29. package/dist/defaults.js.map +1 -0
  30. package/dist/exports/client.d.ts +2 -0
  31. package/dist/exports/client.js +4 -0
  32. package/dist/exports/client.js.map +1 -0
  33. package/dist/exports/rsc.d.ts +1 -0
  34. package/dist/exports/rsc.js +3 -0
  35. package/dist/exports/rsc.js.map +1 -0
  36. package/dist/hooks/index.d.ts +4 -0
  37. package/dist/hooks/index.js +6 -0
  38. package/dist/hooks/index.js.map +1 -0
  39. package/dist/hooks/reservations/calculateEndTime.d.ts +3 -0
  40. package/dist/hooks/reservations/calculateEndTime.js +22 -0
  41. package/dist/hooks/reservations/calculateEndTime.js.map +1 -0
  42. package/dist/hooks/reservations/validateCancellation.d.ts +3 -0
  43. package/dist/hooks/reservations/validateCancellation.js +38 -0
  44. package/dist/hooks/reservations/validateCancellation.js.map +1 -0
  45. package/dist/hooks/reservations/validateConflicts.d.ts +3 -0
  46. package/dist/hooks/reservations/validateConflicts.js +86 -0
  47. package/dist/hooks/reservations/validateConflicts.js.map +1 -0
  48. package/dist/hooks/reservations/validateStatusTransition.d.ts +2 -0
  49. package/dist/hooks/reservations/validateStatusTransition.js +54 -0
  50. package/dist/hooks/reservations/validateStatusTransition.js.map +1 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +3 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/plugin.d.ts +3 -0
  55. package/dist/plugin.js +106 -0
  56. package/dist/plugin.js.map +1 -0
  57. package/dist/translations/en.json +86 -0
  58. package/dist/translations/index.d.ts +3 -0
  59. package/dist/translations/index.js +8 -0
  60. package/dist/translations/index.js.map +1 -0
  61. package/dist/types.d.ts +51 -0
  62. package/dist/types.js +16 -0
  63. package/dist/types.js.map +1 -0
  64. package/dist/utilities/scheduleUtils.d.ts +54 -0
  65. package/dist/utilities/scheduleUtils.js +87 -0
  66. package/dist/utilities/scheduleUtils.js.map +1 -0
  67. package/dist/utilities/slotUtils.d.ts +21 -0
  68. package/dist/utilities/slotUtils.js +28 -0
  69. package/dist/utilities/slotUtils.js.map +1 -0
  70. package/package.json +108 -0
@@ -0,0 +1,126 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styles from './DashboardWidget.module.css';
3
+ export const DashboardWidgetServer = async (props)=>{
4
+ const { req } = props;
5
+ const { i18n, payload } = req;
6
+ const t = i18n.t;
7
+ const slugs = payload.config.admin?.custom?.reservationSlugs;
8
+ if (!slugs) {
9
+ return null;
10
+ }
11
+ const now = new Date();
12
+ const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
13
+ const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
14
+ const { docs: todayReservations } = await payload.find({
15
+ collection: slugs.reservations,
16
+ limit: 100,
17
+ sort: 'startTime',
18
+ where: {
19
+ startTime: {
20
+ greater_than_equal: startOfDay.toISOString(),
21
+ less_than: endOfDay.toISOString()
22
+ }
23
+ }
24
+ });
25
+ const total = todayReservations.length;
26
+ const upcoming = todayReservations.filter((r)=>r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' && new Date(r.startTime) > now).length;
27
+ const completed = todayReservations.filter((r)=>r.status === 'completed').length;
28
+ const cancelled = todayReservations.filter((r)=>r.status === 'cancelled').length;
29
+ const nextAppointment = todayReservations.find((r)=>r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' && new Date(r.startTime) > now);
30
+ return /*#__PURE__*/ _jsxs("div", {
31
+ className: styles.wrapper,
32
+ children: [
33
+ /*#__PURE__*/ _jsx("h3", {
34
+ className: styles.title,
35
+ children: t('reservation:dashboardTitle')
36
+ }),
37
+ /*#__PURE__*/ _jsxs("div", {
38
+ className: styles.statsGrid,
39
+ children: [
40
+ /*#__PURE__*/ _jsxs("div", {
41
+ className: styles.statCard,
42
+ children: [
43
+ /*#__PURE__*/ _jsx("span", {
44
+ className: styles.statValue,
45
+ children: total
46
+ }),
47
+ /*#__PURE__*/ _jsx("span", {
48
+ className: styles.statLabel,
49
+ children: t('reservation:dashboardTotal')
50
+ })
51
+ ]
52
+ }),
53
+ /*#__PURE__*/ _jsxs("div", {
54
+ className: styles.statCard,
55
+ children: [
56
+ /*#__PURE__*/ _jsx("span", {
57
+ className: styles.statValue,
58
+ children: upcoming
59
+ }),
60
+ /*#__PURE__*/ _jsx("span", {
61
+ className: styles.statLabel,
62
+ children: t('reservation:dashboardUpcoming')
63
+ })
64
+ ]
65
+ }),
66
+ /*#__PURE__*/ _jsxs("div", {
67
+ className: styles.statCard,
68
+ children: [
69
+ /*#__PURE__*/ _jsx("span", {
70
+ className: styles.statValue,
71
+ children: completed
72
+ }),
73
+ /*#__PURE__*/ _jsx("span", {
74
+ className: styles.statLabel,
75
+ children: t('reservation:dashboardCompleted')
76
+ })
77
+ ]
78
+ }),
79
+ /*#__PURE__*/ _jsxs("div", {
80
+ className: styles.statCard,
81
+ children: [
82
+ /*#__PURE__*/ _jsx("span", {
83
+ className: styles.statValue,
84
+ children: cancelled
85
+ }),
86
+ /*#__PURE__*/ _jsx("span", {
87
+ className: styles.statLabel,
88
+ children: t('reservation:dashboardCancelled')
89
+ })
90
+ ]
91
+ })
92
+ ]
93
+ }),
94
+ nextAppointment ? /*#__PURE__*/ _jsxs("div", {
95
+ className: styles.nextAppointment,
96
+ children: [
97
+ /*#__PURE__*/ _jsx("strong", {
98
+ children: t('reservation:dashboardNextAppointment')
99
+ }),
100
+ /*#__PURE__*/ _jsxs("p", {
101
+ children: [
102
+ t('reservation:dashboardTime'),
103
+ " ",
104
+ new Date(nextAppointment.startTime).toLocaleTimeString([], {
105
+ hour: '2-digit',
106
+ minute: '2-digit'
107
+ })
108
+ ]
109
+ }),
110
+ /*#__PURE__*/ _jsxs("p", {
111
+ children: [
112
+ t('reservation:dashboardStatus'),
113
+ " ",
114
+ nextAppointment.status
115
+ ]
116
+ })
117
+ ]
118
+ }) : /*#__PURE__*/ _jsx("p", {
119
+ className: styles.noData,
120
+ children: t('reservation:dashboardNoUpcoming')
121
+ })
122
+ ]
123
+ });
124
+ };
125
+
126
+ //# sourceMappingURL=DashboardWidgetServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/DashboardWidget/DashboardWidgetServer.tsx"],"sourcesContent":["import type { WidgetServerProps } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './DashboardWidget.module.css'\n\nexport const DashboardWidgetServer = async (props: WidgetServerProps) => {\n const { req } = props\n const { i18n, payload } = req\n const t = i18n.t as PluginT\n\n const slugs = payload.config.admin?.custom?.reservationSlugs\n if (!slugs) {\n return null\n }\n\n const now = new Date()\n const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)\n\n const { docs: todayReservations } = await payload.find({\n collection: slugs.reservations,\n limit: 100,\n sort: 'startTime',\n where: {\n startTime: {\n greater_than_equal: startOfDay.toISOString(),\n less_than: endOfDay.toISOString(),\n },\n },\n })\n\n const total = todayReservations.length\n const upcoming = todayReservations.filter(\n (r: Record<string, unknown>) =>\n r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' &&\n new Date(r.startTime as string) > now,\n ).length\n const completed = todayReservations.filter(\n (r: Record<string, unknown>) => r.status === 'completed',\n ).length\n const cancelled = todayReservations.filter(\n (r: Record<string, unknown>) => r.status === 'cancelled',\n ).length\n\n const nextAppointment = todayReservations.find(\n (r: Record<string, unknown>) =>\n r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' &&\n new Date(r.startTime as string) > now,\n )\n\n return (\n <div className={styles.wrapper}>\n <h3 className={styles.title}>{t('reservation:dashboardTitle')}</h3>\n <div className={styles.statsGrid}>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{total}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardTotal')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{upcoming}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardUpcoming')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{completed}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardCompleted')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{cancelled}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardCancelled')}</span>\n </div>\n </div>\n {nextAppointment ? (\n <div className={styles.nextAppointment}>\n <strong>{t('reservation:dashboardNextAppointment')}</strong>\n <p>\n {t('reservation:dashboardTime')} {new Date(nextAppointment.startTime as string).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n </p>\n <p>{t('reservation:dashboardStatus')} {nextAppointment.status as string}</p>\n </div>\n ) : (\n <p className={styles.noData}>{t('reservation:dashboardNoUpcoming')}</p>\n )}\n </div>\n )\n}\n"],"names":["styles","DashboardWidgetServer","props","req","i18n","payload","t","slugs","config","admin","custom","reservationSlugs","now","Date","startOfDay","getFullYear","getMonth","getDate","endOfDay","docs","todayReservations","find","collection","reservations","limit","sort","where","startTime","greater_than_equal","toISOString","less_than","total","length","upcoming","filter","r","status","completed","cancelled","nextAppointment","div","className","wrapper","h3","title","statsGrid","statCard","span","statValue","statLabel","strong","p","toLocaleTimeString","hour","minute","noData"],"mappings":";AAIA,OAAOA,YAAY,+BAA8B;AAEjD,OAAO,MAAMC,wBAAwB,OAAOC;IAC1C,MAAM,EAAEC,GAAG,EAAE,GAAGD;IAChB,MAAM,EAAEE,IAAI,EAAEC,OAAO,EAAE,GAAGF;IAC1B,MAAMG,IAAIF,KAAKE,CAAC;IAEhB,MAAMC,QAAQF,QAAQG,MAAM,CAACC,KAAK,EAAEC,QAAQC;IAC5C,IAAI,CAACJ,OAAO;QACV,OAAO;IACT;IAEA,MAAMK,MAAM,IAAIC;IAChB,MAAMC,aAAa,IAAID,KAAKD,IAAIG,WAAW,IAAIH,IAAII,QAAQ,IAAIJ,IAAIK,OAAO;IAC1E,MAAMC,WAAW,IAAIL,KAAKD,IAAIG,WAAW,IAAIH,IAAII,QAAQ,IAAIJ,IAAIK,OAAO,KAAK;IAE7E,MAAM,EAAEE,MAAMC,iBAAiB,EAAE,GAAG,MAAMf,QAAQgB,IAAI,CAAC;QACrDC,YAAYf,MAAMgB,YAAY;QAC9BC,OAAO;QACPC,MAAM;QACNC,OAAO;YACLC,WAAW;gBACTC,oBAAoBd,WAAWe,WAAW;gBAC1CC,WAAWZ,SAASW,WAAW;YACjC;QACF;IACF;IAEA,MAAME,QAAQX,kBAAkBY,MAAM;IACtC,MAAMC,WAAWb,kBAAkBc,MAAM,CACvC,CAACC,IACCA,EAAEC,MAAM,KAAK,eAAeD,EAAEC,MAAM,KAAK,eAAeD,EAAEC,MAAM,KAAK,aACrE,IAAIvB,KAAKsB,EAAER,SAAS,IAAcf,KACpCoB,MAAM;IACR,MAAMK,YAAYjB,kBAAkBc,MAAM,CACxC,CAACC,IAA+BA,EAAEC,MAAM,KAAK,aAC7CJ,MAAM;IACR,MAAMM,YAAYlB,kBAAkBc,MAAM,CACxC,CAACC,IAA+BA,EAAEC,MAAM,KAAK,aAC7CJ,MAAM;IAER,MAAMO,kBAAkBnB,kBAAkBC,IAAI,CAC5C,CAACc,IACCA,EAAEC,MAAM,KAAK,eAAeD,EAAEC,MAAM,KAAK,eAAeD,EAAEC,MAAM,KAAK,aACrE,IAAIvB,KAAKsB,EAAER,SAAS,IAAcf;IAGtC,qBACE,MAAC4B;QAAIC,WAAWzC,OAAO0C,OAAO;;0BAC5B,KAACC;gBAAGF,WAAWzC,OAAO4C,KAAK;0BAAGtC,EAAE;;0BAChC,MAACkC;gBAAIC,WAAWzC,OAAO6C,SAAS;;kCAC9B,MAACL;wBAAIC,WAAWzC,OAAO8C,QAAQ;;0CAC7B,KAACC;gCAAKN,WAAWzC,OAAOgD,SAAS;0CAAGjB;;0CACpC,KAACgB;gCAAKN,WAAWzC,OAAOiD,SAAS;0CAAG3C,EAAE;;;;kCAExC,MAACkC;wBAAIC,WAAWzC,OAAO8C,QAAQ;;0CAC7B,KAACC;gCAAKN,WAAWzC,OAAOgD,SAAS;0CAAGf;;0CACpC,KAACc;gCAAKN,WAAWzC,OAAOiD,SAAS;0CAAG3C,EAAE;;;;kCAExC,MAACkC;wBAAIC,WAAWzC,OAAO8C,QAAQ;;0CAC7B,KAACC;gCAAKN,WAAWzC,OAAOgD,SAAS;0CAAGX;;0CACpC,KAACU;gCAAKN,WAAWzC,OAAOiD,SAAS;0CAAG3C,EAAE;;;;kCAExC,MAACkC;wBAAIC,WAAWzC,OAAO8C,QAAQ;;0CAC7B,KAACC;gCAAKN,WAAWzC,OAAOgD,SAAS;0CAAGV;;0CACpC,KAACS;gCAAKN,WAAWzC,OAAOiD,SAAS;0CAAG3C,EAAE;;;;;;YAGzCiC,gCACC,MAACC;gBAAIC,WAAWzC,OAAOuC,eAAe;;kCACpC,KAACW;kCAAQ5C,EAAE;;kCACX,MAAC6C;;4BACE7C,EAAE;4BAA6B;4BAAE,IAAIO,KAAK0B,gBAAgBZ,SAAS,EAAYyB,kBAAkB,CAAC,EAAE,EAAE;gCAAEC,MAAM;gCAAWC,QAAQ;4BAAU;;;kCAE9I,MAACH;;4BAAG7C,EAAE;4BAA+B;4BAAEiC,gBAAgBH,MAAM;;;;+BAG/D,KAACe;gBAAEV,WAAWzC,OAAOuD,MAAM;0BAAGjD,EAAE;;;;AAIxC,EAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js';
2
+ export declare const DEFAULT_SLUGS: {
3
+ readonly reservations: "reservations";
4
+ readonly resources: "reservation-resources";
5
+ readonly schedules: "reservation-schedules";
6
+ readonly services: "reservation-services";
7
+ };
8
+ export declare const DEFAULT_ADMIN_GROUP = "Reservations";
9
+ export declare const DEFAULT_BUFFER_TIME = 0;
10
+ export declare const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24;
11
+ export declare const DEFAULT_USER_COLLECTION = "users";
12
+ export declare function resolveConfig(pluginOptions: ReservationPluginConfig): ResolvedReservationPluginConfig;
@@ -0,0 +1,29 @@
1
+ export const DEFAULT_SLUGS = {
2
+ reservations: 'reservations',
3
+ resources: 'reservation-resources',
4
+ schedules: 'reservation-schedules',
5
+ services: 'reservation-services'
6
+ };
7
+ export const DEFAULT_ADMIN_GROUP = 'Reservations';
8
+ export const DEFAULT_BUFFER_TIME = 0;
9
+ export const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24;
10
+ export const DEFAULT_USER_COLLECTION = 'users';
11
+ export function resolveConfig(pluginOptions) {
12
+ return {
13
+ access: pluginOptions.access ?? {},
14
+ adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,
15
+ cancellationNoticePeriod: pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,
16
+ defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,
17
+ disabled: pluginOptions.disabled ?? false,
18
+ localized: false,
19
+ slugs: {
20
+ reservations: pluginOptions.slugs?.reservations ?? DEFAULT_SLUGS.reservations,
21
+ resources: pluginOptions.slugs?.resources ?? DEFAULT_SLUGS.resources,
22
+ schedules: pluginOptions.slugs?.schedules ?? DEFAULT_SLUGS.schedules,
23
+ services: pluginOptions.slugs?.services ?? DEFAULT_SLUGS.services
24
+ },
25
+ userCollection: pluginOptions.userCollection ?? DEFAULT_USER_COLLECTION
26
+ };
27
+ }
28
+
29
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js'\n\nexport const DEFAULT_SLUGS = {\n reservations: 'reservations',\n resources: 'reservation-resources',\n schedules: 'reservation-schedules',\n services: 'reservation-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'\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 defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,\n disabled: pluginOptions.disabled ?? false,\n localized: false,\n slugs: {\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","reservations","resources","schedules","services","DEFAULT_ADMIN_GROUP","DEFAULT_BUFFER_TIME","DEFAULT_CANCELLATION_NOTICE_PERIOD","DEFAULT_USER_COLLECTION","resolveConfig","pluginOptions","access","adminGroup","cancellationNoticePeriod","defaultBufferTime","disabled","localized","slugs","userCollection"],"mappings":"AAEA,OAAO,MAAMA,gBAAgB;IAC3BC,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;AAE9C,OAAO,SAASC,cACdC,aAAsC;IAEtC,OAAO;QACLC,QAAQD,cAAcC,MAAM,IAAI,CAAC;QACjCC,YAAYF,cAAcE,UAAU,IAAIP;QACxCQ,0BACEH,cAAcG,wBAAwB,IAAIN;QAC5CO,mBAAmBJ,cAAcI,iBAAiB,IAAIR;QACtDS,UAAUL,cAAcK,QAAQ,IAAI;QACpCC,WAAW;QACXC,OAAO;YACLhB,cAAcS,cAAcO,KAAK,EAAEhB,gBAAgBD,cAAcC,YAAY;YAC7EC,WAAWQ,cAAcO,KAAK,EAAEf,aAAaF,cAAcE,SAAS;YACpEC,WAAWO,cAAcO,KAAK,EAAEd,aAAaH,cAAcG,SAAS;YACpEC,UAAUM,cAAcO,KAAK,EAAEb,YAAYJ,cAAcI,QAAQ;QACnE;QACAc,gBAAgBR,cAAcQ,cAAc,IAAIV;IAClD;AACF"}
@@ -0,0 +1,2 @@
1
+ export { AvailabilityOverview } from '../components/AvailabilityOverview/index.js';
2
+ export { CalendarView } from '../components/CalendarView/index.js';
@@ -0,0 +1,4 @@
1
+ export { AvailabilityOverview } from '../components/AvailabilityOverview/index.js';
2
+ export { CalendarView } from '../components/CalendarView/index.js';
3
+
4
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { AvailabilityOverview } from '../components/AvailabilityOverview/index.js'\nexport { CalendarView } from '../components/CalendarView/index.js'\n"],"names":["AvailabilityOverview","CalendarView"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,8CAA6C;AAClF,SAASC,YAAY,QAAQ,sCAAqC"}
@@ -0,0 +1 @@
1
+ export { DashboardWidgetServer } from '../components/DashboardWidget/DashboardWidgetServer.js';
@@ -0,0 +1,3 @@
1
+ export { DashboardWidgetServer } from '../components/DashboardWidget/DashboardWidgetServer.js';
2
+
3
+ //# sourceMappingURL=rsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { DashboardWidgetServer } from '../components/DashboardWidget/DashboardWidgetServer.js'\n"],"names":["DashboardWidgetServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yDAAwD"}
@@ -0,0 +1,4 @@
1
+ export { calculateEndTime } from './reservations/calculateEndTime.js';
2
+ export { validateCancellation } from './reservations/validateCancellation.js';
3
+ export { validateConflicts } from './reservations/validateConflicts.js';
4
+ export { validateStatusTransition } from './reservations/validateStatusTransition.js';
@@ -0,0 +1,6 @@
1
+ export { calculateEndTime } from './reservations/calculateEndTime.js';
2
+ export { validateCancellation } from './reservations/validateCancellation.js';
3
+ export { validateConflicts } from './reservations/validateConflicts.js';
4
+ export { validateStatusTransition } from './reservations/validateStatusTransition.js';
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../../types.js';
3
+ export declare const calculateEndTime: (config: ResolvedReservationPluginConfig) => CollectionBeforeChangeHook;
@@ -0,0 +1,22 @@
1
+ import { addMinutes } from '../../utilities/slotUtils.js';
2
+ export const calculateEndTime = (config)=>async ({ context, data, req })=>{
3
+ if (context?.skipReservationHooks) {
4
+ return data;
5
+ }
6
+ if (!data?.startTime || !data?.service) {
7
+ return data;
8
+ }
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) {
16
+ const startDate = new Date(data.startTime);
17
+ data.endTime = addMinutes(startDate, service.duration).toISOString();
18
+ }
19
+ return data;
20
+ };
21
+
22
+ //# sourceMappingURL=calculateEndTime.js.map
@@ -0,0 +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 'reservation-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"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../../types.js';
3
+ export declare const validateCancellation: (config: ResolvedReservationPluginConfig) => CollectionBeforeChangeHook;
@@ -0,0 +1,38 @@
1
+ import { ValidationError } from 'payload';
2
+ import { hoursUntil } from '../../utilities/slotUtils.js';
3
+ export const validateCancellation = (config)=>({ context, data, operation, originalDoc, req })=>{
4
+ if (context?.skipReservationHooks) {
5
+ return data;
6
+ }
7
+ if (operation !== 'update') {
8
+ return data;
9
+ }
10
+ const newStatus = data?.status;
11
+ const previousStatus = originalDoc?.status;
12
+ // Only check when transitioning to cancelled
13
+ if (newStatus !== 'cancelled' || previousStatus === 'cancelled') {
14
+ return data;
15
+ }
16
+ const startTime = data?.startTime ?? originalDoc?.startTime;
17
+ if (!startTime) {
18
+ return data;
19
+ }
20
+ const startDate = new Date(startTime);
21
+ const hours = hoursUntil(startDate);
22
+ if (hours < config.cancellationNoticePeriod) {
23
+ throw new ValidationError({
24
+ errors: [
25
+ {
26
+ message: req.t('reservation:errorCancellationNotice', {
27
+ hours: String(Math.round(hours)),
28
+ period: String(config.cancellationNoticePeriod)
29
+ }),
30
+ path: 'status'
31
+ }
32
+ ]
33
+ });
34
+ }
35
+ return data;
36
+ };
37
+
38
+ //# sourceMappingURL=validateCancellation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/reservations/validateCancellation.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nimport { hoursUntil } from '../../utilities/slotUtils.js'\n\nexport const validateCancellation =\n (config: ResolvedReservationPluginConfig): CollectionBeforeChangeHook =>\n ({ context, data, operation, originalDoc, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n if (operation !== 'update') {return data}\n\n const newStatus = data?.status\n const previousStatus = originalDoc?.status\n\n // Only check when transitioning to cancelled\n if (newStatus !== 'cancelled' || previousStatus === 'cancelled') {return data}\n\n const startTime = data?.startTime ?? originalDoc?.startTime\n if (!startTime) {return data}\n\n const startDate = new Date(startTime)\n const hours = hoursUntil(startDate)\n\n if (hours < config.cancellationNoticePeriod) {\n throw new ValidationError({\n errors: [\n {\n message: (req.t as PluginT)('reservation:errorCancellationNotice', {\n hours: String(Math.round(hours)),\n period: String(config.cancellationNoticePeriod),\n }),\n path: 'status',\n },\n ],\n })\n }\n\n return data\n }\n"],"names":["ValidationError","hoursUntil","validateCancellation","config","context","data","operation","originalDoc","req","skipReservationHooks","newStatus","status","previousStatus","startTime","startDate","Date","hours","cancellationNoticePeriod","errors","message","t","String","Math","round","period","path"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAKzC,SAASC,UAAU,QAAQ,+BAA8B;AAEzD,OAAO,MAAMC,uBACX,CAACC,SACD,CAAC,EAAEC,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,GAAG,EAAE;QAC7C,IAAIJ,SAASK,sBAAsB;YAAC,OAAOJ;QAAI;QAE/C,IAAIC,cAAc,UAAU;YAAC,OAAOD;QAAI;QAExC,MAAMK,YAAYL,MAAMM;QACxB,MAAMC,iBAAiBL,aAAaI;QAEpC,6CAA6C;QAC7C,IAAID,cAAc,eAAeE,mBAAmB,aAAa;YAAC,OAAOP;QAAI;QAE7E,MAAMQ,YAAYR,MAAMQ,aAAaN,aAAaM;QAClD,IAAI,CAACA,WAAW;YAAC,OAAOR;QAAI;QAE5B,MAAMS,YAAY,IAAIC,KAAKF;QAC3B,MAAMG,QAAQf,WAAWa;QAEzB,IAAIE,QAAQb,OAAOc,wBAAwB,EAAE;YAC3C,MAAM,IAAIjB,gBAAgB;gBACxBkB,QAAQ;oBACN;wBACEC,SAAS,AAACX,IAAIY,CAAC,CAAa,uCAAuC;4BACjEJ,OAAOK,OAAOC,KAAKC,KAAK,CAACP;4BACzBQ,QAAQH,OAAOlB,OAAOc,wBAAwB;wBAChD;wBACAQ,MAAM;oBACR;iBACD;YACH;QACF;QAEA,OAAOpB;IACT,EAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ import type { ResolvedReservationPluginConfig } from '../../types.js';
3
+ export declare const validateConflicts: (config: ResolvedReservationPluginConfig) => CollectionBeforeChangeHook;
@@ -0,0 +1,86 @@
1
+ import { ValidationError } from 'payload';
2
+ import { computeBlockedWindow } from '../../utilities/slotUtils.js';
3
+ export const validateConflicts = (config)=>async ({ context, data, operation, originalDoc, req })=>{
4
+ if (context?.skipReservationHooks) {
5
+ return data;
6
+ }
7
+ if (!data?.startTime || !data?.endTime || !data?.resource) {
8
+ return data;
9
+ }
10
+ const serviceId = typeof data.service === 'object' ? data.service.id : data.service;
11
+ let bufferBefore = config.defaultBufferTime;
12
+ let bufferAfter = config.defaultBufferTime;
13
+ if (serviceId) {
14
+ try {
15
+ const service = await req.payload.findByID({
16
+ id: serviceId,
17
+ collection: config.slugs.services,
18
+ req
19
+ });
20
+ if (service) {
21
+ bufferBefore = service.bufferTimeBefore ?? config.defaultBufferTime;
22
+ bufferAfter = service.bufferTimeAfter ?? config.defaultBufferTime;
23
+ }
24
+ } catch {
25
+ // Use defaults if service lookup fails
26
+ }
27
+ }
28
+ const startTime = new Date(data.startTime);
29
+ const endTime = new Date(data.endTime);
30
+ const { effectiveEnd, effectiveStart } = computeBlockedWindow(startTime, endTime, bufferBefore, bufferAfter);
31
+ const resourceId = typeof data.resource === 'object' ? data.resource.id : data.resource;
32
+ const where = {
33
+ and: [
34
+ {
35
+ resource: {
36
+ equals: resourceId
37
+ }
38
+ },
39
+ {
40
+ status: {
41
+ not_in: [
42
+ 'cancelled',
43
+ 'no-show'
44
+ ]
45
+ }
46
+ },
47
+ {
48
+ startTime: {
49
+ less_than: effectiveEnd.toISOString()
50
+ }
51
+ },
52
+ {
53
+ endTime: {
54
+ greater_than: effectiveStart.toISOString()
55
+ }
56
+ }
57
+ ]
58
+ };
59
+ // Exclude self on update
60
+ if (operation === 'update' && originalDoc?.id) {
61
+ ;
62
+ where.and.push({
63
+ id: {
64
+ not_equals: originalDoc.id
65
+ }
66
+ });
67
+ }
68
+ const { totalDocs } = await req.payload.count({
69
+ collection: config.slugs.reservations,
70
+ req,
71
+ where
72
+ });
73
+ if (totalDocs > 0) {
74
+ throw new ValidationError({
75
+ errors: [
76
+ {
77
+ message: req.t('reservation:errorConflict'),
78
+ path: 'startTime'
79
+ }
80
+ ]
81
+ });
82
+ }
83
+ return data;
84
+ };
85
+
86
+ //# sourceMappingURL=validateConflicts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/reservations/validateConflicts.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook, Where } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\nimport type { ResolvedReservationPluginConfig } from '../../types.js'\n\nimport { computeBlockedWindow } from '../../utilities/slotUtils.js'\n\nexport const validateConflicts =\n (config: ResolvedReservationPluginConfig): CollectionBeforeChangeHook =>\n async ({ context, data, operation, originalDoc, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n if (!data?.startTime || !data?.endTime || !data?.resource) {return data}\n\n const serviceId = typeof data.service === 'object' ? data.service.id : data.service\n\n let bufferBefore = config.defaultBufferTime\n let bufferAfter = config.defaultBufferTime\n\n if (serviceId) {\n try {\n const service = await req.payload.findByID({\n id: serviceId,\n collection: config.slugs.services as 'reservation-services',\n req,\n })\n if (service) {\n bufferBefore = (service.bufferTimeBefore as number) ?? config.defaultBufferTime\n bufferAfter = (service.bufferTimeAfter as number) ?? config.defaultBufferTime\n }\n } catch {\n // Use defaults if service lookup fails\n }\n }\n\n const startTime = new Date(data.startTime)\n const endTime = new Date(data.endTime)\n const { effectiveEnd, effectiveStart } = computeBlockedWindow(\n startTime,\n endTime,\n bufferBefore,\n bufferAfter,\n )\n\n const resourceId = typeof data.resource === 'object' ? data.resource.id : data.resource\n\n const where: Where = {\n and: [\n { resource: { equals: resourceId } },\n {\n status: {\n not_in: ['cancelled', 'no-show'],\n },\n },\n { startTime: { less_than: effectiveEnd.toISOString() } },\n { endTime: { greater_than: effectiveStart.toISOString() } },\n ],\n }\n\n // Exclude self on update\n if (operation === 'update' && originalDoc?.id) {\n ;(where.and as Where[]).push({ id: { not_equals: originalDoc.id } })\n }\n\n const { totalDocs } = await req.payload.count({\n collection: config.slugs.reservations as 'reservations',\n req,\n where,\n })\n\n if (totalDocs > 0) {\n throw new ValidationError({\n errors: [\n {\n message: (req.t as PluginT)('reservation:errorConflict'),\n path: 'startTime',\n },\n ],\n })\n }\n\n return data\n }\n"],"names":["ValidationError","computeBlockedWindow","validateConflicts","config","context","data","operation","originalDoc","req","skipReservationHooks","startTime","endTime","resource","serviceId","service","id","bufferBefore","defaultBufferTime","bufferAfter","payload","findByID","collection","slugs","services","bufferTimeBefore","bufferTimeAfter","Date","effectiveEnd","effectiveStart","resourceId","where","and","equals","status","not_in","less_than","toISOString","greater_than","push","not_equals","totalDocs","count","reservations","errors","message","t","path"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAKzC,SAASC,oBAAoB,QAAQ,+BAA8B;AAEnE,OAAO,MAAMC,oBACX,CAACC,SACD,OAAO,EAAEC,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,GAAG,EAAE;QACnD,IAAIJ,SAASK,sBAAsB;YAAC,OAAOJ;QAAI;QAE/C,IAAI,CAACA,MAAMK,aAAa,CAACL,MAAMM,WAAW,CAACN,MAAMO,UAAU;YAAC,OAAOP;QAAI;QAEvE,MAAMQ,YAAY,OAAOR,KAAKS,OAAO,KAAK,WAAWT,KAAKS,OAAO,CAACC,EAAE,GAAGV,KAAKS,OAAO;QAEnF,IAAIE,eAAeb,OAAOc,iBAAiB;QAC3C,IAAIC,cAAcf,OAAOc,iBAAiB;QAE1C,IAAIJ,WAAW;YACb,IAAI;gBACF,MAAMC,UAAU,MAAMN,IAAIW,OAAO,CAACC,QAAQ,CAAC;oBACzCL,IAAIF;oBACJQ,YAAYlB,OAAOmB,KAAK,CAACC,QAAQ;oBACjCf;gBACF;gBACA,IAAIM,SAAS;oBACXE,eAAe,AAACF,QAAQU,gBAAgB,IAAerB,OAAOc,iBAAiB;oBAC/EC,cAAc,AAACJ,QAAQW,eAAe,IAAetB,OAAOc,iBAAiB;gBAC/E;YACF,EAAE,OAAM;YACN,uCAAuC;YACzC;QACF;QAEA,MAAMP,YAAY,IAAIgB,KAAKrB,KAAKK,SAAS;QACzC,MAAMC,UAAU,IAAIe,KAAKrB,KAAKM,OAAO;QACrC,MAAM,EAAEgB,YAAY,EAAEC,cAAc,EAAE,GAAG3B,qBACvCS,WACAC,SACAK,cACAE;QAGF,MAAMW,aAAa,OAAOxB,KAAKO,QAAQ,KAAK,WAAWP,KAAKO,QAAQ,CAACG,EAAE,GAAGV,KAAKO,QAAQ;QAEvF,MAAMkB,QAAe;YACnBC,KAAK;gBACH;oBAAEnB,UAAU;wBAAEoB,QAAQH;oBAAW;gBAAE;gBACnC;oBACEI,QAAQ;wBACNC,QAAQ;4BAAC;4BAAa;yBAAU;oBAClC;gBACF;gBACA;oBAAExB,WAAW;wBAAEyB,WAAWR,aAAaS,WAAW;oBAAG;gBAAE;gBACvD;oBAAEzB,SAAS;wBAAE0B,cAAcT,eAAeQ,WAAW;oBAAG;gBAAE;aAC3D;QACH;QAEA,yBAAyB;QACzB,IAAI9B,cAAc,YAAYC,aAAaQ,IAAI;;YAC3Ce,MAAMC,GAAG,CAAaO,IAAI,CAAC;gBAAEvB,IAAI;oBAAEwB,YAAYhC,YAAYQ,EAAE;gBAAC;YAAE;QACpE;QAEA,MAAM,EAAEyB,SAAS,EAAE,GAAG,MAAMhC,IAAIW,OAAO,CAACsB,KAAK,CAAC;YAC5CpB,YAAYlB,OAAOmB,KAAK,CAACoB,YAAY;YACrClC;YACAsB;QACF;QAEA,IAAIU,YAAY,GAAG;YACjB,MAAM,IAAIxC,gBAAgB;gBACxB2C,QAAQ;oBACN;wBACEC,SAAS,AAACpC,IAAIqC,CAAC,CAAa;wBAC5BC,MAAM;oBACR;iBACD;YACH;QACF;QAEA,OAAOzC;IACT,EAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ export declare const validateStatusTransition: () => CollectionBeforeChangeHook;
@@ -0,0 +1,54 @@
1
+ import { ValidationError } from 'payload';
2
+ import { VALID_STATUS_TRANSITIONS } from '../../types.js';
3
+ export const validateStatusTransition = ()=>({ context, data, operation, originalDoc, req })=>{
4
+ if (context?.skipReservationHooks) {
5
+ return data;
6
+ }
7
+ const newStatus = data?.status;
8
+ if (operation === 'create') {
9
+ const isAdmin = Boolean(req.user);
10
+ const allowedOnCreate = isAdmin ? [
11
+ 'pending',
12
+ 'confirmed'
13
+ ] : [
14
+ 'pending'
15
+ ];
16
+ if (newStatus && !allowedOnCreate.includes(newStatus)) {
17
+ const allowed = allowedOnCreate.map((s)=>`"${s}"`).join(' or ');
18
+ throw new ValidationError({
19
+ errors: [
20
+ {
21
+ message: req.t('reservation:errorInvalidCreateStatus', {
22
+ allowed
23
+ }),
24
+ path: 'status'
25
+ }
26
+ ]
27
+ });
28
+ }
29
+ return data;
30
+ }
31
+ // On update
32
+ if (operation === 'update' && newStatus) {
33
+ const previousStatus = originalDoc?.status;
34
+ if (previousStatus && previousStatus !== newStatus) {
35
+ const allowed = VALID_STATUS_TRANSITIONS[previousStatus];
36
+ if (!allowed || !allowed.includes(newStatus)) {
37
+ throw new ValidationError({
38
+ errors: [
39
+ {
40
+ message: req.t('reservation:errorInvalidTransition', {
41
+ from: previousStatus,
42
+ to: newStatus
43
+ }),
44
+ path: 'status'
45
+ }
46
+ ]
47
+ });
48
+ }
49
+ }
50
+ }
51
+ return data;
52
+ };
53
+
54
+ //# sourceMappingURL=validateStatusTransition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/reservations/validateStatusTransition.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport { ValidationError } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\nimport type { ReservationStatus } from '../../types.js'\n\nimport { VALID_STATUS_TRANSITIONS } from '../../types.js'\n\nexport const validateStatusTransition = (): CollectionBeforeChangeHook =>\n ({ context, data, operation, originalDoc, req }) => {\n if (context?.skipReservationHooks) {return data}\n\n const newStatus = data?.status as ReservationStatus | undefined\n\n if (operation === 'create') {\n const isAdmin = Boolean(req.user)\n const allowedOnCreate: ReservationStatus[] = isAdmin\n ? ['pending', 'confirmed']\n : ['pending']\n\n if (newStatus && !allowedOnCreate.includes(newStatus)) {\n const allowed = allowedOnCreate.map((s) => `\"${s}\"`).join(' or ')\n throw new ValidationError({\n errors: [\n {\n message: (req.t as PluginT)('reservation:errorInvalidCreateStatus', { allowed }),\n path: 'status',\n },\n ],\n })\n }\n return data\n }\n\n // On update\n if (operation === 'update' && newStatus) {\n const previousStatus = originalDoc?.status as ReservationStatus | undefined\n\n if (previousStatus && previousStatus !== newStatus) {\n const allowed = VALID_STATUS_TRANSITIONS[previousStatus]\n if (!allowed || !allowed.includes(newStatus)) {\n throw new ValidationError({\n errors: [\n {\n message: (req.t as PluginT)('reservation:errorInvalidTransition', {\n from: previousStatus,\n to: newStatus,\n }),\n path: 'status',\n },\n ],\n })\n }\n }\n }\n\n return data\n }\n"],"names":["ValidationError","VALID_STATUS_TRANSITIONS","validateStatusTransition","context","data","operation","originalDoc","req","skipReservationHooks","newStatus","status","isAdmin","Boolean","user","allowedOnCreate","includes","allowed","map","s","join","errors","message","t","path","previousStatus","from","to"],"mappings":"AAEA,SAASA,eAAe,QAAQ,UAAS;AAKzC,SAASC,wBAAwB,QAAQ,iBAAgB;AAEzD,OAAO,MAAMC,2BAA2B,IACtC,CAAC,EAAEC,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,GAAG,EAAE;QAC7C,IAAIJ,SAASK,sBAAsB;YAAC,OAAOJ;QAAI;QAE/C,MAAMK,YAAYL,MAAMM;QAExB,IAAIL,cAAc,UAAU;YAC1B,MAAMM,UAAUC,QAAQL,IAAIM,IAAI;YAChC,MAAMC,kBAAuCH,UACzC;gBAAC;gBAAW;aAAY,GACxB;gBAAC;aAAU;YAEf,IAAIF,aAAa,CAACK,gBAAgBC,QAAQ,CAACN,YAAY;gBACrD,MAAMO,UAAUF,gBAAgBG,GAAG,CAAC,CAACC,IAAM,CAAC,CAAC,EAAEA,EAAE,CAAC,CAAC,EAAEC,IAAI,CAAC;gBAC1D,MAAM,IAAInB,gBAAgB;oBACxBoB,QAAQ;wBACN;4BACEC,SAAS,AAACd,IAAIe,CAAC,CAAa,wCAAwC;gCAAEN;4BAAQ;4BAC9EO,MAAM;wBACR;qBACD;gBACH;YACF;YACA,OAAOnB;QACT;QAEA,YAAY;QACZ,IAAIC,cAAc,YAAYI,WAAW;YACvC,MAAMe,iBAAiBlB,aAAaI;YAEpC,IAAIc,kBAAkBA,mBAAmBf,WAAW;gBAClD,MAAMO,UAAUf,wBAAwB,CAACuB,eAAe;gBACxD,IAAI,CAACR,WAAW,CAACA,QAAQD,QAAQ,CAACN,YAAY;oBAC5C,MAAM,IAAIT,gBAAgB;wBACxBoB,QAAQ;4BACN;gCACEC,SAAS,AAACd,IAAIe,CAAC,CAAa,sCAAsC;oCAChEG,MAAMD;oCACNE,IAAIjB;gCACN;gCACAc,MAAM;4BACR;yBACD;oBACH;gBACF;YACF;QACF;QAEA,OAAOnB;IACT,EAAC"}
@@ -0,0 +1,2 @@
1
+ export { reservationPlugin } from './plugin.js';
2
+ export type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { reservationPlugin } from './plugin.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { reservationPlugin } from './plugin.js'\nexport type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js'\n"],"names":["reservationPlugin"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,cAAa"}
@@ -0,0 +1,3 @@
1
+ import type { Config } from 'payload';
2
+ import type { ReservationPluginConfig } from './types.js';
3
+ export declare const reservationPlugin: (pluginOptions?: ReservationPluginConfig) => (config: Config) => Config;
package/dist/plugin.js ADDED
@@ -0,0 +1,106 @@
1
+ import { deepMergeSimple } from 'payload/shared';
2
+ import { createReservationsCollection } from './collections/Reservations.js';
3
+ import { createResourcesCollection } from './collections/Resources.js';
4
+ import { createSchedulesCollection } from './collections/Schedules.js';
5
+ import { createServicesCollection } from './collections/Services.js';
6
+ import { resolveConfig } from './defaults.js';
7
+ import { translations } from './translations/index.js';
8
+ /** Check whether a top-level field with the given name already exists */ const hasField = (fields, name)=>fields.some((f)=>'name' in f && f.name === name);
9
+ export const reservationPlugin = (pluginOptions = {})=>(config)=>{
10
+ const resolved = resolveConfig(pluginOptions);
11
+ // Detect localization from the Payload config
12
+ if (config.localization) {
13
+ resolved.localized = true;
14
+ }
15
+ if (!config.collections) {
16
+ config.collections = [];
17
+ }
18
+ if (resolved.disabled) {
19
+ return config;
20
+ }
21
+ // Add the 4 plugin collections
22
+ config.collections.push(createServicesCollection(resolved), createResourcesCollection(resolved), createSchedulesCollection(resolved), createReservationsCollection(resolved));
23
+ // Extend the existing user collection with customer fields
24
+ const userCol = config.collections.find((c)=>c.slug === resolved.userCollection);
25
+ if (userCol) {
26
+ const fieldsToAdd = [
27
+ {
28
+ name: 'name',
29
+ type: 'text',
30
+ maxLength: 200
31
+ },
32
+ {
33
+ name: 'phone',
34
+ type: 'text',
35
+ maxLength: 50
36
+ },
37
+ {
38
+ name: 'notes',
39
+ type: 'textarea'
40
+ },
41
+ {
42
+ name: 'bookings',
43
+ type: 'join',
44
+ collection: resolved.slugs.reservations,
45
+ on: 'customer'
46
+ }
47
+ ];
48
+ for (const field of fieldsToAdd){
49
+ if (!hasField(userCol.fields, field.name)) {
50
+ userCol.fields.push(field);
51
+ }
52
+ }
53
+ } else {
54
+ // eslint-disable-next-line no-console
55
+ console.warn(`[payload-reserve] Could not find collection "${resolved.userCollection}" to extend with customer fields. ` + 'Make sure your Payload config defines this collection before the reservation plugin runs.');
56
+ }
57
+ // Set up admin configuration
58
+ if (!config.admin) {
59
+ config.admin = {};
60
+ }
61
+ if (!config.admin.components) {
62
+ config.admin.components = {};
63
+ }
64
+ // Store slugs in admin custom for component access
65
+ if (!config.admin.custom) {
66
+ config.admin.custom = {};
67
+ }
68
+ config.admin.custom.reservationSlugs = {
69
+ ...resolved.slugs,
70
+ userCollection: resolved.userCollection
71
+ };
72
+ // Add dashboard widget
73
+ if (!config.admin.dashboard) {
74
+ config.admin.dashboard = {
75
+ widgets: []
76
+ };
77
+ }
78
+ if (!config.admin.dashboard.widgets) {
79
+ config.admin.dashboard.widgets = [];
80
+ }
81
+ config.admin.dashboard.widgets.push({
82
+ slug: 'reservation-todays-reservations',
83
+ ComponentPath: 'payload-reserve/rsc#DashboardWidgetServer',
84
+ label: 'Today\'s Reservations',
85
+ maxWidth: 'large',
86
+ minWidth: 'medium'
87
+ });
88
+ // Add availability overview as custom admin view
89
+ if (!config.admin.components.views) {
90
+ config.admin.components.views = {};
91
+ }
92
+ ;
93
+ config.admin.components.views['reservation-availability'] = {
94
+ Component: 'payload-reserve/client#AvailabilityOverview',
95
+ path: '/reservation-availability'
96
+ };
97
+ // Merge plugin translations (user translations take precedence)
98
+ if (!config.i18n) {
99
+ config.i18n = {};
100
+ }
101
+ ;
102
+ config.i18n.translations = deepMergeSimple(translations, config.i18n.translations ?? {});
103
+ return config;
104
+ };
105
+
106
+ //# sourceMappingURL=plugin.js.map