single-user-edit-page #990
| @@ -11,6 +11,10 @@ | |||||||
|     @apply text-white hover:text-primary-100 underline; |     @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 { |   &-no-underline { | ||||||
|     @apply 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.getByPlaceholder("Passwort").press("Enter"); | ||||||
|  |  | ||||||
|   await page.goto("/log/show"); |   await page.goto("/log/show"); | ||||||
|   await page.getByText('(cox2)').click(); |   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||||
|   page.once("dialog", (dialog) => { |   page.once("dialog", (dialog) => { | ||||||
|     dialog.accept().catch(() => {}); |     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 page.getByRole('link', { name: 'Logbuch' }).click(); | ||||||
|   await expect(page.locator('body')).toContainText('Joe'); |   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('Ottensheim (25 km)'); | ||||||
|   await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2'); |   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.getByPlaceholder("Passwort").press("Enter"); | ||||||
|  |  | ||||||
|   await page.goto("/log/show"); |   await page.goto("/log/show"); | ||||||
|   await page.getByText('(cox2)').click(); |   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||||
|   page.once("dialog", (dialog) => { |   page.once("dialog", (dialog) => { | ||||||
|     dialog.accept().catch(() => {}); |     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 page.goto('/log/show'); | ||||||
|   await expect(page.locator('body')).toContainText('cox_only_steering_boat'); |   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)'); |   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.getByPlaceholder("Passwort").press("Enter"); | ||||||
|  |  | ||||||
|   await page.goto("/log/show"); |   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) => { |   page.once("dialog", (dialog) => { | ||||||
|     dialog.accept().catch(() => {}); |     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.getByPlaceholder("Passwort").press("Enter"); | ||||||
|  |  | ||||||
|   await page.goto("/log/show"); |   await page.goto("/log/show"); | ||||||
|   await page.getByText('(cox2)').click(); |   await page.getByRole('link', { name: 'Joe' }).nth(1).click(); | ||||||
|   page.once("dialog", (dialog) => { |   page.once("dialog", (dialog) => { | ||||||
|     dialog.accept().catch(() => {}); |     dialog.accept().catch(() => {}); | ||||||
|   }); |   }); | ||||||
|   | |||||||
| @@ -414,12 +414,14 @@ impl User { | |||||||
|         .await |         .await | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|  |         if !role.hide_in_lists && role.cluster.is_none() { | ||||||
|             ActivityBuilder::new(&format!( |             ActivityBuilder::new(&format!( | ||||||
|                 "{updated_by} hat die Rolle {role} von {self} entfernt." |                 "{updated_by} hat die Rolle {role} von {self} entfernt." | ||||||
|             )) |             )) | ||||||
|             .relevant_for_user(self) |             .relevant_for_user(self) | ||||||
|             .save(db) |             .save(db) | ||||||
|             .await; |             .await; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -499,7 +501,7 @@ impl User { | |||||||
|             ) |             ) | ||||||
|         })?; |         })?; | ||||||
|  |  | ||||||
|         if !role.hide_in_lists { |         if !role.hide_in_lists && role.cluster.is_none() { | ||||||
|             ActivityBuilder::new(&format!( |             ActivityBuilder::new(&format!( | ||||||
|                 "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." |                 "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." | ||||||
|             )) |             )) | ||||||
|   | |||||||
| @@ -1,21 +1,20 @@ | |||||||
| use std::{fmt::Display, ops::DerefMut}; | 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 chrono::{Datelike, Local, NaiveDate}; | ||||||
| use log::info; | use log::info; | ||||||
| use rocket::async_trait; | use rocket::async_trait; | ||||||
| use rocket::{ | use rocket::{ | ||||||
|     Request, |  | ||||||
|     http::{Cookie, Status}, |     http::{Cookie, Status}, | ||||||
|     request::{FromRequest, Outcome}, |     request::{FromRequest, Outcome}, | ||||||
|     time::{Duration, OffsetDateTime}, |     time::{Duration, OffsetDateTime}, | ||||||
|  |     Request, | ||||||
| }; | }; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; | use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; | ||||||
|  |  | ||||||
| use super::activity::ActivityBuilder; | use super::activity::ActivityBuilder; | ||||||
| use super::{ | use super::{ | ||||||
|     Day, |  | ||||||
|     log::Log, |     log::Log, | ||||||
|     logbook::Logbook, |     logbook::Logbook, | ||||||
|     mail::Mail, |     mail::Mail, | ||||||
| @@ -24,6 +23,7 @@ use super::{ | |||||||
|     role::Role, |     role::Role, | ||||||
|     stat::Stat, |     stat::Stat, | ||||||
|     tripdetails::TripDetails, |     tripdetails::TripDetails, | ||||||
|  |     Day, | ||||||
| }; | }; | ||||||
| use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; | use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; | ||||||
| use scheckbuch::ScheckbuchUser; | use scheckbuch::ScheckbuchUser; | ||||||
| @@ -577,10 +577,6 @@ ASKÖ Ruderverein Donau Linz", self.name), | |||||||
|         .execute(db) |         .execute(db) | ||||||
|         .await |         .await | ||||||
|         .unwrap(); //Okay, because we can only create a User of a valid id |         .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) { |     pub async fn delete(&self, db: &SqlitePool, deleted_by: &ManageUserUser) { | ||||||
| @@ -982,21 +978,17 @@ mod test { | |||||||
|     #[sqlx::test] |     #[sqlx::test] | ||||||
|     fn wrong_pw() { |     fn wrong_pw() { | ||||||
|         let pool = testdb!(); |         let pool = testdb!(); | ||||||
|         assert!( |         assert!(User::login(&pool, "admin".into(), "admi".into()) | ||||||
|             User::login(&pool, "admin".into(), "admi".into()) |  | ||||||
|             .await |             .await | ||||||
|                 .is_err() |             .is_err()); | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[sqlx::test] |     #[sqlx::test] | ||||||
|     fn wrong_username() { |     fn wrong_username() { | ||||||
|         let pool = testdb!(); |         let pool = testdb!(); | ||||||
|         assert!( |         assert!(User::login(&pool, "admi".into(), "admin".into()) | ||||||
|             User::login(&pool, "admi".into(), "admin".into()) |  | ||||||
|             .await |             .await | ||||||
|                 .is_err() |             .is_err()); | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[sqlx::test] |     #[sqlx::test] | ||||||
| @@ -1015,11 +1007,9 @@ mod test { | |||||||
|         let pool = testdb!(); |         let pool = testdb!(); | ||||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); |         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||||
|  |  | ||||||
|         assert!( |         assert!(User::login(&pool, "admin".into(), "abc".into()) | ||||||
|             User::login(&pool, "admin".into(), "abc".into()) |  | ||||||
|             .await |             .await | ||||||
|                 .is_err() |             .is_err()); | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         user.update_pw(&pool, "abc".into()).await; |         user.update_pw(&pool, "abc".into()).await; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,11 +7,11 @@ use crate::{ | |||||||
|         mail::valid_mails, |         mail::valid_mails, | ||||||
|         role::Role, |         role::Role, | ||||||
|         user::{ |         user::{ | ||||||
|             AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, |  | ||||||
|             UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, |  | ||||||
|             clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member, |             clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member, | ||||||
|             regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser, |             regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser, | ||||||
|             schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser, |             schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser, | ||||||
|  |             AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, | ||||||
|  |             UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     tera::Config, |     tera::Config, | ||||||
| @@ -19,7 +19,6 @@ use crate::{ | |||||||
| use chrono::NaiveDate; | use chrono::NaiveDate; | ||||||
| use futures::future::join_all; | use futures::future::join_all; | ||||||
| use rocket::{ | use rocket::{ | ||||||
|     FromForm, Request, Route, State, |  | ||||||
|     form::Form, |     form::Form, | ||||||
|     fs::TempFile, |     fs::TempFile, | ||||||
|     get, |     get, | ||||||
| @@ -27,9 +26,9 @@ use rocket::{ | |||||||
|     post, |     post, | ||||||
|     request::{FlashMessage, FromRequest, Outcome}, |     request::{FlashMessage, FromRequest, Outcome}, | ||||||
|     response::{Flash, Redirect}, |     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; | use sqlx::SqlitePool; | ||||||
|  |  | ||||||
| // Custom request guard to extract the Referer header | // Custom request guard to extract the Referer header | ||||||
| @@ -133,6 +132,12 @@ async fn view( | |||||||
|             format!("User mit ID {} gibts ned", user), |             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 member = Member::from(db, user.clone()).await; | ||||||
|     let fee = user.fee(db).await; |     let fee = user.fee(db).await; | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| use rocket::{ | use rocket::{ | ||||||
|     FromForm, Request, Route, State, |  | ||||||
|     form::Form, |     form::Form, | ||||||
|     get, |     get, | ||||||
|     http::{Cookie, CookieJar}, |     http::{Cookie, CookieJar}, | ||||||
| @@ -9,11 +8,13 @@ use rocket::{ | |||||||
|     response::{Flash, Redirect}, |     response::{Flash, Redirect}, | ||||||
|     routes, |     routes, | ||||||
|     time::{Duration, OffsetDateTime}, |     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 sqlx::SqlitePool; | ||||||
|  |  | ||||||
| use crate::model::{ | use crate::model::{ | ||||||
|  |     activity::ActivityBuilder, | ||||||
|     log::Log, |     log::Log, | ||||||
|     user::{LoginError, User}, |     user::{LoginError, User}, | ||||||
| }; | }; | ||||||
| @@ -82,13 +83,12 @@ async fn login( | |||||||
|  |  | ||||||
|     cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id))); |     cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id))); | ||||||
|  |  | ||||||
|     Log::create( |     ActivityBuilder::new(&format!( | ||||||
|         db, |         "{user} hat sich eingeloggt (User-Agent: {})", | ||||||
|         format!( |         agent.0 | ||||||
|             "Succ login of {} with this useragent: {}", |     )) | ||||||
|             login.name, agent.0 |     .relevant_for_user(&user) | ||||||
|         ), |     .save(db) | ||||||
|     ) |  | ||||||
|     .await; |     .await; | ||||||
|  |  | ||||||
|     // Check for redirect_url cookie and redirect accordingly |     // Check for redirect_url cookie and redirect accordingly | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| use std::net::IpAddr; | use std::net::IpAddr; | ||||||
|  |  | ||||||
| use rocket::{ | use rocket::{ | ||||||
|     Request, Route, State, |  | ||||||
|     form::Form, |     form::Form, | ||||||
|     get, |     get, | ||||||
|     http::{Cookie, CookieJar}, |     http::{Cookie, CookieJar}, | ||||||
| @@ -10,8 +9,9 @@ use rocket::{ | |||||||
|     response::{Flash, Redirect}, |     response::{Flash, Redirect}, | ||||||
|     routes, |     routes, | ||||||
|     time::{Duration, OffsetDateTime}, |     time::{Duration, OffsetDateTime}, | ||||||
|  |     Request, Route, State, | ||||||
| }; | }; | ||||||
| use rocket_dyn_templates::{Template, context}; | use rocket_dyn_templates::{context, Template}; | ||||||
| use sqlx::SqlitePool; | use sqlx::SqlitePool; | ||||||
| use tera::Context; | use tera::Context; | ||||||
|  |  | ||||||
| @@ -110,10 +110,13 @@ async fn index( | |||||||
| #[get("/show", rank = 3)] | #[get("/show", rank = 3)] | ||||||
| async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template { | async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template { | ||||||
|     let logs = Logbook::completed(db).await; |     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( |     Template::render( | ||||||
|         "log.completed", |         "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 sqlx::SqlitePool; | ||||||
|  |  | ||||||
|     use crate::model::logbook::Logbook; |     use crate::model::logbook::Logbook; | ||||||
|     use crate::tera::{User, log::Boat}; |     use crate::tera::{log::Boat, User}; | ||||||
|     use crate::testdb; |     use crate::testdb; | ||||||
|  |  | ||||||
|     #[sqlx::test] |     #[sqlx::test] | ||||||
|   | |||||||
| @@ -12,13 +12,13 @@ | |||||||
|                 <div class="grid sm:grid-cols-3 gap-3 mt-3"> |                 <div class="grid sm:grid-cols-3 gap-3 mt-3"> | ||||||
|                       <button type="button" |                       <button type="button" | ||||||
|                               onclick="document.getElementById('add-clubuser').showModal()" |                               onclick="document.getElementById('add-clubuser').showModal()" | ||||||
|                               class="btn btn-primary">Vereinsmitglied</button> |                               class="btn btn-primary">🥳 Vereinsmitglied</button> | ||||||
|                       <button type="button" |                       <button type="button" | ||||||
|                               onclick="document.getElementById('add-scheckbuch').showModal()" |                               onclick="document.getElementById('add-scheckbuch').showModal()" | ||||||
|                               class="btn btn-dark">Scheckbuch</button> |                               class="btn btn-dark">🧑🏫 Scheckbuch</button> | ||||||
|                       <button type="button" |                       <button type="button" | ||||||
|                               onclick="document.getElementById('add-schnupperkurs').showModal()" |                               onclick="document.getElementById('add-schnupperkurs').showModal()" | ||||||
|                               class="btn btn-dark">Schnupperkurs</button> |                               class="btn btn-dark">👨🎓 Schnupperkurs</button> | ||||||
|  |  | ||||||
|  |  | ||||||
|                       </div> |                       </div> | ||||||
|   | |||||||
| @@ -4,11 +4,13 @@ | |||||||
| {% block content %} | {% block content %} | ||||||
|     <div class="max-w-screen-lg w-full"> |     <div class="max-w-screen-lg w-full"> | ||||||
|         {% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %} |         {% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %} | ||||||
|  |             <div class="mb-5 lg:mb-0"> | ||||||
|                 <a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a> |                 <a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a> | ||||||
|  |             </div> | ||||||
|         {% endif %} |         {% endif %} | ||||||
|         <h1 class="h1">{{ user.name }}</h1> |         <h1 class="h1">{{ user.name }}</h1> | ||||||
|         <div class="grid sm:grid-cols-2 gap-3"> |         <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 mt-5"> |             <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow"> | ||||||
|                 <h2 class="h2"> |                 <h2 class="h2"> | ||||||
|                     Grunddaten |                     Grunddaten | ||||||
|                     <br /> |                     <br /> | ||||||
| @@ -53,7 +55,7 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </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"> |                 <h2 class="h2"> | ||||||
|                     Mitgliedschaft |                     Mitgliedschaft | ||||||
|                     <br /> |                     <br /> | ||||||
| @@ -119,12 +121,12 @@ | |||||||
|                         </div> |                         </div> | ||||||
|                         {% if allowed_to_edit %} |                         {% if allowed_to_edit %} | ||||||
|                             <div class="py-3"> |                             <div class="py-3"> | ||||||
|                                 <div class="mt-3 text-right"> |                                 <div class="text-right"> | ||||||
|                                     <button type="button" |                                     <button type="button" | ||||||
|                                             onclick="document.getElementById('change-member-type').showModal()" |                                             onclick="document.getElementById('change-member-type').showModal()" | ||||||
|                                             class="btn btn-dark">Mitgliedsstatus ändern</button> |                                             class="btn btn-dark">Mitgliedsstatus ändern</button> | ||||||
|                                     <a href="/admin/user/{{ user.id }}/delete" |                                     <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?');"> |                                        onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');"> | ||||||
|                                         {% include "includes/delete-icon" %} |                                         {% include "includes/delete-icon" %} | ||||||
|                                         Mitglied ist ausgetreten |                                         Mitglied ist ausgetreten | ||||||
| @@ -285,7 +287,7 @@ | |||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             {% if is_clubmember %} |             {% 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> |                     <h2 class="h2">Rollen</h2> | ||||||
|                     <div> |                     <div> | ||||||
|                         <ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full"> |                         <ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full"> | ||||||
| @@ -363,7 +365,7 @@ | |||||||
|                 </div> |                 </div> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|             {% if supposed_to_pay %} |             {% 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> |                     <h2 class="h2">💸-Beitrag</h2> | ||||||
|                     <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> |                     <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> | ||||||
|                         <div class="py-3"> |                         <div class="py-3"> | ||||||
| @@ -400,13 +402,13 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|             {% endif %} |             {% 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> |                 <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"> |                     <div class="py-3"> | ||||||
|                         <ul class="list-disc ms-4"> |                         <ul class="list-disc ms-4"> | ||||||
|                             {% for activity in activities %} |                             {% 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 %} |                             {% else %} | ||||||
|                                 <li>Noch keine Aktivität... Stay tuned 😆</li> |                                 <li>Noch keine Aktivität... Stay tuned 😆</li> | ||||||
|                             {% endfor %} |                             {% endfor %} | ||||||
| @@ -414,13 +416,13 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </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> |                 <h2 class="h2">Ergo-Challenge</h2> | ||||||
|                 <div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600"> |                 <div class="mx-3"> | ||||||
|                     <div class="py-3"> |                     <div class="grid gap-3 pb-3 mt-3"> | ||||||
|                         {{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }} |                         {{ macros::inputgroup(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::inputgroup(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) }} |                         {{ macros::inputgroup(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }} | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|   | |||||||
| @@ -183,8 +183,6 @@ | |||||||
|     <div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative" |     <div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative" | ||||||
|          data-filterable="true" |          data-filterable="true" | ||||||
|          data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}"> |          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 %} |                 {% 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"> |                     <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 %} |                         {% if log.logtype == 1 %} | ||||||
| @@ -199,7 +197,15 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 {% endif %} |                 {% endif %} | ||||||
|                 <div {% if log.logtype %}class="mt-4 sm:mt-0"{% 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 -}} |                     <small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}} | ||||||
|                         {% if log.shipmaster_only_steering %} |                         {% if log.shipmaster_only_steering %} | ||||||
|                             - handgesteuert |                             - handgesteuert | ||||||
| @@ -252,35 +258,52 @@ | |||||||
|                             {% endif %} |                             {% endif %} | ||||||
|                         {% endif %} |                         {% endif %} | ||||||
|                     </div> |                     </div> | ||||||
|                 </summary> |  | ||||||
|             {% if allowed_to_edit %} |             {% if allowed_to_edit %} | ||||||
|                     <form action="/log/update" method="post"> |                       <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="id" value="{{ log.id }}" /> | ||||||
|                         <input type="hidden" name="boat_id" value="{{ log.boat_id }}" /> |                         <input type="hidden" name="boat_id" value="{{ log.boat_id }}" /> | ||||||
|                         <input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" /> |                         <input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" /> | ||||||
|                         <input type="hidden" |                         <input type="hidden" | ||||||
|                                name="steering_person" |                                name="steering_person" | ||||||
|                                value="{{ log.steering_person }}" /> |                                value="{{ log.steering_person }}" /> | ||||||
|                         Handgesteuert: |                         {{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }} | ||||||
|                         <input type="checkbox" |                         <input type="datetime-local" class="input rounded-md" name="departure" value="{{ log.departure }}" /> | ||||||
|                                name="shipmaster_only_steering" |                         <input type="datetime-local" class="input rounded-md" name="arrival" value="{{ log.arrival }}" /> | ||||||
|                                {% 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 }}" /> |  | ||||||
|                         <input type="hidden" name="destination" value="{{ log.destination }}" /> |                         <input type="hidden" name="destination" value="{{ log.destination }}" /> | ||||||
|                         <input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" /> |                         <input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" /> | ||||||
|                         <input type="hidden" name="comments" value="{{ log.comments }}" /> |                         <input type="hidden" name="comments" value="{{ log.comments }}" /> | ||||||
|                         <input type="hidden" name="logtype" value="{{ log.logtype }}" /> |                         <input type="hidden" name="logtype" value="{{ log.logtype }}" /> | ||||||
|                         <input type="submit" value="Updaten" /> |                         <input type="submit" class="btn btn-primary" value="Updaten" /> | ||||||
|                     </form> |                     </form> | ||||||
|                     <a href="/log/{{ log.id }}/delete" |                     <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?');"> |                        onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');"> | ||||||
|                         {% include "includes/delete-icon" %} |                         {% include "includes/delete-icon" %} | ||||||
|                         Löschen |                         Löschen | ||||||
|                     </a> |                     </a> | ||||||
|  |                         </div> | ||||||
|  |                         </div> | ||||||
|  |                       </dialog> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|             </details> |  | ||||||
|         </div> |         </div> | ||||||
|     {% endmacro show_old %} |     {% endmacro show_old %} | ||||||
|     {% macro home(log) %} |     {% macro home(log) %} | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ | |||||||
|             {% for log in logs %} |             {% for log in logs %} | ||||||
|                 {% set_global allowed_to_edit = false %} |                 {% set_global allowed_to_edit = false %} | ||||||
|                 {% if loggedin_user %} |                 {% 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 %} |                         {% set_global allowed_to_edit = true %} | ||||||
|                     {% endif %} |                     {% endif %} | ||||||
|                 {% endif %} |                 {% endif %} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user