Files
vitrify-me/apps/web/lib/pocketbase.ts
2025-08-26 23:36:24 +04:00

125 lines
5.1 KiB
TypeScript

import PocketBase from 'pocketbase';
import { BookingType, TimeSlot } 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;
},
};