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,69 @@
|
|
|
1
|
+
export interface CalendarEvent {
|
|
2
|
+
blockoutReason?: string;
|
|
3
|
+
customerId?: string;
|
|
4
|
+
customerName?: string;
|
|
5
|
+
end: Date;
|
|
6
|
+
guestId?: string;
|
|
7
|
+
guestName?: string;
|
|
8
|
+
id: string;
|
|
9
|
+
notes?: string;
|
|
10
|
+
serviceColor?: string;
|
|
11
|
+
serviceId?: string;
|
|
12
|
+
serviceName?: string;
|
|
13
|
+
start: Date;
|
|
14
|
+
status?: 'cancelled' | 'completed' | 'confirmed' | 'no-show' | 'scheduled';
|
|
15
|
+
teamMemberColor?: string;
|
|
16
|
+
teamMemberId?: string;
|
|
17
|
+
teamMemberName?: string;
|
|
18
|
+
title: string;
|
|
19
|
+
type: 'appointment' | 'blockout';
|
|
20
|
+
}
|
|
21
|
+
export interface TeamMember {
|
|
22
|
+
color: string;
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
takingAppointments: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface Service {
|
|
28
|
+
color: string;
|
|
29
|
+
duration: number;
|
|
30
|
+
id: string;
|
|
31
|
+
isActive: boolean;
|
|
32
|
+
name: string;
|
|
33
|
+
}
|
|
34
|
+
export type CalendarViewMode = 'day' | 'week';
|
|
35
|
+
export interface CalendarProps {
|
|
36
|
+
endHour?: number;
|
|
37
|
+
events: CalendarEvent[];
|
|
38
|
+
onDateChange?: (date: Date) => void;
|
|
39
|
+
onEventClick?: (event: CalendarEvent) => void;
|
|
40
|
+
onEventDrop?: (event: CalendarEvent, newStart: Date) => void;
|
|
41
|
+
onSlotClick?: (start: Date, end: Date, teamMemberId?: string) => void;
|
|
42
|
+
onViewModeChange?: (mode: CalendarViewMode) => void;
|
|
43
|
+
selectedDate?: Date;
|
|
44
|
+
slotDuration?: number;
|
|
45
|
+
startHour?: number;
|
|
46
|
+
teamMembers?: TeamMember[];
|
|
47
|
+
viewMode?: CalendarViewMode;
|
|
48
|
+
}
|
|
49
|
+
export interface WeekViewProps extends Omit<CalendarProps, 'onViewModeChange' | 'viewMode'> {
|
|
50
|
+
weekStart: Date;
|
|
51
|
+
}
|
|
52
|
+
export interface DayViewProps extends Omit<CalendarProps, 'onViewModeChange' | 'viewMode'> {
|
|
53
|
+
date: Date;
|
|
54
|
+
}
|
|
55
|
+
export interface EventRendererProps {
|
|
56
|
+
event: CalendarEvent;
|
|
57
|
+
onClick?: (event: CalendarEvent) => void;
|
|
58
|
+
style: React.CSSProperties;
|
|
59
|
+
}
|
|
60
|
+
export interface EventPopoverProps {
|
|
61
|
+
event: CalendarEvent;
|
|
62
|
+
onClose: () => void;
|
|
63
|
+
onDelete?: (event: CalendarEvent) => void;
|
|
64
|
+
onEdit?: (event: CalendarEvent) => void;
|
|
65
|
+
position: {
|
|
66
|
+
left: number;
|
|
67
|
+
top: number;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/calendar/types.ts"],"sourcesContent":["export interface CalendarEvent {\n blockoutReason?: string\n customerId?: string\n customerName?: string\n end: Date\n guestId?: string\n guestName?: string\n id: string\n notes?: string\n serviceColor?: string\n serviceId?: string\n serviceName?: string\n start: Date\n status?: 'cancelled' | 'completed' | 'confirmed' | 'no-show' | 'scheduled'\n teamMemberColor?: string\n teamMemberId?: string\n teamMemberName?: string\n title: string\n type: 'appointment' | 'blockout'\n}\n\nexport interface TeamMember {\n color: string\n id: string\n name: string\n takingAppointments: boolean\n}\n\nexport interface Service {\n color: string\n duration: number\n id: string\n isActive: boolean\n name: string\n}\n\nexport type CalendarViewMode = 'day' | 'week'\n\nexport interface CalendarProps {\n endHour?: number\n events: CalendarEvent[]\n onDateChange?: (date: Date) => void\n onEventClick?: (event: CalendarEvent) => void\n onEventDrop?: (event: CalendarEvent, newStart: Date) => void\n onSlotClick?: (start: Date, end: Date, teamMemberId?: string) => void\n onViewModeChange?: (mode: CalendarViewMode) => void\n selectedDate?: Date\n slotDuration?: number // in minutes\n startHour?: number\n teamMembers?: TeamMember[]\n viewMode?: CalendarViewMode\n}\n\nexport interface WeekViewProps extends Omit<CalendarProps, 'onViewModeChange' | 'viewMode'> {\n weekStart: Date\n}\n\nexport interface DayViewProps extends Omit<CalendarProps, 'onViewModeChange' | 'viewMode'> {\n date: Date\n}\n\nexport interface EventRendererProps {\n event: CalendarEvent\n onClick?: (event: CalendarEvent) => void\n style: React.CSSProperties\n}\n\nexport interface EventPopoverProps {\n event: CalendarEvent\n onClose: () => void\n onDelete?: (event: CalendarEvent) => void\n onEdit?: (event: CalendarEvent) => void\n position: { left: number; top: number }\n}\n"],"names":[],"mappings":"AAmEA,WAMC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/customEndpointHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nexport const customEndpointHandler: PayloadHandler = () => {\n return Response.json({ message: 'Hello from custom endpoint' })\n}\n"],"names":["customEndpointHandler","Response","json","message"],"mappings":"AAEA,OAAO,MAAMA,wBAAwC;IACnD,OAAOC,SAASC,IAAI,CAAC;QAAEC,SAAS;IAA6B;AAC/D,EAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PayloadHandler } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* GET /api/appointments/available-slots
|
|
4
|
+
*
|
|
5
|
+
* Query params:
|
|
6
|
+
* - date: YYYY-MM-DD (required)
|
|
7
|
+
* - serviceId: string (required)
|
|
8
|
+
* - teamMemberId: string (optional)
|
|
9
|
+
*
|
|
10
|
+
* Returns available time slots for booking
|
|
11
|
+
*/
|
|
12
|
+
export declare const getAvailableSlotsHandler: PayloadHandler;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/appointments/available-slots
|
|
3
|
+
*
|
|
4
|
+
* Query params:
|
|
5
|
+
* - date: YYYY-MM-DD (required)
|
|
6
|
+
* - serviceId: string (required)
|
|
7
|
+
* - teamMemberId: string (optional)
|
|
8
|
+
*
|
|
9
|
+
* Returns available time slots for booking
|
|
10
|
+
*/ export const getAvailableSlotsHandler = async (req)=>{
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
13
|
+
const dateParam = url.searchParams.get('date');
|
|
14
|
+
const serviceId = url.searchParams.get('serviceId');
|
|
15
|
+
const teamMemberId = url.searchParams.get('teamMemberId');
|
|
16
|
+
// Validate required params
|
|
17
|
+
if (!dateParam) {
|
|
18
|
+
return Response.json({
|
|
19
|
+
error: 'Missing required parameter: date (YYYY-MM-DD format)'
|
|
20
|
+
}, {
|
|
21
|
+
status: 400
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (!serviceId) {
|
|
25
|
+
return Response.json({
|
|
26
|
+
error: 'Missing required parameter: serviceId'
|
|
27
|
+
}, {
|
|
28
|
+
status: 400
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Parse date
|
|
32
|
+
const requestedDate = new Date(dateParam);
|
|
33
|
+
if (isNaN(requestedDate.getTime())) {
|
|
34
|
+
return Response.json({
|
|
35
|
+
error: 'Invalid date format. Use YYYY-MM-DD'
|
|
36
|
+
}, {
|
|
37
|
+
status: 400
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Get service details
|
|
41
|
+
const service = await req.payload.findByID({
|
|
42
|
+
id: serviceId,
|
|
43
|
+
collection: 'services'
|
|
44
|
+
});
|
|
45
|
+
if (!service) {
|
|
46
|
+
return Response.json({
|
|
47
|
+
error: 'Service not found'
|
|
48
|
+
}, {
|
|
49
|
+
status: 404
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!service.isActive) {
|
|
53
|
+
return Response.json({
|
|
54
|
+
error: 'Service is not available for booking'
|
|
55
|
+
}, {
|
|
56
|
+
status: 400
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Get opening times settings
|
|
60
|
+
const openingTimes = await req.payload.findGlobal({
|
|
61
|
+
slug: 'opening-times'
|
|
62
|
+
});
|
|
63
|
+
// Check booking window
|
|
64
|
+
const now = new Date();
|
|
65
|
+
const maxBookingDate = new Date(now);
|
|
66
|
+
maxBookingDate.setDate(maxBookingDate.getDate() + (openingTimes?.maxAdvanceBookingDays || 30));
|
|
67
|
+
const minBookingDate = new Date(now);
|
|
68
|
+
minBookingDate.setHours(minBookingDate.getHours() + (openingTimes?.minAdvanceBookingHours || 1));
|
|
69
|
+
if (requestedDate > maxBookingDate) {
|
|
70
|
+
return Response.json({
|
|
71
|
+
error: `Cannot book more than ${openingTimes?.maxAdvanceBookingDays || 30} days in advance`
|
|
72
|
+
}, {
|
|
73
|
+
status: 400
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (requestedDate < minBookingDate) {
|
|
77
|
+
return Response.json({
|
|
78
|
+
error: `Must book at least ${openingTimes?.minAdvanceBookingHours || 1} hours in advance`
|
|
79
|
+
}, {
|
|
80
|
+
status: 400
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Get day of week
|
|
84
|
+
const dayNames = [
|
|
85
|
+
'sunday',
|
|
86
|
+
'monday',
|
|
87
|
+
'tuesday',
|
|
88
|
+
'wednesday',
|
|
89
|
+
'thursday',
|
|
90
|
+
'friday',
|
|
91
|
+
'saturday'
|
|
92
|
+
];
|
|
93
|
+
const dayOfWeek = dayNames[requestedDate.getDay()];
|
|
94
|
+
// Find business hours for this day
|
|
95
|
+
const daySchedule = openingTimes?.schedule?.find((s)=>s.day === dayOfWeek);
|
|
96
|
+
if (!daySchedule || !daySchedule.isOpen) {
|
|
97
|
+
return Response.json({
|
|
98
|
+
date: dateParam,
|
|
99
|
+
message: 'Business is closed on this day',
|
|
100
|
+
serviceId,
|
|
101
|
+
slots: [],
|
|
102
|
+
teamMemberId: teamMemberId || undefined
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Calculate slot duration (service duration + buffer)
|
|
106
|
+
const slotDuration = service.duration || 30;
|
|
107
|
+
const bufferBefore = service.bufferBefore || 0;
|
|
108
|
+
const bufferAfter = service.bufferAfter || 0;
|
|
109
|
+
const totalSlotTime = slotDuration + bufferBefore + bufferAfter;
|
|
110
|
+
// Parse business hours
|
|
111
|
+
const [openHour, openMin] = (daySchedule.openTime || '09:00').split(':').map(Number);
|
|
112
|
+
const [closeHour, closeMin] = (daySchedule.closeTime || '17:00').split(':').map(Number);
|
|
113
|
+
// Parse break times if set
|
|
114
|
+
let breakStart = null;
|
|
115
|
+
let breakEnd = null;
|
|
116
|
+
if (daySchedule.breakStart && daySchedule.breakEnd) {
|
|
117
|
+
const [breakStartHour, breakStartMin] = daySchedule.breakStart.split(':').map(Number);
|
|
118
|
+
const [breakEndHour, breakEndMin] = daySchedule.breakEnd.split(':').map(Number);
|
|
119
|
+
breakStart = breakStartHour * 60 + breakStartMin;
|
|
120
|
+
breakEnd = breakEndHour * 60 + breakEndMin;
|
|
121
|
+
}
|
|
122
|
+
// Get team member availability if specified
|
|
123
|
+
let teamMemberSchedule = null;
|
|
124
|
+
if (teamMemberId) {
|
|
125
|
+
const teamMember = await req.payload.findByID({
|
|
126
|
+
id: teamMemberId,
|
|
127
|
+
collection: 'team-members'
|
|
128
|
+
});
|
|
129
|
+
if (!teamMember) {
|
|
130
|
+
return Response.json({
|
|
131
|
+
error: 'Team member not found'
|
|
132
|
+
}, {
|
|
133
|
+
status: 404
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!teamMember.takingAppointments) {
|
|
137
|
+
return Response.json({
|
|
138
|
+
error: 'Team member is not taking appointments'
|
|
139
|
+
}, {
|
|
140
|
+
status: 400
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Check if team member provides this service
|
|
144
|
+
const teamMemberServices = teamMember.services || [];
|
|
145
|
+
const serviceIds = teamMemberServices.map((s)=>typeof s === 'object' ? s.id : s);
|
|
146
|
+
if (serviceIds.length > 0 && !serviceIds.includes(serviceId)) {
|
|
147
|
+
return Response.json({
|
|
148
|
+
error: 'Team member does not provide this service'
|
|
149
|
+
}, {
|
|
150
|
+
status: 400
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Get team member's schedule for this day
|
|
154
|
+
teamMemberSchedule = teamMember.availability?.find((a)=>a.day === dayOfWeek);
|
|
155
|
+
if (teamMemberSchedule && !teamMemberSchedule.isAvailable) {
|
|
156
|
+
return Response.json({
|
|
157
|
+
date: dateParam,
|
|
158
|
+
message: 'Team member is not available on this day',
|
|
159
|
+
serviceId,
|
|
160
|
+
slots: [],
|
|
161
|
+
teamMemberId
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Get existing appointments for this day
|
|
166
|
+
const dayStart = new Date(requestedDate);
|
|
167
|
+
dayStart.setHours(0, 0, 0, 0);
|
|
168
|
+
const dayEnd = new Date(requestedDate);
|
|
169
|
+
dayEnd.setHours(23, 59, 59, 999);
|
|
170
|
+
const existingAppointments = await req.payload.find({
|
|
171
|
+
collection: 'appointments',
|
|
172
|
+
limit: 100,
|
|
173
|
+
where: {
|
|
174
|
+
and: [
|
|
175
|
+
{
|
|
176
|
+
startDateTime: {
|
|
177
|
+
greater_than_equal: dayStart.toISOString()
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
startDateTime: {
|
|
182
|
+
less_than: dayEnd.toISOString()
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
status: {
|
|
187
|
+
not_in: [
|
|
188
|
+
'cancelled'
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
...teamMemberId ? [
|
|
193
|
+
{
|
|
194
|
+
teamMember: {
|
|
195
|
+
equals: teamMemberId
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
] : []
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
// Convert appointments to blocked time ranges (in minutes from midnight)
|
|
203
|
+
const blockedRanges = [];
|
|
204
|
+
for (const apt of existingAppointments.docs){
|
|
205
|
+
const aptStart = new Date(apt.startDateTime);
|
|
206
|
+
const aptEnd = apt.endDateTime ? new Date(apt.endDateTime) : new Date(aptStart.getTime() + 30 * 60000);
|
|
207
|
+
const startMinutes = aptStart.getHours() * 60 + aptStart.getMinutes();
|
|
208
|
+
const endMinutes = aptEnd.getHours() * 60 + aptEnd.getMinutes();
|
|
209
|
+
blockedRanges.push({
|
|
210
|
+
end: endMinutes + bufferAfter,
|
|
211
|
+
start: startMinutes - bufferBefore
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Determine effective working hours
|
|
215
|
+
let effectiveOpenMinutes = openHour * 60 + openMin;
|
|
216
|
+
let effectiveCloseMinutes = closeHour * 60 + closeMin;
|
|
217
|
+
// Apply team member schedule if available
|
|
218
|
+
if (teamMemberSchedule) {
|
|
219
|
+
const [tmOpenHour, tmOpenMin] = (teamMemberSchedule.startTime || '09:00').split(':').map(Number);
|
|
220
|
+
const [tmCloseHour, tmCloseMin] = (teamMemberSchedule.endTime || '17:00').split(':').map(Number);
|
|
221
|
+
effectiveOpenMinutes = Math.max(effectiveOpenMinutes, tmOpenHour * 60 + tmOpenMin);
|
|
222
|
+
effectiveCloseMinutes = Math.min(effectiveCloseMinutes, tmCloseHour * 60 + tmCloseMin);
|
|
223
|
+
// Apply team member break
|
|
224
|
+
if (teamMemberSchedule.breakStart && teamMemberSchedule.breakEnd) {
|
|
225
|
+
const [tmBreakStartHour, tmBreakStartMin] = teamMemberSchedule.breakStart.split(':').map(Number);
|
|
226
|
+
const [tmBreakEndHour, tmBreakEndMin] = teamMemberSchedule.breakEnd.split(':').map(Number);
|
|
227
|
+
// Use the more restrictive break
|
|
228
|
+
if (breakStart === null) {
|
|
229
|
+
breakStart = tmBreakStartHour * 60 + tmBreakStartMin;
|
|
230
|
+
breakEnd = tmBreakEndHour * 60 + tmBreakEndMin;
|
|
231
|
+
} else {
|
|
232
|
+
breakStart = Math.min(breakStart, tmBreakStartHour * 60 + tmBreakStartMin);
|
|
233
|
+
breakEnd = Math.max(breakEnd, tmBreakEndHour * 60 + tmBreakEndMin);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Generate slots
|
|
238
|
+
const slots = [];
|
|
239
|
+
const slotInterval = openingTimes?.slotDuration || 30;
|
|
240
|
+
for(let minutes = effectiveOpenMinutes; minutes + totalSlotTime <= effectiveCloseMinutes; minutes += slotInterval){
|
|
241
|
+
const slotEnd = minutes + totalSlotTime;
|
|
242
|
+
// Check if slot is during break
|
|
243
|
+
if (breakStart !== null && breakEnd !== null) {
|
|
244
|
+
if (minutes < breakEnd && slotEnd > breakStart) {
|
|
245
|
+
continue; // Skip slots that overlap with break
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Check if slot conflicts with existing appointments
|
|
249
|
+
let isBlocked = false;
|
|
250
|
+
for (const blocked of blockedRanges){
|
|
251
|
+
if (minutes < blocked.end && slotEnd > blocked.start) {
|
|
252
|
+
isBlocked = true;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Create slot datetime
|
|
257
|
+
const slotStart = new Date(requestedDate);
|
|
258
|
+
slotStart.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0);
|
|
259
|
+
const slotEndDate = new Date(requestedDate);
|
|
260
|
+
slotEndDate.setHours(Math.floor(slotEnd / 60), slotEnd % 60, 0, 0);
|
|
261
|
+
// Check if slot is in the past
|
|
262
|
+
if (slotStart < now) {
|
|
263
|
+
isBlocked = true;
|
|
264
|
+
}
|
|
265
|
+
slots.push({
|
|
266
|
+
available: !isBlocked,
|
|
267
|
+
end: slotEndDate.toISOString(),
|
|
268
|
+
start: slotStart.toISOString()
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const response = {
|
|
272
|
+
date: dateParam,
|
|
273
|
+
serviceId,
|
|
274
|
+
slots,
|
|
275
|
+
teamMemberId: teamMemberId || undefined
|
|
276
|
+
};
|
|
277
|
+
return Response.json(response);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
req.payload.logger.error({
|
|
280
|
+
error,
|
|
281
|
+
msg: 'Error getting available slots'
|
|
282
|
+
});
|
|
283
|
+
return Response.json({
|
|
284
|
+
error: 'Internal server error'
|
|
285
|
+
}, {
|
|
286
|
+
status: 500
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
//# sourceMappingURL=getAvailableSlots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/getAvailableSlots.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\ninterface TimeSlot {\n available: boolean\n end: string // ISO datetime\n start: string // ISO datetime\n}\n\ninterface AvailableSlotsResponse {\n date: string\n serviceId: string\n slots: TimeSlot[]\n teamMemberId?: string\n}\n\n/**\n * GET /api/appointments/available-slots\n *\n * Query params:\n * - date: YYYY-MM-DD (required)\n * - serviceId: string (required)\n * - teamMemberId: string (optional)\n *\n * Returns available time slots for booking\n */\nexport const getAvailableSlotsHandler: PayloadHandler = async (req) => {\n try {\n const url = new URL(req.url || '', 'http://localhost')\n const dateParam = url.searchParams.get('date')\n const serviceId = url.searchParams.get('serviceId')\n const teamMemberId = url.searchParams.get('teamMemberId')\n\n // Validate required params\n if (!dateParam) {\n return Response.json(\n { error: 'Missing required parameter: date (YYYY-MM-DD format)' },\n { status: 400 }\n )\n }\n\n if (!serviceId) {\n return Response.json(\n { error: 'Missing required parameter: serviceId' },\n { status: 400 }\n )\n }\n\n // Parse date\n const requestedDate = new Date(dateParam)\n if (isNaN(requestedDate.getTime())) {\n return Response.json(\n { error: 'Invalid date format. Use YYYY-MM-DD' },\n { status: 400 }\n )\n }\n\n // Get service details\n const service = await req.payload.findByID({\n id: serviceId,\n collection: 'services',\n })\n\n if (!service) {\n return Response.json(\n { error: 'Service not found' },\n { status: 404 }\n )\n }\n\n if (!service.isActive) {\n return Response.json(\n { error: 'Service is not available for booking' },\n { status: 400 }\n )\n }\n\n // Get opening times settings\n const openingTimes = await req.payload.findGlobal({\n slug: 'opening-times',\n })\n\n // Check booking window\n const now = new Date()\n const maxBookingDate = new Date(now)\n maxBookingDate.setDate(maxBookingDate.getDate() + (openingTimes?.maxAdvanceBookingDays || 30))\n\n const minBookingDate = new Date(now)\n minBookingDate.setHours(minBookingDate.getHours() + (openingTimes?.minAdvanceBookingHours || 1))\n\n if (requestedDate > maxBookingDate) {\n return Response.json(\n { error: `Cannot book more than ${openingTimes?.maxAdvanceBookingDays || 30} days in advance` },\n { status: 400 }\n )\n }\n\n if (requestedDate < minBookingDate) {\n return Response.json(\n { error: `Must book at least ${openingTimes?.minAdvanceBookingHours || 1} hours in advance` },\n { status: 400 }\n )\n }\n\n // Get day of week\n const dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']\n const dayOfWeek = dayNames[requestedDate.getDay()]\n\n // Find business hours for this day\n const daySchedule = openingTimes?.schedule?.find(\n (s: any) => s.day === dayOfWeek\n )\n\n if (!daySchedule || !daySchedule.isOpen) {\n return Response.json({\n date: dateParam,\n message: 'Business is closed on this day',\n serviceId,\n slots: [],\n teamMemberId: teamMemberId || undefined,\n })\n }\n\n // Calculate slot duration (service duration + buffer)\n const slotDuration = service.duration || 30\n const bufferBefore = service.bufferBefore || 0\n const bufferAfter = service.bufferAfter || 0\n const totalSlotTime = slotDuration + bufferBefore + bufferAfter\n\n // Parse business hours\n const [openHour, openMin] = (daySchedule.openTime || '09:00').split(':').map(Number)\n const [closeHour, closeMin] = (daySchedule.closeTime || '17:00').split(':').map(Number)\n\n // Parse break times if set\n let breakStart: null | number = null\n let breakEnd: null | number = null\n if (daySchedule.breakStart && daySchedule.breakEnd) {\n const [breakStartHour, breakStartMin] = daySchedule.breakStart.split(':').map(Number)\n const [breakEndHour, breakEndMin] = daySchedule.breakEnd.split(':').map(Number)\n breakStart = breakStartHour * 60 + breakStartMin\n breakEnd = breakEndHour * 60 + breakEndMin\n }\n\n // Get team member availability if specified\n let teamMemberSchedule: any = null\n if (teamMemberId) {\n const teamMember = await req.payload.findByID({\n id: teamMemberId,\n collection: 'team-members',\n })\n\n if (!teamMember) {\n return Response.json(\n { error: 'Team member not found' },\n { status: 404 }\n )\n }\n\n if (!teamMember.takingAppointments) {\n return Response.json(\n { error: 'Team member is not taking appointments' },\n { status: 400 }\n )\n }\n\n // Check if team member provides this service\n const teamMemberServices = teamMember.services || []\n const serviceIds = teamMemberServices.map((s: any) =>\n typeof s === 'object' ? s.id : s\n )\n\n if (serviceIds.length > 0 && !serviceIds.includes(serviceId)) {\n return Response.json(\n { error: 'Team member does not provide this service' },\n { status: 400 }\n )\n }\n\n // Get team member's schedule for this day\n teamMemberSchedule = teamMember.availability?.find(\n (a: any) => a.day === dayOfWeek\n )\n\n if (teamMemberSchedule && !teamMemberSchedule.isAvailable) {\n return Response.json({\n date: dateParam,\n message: 'Team member is not available on this day',\n serviceId,\n slots: [],\n teamMemberId,\n })\n }\n }\n\n // Get existing appointments for this day\n const dayStart = new Date(requestedDate)\n dayStart.setHours(0, 0, 0, 0)\n const dayEnd = new Date(requestedDate)\n dayEnd.setHours(23, 59, 59, 999)\n\n const existingAppointments = await req.payload.find({\n collection: 'appointments',\n limit: 100,\n where: {\n and: [\n {\n startDateTime: {\n greater_than_equal: dayStart.toISOString(),\n },\n },\n {\n startDateTime: {\n less_than: dayEnd.toISOString(),\n },\n },\n {\n status: {\n not_in: ['cancelled'],\n },\n },\n ...(teamMemberId\n ? [{ teamMember: { equals: teamMemberId } }]\n : []),\n ],\n },\n })\n\n // Convert appointments to blocked time ranges (in minutes from midnight)\n const blockedRanges: Array<{ end: number; start: number }> = []\n for (const apt of existingAppointments.docs) {\n const aptStart = new Date(apt.startDateTime)\n const aptEnd = apt.endDateTime ? new Date(apt.endDateTime) : new Date(aptStart.getTime() + 30 * 60000)\n\n const startMinutes = aptStart.getHours() * 60 + aptStart.getMinutes()\n const endMinutes = aptEnd.getHours() * 60 + aptEnd.getMinutes()\n\n blockedRanges.push({\n end: endMinutes + bufferAfter,\n start: startMinutes - bufferBefore,\n })\n }\n\n // Determine effective working hours\n let effectiveOpenMinutes = openHour * 60 + openMin\n let effectiveCloseMinutes = closeHour * 60 + closeMin\n\n // Apply team member schedule if available\n if (teamMemberSchedule) {\n const [tmOpenHour, tmOpenMin] = (teamMemberSchedule.startTime || '09:00').split(':').map(Number)\n const [tmCloseHour, tmCloseMin] = (teamMemberSchedule.endTime || '17:00').split(':').map(Number)\n\n effectiveOpenMinutes = Math.max(effectiveOpenMinutes, tmOpenHour * 60 + tmOpenMin)\n effectiveCloseMinutes = Math.min(effectiveCloseMinutes, tmCloseHour * 60 + tmCloseMin)\n\n // Apply team member break\n if (teamMemberSchedule.breakStart && teamMemberSchedule.breakEnd) {\n const [tmBreakStartHour, tmBreakStartMin] = teamMemberSchedule.breakStart.split(':').map(Number)\n const [tmBreakEndHour, tmBreakEndMin] = teamMemberSchedule.breakEnd.split(':').map(Number)\n\n // Use the more restrictive break\n if (breakStart === null) {\n breakStart = tmBreakStartHour * 60 + tmBreakStartMin\n breakEnd = tmBreakEndHour * 60 + tmBreakEndMin\n } else {\n breakStart = Math.min(breakStart, tmBreakStartHour * 60 + tmBreakStartMin)\n breakEnd = Math.max(breakEnd!, tmBreakEndHour * 60 + tmBreakEndMin)\n }\n }\n }\n\n // Generate slots\n const slots: TimeSlot[] = []\n const slotInterval = openingTimes?.slotDuration || 30\n\n for (let minutes = effectiveOpenMinutes; minutes + totalSlotTime <= effectiveCloseMinutes; minutes += slotInterval) {\n const slotEnd = minutes + totalSlotTime\n\n // Check if slot is during break\n if (breakStart !== null && breakEnd !== null) {\n if (minutes < breakEnd && slotEnd > breakStart) {\n continue // Skip slots that overlap with break\n }\n }\n\n // Check if slot conflicts with existing appointments\n let isBlocked = false\n for (const blocked of blockedRanges) {\n if (minutes < blocked.end && slotEnd > blocked.start) {\n isBlocked = true\n break\n }\n }\n\n // Create slot datetime\n const slotStart = new Date(requestedDate)\n slotStart.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0)\n\n const slotEndDate = new Date(requestedDate)\n slotEndDate.setHours(Math.floor(slotEnd / 60), slotEnd % 60, 0, 0)\n\n // Check if slot is in the past\n if (slotStart < now) {\n isBlocked = true\n }\n\n slots.push({\n available: !isBlocked,\n end: slotEndDate.toISOString(),\n start: slotStart.toISOString(),\n })\n }\n\n const response: AvailableSlotsResponse = {\n date: dateParam,\n serviceId,\n slots,\n teamMemberId: teamMemberId || undefined,\n }\n\n return Response.json(response)\n } catch (error) {\n req.payload.logger.error({\n error,\n msg: 'Error getting available slots',\n })\n\n return Response.json(\n { error: 'Internal server error' },\n { status: 500 }\n )\n }\n}\n"],"names":["getAvailableSlotsHandler","req","url","URL","dateParam","searchParams","get","serviceId","teamMemberId","Response","json","error","status","requestedDate","Date","isNaN","getTime","service","payload","findByID","id","collection","isActive","openingTimes","findGlobal","slug","now","maxBookingDate","setDate","getDate","maxAdvanceBookingDays","minBookingDate","setHours","getHours","minAdvanceBookingHours","dayNames","dayOfWeek","getDay","daySchedule","schedule","find","s","day","isOpen","date","message","slots","undefined","slotDuration","duration","bufferBefore","bufferAfter","totalSlotTime","openHour","openMin","openTime","split","map","Number","closeHour","closeMin","closeTime","breakStart","breakEnd","breakStartHour","breakStartMin","breakEndHour","breakEndMin","teamMemberSchedule","teamMember","takingAppointments","teamMemberServices","services","serviceIds","length","includes","availability","a","isAvailable","dayStart","dayEnd","existingAppointments","limit","where","and","startDateTime","greater_than_equal","toISOString","less_than","not_in","equals","blockedRanges","apt","docs","aptStart","aptEnd","endDateTime","startMinutes","getMinutes","endMinutes","push","end","start","effectiveOpenMinutes","effectiveCloseMinutes","tmOpenHour","tmOpenMin","startTime","tmCloseHour","tmCloseMin","endTime","Math","max","min","tmBreakStartHour","tmBreakStartMin","tmBreakEndHour","tmBreakEndMin","slotInterval","minutes","slotEnd","isBlocked","blocked","slotStart","floor","slotEndDate","available","response","logger","msg"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
|
|
2
|
+
export { CalendarContainer, DayView, EventPopover, EventRenderer, WeekView, } from '../components/calendar/index.js';
|
|
3
|
+
export type { CalendarEvent, CalendarProps, CalendarViewMode, DayViewProps, EventPopoverProps, EventRendererProps, Service, TeamMember, WeekViewProps, } from '../components/calendar/types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\nexport {\n CalendarContainer,\n DayView,\n EventPopover,\n EventRenderer,\n WeekView,\n} from '../components/calendar/index.js'\nexport type {\n CalendarEvent,\n CalendarProps,\n CalendarViewMode,\n DayViewProps,\n EventPopoverProps,\n EventRendererProps,\n Service,\n TeamMember,\n WeekViewProps,\n} from '../components/calendar/types.js'\n"],"names":["BeforeDashboardClient","CalendarContainer","DayView","EventPopover","EventRenderer","WeekView"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC;AAC9E,SACEC,iBAAiB,EACjBC,OAAO,EACPC,YAAY,EACZC,aAAa,EACbC,QAAQ,QACH,kCAAiC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js'\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
|