rowt/frontend/main.ts
philipp b7e8e1fa37
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m40s
CI/CD Pipeline / deploy-staging (push) Successful in 5m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
keep defaults
2024-04-07 15:30:52 +02:00

772 lines
23 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Choices from "choices.js";
import { Sidebar } from "./js/sidebar";
import "./scss/app.scss";
export interface choiceMap {
[details: string]: Choices;
}
let choiceObjects: choiceMap = {};
let boat_in_ottensheim = true;
let boat_reserved_today= true;
document.addEventListener("DOMContentLoaded", function () {
changeTheme();
initcolorTheme();
initSearch();
initSidebar();
initToggle();
replaceStrings();
initChoices();
initBoatActions();
selectBoatChange();
addRelationMagic(<HTMLElement>document.querySelector("body"));
reloadPage();
setCurrentdate(<HTMLInputElement>document.querySelector("#departure"));
});
function changeTheme() {
let toggleBtn = <HTMLElement>document.querySelector("#theme-toggle-js");
if (toggleBtn) {
toggleBtn.addEventListener("click", function () {
if (toggleBtn.dataset.theme === "light") {
setTheme("dark", true);
} else {
setTheme("light", true);
}
});
}
}
/***
* init javascript
* 1) detect native color scheme or use set theme in local storage
* 2) detect view (desktop or responsive) if on mobile device with touch screen
* 3) set base font size to 112.5% -> 18px
*/
function initcolorTheme() {
colorThemeWatcher();
let theme = localStorage.getItem("theme");
if (theme == null || theme === "auto") {
if (
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
setTheme("dark", false);
} else {
setTheme("light", false);
}
} else {
setTheme(theme);
}
}
/***
* Listener operating system native color configuration
*/
function colorThemeWatcher() {
try {
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
setTheme(e.matches ? "dark" : "light");
});
} catch {
console.warn("color theme watcher not supported");
}
}
/**
* Define color scheme, colors without losing the base font size configuration
* and add data-theme to html tag
* @param theme
*/
function setTheme(theme: string, setLocalStorage = true) {
let toggleBtn = document.querySelector("#theme-toggle-js");
if (setLocalStorage) {
localStorage.setItem("theme", theme);
}
if (toggleBtn) toggleBtn.setAttribute("data-theme", theme);
if (
document.documentElement.classList.contains("dark") &&
theme === "light"
) {
document.documentElement.classList.remove("dark");
} else if (theme === "dark") {
document.documentElement.classList.add("dark");
}
}
function setCurrentdate(input: HTMLInputElement) {
if (input) {
const now = new Date();
const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
input.value = formattedDateTime;
}
}
interface ChoiceBoatEvent extends Event {
detail: {
value: string;
label: string;
customProperties: {
amount_seats: number;
owner: number;
default_destination: string;
boat_in_ottensheim: boolean;
boat_reserved_today: boolean;
};
};
}
function selectBoatChange() {
const boatSelect = <HTMLSelectElement>document.querySelector("#boat_id");
if (boatSelect) {
const boatChoice = new Choices(boatSelect, {
loadingText: "Wird geladen...",
noResultsText: "Keine Ergebnisse gefunden",
noChoicesText: "Keine Ergebnisse gefunden",
itemSelectText: "Zum Auswählen klicken",
} as any);
boatSelect.addEventListener(
"addItem",
function (e) {
const event = e as ChoiceBoatEvent;
boat_reserved_today = event.detail.customProperties.boat_reserved_today;
if (boat_reserved_today){
alert(event.detail.label.trim()+' wurde heute reserviert. Bitte kontrolliere, dass du die Reservierung nicht störst.');
}
boat_in_ottensheim = event.detail.customProperties.boat_in_ottensheim;
const amount_seats = event.detail.customProperties.amount_seats;
setMaxAmountRowers("newrower", amount_seats);
const destination = <HTMLSelectElement>(
document.querySelector("#destination")
);
destination.value = event.detail.customProperties.default_destination;
if (event.detail.customProperties.owner) {
choiceObjects["newrower"].setChoiceByValue(
event.detail.customProperties.owner + "",
);
}
const inputElement = document.getElementById(
"departure",
) as HTMLInputElement;
const now = new Date();
const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
inputElement.value = formattedDateTime;
const distinput = <HTMLInputElement>(
document.querySelector("#distance_in_km")
);
distinput.value = "";
const destinput = <HTMLInputElement>(
document.querySelector("#destination")
);
destinput.dispatchEvent(new Event("input"));
},
false,
);
choiceObjects[boatSelect.id] = boatChoice;
choiceObjects["boat_id"] = boatChoice;
}
}
function reloadPage() {
if (!window.location.href.includes("ergo")) {
let pageTitle = document.title;
let attentionMessage = "Riemen- und Dollenbruch";
document.addEventListener("visibilitychange", function () {
let isPageActive = !document.hidden;
if (!isPageActive) {
document.title = attentionMessage;
} else {
document.title = pageTitle;
location.reload();
}
});
}
}
function setMaxAmountRowers(name: string, rowers: number) {
if (choiceObjects[name]) {
choiceObjects[name].removeActiveItems(-1);
//let curSelection = choiceObjects[name].getValue(true);
//let amount_to_delete = (<any>curSelection).length - rowers;
//if (amount_to_delete > 0){
// let to_delete = (<any>curSelection).slice(-amount_to_delete);
// for (let del of to_delete) {
// choiceObjects[name].removeActiveItemsByValue(del);
// }
//}
let input = <HTMLElement>document.querySelector("#" + name);
if (input) {
choiceObjects[name].config.maxItemCount = rowers;
if (rowers === 0) {
choiceObjects[name].disable();
input.parentElement?.parentElement?.parentElement?.classList.add(
"hidden",
);
input.parentElement?.parentElement?.parentElement?.classList.add(
"md:block",
);
input.parentElement?.parentElement?.parentElement?.classList.add(
"opacity-50",
);
} else {
choiceObjects[name].enable();
input.parentElement?.parentElement?.parentElement?.classList.remove(
"hidden",
);
input.parentElement?.parentElement?.parentElement?.classList.remove(
"md:block",
);
input.parentElement?.parentElement?.parentElement?.classList.remove(
"opacity-50",
);
}
}
//let only_steering = <HTMLSelectElement>document.querySelector('#shipmaster_only_steering');
//if(only_steering) {
// if(isShipmasterSteering == 'true') {
// only_steering.removeAttribute('disabled');
// only_steering.setAttribute('checked', 'true');
// only_steering.parentElement?.parentElement?.parentElement?.classList.remove('hidden');
// only_steering.parentElement?.parentElement?.parentElement?.classList.remove('md:block');
// only_steering.parentElement?.parentElement?.parentElement?.classList.remove('opacity-50');
// } else {
// only_steering.setAttribute('disabled', 'disabled');
// only_steering.removeAttribute('checked');
// only_steering.parentElement?.parentElement?.parentElement?.classList.add('hidden');
// only_steering.parentElement?.parentElement?.parentElement?.classList.add('md:block');
// only_steering.parentElement?.parentElement?.parentElement?.classList.add('opacity-50');
// }
//}
let shipmaster = <HTMLElement>(
document.querySelector("#shipmaster-" + name + "js")
);
let steering_person = <HTMLElement>(
document.querySelector("#steering_person-" + name + "js")
);
if (rowers == 1) {
if (shipmaster.parentNode) {
(<HTMLElement>shipmaster.parentNode).classList.add("hidden");
}
shipmaster.removeAttribute("required");
if (steering_person.parentNode) {
(<HTMLElement>steering_person.parentNode).classList.add("hidden");
}
steering_person.removeAttribute("required");
} else {
if (shipmaster.parentNode) {
(<HTMLElement>shipmaster.parentNode).classList.remove("hidden");
}
shipmaster.setAttribute("required", "required");
if (steering_person.parentNode) {
(<HTMLElement>steering_person.parentNode).classList.remove("hidden");
}
steering_person.setAttribute("required", "required");
}
}
}
function initBoatActions() {
const boatSelects = document.querySelectorAll(
'.boats-js[data-onclick="true"]',
);
if (boatSelects) {
Array.prototype.forEach.call(boatSelects, (select: HTMLInputElement) => {
select.addEventListener("click", function () {
if (select.dataset.seats) {
if (select.dataset.id) {
choiceObjects["boat_id"].setChoiceByValue(select.dataset.id);
}
window.scrollTo(0, 0);
}
});
});
}
}
function initChoices() {
const selects = document.querySelectorAll('select[data-init="true"]');
if (selects) {
Array.prototype.forEach.call(selects, (select: HTMLInputElement) => {
initNewChoice(select);
});
}
}
interface ChoiceEvent extends Event {
detail: {
value: string;
label: string;
customProperties: {
is_cox: boolean;
steers: boolean;
cox_on_boat: boolean;
is_racing: boolean;
};
};
}
function initNewChoice(select: HTMLInputElement) {
let seats = 0;
if (select.dataset && select.dataset.seats) {
seats = +select.dataset.seats;
}
let shipmaster = <HTMLElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
let steering_person = <HTMLElement>(
document.querySelector("#steering_person-" + select.id + "js")
);
if (seats == 1) {
if (shipmaster.parentNode) {
(<HTMLElement>shipmaster.parentNode).classList.add("hidden");
}
shipmaster.removeAttribute("required");
if (steering_person.parentNode) {
(<HTMLElement>steering_person.parentNode).classList.add("hidden");
}
steering_person.removeAttribute("required");
} else {
if (shipmaster.parentNode) {
(<HTMLElement>shipmaster.parentNode).classList.remove("hidden");
}
shipmaster.setAttribute("required", "required");
if (steering_person.parentNode) {
(<HTMLElement>steering_person.parentNode).classList.remove("hidden");
}
steering_person.setAttribute("required", "required");
}
const choice = new Choices(select, {
searchFields: ['label', 'value', 'customProperties.searchableText'],
removeItemButton: true,
loadingText: "Wird geladen...",
noResultsText: "Keine Ergebnisse gefunden",
noChoicesText: "Keine Ergebnisse gefunden",
itemSelectText: "Zum Auswählen klicken",
placeholderValue: "Ruderer auswählen",
maxItemCount: seats,
maxItemText: (maxItemCount) => {
return `Nur ${maxItemCount} Ruderer können hinzugefügt werden`;
},
callbackOnInit: function () {
this._currentState.items.forEach(function (obj) {
if (boat_in_ottensheim && obj.customProperties) {
if (obj.customProperties.is_racing) {
const coxSelect = <HTMLSelectElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
var new_option = new Option(obj.label, obj.value);
if (obj.customProperties.cox_on_boat) {
new_option.selected = true;
}
coxSelect.add(new_option);
}
}
if (obj.customProperties && obj.customProperties.is_cox) {
const coxSelect = <HTMLSelectElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
var new_option = new Option(obj.label, obj.value);
if (obj.customProperties.cox_on_boat) {
new_option.selected = true;
}
coxSelect.add(new_option);
}
const steeringSelect = <HTMLSelectElement>(
document.querySelector("#steering_person-" + select.id + "js")
);
if (steeringSelect) {
var new_option = new Option(obj.label, obj.value);
if (obj.customProperties && obj.customProperties.steers) {
new_option.selected = true;
}
steeringSelect.add(new_option);
}
});
},
});
choiceObjects[select.id] = choice;
select.addEventListener(
"addItem",
function (e) {
const event = e as ChoiceEvent;
const user_id = event.detail.value;
const name = event.detail.label;
if (boat_in_ottensheim && event.detail.customProperties.is_racing) {
if (event.detail.customProperties.is_racing) {
const coxSelect = <HTMLSelectElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
if (coxSelect) {
coxSelect.add(new Option(name, user_id));
}
}
}
if (event.detail.customProperties.is_cox) {
const coxSelect = <HTMLSelectElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
if (coxSelect) {
coxSelect.add(new Option(name, user_id));
}
}
const steeringSelect = <HTMLSelectElement>(
document.querySelector("#steering_person-" + select.id + "js")
);
if (steeringSelect) {
steeringSelect.add(new Option(name, user_id));
}
},
false,
);
select.addEventListener(
"removeItem",
function (e) {
const event = e as ChoiceEvent;
const user_id = event.detail.value;
const coxSelect = <HTMLSelectElement>(
document.querySelector("#shipmaster-" + select.id + "js")
);
if (coxSelect) {
for (var i = 0; i < coxSelect.length; i++) {
if (coxSelect.options[i].value == user_id) coxSelect.remove(i);
}
}
const steeringSelect = <HTMLSelectElement>(
document.querySelector("#steering_person-" + select.id + "js")
);
if (steeringSelect) {
for (var i = 0; i < steeringSelect.length; i++) {
if (steeringSelect.options[i].value == user_id)
steeringSelect.remove(i);
}
}
},
false,
);
choiceObjects[select.id] = choice;
}
function initToggle() {
// get filter btns & set object sessionStorage
const btns = <NodeListOf<HTMLButtonElement>>(
document.querySelectorAll(".filter-trips-js")
);
let filterObject = new Map();
if (btns) {
Array.prototype.forEach.call(btns, (btn: HTMLButtonElement) => {
filterObject.set(btn.dataset.action, btn.ariaPressed);
btn.addEventListener("click", () => {
let filter = sessionStorage.getItem("tripsFilter");
if (filter) {
let filterMap = new Map(JSON.parse(filter));
for (let entry of filterMap.entries()) {
if (entry[0] === btn.dataset.action && entry[1] !== "true") {
filterMap.set(entry[0], "true");
} else {
filterMap.set(entry[0], "false");
}
}
sessionStorage.setItem(
"tripsFilter",
JSON.stringify(Array.from(filterMap.entries())),
);
}
resetFilteredElements();
if (btn.getAttribute("aria-pressed") === "false") {
Array.prototype.forEach.call(btns, (b: HTMLButtonElement) => {
b.setAttribute("aria-pressed", "false");
});
triggerFilterAction(btn.dataset.action);
} else {
btn.setAttribute("aria-pressed", "false");
}
});
});
}
let filter = sessionStorage.getItem("tripsFilter");
if (filter) {
let filterMap = new Map(JSON.parse(filter));
for (let entry of filterMap.entries()) {
if (entry[1] === "true") {
triggerFilterAction(entry[0]);
}
}
} else {
sessionStorage.setItem(
"tripsFilter",
JSON.stringify(Array.from(filterObject.entries())),
);
}
}
function resetFilteredElements() {
const hiddenElements = document.querySelectorAll(".reset-js.hidden");
if (hiddenElements) {
Array.prototype.forEach.call(
hiddenElements,
(hiddenElement: HTMLButtonElement) => {
hiddenElement.classList.remove("hidden");
},
);
}
}
function triggerFilterAction(activeFilter: any) {
const activeBtn = document.querySelector(
'button[data-action="' + activeFilter + '"]',
);
if (activeBtn) {
activeBtn.setAttribute("aria-pressed", "true");
filterAction(activeFilter);
}
}
function filterAction(activeFilter: string) {
switch (activeFilter) {
case "filter-days": {
filterDays();
break;
}
case "filter-coxs": {
filterCoxs();
break;
}
}
}
function filterDays() {
const daysNoTrips = document.querySelectorAll('div[data-trips="0"]');
Array.prototype.forEach.call(daysNoTrips, (day: HTMLElement) => {
day.classList.toggle("hidden");
});
}
function filterCoxs() {
const noCoxNeeded = document.querySelectorAll('div[data-coxneeded="false"]');
Array.prototype.forEach.call(noCoxNeeded, (notNeeded: HTMLElement) => {
notNeeded.classList.toggle("hidden");
});
}
function initSearch() {
const input = <HTMLInputElement>document.querySelector("#filter-js");
if (input) {
filterElements(input.value);
input.addEventListener("input", () => {
filterElements(input.value);
});
}
}
function filterElements(input: string) {
const elements = document.querySelectorAll('div[data-filterable="true"]');
let resultWrapper = <HTMLElement>document.querySelector("#filter-result-js"),
amountShownElements = 0;
Array.prototype.forEach.call(elements, (element: HTMLElement) => {
// set both strings (input & dataset filter) to lowercase to not be case sensitive
let filterString = element.dataset.filter?.toLocaleLowerCase();
// bulk hide all elements
element.style.display = "none";
// show if input matches
if (filterString?.includes(input.toLocaleLowerCase())) {
element.style.display = "flex";
amountShownElements++;
}
});
if (resultWrapper) {
resultWrapper.innerHTML =
amountShownElements === 0
? "Kein Ergebnis gefunden"
: "<strong>" +
amountShownElements +
"</strong>" +
(amountShownElements > 1 ? " Ergebnisse" : " Ergebnis") +
" gefunden";
}
}
function initSidebar() {
const sidebarTrigger = <NodeListOf<HTMLElement>>(
document.querySelectorAll("[data-trigger]")
);
if (sidebarTrigger) {
Array.prototype.forEach.call(
sidebarTrigger,
(triggerElement: HTMLElement) => {
if (triggerElement.dataset.trigger) {
const sidebar = new Sidebar(triggerElement.dataset.trigger);
triggerElement.addEventListener("click", (e) => {
e.preventDefault();
if (triggerElement.dataset.trigger === "sidebar") {
initTripSidebar(triggerElement);
}
sidebar.toggle();
});
}
},
);
}
}
function initTripSidebar(triggerElement: HTMLElement) {
const sidebarElement = <HTMLElement>document.querySelector("#sidebar");
if (
sidebarElement &&
triggerElement.dataset.body &&
triggerElement.dataset.header
) {
let body = <HTMLElement>document.querySelector(triggerElement.dataset.body);
let bodyElement = <HTMLElement>body.cloneNode(true);
let bodyContainerElement = <HTMLElement>(
sidebarElement.querySelector(".body-js")
);
/* Quickfix duplicate ids checkboxes */
const checkboxes = <NodeListOf<HTMLElement>>(
bodyElement.querySelectorAll('input[type="checkbox"]')
);
Array.prototype.forEach.call(checkboxes, (checkbox: HTMLElement) => {
if (checkbox) {
checkbox.parentElement?.setAttribute("for", checkbox.id + "js");
checkbox.id += "js";
}
});
const prefixedContent = <NodeListOf<HTMLElement>>(
bodyElement.querySelectorAll(".change-id-js")
);
Array.prototype.forEach.call(prefixedContent, (content: HTMLElement) => {
if (content) {
content.id += "js";
if (content.dataset.relation) {
content.dataset.relation += "js";
}
}
});
if (bodyContainerElement) {
bodyContainerElement.innerHTML = "";
bodyContainerElement.append(bodyElement);
addRelationMagic(bodyElement);
}
if (triggerElement.dataset.day) {
let hiddenElement = <HTMLInputElement>(
bodyElement.querySelector(".day-js")
);
if (hiddenElement) {
hiddenElement.value = triggerElement.dataset.day;
}
}
let headerElement = sidebarElement.querySelector(".header-js");
if (headerElement) {
headerElement.innerHTML = triggerElement.dataset.header;
}
const selects = bodyElement.querySelectorAll("select[multiple]");
if (selects) {
Array.prototype.forEach.call(selects, (select: HTMLInputElement) => {
initNewChoice(select);
});
}
}
const defaultDateTimes = <NodeListOf<HTMLInputElement>>(
document.querySelectorAll(".current-date-time")
);
defaultDateTimes.forEach((defaultDateTime) => {
setCurrentdate(defaultDateTime);
});
}
function addRelationMagic(bodyElement: HTMLElement) {
const fields = bodyElement.querySelectorAll(".set-distance-js");
if (fields) {
Array.prototype.forEach.call(fields, (field: HTMLInputElement) => {
if (field.dataset.relation) {
const relatedField = <HTMLInputElement>(
bodyElement.querySelector("#" + field.dataset.relation)
);
if (relatedField) {
field.addEventListener("input", (e) => {
e.preventDefault();
const dataList = <HTMLDataListElement>(
document.querySelector("#destinations")
);
if (dataList) {
var option = Array.prototype.find.call(
dataList.options,
function (option) {
return option.value === field.value;
},
);
// Get distance
const distance = option.getAttribute("distance");
if (distance) relatedField.value = distance;
}
});
}
}
});
}
}
function replaceStrings() {
const weekdays = document.querySelectorAll(".weekday-js");
Array.prototype.forEach.call(weekdays, (weekday: HTMLElement) => {
weekday.innerHTML = weekday.innerHTML.replace("Freitag", "Markttag");
});
}