147 lines
5.9 KiB
TypeScript
147 lines
5.9 KiB
TypeScript
import PocketBase from 'pocketbase';
|
|
import { BookingType, TimeSlot, Booking } from '@/types/bookings';
|
|
|
|
// Initialize PocketBase client
|
|
export const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL || 'http://127.0.0.1:8090');
|
|
|
|
// Disable auto cancellation for SSR
|
|
pb.autoCancellation(false);
|
|
|
|
// API functions for booking system
|
|
export const bookingApi = {
|
|
// Get all active booking types
|
|
async getBookingTypes(): Promise<BookingType[]> {
|
|
try {
|
|
const records = await pb.collection('bookingTypes').getFullList<BookingType>({
|
|
sort: 'created',
|
|
expand: 'resources',
|
|
});
|
|
return records;
|
|
} catch (error) {
|
|
console.error('Error fetching booking types:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Get time slots for a specific booking type
|
|
async getTimeSlotsForBookingType(bookingTypeId: string): Promise<TimeSlot[]> {
|
|
try {
|
|
const today = new Date();
|
|
const oneMonthFromToday = new Date();
|
|
oneMonthFromToday.setMonth(today.getMonth() + 1);
|
|
|
|
const todayStr = today.toISOString().split('T')[0];
|
|
const oneMonthStr = oneMonthFromToday.toISOString().split('T')[0];
|
|
|
|
const records = await pb.collection('timeSlots').getFullList<TimeSlot>({
|
|
filter: `is_active = true && booking_types ~ "${bookingTypeId}" && start_time >= "${todayStr}" && end_time <= "${oneMonthStr}"`,
|
|
sort: 'start_time',
|
|
});
|
|
return records;
|
|
} catch (error) {
|
|
console.error('Error fetching time slots for booking type:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Generate available time slots grouped by date
|
|
generateAvailableTimeSlots(timeSlots: TimeSlot[]): { [date: string]: { start_time: string; end_time: string }[] } {
|
|
const today = new Date();
|
|
const oneMonthFromToday = new Date();
|
|
oneMonthFromToday.setMonth(today.getMonth() + 1);
|
|
|
|
// Step 1: Generate massive array of individual time slots with specific dates
|
|
const allTimeSlots: { start_time: string; end_time: string }[] = [];
|
|
|
|
timeSlots.forEach(slot => {
|
|
if (!slot.is_reccuring || !slot.recurrence_pattern) {
|
|
// Handle non-recurring slots - use as is
|
|
allTimeSlots.push({
|
|
start_time: slot.start_time,
|
|
end_time: slot.end_time
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Handle recurring slots - generate dates based on recurrence pattern
|
|
const pattern = slot.recurrence_pattern;
|
|
const endDate = pattern.end_date ? new Date(pattern.end_date) : oneMonthFromToday;
|
|
const finalEndDate = endDate < oneMonthFromToday ? endDate : oneMonthFromToday;
|
|
|
|
// Extract time from original start_time and end_time
|
|
const originalStartTime = new Date(slot.start_time);
|
|
const originalEndTime = new Date(slot.end_time);
|
|
const startHours = originalStartTime.getHours();
|
|
const startMinutes = originalStartTime.getMinutes();
|
|
const endHours = originalEndTime.getHours();
|
|
const endMinutes = originalEndTime.getMinutes();
|
|
|
|
let currentDate = new Date(today);
|
|
|
|
while (currentDate <= finalEndDate) {
|
|
const dayOfWeek = currentDate.getDay();
|
|
// Convert Sunday (0) to 6, Monday (1) to 0, etc. to match pattern format
|
|
const patternDay = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
|
|
if (pattern.days && pattern.days.includes(patternDay)) {
|
|
// Create specific datetime for this occurrence
|
|
const specificStartTime = new Date(currentDate);
|
|
specificStartTime.setHours(startHours, startMinutes, 0, 0);
|
|
|
|
const specificEndTime = new Date(currentDate);
|
|
specificEndTime.setHours(endHours, endMinutes, 0, 0);
|
|
|
|
allTimeSlots.push({
|
|
start_time: specificStartTime.toISOString(),
|
|
end_time: specificEndTime.toISOString()
|
|
});
|
|
}
|
|
|
|
currentDate.setDate(currentDate.getDate() + 1);
|
|
}
|
|
});
|
|
|
|
// Step 2: Group time slots by date (without time)
|
|
const availableSlotsByDate: { [date: string]: { start_time: string; end_time: string }[] } = {};
|
|
|
|
allTimeSlots.forEach(timeSlot => {
|
|
const dateStr = timeSlot.start_time.split('T')[0];
|
|
|
|
if (dateStr) {
|
|
if (!availableSlotsByDate[dateStr]) {
|
|
availableSlotsByDate[dateStr] = [];
|
|
}
|
|
availableSlotsByDate[dateStr].push(timeSlot);
|
|
}
|
|
});
|
|
|
|
// Step 3: Sort time slots within each date
|
|
Object.keys(availableSlotsByDate).forEach(date => {
|
|
availableSlotsByDate[date]?.sort((a, b) => a.start_time.localeCompare(b.start_time));
|
|
});
|
|
|
|
return availableSlotsByDate;
|
|
},
|
|
|
|
// Get all bookings for a specific date filtered by booking type IDs
|
|
async getBookingsForDate(
|
|
date: string,
|
|
bookingTypeIds: string[]
|
|
): Promise<Booking[]> {
|
|
try {
|
|
// Create filter for booking type IDs
|
|
const bookingTypeFilter = bookingTypeIds.map(id => `booking_type = "${id}"`).join(' || ');
|
|
|
|
const bookings = await pb.collection('bookings').getFullList<Booking>({
|
|
filter: `status = "confirmed" && start_time ~ "${date}" && (${bookingTypeFilter})`,
|
|
sort: 'start_time'
|
|
});
|
|
|
|
console.log(`Bookings for ${date}:`, bookings);
|
|
return bookings;
|
|
} catch (error) {
|
|
console.error('Error fetching bookings for date:', error);
|
|
return [];
|
|
}
|
|
},
|
|
}; |