api/
ved/
admin/
modules/
gyar/
admin/
cron/
weuweb01/ved/admin/js/admin.js8 lines
// Classes
import { vedAPI as api, Cookies, getAuthHeader, httpErrorMessage } from "./modules.js";
class Calendar {
static weekdays = ["Mån", "Tis", "Ons", "Tors", "Fre", "Lör", "Sön"];
static months = ["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"];
#shouldButtonBeDisabledCallback
#monthChangedCallback
constructor(element) {
let header = element.querySelector(".calendar-header");
if (!header) throw new Error("Calendar needs a calendar-header.");
let buttons = header.querySelectorAll("button");
if (buttons.length != 2) throw new Error("Calendar header needs two buttons.");
this.monthHeader = header.querySelector("p");
if (!this.monthHeader) throw new Error("Calendar header is missing a paragraph element.");
this.body = element.querySelector(".calendar-body");
if (!this.body) throw new Error("Calendar needs a calendar-body.");
this.previousButton = buttons[0];
this.nextButton = buttons[1];
this.dateButtons = new Map()
this.markedButton = undefined;
this.markedDate = -1;
this.date = new Date();
this.date.setUTCHours(0, 0, 0, 0);
this.dateTimestamp = this.date.getTime();
this.date.setUTCDate(1);
this.monthTimestamp = this.date.getTime();
this.previousButton.addEventListener("click", () => this.changeMonth(-1));
this.nextButton.addEventListener("click", () => this.changeMonth(1));
this.monthChangedCallback = undefined;
this.dateSelectedCallback = undefined;
Calendar.weekdays.forEach(day => CreateElement("p", day, this.body));
this.updateAppearance();
}
async changeMonth(delta) {
let old = this.date.getTime();
this.date.setUTCMonth(this.date.getUTCMonth() + delta);
if (this.date.getTime() < this.monthTimestamp) {
this.date.setTime(old);
return;
}
if (this.monthChangedCallback) {
let result = await this.monthChangedCallback(this.date.getUTCFullYear(), this.date.getUTCMonth());
if (!result) {
this.date.setTime(old);
return;
}
}
this.updateAppearance();
}
async updateAppearance() {
this.dateButtons.forEach((date, button) => button.remove());
this.dateButtons.clear();
let date = new Date(this.date);
date.setUTCHours(0, 0, 0, 0);
date.setUTCDate(1);
let weekdayOffset = ConvertWeekday(date.getUTCDay());
let timestamp = date.getTime();
this.monthHeader.innerText = Calendar.months[date.getUTCMonth()] + " " + date.getUTCFullYear();
let i = 1;
while (true) {
date.setTime(timestamp + (i - weekdayOffset - 1) * 86400000);
let dateValue = date.getUTCDate();
if (i >= 28 & dateValue <= 7 & i % 7 == 1) break;
let button = CreateElement("button", dateValue, this.body);
if ((i <= 7 && dateValue > 15) || (i > 28 && dateValue <= 15)) button.classList.add("unfocused");
let isClickable = true;
if (this.shouldButtonBeDisabledCallback) {
let retValue = this.shouldButtonBeDisabledCallback(date);
if (retValue != undefined) {
let [disabled, unfocused] = retValue;
if (disabled) {
button.classList.add("disabled");
button.setAttribute("tabindex", -1);
isClickable = false;
}
if (unfocused)
button.classList.add("unfocused");
}
}
this.dateButtons.set(button, date.getTime());
if (isClickable) {
let mark = async () => {
let newDate = this.dateButtons.get(button);
if (this.dateSelectedCallback) {
let result = await this.dateSelectedCallback(newDate);
if (!result)
return;
}
if (this.markedButton) this.markedButton.classList.remove("selected");
button.classList.add("selected");
this.markedButton = button;
this.markedDate = newDate;
};
button.addEventListener("click", mark);
if (this.markedDate == date.getTime()) await mark();
}
i++;
}
this.body.style["grid-template-columns"] = "1fr 1fr 1fr 1fr 1fr 1fr 1fr";
this.body.style["grid-template-rows"] = "1fr" + " 2fr".repeat(i / 7);
}
get shouldButtonBeDisabledCallback() {
return this.#shouldButtonBeDisabledCallback;
}
set shouldButtonBeDisabledCallback(newValue) {
this.#shouldButtonBeDisabledCallback = newValue;
this.updateAppearance();
}
get monthChangedCallback() {
return this.#monthChangedCallback;
}
set monthChangedCallback(newValue) {
this.#monthChangedCallback = newValue;
this.changeMonth(0);
}
}
class BookingsGrid {
constructor(element, timeBar) {
this.element = element;
this.timeBar = timeBar;
this.todayMs = undefined;
let today = new Date();
today.setHours(0, 0, 0, 0);
this.todayMs = today.getTime();
//setInterval(() => this.updateTimeBar(), 1000);
}
changeConfig(openingMinute, closingMinute) {
while (this.element.firstChild) {
this.element.removeChild(this.element.lastChild);
}
this.bookings = [];
this.openingMinute = openingMinute;
this.closingMinute = closingMinute;
let deltaMinutes = (closingMinute - openingMinute);
let size = (deltaMinutes > 360 ? 2 : 1);
this.element.style["grid-template-columns"] = "4rem" + " 1fr".repeat(seatCount);
this.element.style["grid-template-rows"] = "1fr ".repeat(deltaMinutes / bookingInterval);
for (let i = 0; i < deltaMinutes / bookingInterval; i += size) {
let time = i * bookingInterval + openingMinute;
let minutes = time % 60;
let hours = (time - minutes) / 60;
let text = CreateElement("p", `${DoubleDigit(hours)}:${DoubleDigit(minutes)}`, this.element);
text.style["grid-row"] = `${i + 1} / span ${size}`;
}
//this.updateTimeBar();
}
addBooking(minute, clickCallback) {
let gridRow = Math.floor((minute - this.openingMinute) / bookingInterval) + 1;
let size = Math.floor(bookingLength / bookingInterval);
let column;
for (column = 1; column <= 10; column++) {
var fits = true;
this.bookings.forEach(booking => {
if (booking.start + bookingLength > minute || booking.start - bookingLength < minute) fits = false;
});
if (fits) break;
}
let button = CreateElement("button", "", this.element);
button.style["grid-row"] = `${gridRow} / span ${size}`;
button.classList.add("booking-button");
this.bookings.push({
start: minute,
column: column,
button: button
})
button.addEventListener("click", () => {
if (this.selectedButton) this.selectedButton.classList.remove("selected");
button.classList.add("selected");
this.selectedButton = button;
clickCallback();
});
return button;
}
updateTimeBar() {
var timeBarOffset = -this.timeBar.clientHeight / 2;
let date = Date.now();
let delta = date - this.todayMs;
if (delta > 86400000)
updateToday();
let hours = delta / 3600000;
let offset = (hours - this.openHour) / (this.closeHour - this.openHour);
let pxOffset = Math.round(this.element.clientHeight * offset + timeBarOffset);
console.log(this.timeBar.clientHeight);
this.timeBar.style.transform = `translate(4rem, ${pxOffset}px)`;
this.timeBar.classList.toggle("hidden", (clamp(offset, 0, 1) != offset))
}
}
// Global functions
const FromId = id => document.getElementById(id);
function clamp(x, min, max) {
return Math.max(Math.min(x, max), min);
}
function CreateElement(element, text, parent) {
let newElement = document.createElement(element);
if (text) {
let textNode = document.createTextNode(text);
newElement.appendChild(textNode);
}
if (parent) parent.appendChild(newElement);
return newElement;
}
function ConvertWeekday(value) {
return (value == 0 ? 7 : value) - 1;
}
function DoubleDigit(value) {
return value < 10 ? "0" + value : value;
}
function FormatDate(date) {
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// Main
const calendar = new Calendar(FromId("calendar"), true);
const grid = new BookingsGrid(FromId("time-grid"), FromId("meter"));
const dayInformation = FromId("day-information");
const bookingInformation = FromId("booking-information");
const deleteBookingButton = FromId("delete-booking");
let auth = getAuthHeader();
var seatCount = 1;
var bookingInterval = 0;
var bookingLength = 0;
async function loadBookingData() {
let res = await api.fetch("bookingData");
if (!res.ok) {
alert(httpErrorMessage(res.status));
return false;
}
let data = await res.json();
seatCount = data["seatCount"];
bookingInterval = data["bookingInterval"];
bookingLength = data["bookingLength"];
}
loadBookingData();
var monthData = [];
calendar.monthChangedCallback = async (year, month) => {
let res = await api.fetch(`monthBookingData?year=${year}&month=${month + 1}`);
if (!res.ok) {
alert(httpErrorMessage(res.status));
return false;
}
let data = await res.json();
monthData = data["dates"];
return true;
}
calendar.shouldButtonBeDisabledCallback = (date) => {
let dateDataL = monthData[FormatDate(date)];
if (!dateDataL || !dateDataL["isOpen"]) return [true, false];
return [false, false];
}
var dateData;
var selectedBookingData;
var selectedBookingButton;
calendar.dateSelectedCallback = async dateTime => {
let date = new Date(dateTime);
let formatted = FormatDate(date);
let res = await api.fetch(`bookings?date=${formatted}`, "GET", { "headers": { "Authorization": auth } });
if (!res.ok) {
alert(httpErrorMessage(res.status));
return false;
}
dateData = await res.json();
bookingInformation.innerText = "";
deleteBookingButton.setAttribute("hidden", "");
grid.changeConfig(dateData["openingTimeMinutes"], dateData["closingTimeMinutes"]);
let bookingCount = dateData["bookings"].length;
let personCount = dateData["bookings"].reduce((accumulator, booking) => accumulator + booking["adultCount"] + booking["childCount"], 0);
dayInformation.innerText = `Antal bokningar: ${bookingCount}
Antal personer: ${personCount}`;
dateData["bookings"].forEach(booking => {
date = new Date(booking.datetime);
let dateFormatted = `${date.getDate()} ${Calendar.months[date.getMonth()]} ${date.getFullYear()}`;
let timeFormatted = `${DoubleDigit(date.getHours())}:${DoubleDigit(date.getMinutes())}`
let time = date.getHours() * 60 + date.getMinutes();
let button = grid.addBooking(time, () => {
bookingInformation.innerText = `Namn: ${booking.name}
Telefonnummer: ${booking.phoneNumber}
Antal vuxna: ${booking.adultCount}
Antal barn: ${booking.childCount}
Datum: ${dateFormatted}
Tid: ${timeFormatted}`;
deleteBookingButton.removeAttribute("hidden");
selectedBookingData = booking;
selectedBookingButton = button;
});
})
return true;
}
deleteBookingButton.addEventListener("click", async () => {
if (!selectedBookingData) return;
let res = await api.fetch("bookings", "DELETE", { "body": JSON.stringify({ "bookingId": selectedBookingData.bookingId }), "headers": { "Authorization": auth } });
if (!res.ok) {
alert(httpErrorMessage(res.status));
return false;
}
grid.element.removeChild(selectedBookingButton);
bookingInformation.innerText = "";
deleteBookingButton.setAttribute("hidden", "");
const index = dateData.bookings.indexOf(selectedBookingData);
if (index > -1) {
dateData.bookings.splice(index, 1);
}
let bookingCount = dateData["bookings"].length;
let personCount = dateData["bookings"].reduce((accumulator, booking) => accumulator + booking["adultCount"] + booking["childCount"], 0);
dayInformation.innerText = `Antal bokningar: ${bookingCount}
Antal personer: ${personCount}`;
});