freesched/public/script.js

245 lines
9.4 KiB
JavaScript
Raw Normal View History

2025-02-21 20:20:12 +00:00
// public/public.js (for index.html)
document.addEventListener('DOMContentLoaded', async () => {
const calendarDates = document.getElementById('calendarDates');
const monthYear = document.getElementById('monthYear');
const prevMonthBtn = document.getElementById('prevMonth');
const nextMonthBtn = document.getElementById('nextMonth');
const currentTimeElement = document.getElementById('currentTime');
const timeSlotsContainer = document.getElementById('timeSlots');
// Check if navigation buttons exist before adding event listeners
if (!prevMonthBtn || !nextMonthBtn) {
console.error('Navigation buttons (prevMonth or nextMonth) not found in the DOM');
return;
}
let currentDate = new Date();
let currentMonth = currentDate.getMonth();
let currentYear = currentDate.getFullYear();
let selectedDate = null; // Track selected date
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
// Fetch available dates from the API
async function fetchAvailability() {
try {
const response = await fetch('/api/availability');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const availability = await response.json();
return availability;
} catch (error) {
console.error('Error fetching availability:', error);
return {};
}
}
// Render the calendar with available dates highlighted and automatically select/display the current date
async function renderCalendar(month, year) {
// Fetch availability first to ensure data is ready
const availability = await fetchAvailability();
calendarDates.innerHTML = '';
monthYear.textContent = `${months[month]} ${year}`;
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const today = new Date();
// Start of today (midnight)
const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
// Create blank cells for days before the first day of the month
for (let i = 0; i < firstDay; i++) {
const blank = document.createElement('div');
blank.classList.add('date-item', 'p-2', 'rounded-pill', 'text-center', 'text-muted');
calendarDates.appendChild(blank);
}
// Populate the calendar with days
for (let i = 1; i <= daysInMonth; i++) {
const day = document.createElement('div');
day.classList.add('date-item', 'p-2', 'rounded-pill', 'text-center');
day.textContent = i;
const date = new Date(year, month, i);
const dateStr = date.toISOString().split('T')[0];
// Check if date is in the past
const isPastDate = date < startOfToday;
if (date.toDateString() === today.toDateString()) {
day.classList.add('current');
}
if (availability[dateStr]) {
day.classList.add('available');
}
if (isPastDate) {
day.classList.add('text-muted');
day.style.cursor = 'not-allowed';
} else {
day.addEventListener('click', () => selectDate(date, firstDay));
day.style.cursor = 'pointer';
}
calendarDates.appendChild(day);
}
// Only try to select today if we're in the current month and year
if (today.getMonth() === month && today.getFullYear() === year) {
await selectDate(today, firstDay); // Use await to ensure selection and display complete
}
}
// Handle date selection
async function selectDate(date, firstDay) {
// Check if the date is in the past
const today = new Date();
const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
if (date < startOfToday) {
console.warn('Attempted to select a past date');
return;
}
selectedDate = date;
const dateItems = document.querySelectorAll('#calendarDates .date-item');
dateItems.forEach(item => item.classList.remove('selected')); // Ensure only one date is selected
// Find the exact date item for the clicked or auto-selected date
const selectedDay = Array.from(dateItems).find(item => {
const dayText = item.textContent;
const itemDate = new Date(currentYear, currentMonth, parseInt(dayText));
return itemDate.toDateString() === date.toDateString();
});
if (selectedDay) selectedDay.classList.add('selected');
await updateTimeSlots(date); // Update time slots based on selected date, ensuring async completion
}
// Helper function to convert time string to minutes for sorting
function timeToMinutes(timeStr) {
console.log('Converting time:', timeStr);
const match = timeStr.match(/([0-9]+)(?::([0-9]+))?(am|pm)/i);
if (!match) {
console.warn('No match for time:', timeStr);
return 0;
}
let [_, hours, minutes, period] = match;
hours = parseInt(hours);
minutes = minutes ? parseInt(minutes) : 0;
period = period.toLowerCase();
if (period === 'pm' && hours !== 12) hours += 12;
if (period === 'am' && hours === 12) hours = 0;
const totalMinutes = hours * 60 + minutes;
console.log(`${timeStr} -> ${totalMinutes} minutes`);
return totalMinutes;
}
// Update time slots based on selected date
async function updateTimeSlots(date) {
const availability = await fetchAvailability();
const dateStr = date.toISOString().split('T')[0];
let times = [];
if (availability[dateStr]) {
// Ensure availability[dateStr] is a string or array before processing
if (typeof availability[dateStr] === 'string') {
times = availability[dateStr].split(',').map(t => t.trim());
} else if (Array.isArray(availability[dateStr])) {
times = availability[dateStr].map(t => t.trim());
} else {
console.warn(`Unexpected data format for date ${dateStr}:`, availability[dateStr]);
}
}
2025-02-25 05:27:29 +00:00
// Clear available time slots or message
2025-02-21 20:20:12 +00:00
timeSlotsContainer.innerHTML = '';
// Get current time in minutes for comparison
const now = new Date();
const currentMinutes = now.getHours() * 60 + now.getMinutes();
const isToday = date.toDateString() === now.toDateString();
// Filter out past times if it's today
if (isToday) {
times = times.filter(time => timeToMinutes(time) > currentMinutes);
}
// Sort remaining times chronologically
times.sort((a, b) => timeToMinutes(a) - timeToMinutes(b));
if (times.length === 0) {
// Display appropriate message based on context
const noAvailabilityMsg = document.createElement('p');
noAvailabilityMsg.classList.add('text-muted', 'text-center', 'fw-bold', 'fs-5', 'py-3');
let message;
if (isToday) {
if (currentMinutes > timeToMinutes('5:00pm')) {
message = 'No more availability today.';
} else {
message = 'No available time slots for today.';
}
} else {
message = 'No availability on selected date.';
}
noAvailabilityMsg.textContent = message;
timeSlotsContainer.appendChild(noAvailabilityMsg);
} else {
// Populate time slots dynamically
times.forEach(time => {
const button = document.createElement('button');
button.classList.add('time-slot', 'btn', 'rounded-pill', 'w-100', 'mb-2');
button.classList.add('btn-outline-primary', 'available');
button.textContent = time;
button.addEventListener('click', () => {
if (button.classList.contains('available') && selectedDate) {
const allTimeSlots = timeSlotsContainer.querySelectorAll('.time-slot');
allTimeSlots.forEach(s => s.classList.remove('selected'));
button.classList.add('selected');
}
});
timeSlotsContainer.appendChild(button);
});
}
}
// Navigate to previous month
prevMonthBtn.addEventListener('click', () => {
currentMonth--;
if (currentMonth < 0) {
currentMonth = 11;
currentYear--;
}
renderCalendar(currentMonth, currentYear);
});
// Navigate to next month
nextMonthBtn.addEventListener('click', () => {
currentMonth++;
if (currentMonth > 11) {
currentMonth = 0;
currentYear++;
}
renderCalendar(currentMonth, currentYear);
});
// Update current time in Eastern Time (US & Canada)
function updateCurrentTime() {
const options = {
timeZone: 'America/New_York',
hour: '2-digit',
minute: '2-digit',
hour12: true
};
const time = new Date().toLocaleTimeString('en-US', options);
currentTimeElement.textContent = time;
}
// Update time every second
setInterval(updateCurrentTime, 1000);
updateCurrentTime(); // Initial call
// Initial render (set to current date, automatically selecting and displaying it)
await renderCalendar(currentMonth, currentYear); // Use await to ensure the initial render completes
});