rowt/frontend/main.ts
2023-10-30 15:32:17 +01:00

551 lines
18 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 = {};
document.addEventListener('DOMContentLoaded', function() {
initSearch();
initSidebar();
initToggle();
replaceStrings();
initChoices();
initBoatActions();
selectBoatChange();
addRelationMagic(<HTMLElement>document.querySelector('body'));
reloadPage();
setCurrentdate(<HTMLInputElement>document.querySelector('#departure'));
});
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;
}
}
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',
callbackOnCreateTemplates: function () {
return {
option: ({ label, value, customProperties, active, disabled, }: any) => {
const opt: HTMLOptionElement = Choices.defaults.templates.option.call(
this,
{ label, value, customProperties, active, disabled }
);
// We get the original <option> from choicejs
const originalOption: HTMLOptionElement = this._presetOptions.filter(
(option: HTMLOptionElement) => option.value === value
)[0];
const rowers = Number(originalOption.dataset.amount_seats);
setMaxAmountRowers("newrower", rowers);
return opt;
}
}
}
} as any);
choiceObjects[boatSelect.id] = boatChoice;
}
}
function reloadPage() {
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]) {
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');
// }
//}
}
}
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) {
const rowers = Number(select.dataset.seats);
setMaxAmountRowers("newrower", rowers);
if (select.dataset.id) {
choiceObjects['boat_id'].setChoiceByValue(select.dataset.id);
}
window.scrollTo(0, 0);
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;
}
});
});
}
}
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,
}
};
}
function initNewChoice(select: HTMLInputElement) {
const choice = new Choices(select, {
removeItemButton: true,
loadingText: 'Wird geladen...',
noResultsText: 'Keine Ergebnisse gefunden',
noChoicesText: 'Keine Ergebnisse gefunden',
itemSelectText: 'Zum Auswählen klicken',
placeholderValue: 'Ruderer auswählen',
maxItemCount: -1, //TODO
maxItemText: (maxItemCount) => {
return `Nur ${maxItemCount} Ruderer können hinzugefügt werden`;
},
callbackOnCreateTemplates: function () {
return {
option: ({ label, value, customProperties, active, disabled, }: any) => {
const opt: HTMLOptionElement = Choices.defaults.templates.option.call(
this,
{ label, value, customProperties, active, disabled }
);
// We get the original <option> from choicejs
//const originalOption: HTMLOptionElement = this._presetOptions.filter(
// (option: HTMLOptionElement) => option.value === value
//)[0];
//
//const rowers = Number(originalOption.dataset.amount_seats);
//const isShipmasterSteering = originalOption.dataset.default_shipmaster_only_steering;
//setMaxAmountRowers(select.id, curr_boat_rowers, isShipmasterSteering);
return opt;
}
}
},
callbackOnInit: function() {
this._currentState.items.forEach(function(obj){
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 (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;
}
case 'filter-months': {
filterMonths(activeFilter)
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 filterMonths(action: string) {
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
const monthToggle = <HTMLButtonElement>document.querySelector('button[data-action="' + action + '"]');
if(monthToggle) {
const currentMonth = monthToggle.dataset.month;
if(currentMonth) {
const index = months.indexOf(currentMonth);
if (index > -1) { // only splice array when item is found
months.splice(index, 1); // 2nd parameter means remove one item only
}
Array.prototype.forEach.call(months, (month: HTMLElement) => {
const notThisMonth = document.querySelectorAll('div[data-month="' + month + '"]');
Array.prototype.forEach.call(notThisMonth, (notThisMonth: HTMLElement) => {
notThisMonth.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');
});
}