From 442257df0192004c7d6e7e32a46e58027efb377b Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 4 Mar 2024 13:28:42 +0100 Subject: [PATCH] reformat files --- README.md | 6 + frontend/logbook.ts | 87 ++- frontend/main.ts | 803 ++++++++++++--------- frontend/tests/cox.spec.ts | 196 ++--- frontend/tests/log.spec.ts | 128 ++-- templates/admin/boat/index.html.tera | 22 +- templates/admin/mail.html.tera | 35 +- templates/admin/user/fees.html.tera | 83 +-- templates/admin/user/index.html.tera | 185 +++-- templates/auth/login.html.tera | 67 +- templates/auth/set-pw.html.tera | 49 +- templates/base.html.tera | 76 +- templates/boatdamages.html.tera | 182 ++--- templates/dynamics/sidebar.html.tera | 27 +- templates/ergo.final.html.tera | 70 +- templates/ergo.html.tera | 370 +++++----- templates/forms/event.html.tera | 29 +- templates/forms/trip.html.tera | 22 +- templates/includes/book.html.tera | 13 +- templates/includes/buttons.html.tera | 30 +- templates/includes/cox-icon.html.tera | 9 +- templates/includes/delete-icon.html.tera | 10 +- templates/includes/footer.html.tera | 51 +- templates/includes/forms/boat.html.tera | 148 ++-- templates/includes/forms/log.html.tera | 468 ++++++------ templates/includes/funnel-icon.html.tera | 9 +- templates/includes/lock-icon.html.tera | 7 +- templates/includes/macros.html.tera | 427 +++++------ templates/includes/pencil.html.tera | 11 +- templates/includes/plus-icon.html.tera | 9 +- templates/includes/qrcode.html.tera | 5 +- templates/includes/question-icon.html.tera | 8 +- templates/index.html.tera | 218 +++--- templates/kiosk.html.tera | 72 +- templates/log.completed.html.tera | 34 +- templates/log.html.tera | 77 +- templates/planned.html.tera | 675 ++++++++--------- templates/stat.boats.html.tera | 74 +- templates/stat.people.html.tera | 123 ++-- 39 files changed, 2570 insertions(+), 2345 deletions(-) diff --git a/README.md b/README.md index 96d2517..4e61b4d 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,9 @@ ## Backend (Unit + Integration) `cargo t` + +# Lints + +- Rust: `cargo check` +- Tera files: `djlint **.html.tera --profile=jinja --reformat` +- Typescript: `prettier -w *.ts` diff --git a/frontend/logbook.ts b/frontend/logbook.ts index e3c96b3..39d3dd4 100644 --- a/frontend/logbook.ts +++ b/frontend/logbook.ts @@ -1,77 +1,76 @@ -import * as d3 from 'd3'; +import * as d3 from "d3"; export interface Data { date: Date; km: number; } -if(sessionStorage.getItem('userStats')) { - const data = JSON.parse(sessionStorage.getItem('userStats') || '{}') as Data[]; +if (sessionStorage.getItem("userStats")) { + const data = JSON.parse( + sessionStorage.getItem("userStats") || "{}", + ) as Data[]; - if(data.length >= 2) { + if (data.length >= 2) { const margin = { top: 20, right: 20, bottom: 50, left: 50 }; const width: number = 960 - margin.left - margin.right; const height: number = 500 - margin.top - margin.bottom; - + data.forEach((d: Data) => { - d.date = new Date(d.date) + d.date = new Date(d.date); d.km = +d.km; }); - - const x = d3.scaleTime() + + const x = d3 + .scaleTime() .domain(<[Date, Date]>d3.extent(data, (d: Data) => d.date)) .range([0, width]); - - const y = d3.scaleLinear() + + const y = d3 + .scaleLinear() .domain([0, Number(d3.max(data, (d: Data) => d.km))]) .range([height, 0]); - - const line = d3.line() + + const line = d3 + .line() .x((d: Data) => x(d.date)) .y((d: Data) => y(d.km)); - - const svg = d3.select('#container') - .append('svg') - .attr('width', width + margin.left + margin.right) - .attr('height', height + margin.top + margin.bottom) + + const svg = d3 + .select("#container") + .append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) .call(responsivefy) - .append('g') - .attr('transform', `translate(${margin.left},${margin.top})`); - - svg.append('path') - .data([data]) - .attr('class', 'line') - .attr('d', line); - - svg.append('g') - .attr('transform', `translate(0,${height})`) + .append("g") + .attr("transform", `translate(${margin.left},${margin.top})`); + + svg.append("path").data([data]).attr("class", "line").attr("d", line); + + svg + .append("g") + .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x)); - - svg.append('g') - .call(d3.axisLeft(y)); + + svg.append("g").call(d3.axisLeft(y)); } } - - function responsivefy(svg: any) { const container = d3.select(svg.node().parentNode); - const width = parseInt(svg.style('width'), 10); - const height = parseInt(svg.style('height'), 10); + const width = parseInt(svg.style("width"), 10); + const height = parseInt(svg.style("height"), 10); const aspect = width / height; - svg.attr('viewBox', `0 0 ${width} ${height}`) - .attr('preserveAspectRatio', 'xMinYMid') + svg + .attr("viewBox", `0 0 ${width} ${height}`) + .attr("preserveAspectRatio", "xMinYMid") .call(resize); - d3.select(window).on( - 'resize.' + container.attr('id'), - resize - ); + d3.select(window).on("resize." + container.attr("id"), resize); function resize() { - const w = parseInt(container.style('width')); - svg.attr('width', w); - svg.attr('height', Math.round(w / aspect)); + const w = parseInt(container.style("width")); + svg.attr("width", w); + svg.attr("height", Math.round(w / aspect)); } -} \ No newline at end of file +} diff --git a/frontend/main.ts b/frontend/main.ts index 2ddfe48..4ac89e2 100644 --- a/frontend/main.ts +++ b/frontend/main.ts @@ -1,6 +1,6 @@ import Choices from "choices.js"; -import { Sidebar } from './js/sidebar'; -import './scss/app.scss' +import { Sidebar } from "./js/sidebar"; +import "./scss/app.scss"; export interface choiceMap { [details: string]: Choices; } @@ -8,7 +8,7 @@ export interface choiceMap { let choiceObjects: choiceMap = {}; let boat_in_ottensheim = true; -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener("DOMContentLoaded", function () { changeTheme(); initcolorTheme(); initSearch(); @@ -18,137 +18,159 @@ document.addEventListener('DOMContentLoaded', function() { initChoices(); initBoatActions(); selectBoatChange(); - addRelationMagic(document.querySelector('body')); + addRelationMagic(document.querySelector("body")); reloadPage(); - setCurrentdate(document.querySelector('#departure')); + setCurrentdate(document.querySelector("#departure")); }); function changeTheme() { - let toggleBtn = document.querySelector('#theme-toggle-js'); + let toggleBtn = document.querySelector("#theme-toggle-js"); - if(toggleBtn) { - toggleBtn.addEventListener('click', function() { - if(toggleBtn.dataset.theme === 'light') { - setTheme('dark', true); + if (toggleBtn) { + toggleBtn.addEventListener("click", function () { + if (toggleBtn.dataset.theme === "light") { + setTheme("dark", true); } else { - setTheme('light', true); + 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 -*/ + * 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'); + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", (e) => { + setTheme(e.matches ? "dark" : "light"); }); } catch { - console.warn('color theme watcher not supported'); + 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 -*/ + * 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'); + let toggleBtn = document.querySelector("#theme-toggle-js"); if (setLocalStorage) { - localStorage.setItem('theme', theme); + localStorage.setItem("theme", theme); } - if (toggleBtn) toggleBtn.setAttribute('data-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'); + 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; + 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{ +interface ChoiceBoatEvent extends Event { detail: { - value: string; - label: string, + value: string; + label: string; customProperties: { - amount_seats: number, - owner: number, - default_destination: string, - boat_in_ottensheim: boolean, - } + amount_seats: number; + owner: number; + default_destination: string; + boat_in_ottensheim: boolean; + }; }; } function selectBoatChange() { - const boatSelect = document.querySelector('#boat_id'); + const boatSelect = document.querySelector("#boat_id"); - if(boatSelect) { + if (boatSelect) { const boatChoice = new Choices(boatSelect, { - loadingText: 'Wird geladen...', - noResultsText: 'Keine Ergebnisse gefunden', - noChoicesText: 'Keine Ergebnisse gefunden', - itemSelectText: 'Zum Auswählen klicken', + 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_in_ottensheim = event.detail.customProperties.boat_in_ottensheim; + boatSelect.addEventListener( + "addItem", + function (e) { + const event = e as ChoiceBoatEvent; + boat_in_ottensheim = event.detail.customProperties.boat_in_ottensheim; - const amount_seats = event.detail.customProperties.amount_seats; - setMaxAmountRowers("newrower", amount_seats); + const amount_seats = event.detail.customProperties.amount_seats; + setMaxAmountRowers("newrower", amount_seats); - const destination = document.querySelector('#destination'); - destination.value = event.detail.customProperties.default_destination; + const destination = ( + document.querySelector("#destination") + ); + destination.value = event.detail.customProperties.default_destination; - if (event.detail.customProperties.owner){ - choiceObjects["newrower"].setChoiceByValue(event.detail.customProperties.owner+""); - } + if (event.detail.customProperties.owner) { + choiceObjects["newrower"].setChoiceByValue( + event.detail.customProperties.owner + "", + ); + } - const inputElement = document.getElementById("departure") as HTMLInputElement; + 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')}`; - + 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 = document.querySelector('#distance_in_km'); - distinput.value = ""; + const distinput = ( + document.querySelector("#distance_in_km") + ); + distinput.value = ""; - const destinput= document.querySelector('#destination'); - destinput.dispatchEvent(new Event('input')); - },false); + const destinput = ( + document.querySelector("#destination") + ); + destinput.dispatchEvent(new Event("input")); + }, + false, + ); choiceObjects[boatSelect.id] = boatChoice; choiceObjects["boat_id"] = boatChoice; @@ -156,25 +178,25 @@ function selectBoatChange() { } function reloadPage() { - if (!window.location.href.includes("ergo")){ + if (!window.location.href.includes("ergo")) { let pageTitle = document.title; - let attentionMessage = 'Riemen- und Dollenbruch'; + let attentionMessage = "Riemen- und Dollenbruch"; - document.addEventListener('visibilitychange', function() { - let isPageActive = !document.hidden; + document.addEventListener("visibilitychange", function () { + let isPageActive = !document.hidden; - if(!isPageActive){ - document.title = attentionMessage; - } else { - document.title = pageTitle; - location.reload(); - } + if (!isPageActive) { + document.title = attentionMessage; + } else { + document.title = pageTitle; + location.reload(); + } }); } } function setMaxAmountRowers(name: string, rowers: number) { - if(choiceObjects[name]) { + if (choiceObjects[name]) { choiceObjects[name].removeActiveItems(-1); //let curSelection = choiceObjects[name].getValue(true); //let amount_to_delete = (curSelection).length - rowers; @@ -186,19 +208,31 @@ function setMaxAmountRowers(name: string, rowers: number) { // } //} - let input = document.querySelector('#'+name); - if(input) { + let input = 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].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'); + input.parentElement?.parentElement?.parentElement?.classList.remove( + "hidden", + ); + input.parentElement?.parentElement?.parentElement?.classList.remove( + "md:block", + ); + input.parentElement?.parentElement?.parentElement?.classList.remove( + "opacity-50", + ); } } @@ -215,43 +249,49 @@ function setMaxAmountRowers(name: string, rowers: number) { // 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'); + // only_steering.parentElement?.parentElement?.parentElement?.classList.add('opacity-50'); // } //} - let shipmaster = document.querySelector('#shipmaster-'+name+'js'); - let steering_person = document.querySelector('#steering_person-'+name+'js'); - if (rowers == 1){ - if (shipmaster.parentNode) { - (shipmaster.parentNode).classList.add('hidden'); - } - shipmaster.removeAttribute('required'); + let shipmaster = ( + document.querySelector("#shipmaster-" + name + "js") + ); + let steering_person = ( + document.querySelector("#steering_person-" + name + "js") + ); + if (rowers == 1) { + if (shipmaster.parentNode) { + (shipmaster.parentNode).classList.add("hidden"); + } + shipmaster.removeAttribute("required"); - if (steering_person.parentNode){ - (steering_person.parentNode).classList.add('hidden'); - } - steering_person.removeAttribute('required'); - }else{ - if (shipmaster.parentNode){ - (shipmaster.parentNode).classList.remove('hidden'); - } - shipmaster.setAttribute('required', 'required'); + if (steering_person.parentNode) { + (steering_person.parentNode).classList.add("hidden"); + } + steering_person.removeAttribute("required"); + } else { + if (shipmaster.parentNode) { + (shipmaster.parentNode).classList.remove("hidden"); + } + shipmaster.setAttribute("required", "required"); - if (steering_person.parentNode){ - (steering_person.parentNode).classList.remove('hidden'); - } - steering_person.setAttribute('required', 'required'); + if (steering_person.parentNode) { + (steering_person.parentNode).classList.remove("hidden"); + } + steering_person.setAttribute("required", "required"); } } } function initBoatActions() { - const boatSelects = document.querySelectorAll('.boats-js[data-onclick="true"]'); - if(boatSelects) { + 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) { + select.addEventListener("click", function () { + if (select.dataset.seats) { if (select.dataset.id) { - choiceObjects['boat_id'].setChoiceByValue(select.dataset.id); + choiceObjects["boat_id"].setChoiceByValue(select.dataset.id); } window.scrollTo(0, 0); @@ -263,23 +303,23 @@ function initBoatActions() { function initChoices() { const selects = document.querySelectorAll('select[data-init="true"]'); - if(selects) { + if (selects) { Array.prototype.forEach.call(selects, (select: HTMLInputElement) => { initNewChoice(select); }); } } -interface ChoiceEvent extends Event{ +interface ChoiceEvent extends Event { detail: { - value: string; - label: string, + value: string; + label: string; customProperties: { - is_cox: boolean, - steers: boolean, - cox_on_boat: boolean, - is_racing: boolean, - } + is_cox: boolean; + steers: boolean; + cox_on_boat: boolean; + is_racing: boolean; + }; }; } @@ -289,198 +329,237 @@ function initNewChoice(select: HTMLInputElement) { seats = +select.dataset.seats; } - let shipmaster = document.querySelector('#shipmaster-'+select.id+'js'); - let steering_person = document.querySelector('#steering_person-'+select.id+'js'); - if (seats == 1){ - if (shipmaster.parentNode) { - (shipmaster.parentNode).classList.add('hidden'); - } - shipmaster.removeAttribute('required'); - - if (steering_person.parentNode){ - (steering_person.parentNode).classList.add('hidden'); - } - steering_person.removeAttribute('required'); - }else{ - if (shipmaster.parentNode){ - (shipmaster.parentNode).classList.remove('hidden'); - } - shipmaster.setAttribute('required', 'required'); - - if (steering_person.parentNode){ - (steering_person.parentNode).classList.remove('hidden'); - } - steering_person.setAttribute('required', 'required'); + let shipmaster = ( + document.querySelector("#shipmaster-" + select.id + "js") + ); + let steering_person = ( + document.querySelector("#steering_person-" + select.id + "js") + ); + if (seats == 1) { + if (shipmaster.parentNode) { + (shipmaster.parentNode).classList.add("hidden"); } + shipmaster.removeAttribute("required"); + + if (steering_person.parentNode) { + (steering_person.parentNode).classList.add("hidden"); + } + steering_person.removeAttribute("required"); + } else { + if (shipmaster.parentNode) { + (shipmaster.parentNode).classList.remove("hidden"); + } + shipmaster.setAttribute("required", "required"); + + if (steering_person.parentNode) { + (steering_person.parentNode).classList.remove("hidden"); + } + steering_person.setAttribute("required", "required"); + } 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: seats, + 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 = 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 = 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 = 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); - } - }); - } + callbackOnInit: function () { + this._currentState.items.forEach(function (obj) { + if (boat_in_ottensheim && obj.customProperties) { + if (obj.customProperties.is_racing) { + const coxSelect = ( + 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 = ( + 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 = ( + 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; + 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 = document.querySelector('#shipmaster-' + select.id + 'js'); - if (coxSelect){ - coxSelect.add(new Option(name, user_id)); - } - } - } - if (event.detail.customProperties.is_cox) { - const coxSelect = document.querySelector('#shipmaster-' + select.id + 'js'); - if (coxSelect){ - coxSelect.add(new Option(name, user_id)); - } - } + if (boat_in_ottensheim && event.detail.customProperties.is_racing) { + if (event.detail.customProperties.is_racing) { + const coxSelect = ( + document.querySelector("#shipmaster-" + select.id + "js") + ); + if (coxSelect) { + coxSelect.add(new Option(name, user_id)); + } + } + } + if (event.detail.customProperties.is_cox) { + const coxSelect = ( + document.querySelector("#shipmaster-" + select.id + "js") + ); + if (coxSelect) { + coxSelect.add(new Option(name, user_id)); + } + } - const steeringSelect = document.querySelector('#steering_person-' + select.id + 'js'); - if (steeringSelect) { - steeringSelect.add(new Option(name, user_id)); - } - },false); + const steeringSelect = ( + 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; + select.addEventListener( + "removeItem", + function (e) { + const event = e as ChoiceEvent; - const user_id = event.detail.value; + const user_id = event.detail.value; - const coxSelect = document.querySelector('#shipmaster-' + select.id + 'js'); - if (coxSelect) { - for (var i=0; i( + 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 = document.querySelector('#steering_person-' + select.id + 'js'); - if (steeringSelect) { - for (var i=0; i( + 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 = >document.querySelectorAll('.filter-trips-js'); + const btns = >( + document.querySelectorAll(".filter-trips-js") + ); let filterObject = new Map(); - if(btns) { + 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) { + + 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'); + if (entry[0] === btn.dataset.action && entry[1] !== "true") { + filterMap.set(entry[0], "true"); } else { - filterMap.set(entry[0],'false'); + filterMap.set(entry[0], "false"); } } - sessionStorage.setItem('tripsFilter', JSON.stringify( Array.from(filterMap.entries()))); + sessionStorage.setItem( + "tripsFilter", + JSON.stringify(Array.from(filterMap.entries())), + ); } resetFilteredElements(); - if(btn.getAttribute('aria-pressed') === 'false'){ + if (btn.getAttribute("aria-pressed") === "false") { Array.prototype.forEach.call(btns, (b: HTMLButtonElement) => { - b.setAttribute('aria-pressed', 'false'); + b.setAttribute("aria-pressed", "false"); }); triggerFilterAction(btn.dataset.action); } else { - btn.setAttribute('aria-pressed', 'false'); + btn.setAttribute("aria-pressed", "false"); } }); - }); + }); } - let filter = sessionStorage.getItem('tripsFilter'); - if(filter) { + let filter = sessionStorage.getItem("tripsFilter"); + if (filter) { let filterMap = new Map(JSON.parse(filter)); for (let entry of filterMap.entries()) { - if(entry[1] === 'true') { + if (entry[1] === "true") { triggerFilterAction(entry[0]); } } } else { - sessionStorage.setItem('tripsFilter', JSON.stringify( Array.from(filterObject.entries()))); + 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'); - }); + 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'); + 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': { + switch (activeFilter) { + case "filter-days": { filterDays(); break; } - case 'filter-coxs': { + case "filter-coxs": { filterCoxs(); break; } @@ -490,24 +569,24 @@ function filterAction(activeFilter: string) { function filterDays() { const daysNoTrips = document.querySelectorAll('div[data-trips="0"]'); Array.prototype.forEach.call(daysNoTrips, (day: HTMLElement) => { - day.classList.toggle('hidden'); + 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'); + notNeeded.classList.toggle("hidden"); }); } function initSearch() { - const input = document.querySelector('#filter-js'); + const input = document.querySelector("#filter-js"); - if(input) { + if (input) { filterElements(input.value); - input.addEventListener('input', () => { + input.addEventListener("input", () => { filterElements(input.value); }); } @@ -515,130 +594,160 @@ function initSearch() { function filterElements(input: string) { const elements = document.querySelectorAll('div[data-filterable="true"]'); - let resultWrapper = document.querySelector('#filter-result-js'), - amountShownElements = 0; + let resultWrapper = 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'; + element.style.display = "none"; // show if input matches - if(filterString?.includes(input.toLocaleLowerCase())) { - element.style.display = 'flex'; - amountShownElements ++; + if (filterString?.includes(input.toLocaleLowerCase())) { + element.style.display = "flex"; + amountShownElements++; } }); - if(resultWrapper) { - resultWrapper.innerHTML = (amountShownElements === 0 ? 'Kein Ergebnis gefunden' : '' + amountShownElements + '' + (amountShownElements > 1 ? ' Ergebnisse' : ' Ergebnis') + ' gefunden'); + if (resultWrapper) { + resultWrapper.innerHTML = + amountShownElements === 0 + ? "Kein Ergebnis gefunden" + : "" + + amountShownElements + + "" + + (amountShownElements > 1 ? " Ergebnisse" : " Ergebnis") + + " gefunden"; } } function initSidebar() { - const sidebarTrigger = >document.querySelectorAll('[data-trigger]'); + const sidebarTrigger = >( + document.querySelectorAll("[data-trigger]") + ); - if(sidebarTrigger) { - Array.prototype.forEach.call(sidebarTrigger, (triggerElement: HTMLElement) => { - if(triggerElement.dataset.trigger) { - const sidebar = new Sidebar(triggerElement.dataset.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); - } + triggerElement.addEventListener("click", (e) => { + e.preventDefault(); + if (triggerElement.dataset.trigger === "sidebar") { + initTripSidebar(triggerElement); + } - sidebar.toggle(); - }); - } - }); + sidebar.toggle(); + }); + } + }, + ); } } function initTripSidebar(triggerElement: HTMLElement) { - const sidebarElement = document.querySelector('#sidebar'); - if(sidebarElement && triggerElement.dataset.body && triggerElement.dataset.header) { + const sidebarElement = document.querySelector("#sidebar"); + if ( + sidebarElement && + triggerElement.dataset.body && + triggerElement.dataset.header + ) { let body = document.querySelector(triggerElement.dataset.body); let bodyElement = body.cloneNode(true); - let bodyContainerElement = sidebarElement.querySelector('.body-js'); + let bodyContainerElement = ( + sidebarElement.querySelector(".body-js") + ); - /* Quickfix duplicate ids checkboxes */ - const checkboxes = >bodyElement.querySelectorAll('input[type="checkbox"]'); + const checkboxes = >( + bodyElement.querySelectorAll('input[type="checkbox"]') + ); Array.prototype.forEach.call(checkboxes, (checkbox: HTMLElement) => { - if(checkbox) { - checkbox.parentElement?.setAttribute('for', checkbox.id + 'js'); - checkbox.id += 'js'; + if (checkbox) { + checkbox.parentElement?.setAttribute("for", checkbox.id + "js"); + checkbox.id += "js"; } }); - const prefixedContent = >bodyElement.querySelectorAll('.change-id-js'); + const prefixedContent = >( + bodyElement.querySelectorAll(".change-id-js") + ); Array.prototype.forEach.call(prefixedContent, (content: HTMLElement) => { - if(content) { - content.id += 'js'; + if (content) { + content.id += "js"; - if(content.dataset.relation){ - content.dataset.relation += 'js'; + if (content.dataset.relation) { + content.dataset.relation += "js"; } } }); - - if(bodyContainerElement) { - bodyContainerElement.innerHTML = ''; + if (bodyContainerElement) { + bodyContainerElement.innerHTML = ""; bodyContainerElement.append(bodyElement); addRelationMagic(bodyElement); } - if(triggerElement.dataset.day) { - let hiddenElement = bodyElement.querySelector('.day-js'); - if(hiddenElement) { + if (triggerElement.dataset.day) { + let hiddenElement = ( + bodyElement.querySelector(".day-js") + ); + if (hiddenElement) { hiddenElement.value = triggerElement.dataset.day; } } - let headerElement = sidebarElement.querySelector('.header-js'); - if(headerElement) { + 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) => { + const selects = bodyElement.querySelectorAll("select[multiple]"); + if (selects) { + Array.prototype.forEach.call(selects, (select: HTMLInputElement) => { initNewChoice(select); - }); - } + }); + } } - const defaultDateTimes = >document.querySelectorAll('.current-date-time'); - defaultDateTimes.forEach((defaultDateTime) => { - setCurrentdate(defaultDateTime); - }); - + const defaultDateTimes = >( + document.querySelectorAll(".current-date-time") + ); + defaultDateTimes.forEach((defaultDateTime) => { + setCurrentdate(defaultDateTime); + }); } function addRelationMagic(bodyElement: HTMLElement) { - const fields = bodyElement.querySelectorAll('.set-distance-js'); + const fields = bodyElement.querySelectorAll(".set-distance-js"); - if(fields) { + if (fields) { Array.prototype.forEach.call(fields, (field: HTMLInputElement) => { - if(field.dataset.relation){ - const relatedField = bodyElement.querySelector('#' + field.dataset.relation); - if(relatedField) { - field.addEventListener('input', (e) => { - e.preventDefault(); - const dataList = document.querySelector('#destinations'); - if(dataList) { - var option = Array.prototype.find.call(dataList.options, function(option) { + if (field.dataset.relation) { + const relatedField = ( + bodyElement.querySelector("#" + field.dataset.relation) + ); + if (relatedField) { + field.addEventListener("input", (e) => { + e.preventDefault(); + const dataList = ( + 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; - } + }, + ); + + // Get distance + const distance = option.getAttribute("distance"); + if (distance) relatedField.value = distance; + } }); } } @@ -647,8 +756,8 @@ function addRelationMagic(bodyElement: HTMLElement) { } function replaceStrings() { - const weekdays = document.querySelectorAll('.weekday-js'); + const weekdays = document.querySelectorAll(".weekday-js"); Array.prototype.forEach.call(weekdays, (weekday: HTMLElement) => { - weekday.innerHTML = weekday.innerHTML.replace('Freitag', 'Markttag'); + weekday.innerHTML = weekday.innerHTML.replace("Freitag", "Markttag"); }); } diff --git a/frontend/tests/cox.spec.ts b/frontend/tests/cox.spec.ts index b5c8637..54f8f1f 100644 --- a/frontend/tests/cox.spec.ts +++ b/frontend/tests/cox.spec.ts @@ -1,114 +1,144 @@ -import { test, expect, Page } from '@playwright/test'; +import { test, expect, Page } from "@playwright/test"; -test('cox can create and delete trip', async ({ page }) => { - await page.goto('http://localhost:8000/auth'); - await page.getByPlaceholder('Name').click(); - await page.getByPlaceholder('Name').fill('cox'); - await page.getByPlaceholder('Name').press('Tab'); - await page.getByPlaceholder('Passwort').fill('cox'); - await page.getByPlaceholder('Passwort').press('Enter'); - await page.getByRole('link', { name: 'Geplante Ausfahrten' }).click(); - await page.locator('.relative').first().click(); - await page.locator('#sidebar #planned_starting_time').click(); - await page.locator('#sidebar #planned_starting_time').fill('18:00'); - await page.locator('#sidebar #planned_starting_time').press('Tab'); - await page.locator('#sidebar #planned_starting_time').press('Tab'); - await page.getByRole('spinbutton').fill('5'); - await page.getByRole('button', { name: 'Erstellen', exact: true }).click(); - await expect(page.locator('body')).toContainText('18:00 Uhr (cox) Details'); +test("cox can create and delete trip", async ({ page }) => { + await page.goto("http://localhost:8000/auth"); + await page.getByPlaceholder("Name").click(); + await page.getByPlaceholder("Name").fill("cox"); + await page.getByPlaceholder("Name").press("Tab"); + await page.getByPlaceholder("Passwort").fill("cox"); + await page.getByPlaceholder("Passwort").press("Enter"); + await page.getByRole("link", { name: "Geplante Ausfahrten" }).click(); + await page.locator(".relative").first().click(); + await page.locator("#sidebar #planned_starting_time").click(); + await page.locator("#sidebar #planned_starting_time").fill("18:00"); + await page.locator("#sidebar #planned_starting_time").press("Tab"); + await page.locator("#sidebar #planned_starting_time").press("Tab"); + await page.getByRole("spinbutton").fill("5"); + await page.getByRole("button", { name: "Erstellen", exact: true }).click(); + await expect(page.locator("body")).toContainText("18:00 Uhr (cox) Details"); - await page.goto('http://localhost:8000/planned'); - await page.getByRole('link', { name: 'Details' }).click(); - await page.getByRole('link', { name: 'Termin löschen' }).click(); - await expect(page.locator('body')).toContainText('Erfolgreich gelöscht!'); + await page.goto("http://localhost:8000/planned"); + await page.getByRole("link", { name: "Details" }).click(); + await page.getByRole("link", { name: "Termin löschen" }).click(); + await expect(page.locator("body")).toContainText("Erfolgreich gelöscht!"); }); // TODO: group -> cox can create trips // TODO: cox can help/register at trips/events -test.describe('cox can edit trips', () => { +test.describe("cox can edit trips", () => { let sharedPage: Page; test.beforeEach(async ({ browser }) => { const page = await browser.newPage(); - await page.goto('http://localhost:8000/auth'); - await page.getByPlaceholder('Name').click(); - await page.getByPlaceholder('Name').fill('cox'); - await page.getByPlaceholder('Name').press('Tab'); - await page.getByPlaceholder('Passwort').fill('cox'); - await page.getByPlaceholder('Passwort').press('Enter'); - await page.getByRole('link', { name: 'Geplante Ausfahrten' }).click(); - await page.locator('.relative').first().click(); - await page.locator('#sidebar #planned_starting_time').click(); - await page.locator('#sidebar #planned_starting_time').fill('18:00'); - await page.locator('#sidebar #planned_starting_time').press('Tab'); - await page.locator('#sidebar #planned_starting_time').press('Tab'); - await page.getByRole('spinbutton').fill('5'); - await page.getByRole('button', { name: 'Erstellen', exact: true }).click(); + await page.goto("http://localhost:8000/auth"); + await page.getByPlaceholder("Name").click(); + await page.getByPlaceholder("Name").fill("cox"); + await page.getByPlaceholder("Name").press("Tab"); + await page.getByPlaceholder("Passwort").fill("cox"); + await page.getByPlaceholder("Passwort").press("Enter"); + await page.getByRole("link", { name: "Geplante Ausfahrten" }).click(); + await page.locator(".relative").first().click(); + await page.locator("#sidebar #planned_starting_time").click(); + await page.locator("#sidebar #planned_starting_time").fill("18:00"); + await page.locator("#sidebar #planned_starting_time").press("Tab"); + await page.locator("#sidebar #planned_starting_time").press("Tab"); + await page.getByRole("spinbutton").fill("5"); + await page.getByRole("button", { name: "Erstellen", exact: true }).click(); sharedPage = page; }); - test('edit remarks', async () => { - await sharedPage.goto('http://localhost:8000/planned'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await sharedPage.locator('#sidebar #notes').click(); - await sharedPage.locator('#sidebar #notes').fill('Meine Anmerkung'); - await sharedPage.getByRole('button', { name: 'Speichern' }).click(); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await expect(sharedPage.locator('#sidebar')).toContainText('Meine Anmerkung'); + test("edit remarks", async () => { + await sharedPage.goto("http://localhost:8000/planned"); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await sharedPage.locator("#sidebar #notes").click(); + await sharedPage.locator("#sidebar #notes").fill("Meine Anmerkung"); + await sharedPage.getByRole("button", { name: "Speichern" }).click(); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Meine Anmerkung", + ); - await sharedPage.getByRole('button', { name: 'Ausfahrt erstellen schließen' }).click(); + await sharedPage + .getByRole("button", { name: "Ausfahrt erstellen schließen" }) + .click(); }); - test('add and remove guest', async () => { - await sharedPage.goto('http://localhost:8000/planned'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await sharedPage.locator('#sidebar #user_note').click(); - await sharedPage.locator('#sidebar #user_note').fill('Mein Gast'); - await sharedPage.getByRole('button', { name: 'Gast hinzufügen' }).click(); - await expect(sharedPage.locator('body')).toContainText('Erfolgreich angemeldet!'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await expect(sharedPage.locator('#sidebar')).toContainText('Freie Plätze: 4'); - await expect(sharedPage.locator('#sidebar')).toContainText('Mein Gast (Gast) Abmelden'); - await expect(sharedPage.getByRole('link', { name: 'Termin löschen' })).not.toBeVisible(); + test("add and remove guest", async () => { + await sharedPage.goto("http://localhost:8000/planned"); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await sharedPage.locator("#sidebar #user_note").click(); + await sharedPage.locator("#sidebar #user_note").fill("Mein Gast"); + await sharedPage.getByRole("button", { name: "Gast hinzufügen" }).click(); + await expect(sharedPage.locator("body")).toContainText( + "Erfolgreich angemeldet!", + ); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Freie Plätze: 4", + ); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Mein Gast (Gast) Abmelden", + ); + await expect( + sharedPage.getByRole("link", { name: "Termin löschen" }), + ).not.toBeVisible(); - await sharedPage.getByRole('link', { name: 'Abmelden' }).click(); - await expect(sharedPage.locator('body')).toContainText('Erfolgreich abgemeldet!'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await expect(sharedPage.locator('#sidebar')).toContainText('Freie Plätze: 5'); - await expect(sharedPage.locator('#sidebar')).toContainText('Keine Ruderer angemeldet'); - await expect(sharedPage.getByRole('link', { name: 'Termin löschen' })).toBeVisible(); + await sharedPage.getByRole("link", { name: "Abmelden" }).click(); + await expect(sharedPage.locator("body")).toContainText( + "Erfolgreich abgemeldet!", + ); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Freie Plätze: 5", + ); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Keine Ruderer angemeldet", + ); + await expect( + sharedPage.getByRole("link", { name: "Termin löschen" }), + ).toBeVisible(); - await sharedPage.getByRole('button', { name: 'Ausfahrt erstellen schließen' }).click(); + await sharedPage + .getByRole("button", { name: "Ausfahrt erstellen schließen" }) + .click(); }); - test('change amount rower', async () => { - await sharedPage.goto('http://localhost:8000/planned'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await expect(sharedPage.locator('#sidebar')).toContainText('Freie Plätze: 5'); - await sharedPage.getByRole('spinbutton').click(); - await sharedPage.getByRole('spinbutton').fill('3'); - await sharedPage.getByRole('button', { name: 'Speichern' }).click(); - await expect(sharedPage.locator('body')).toContainText('Ausfahrt erfolgreich aktualisiert.'); + test("change amount rower", async () => { + await sharedPage.goto("http://localhost:8000/planned"); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Freie Plätze: 5", + ); + await sharedPage.getByRole("spinbutton").click(); + await sharedPage.getByRole("spinbutton").fill("3"); + await sharedPage.getByRole("button", { name: "Speichern" }).click(); + await expect(sharedPage.locator("body")).toContainText( + "Ausfahrt erfolgreich aktualisiert.", + ); }); - test('call off trip', async () => { - await sharedPage.goto('http://localhost:8000/planned'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await expect(sharedPage.locator('#sidebar')).toContainText('Freie Plätze: 5'); - await sharedPage.getByRole('spinbutton').click(); - await sharedPage.getByRole('spinbutton').fill('0'); - await sharedPage.getByRole('button', { name: 'Speichern' }).click(); - await expect(sharedPage.locator('body')).toContainText('Ausfahrt erfolgreich aktualisiert.'); - await expect(sharedPage.locator('body')).toContainText('(Absage cox )'); + test("call off trip", async () => { + await sharedPage.goto("http://localhost:8000/planned"); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await expect(sharedPage.locator("#sidebar")).toContainText( + "Freie Plätze: 5", + ); + await sharedPage.getByRole("spinbutton").click(); + await sharedPage.getByRole("spinbutton").fill("0"); + await sharedPage.getByRole("button", { name: "Speichern" }).click(); + await expect(sharedPage.locator("body")).toContainText( + "Ausfahrt erfolgreich aktualisiert.", + ); + await expect(sharedPage.locator("body")).toContainText("(Absage cox )"); }); test.afterEach(async () => { - await sharedPage.goto('http://localhost:8000/planned'); - await sharedPage.getByRole('link', { name: 'Details' }).click(); - await sharedPage.getByRole('link', { name: 'Termin löschen' }).click(); + await sharedPage.goto("http://localhost:8000/planned"); + await sharedPage.getByRole("link", { name: "Details" }).click(); + await sharedPage.getByRole("link", { name: "Termin löschen" }).click(); await sharedPage.close(); }); diff --git a/frontend/tests/log.spec.ts b/frontend/tests/log.spec.ts index f714b5c..e1ceb39 100644 --- a/frontend/tests/log.spec.ts +++ b/frontend/tests/log.spec.ts @@ -1,69 +1,85 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from "@playwright/test"; -test('Cox can start and cancel trip', async ({ page }, testInfo) => { - await page.goto('http://localhost:8000/auth'); - await page.getByPlaceholder('Name').click(); - await page.getByPlaceholder('Name').fill('cox2'); - await page.getByPlaceholder('Name').press('Tab'); - await page.getByPlaceholder('Passwort').fill('cox'); - await page.getByPlaceholder('Passwort').press('Enter'); +test("Cox can start and cancel trip", async ({ page }, testInfo) => { + await page.goto("http://localhost:8000/auth"); + await page.getByPlaceholder("Name").click(); + await page.getByPlaceholder("Name").fill("cox2"); + await page.getByPlaceholder("Name").press("Tab"); + await page.getByPlaceholder("Passwort").fill("cox"); + await page.getByPlaceholder("Passwort").press("Enter"); - await page.goto('http://localhost:8000/'); - await page.getByRole('link', { name: 'Ausfahrt eintragen' }).click(); - if (testInfo.project.name.includes('Mobile')) { // No left boat selector on mobile views - await page.getByText('Kaputtes Boot :-( (7 x)').nth(1).click(); - await page.getByRole('option', { name: 'Joe' }).click(); - } else{ - await page.getByText('Joe', { exact: true }).click(); + await page.goto("http://localhost:8000/"); + await page.getByRole("link", { name: "Ausfahrt eintragen" }).click(); + if (testInfo.project.name.includes("Mobile")) { + // No left boat selector on mobile views + await page.getByText("Kaputtes Boot :-( (7 x)").nth(1).click(); + await page.getByRole("option", { name: "Joe" }).click(); + } else { + await page.getByText("Joe", { exact: true }).click(); } - await page.getByPlaceholder('Ruderer auswählen').click(); - await page.getByRole('option', { name: 'rower2' }).click(); - await page.getByRole('option', { name: 'cox2' }).click(); - await expect(page.getByRole('listbox')).toContainText('Nur 2 Ruderer können hinzugefügt werden'); - await expect(page.locator('#shipmaster-newrowerjs')).toContainText('cox'); - await expect(page.locator('#steering_person-newrowerjs')).toContainText('rower2 cox'); - await page.getByRole('button', { name: 'Ausfahrt eintragen' }).click(); - await expect(page.locator('body')).toContainText('Ausfahrt erfolgreich hinzugefügt'); - await expect(page.locator('body')).toContainText('Joe'); + await page.getByPlaceholder("Ruderer auswählen").click(); + await page.getByRole("option", { name: "rower2" }).click(); + await page.getByRole("option", { name: "cox2" }).click(); + await expect(page.getByRole("listbox")).toContainText( + "Nur 2 Ruderer können hinzugefügt werden", + ); + await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); + await expect(page.locator("#steering_person-newrowerjs")).toContainText( + "rower2 cox", + ); + await page.getByRole("button", { name: "Ausfahrt eintragen" }).click(); + await expect(page.locator("body")).toContainText( + "Ausfahrt erfolgreich hinzugefügt", + ); + await expect(page.locator("body")).toContainText("Joe"); - await page.getByRole('link', { name: 'Joe' }).click(); - page.once('dialog', dialog => { + await page.getByRole("link", { name: "Joe" }).click(); + page.once("dialog", (dialog) => { dialog.accept().catch(() => {}); }); - await page.getByRole('link', { name: 'Löschen' }).click(); + await page.getByRole("link", { name: "Löschen" }).click(); }); -test('Cox can start and finish trip', async ({ page }, testInfo) => { - await page.goto('http://localhost:8000/auth'); - await page.getByPlaceholder('Name').click(); - await page.getByPlaceholder('Name').fill('cox2'); - await page.getByPlaceholder('Name').press('Tab'); - await page.getByPlaceholder('Passwort').fill('cox'); - await page.getByPlaceholder('Passwort').press('Enter'); +test("Cox can start and finish trip", async ({ page }, testInfo) => { + await page.goto("http://localhost:8000/auth"); + await page.getByPlaceholder("Name").click(); + await page.getByPlaceholder("Name").fill("cox2"); + await page.getByPlaceholder("Name").press("Tab"); + await page.getByPlaceholder("Passwort").fill("cox"); + await page.getByPlaceholder("Passwort").press("Enter"); - await page.goto('http://localhost:8000/'); - await page.getByRole('link', { name: 'Ausfahrt eintragen' }).click(); - if (testInfo.project.name.includes('Mobile')) { // No left boat selector on mobile views - await page.getByText('Kaputtes Boot :-( (7 x)').nth(1).click(); - await page.getByRole('option', { name: 'Joe' }).click(); - } else{ - await page.getByText('Joe', { exact: true }).click(); + await page.goto("http://localhost:8000/"); + await page.getByRole("link", { name: "Ausfahrt eintragen" }).click(); + if (testInfo.project.name.includes("Mobile")) { + // No left boat selector on mobile views + await page.getByText("Kaputtes Boot :-( (7 x)").nth(1).click(); + await page.getByRole("option", { name: "Joe" }).click(); + } else { + await page.getByText("Joe", { exact: true }).click(); } - await page.getByPlaceholder('Ruderer auswählen').click(); - await page.getByRole('option', { name: 'rower2' }).click(); - await page.getByRole('option', { name: 'cox2' }).click(); - await expect(page.getByRole('listbox')).toContainText('Nur 2 Ruderer können hinzugefügt werden'); - await expect(page.locator('#shipmaster-newrowerjs')).toContainText('cox'); - await expect(page.locator('#steering_person-newrowerjs')).toContainText('rower2 cox'); - await page.getByRole('button', { name: 'Ausfahrt eintragen' }).click(); - await expect(page.locator('body')).toContainText('Ausfahrt erfolgreich hinzugefügt'); - await expect(page.locator('body')).toContainText('Joe'); + await page.getByPlaceholder("Ruderer auswählen").click(); + await page.getByRole("option", { name: "rower2" }).click(); + await page.getByRole("option", { name: "cox2" }).click(); + await expect(page.getByRole("listbox")).toContainText( + "Nur 2 Ruderer können hinzugefügt werden", + ); + await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox"); + await expect(page.locator("#steering_person-newrowerjs")).toContainText( + "rower2 cox", + ); + await page.getByRole("button", { name: "Ausfahrt eintragen" }).click(); + await expect(page.locator("body")).toContainText( + "Ausfahrt erfolgreich hinzugefügt", + ); + await expect(page.locator("body")).toContainText("Joe"); - await page.goto('http://localhost:8000/log'); + await page.goto("http://localhost:8000/log"); await page.waitForTimeout(60000); - await page.locator('div:nth-child(2) > .border-0').click(); - await page.getByRole('combobox', { name: 'Destination' }).click(); - await page.getByRole('combobox', { name: 'Destination' }).fill('Ottensheim'); - await page.getByRole('button', { name: 'Ausfahrt beenden' }).click(); - await expect(page.locator('body')).toContainText('Ausfahrt korrekt eingetragen'); + await page.locator("div:nth-child(2) > .border-0").click(); + await page.getByRole("combobox", { name: "Destination" }).click(); + await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim"); + await page.getByRole("button", { name: "Ausfahrt beenden" }).click(); + await expect(page.locator("body")).toContainText( + "Ausfahrt korrekt eingetragen", + ); }); diff --git a/templates/admin/boat/index.html.tera b/templates/admin/boat/index.html.tera index c19f107..33127e1 100644 --- a/templates/admin/boat/index.html.tera +++ b/templates/admin/boat/index.html.tera @@ -1,21 +1,11 @@ {% import "includes/macros" as macros %} {% import "includes/forms/boat" as boat %} - {% extends "base" %} - {% block content %} - {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} - {% endif %} - -
-

Boats

- {{ boat::new() }} - - {% for boat in boats %} - {{ boat::edit(boat=boat, uuid=loop.index) }} - {% endfor %} - -
- + {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} +
+

Boats

+ {{ boat::new() }} + {% for boat in boats %}{{ boat::edit(boat=boat, uuid=loop.index) }}{% endfor %} +
{% endblock content %} diff --git a/templates/admin/mail.html.tera b/templates/admin/mail.html.tera index be16840..05a0b8f 100644 --- a/templates/admin/mail.html.tera +++ b/templates/admin/mail.html.tera @@ -1,27 +1,18 @@ {% import "includes/macros" as macros %} {% import "includes/forms/boat" as boat %} - {% extends "base" %} - {% block content %} - {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} - {% endif %} - -
-

Mail

-
- - - - - -
- -
- + {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} +
+

Mail

+
+ + + + + +
+
{% endblock content %} diff --git a/templates/admin/user/fees.html.tera b/templates/admin/user/fees.html.tera index 0a786ed..425ce4f 100644 --- a/templates/admin/user/fees.html.tera +++ b/templates/admin/user/fees.html.tera @@ -1,47 +1,44 @@ {% import "includes/macros" as macros %} - {% extends "base" %} - {% block content %} -
- {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} - {% endif %} - -

Gebühren

- - -
- - -
- - -
-
- - {% for fee in fees | sort(attribute="name") %} -
-
-
- {{ fee.name }} -
-
- {{ fee.sum_in_cents / 100 }}€: -
-
- {% for p in fee.parts %} - {{ p.0 }} ({{ p.1 / 100 }}€) {% if not loop.last %} + {% endif %} - {% endfor %} -
- {% if "admin" in loggedin_user.roles %} - Zahlungsstatus ändern - {% endif %} -
-
- {% endfor %} -
- -
- +
+ {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} +

Gebühren

+ +
+ + +
+ +
+
+ {% for fee in fees | sort(attribute="name") %} +
+
+
+ {{ fee.name }} +
+
{{ fee.sum_in_cents / 100 }}€:
+
+ {% for p in fee.parts %} + {{ p.0 }} ({{ p.1 / 100 }}€) + {% if not loop.last %}+{% endif %} + {% endfor %} +
+ {% if "admin" in loggedin_user.roles %} + Zahlungsstatus ändern + {% endif %} +
+
+ {% endfor %} +
+
{% endblock content %} diff --git a/templates/admin/user/index.html.tera b/templates/admin/user/index.html.tera index 619f838..932975d 100644 --- a/templates/admin/user/index.html.tera +++ b/templates/admin/user/index.html.tera @@ -1,98 +1,97 @@ {% import "includes/macros" as macros %} - {% extends "base" %} - {% block content %} -
- {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} - {% endif %} - -

Users

- - {% if allowed_to_edit %} -
-
-

Neuen User hinzufügen

-
-
- - -
-
-
-
- -
-
- {% endif %} - - -
- - -
- - -
-
- {% for user in users %} -
-
-
- -
{{ user.name }} - {% if user.last_access %} - (last access: - {{ user.last_access | date }}) - {% endif %} - {% if user.pw %} - Passwort zurücksetzen - {% endif %} -
-
- {% for role in roles %} - {{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }} - {% endfor%} - {{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Mitglied seit', name='member_since_date', id=loop.index, type="text", value=user.member_since_date, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Geburtsdatum', name='birthdate', id=loop.index, type="text", value=user.birthdate, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Mail', name='mail', id=loop.index, type="text", value=user.mail, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Nickname', name='nickname', id=loop.index, type="text", value=user.nickname, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Notizen', name='notes', id=loop.index, type="text", value=user.notes, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Telefon', name='phone', id=loop.index, type="text", value=user.phone, readonly=allowed_to_edit == false) }} - {{ macros::input(label='Adresse', name='address', id=loop.index, type="text", value=user.address, readonly=allowed_to_edit == false) }} - {% if allowed_to_edit %} - {{ macros::select(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen') }} - {% endif %} -
-
- {% if allowed_to_edit %} - - {% endif %} -
+
+ {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} +

Users

+ {% if allowed_to_edit %} +
+
+

Neuen User hinzufügen

+
+
+ + +
+
+
+
+ +
+
+ {% endif %} + +
+ +
- {% endfor %} -
- -
- + +
+
+ {% for user in users %} +
+
+
+ +
+ {{ user.name }} + {% if user.last_access %} + (last access: + {{ user.last_access | date }}) + {% endif %} + {% if user.pw %} + Passwort zurücksetzen + {% endif %} +
+
+ {% for role in roles %} + {{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }} + {% endfor %} + {{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Mitglied seit', name='member_since_date', id=loop.index, type="text", value=user.member_since_date, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Geburtsdatum', name='birthdate', id=loop.index, type="text", value=user.birthdate, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Mail', name='mail', id=loop.index, type="text", value=user.mail, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Nickname', name='nickname', id=loop.index, type="text", value=user.nickname, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Notizen', name='notes', id=loop.index, type="text", value=user.notes, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Telefon', name='phone', id=loop.index, type="text", value=user.phone, readonly=allowed_to_edit == false) }} + {{ macros::input(label='Adresse', name='address', id=loop.index, type="text", value=user.address, readonly=allowed_to_edit == false) }} + {% if allowed_to_edit %} + {{ macros::select(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen') }} + {% endif %} +
+
+ {% if allowed_to_edit %} + + {% endif %} +
+
+ {% endfor %} +
+
{% endblock content %} diff --git a/templates/auth/login.html.tera b/templates/auth/login.html.tera index cdd5980..a57c505 100644 --- a/templates/auth/login.html.tera +++ b/templates/auth/login.html.tera @@ -1,39 +1,34 @@ {% extends "base" %} - {% block content %} -
-
- Logo Ruderassistent -

Ruderassistent

-
- - {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} - {% endif %} -
- -
-
- {% if name %} - {{ macros::input(label='Name', name='name', type='input', required=true, class='rounded-t-md',hide_label=true,value=name, autofocus=true) }} - {% else %} - {{ macros::input(label='Name', name='name', type='input', required=true, class='rounded-t-md',hide_label=true, autofocus=true) }} - - {% endif %} -
-
- {{ macros::input(label='Passwort', name='password', type='password', class='rounded-b-md',hide_label=true) }} -
-
- -
- -
-
-
+
+
+ Logo Ruderassistent +

Ruderassistent

+
+ {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} +
+ +
+
+ {% if name %} + {{ macros::input(label='Name', name='name', type='input', required=true, class='rounded-t-md',hide_label=true,value=name, autofocus=true) }} + {% else %} + {{ macros::input(label='Name', name='name', type='input', required=true, class='rounded-t-md',hide_label=true, autofocus=true) }} + {% endif %} +
+
+ {{ macros::input(label='Passwort', name='password', type='password', class='rounded-b-md',hide_label=true) }} +
+
+
+ +
+
+
{% endblock content %} diff --git a/templates/auth/set-pw.html.tera b/templates/auth/set-pw.html.tera index 59a8981..cde1eaa 100644 --- a/templates/auth/set-pw.html.tera +++ b/templates/auth/set-pw.html.tera @@ -1,30 +1,25 @@ {% extends "base" %} - {% block content %} -
-

Passwort setzen

- -
- - -
-
- {{ macros::input(label='Passwort', name='password', type='password', required=true, class='rounded-t-md',hide_label=true) }} -
-
- {{ macros::input(label='Passwort bestätigen', name='password_confirm', type='password', required=true, class='rounded-b-md',hide_label=true) }} -
-
- -
- -
-
-
- +
+

Passwort setzen

+
+ + +
+
+ {{ macros::input(label='Passwort', name='password', type='password', required=true, class='rounded-t-md',hide_label=true) }} +
+
+ {{ macros::input(label='Passwort bestätigen', name='password_confirm', type='password', required=true, class='rounded-b-md',hide_label=true) }} +
+
+
+ +
+
+
{% endblock content %} diff --git a/templates/base.html.tera b/templates/base.html.tera index c33fefc..e9c6eb3 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -1,46 +1,40 @@ {% import "includes/macros" as macros %} - - - - - - - Ruderassistent - ASKÖ Ruderverein Donau Linz - - - {% if loggedin_user %} - {{ macros::header(loggedin_user=loggedin_user) }} - {% endif %} - {% if show_kiosk_header %} -
- - -
- {% endif %} - -
{% block content %}{% endblock content %} -
- - {% if loggedin_user %} - {% include "includes/footer" %} + + + + + + Ruderassistent - ASKÖ Ruderverein Donau Linz + + + {% if loggedin_user %}{{ macros::header(loggedin_user=loggedin_user) }}{% endif %} + {% if show_kiosk_header %} +
+
+ +
+ +
+ {% endif %} +
+ {% block content %} + {% endblock content %} +
+ {% if loggedin_user %} + {% include "includes/footer" %} {% endif %} - {% include "dynamics/sidebar" %} - - - - - - + + + + + diff --git a/templates/boatdamages.html.tera b/templates/boatdamages.html.tera index cdfcff1..d863c49 100644 --- a/templates/boatdamages.html.tera +++ b/templates/boatdamages.html.tera @@ -1,87 +1,107 @@ {% import "includes/macros" as macros %} {% import "includes/forms/log" as log %} - {% extends "base" %} - {% block content %} - -
-

Bootschäden

- - {% if flash %} - {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3 mt-3") }} - {% endif %} - -

- Neuen Schaden - - - {% include "includes/plus-icon" %} - Neuen Schaden eintragen - -

- -