MAKENTNU/web

View on GitHub
src/make_queue/static/make_queue/js/reservation_form.js

Summary

Maintainability
D
1 day
Test Coverage
/*
* Linking `reservation_form.css` is required when linking this script.
* The `var` variables below must also be defined.
*/
// noinspection ES6ConvertVarToLetConst
var maximumDay;
// noinspection ES6ConvertVarToLetConst
var shouldForceNewTime;
 
const reservations = [];
const reservationRules = [];
let canIgnoreRules = false;
 
const $machineTypeDropdown = $("#machine-type-dropdown");
const $machineNameDropdown = $("#machine-name-dropdown");
const $startTimeField = $("#start-time");
const $startTimeFieldInput = $startTimeField.find("input");
const $endTimeField = $("#end-time");
const $endTimeFieldInput = $endTimeField.find("input");
const $eventField = $("#event-pk");
 
Function `getFutureReservations` has 29 lines of code (exceeds 25 allowed). Consider refactoring.
function getFutureReservations(machineID, forceNewTime) {
/**
* Retrieves future reservations and all reservation rules from the server.
*/
let jsonUrl = `${LANG_PREFIX}/api/reservation/machines/${machineID}/data/`;
const reservationPK = $("#reservation-form").data("reservation");
if (reservationPK) {
jsonUrl += `?exclude_reservation=${reservationPK}`;
}
$.getJSON(jsonUrl, function (data) {
reservations.length = 0;
$.each(data.reservations, function (index, value) {
reservations.push({
// The values from the server are ISO-formatted strings
startTime: new Date(value.start_time),
endTime: new Date(value.end_time),
});
});
 
canIgnoreRules = data.can_ignore_rules;
calendar.updateCanIgnoreRules(canIgnoreRules);
 
reservationRules.length = 0;
$.each(data.rules, function (index, value) {
reservationRules.push({
periods: value.periods,
max_inside: value.max_hours,
max_crossed: value.max_hours_crossed,
});
});
// Indicates if we want to update the start date or not
if (forceNewTime) {
const startTime = getFirstReservableTimeSlot(new Date());
$startTimeField.calendar("set date", startTime);
$endTimeField.calendar("set date", getMaxDateReservation(startTime));
}
});
}
 
Function `getFirstReservableTimeSlot` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.
function getFirstReservableTimeSlot(date) {
/**
* Finds the first reservable time slot on or after the supplied date
*/
let found = false;
while (!found) {
found = true;
for (let reservation of reservations) {
// Ignore 5 minute gaps
if (date >= reservation.startTime - new Date(300000) && date < reservation.endTime) {
found = false;
date = reservation.endTime;
}
}
}
return date;
}
 
function isNonReservedDate(date) {
/**
* Checks if the given date is inside any reservation
*/
for (const reservation of reservations) {
if (date >= reservation.startTime && date < reservation.endTime)
return false;
}
return true;
}
 
function isCompletelyReserved(start, end) {
/**
* Checks if the given time period is completely reserved (ignores open slots of less than 5 minutes)
*/
const maxDifference = 5 * 60 * 1000;
const reservationsInPeriod = getReservationsInPeriod(start, end);
if (!reservationsInPeriod.length)
return false;
reservationsInPeriod.sort((a, b) => a.startTime - b.startTime);
let currentTime = start;
for (const reservation of reservationsInPeriod) {
if (reservation.startTime > new Date(currentTime.valueOf() + maxDifference))
return false;
currentTime = reservation.endTime;
}
return currentTime >= new Date(end.valueOf() - maxDifference);
}
 
Function `getReservationsInPeriod` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.
function getReservationsInPeriod(start, end) {
/**
* Returns all reservations that are at least partially within the given time period.
*/
let reservationsInPeriod = [];
for (const reservation of reservations) {
Consider simplifying this complex logical expression.
if ((reservation.startTime <= start && reservation.endTime > start) ||
(reservation.startTime > start && reservation.endTime < end) ||
(reservation.startTime < end && reservation.endTime > end)) {
reservationsInPeriod.push(reservation);
}
}
return reservationsInPeriod;
}
 
Function `getMaxDateReservation` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.
function getMaxDateReservation(date) {
/**
* Finds the latest date at which a reservation which starts at the given date can end and still be valid.
*/
let maxDate = new Date(date.valueOf());
let shouldRestrictToRules = !canIgnoreRules;
if ($("#event-checkbox input").is(":checked") || $("#special-checkbox input").is(":checked")) {
maxDate.setDate(maxDate.getDate() + 56);
shouldRestrictToRules = false;
} else {
// Normal reservations should never be more than 1 week
maxDate = new Date(maxDate.valueOf() + 7 * 24 * 60 * 60 * 1000 - 1000);
}
for (const reservation of reservations) {
if (date <= reservation.startTime && reservation.startTime < maxDate)
maxDate = new Date(reservation.startTime.valueOf());
}
if (shouldRestrictToRules) {
// If the user/reservation type cannot ignore rules, shrink the reservation until it
// is valid given the rules for the machine type
return modifyToFirstValid(reservationRules, date, maxDate, 1);
}
return maxDate;
}
 
function updateMaxEndDate() {
/**
* Updates the max date of the calendar indicating the end time of the reservation.
*/
const currentStartDate = $startTimeField.calendar("get date");
if (currentStartDate !== null) {
$endTimeField.calendar("setting", "maxDate", getMaxDateReservation(currentStartDate));
}
}
 
let minDateStartTime = new Date();
if ($startTimeField.children("div").hasClass("disabled")) {
minDateStartTime = new Date(new Date($startTimeFieldInput.val()) - new Date(5 * 60 * 1000));
}
$startTimeField.calendar({
minDate: minDateStartTime,
maxDate: maximumDay,
mode: "minute",
endCalendar: $endTimeField,
initialDate: new Date($startTimeFieldInput.val()),
firstDayOfWeek: 1,
isDisabled: function (date, mode) {
if (!date)
return true;
if (mode === "minute")
return !isNonReservedDate(date);
if (mode === "hour") {
date = new Date(date.valueOf());
date.setMinutes(0, 0, 0);
return isCompletelyReserved(date, new Date(date.valueOf() + 60 * 60 * 1000));
}
if (mode === "day") {
date = new Date(date.valueOf());
date.setHours(0, 0, 0, 0);
return isCompletelyReserved(date, new Date(date.valueOf() + 24 * 60 * 60 * 1000));
}
Avoid too many `return` statements within this function.
return false;
},
onChange: function (value) {
if (value === undefined)
return true;
const shouldChange = isNonReservedDate(value);
if (shouldChange) {
$endTimeField.calendar("setting", "maxDate", getMaxDateReservation(value));
}
return shouldChange;
},
},
);
 
$endTimeField.calendar({
firstDayOfWeek: 1,
startCalendar: $startTimeField,
minDate: new Date(),
});
 
$("#reservation-form .ui.dropdown").dropdown();
Similar blocks of code found in 2 locations. Consider refactoring.
$("#event-checkbox").checkbox({
onChange: function () {
$("#event-name-input").toggleClass("display-none", !$(this).is(":checked"));
if ($(this).is(":checked")) {
$("#special-checkbox").checkbox("uncheck");
$startTimeField.calendar("setting", "maxDate", null);
} else {
$startTimeField.calendar("setting", "maxDate", maximumDay);
}
updateMaxEndDate();
},
});
Similar blocks of code found in 2 locations. Consider refactoring.
$("#special-checkbox").checkbox({
onChange: function () {
$("#special-input").toggleClass("display-none", !$(this).is(":checked"));
if ($(this).is(":checked")) {
$("#event-checkbox").checkbox("uncheck");
$startTimeField.calendar("setting", "maxDate", null);
} else {
$startTimeField.calendar("setting", "maxDate", maximumDay);
}
updateMaxEndDate();
},
});
 
$machineTypeDropdown.dropdown({
onChange: function (selectedMachineType, text, $choice) {
if (!$("#machine-type-dropdown").is(".disabled")) {
$machineNameDropdown.toggleClass("disabled", false);
 
// Replace the shown machine items from the last selected machine type with the ones from the currently selected machine type
$("#machine-name-dropdown .menu .item").toggleClass("display-none", true);
const $machinesOfSelectedType = $(`#machine-name-dropdown .menu .item.machine-type-${selectedMachineType}`);
$machinesOfSelectedType.toggleClass("display-none", false);
// Select the first machine of the selected machine type
$machineNameDropdown.dropdown(
"set selected", $machinesOfSelectedType.first().data("value"),
);
}
},
}).dropdown(
"set selected", $(".selected-machine-type").data("value"),
);
 
$machineNameDropdown.dropdown(
"set selected", $(".selected-machine-name").data("value"),
).dropdown({
onChange: function (value, text, $choice) {
if (value && value !== "default") {
getFutureReservations(value, true);
calendar.changeMachine(value);
}
$("#start-time > div, #end-time > div").toggleClass("disabled", value === "default");
$("#start-time, #end-time").calendar("clear");
},
});
 
$("form").submit(function (event) {
let is_valid = true;
$machineNameDropdown.toggleClass("error-border", false);
$startTimeFieldInput.toggleClass("error-border", false);
$endTimeFieldInput.toggleClass("error-border", false);
$eventField.toggleClass("error-border", false);
 
if ($machineNameDropdown.dropdown("get value") === "default") {
$machineNameDropdown.toggleClass("error-border", true);
is_valid = false;
}
 
if ($startTimeField.calendar("get date") === null) {
$startTimeFieldInput.toggleClass("error-border", true);
is_valid = false;
}
 
if ($endTimeField.calendar("get date") === null) {
$endTimeFieldInput.toggleClass("error-border", true);
is_valid = false;
}
 
if ($("#event-checkbox input").is(":checked") && $eventField.dropdown("get value") === "") {
$eventField.toggleClass("error-border", true);
is_valid = false;
}
 
if (!is_valid)
return event.preventDefault();
});
 
function timeSelectionPopupHTML() {
/**
* Creates a valid popup for the time selection utility in the reservation calendar
*/
const $button = $("<div>").addClass("ui fluid make-bg-yellow button").html(gettext("Choose time"));
$button.on("mousedown touchstart", () => {
$startTimeField.calendar("set date", calendar.getSelectionStartTime());
$endTimeField.calendar("set date", calendar.getSelectionEndTime());
calendar.resetSelection();
});
return $button;
}
 
function getMachineType() {
return $machineTypeDropdown.dropdown("get value");
}
 
function getMachine() {
return $machineNameDropdown.dropdown("get value");
}
 
const calendar = new ReservationCalendar($(".reservation-calendar"), {
date: new Date(),
machineType: getMachineType(),
machine: getMachine(),
selection: true,
canIgnoreRules: false,
selectionPopupContent: timeSelectionPopupHTML,
});
 
if ($startTimeField.calendar("get date") !== null) {
calendar.showDate($startTimeField.calendar("get date"));
}
 
getFutureReservations($machineNameDropdown.dropdown("get value"), shouldForceNewTime);