gorombo-payload-appointments 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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/dist/collections/Appointments.d.ts +2 -0
  4. package/dist/collections/Appointments.js +165 -0
  5. package/dist/collections/Appointments.js.map +1 -0
  6. package/dist/collections/GuestCustomers.d.ts +2 -0
  7. package/dist/collections/GuestCustomers.js +106 -0
  8. package/dist/collections/GuestCustomers.js.map +1 -0
  9. package/dist/collections/Services.d.ts +2 -0
  10. package/dist/collections/Services.js +147 -0
  11. package/dist/collections/Services.js.map +1 -0
  12. package/dist/collections/TeamMembers.d.ts +2 -0
  13. package/dist/collections/TeamMembers.js +184 -0
  14. package/dist/collections/TeamMembers.js.map +1 -0
  15. package/dist/components/BeforeDashboardClient.d.ts +2 -0
  16. package/dist/components/BeforeDashboardClient.js +162 -0
  17. package/dist/components/BeforeDashboardClient.js.map +1 -0
  18. package/dist/components/BeforeDashboardServer.d.ts +2 -0
  19. package/dist/components/BeforeDashboardServer.js +22 -0
  20. package/dist/components/BeforeDashboardServer.js.map +1 -0
  21. package/dist/components/BeforeDashboardServer.module.css +5 -0
  22. package/dist/components/calendar/Calendar.module.css +506 -0
  23. package/dist/components/calendar/CalendarContainer.d.ts +3 -0
  24. package/dist/components/calendar/CalendarContainer.js +246 -0
  25. package/dist/components/calendar/CalendarContainer.js.map +1 -0
  26. package/dist/components/calendar/DayView.d.ts +3 -0
  27. package/dist/components/calendar/DayView.js +192 -0
  28. package/dist/components/calendar/DayView.js.map +1 -0
  29. package/dist/components/calendar/EventPopover.d.ts +3 -0
  30. package/dist/components/calendar/EventPopover.js +257 -0
  31. package/dist/components/calendar/EventPopover.js.map +1 -0
  32. package/dist/components/calendar/EventRenderer.d.ts +3 -0
  33. package/dist/components/calendar/EventRenderer.js +76 -0
  34. package/dist/components/calendar/EventRenderer.js.map +1 -0
  35. package/dist/components/calendar/WeekView.d.ts +3 -0
  36. package/dist/components/calendar/WeekView.js +203 -0
  37. package/dist/components/calendar/WeekView.js.map +1 -0
  38. package/dist/components/calendar/index.d.ts +6 -0
  39. package/dist/components/calendar/index.js +7 -0
  40. package/dist/components/calendar/index.js.map +1 -0
  41. package/dist/components/calendar/types.d.ts +69 -0
  42. package/dist/components/calendar/types.js +3 -0
  43. package/dist/components/calendar/types.js.map +1 -0
  44. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  45. package/dist/endpoints/customEndpointHandler.js +7 -0
  46. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  47. package/dist/endpoints/getAvailableSlots.d.ts +12 -0
  48. package/dist/endpoints/getAvailableSlots.js +291 -0
  49. package/dist/endpoints/getAvailableSlots.js.map +1 -0
  50. package/dist/exports/client.d.ts +3 -0
  51. package/dist/exports/client.js +4 -0
  52. package/dist/exports/client.js.map +1 -0
  53. package/dist/exports/rsc.d.ts +1 -0
  54. package/dist/exports/rsc.js +3 -0
  55. package/dist/exports/rsc.js.map +1 -0
  56. package/dist/globals/OpeningTimes.d.ts +2 -0
  57. package/dist/globals/OpeningTimes.js +196 -0
  58. package/dist/globals/OpeningTimes.js.map +1 -0
  59. package/dist/hooks/addAdminTitle.d.ts +7 -0
  60. package/dist/hooks/addAdminTitle.js +86 -0
  61. package/dist/hooks/addAdminTitle.js.map +1 -0
  62. package/dist/hooks/sendCustomerEmail.d.ts +6 -0
  63. package/dist/hooks/sendCustomerEmail.js +351 -0
  64. package/dist/hooks/sendCustomerEmail.js.map +1 -0
  65. package/dist/hooks/setEndDateTime.d.ts +6 -0
  66. package/dist/hooks/setEndDateTime.js +44 -0
  67. package/dist/hooks/setEndDateTime.js.map +1 -0
  68. package/dist/hooks/validateCustomerOrGuest.d.ts +6 -0
  69. package/dist/hooks/validateCustomerOrGuest.js +21 -0
  70. package/dist/hooks/validateCustomerOrGuest.js.map +1 -0
  71. package/dist/index.d.ts +23 -0
  72. package/dist/index.js +183 -0
  73. package/dist/index.js.map +1 -0
  74. package/package.json +135 -0
@@ -0,0 +1,257 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { useEffect, useRef } from 'react';
4
+ import styles from './Calendar.module.css';
5
+ function formatEventTime(date) {
6
+ return date.toLocaleTimeString('en-US', {
7
+ hour: 'numeric',
8
+ minute: '2-digit'
9
+ });
10
+ }
11
+ function formatEventDate(date) {
12
+ return date.toLocaleDateString('en-US', {
13
+ day: 'numeric',
14
+ month: 'long',
15
+ weekday: 'long'
16
+ });
17
+ }
18
+ function getStatusLabel(status) {
19
+ switch(status){
20
+ case 'cancelled':
21
+ return 'Cancelled';
22
+ case 'completed':
23
+ return 'Completed';
24
+ case 'confirmed':
25
+ return 'Confirmed';
26
+ case 'no-show':
27
+ return 'No Show';
28
+ case 'scheduled':
29
+ return 'Scheduled';
30
+ default:
31
+ return 'Unknown';
32
+ }
33
+ }
34
+ function getStatusClass(status) {
35
+ switch(status){
36
+ case 'cancelled':
37
+ return styles.cancelled;
38
+ case 'completed':
39
+ return styles.completed;
40
+ case 'confirmed':
41
+ return styles.confirmed;
42
+ case 'scheduled':
43
+ return styles.scheduled;
44
+ default:
45
+ return '';
46
+ }
47
+ }
48
+ export const EventPopover = ({ event, onClose, onDelete, onEdit, position })=>{
49
+ const popoverRef = useRef(null);
50
+ useEffect(()=>{
51
+ const handleClickOutside = (e)=>{
52
+ if (popoverRef.current && !popoverRef.current.contains(e.target)) {
53
+ onClose();
54
+ }
55
+ };
56
+ const handleEscape = (e)=>{
57
+ if (e.key === 'Escape') {
58
+ onClose();
59
+ }
60
+ };
61
+ document.addEventListener('mousedown', handleClickOutside);
62
+ document.addEventListener('keydown', handleEscape);
63
+ return ()=>{
64
+ document.removeEventListener('mousedown', handleClickOutside);
65
+ document.removeEventListener('keydown', handleEscape);
66
+ };
67
+ }, [
68
+ onClose
69
+ ]);
70
+ // Adjust position if popover would overflow viewport
71
+ useEffect(()=>{
72
+ if (popoverRef.current) {
73
+ const rect = popoverRef.current.getBoundingClientRect();
74
+ const viewportWidth = window.innerWidth;
75
+ const viewportHeight = window.innerHeight;
76
+ let adjustedLeft = position.left;
77
+ let adjustedTop = position.top;
78
+ if (rect.right > viewportWidth) {
79
+ adjustedLeft = viewportWidth - rect.width - 20;
80
+ }
81
+ if (rect.bottom > viewportHeight) {
82
+ adjustedTop = viewportHeight - rect.height - 20;
83
+ }
84
+ if (adjustedLeft !== position.left || adjustedTop !== position.top) {
85
+ popoverRef.current.style.left = `${Math.max(10, adjustedLeft)}px`;
86
+ popoverRef.current.style.top = `${Math.max(10, adjustedTop)}px`;
87
+ }
88
+ }
89
+ }, [
90
+ position
91
+ ]);
92
+ const customerName = event.customerName || event.guestName || 'Unknown';
93
+ const isBlockout = event.type === 'blockout';
94
+ return /*#__PURE__*/ _jsxs(_Fragment, {
95
+ children: [
96
+ /*#__PURE__*/ _jsx("div", {
97
+ "aria-label": "Close popover",
98
+ className: styles.overlay,
99
+ onClick: onClose,
100
+ onKeyDown: (e)=>e.key === 'Escape' && onClose(),
101
+ role: "button",
102
+ tabIndex: 0
103
+ }),
104
+ /*#__PURE__*/ _jsxs("div", {
105
+ className: styles.popover,
106
+ ref: popoverRef,
107
+ style: {
108
+ left: position.left,
109
+ top: position.top
110
+ },
111
+ children: [
112
+ /*#__PURE__*/ _jsxs("div", {
113
+ className: styles.popoverHeader,
114
+ children: [
115
+ /*#__PURE__*/ _jsxs("div", {
116
+ children: [
117
+ /*#__PURE__*/ _jsx("h3", {
118
+ className: styles.popoverTitle,
119
+ children: isBlockout ? event.blockoutReason || 'Blocked Time' : event.serviceName || event.title
120
+ }),
121
+ !isBlockout && event.status && /*#__PURE__*/ _jsx("span", {
122
+ className: `${styles.popoverStatus} ${getStatusClass(event.status)}`,
123
+ children: getStatusLabel(event.status)
124
+ })
125
+ ]
126
+ }),
127
+ /*#__PURE__*/ _jsx("button", {
128
+ className: styles.popoverClose,
129
+ onClick: onClose,
130
+ children: /*#__PURE__*/ _jsxs("svg", {
131
+ fill: "none",
132
+ height: "16",
133
+ stroke: "currentColor",
134
+ strokeLinecap: "round",
135
+ strokeLinejoin: "round",
136
+ strokeWidth: "2",
137
+ viewBox: "0 0 24 24",
138
+ width: "16",
139
+ children: [
140
+ /*#__PURE__*/ _jsx("line", {
141
+ x1: "18",
142
+ x2: "6",
143
+ y1: "6",
144
+ y2: "18"
145
+ }),
146
+ /*#__PURE__*/ _jsx("line", {
147
+ x1: "6",
148
+ x2: "18",
149
+ y1: "6",
150
+ y2: "18"
151
+ })
152
+ ]
153
+ })
154
+ })
155
+ ]
156
+ }),
157
+ /*#__PURE__*/ _jsxs("div", {
158
+ className: styles.popoverBody,
159
+ children: [
160
+ /*#__PURE__*/ _jsxs("div", {
161
+ className: styles.popoverRow,
162
+ children: [
163
+ /*#__PURE__*/ _jsx("span", {
164
+ className: styles.popoverLabel,
165
+ children: "Date"
166
+ }),
167
+ /*#__PURE__*/ _jsx("span", {
168
+ className: styles.popoverValue,
169
+ children: formatEventDate(new Date(event.start))
170
+ })
171
+ ]
172
+ }),
173
+ /*#__PURE__*/ _jsxs("div", {
174
+ className: styles.popoverRow,
175
+ children: [
176
+ /*#__PURE__*/ _jsx("span", {
177
+ className: styles.popoverLabel,
178
+ children: "Time"
179
+ }),
180
+ /*#__PURE__*/ _jsxs("span", {
181
+ className: styles.popoverValue,
182
+ children: [
183
+ formatEventTime(new Date(event.start)),
184
+ " -",
185
+ ' ',
186
+ formatEventTime(new Date(event.end))
187
+ ]
188
+ })
189
+ ]
190
+ }),
191
+ !isBlockout && /*#__PURE__*/ _jsxs(_Fragment, {
192
+ children: [
193
+ /*#__PURE__*/ _jsxs("div", {
194
+ className: styles.popoverRow,
195
+ children: [
196
+ /*#__PURE__*/ _jsx("span", {
197
+ className: styles.popoverLabel,
198
+ children: "Customer"
199
+ }),
200
+ /*#__PURE__*/ _jsx("span", {
201
+ className: styles.popoverValue,
202
+ children: customerName
203
+ })
204
+ ]
205
+ }),
206
+ event.teamMemberName && /*#__PURE__*/ _jsxs("div", {
207
+ className: styles.popoverRow,
208
+ children: [
209
+ /*#__PURE__*/ _jsx("span", {
210
+ className: styles.popoverLabel,
211
+ children: "With"
212
+ }),
213
+ /*#__PURE__*/ _jsx("span", {
214
+ className: styles.popoverValue,
215
+ children: event.teamMemberName
216
+ })
217
+ ]
218
+ })
219
+ ]
220
+ }),
221
+ event.notes && /*#__PURE__*/ _jsxs("div", {
222
+ className: styles.popoverRow,
223
+ children: [
224
+ /*#__PURE__*/ _jsx("span", {
225
+ className: styles.popoverLabel,
226
+ children: "Notes"
227
+ }),
228
+ /*#__PURE__*/ _jsx("span", {
229
+ className: styles.popoverValue,
230
+ children: event.notes
231
+ })
232
+ ]
233
+ })
234
+ ]
235
+ }),
236
+ (onEdit || onDelete) && /*#__PURE__*/ _jsxs("div", {
237
+ className: styles.popoverActions,
238
+ children: [
239
+ onEdit && /*#__PURE__*/ _jsx("button", {
240
+ className: `${styles.popoverButton} ${styles.primary}`,
241
+ onClick: ()=>onEdit(event),
242
+ children: "Edit"
243
+ }),
244
+ onDelete && /*#__PURE__*/ _jsx("button", {
245
+ className: `${styles.popoverButton} ${styles.secondary}`,
246
+ onClick: ()=>onDelete(event),
247
+ children: "Cancel"
248
+ })
249
+ ]
250
+ })
251
+ ]
252
+ })
253
+ ]
254
+ });
255
+ };
256
+
257
+ //# sourceMappingURL=EventPopover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/calendar/EventPopover.tsx"],"sourcesContent":["'use client'\n\nimport React, { useEffect, useRef } from 'react'\n\nimport type { EventPopoverProps } from './types.js'\n\nimport styles from './Calendar.module.css'\n\nfunction formatEventTime(date: Date): string {\n return date.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\nfunction formatEventDate(date: Date): string {\n return date.toLocaleDateString('en-US', {\n day: 'numeric',\n month: 'long',\n weekday: 'long',\n })\n}\n\nfunction getStatusLabel(status?: string): string {\n switch (status) {\n case 'cancelled':\n return 'Cancelled'\n case 'completed':\n return 'Completed'\n case 'confirmed':\n return 'Confirmed'\n case 'no-show':\n return 'No Show'\n case 'scheduled':\n return 'Scheduled'\n default:\n return 'Unknown'\n }\n}\n\nfunction getStatusClass(status?: string): string {\n switch (status) {\n case 'cancelled':\n return styles.cancelled\n case 'completed':\n return styles.completed\n case 'confirmed':\n return styles.confirmed\n case 'scheduled':\n return styles.scheduled\n default:\n return ''\n }\n}\n\nexport const EventPopover: React.FC<EventPopoverProps> = ({\n event,\n onClose,\n onDelete,\n onEdit,\n position,\n}) => {\n const popoverRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(e.target as Node)\n ) {\n onClose()\n }\n }\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose()\n }\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [onClose])\n\n // Adjust position if popover would overflow viewport\n useEffect(() => {\n if (popoverRef.current) {\n const rect = popoverRef.current.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let adjustedLeft = position.left\n let adjustedTop = position.top\n\n if (rect.right > viewportWidth) {\n adjustedLeft = viewportWidth - rect.width - 20\n }\n\n if (rect.bottom > viewportHeight) {\n adjustedTop = viewportHeight - rect.height - 20\n }\n\n if (adjustedLeft !== position.left || adjustedTop !== position.top) {\n popoverRef.current.style.left = `${Math.max(10, adjustedLeft)}px`\n popoverRef.current.style.top = `${Math.max(10, adjustedTop)}px`\n }\n }\n }, [position])\n\n const customerName = event.customerName || event.guestName || 'Unknown'\n const isBlockout = event.type === 'blockout'\n\n return (\n <>\n <div\n aria-label=\"Close popover\"\n className={styles.overlay}\n onClick={onClose}\n onKeyDown={(e) => e.key === 'Escape' && onClose()}\n role=\"button\"\n tabIndex={0}\n />\n <div\n className={styles.popover}\n ref={popoverRef}\n style={{ left: position.left, top: position.top }}\n >\n <div className={styles.popoverHeader}>\n <div>\n <h3 className={styles.popoverTitle}>\n {isBlockout\n ? event.blockoutReason || 'Blocked Time'\n : event.serviceName || event.title}\n </h3>\n {!isBlockout && event.status && (\n <span\n className={`${styles.popoverStatus} ${getStatusClass(event.status)}`}\n >\n {getStatusLabel(event.status)}\n </span>\n )}\n </div>\n <button className={styles.popoverClose} onClick={onClose}>\n <svg\n fill=\"none\"\n height=\"16\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n viewBox=\"0 0 24 24\"\n width=\"16\"\n >\n <line x1=\"18\" x2=\"6\" y1=\"6\" y2=\"18\" />\n <line x1=\"6\" x2=\"18\" y1=\"6\" y2=\"18\" />\n </svg>\n </button>\n </div>\n\n <div className={styles.popoverBody}>\n <div className={styles.popoverRow}>\n <span className={styles.popoverLabel}>Date</span>\n <span className={styles.popoverValue}>\n {formatEventDate(new Date(event.start))}\n </span>\n </div>\n\n <div className={styles.popoverRow}>\n <span className={styles.popoverLabel}>Time</span>\n <span className={styles.popoverValue}>\n {formatEventTime(new Date(event.start))} -{' '}\n {formatEventTime(new Date(event.end))}\n </span>\n </div>\n\n {!isBlockout && (\n <>\n <div className={styles.popoverRow}>\n <span className={styles.popoverLabel}>Customer</span>\n <span className={styles.popoverValue}>{customerName}</span>\n </div>\n\n {event.teamMemberName && (\n <div className={styles.popoverRow}>\n <span className={styles.popoverLabel}>With</span>\n <span className={styles.popoverValue}>\n {event.teamMemberName}\n </span>\n </div>\n )}\n </>\n )}\n\n {event.notes && (\n <div className={styles.popoverRow}>\n <span className={styles.popoverLabel}>Notes</span>\n <span className={styles.popoverValue}>{event.notes}</span>\n </div>\n )}\n </div>\n\n {(onEdit || onDelete) && (\n <div className={styles.popoverActions}>\n {onEdit && (\n <button\n className={`${styles.popoverButton} ${styles.primary}`}\n onClick={() => onEdit(event)}\n >\n Edit\n </button>\n )}\n {onDelete && (\n <button\n className={`${styles.popoverButton} ${styles.secondary}`}\n onClick={() => onDelete(event)}\n >\n Cancel\n </button>\n )}\n </div>\n )}\n </div>\n </>\n )\n}\n"],"names":["React","useEffect","useRef","styles","formatEventTime","date","toLocaleTimeString","hour","minute","formatEventDate","toLocaleDateString","day","month","weekday","getStatusLabel","status","getStatusClass","cancelled","completed","confirmed","scheduled","EventPopover","event","onClose","onDelete","onEdit","position","popoverRef","handleClickOutside","e","current","contains","target","handleEscape","key","document","addEventListener","removeEventListener","rect","getBoundingClientRect","viewportWidth","window","innerWidth","viewportHeight","innerHeight","adjustedLeft","left","adjustedTop","top","right","width","bottom","height","style","Math","max","customerName","guestName","isBlockout","type","div","aria-label","className","overlay","onClick","onKeyDown","role","tabIndex","popover","ref","popoverHeader","h3","popoverTitle","blockoutReason","serviceName","title","span","popoverStatus","button","popoverClose","svg","fill","stroke","strokeLinecap","strokeLinejoin","strokeWidth","viewBox","line","x1","x2","y1","y2","popoverBody","popoverRow","popoverLabel","popoverValue","Date","start","end","teamMemberName","notes","popoverActions","popoverButton","primary","secondary"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { EventRendererProps } from './types.js';
3
+ export declare const EventRenderer: React.FC<EventRendererProps>;
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import styles from './Calendar.module.css';
5
+ function formatEventTime(start, end) {
6
+ const formatTime = (d)=>{
7
+ const hours = d.getHours();
8
+ const minutes = d.getMinutes();
9
+ const period = hours >= 12 ? 'pm' : 'am';
10
+ const displayHours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
11
+ return minutes === 0 ? `${displayHours}${period}` : `${displayHours}:${minutes.toString().padStart(2, '0')}${period}`;
12
+ };
13
+ return `${formatTime(start)} - ${formatTime(end)}`;
14
+ }
15
+ function getStatusClass(status) {
16
+ switch(status){
17
+ case 'cancelled':
18
+ return styles.cancelled;
19
+ case 'completed':
20
+ return styles.completed;
21
+ case 'confirmed':
22
+ return styles.confirmed;
23
+ case 'no-show':
24
+ return styles.noShow;
25
+ case 'scheduled':
26
+ return styles.scheduled;
27
+ default:
28
+ return '';
29
+ }
30
+ }
31
+ export const EventRenderer = ({ event, onClick, style })=>{
32
+ const handleClick = (e)=>{
33
+ e.stopPropagation();
34
+ if (onClick) {
35
+ onClick(event);
36
+ }
37
+ };
38
+ const backgroundColor = event.type === 'blockout' ? undefined : event.serviceColor || event.teamMemberColor || '#3b82f6';
39
+ const borderColor = backgroundColor;
40
+ const eventStyle = {
41
+ ...style,
42
+ backgroundColor: event.type === 'blockout' ? undefined : `${backgroundColor}20`,
43
+ borderLeftColor: borderColor,
44
+ color: event.type === 'blockout' ? undefined : backgroundColor
45
+ };
46
+ const eventClasses = [
47
+ styles.event,
48
+ event.type === 'appointment' ? styles.appointment : styles.blockout,
49
+ getStatusClass(event.status)
50
+ ].filter(Boolean).join(' ');
51
+ const customerName = event.customerName || event.guestName || 'Unknown Customer';
52
+ return /*#__PURE__*/ _jsxs("div", {
53
+ className: eventClasses,
54
+ onClick: handleClick,
55
+ onKeyDown: (e)=>e.key === 'Enter' && handleClick(e),
56
+ role: "button",
57
+ style: eventStyle,
58
+ tabIndex: 0,
59
+ children: [
60
+ /*#__PURE__*/ _jsx("div", {
61
+ className: styles.eventTitle,
62
+ children: event.type === 'blockout' ? event.blockoutReason || 'Blocked' : event.serviceName || event.title
63
+ }),
64
+ /*#__PURE__*/ _jsx("div", {
65
+ className: styles.eventTime,
66
+ children: formatEventTime(new Date(event.start), new Date(event.end))
67
+ }),
68
+ event.type === 'appointment' && /*#__PURE__*/ _jsx("div", {
69
+ className: styles.eventCustomer,
70
+ children: customerName
71
+ })
72
+ ]
73
+ });
74
+ };
75
+
76
+ //# sourceMappingURL=EventRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/calendar/EventRenderer.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\n\nimport type { EventRendererProps } from './types.js'\n\nimport styles from './Calendar.module.css'\n\nfunction formatEventTime(start: Date, end: Date): string {\n const formatTime = (d: Date) => {\n const hours = d.getHours()\n const minutes = d.getMinutes()\n const period = hours >= 12 ? 'pm' : 'am'\n const displayHours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours\n return minutes === 0 ? `${displayHours}${period}` : `${displayHours}:${minutes.toString().padStart(2, '0')}${period}`\n }\n\n return `${formatTime(start)} - ${formatTime(end)}`\n}\n\nfunction getStatusClass(status?: string): string {\n switch (status) {\n case 'cancelled':\n return styles.cancelled\n case 'completed':\n return styles.completed\n case 'confirmed':\n return styles.confirmed\n case 'no-show':\n return styles.noShow\n case 'scheduled':\n return styles.scheduled\n default:\n return ''\n }\n}\n\nexport const EventRenderer: React.FC<EventRendererProps> = ({\n event,\n onClick,\n style,\n}) => {\n const handleClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n if (onClick) {\n onClick(event)\n }\n }\n\n const backgroundColor =\n event.type === 'blockout'\n ? undefined\n : event.serviceColor || event.teamMemberColor || '#3b82f6'\n\n const borderColor = backgroundColor\n\n const eventStyle: React.CSSProperties = {\n ...style,\n backgroundColor:\n event.type === 'blockout' ? undefined : `${backgroundColor}20`,\n borderLeftColor: borderColor,\n color: event.type === 'blockout' ? undefined : backgroundColor,\n }\n\n const eventClasses = [\n styles.event,\n event.type === 'appointment' ? styles.appointment : styles.blockout,\n getStatusClass(event.status),\n ]\n .filter(Boolean)\n .join(' ')\n\n const customerName =\n event.customerName || event.guestName || 'Unknown Customer'\n\n return (\n <div\n className={eventClasses}\n onClick={handleClick}\n onKeyDown={(e) => e.key === 'Enter' && handleClick(e as unknown as React.MouseEvent)}\n role=\"button\"\n style={eventStyle}\n tabIndex={0}\n >\n <div className={styles.eventTitle}>\n {event.type === 'blockout'\n ? event.blockoutReason || 'Blocked'\n : event.serviceName || event.title}\n </div>\n <div className={styles.eventTime}>\n {formatEventTime(new Date(event.start), new Date(event.end))}\n </div>\n {event.type === 'appointment' && (\n <div className={styles.eventCustomer}>{customerName}</div>\n )}\n </div>\n )\n}\n"],"names":["React","styles","formatEventTime","start","end","formatTime","d","hours","getHours","minutes","getMinutes","period","displayHours","toString","padStart","getStatusClass","status","cancelled","completed","confirmed","noShow","scheduled","EventRenderer","event","onClick","style","handleClick","e","stopPropagation","backgroundColor","type","undefined","serviceColor","teamMemberColor","borderColor","eventStyle","borderLeftColor","color","eventClasses","appointment","blockout","filter","Boolean","join","customerName","guestName","div","className","onKeyDown","key","role","tabIndex","eventTitle","blockoutReason","serviceName","title","eventTime","Date","eventCustomer"],"mappings":"AAAA;;AAEA,OAAOA,WAAW,QAAO;AAIzB,OAAOC,YAAY,wBAAuB;AAE1C,SAASC,gBAAgBC,KAAW,EAAEC,GAAS;IAC7C,MAAMC,aAAa,CAACC;QAClB,MAAMC,QAAQD,EAAEE,QAAQ;QACxB,MAAMC,UAAUH,EAAEI,UAAU;QAC5B,MAAMC,SAASJ,SAAS,KAAK,OAAO;QACpC,MAAMK,eAAeL,UAAU,IAAI,KAAKA,QAAQ,KAAKA,QAAQ,KAAKA;QAClE,OAAOE,YAAY,IAAI,GAAGG,eAAeD,QAAQ,GAAG,GAAGC,aAAa,CAAC,EAAEH,QAAQI,QAAQ,GAAGC,QAAQ,CAAC,GAAG,OAAOH,QAAQ;IACvH;IAEA,OAAO,GAAGN,WAAWF,OAAO,GAAG,EAAEE,WAAWD,MAAM;AACpD;AAEA,SAASW,eAAeC,MAAe;IACrC,OAAQA;QACN,KAAK;YACH,OAAOf,OAAOgB,SAAS;QACzB,KAAK;YACH,OAAOhB,OAAOiB,SAAS;QACzB,KAAK;YACH,OAAOjB,OAAOkB,SAAS;QACzB,KAAK;YACH,OAAOlB,OAAOmB,MAAM;QACtB,KAAK;YACH,OAAOnB,OAAOoB,SAAS;QACzB;YACE,OAAO;IACX;AACF;AAEA,OAAO,MAAMC,gBAA8C,CAAC,EAC1DC,KAAK,EACLC,OAAO,EACPC,KAAK,EACN;IACC,MAAMC,cAAc,CAACC;QACnBA,EAAEC,eAAe;QACjB,IAAIJ,SAAS;YACXA,QAAQD;QACV;IACF;IAEA,MAAMM,kBACJN,MAAMO,IAAI,KAAK,aACXC,YACAR,MAAMS,YAAY,IAAIT,MAAMU,eAAe,IAAI;IAErD,MAAMC,cAAcL;IAEpB,MAAMM,aAAkC;QACtC,GAAGV,KAAK;QACRI,iBACEN,MAAMO,IAAI,KAAK,aAAaC,YAAY,GAAGF,gBAAgB,EAAE,CAAC;QAChEO,iBAAiBF;QACjBG,OAAOd,MAAMO,IAAI,KAAK,aAAaC,YAAYF;IACjD;IAEA,MAAMS,eAAe;QACnBrC,OAAOsB,KAAK;QACZA,MAAMO,IAAI,KAAK,gBAAgB7B,OAAOsC,WAAW,GAAGtC,OAAOuC,QAAQ;QACnEzB,eAAeQ,MAAMP,MAAM;KAC5B,CACEyB,MAAM,CAACC,SACPC,IAAI,CAAC;IAER,MAAMC,eACJrB,MAAMqB,YAAY,IAAIrB,MAAMsB,SAAS,IAAI;IAE3C,qBACE,MAACC;QACCC,WAAWT;QACXd,SAASE;QACTsB,WAAW,CAACrB,IAAMA,EAAEsB,GAAG,KAAK,WAAWvB,YAAYC;QACnDuB,MAAK;QACLzB,OAAOU;QACPgB,UAAU;;0BAEV,KAACL;gBAAIC,WAAW9C,OAAOmD,UAAU;0BAC9B7B,MAAMO,IAAI,KAAK,aACZP,MAAM8B,cAAc,IAAI,YACxB9B,MAAM+B,WAAW,IAAI/B,MAAMgC,KAAK;;0BAEtC,KAACT;gBAAIC,WAAW9C,OAAOuD,SAAS;0BAC7BtD,gBAAgB,IAAIuD,KAAKlC,MAAMpB,KAAK,GAAG,IAAIsD,KAAKlC,MAAMnB,GAAG;;YAE3DmB,MAAMO,IAAI,KAAK,+BACd,KAACgB;gBAAIC,WAAW9C,OAAOyD,aAAa;0BAAGd;;;;AAI/C,EAAC"}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { WeekViewProps } from './types.js';
3
+ export declare const WeekView: React.FC<WeekViewProps>;
@@ -0,0 +1,203 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { useMemo } from 'react';
4
+ import styles from './Calendar.module.css';
5
+ import { EventRenderer } from './EventRenderer.js';
6
+ const DAY_NAMES = [
7
+ 'Sun',
8
+ 'Mon',
9
+ 'Tue',
10
+ 'Wed',
11
+ 'Thu',
12
+ 'Fri',
13
+ 'Sat'
14
+ ];
15
+ function getWeekDays(weekStart) {
16
+ const days = [];
17
+ const start = new Date(weekStart);
18
+ start.setHours(0, 0, 0, 0);
19
+ for(let i = 0; i < 7; i++){
20
+ const day = new Date(start);
21
+ day.setDate(start.getDate() + i);
22
+ days.push(day);
23
+ }
24
+ return days;
25
+ }
26
+ function formatTime(hour) {
27
+ const period = hour >= 12 ? 'PM' : 'AM';
28
+ const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
29
+ return `${displayHour} ${period}`;
30
+ }
31
+ function isSameDay(date1, date2) {
32
+ return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
33
+ }
34
+ function isToday(date) {
35
+ return isSameDay(date, new Date());
36
+ }
37
+ function getEventPosition(event, day, startHour, endHour) {
38
+ const eventStart = new Date(event.start);
39
+ const eventEnd = new Date(event.end);
40
+ if (!isSameDay(eventStart, day) && !isSameDay(eventEnd, day)) {
41
+ // Check if the day is between start and end
42
+ if (day < eventStart || day > eventEnd) {
43
+ return null;
44
+ }
45
+ }
46
+ const dayStart = new Date(day);
47
+ dayStart.setHours(startHour, 0, 0, 0);
48
+ const dayEnd = new Date(day);
49
+ dayEnd.setHours(endHour, 0, 0, 0);
50
+ // Clamp event times to the visible day range
51
+ const visibleStart = eventStart < dayStart ? dayStart : eventStart;
52
+ const visibleEnd = eventEnd > dayEnd ? dayEnd : eventEnd;
53
+ if (visibleStart >= visibleEnd) {
54
+ return null;
55
+ }
56
+ const totalMinutes = (endHour - startHour) * 60;
57
+ const startMinutes = (visibleStart.getHours() - startHour) * 60 + visibleStart.getMinutes();
58
+ const endMinutes = (visibleEnd.getHours() - startHour) * 60 + visibleEnd.getMinutes();
59
+ const top = startMinutes / totalMinutes * 100;
60
+ const height = (endMinutes - startMinutes) / totalMinutes * 100;
61
+ return {
62
+ height: Math.max(height, 2),
63
+ top
64
+ };
65
+ }
66
+ function getCurrentTimePosition(startHour, endHour) {
67
+ const now = new Date();
68
+ const currentHour = now.getHours();
69
+ const currentMinutes = now.getMinutes();
70
+ if (currentHour < startHour || currentHour >= endHour) {
71
+ return null;
72
+ }
73
+ const totalMinutes = (endHour - startHour) * 60;
74
+ const minutesSinceStart = (currentHour - startHour) * 60 + currentMinutes;
75
+ return minutesSinceStart / totalMinutes * 100;
76
+ }
77
+ export const WeekView = ({ endHour = 18, events, onEventClick, onSlotClick, startHour = 8, weekStart })=>{
78
+ const weekDays = useMemo(()=>getWeekDays(weekStart), [
79
+ weekStart
80
+ ]);
81
+ const hours = useMemo(()=>Array.from({
82
+ length: endHour - startHour
83
+ }, (_, i)=>startHour + i), [
84
+ startHour,
85
+ endHour
86
+ ]);
87
+ const currentTimePosition = useMemo(()=>{
88
+ const today = weekDays.find(isToday);
89
+ if (!today) {
90
+ return null;
91
+ }
92
+ return getCurrentTimePosition(startHour, endHour);
93
+ }, [
94
+ weekDays,
95
+ startHour,
96
+ endHour
97
+ ]);
98
+ const eventsByDay = useMemo(()=>{
99
+ const map = new Map();
100
+ weekDays.forEach((day)=>{
101
+ const dayKey = day.toISOString().split('T')[0];
102
+ const dayEvents = events.filter((event)=>{
103
+ const eventStart = new Date(event.start);
104
+ const eventEnd = new Date(event.end);
105
+ return isSameDay(eventStart, day) || isSameDay(eventEnd, day) || eventStart < day && eventEnd > day;
106
+ });
107
+ map.set(dayKey, dayEvents);
108
+ });
109
+ return map;
110
+ }, [
111
+ events,
112
+ weekDays
113
+ ]);
114
+ const handleSlotClick = (day, hour)=>{
115
+ if (onSlotClick) {
116
+ const start = new Date(day);
117
+ start.setHours(hour, 0, 0, 0);
118
+ const end = new Date(start);
119
+ end.setHours(hour + 1, 0, 0, 0);
120
+ onSlotClick(start, end);
121
+ }
122
+ };
123
+ return /*#__PURE__*/ _jsxs("div", {
124
+ className: styles.weekGrid,
125
+ children: [
126
+ /*#__PURE__*/ _jsx("div", {
127
+ className: styles.timeGutter
128
+ }),
129
+ weekDays.map((day)=>/*#__PURE__*/ _jsxs("div", {
130
+ className: `${styles.dayHeader} ${isToday(day) ? styles.isToday : ''}`,
131
+ children: [
132
+ /*#__PURE__*/ _jsx("div", {
133
+ className: styles.dayName,
134
+ children: DAY_NAMES[day.getDay()]
135
+ }),
136
+ /*#__PURE__*/ _jsx("div", {
137
+ className: styles.dayNumber,
138
+ children: day.getDate()
139
+ })
140
+ ]
141
+ }, day.toISOString())),
142
+ hours.map((hour)=>/*#__PURE__*/ _jsxs(React.Fragment, {
143
+ children: [
144
+ /*#__PURE__*/ _jsx("div", {
145
+ className: styles.timeSlot,
146
+ children: formatTime(hour)
147
+ }),
148
+ weekDays.map((day)=>{
149
+ const dayKey = day.toISOString().split('T')[0];
150
+ const dayEvents = eventsByDay.get(dayKey) || [];
151
+ return /*#__PURE__*/ _jsxs("div", {
152
+ className: styles.dayColumn,
153
+ style: {
154
+ gridRow: 'span 1'
155
+ },
156
+ children: [
157
+ /*#__PURE__*/ _jsx("div", {
158
+ className: styles.hourSlot,
159
+ onClick: ()=>handleSlotClick(day, hour),
160
+ onKeyDown: (e)=>e.key === 'Enter' && handleSlotClick(day, hour),
161
+ role: "button",
162
+ tabIndex: 0
163
+ }),
164
+ /*#__PURE__*/ _jsx("div", {
165
+ className: styles.halfHourLine,
166
+ style: {
167
+ top: '50%'
168
+ }
169
+ }),
170
+ hour === hours[0] && /*#__PURE__*/ _jsxs(_Fragment, {
171
+ children: [
172
+ dayEvents.map((event)=>{
173
+ const position = getEventPosition(event, day, startHour, endHour);
174
+ if (!position) {
175
+ return null;
176
+ }
177
+ return /*#__PURE__*/ _jsx(EventRenderer, {
178
+ event: event,
179
+ onClick: onEventClick,
180
+ style: {
181
+ height: `${position.height}%`,
182
+ top: `${position.top}%`
183
+ }
184
+ }, event.id);
185
+ }),
186
+ isToday(day) && currentTimePosition !== null && /*#__PURE__*/ _jsx("div", {
187
+ className: styles.currentTimeIndicator,
188
+ style: {
189
+ top: `${currentTimePosition}%`
190
+ }
191
+ })
192
+ ]
193
+ })
194
+ ]
195
+ }, `${dayKey}-${hour}`);
196
+ })
197
+ ]
198
+ }, hour))
199
+ ]
200
+ });
201
+ };
202
+
203
+ //# sourceMappingURL=WeekView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/calendar/WeekView.tsx"],"sourcesContent":["'use client'\n\nimport React, { useMemo } from 'react'\n\nimport type { CalendarEvent, WeekViewProps } from './types.js'\n\nimport styles from './Calendar.module.css'\nimport { EventRenderer } from './EventRenderer.js'\n\nconst DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n\nfunction getWeekDays(weekStart: Date): Date[] {\n const days: Date[] = []\n const start = new Date(weekStart)\n start.setHours(0, 0, 0, 0)\n\n for (let i = 0; i < 7; i++) {\n const day = new Date(start)\n day.setDate(start.getDate() + i)\n days.push(day)\n }\n\n return days\n}\n\nfunction formatTime(hour: number): string {\n const period = hour >= 12 ? 'PM' : 'AM'\n const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour\n return `${displayHour} ${period}`\n}\n\nfunction isSameDay(date1: Date, date2: Date): boolean {\n return (\n date1.getFullYear() === date2.getFullYear() &&\n date1.getMonth() === date2.getMonth() &&\n date1.getDate() === date2.getDate()\n )\n}\n\nfunction isToday(date: Date): boolean {\n return isSameDay(date, new Date())\n}\n\nfunction getEventPosition(\n event: CalendarEvent,\n day: Date,\n startHour: number,\n endHour: number\n): { height: number; top: number } | null {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n\n if (!isSameDay(eventStart, day) && !isSameDay(eventEnd, day)) {\n // Check if the day is between start and end\n if (day < eventStart || day > eventEnd) {\n return null\n }\n }\n\n const dayStart = new Date(day)\n dayStart.setHours(startHour, 0, 0, 0)\n const dayEnd = new Date(day)\n dayEnd.setHours(endHour, 0, 0, 0)\n\n // Clamp event times to the visible day range\n const visibleStart = eventStart < dayStart ? dayStart : eventStart\n const visibleEnd = eventEnd > dayEnd ? dayEnd : eventEnd\n\n if (visibleStart >= visibleEnd) {\n return null\n }\n\n const totalMinutes = (endHour - startHour) * 60\n const startMinutes =\n (visibleStart.getHours() - startHour) * 60 + visibleStart.getMinutes()\n const endMinutes =\n (visibleEnd.getHours() - startHour) * 60 + visibleEnd.getMinutes()\n\n const top = (startMinutes / totalMinutes) * 100\n const height = ((endMinutes - startMinutes) / totalMinutes) * 100\n\n return { height: Math.max(height, 2), top }\n}\n\nfunction getCurrentTimePosition(startHour: number, endHour: number): null | number {\n const now = new Date()\n const currentHour = now.getHours()\n const currentMinutes = now.getMinutes()\n\n if (currentHour < startHour || currentHour >= endHour) {\n return null\n }\n\n const totalMinutes = (endHour - startHour) * 60\n const minutesSinceStart = (currentHour - startHour) * 60 + currentMinutes\n\n return (minutesSinceStart / totalMinutes) * 100\n}\n\nexport const WeekView: React.FC<WeekViewProps> = ({\n endHour = 18,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n weekStart,\n}) => {\n const weekDays = useMemo(() => getWeekDays(weekStart), [weekStart])\n const hours = useMemo(\n () => Array.from({ length: endHour - startHour }, (_, i) => startHour + i),\n [startHour, endHour]\n )\n\n const currentTimePosition = useMemo(() => {\n const today = weekDays.find(isToday)\n if (!today) {return null}\n return getCurrentTimePosition(startHour, endHour)\n }, [weekDays, startHour, endHour])\n\n const eventsByDay = useMemo(() => {\n const map = new Map<string, CalendarEvent[]>()\n\n weekDays.forEach((day) => {\n const dayKey = day.toISOString().split('T')[0]\n const dayEvents = events.filter((event: CalendarEvent) => {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n return (\n isSameDay(eventStart, day) ||\n isSameDay(eventEnd, day) ||\n (eventStart < day && eventEnd > day)\n )\n })\n map.set(dayKey, dayEvents)\n })\n\n return map\n }, [events, weekDays])\n\n const handleSlotClick = (day: Date, hour: number) => {\n if (onSlotClick) {\n const start = new Date(day)\n start.setHours(hour, 0, 0, 0)\n const end = new Date(start)\n end.setHours(hour + 1, 0, 0, 0)\n onSlotClick(start, end)\n }\n }\n\n return (\n <div className={styles.weekGrid}>\n {/* Header row */}\n <div className={styles.timeGutter} />\n {weekDays.map((day) => (\n <div\n className={`${styles.dayHeader} ${isToday(day) ? styles.isToday : ''}`}\n key={day.toISOString()}\n >\n <div className={styles.dayName}>{DAY_NAMES[day.getDay()]}</div>\n <div className={styles.dayNumber}>{day.getDate()}</div>\n </div>\n ))}\n\n {/* Time slots */}\n {hours.map((hour) => (\n <React.Fragment key={hour}>\n <div className={styles.timeSlot}>{formatTime(hour)}</div>\n {weekDays.map((day) => {\n const dayKey = day.toISOString().split('T')[0]\n const dayEvents = eventsByDay.get(dayKey) || []\n\n return (\n <div\n className={styles.dayColumn}\n key={`${dayKey}-${hour}`}\n style={{ gridRow: 'span 1' }}\n >\n <div\n className={styles.hourSlot}\n onClick={() => handleSlotClick(day, hour)}\n onKeyDown={(e) => e.key === 'Enter' && handleSlotClick(day, hour)}\n role=\"button\"\n tabIndex={0}\n />\n <div\n className={styles.halfHourLine}\n style={{ top: '50%' }}\n />\n {hour === hours[0] && (\n <>\n {dayEvents.map((event) => {\n const position = getEventPosition(\n event,\n day,\n startHour,\n endHour\n )\n if (!position) {return null}\n\n return (\n <EventRenderer\n event={event}\n key={event.id}\n onClick={onEventClick}\n style={{\n height: `${position.height}%`,\n top: `${position.top}%`,\n }}\n />\n )\n })}\n {isToday(day) && currentTimePosition !== null && (\n <div\n className={styles.currentTimeIndicator}\n style={{ top: `${currentTimePosition}%` }}\n />\n )}\n </>\n )}\n </div>\n )\n })}\n </React.Fragment>\n ))}\n </div>\n )\n}\n"],"names":["React","useMemo","styles","EventRenderer","DAY_NAMES","getWeekDays","weekStart","days","start","Date","setHours","i","day","setDate","getDate","push","formatTime","hour","period","displayHour","isSameDay","date1","date2","getFullYear","getMonth","isToday","date","getEventPosition","event","startHour","endHour","eventStart","eventEnd","end","dayStart","dayEnd","visibleStart","visibleEnd","totalMinutes","startMinutes","getHours","getMinutes","endMinutes","top","height","Math","max","getCurrentTimePosition","now","currentHour","currentMinutes","minutesSinceStart","WeekView","events","onEventClick","onSlotClick","weekDays","hours","Array","from","length","_","currentTimePosition","today","find","eventsByDay","map","Map","forEach","dayKey","toISOString","split","dayEvents","filter","set","handleSlotClick","div","className","weekGrid","timeGutter","dayHeader","dayName","getDay","dayNumber","Fragment","timeSlot","get","dayColumn","style","gridRow","hourSlot","onClick","onKeyDown","e","key","role","tabIndex","halfHourLine","position","id","currentTimeIndicator"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { CalendarContainer } from './CalendarContainer.js';
2
+ export { DayView } from './DayView.js';
3
+ export { EventPopover } from './EventPopover.js';
4
+ export { EventRenderer } from './EventRenderer.js';
5
+ export type { CalendarEvent, CalendarProps, CalendarViewMode, DayViewProps, EventPopoverProps, EventRendererProps, Service, TeamMember, WeekViewProps, } from './types.js';
6
+ export { WeekView } from './WeekView.js';
@@ -0,0 +1,7 @@
1
+ export { CalendarContainer } from './CalendarContainer.js';
2
+ export { DayView } from './DayView.js';
3
+ export { EventPopover } from './EventPopover.js';
4
+ export { EventRenderer } from './EventRenderer.js';
5
+ export { WeekView } from './WeekView.js';
6
+
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/calendar/index.ts"],"sourcesContent":["export { CalendarContainer } from './CalendarContainer.js'\nexport { DayView } from './DayView.js'\nexport { EventPopover } from './EventPopover.js'\nexport { EventRenderer } from './EventRenderer.js'\nexport type {\n CalendarEvent,\n CalendarProps,\n CalendarViewMode,\n DayViewProps,\n EventPopoverProps,\n EventRendererProps,\n Service,\n TeamMember,\n WeekViewProps,\n} from './types.js'\nexport { WeekView } from './WeekView.js'\n"],"names":["CalendarContainer","DayView","EventPopover","EventRenderer","WeekView"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,YAAY,QAAQ,oBAAmB;AAChD,SAASC,aAAa,QAAQ,qBAAoB;AAYlD,SAASC,QAAQ,QAAQ,gBAAe"}