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.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/dist/collections/Appointments.d.ts +2 -0
- package/dist/collections/Appointments.js +165 -0
- package/dist/collections/Appointments.js.map +1 -0
- package/dist/collections/GuestCustomers.d.ts +2 -0
- package/dist/collections/GuestCustomers.js +106 -0
- package/dist/collections/GuestCustomers.js.map +1 -0
- package/dist/collections/Services.d.ts +2 -0
- package/dist/collections/Services.js +147 -0
- package/dist/collections/Services.js.map +1 -0
- package/dist/collections/TeamMembers.d.ts +2 -0
- package/dist/collections/TeamMembers.js +184 -0
- package/dist/collections/TeamMembers.js.map +1 -0
- package/dist/components/BeforeDashboardClient.d.ts +2 -0
- package/dist/components/BeforeDashboardClient.js +162 -0
- package/dist/components/BeforeDashboardClient.js.map +1 -0
- package/dist/components/BeforeDashboardServer.d.ts +2 -0
- package/dist/components/BeforeDashboardServer.js +22 -0
- package/dist/components/BeforeDashboardServer.js.map +1 -0
- package/dist/components/BeforeDashboardServer.module.css +5 -0
- package/dist/components/calendar/Calendar.module.css +506 -0
- package/dist/components/calendar/CalendarContainer.d.ts +3 -0
- package/dist/components/calendar/CalendarContainer.js +246 -0
- package/dist/components/calendar/CalendarContainer.js.map +1 -0
- package/dist/components/calendar/DayView.d.ts +3 -0
- package/dist/components/calendar/DayView.js +192 -0
- package/dist/components/calendar/DayView.js.map +1 -0
- package/dist/components/calendar/EventPopover.d.ts +3 -0
- package/dist/components/calendar/EventPopover.js +257 -0
- package/dist/components/calendar/EventPopover.js.map +1 -0
- package/dist/components/calendar/EventRenderer.d.ts +3 -0
- package/dist/components/calendar/EventRenderer.js +76 -0
- package/dist/components/calendar/EventRenderer.js.map +1 -0
- package/dist/components/calendar/WeekView.d.ts +3 -0
- package/dist/components/calendar/WeekView.js +203 -0
- package/dist/components/calendar/WeekView.js.map +1 -0
- package/dist/components/calendar/index.d.ts +6 -0
- package/dist/components/calendar/index.js +7 -0
- package/dist/components/calendar/index.js.map +1 -0
- package/dist/components/calendar/types.d.ts +69 -0
- package/dist/components/calendar/types.js +3 -0
- package/dist/components/calendar/types.js.map +1 -0
- package/dist/endpoints/customEndpointHandler.d.ts +2 -0
- package/dist/endpoints/customEndpointHandler.js +7 -0
- package/dist/endpoints/customEndpointHandler.js.map +1 -0
- package/dist/endpoints/getAvailableSlots.d.ts +12 -0
- package/dist/endpoints/getAvailableSlots.js +291 -0
- package/dist/endpoints/getAvailableSlots.js.map +1 -0
- package/dist/exports/client.d.ts +3 -0
- package/dist/exports/client.js +4 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/globals/OpeningTimes.d.ts +2 -0
- package/dist/globals/OpeningTimes.js +196 -0
- package/dist/globals/OpeningTimes.js.map +1 -0
- package/dist/hooks/addAdminTitle.d.ts +7 -0
- package/dist/hooks/addAdminTitle.js +86 -0
- package/dist/hooks/addAdminTitle.js.map +1 -0
- package/dist/hooks/sendCustomerEmail.d.ts +6 -0
- package/dist/hooks/sendCustomerEmail.js +351 -0
- package/dist/hooks/sendCustomerEmail.js.map +1 -0
- package/dist/hooks/setEndDateTime.d.ts +6 -0
- package/dist/hooks/setEndDateTime.js +44 -0
- package/dist/hooks/setEndDateTime.js.map +1 -0
- package/dist/hooks/validateCustomerOrGuest.d.ts +6 -0
- package/dist/hooks/validateCustomerOrGuest.js +21 -0
- package/dist/hooks/validateCustomerOrGuest.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -0
- package/package.json +135 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
4
|
+
import styles from './Calendar.module.css';
|
|
5
|
+
import { DayView } from './DayView.js';
|
|
6
|
+
import { EventPopover } from './EventPopover.js';
|
|
7
|
+
import { WeekView } from './WeekView.js';
|
|
8
|
+
function getWeekStart(date) {
|
|
9
|
+
const d = new Date(date);
|
|
10
|
+
d.setHours(0, 0, 0, 0);
|
|
11
|
+
const day = d.getDay();
|
|
12
|
+
d.setDate(d.getDate() - day);
|
|
13
|
+
return d;
|
|
14
|
+
}
|
|
15
|
+
const MONTH_NAMES = [
|
|
16
|
+
'January',
|
|
17
|
+
'February',
|
|
18
|
+
'March',
|
|
19
|
+
'April',
|
|
20
|
+
'May',
|
|
21
|
+
'June',
|
|
22
|
+
'July',
|
|
23
|
+
'August',
|
|
24
|
+
'September',
|
|
25
|
+
'October',
|
|
26
|
+
'November',
|
|
27
|
+
'December'
|
|
28
|
+
];
|
|
29
|
+
export const CalendarContainer = ({ endHour = 18, events, onDateChange, onEventClick, onEventDrop, onSlotClick, onViewModeChange, selectedDate: controlledSelectedDate, slotDuration = 30, startHour = 8, teamMembers, viewMode: controlledViewMode })=>{
|
|
30
|
+
// Local state for uncontrolled mode
|
|
31
|
+
const [localViewMode, setLocalViewMode] = useState('week');
|
|
32
|
+
const [localSelectedDate, setLocalSelectedDate] = useState(new Date());
|
|
33
|
+
const [popoverEvent, setPopoverEvent] = useState(null);
|
|
34
|
+
const [popoverPosition, setPopoverPosition] = useState({
|
|
35
|
+
left: 0,
|
|
36
|
+
top: 0
|
|
37
|
+
});
|
|
38
|
+
// Use controlled or local state
|
|
39
|
+
const viewMode = controlledViewMode ?? localViewMode;
|
|
40
|
+
const selectedDate = controlledSelectedDate ?? localSelectedDate;
|
|
41
|
+
const handleViewModeChange = useCallback((mode)=>{
|
|
42
|
+
if (onViewModeChange) {
|
|
43
|
+
onViewModeChange(mode);
|
|
44
|
+
} else {
|
|
45
|
+
setLocalViewMode(mode);
|
|
46
|
+
}
|
|
47
|
+
}, [
|
|
48
|
+
onViewModeChange
|
|
49
|
+
]);
|
|
50
|
+
const handleDateChange = useCallback((date)=>{
|
|
51
|
+
if (onDateChange) {
|
|
52
|
+
onDateChange(date);
|
|
53
|
+
} else {
|
|
54
|
+
setLocalSelectedDate(date);
|
|
55
|
+
}
|
|
56
|
+
}, [
|
|
57
|
+
onDateChange
|
|
58
|
+
]);
|
|
59
|
+
const weekStart = useMemo(()=>getWeekStart(selectedDate), [
|
|
60
|
+
selectedDate
|
|
61
|
+
]);
|
|
62
|
+
const handlePrev = useCallback(()=>{
|
|
63
|
+
const newDate = new Date(selectedDate);
|
|
64
|
+
if (viewMode === 'week') {
|
|
65
|
+
newDate.setDate(newDate.getDate() - 7);
|
|
66
|
+
} else {
|
|
67
|
+
newDate.setDate(newDate.getDate() - 1);
|
|
68
|
+
}
|
|
69
|
+
handleDateChange(newDate);
|
|
70
|
+
}, [
|
|
71
|
+
selectedDate,
|
|
72
|
+
viewMode,
|
|
73
|
+
handleDateChange
|
|
74
|
+
]);
|
|
75
|
+
const handleNext = useCallback(()=>{
|
|
76
|
+
const newDate = new Date(selectedDate);
|
|
77
|
+
if (viewMode === 'week') {
|
|
78
|
+
newDate.setDate(newDate.getDate() + 7);
|
|
79
|
+
} else {
|
|
80
|
+
newDate.setDate(newDate.getDate() + 1);
|
|
81
|
+
}
|
|
82
|
+
handleDateChange(newDate);
|
|
83
|
+
}, [
|
|
84
|
+
selectedDate,
|
|
85
|
+
viewMode,
|
|
86
|
+
handleDateChange
|
|
87
|
+
]);
|
|
88
|
+
const handleToday = useCallback(()=>{
|
|
89
|
+
handleDateChange(new Date());
|
|
90
|
+
}, [
|
|
91
|
+
handleDateChange
|
|
92
|
+
]);
|
|
93
|
+
const handleEventClick = useCallback((event)=>{
|
|
94
|
+
// Calculate popover position near the event
|
|
95
|
+
const rect = document.querySelector(`[data-event-id="${event.id}"]`)?.getBoundingClientRect();
|
|
96
|
+
if (rect) {
|
|
97
|
+
setPopoverPosition({
|
|
98
|
+
left: rect.right + 10,
|
|
99
|
+
top: rect.top + window.scrollY
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback to center of screen
|
|
103
|
+
setPopoverPosition({
|
|
104
|
+
left: window.innerWidth / 2 - 150,
|
|
105
|
+
top: window.innerHeight / 2 - 150
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
setPopoverEvent(event);
|
|
109
|
+
if (onEventClick) {
|
|
110
|
+
onEventClick(event);
|
|
111
|
+
}
|
|
112
|
+
}, [
|
|
113
|
+
onEventClick
|
|
114
|
+
]);
|
|
115
|
+
const handleClosePopover = useCallback(()=>{
|
|
116
|
+
setPopoverEvent(null);
|
|
117
|
+
}, []);
|
|
118
|
+
const handleEditEvent = useCallback((event)=>{
|
|
119
|
+
handleClosePopover();
|
|
120
|
+
// Navigate to edit page
|
|
121
|
+
window.location.href = `/admin/collections/appointments/${event.id}`;
|
|
122
|
+
}, [
|
|
123
|
+
handleClosePopover
|
|
124
|
+
]);
|
|
125
|
+
const dateTitle = useMemo(()=>{
|
|
126
|
+
if (viewMode === 'week') {
|
|
127
|
+
const weekEnd = new Date(weekStart);
|
|
128
|
+
weekEnd.setDate(weekEnd.getDate() + 6);
|
|
129
|
+
if (weekStart.getMonth() === weekEnd.getMonth()) {
|
|
130
|
+
return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()} - ${weekEnd.getDate()}, ${weekStart.getFullYear()}`;
|
|
131
|
+
} else if (weekStart.getFullYear() === weekEnd.getFullYear()) {
|
|
132
|
+
return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()} - ${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}, ${weekStart.getFullYear()}`;
|
|
133
|
+
} else {
|
|
134
|
+
return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()}, ${weekStart.getFullYear()} - ${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}, ${weekEnd.getFullYear()}`;
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
return `${MONTH_NAMES[selectedDate.getMonth()]} ${selectedDate.getDate()}, ${selectedDate.getFullYear()}`;
|
|
138
|
+
}
|
|
139
|
+
}, [
|
|
140
|
+
viewMode,
|
|
141
|
+
weekStart,
|
|
142
|
+
selectedDate
|
|
143
|
+
]);
|
|
144
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
145
|
+
className: styles.calendarContainer,
|
|
146
|
+
children: [
|
|
147
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
148
|
+
className: styles.calendarHeader,
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
151
|
+
className: styles.calendarNav,
|
|
152
|
+
children: [
|
|
153
|
+
/*#__PURE__*/ _jsx("button", {
|
|
154
|
+
"aria-label": "Previous",
|
|
155
|
+
className: styles.navButton,
|
|
156
|
+
onClick: handlePrev,
|
|
157
|
+
children: /*#__PURE__*/ _jsx("svg", {
|
|
158
|
+
fill: "none",
|
|
159
|
+
height: "16",
|
|
160
|
+
stroke: "currentColor",
|
|
161
|
+
strokeLinecap: "round",
|
|
162
|
+
strokeLinejoin: "round",
|
|
163
|
+
strokeWidth: "2",
|
|
164
|
+
viewBox: "0 0 24 24",
|
|
165
|
+
width: "16",
|
|
166
|
+
children: /*#__PURE__*/ _jsx("polyline", {
|
|
167
|
+
points: "15 18 9 12 15 6"
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
/*#__PURE__*/ _jsx("button", {
|
|
172
|
+
"aria-label": "Next",
|
|
173
|
+
className: styles.navButton,
|
|
174
|
+
onClick: handleNext,
|
|
175
|
+
children: /*#__PURE__*/ _jsx("svg", {
|
|
176
|
+
fill: "none",
|
|
177
|
+
height: "16",
|
|
178
|
+
stroke: "currentColor",
|
|
179
|
+
strokeLinecap: "round",
|
|
180
|
+
strokeLinejoin: "round",
|
|
181
|
+
strokeWidth: "2",
|
|
182
|
+
viewBox: "0 0 24 24",
|
|
183
|
+
width: "16",
|
|
184
|
+
children: /*#__PURE__*/ _jsx("polyline", {
|
|
185
|
+
points: "9 18 15 12 9 6"
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
}),
|
|
189
|
+
/*#__PURE__*/ _jsx("span", {
|
|
190
|
+
className: styles.dateTitle,
|
|
191
|
+
children: dateTitle
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
}),
|
|
195
|
+
/*#__PURE__*/ _jsx("button", {
|
|
196
|
+
className: styles.todayButton,
|
|
197
|
+
onClick: handleToday,
|
|
198
|
+
children: "Today"
|
|
199
|
+
}),
|
|
200
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
201
|
+
className: styles.viewToggle,
|
|
202
|
+
children: [
|
|
203
|
+
/*#__PURE__*/ _jsx("button", {
|
|
204
|
+
className: `${styles.viewButton} ${viewMode === 'week' ? styles.active : ''}`,
|
|
205
|
+
onClick: ()=>handleViewModeChange('week'),
|
|
206
|
+
children: "Week"
|
|
207
|
+
}),
|
|
208
|
+
/*#__PURE__*/ _jsx("button", {
|
|
209
|
+
className: `${styles.viewButton} ${viewMode === 'day' ? styles.active : ''}`,
|
|
210
|
+
onClick: ()=>handleViewModeChange('day'),
|
|
211
|
+
children: "Day"
|
|
212
|
+
})
|
|
213
|
+
]
|
|
214
|
+
})
|
|
215
|
+
]
|
|
216
|
+
}),
|
|
217
|
+
viewMode === 'week' ? /*#__PURE__*/ _jsx(WeekView, {
|
|
218
|
+
endHour: endHour,
|
|
219
|
+
events: events,
|
|
220
|
+
onEventClick: handleEventClick,
|
|
221
|
+
onSlotClick: onSlotClick,
|
|
222
|
+
slotDuration: slotDuration,
|
|
223
|
+
startHour: startHour,
|
|
224
|
+
teamMembers: teamMembers,
|
|
225
|
+
weekStart: weekStart
|
|
226
|
+
}) : /*#__PURE__*/ _jsx(DayView, {
|
|
227
|
+
date: selectedDate,
|
|
228
|
+
endHour: endHour,
|
|
229
|
+
events: events,
|
|
230
|
+
onEventClick: handleEventClick,
|
|
231
|
+
onSlotClick: onSlotClick,
|
|
232
|
+
slotDuration: slotDuration,
|
|
233
|
+
startHour: startHour,
|
|
234
|
+
teamMembers: teamMembers
|
|
235
|
+
}),
|
|
236
|
+
popoverEvent && /*#__PURE__*/ _jsx(EventPopover, {
|
|
237
|
+
event: popoverEvent,
|
|
238
|
+
onClose: handleClosePopover,
|
|
239
|
+
onEdit: handleEditEvent,
|
|
240
|
+
position: popoverPosition
|
|
241
|
+
})
|
|
242
|
+
]
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
//# sourceMappingURL=CalendarContainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/calendar/CalendarContainer.tsx"],"sourcesContent":["'use client'\n\nimport React, { useCallback, useMemo, useState } from 'react'\n\nimport type { CalendarEvent, CalendarProps, CalendarViewMode } from './types.js'\n\nimport styles from './Calendar.module.css'\nimport { DayView } from './DayView.js'\nimport { EventPopover } from './EventPopover.js'\nimport { WeekView } from './WeekView.js'\n\nfunction getWeekStart(date: Date): Date {\n const d = new Date(date)\n d.setHours(0, 0, 0, 0)\n const day = d.getDay()\n d.setDate(d.getDate() - day)\n return d\n}\n\nconst MONTH_NAMES = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n]\n\nexport const CalendarContainer: React.FC<CalendarProps> = ({\n endHour = 18,\n events,\n onDateChange,\n onEventClick,\n onEventDrop,\n onSlotClick,\n onViewModeChange,\n selectedDate: controlledSelectedDate,\n slotDuration = 30,\n startHour = 8,\n teamMembers,\n viewMode: controlledViewMode,\n}) => {\n // Local state for uncontrolled mode\n const [localViewMode, setLocalViewMode] = useState<CalendarViewMode>('week')\n const [localSelectedDate, setLocalSelectedDate] = useState(new Date())\n const [popoverEvent, setPopoverEvent] = useState<CalendarEvent | null>(null)\n const [popoverPosition, setPopoverPosition] = useState({ left: 0, top: 0 })\n\n // Use controlled or local state\n const viewMode = controlledViewMode ?? localViewMode\n const selectedDate = controlledSelectedDate ?? localSelectedDate\n\n const handleViewModeChange = useCallback(\n (mode: CalendarViewMode) => {\n if (onViewModeChange) {\n onViewModeChange(mode)\n } else {\n setLocalViewMode(mode)\n }\n },\n [onViewModeChange]\n )\n\n const handleDateChange = useCallback(\n (date: Date) => {\n if (onDateChange) {\n onDateChange(date)\n } else {\n setLocalSelectedDate(date)\n }\n },\n [onDateChange]\n )\n\n const weekStart = useMemo(() => getWeekStart(selectedDate), [selectedDate])\n\n const handlePrev = useCallback(() => {\n const newDate = new Date(selectedDate)\n if (viewMode === 'week') {\n newDate.setDate(newDate.getDate() - 7)\n } else {\n newDate.setDate(newDate.getDate() - 1)\n }\n handleDateChange(newDate)\n }, [selectedDate, viewMode, handleDateChange])\n\n const handleNext = useCallback(() => {\n const newDate = new Date(selectedDate)\n if (viewMode === 'week') {\n newDate.setDate(newDate.getDate() + 7)\n } else {\n newDate.setDate(newDate.getDate() + 1)\n }\n handleDateChange(newDate)\n }, [selectedDate, viewMode, handleDateChange])\n\n const handleToday = useCallback(() => {\n handleDateChange(new Date())\n }, [handleDateChange])\n\n const handleEventClick = useCallback(\n (event: CalendarEvent) => {\n // Calculate popover position near the event\n const rect = document\n .querySelector(`[data-event-id=\"${event.id}\"]`)\n ?.getBoundingClientRect()\n\n if (rect) {\n setPopoverPosition({\n left: rect.right + 10,\n top: rect.top + window.scrollY,\n })\n } else {\n // Fallback to center of screen\n setPopoverPosition({\n left: window.innerWidth / 2 - 150,\n top: window.innerHeight / 2 - 150,\n })\n }\n\n setPopoverEvent(event)\n\n if (onEventClick) {\n onEventClick(event)\n }\n },\n [onEventClick]\n )\n\n const handleClosePopover = useCallback(() => {\n setPopoverEvent(null)\n }, [])\n\n const handleEditEvent = useCallback(\n (event: CalendarEvent) => {\n handleClosePopover()\n // Navigate to edit page\n window.location.href = `/admin/collections/appointments/${event.id}`\n },\n [handleClosePopover]\n )\n\n const dateTitle = useMemo(() => {\n if (viewMode === 'week') {\n const weekEnd = new Date(weekStart)\n weekEnd.setDate(weekEnd.getDate() + 6)\n\n if (weekStart.getMonth() === weekEnd.getMonth()) {\n return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()} - ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n } else if (weekStart.getFullYear() === weekEnd.getFullYear()) {\n return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()} - ${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n } else {\n return `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()}, ${weekStart.getFullYear()} - ${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}, ${weekEnd.getFullYear()}`\n }\n } else {\n return `${MONTH_NAMES[selectedDate.getMonth()]} ${selectedDate.getDate()}, ${selectedDate.getFullYear()}`\n }\n }, [viewMode, weekStart, selectedDate])\n\n return (\n <div className={styles.calendarContainer}>\n <div className={styles.calendarHeader}>\n <div className={styles.calendarNav}>\n <button\n aria-label=\"Previous\"\n className={styles.navButton}\n onClick={handlePrev}\n >\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 <polyline points=\"15 18 9 12 15 6\" />\n </svg>\n </button>\n <button\n aria-label=\"Next\"\n className={styles.navButton}\n onClick={handleNext}\n >\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 <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n </button>\n <span className={styles.dateTitle}>{dateTitle}</span>\n </div>\n\n <button className={styles.todayButton} onClick={handleToday}>\n Today\n </button>\n\n <div className={styles.viewToggle}>\n <button\n className={`${styles.viewButton} ${viewMode === 'week' ? styles.active : ''}`}\n onClick={() => handleViewModeChange('week')}\n >\n Week\n </button>\n <button\n className={`${styles.viewButton} ${viewMode === 'day' ? styles.active : ''}`}\n onClick={() => handleViewModeChange('day')}\n >\n Day\n </button>\n </div>\n </div>\n\n {viewMode === 'week' ? (\n <WeekView\n endHour={endHour}\n events={events}\n onEventClick={handleEventClick}\n onSlotClick={onSlotClick}\n slotDuration={slotDuration}\n startHour={startHour}\n teamMembers={teamMembers}\n weekStart={weekStart}\n />\n ) : (\n <DayView\n date={selectedDate}\n endHour={endHour}\n events={events}\n onEventClick={handleEventClick}\n onSlotClick={onSlotClick}\n slotDuration={slotDuration}\n startHour={startHour}\n teamMembers={teamMembers}\n />\n )}\n\n {popoverEvent && (\n <EventPopover\n event={popoverEvent}\n onClose={handleClosePopover}\n onEdit={handleEditEvent}\n position={popoverPosition}\n />\n )}\n </div>\n )\n}\n"],"names":["React","useCallback","useMemo","useState","styles","DayView","EventPopover","WeekView","getWeekStart","date","d","Date","setHours","day","getDay","setDate","getDate","MONTH_NAMES","CalendarContainer","endHour","events","onDateChange","onEventClick","onEventDrop","onSlotClick","onViewModeChange","selectedDate","controlledSelectedDate","slotDuration","startHour","teamMembers","viewMode","controlledViewMode","localViewMode","setLocalViewMode","localSelectedDate","setLocalSelectedDate","popoverEvent","setPopoverEvent","popoverPosition","setPopoverPosition","left","top","handleViewModeChange","mode","handleDateChange","weekStart","handlePrev","newDate","handleNext","handleToday","handleEventClick","event","rect","document","querySelector","id","getBoundingClientRect","right","window","scrollY","innerWidth","innerHeight","handleClosePopover","handleEditEvent","location","href","dateTitle","weekEnd","getMonth","getFullYear","div","className","calendarContainer","calendarHeader","calendarNav","button","aria-label","navButton","onClick","svg","fill","height","stroke","strokeLinecap","strokeLinejoin","strokeWidth","viewBox","width","polyline","points","span","todayButton","viewToggle","viewButton","active","onClose","onEdit","position"],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
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
|
+
'Sunday',
|
|
8
|
+
'Monday',
|
|
9
|
+
'Tuesday',
|
|
10
|
+
'Wednesday',
|
|
11
|
+
'Thursday',
|
|
12
|
+
'Friday',
|
|
13
|
+
'Saturday'
|
|
14
|
+
];
|
|
15
|
+
const MONTH_NAMES = [
|
|
16
|
+
'January',
|
|
17
|
+
'February',
|
|
18
|
+
'March',
|
|
19
|
+
'April',
|
|
20
|
+
'May',
|
|
21
|
+
'June',
|
|
22
|
+
'July',
|
|
23
|
+
'August',
|
|
24
|
+
'September',
|
|
25
|
+
'October',
|
|
26
|
+
'November',
|
|
27
|
+
'December'
|
|
28
|
+
];
|
|
29
|
+
function formatTime(hour) {
|
|
30
|
+
const period = hour >= 12 ? 'PM' : 'AM';
|
|
31
|
+
const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
32
|
+
return `${displayHour} ${period}`;
|
|
33
|
+
}
|
|
34
|
+
function isToday(date) {
|
|
35
|
+
const today = new Date();
|
|
36
|
+
return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate();
|
|
37
|
+
}
|
|
38
|
+
function isSameDay(date1, date2) {
|
|
39
|
+
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
|
|
40
|
+
}
|
|
41
|
+
function getEventPosition(event, day, startHour, endHour) {
|
|
42
|
+
const eventStart = new Date(event.start);
|
|
43
|
+
const eventEnd = new Date(event.end);
|
|
44
|
+
if (!isSameDay(eventStart, day) && !isSameDay(eventEnd, day)) {
|
|
45
|
+
if (day < eventStart || day > eventEnd) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const dayStart = new Date(day);
|
|
50
|
+
dayStart.setHours(startHour, 0, 0, 0);
|
|
51
|
+
const dayEnd = new Date(day);
|
|
52
|
+
dayEnd.setHours(endHour, 0, 0, 0);
|
|
53
|
+
const visibleStart = eventStart < dayStart ? dayStart : eventStart;
|
|
54
|
+
const visibleEnd = eventEnd > dayEnd ? dayEnd : eventEnd;
|
|
55
|
+
if (visibleStart >= visibleEnd) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const totalMinutes = (endHour - startHour) * 60;
|
|
59
|
+
const startMinutes = (visibleStart.getHours() - startHour) * 60 + visibleStart.getMinutes();
|
|
60
|
+
const endMinutes = (visibleEnd.getHours() - startHour) * 60 + visibleEnd.getMinutes();
|
|
61
|
+
const top = startMinutes / totalMinutes * 100;
|
|
62
|
+
const height = (endMinutes - startMinutes) / totalMinutes * 100;
|
|
63
|
+
return {
|
|
64
|
+
height: Math.max(height, 2),
|
|
65
|
+
top
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function getCurrentTimePosition(startHour, endHour) {
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const currentHour = now.getHours();
|
|
71
|
+
const currentMinutes = now.getMinutes();
|
|
72
|
+
if (currentHour < startHour || currentHour >= endHour) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const totalMinutes = (endHour - startHour) * 60;
|
|
76
|
+
const minutesSinceStart = (currentHour - startHour) * 60 + currentMinutes;
|
|
77
|
+
return minutesSinceStart / totalMinutes * 100;
|
|
78
|
+
}
|
|
79
|
+
export const DayView = ({ date, endHour = 18, events, onEventClick, onSlotClick, startHour = 8 })=>{
|
|
80
|
+
const hours = useMemo(()=>Array.from({
|
|
81
|
+
length: endHour - startHour
|
|
82
|
+
}, (_, i)=>startHour + i), [
|
|
83
|
+
startHour,
|
|
84
|
+
endHour
|
|
85
|
+
]);
|
|
86
|
+
const currentTimePosition = useMemo(()=>{
|
|
87
|
+
if (!isToday(date)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return getCurrentTimePosition(startHour, endHour);
|
|
91
|
+
}, [
|
|
92
|
+
date,
|
|
93
|
+
startHour,
|
|
94
|
+
endHour
|
|
95
|
+
]);
|
|
96
|
+
const dayEvents = useMemo(()=>{
|
|
97
|
+
return events.filter((event)=>{
|
|
98
|
+
const eventStart = new Date(event.start);
|
|
99
|
+
const eventEnd = new Date(event.end);
|
|
100
|
+
return isSameDay(eventStart, date) || isSameDay(eventEnd, date) || eventStart < date && eventEnd > date;
|
|
101
|
+
});
|
|
102
|
+
}, [
|
|
103
|
+
events,
|
|
104
|
+
date
|
|
105
|
+
]);
|
|
106
|
+
const handleSlotClick = (hour)=>{
|
|
107
|
+
if (onSlotClick) {
|
|
108
|
+
const start = new Date(date);
|
|
109
|
+
start.setHours(hour, 0, 0, 0);
|
|
110
|
+
const end = new Date(start);
|
|
111
|
+
end.setHours(hour + 1, 0, 0, 0);
|
|
112
|
+
onSlotClick(start, end);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const formattedDate = `${DAY_NAMES[date.getDay()]}, ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
|
|
116
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
117
|
+
className: styles.dayGrid,
|
|
118
|
+
children: [
|
|
119
|
+
/*#__PURE__*/ _jsx("div", {
|
|
120
|
+
className: styles.timeGutter
|
|
121
|
+
}),
|
|
122
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
123
|
+
className: `${styles.dayHeader} ${isToday(date) ? styles.isToday : ''}`,
|
|
124
|
+
children: [
|
|
125
|
+
/*#__PURE__*/ _jsx("div", {
|
|
126
|
+
className: styles.dayName,
|
|
127
|
+
children: formattedDate
|
|
128
|
+
}),
|
|
129
|
+
isToday(date) && /*#__PURE__*/ _jsx("div", {
|
|
130
|
+
className: styles.dayNumber,
|
|
131
|
+
children: "Today"
|
|
132
|
+
})
|
|
133
|
+
]
|
|
134
|
+
}),
|
|
135
|
+
hours.map((hour, index)=>/*#__PURE__*/ _jsxs(React.Fragment, {
|
|
136
|
+
children: [
|
|
137
|
+
/*#__PURE__*/ _jsx("div", {
|
|
138
|
+
className: styles.timeSlot,
|
|
139
|
+
children: formatTime(hour)
|
|
140
|
+
}),
|
|
141
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
142
|
+
className: styles.dayColumn,
|
|
143
|
+
style: {
|
|
144
|
+
gridRow: 'span 1'
|
|
145
|
+
},
|
|
146
|
+
children: [
|
|
147
|
+
/*#__PURE__*/ _jsx("div", {
|
|
148
|
+
className: styles.hourSlot,
|
|
149
|
+
onClick: ()=>handleSlotClick(hour),
|
|
150
|
+
onKeyDown: (e)=>e.key === 'Enter' && handleSlotClick(hour),
|
|
151
|
+
role: "button",
|
|
152
|
+
tabIndex: 0
|
|
153
|
+
}),
|
|
154
|
+
/*#__PURE__*/ _jsx("div", {
|
|
155
|
+
className: styles.halfHourLine,
|
|
156
|
+
style: {
|
|
157
|
+
top: '50%'
|
|
158
|
+
}
|
|
159
|
+
}),
|
|
160
|
+
index === 0 && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
161
|
+
children: [
|
|
162
|
+
dayEvents.map((event)=>{
|
|
163
|
+
const position = getEventPosition(event, date, startHour, endHour);
|
|
164
|
+
if (!position) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return /*#__PURE__*/ _jsx(EventRenderer, {
|
|
168
|
+
event: event,
|
|
169
|
+
onClick: onEventClick,
|
|
170
|
+
style: {
|
|
171
|
+
height: `${position.height}%`,
|
|
172
|
+
top: `${position.top}%`
|
|
173
|
+
}
|
|
174
|
+
}, event.id);
|
|
175
|
+
}),
|
|
176
|
+
currentTimePosition !== null && /*#__PURE__*/ _jsx("div", {
|
|
177
|
+
className: styles.currentTimeIndicator,
|
|
178
|
+
style: {
|
|
179
|
+
top: `${currentTimePosition}%`
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
]
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
})
|
|
186
|
+
]
|
|
187
|
+
}, hour))
|
|
188
|
+
]
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
//# sourceMappingURL=DayView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/calendar/DayView.tsx"],"sourcesContent":["'use client'\n\nimport React, { useMemo } from 'react'\n\nimport type { CalendarEvent, DayViewProps } from './types.js'\n\nimport styles from './Calendar.module.css'\nimport { EventRenderer } from './EventRenderer.js'\n\nconst DAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']\nconst MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\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 isToday(date: Date): boolean {\n const today = new Date()\n return (\n date.getFullYear() === today.getFullYear() &&\n date.getMonth() === today.getMonth() &&\n date.getDate() === today.getDate()\n )\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 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 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 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 DayView: React.FC<DayViewProps> = ({\n date,\n endHour = 18,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n}) => {\n const hours = useMemo(\n () => Array.from({ length: endHour - startHour }, (_, i) => startHour + i),\n [startHour, endHour]\n )\n\n const currentTimePosition = useMemo(() => {\n if (!isToday(date)) {return null}\n return getCurrentTimePosition(startHour, endHour)\n }, [date, startHour, endHour])\n\n const dayEvents = useMemo((): CalendarEvent[] => {\n return events.filter((event: CalendarEvent) => {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n return (\n isSameDay(eventStart, date) ||\n isSameDay(eventEnd, date) ||\n (eventStart < date && eventEnd > date)\n )\n })\n }, [events, date])\n\n const handleSlotClick = (hour: number) => {\n if (onSlotClick) {\n const start = new Date(date)\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 const formattedDate = `${DAY_NAMES[date.getDay()]}, ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`\n\n return (\n <div className={styles.dayGrid}>\n {/* Header row */}\n <div className={styles.timeGutter} />\n <div className={`${styles.dayHeader} ${isToday(date) ? styles.isToday : ''}`}>\n <div className={styles.dayName}>{formattedDate}</div>\n {isToday(date) && <div className={styles.dayNumber}>Today</div>}\n </div>\n\n {/* Time slots */}\n {hours.map((hour, index) => (\n <React.Fragment key={hour}>\n <div className={styles.timeSlot}>{formatTime(hour)}</div>\n <div className={styles.dayColumn} style={{ gridRow: 'span 1' }}>\n <div\n className={styles.hourSlot}\n onClick={() => handleSlotClick(hour)}\n onKeyDown={(e) => e.key === 'Enter' && handleSlotClick(hour)}\n role=\"button\"\n tabIndex={0}\n />\n <div className={styles.halfHourLine} style={{ top: '50%' }} />\n {index === 0 && (\n <>\n {dayEvents.map((event: CalendarEvent) => {\n const position = getEventPosition(event, date, startHour, endHour)\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 {currentTimePosition !== null && (\n <div\n className={styles.currentTimeIndicator}\n style={{ top: `${currentTimePosition}%` }}\n />\n )}\n </>\n )}\n </div>\n </React.Fragment>\n ))}\n </div>\n )\n}\n"],"names":["React","useMemo","styles","EventRenderer","DAY_NAMES","MONTH_NAMES","formatTime","hour","period","displayHour","isToday","date","today","Date","getFullYear","getMonth","getDate","isSameDay","date1","date2","getEventPosition","event","day","startHour","endHour","eventStart","start","eventEnd","end","dayStart","setHours","dayEnd","visibleStart","visibleEnd","totalMinutes","startMinutes","getHours","getMinutes","endMinutes","top","height","Math","max","getCurrentTimePosition","now","currentHour","currentMinutes","minutesSinceStart","DayView","events","onEventClick","onSlotClick","hours","Array","from","length","_","i","currentTimePosition","dayEvents","filter","handleSlotClick","formattedDate","getDay","div","className","dayGrid","timeGutter","dayHeader","dayName","dayNumber","map","index","Fragment","timeSlot","dayColumn","style","gridRow","hourSlot","onClick","onKeyDown","e","key","role","tabIndex","halfHourLine","position","id","currentTimeIndicator"],"mappings":"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"}
|