Merge pull request 'single-user-edit-page' (#990) from single-user-edit-page into staging
Reviewed-on: #990
This commit was merged in pull request #990.
	This commit is contained in:
		| @@ -11,6 +11,10 @@ | ||||
|     @apply text-white hover:text-primary-100 underline; | ||||
|   } | ||||
|  | ||||
|   &-black { | ||||
|     @apply text-black hover:text-primary-950 dark:text-white hover:dark:text-primary-300 underline; | ||||
|   } | ||||
|  | ||||
|   &-no-underline { | ||||
|     @apply no-underline; | ||||
|   } | ||||
|   | ||||
| @@ -115,7 +115,7 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => { | ||||
|   await page.getByPlaceholder("Passwort").press("Enter"); | ||||
|  | ||||
|   await page.goto("/log/show"); | ||||
|   await page.getByText('(cox2)').click(); | ||||
|   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||
|   page.once("dialog", (dialog) => { | ||||
|     dialog.accept().catch(() => {}); | ||||
|   }); | ||||
| @@ -208,7 +208,6 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => { | ||||
|  | ||||
|   await page.getByRole('link', { name: 'Logbuch' }).click(); | ||||
|   await expect(page.locator('body')).toContainText('Joe'); | ||||
|   await expect(page.locator('body')).toContainText('(cox2)'); | ||||
|   await expect(page.locator('body')).toContainText('Ottensheim (25 km)'); | ||||
|   await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2'); | ||||
|    | ||||
| @@ -225,7 +224,7 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => { | ||||
|   await page.getByPlaceholder("Passwort").press("Enter"); | ||||
|  | ||||
|   await page.goto("/log/show"); | ||||
|   await page.getByText('(cox2)').click(); | ||||
|   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||
|   page.once("dialog", (dialog) => { | ||||
|     dialog.accept().catch(() => {}); | ||||
|   }); | ||||
| @@ -286,7 +285,6 @@ test("Cox can start and finish trip with cox steering only", async ({ page }, te | ||||
|  | ||||
|   await page.goto('/log/show'); | ||||
|   await expect(page.locator('body')).toContainText('cox_only_steering_boat'); | ||||
|   await expect(page.locator('body')).toContainText('(cox2 - handgesteuert)'); | ||||
|   await expect(page.locator('body')).toContainText('Ottensheim (25 km)'); | ||||
|    | ||||
|  | ||||
| @@ -302,7 +300,7 @@ test("Cox can start and finish trip with cox steering only", async ({ page }, te | ||||
|   await page.getByPlaceholder("Passwort").press("Enter"); | ||||
|  | ||||
|   await page.goto("/log/show"); | ||||
|   await page.getByText('(cox2 - handgesteuert)').click(); | ||||
|   await page.getByRole("link", { name: "cox_only_steering_boat" }).click(); | ||||
|   page.once("dialog", (dialog) => { | ||||
|     dialog.accept().catch(() => {}); | ||||
|   }); | ||||
| @@ -371,7 +369,7 @@ test("Kiosk can start and finish trip in one stop", async ({ page }, testInfo) = | ||||
|   await page.getByPlaceholder("Passwort").press("Enter"); | ||||
|  | ||||
|   await page.goto("/log/show"); | ||||
|   await page.getByText('(cox2)').click(); | ||||
|   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||
|   page.once("dialog", (dialog) => { | ||||
|     dialog.accept().catch(() => {}); | ||||
|   }); | ||||
|   | ||||
| @@ -414,12 +414,14 @@ impl User { | ||||
|         .await | ||||
|         .unwrap(); | ||||
|  | ||||
|         ActivityBuilder::new(&format!( | ||||
|             "{updated_by} hat die Rolle {role} von {self} entfernt." | ||||
|         )) | ||||
|         .relevant_for_user(self) | ||||
|         .save(db) | ||||
|         .await; | ||||
|         if !role.hide_in_lists && role.cluster.is_none() { | ||||
|             ActivityBuilder::new(&format!( | ||||
|                 "{updated_by} hat die Rolle {role} von {self} entfernt." | ||||
|             )) | ||||
|             .relevant_for_user(self) | ||||
|             .save(db) | ||||
|             .await; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| @@ -499,7 +501,7 @@ impl User { | ||||
|             ) | ||||
|         })?; | ||||
|  | ||||
|         if !role.hide_in_lists { | ||||
|         if !role.hide_in_lists && role.cluster.is_none() { | ||||
|             ActivityBuilder::new(&format!( | ||||
|                 "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." | ||||
|             )) | ||||
|   | ||||
| @@ -1,21 +1,20 @@ | ||||
| use std::{fmt::Display, ops::DerefMut}; | ||||
|  | ||||
| use argon2::{Argon2, PasswordHasher, password_hash::SaltString}; | ||||
| use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; | ||||
| use chrono::{Datelike, Local, NaiveDate}; | ||||
| use log::info; | ||||
| use rocket::async_trait; | ||||
| use rocket::{ | ||||
|     Request, | ||||
|     http::{Cookie, Status}, | ||||
|     request::{FromRequest, Outcome}, | ||||
|     time::{Duration, OffsetDateTime}, | ||||
|     Request, | ||||
| }; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; | ||||
|  | ||||
| use super::activity::ActivityBuilder; | ||||
| use super::{ | ||||
|     Day, | ||||
|     log::Log, | ||||
|     logbook::Logbook, | ||||
|     mail::Mail, | ||||
| @@ -24,6 +23,7 @@ use super::{ | ||||
|     role::Role, | ||||
|     stat::Stat, | ||||
|     tripdetails::TripDetails, | ||||
|     Day, | ||||
| }; | ||||
| use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; | ||||
| use scheckbuch::ScheckbuchUser; | ||||
| @@ -512,7 +512,7 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|             .save(db) | ||||
|             .await; | ||||
|             return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has | ||||
|             //been deleted | ||||
|                                                                 //been deleted | ||||
|         } | ||||
|  | ||||
|         if let Some(user_pw) = user.pw.as_ref() { | ||||
| @@ -577,10 +577,6 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|         .execute(db) | ||||
|         .await | ||||
|         .unwrap(); //Okay, because we can only create a User of a valid id | ||||
|         ActivityBuilder::new(&format!("User {self} hat sich eingeloggt.")) | ||||
|             .relevant_for_user(self) | ||||
|             .save(db) | ||||
|             .await; | ||||
|     } | ||||
|  | ||||
|     pub async fn delete(&self, db: &SqlitePool, deleted_by: &ManageUserUser) { | ||||
| @@ -622,9 +618,9 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|     pub(crate) async fn amount_days_to_show(&self, db: &SqlitePool) -> i64 { | ||||
|         if self.allowed_to_steer(db).await { | ||||
|             let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap(); //Ok, | ||||
|             //december | ||||
|             //has 31 | ||||
|             //days | ||||
|                                                                                              //december | ||||
|                                                                                              //has 31 | ||||
|                                                                                              //days | ||||
|             let days_left_in_year = end_of_year | ||||
|                 .signed_duration_since(Local::now().date_naive()) | ||||
|                 .num_days() | ||||
| @@ -633,9 +629,9 @@ ASKÖ Ruderverein Donau Linz", self.name), | ||||
|             if days_left_in_year <= 31 { | ||||
|                 let end_of_next_year = | ||||
|                     NaiveDate::from_ymd_opt(Local::now().year() + 1, 12, 31).unwrap(); //Ok, | ||||
|                 //december | ||||
|                 //has 31 | ||||
|                 //days | ||||
|                                                                                        //december | ||||
|                                                                                        //has 31 | ||||
|                                                                                        //days | ||||
|                 end_of_next_year | ||||
|                     .signed_duration_since(Local::now().date_naive()) | ||||
|                     .num_days() | ||||
| @@ -867,8 +863,8 @@ special_user!(SteeringUser, +"cox", +"Bootsführer"); | ||||
| special_user!(AdminUser, +"admin"); | ||||
| special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied"); | ||||
| special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO: | ||||
| // remove -> | ||||
| // RegularUser | ||||
|                                                                                        // remove -> | ||||
|                                                                                        // RegularUser | ||||
| special_user!(SchnupperBetreuerUser, +"schnupper-betreuer"); | ||||
| special_user!(VorstandUser, +"admin", +"Vorstand"); | ||||
| special_user!(EventUser, +"manage_events"); | ||||
| @@ -982,21 +978,17 @@ mod test { | ||||
|     #[sqlx::test] | ||||
|     fn wrong_pw() { | ||||
|         let pool = testdb!(); | ||||
|         assert!( | ||||
|             User::login(&pool, "admin".into(), "admi".into()) | ||||
|                 .await | ||||
|                 .is_err() | ||||
|         ); | ||||
|         assert!(User::login(&pool, "admin".into(), "admi".into()) | ||||
|             .await | ||||
|             .is_err()); | ||||
|     } | ||||
|  | ||||
|     #[sqlx::test] | ||||
|     fn wrong_username() { | ||||
|         let pool = testdb!(); | ||||
|         assert!( | ||||
|             User::login(&pool, "admi".into(), "admin".into()) | ||||
|                 .await | ||||
|                 .is_err() | ||||
|         ); | ||||
|         assert!(User::login(&pool, "admi".into(), "admin".into()) | ||||
|             .await | ||||
|             .is_err()); | ||||
|     } | ||||
|  | ||||
|     #[sqlx::test] | ||||
| @@ -1015,11 +1007,9 @@ mod test { | ||||
|         let pool = testdb!(); | ||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||
|  | ||||
|         assert!( | ||||
|             User::login(&pool, "admin".into(), "abc".into()) | ||||
|                 .await | ||||
|                 .is_err() | ||||
|         ); | ||||
|         assert!(User::login(&pool, "admin".into(), "abc".into()) | ||||
|             .await | ||||
|             .is_err()); | ||||
|  | ||||
|         user.update_pw(&pool, "abc".into()).await; | ||||
|  | ||||
|   | ||||
| @@ -7,11 +7,11 @@ use crate::{ | ||||
|         mail::valid_mails, | ||||
|         role::Role, | ||||
|         user::{ | ||||
|             AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, | ||||
|             UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, | ||||
|             clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member, | ||||
|             regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser, | ||||
|             schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser, | ||||
|             AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, | ||||
|             UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, | ||||
|         }, | ||||
|     }, | ||||
|     tera::Config, | ||||
| @@ -19,7 +19,6 @@ use crate::{ | ||||
| use chrono::NaiveDate; | ||||
| use futures::future::join_all; | ||||
| use rocket::{ | ||||
|     FromForm, Request, Route, State, | ||||
|     form::Form, | ||||
|     fs::TempFile, | ||||
|     get, | ||||
| @@ -27,9 +26,9 @@ use rocket::{ | ||||
|     post, | ||||
|     request::{FlashMessage, FromRequest, Outcome}, | ||||
|     response::{Flash, Redirect}, | ||||
|     routes, | ||||
|     routes, FromForm, Request, Route, State, | ||||
| }; | ||||
| use rocket_dyn_templates::{Template, tera::Context}; | ||||
| use rocket_dyn_templates::{tera::Context, Template}; | ||||
| use sqlx::SqlitePool; | ||||
|  | ||||
| // Custom request guard to extract the Referer header | ||||
| @@ -133,6 +132,12 @@ async fn view( | ||||
|             format!("User mit ID {} gibts ned", user), | ||||
|         )); | ||||
|     }; | ||||
|     if user.name == "Externe Steuerperson" { | ||||
|         return Err(Flash::error( | ||||
|             Redirect::to("/admin/user"), | ||||
|             "Diese besondere Person kannst du dir leider nicht anschauen, mein lieber neugieriger Ruderant!" | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     let member = Member::from(db, user.clone()).await; | ||||
|     let fee = user.fee(db).await; | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| use rocket::{ | ||||
|     FromForm, Request, Route, State, | ||||
|     form::Form, | ||||
|     get, | ||||
|     http::{Cookie, CookieJar}, | ||||
| @@ -9,11 +8,13 @@ use rocket::{ | ||||
|     response::{Flash, Redirect}, | ||||
|     routes, | ||||
|     time::{Duration, OffsetDateTime}, | ||||
|     FromForm, Request, Route, State, | ||||
| }; | ||||
| use rocket_dyn_templates::{Template, context, tera}; | ||||
| use rocket_dyn_templates::{context, tera, Template}; | ||||
| use sqlx::SqlitePool; | ||||
|  | ||||
| use crate::model::{ | ||||
|     activity::ActivityBuilder, | ||||
|     log::Log, | ||||
|     user::{LoginError, User}, | ||||
| }; | ||||
| @@ -82,13 +83,12 @@ async fn login( | ||||
|  | ||||
|     cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id))); | ||||
|  | ||||
|     Log::create( | ||||
|         db, | ||||
|         format!( | ||||
|             "Succ login of {} with this useragent: {}", | ||||
|             login.name, agent.0 | ||||
|         ), | ||||
|     ) | ||||
|     ActivityBuilder::new(&format!( | ||||
|         "{user} hat sich eingeloggt (User-Agent: {})", | ||||
|         agent.0 | ||||
|     )) | ||||
|     .relevant_for_user(&user) | ||||
|     .save(db) | ||||
|     .await; | ||||
|  | ||||
|     // Check for redirect_url cookie and redirect accordingly | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| use std::net::IpAddr; | ||||
|  | ||||
| use rocket::{ | ||||
|     Request, Route, State, | ||||
|     form::Form, | ||||
|     get, | ||||
|     http::{Cookie, CookieJar}, | ||||
| @@ -10,8 +9,9 @@ use rocket::{ | ||||
|     response::{Flash, Redirect}, | ||||
|     routes, | ||||
|     time::{Duration, OffsetDateTime}, | ||||
|     Request, Route, State, | ||||
| }; | ||||
| use rocket_dyn_templates::{Template, context}; | ||||
| use rocket_dyn_templates::{context, Template}; | ||||
| use sqlx::SqlitePool; | ||||
| use tera::Context; | ||||
|  | ||||
| @@ -110,10 +110,13 @@ async fn index( | ||||
| #[get("/show", rank = 3)] | ||||
| async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template { | ||||
|     let logs = Logbook::completed(db).await; | ||||
|     let boats = Boat::all(db).await; | ||||
|     let users = User::all(db).await; | ||||
|     let logtypes = LogType::all(db).await; | ||||
|  | ||||
|     Template::render( | ||||
|         "log.completed", | ||||
|         context!(logs, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await), | ||||
|         context!(logs, boats, users, logtypes, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await), | ||||
|     ) | ||||
| } | ||||
|  | ||||
| @@ -582,7 +585,7 @@ mod test { | ||||
|     use sqlx::SqlitePool; | ||||
|  | ||||
|     use crate::model::logbook::Logbook; | ||||
|     use crate::tera::{User, log::Boat}; | ||||
|     use crate::tera::{log::Boat, User}; | ||||
|     use crate::testdb; | ||||
|  | ||||
|     #[sqlx::test] | ||||
|   | ||||
| @@ -12,13 +12,13 @@ | ||||
|                 <div class="grid sm:grid-cols-3 gap-3 mt-3"> | ||||
|                       <button type="button" | ||||
|                               onclick="document.getElementById('add-clubuser').showModal()" | ||||
|                               class="btn btn-primary">Vereinsmitglied</button> | ||||
|                               class="btn btn-primary">🥳 Vereinsmitglied</button> | ||||
|                       <button type="button" | ||||
|                               onclick="document.getElementById('add-scheckbuch').showModal()" | ||||
|                               class="btn btn-dark">Scheckbuch</button> | ||||
|                               class="btn btn-dark">🧑🏫 Scheckbuch</button> | ||||
|                       <button type="button" | ||||
|                               onclick="document.getElementById('add-schnupperkurs').showModal()" | ||||
|                               class="btn btn-dark">Schnupperkurs</button> | ||||
|                               class="btn btn-dark">👨🎓 Schnupperkurs</button> | ||||
|  | ||||
|  | ||||
|                       </div> | ||||
|   | ||||
| @@ -4,11 +4,13 @@ | ||||
| {% block content %} | ||||
|     <div class="max-w-screen-lg w-full"> | ||||
|         {% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %} | ||||
|             <a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a> | ||||
|             <div class="mb-5 lg:mb-0"> | ||||
|                 <a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|         <h1 class="h1">{{ user.name }}</h1> | ||||
|         <div class="grid sm:grid-cols-2 gap-3"> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|         <div class="grid sm:grid-cols-2 gap-8 my-8"> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                 <h2 class="h2"> | ||||
|                     Grunddaten | ||||
|                     <br /> | ||||
| @@ -53,7 +55,7 @@ | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                 <h2 class="h2"> | ||||
|                     Mitgliedschaft | ||||
|                     <br /> | ||||
| @@ -119,12 +121,12 @@ | ||||
|                         </div> | ||||
|                         {% if allowed_to_edit %} | ||||
|                             <div class="py-3"> | ||||
|                                 <div class="mt-3 text-right"> | ||||
|                                 <div class="text-right"> | ||||
|                                     <button type="button" | ||||
|                                             onclick="document.getElementById('change-member-type').showModal()" | ||||
|                                             class="btn btn-dark">Mitgliedsstatus ändern</button> | ||||
|                                     <a href="/admin/user/{{ user.id }}/delete" | ||||
|                                        class="btn btn-alert" | ||||
|                                        class="btn btn-alert mt-3" | ||||
|                                        onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');"> | ||||
|                                         {% include "includes/delete-icon" %} | ||||
|                                         Mitglied ist ausgetreten | ||||
| @@ -285,7 +287,7 @@ | ||||
|                 </div> | ||||
|             </div> | ||||
|             {% if is_clubmember %} | ||||
|                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                     <h2 class="h2">Rollen</h2> | ||||
|                     <div> | ||||
|                         <ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full"> | ||||
| @@ -363,7 +365,7 @@ | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|             {% if supposed_to_pay %} | ||||
|                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                     <h2 class="h2">💸-Beitrag</h2> | ||||
|                     <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> | ||||
|                         <div class="py-3"> | ||||
| @@ -400,13 +402,13 @@ | ||||
|                     </div> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                 <h2 class="h2">Aktivitäten</h2> | ||||
|                 <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> | ||||
|                 <div class="mx-3 max-h-60 overflow-y-scroll"> | ||||
|                     <div class="py-3"> | ||||
|                         <ul class="list-disc ms-4"> | ||||
|                             {% for activity in activities %} | ||||
|                                 <li>{{ activity.created_at | date(format="%d. %m. %Y") }}: {{ activity.text }}</li> | ||||
|                                 <li><strong>{{ activity.created_at | date(format="%d. %m. %Y") }}:</strong> <small>{{ activity.text }}</small></li> | ||||
|                             {% else %} | ||||
|                                 <li>Noch keine Aktivität... Stay tuned 😆</li> | ||||
|                             {% endfor %} | ||||
| @@ -414,13 +416,13 @@ | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"> | ||||
|             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||
|                 <h2 class="h2">Ergo-Challenge</h2> | ||||
|                 <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> | ||||
|                     <div class="py-3"> | ||||
|                         {{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }} | ||||
|                         {{ macros::input(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }} | ||||
|                         {{ macros::input(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }} | ||||
|                 <div class="mx-3"> | ||||
|                     <div class="grid gap-3 pb-3 mt-3"> | ||||
|                         {{ macros::inputgroup(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }} | ||||
|                         {{ macros::inputgroup(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }} | ||||
|                         {{ macros::inputgroup(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|   | ||||
| @@ -183,8 +183,6 @@ | ||||
|     <div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative" | ||||
|          data-filterable="true" | ||||
|          data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}"> | ||||
|         <details> | ||||
|             <summary style="list-style: none;"> | ||||
|                 {% if log.logtype and not hide_type %} | ||||
|                     <div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold"> | ||||
|                         {% if log.logtype == 1 %} | ||||
| @@ -199,7 +197,15 @@ | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|                 <div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}> | ||||
|                     <strong class="text-black dark:text-white">{{ log.boat.name }}</strong> | ||||
|                      {% if allowed_to_edit %} | ||||
|                         <a href="#" | ||||
|                               onclick="document.getElementById('change-{{ log.id }}').showModal()" | ||||
|                               class="link link-black font-bold">{{ log.boat.name }}</a> | ||||
|                      {% else %} | ||||
|                         <strong class="text-black dark:text-white"> | ||||
|                               {{ log.boat.name }} | ||||
|                         </strong> | ||||
|                      {% endif %} | ||||
|                     <small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}} | ||||
|                         {% if log.shipmaster_only_steering %} | ||||
|                             - handgesteuert | ||||
| @@ -252,35 +258,52 @@ | ||||
|                             {% endif %} | ||||
|                         {% endif %} | ||||
|                     </div> | ||||
|                 </summary> | ||||
|                 {% if allowed_to_edit %} | ||||
|                     <form action="/log/update" method="post"> | ||||
|             {% if allowed_to_edit %} | ||||
|                       <dialog id="change-{{ log.id }}" | ||||
|                               class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md" | ||||
|                               onclick="document.getElementById('change-{{ log.id }}').close()"> | ||||
|                           <div onclick="event.stopPropagation();" class="p-3"> | ||||
|                               <button type="button" | ||||
|                                       onclick="document.getElementById('change-{{ log.id }}').close()" | ||||
|                                       title="Schließen" | ||||
|                                       class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3"> | ||||
|                                   <svg class="inline h-5 w-5" | ||||
|                                         width="16" | ||||
|                                         height="16" | ||||
|                                         fill="currentColor" | ||||
|                                         viewBox="0 0 16 16"> | ||||
|                                       <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path> | ||||
|                                   </svg> | ||||
|                               </button> | ||||
|                               <div class="mt-8"> | ||||
|                               <h2 class="h3">Eintrag '{{ log.boat.name }}' ändern </h2> | ||||
|                               <p class="text-center mb-3">{{ log.id }}</p> | ||||
|                                <form action="/log/update" method="post" class="grid gap-3"> | ||||
|                         <input type="hidden" name="id" value="{{ log.id }}" /> | ||||
|                         <input type="hidden" name="boat_id" value="{{ log.boat_id }}" /> | ||||
|                         <input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" /> | ||||
|                         <input type="hidden" | ||||
|                                name="steering_person" | ||||
|                                value="{{ log.steering_person }}" /> | ||||
|                         Handgesteuert: | ||||
|                         <input type="checkbox" | ||||
|                                name="shipmaster_only_steering" | ||||
|                                {% if log.shipmaster_only_steering %}checked="checked"{% endif %} /> | ||||
|                         <input type="datetime-local" name="departure" value="{{ log.departure }}" /> | ||||
|                         <input type="datetime-local" name="arrival" value="{{ log.arrival }}" /> | ||||
|                         {{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }} | ||||
|                         <input type="datetime-local" class="input rounded-md" name="departure" value="{{ log.departure }}" /> | ||||
|                         <input type="datetime-local" class="input rounded-md" name="arrival" value="{{ log.arrival }}" /> | ||||
|                         <input type="hidden" name="destination" value="{{ log.destination }}" /> | ||||
|                         <input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" /> | ||||
|                         <input type="hidden" name="comments" value="{{ log.comments }}" /> | ||||
|                         <input type="hidden" name="logtype" value="{{ log.logtype }}" /> | ||||
|                         <input type="submit" value="Updaten" /> | ||||
|                         <input type="submit" class="btn btn-primary" value="Updaten" /> | ||||
|                     </form> | ||||
|                     <a href="/log/{{ log.id }}/delete" | ||||
|                        class="w-28 btn btn-alert" | ||||
|                        class="w-28 btn btn-alert mt-3" | ||||
|                        onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');"> | ||||
|                         {% include "includes/delete-icon" %} | ||||
|                         Löschen | ||||
|                     </a> | ||||
|                 {% endif %} | ||||
|             </details> | ||||
|                         </div> | ||||
|                         </div> | ||||
|                       </dialog> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     {% endmacro show_old %} | ||||
|     {% macro home(log) %} | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
|             {% for log in logs %} | ||||
|                 {% set_global allowed_to_edit = false %} | ||||
|                 {% if loggedin_user %} | ||||
|                     {% if "Vorstand" in loggedin_user.roles %} | ||||
|                     {% if "Vorstand" in loggedin_user.roles or "admin" in loggedin_user.roles %} | ||||
|                         {% set_global allowed_to_edit = true %} | ||||
|                     {% endif %} | ||||
|                 {% endif %} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user