316 lines
10 KiB
JavaScript
Executable File
316 lines
10 KiB
JavaScript
Executable File
// Private variable scoped to this file only
|
|
let todaysBookings = [];
|
|
let selectedDateBookings = [];
|
|
|
|
const input1 = document.getElementById("name");
|
|
const input2 = document.getElementById("email");
|
|
const input3 = document.getElementById("phone");
|
|
const dpicker = document.getElementById("ranged");
|
|
const dpicker2 = document.getElementById("currentDate");
|
|
|
|
export async function getData() {
|
|
try {
|
|
const res = await fetch('/api/book', { method: 'GET' });
|
|
if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
|
|
return await res.json();
|
|
} catch (err) {
|
|
console.error("Error fetching data:", err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
//formats timestamp to dd/mm/yyyy
|
|
function formatToDMY(isoString) {
|
|
const date = new Date(isoString);
|
|
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
const year = date.getUTCFullYear();
|
|
return `${day}/${month}/${year}`;
|
|
}
|
|
|
|
//formats timestamp to dd/MM/yyyy
|
|
function formatDateToDayMonthYear(dateStr) {
|
|
const parts = dateStr.split(/[-\/]/);
|
|
if (parts.length < 3) return "Invalid Date";
|
|
const year = parseInt(parts[0], 10);
|
|
const month = parseInt(parts[1], 10) - 1;
|
|
const day = parseInt(parts[2], 10);
|
|
|
|
if (isNaN(year) || isNaN(month) || isNaN(day)) return "Invalid Date";
|
|
|
|
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
return `${day} ${months[month]} ${year}`;
|
|
}
|
|
|
|
const todayStr = new Date().toISOString().split("T")[0]; // "YYYY-MM-DD"
|
|
|
|
function addDays(dateStr, days) {
|
|
const date = new Date(dateStr);
|
|
date.setDate(date.getDate() + days);
|
|
return date.toISOString().split("T")[0];
|
|
}
|
|
|
|
function formatToYMD(timestamp) {
|
|
return new Date(timestamp).toISOString().split("T")[0];
|
|
}
|
|
|
|
// Loads bookings for the given day offset and stores them in todaysBookings
|
|
export async function loadTodaysBookings(dayOffset = 0) {
|
|
const bookings = await getData();
|
|
const targetDate = addDays(todayStr, dayOffset);
|
|
|
|
// Filter bookings for the selected day by time
|
|
const unsortedBookings = bookings.filter(
|
|
b => formatToYMD(b.booking_date) === targetDate
|
|
);
|
|
// Sort by booking_time from earliest to latest
|
|
todaysBookings = unsortedBookings.sort((a, b) => {
|
|
const [hA, mA, sA] = a.booking_time.split(':').map(Number);
|
|
const [hB, mB, sB] = b.booking_time.split(':').map(Number);
|
|
|
|
const totalA = hA * 3600 + mA * 60 + sA;
|
|
const totalB = hB * 3600 + mB * 60 + sB;
|
|
|
|
return totalA - totalB;
|
|
});
|
|
|
|
return todaysBookings;
|
|
}
|
|
//loads date selected bookings
|
|
|
|
export async function loadSelectedDateBookings(date) {
|
|
const bookings = await getData();
|
|
|
|
// Filter bookings for the selected day by time
|
|
const unsortedBookings = bookings.filter(
|
|
b => formatToDMY(b.booking_date) === date
|
|
);
|
|
|
|
// Sort by booking_time from earliest to latest
|
|
selectedDateBookings = unsortedBookings.sort((a, b) => {
|
|
const [hA, mA, sA] = a.booking_time.split(':').map(Number);
|
|
const [hB, mB, sB] = b.booking_time.split(':').map(Number);
|
|
|
|
const totalA = hA * 3600 + mA * 60 + sA;
|
|
const totalB = hB * 3600 + mB * 60 + sB;
|
|
|
|
return totalA - totalB;
|
|
});
|
|
return selectedDateBookings;
|
|
}
|
|
|
|
|
|
// Checks if a given hour is booked in todaysBookings
|
|
export function isHourBooked(time) {
|
|
return selectedDateBookings.some(b => b.booking_time === time);
|
|
}
|
|
|
|
// Render the bookings for the dayOffset and update the DOM
|
|
export async function renderBookings(dayOffset = 0) {
|
|
await loadTodaysBookings(dayOffset);
|
|
const listContainer = document.getElementById("bookingsList");
|
|
const today = addDays(todayStr, dayOffset);
|
|
dpicker2.value = today;
|
|
|
|
dpicker2.addEventListener("change", () => {
|
|
const date1 = new Date(dpicker2.value);
|
|
const date2 = new Date(todayStr);
|
|
if (!isNaN(date1) && !isNaN(date2)) {
|
|
currentDayOffset = Math.round((date1 - date2) / (1000 * 60 * 60 * 24));
|
|
renderBookings(currentDayOffset);
|
|
}
|
|
});
|
|
|
|
// Clear the container first
|
|
listContainer.innerHTML = "";
|
|
|
|
if (todaysBookings.length === 0) {
|
|
// Display message if no bookings
|
|
const noBookingsDiv = document.createElement("div");
|
|
noBookingsDiv.className = "no-bookings";
|
|
noBookingsDiv.textContent = "No bookings found for this date.";
|
|
listContainer.appendChild(noBookingsDiv);
|
|
return; // Exit early since there are no bookings
|
|
}
|
|
|
|
// Render bookings if there are any
|
|
todaysBookings.forEach(booking => {
|
|
const bookingDiv = document.createElement("div");
|
|
bookingDiv.className = "booking-item";
|
|
|
|
|
|
//update booking
|
|
window.updateData = function(id) {
|
|
fetch('/api/book', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ accepted: true, id })
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) throw new Error('Failed to update booking');
|
|
return response.json();
|
|
})
|
|
.then(() => {
|
|
loadTodaysBookings(currentDayOffset);
|
|
renderBookings(currentDayOffset);
|
|
})
|
|
.catch(err => console.error('Error updating booking:', err));
|
|
};
|
|
|
|
//delete booking
|
|
window.deleteBooking = async function(id) {
|
|
try {
|
|
const res = await fetch(`/api/book/${id}`, { method: 'DELETE' });
|
|
if (!res.ok) throw new Error('Failed to delete booking');
|
|
|
|
await loadTodaysBookings(currentDayOffset);
|
|
renderBookings(currentDayOffset);
|
|
} catch (err) {
|
|
console.error('Error deleting booking:', err);
|
|
}
|
|
};
|
|
|
|
|
|
const formattedDate = booking.booking_date ? formatDateToDayMonthYear(booking.booking_date) : "No date";
|
|
const bookingHour = booking.booking_time ? booking.booking_time.split(':')[0] + ":" + booking.booking_time.split(':')[1] : "No hour";
|
|
|
|
let status = "Pending approval";
|
|
let buttonsStyle = "padding-left: 2vh; width: 20%;";
|
|
let buttonsStyle2 = "padding-left: 2vh; width: 20%;";
|
|
if (booking.accepted) {
|
|
status = "Accepted";
|
|
buttonsStyle += " display: none;";
|
|
} else {
|
|
buttonsStyle2 += " display: none;";
|
|
}
|
|
|
|
let buttonsStyle3 = "padding-left: 2vh; width: 20%;";
|
|
|
|
window.getInfo = function(button, showClosed) {
|
|
const bookingContainer = button.closest(".bookingContainer");
|
|
const openedDiv = bookingContainer.querySelector(".opened");
|
|
const closedDiv = bookingContainer.querySelector(".closed");
|
|
const btns2 = bookingContainer.querySelector(".btns2");
|
|
const btns3 = bookingContainer.querySelector(".btns3");
|
|
const openedInfo = bookingContainer.querySelector(".openedInfo");
|
|
const closedInfo = bookingContainer.querySelector(".closedInfo");
|
|
|
|
if (showClosed) {
|
|
openedDiv.style.display = "none";
|
|
btns2.style.display = "none";
|
|
openedInfo.style.display = "none";
|
|
closedDiv.style.display = "block";
|
|
btns3.style.display = "block";
|
|
closedInfo.style.display = "block";
|
|
|
|
} else {
|
|
openedDiv.style.display = "block";
|
|
btns2.style.display = "block";
|
|
closedDiv.style.display = "none";
|
|
btns3.style.display = "none";
|
|
openedInfo.style.display = "block";
|
|
closedInfo.style.display = "none";
|
|
}
|
|
};
|
|
|
|
bookingDiv.innerHTML = `
|
|
<div class="bookingContainer">
|
|
<div style="padding-left: 2vh; width: 20%;">
|
|
<h1 class="opened" style="font-size: 5vmin; display: block;">${bookingHour}</h1>
|
|
<button class="closed" style="display: none; margin-left: 2vh;" onclick="deleteBooking(${booking.id})">
|
|
<img style="width: 4vh; height: 4vh;" src="img/delete.png">
|
|
</button>
|
|
</div>
|
|
<div class="openedInfo" style="display: block;">
|
|
<div class="userinfo-container">
|
|
<h3 class="userinfo-target">
|
|
Booking for: <br>${booking.name}
|
|
<div class="userinfo-box">Email: ${booking.email || "No email"}<br>Phone: ${booking.phone || "No phone"}</div>
|
|
</h3>
|
|
|
|
<div class="bookingContent">
|
|
<h5>Status: ${status}</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="closedInfo" style="display: none;">
|
|
<div>
|
|
<h5>
|
|
Name: ${booking.name || "No name"}<br>
|
|
</h5>
|
|
<h5>
|
|
Email: ${booking.email || "No email"}<br>
|
|
</h5>
|
|
<h5>
|
|
Phone: ${booking.phone || "No phone"}<br>
|
|
</h5>
|
|
<h5>
|
|
Booking Date: ${formattedDate}, ${booking.booking_time}<br>
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="btns" style="${buttonsStyle}">
|
|
<div style="margin-right: 2.5vh;">
|
|
<button class="bookingBtns" onclick="updateData(${booking.id})">
|
|
<img style="width: 5vh; height: 5vh;" src="img/done.png">
|
|
</button>
|
|
</div>
|
|
<div style="margin-right: 2.5vh;">
|
|
<button class="bookingBtns" onclick="deleteBooking(${booking.id})">
|
|
<img style="width: 5vh; height: 5vh;" src="img/delete.png">
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="btns2" style="${buttonsStyle2}">
|
|
<div style="margin-right: 2vh;">
|
|
<button class="bookingBtns">
|
|
<img style="width: 4vh; height: 4vh;" src="img/info.png" onclick="getInfo(this, true)">
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="btns3" style="${buttonsStyle3} display: none;">
|
|
<div class="backBtn" style="margin-right: 2vh;">
|
|
<button class="bookingBtns">
|
|
<img style="width: 4vh; height: 4vh;" src="img/back.png" onclick="getInfo(this, false)">
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
listContainer.appendChild(bookingDiv);
|
|
});
|
|
}
|
|
|
|
window.renderBookings = renderBookings;
|
|
|
|
// Controls to move days in the admin panel
|
|
let currentDayOffset = 0;
|
|
|
|
document.getElementById("prevBtn").addEventListener("click", () => {
|
|
currentDayOffset--;
|
|
renderBookings(currentDayOffset);
|
|
});
|
|
|
|
document.getElementById("nextBtn").addEventListener("click", () => {
|
|
currentDayOffset++;
|
|
renderBookings(currentDayOffset);
|
|
});
|
|
|
|
// Initial render
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
renderBookings(currentDayOffset);
|
|
});
|
|
|
|
//loads selected date bookings
|
|
[input1, input2, input3, dpicker].forEach(input => {
|
|
input.addEventListener("input", () => loadSelectedDateBookings(dpicker.value));
|
|
input.addEventListener("change", () => loadSelectedDateBookings(dpicker.value));
|
|
});
|
|
|
|
setInterval(() => {
|
|
loadSelectedDateBookings(dpicker.value);
|
|
}, 200); |