forked from Ruderverein-Donau-Linz/rowt
		
	add ergo tool
This commit is contained in:
		@@ -42,7 +42,7 @@ deploy-staging:
 | 
				
			|||||||
    - scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
 | 
					    - scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
 | 
				
			||||||
    - scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
 | 
					    - scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen &&  && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'mv  /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'mv  /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
@@ -62,7 +62,7 @@ deploy-main:
 | 
				
			|||||||
    - scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
					    - scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
				
			||||||
    - scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
					    - scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
				
			||||||
    - scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
					    - scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'mv  /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'mv  /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
 | 
				
			||||||
    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
 | 
					    - ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
[default]
 | 
					[default]
 | 
				
			||||||
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
 | 
					secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
 | 
				
			||||||
rss_key = "rss-key-for-ci"
 | 
					rss_key = "rss-key-for-ci"
 | 
				
			||||||
 | 
					limits = { file = "10 MiB"}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,3 +116,9 @@ CREATE TABLE IF NOT EXISTS "boat_damage" (
 | 
				
			|||||||
	"lock_boat" boolean not null default false -- if true: noone can use the boat 
 | 
						"lock_boat" boolean not null default false -- if true: noone can use the boat 
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- tmp ergo challenge stuff
 | 
				
			||||||
 | 
					ALTER TABLE user ADD COLUMN dob text;
 | 
				
			||||||
 | 
					ALTER TABLE user ADD COLUMN weight text;
 | 
				
			||||||
 | 
					ALTER TABLE user ADD COLUMN sex text;
 | 
				
			||||||
 | 
					ALTER TABLE user ADD COLUMN dirty_thirty text;
 | 
				
			||||||
 | 
					ALTER TABLE user ADD COLUMN dirty_dozen text;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ impl Rower {
 | 
				
			|||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            User,
 | 
					            User,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user
 | 
					FROM user
 | 
				
			||||||
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
 | 
					WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
 | 
				
			||||||
        ",
 | 
					        ",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize};
 | 
				
			|||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
 | 
					use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{log::Log, tripdetails::TripDetails, Day};
 | 
					use super::{log::Log, tripdetails::TripDetails, Day};
 | 
				
			||||||
 | 
					use crate::tera::admin::user::UserEditForm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
 | 
					#[derive(FromRow, Debug, Serialize, Deserialize)]
 | 
				
			||||||
pub struct User {
 | 
					pub struct User {
 | 
				
			||||||
@@ -24,6 +25,9 @@ pub struct User {
 | 
				
			|||||||
    pub is_admin: bool,
 | 
					    pub is_admin: bool,
 | 
				
			||||||
    pub is_guest: bool,
 | 
					    pub is_guest: bool,
 | 
				
			||||||
    pub is_tech: bool,
 | 
					    pub is_tech: bool,
 | 
				
			||||||
 | 
					    pub dob: Option<String>,
 | 
				
			||||||
 | 
					    pub weight: Option<String>,
 | 
				
			||||||
 | 
					    pub sex: Option<String>,
 | 
				
			||||||
    pub deleted: bool,
 | 
					    pub deleted: bool,
 | 
				
			||||||
    pub last_access: Option<chrono::NaiveDateTime>,
 | 
					    pub last_access: Option<chrono::NaiveDateTime>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -92,7 +96,7 @@ impl User {
 | 
				
			|||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            Self,
 | 
					            Self,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user 
 | 
					FROM user 
 | 
				
			||||||
WHERE id like ?
 | 
					WHERE id like ?
 | 
				
			||||||
        ",
 | 
					        ",
 | 
				
			||||||
@@ -107,7 +111,7 @@ WHERE id like ?
 | 
				
			|||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            Self,
 | 
					            Self,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user 
 | 
					FROM user 
 | 
				
			||||||
WHERE id like ?
 | 
					WHERE id like ?
 | 
				
			||||||
        ",
 | 
					        ",
 | 
				
			||||||
@@ -122,7 +126,7 @@ WHERE id like ?
 | 
				
			|||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            Self,
 | 
					            Self,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user 
 | 
					FROM user 
 | 
				
			||||||
WHERE name like ?
 | 
					WHERE name like ?
 | 
				
			||||||
        ",
 | 
					        ",
 | 
				
			||||||
@@ -164,7 +168,7 @@ WHERE name like ?
 | 
				
			|||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            Self,
 | 
					            Self,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user
 | 
					FROM user
 | 
				
			||||||
WHERE deleted = 0
 | 
					WHERE deleted = 0
 | 
				
			||||||
ORDER BY last_access DESC
 | 
					ORDER BY last_access DESC
 | 
				
			||||||
@@ -175,11 +179,26 @@ ORDER BY last_access DESC
 | 
				
			|||||||
        .unwrap()
 | 
					        .unwrap()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub async fn ergo(db: &SqlitePool) -> Vec<Self> {
 | 
				
			||||||
 | 
					        sqlx::query_as!(
 | 
				
			||||||
 | 
					            Self,
 | 
				
			||||||
 | 
					            "
 | 
				
			||||||
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
 | 
					FROM user
 | 
				
			||||||
 | 
					WHERE deleted = 0 AND dob is not null and weight is not null and sex is not null
 | 
				
			||||||
 | 
					ORDER BY last_access DESC
 | 
				
			||||||
 | 
					        "
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .fetch_all(db)
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn cox(db: &SqlitePool) -> Vec<Self> {
 | 
					    pub async fn cox(db: &SqlitePool) -> Vec<Self> {
 | 
				
			||||||
        sqlx::query_as!(
 | 
					        sqlx::query_as!(
 | 
				
			||||||
            Self,
 | 
					            Self,
 | 
				
			||||||
            "
 | 
					            "
 | 
				
			||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
 | 
					SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
 | 
				
			||||||
FROM user
 | 
					FROM user
 | 
				
			||||||
WHERE deleted = 0 AND is_cox=true
 | 
					WHERE deleted = 0 AND is_cox=true
 | 
				
			||||||
ORDER BY last_access DESC
 | 
					ORDER BY last_access DESC
 | 
				
			||||||
@@ -201,20 +220,16 @@ ORDER BY last_access DESC
 | 
				
			|||||||
        .is_ok()
 | 
					        .is_ok()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn update(
 | 
					    pub async fn update(&self, db: &SqlitePool, data: UserEditForm) {
 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        db: &SqlitePool,
 | 
					 | 
				
			||||||
        is_cox: bool,
 | 
					 | 
				
			||||||
        is_admin: bool,
 | 
					 | 
				
			||||||
        is_guest: bool,
 | 
					 | 
				
			||||||
        is_tech: bool,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        sqlx::query!(
 | 
					        sqlx::query!(
 | 
				
			||||||
            "UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ?, is_tech = ? where id = ?",
 | 
					            "UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ?, is_tech = ?, dob = ?, weight = ?, sex = ? where id = ?",
 | 
				
			||||||
            is_cox,
 | 
					            data.is_cox,
 | 
				
			||||||
            is_admin,
 | 
					            data.is_admin,
 | 
				
			||||||
            is_guest,
 | 
					            data.is_guest,
 | 
				
			||||||
            is_tech,
 | 
					            data.is_tech,
 | 
				
			||||||
 | 
					            data.dob,
 | 
				
			||||||
 | 
					            data.weight,
 | 
				
			||||||
 | 
					            data.sex,
 | 
				
			||||||
            self.id
 | 
					            self.id
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .execute(db)
 | 
					        .execute(db)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,12 +58,15 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<R
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(FromForm)]
 | 
					#[derive(FromForm)]
 | 
				
			||||||
struct UserEditForm {
 | 
					pub struct UserEditForm {
 | 
				
			||||||
    id: i32,
 | 
					    pub(crate) id: i32,
 | 
				
			||||||
    is_guest: bool,
 | 
					    pub(crate) is_guest: bool,
 | 
				
			||||||
    is_cox: bool,
 | 
					    pub(crate) is_cox: bool,
 | 
				
			||||||
    is_admin: bool,
 | 
					    pub(crate) is_admin: bool,
 | 
				
			||||||
    is_tech: bool,
 | 
					    pub(crate) is_tech: bool,
 | 
				
			||||||
 | 
					    pub(crate) dob: Option<String>,
 | 
				
			||||||
 | 
					    pub(crate) weight: Option<String>,
 | 
				
			||||||
 | 
					    pub(crate) sex: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[post("/user", data = "<data>")]
 | 
					#[post("/user", data = "<data>")]
 | 
				
			||||||
@@ -80,8 +83,7 @@ async fn update(
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    user.update(db, data.is_cox, data.is_admin, data.is_guest, data.is_tech)
 | 
					    user.update(db, data.into_inner()).await;
 | 
				
			||||||
        .await;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Flash::success(Redirect::to("/admin/user"), "Successfully updated user")
 | 
					    Flash::success(Redirect::to("/admin/user"), "Successfully updated user")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										191
									
								
								src/tera/ergo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/tera/ergo.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
				
			|||||||
 | 
					use std::env;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use rocket::{
 | 
				
			||||||
 | 
					    form::Form,
 | 
				
			||||||
 | 
					    fs::TempFile,
 | 
				
			||||||
 | 
					    get,
 | 
				
			||||||
 | 
					    http::ContentType,
 | 
				
			||||||
 | 
					    post,
 | 
				
			||||||
 | 
					    request::FlashMessage,
 | 
				
			||||||
 | 
					    response::{Flash, Redirect},
 | 
				
			||||||
 | 
					    routes, FromForm, Route, State,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use rocket_dyn_templates::{context, Template};
 | 
				
			||||||
 | 
					use serde::Serialize;
 | 
				
			||||||
 | 
					use sqlx::SqlitePool;
 | 
				
			||||||
 | 
					use tera::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::model::{
 | 
				
			||||||
 | 
					    log::Log,
 | 
				
			||||||
 | 
					    user::{AdminUser, NonGuestUser, User},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Serialize)]
 | 
				
			||||||
 | 
					struct ErgoStat {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    dob: Option<String>,
 | 
				
			||||||
 | 
					    weight: Option<String>,
 | 
				
			||||||
 | 
					    sex: Option<String>,
 | 
				
			||||||
 | 
					    result: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/final")]
 | 
				
			||||||
 | 
					async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
 | 
				
			||||||
 | 
					    let thirty = sqlx::query_as!(
 | 
				
			||||||
 | 
					        ErgoStat,
 | 
				
			||||||
 | 
					        "SELECT name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_all(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let dozen= sqlx::query_as!(
 | 
				
			||||||
 | 
					        ErgoStat,
 | 
				
			||||||
 | 
					        "SELECT name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_all(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Template::render(
 | 
				
			||||||
 | 
					        "ergo.final",
 | 
				
			||||||
 | 
					        context!(loggedin_user: &_user.user, thirty, dozen),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/reset")]
 | 
				
			||||||
 | 
					async fn reset(db: &State<SqlitePool>, _user: AdminUser) -> Flash<Redirect> {
 | 
				
			||||||
 | 
					    sqlx::query!("UPDATE user SET dirty_thirty = NULL, dirty_dozen = NULL;")
 | 
				
			||||||
 | 
					        .execute(db.inner())
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Flash::success(
 | 
				
			||||||
 | 
					        Redirect::to("/ergo"),
 | 
				
			||||||
 | 
					        "Erfolgreich zurückgesetzt (Bilder müssen manuell gelöscht werden!)",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/")]
 | 
				
			||||||
 | 
					async fn index(
 | 
				
			||||||
 | 
					    db: &State<SqlitePool>,
 | 
				
			||||||
 | 
					    user: NonGuestUser,
 | 
				
			||||||
 | 
					    flash: Option<FlashMessage<'_>>,
 | 
				
			||||||
 | 
					) -> Template {
 | 
				
			||||||
 | 
					    let users = User::ergo(db).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let thirty = sqlx::query_as!(
 | 
				
			||||||
 | 
					        ErgoStat,
 | 
				
			||||||
 | 
					        "SELECT name, dirty_thirty as result, dob, weight, sex  FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_all(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let dozen= sqlx::query_as!(
 | 
				
			||||||
 | 
					        ErgoStat,
 | 
				
			||||||
 | 
					        "SELECT name, dirty_dozen as result, dob, weight, sex  FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_all(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut context = Context::new();
 | 
				
			||||||
 | 
					    if let Some(msg) = flash {
 | 
				
			||||||
 | 
					        context.insert("flash", &msg.into_inner());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context.insert("loggedin_user", &user.user);
 | 
				
			||||||
 | 
					    context.insert("users", &users);
 | 
				
			||||||
 | 
					    context.insert("thirty", &thirty);
 | 
				
			||||||
 | 
					    context.insert("dozen", &dozen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Template::render("ergo", context.into_json())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(FromForm, Debug)]
 | 
				
			||||||
 | 
					pub struct ErgoToAdd<'a> {
 | 
				
			||||||
 | 
					    user: i64,
 | 
				
			||||||
 | 
					    result: String,
 | 
				
			||||||
 | 
					    proof: TempFile<'a>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[post("/thirty", data = "<data>", format = "multipart/form-data")]
 | 
				
			||||||
 | 
					async fn new_thirty(
 | 
				
			||||||
 | 
					    db: &State<SqlitePool>,
 | 
				
			||||||
 | 
					    mut data: Form<ErgoToAdd<'_>>,
 | 
				
			||||||
 | 
					    created_by: NonGuestUser,
 | 
				
			||||||
 | 
					) -> Flash<Redirect> {
 | 
				
			||||||
 | 
					    let user = User::find_by_id(db, data.user as i32).await.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let extension = if data.proof.content_type() == Some(&ContentType::JPEG) {
 | 
				
			||||||
 | 
					        "jpg"
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return Flash::error(Redirect::to("/ergo"), "Es werden nur JPG Bilder akzeptiert");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let base_dir = env::current_dir().unwrap();
 | 
				
			||||||
 | 
					    let file_path = base_dir.join(format!("data-ergo/thirty/{}.{extension}", user.name));
 | 
				
			||||||
 | 
					    if let Err(e) = data.proof.move_copy_to(file_path).await {
 | 
				
			||||||
 | 
					        eprintln!("Failed to persist file: {:?}", e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sqlx::query!(
 | 
				
			||||||
 | 
					        "UPDATE user SET dirty_thirty = ? where id = ?",
 | 
				
			||||||
 | 
					        data.result,
 | 
				
			||||||
 | 
					        data.user
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .execute(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap(); //Okay, because we can only create a User of a valid id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Log::create(
 | 
				
			||||||
 | 
					        db,
 | 
				
			||||||
 | 
					        format!("{created_by:?} created thirty-ergo entry: {data:?}"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[post("/dozen", data = "<data>", format = "multipart/form-data")]
 | 
				
			||||||
 | 
					async fn new_dozen(
 | 
				
			||||||
 | 
					    db: &State<SqlitePool>,
 | 
				
			||||||
 | 
					    mut data: Form<ErgoToAdd<'_>>,
 | 
				
			||||||
 | 
					    created_by: NonGuestUser,
 | 
				
			||||||
 | 
					) -> Flash<Redirect> {
 | 
				
			||||||
 | 
					    let user = User::find_by_id(db, data.user as i32).await.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let extension = if data.proof.content_type() == Some(&ContentType::JPEG) {
 | 
				
			||||||
 | 
					        "jpg"
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return Flash::error(Redirect::to("/ergo"), "Es werden nur JPG Bilder akzeptiert");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let base_dir = env::current_dir().unwrap();
 | 
				
			||||||
 | 
					    let file_path = base_dir.join(format!("data-ergo/dozen/{}.{extension}", user.name));
 | 
				
			||||||
 | 
					    if let Err(e) = data.proof.move_copy_to(file_path).await {
 | 
				
			||||||
 | 
					        eprintln!("Failed to persist file: {:?}", e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sqlx::query!(
 | 
				
			||||||
 | 
					        "UPDATE user SET dirty_dozen = ? where id = ?",
 | 
				
			||||||
 | 
					        data.result,
 | 
				
			||||||
 | 
					        data.user
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .execute(db.inner())
 | 
				
			||||||
 | 
					    .await
 | 
				
			||||||
 | 
					    .unwrap(); //Okay, because we can only create a User of a valid id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Log::create(
 | 
				
			||||||
 | 
					        db,
 | 
				
			||||||
 | 
					        format!("{created_by:?} created dozen-ergo entry: {data:?}"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn routes() -> Vec<Route> {
 | 
				
			||||||
 | 
					    routes![index, new_thirty, new_dozen, send, reset]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod test {}
 | 
				
			||||||
@@ -20,10 +20,11 @@ use crate::model::{
 | 
				
			|||||||
    usertrip::{UserTrip, UserTripDeleteError, UserTripError},
 | 
					    usertrip::{UserTrip, UserTripDeleteError, UserTripError},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod admin;
 | 
					pub(crate) mod admin;
 | 
				
			||||||
mod auth;
 | 
					mod auth;
 | 
				
			||||||
mod boatdamage;
 | 
					mod boatdamage;
 | 
				
			||||||
mod cox;
 | 
					mod cox;
 | 
				
			||||||
 | 
					mod ergo;
 | 
				
			||||||
mod log;
 | 
					mod log;
 | 
				
			||||||
mod misc;
 | 
					mod misc;
 | 
				
			||||||
mod stat;
 | 
					mod stat;
 | 
				
			||||||
@@ -56,6 +57,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
 | 
				
			|||||||
    if let Some(msg) = flash {
 | 
					    if let Some(msg) = flash {
 | 
				
			||||||
        context.insert("flash", &msg.into_inner());
 | 
					        context.insert("flash", &msg.into_inner());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    println!("{user:#?}");
 | 
				
			||||||
    context.insert("loggedin_user", &user);
 | 
					    context.insert("loggedin_user", &user);
 | 
				
			||||||
    context.insert("days", &days);
 | 
					    context.insert("days", &days);
 | 
				
			||||||
    Template::render("index", context.into_json())
 | 
					    Template::render("index", context.into_json())
 | 
				
			||||||
@@ -212,6 +214,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
 | 
				
			|||||||
        .mount("/auth", auth::routes())
 | 
					        .mount("/auth", auth::routes())
 | 
				
			||||||
        .mount("/wikiauth", routes![wikiauth])
 | 
					        .mount("/wikiauth", routes![wikiauth])
 | 
				
			||||||
        .mount("/log", log::routes())
 | 
					        .mount("/log", log::routes())
 | 
				
			||||||
 | 
					        .mount("/ergo", ergo::routes())
 | 
				
			||||||
        .mount("/stat", stat::routes())
 | 
					        .mount("/stat", stat::routes())
 | 
				
			||||||
        .mount("/boatdamage", boatdamage::routes())
 | 
					        .mount("/boatdamage", boatdamage::routes())
 | 
				
			||||||
        .mount("/cox", cox::routes())
 | 
					        .mount("/cox", cox::routes())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,9 @@
 | 
				
			|||||||
                {{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }}
 | 
					                {{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }}
 | 
				
			||||||
                {{ macros::checkbox(label='Technical', name='is_tech', id=loop.index , checked=user.is_tech) }}
 | 
					                {{ macros::checkbox(label='Technical', name='is_tech', id=loop.index , checked=user.is_tech) }}
 | 
				
			||||||
                {{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }}
 | 
					                {{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }}
 | 
				
			||||||
 | 
					                {{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob) }}
 | 
				
			||||||
 | 
					                {{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight) }}
 | 
				
			||||||
 | 
					                {{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex) }}
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              {% if user.pw %}
 | 
					              {% if user.pw %}
 | 
				
			||||||
                <a class="inline-block mt-1 text-primary-600 hover:text-primary-900 underline" href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
 | 
					                <a class="inline-block mt-1 text-primary-600 hover:text-primary-900 underline" href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										31
									
								
								templates/ergo.final.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								templates/ergo.final.html.tera
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					{% import "includes/macros" as macros %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% extends "base" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
						<div class="max-w-screen-lg w-full">
 | 
				
			||||||
 | 
							{% if flash %}
 | 
				
			||||||
 | 
								{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
 | 
				
			||||||
 | 
							{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<h1 class="h1">Aktuelle Woche</h1>
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Thirty</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">{% for stat in thirty %}{{ stat.name }}	{{ stat.dob }}	{{ stat.weight }}	{{ stat.sex }}		Donau Linz	{{ stat.result }}
 | 
				
			||||||
 | 
									{% endfor %}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Dozen</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">{% for stat in dozen %}{{ stat.name }}	{{ stat.dob }}	{{ stat.weight }}	{{ stat.sex }}		Donau Linz	{{ stat.result }}
 | 
				
			||||||
 | 
									{% endfor %}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock content%}
 | 
				
			||||||
							
								
								
									
										80
									
								
								templates/ergo.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								templates/ergo.html.tera
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					{% import "includes/macros" as macros %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% extends "base" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
						<div class="max-w-screen-lg w-full">
 | 
				
			||||||
 | 
							{% if flash %}
 | 
				
			||||||
 | 
								{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
 | 
				
			||||||
 | 
							{% endif %}
 | 
				
			||||||
 | 
							<h1 class="h1">Neuer Eintrag</h1>
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Thirty</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">
 | 
				
			||||||
 | 
									<form action="/ergo/thirty" method="post" enctype="multipart/form-data">
 | 
				
			||||||
 | 
										<label for="user-thirty" class="text-sm text-gray-600">Ergo-Fahrer</label>
 | 
				
			||||||
 | 
										<select name="user" id="user-thirty" class="input">
 | 
				
			||||||
 | 
											{% for user in users %}
 | 
				
			||||||
 | 
												<option value="{{ user.id }}">{{ user.name }}</option>
 | 
				
			||||||
 | 
											{% endfor %}
 | 
				
			||||||
 | 
										</select>
 | 
				
			||||||
 | 
										{{ macros::input(label="Zeit [(hh:)mm:ss]/Distanz [m]", name="result", required=true, type="text", class="input") }}
 | 
				
			||||||
 | 
										<input type="file" name="proof" class="input">
 | 
				
			||||||
 | 
										<input type="submit" value="Speichern" class="btn btn-primary w-full col-span-4 m-auto"/>
 | 
				
			||||||
 | 
									</form>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Dozen</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">
 | 
				
			||||||
 | 
									<form action="/ergo/dozen" method="post" enctype="multipart/form-data">
 | 
				
			||||||
 | 
										<label for="user-dozen" class="text-sm text-gray-600">Ergo-Fahrer</label>
 | 
				
			||||||
 | 
										<select name="user" id="user-dozen" class="input">
 | 
				
			||||||
 | 
											<option disabled="disabled">User auswählen</option>
 | 
				
			||||||
 | 
											{% for user in users %}
 | 
				
			||||||
 | 
												<option value="{{ user.id }}">{{ user.name }}</option>
 | 
				
			||||||
 | 
											{% endfor %}
 | 
				
			||||||
 | 
										</select>
 | 
				
			||||||
 | 
										{{ macros::input(label="Zeit [(hh:)mm:ss]/Distanz [m]", name="result", required=true, type="text", class="input") }}
 | 
				
			||||||
 | 
										<input type="file" name="proof" class="input">
 | 
				
			||||||
 | 
										<input type="submit" value="Speichern" class="btn btn-primary w-full col-span-4 m-auto"/>
 | 
				
			||||||
 | 
									</form>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<h1 class="h1">Aktuelle Woche</h1>
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Thirty</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">
 | 
				
			||||||
 | 
									<ol>
 | 
				
			||||||
 | 
									{% for stat in thirty %}
 | 
				
			||||||
 | 
										<li>{{ stat.name }}: {{ stat.result }}</li>
 | 
				
			||||||
 | 
									{% endfor %}
 | 
				
			||||||
 | 
									</ol>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<details>
 | 
				
			||||||
 | 
							  <summary>Dirty Dozen</summary>
 | 
				
			||||||
 | 
							  <p>
 | 
				
			||||||
 | 
								<div class="border-r border-l">
 | 
				
			||||||
 | 
									<ol>
 | 
				
			||||||
 | 
									{% for stat in dozen%}
 | 
				
			||||||
 | 
										<li>{{ stat.name }}: {{ stat.result }}</li>
 | 
				
			||||||
 | 
									{% endfor %}
 | 
				
			||||||
 | 
									</ol>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							  </p>
 | 
				
			||||||
 | 
							</details>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock content%}
 | 
				
			||||||
@@ -1,70 +1,129 @@
 | 
				
			|||||||
{% macro header(loggedin_user) %}
 | 
					{% macro header(loggedin_user) %}
 | 
				
			||||||
	<header class="bg-primary-900 text-white flex justify-center p-3 fixed w-full z-10">
 | 
					  <header
 | 
				
			||||||
		<div class="max-w-screen-xl w-full flex justify-between items-center">
 | 
					    class="bg-primary-900 text-white flex justify-center p-3 fixed w-full z-10"
 | 
				
			||||||
			<div class="w-1/3 truncate">
 | 
					  >
 | 
				
			||||||
				<a href="/">
 | 
					    <div class="max-w-screen-xl w-full flex justify-between items-center">
 | 
				
			||||||
					Hü
 | 
					      <div class="w-1/3 truncate">
 | 
				
			||||||
					{{ loggedin_user.name }}
 | 
					        <a href="/">
 | 
				
			||||||
				</a>
 | 
					          Hü
 | 
				
			||||||
			</div>
 | 
					          {{ loggedin_user.name }}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
			<div>
 | 
					      <div>
 | 
				
			||||||
				<a href="https://wiki.rudernlinz.at/ruderassistent#faq" target="_blank" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
 | 
					        <a
 | 
				
			||||||
					{% include "includes/question-icon" %}
 | 
					          href="https://wiki.rudernlinz.at/ruderassistent#faq"
 | 
				
			||||||
					<span class="sr-only">FAQs</span>
 | 
					          target="_blank"
 | 
				
			||||||
				</a>
 | 
					          class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
 | 
				
			||||||
	      {% if not loggedin_user.is_guest %}
 | 
					        >
 | 
				
			||||||
          <a href="#" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
 | 
					          {% include "includes/question-icon" %}
 | 
				
			||||||
                      data-sidebar="true" data-trigger="sidebar" data-header="Logbuch" data-body="#mobile-menu">
 | 
					          <span class="sr-only">FAQs</span>
 | 
				
			||||||
            {% include "includes/book" %}
 | 
					        </a>
 | 
				
			||||||
            <span class="sr-only">Logbuch</span>
 | 
					        {% if not loggedin_user.is_guest %}
 | 
				
			||||||
          </a>
 | 
					        <a
 | 
				
			||||||
          <div class="hidden">
 | 
					          href="#"
 | 
				
			||||||
            <div id="mobile-menu">
 | 
					          class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
 | 
				
			||||||
              <a href="/log" class="block w-100 py-2 hover:text-primary-600">
 | 
					          data-sidebar="true"
 | 
				
			||||||
                Ausfahrt eintragen
 | 
					          data-trigger="sidebar"
 | 
				
			||||||
              </a>
 | 
					          data-header="Logbuch"
 | 
				
			||||||
              <a href="/log/show" class="block w-100 py-2 hover:text-primary-600 border-t">
 | 
					          data-body="#mobile-menu"
 | 
				
			||||||
                Logbuch
 | 
					        >
 | 
				
			||||||
              </a>
 | 
					          {% include "includes/book" %}
 | 
				
			||||||
              <a href="/stat" class="block w-100 py-2 hover:text-primary-600 border-t">
 | 
					          <span class="sr-only">Logbuch</span>
 | 
				
			||||||
                Statistik
 | 
					        </a>
 | 
				
			||||||
              </a>
 | 
					        <div class="hidden">
 | 
				
			||||||
              <a href="/stat/boats" class="block w-100 py-2 hover:text-primary-600 border-t">
 | 
					          <div id="mobile-menu">
 | 
				
			||||||
                Bootsauswertung
 | 
					            <a href="/log" class="block w-100 py-2 hover:text-primary-600">
 | 
				
			||||||
              </a>
 | 
					              Ausfahrt eintragen
 | 
				
			||||||
	      {% if loggedin_user.is_admin %}
 | 
					            </a>
 | 
				
			||||||
              	<a href="/admin/boat" class="block w-100 py-2 hover:text-primary-600 border-t">
 | 
					            <a
 | 
				
			||||||
              	  Boote
 | 
					              href="/log/show"
 | 
				
			||||||
              	</a>
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
	      {% endif %}
 | 
					            >
 | 
				
			||||||
              <a href="/boatdamage" class="block w-100 py-2 hover:text-primary-600 border-t">
 | 
					              Logbuch
 | 
				
			||||||
                Bootsschaden
 | 
					            </a>
 | 
				
			||||||
              </a>
 | 
						    {% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
 | 
				
			||||||
            </div>
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/ergo"
 | 
				
			||||||
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Ergo
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
						    {% endif %}
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/stat"
 | 
				
			||||||
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Statistik
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/stat/boats"
 | 
				
			||||||
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Bootsauswertung
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            {% if loggedin_user.is_admin %}
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/admin/boat"
 | 
				
			||||||
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Boote
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/boatdamage"
 | 
				
			||||||
 | 
					              class="block w-100 py-2 hover:text-primary-600 border-t"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Bootsschaden
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
	{% endif %}
 | 
					        </div>
 | 
				
			||||||
 | 
					        {% endif %} {% if loggedin_user.is_admin %}
 | 
				
			||||||
				{% if loggedin_user.is_admin %}
 | 
					        <a
 | 
				
			||||||
					<a href="/admin/user" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
 | 
					          href="/admin/user"
 | 
				
			||||||
						<svg class="inline h-4" width="16" height="16" fill="currentColor" class="bi bi-person-lines-fill" viewbox="0 0 16 16">
 | 
					          class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
 | 
				
			||||||
							<path d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"/>
 | 
					        >
 | 
				
			||||||
						</svg>
 | 
					          <svg
 | 
				
			||||||
						<span class="sr-only">Userverwaltung</span>
 | 
					            class="inline h-4"
 | 
				
			||||||
					</a>
 | 
					            width="16"
 | 
				
			||||||
				{% endif %}
 | 
					            height="16"
 | 
				
			||||||
				<a href="/auth/logout" class="inline-flex justify-center rounded-md bg-primary-600 ml-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
 | 
					            fill="currentColor"
 | 
				
			||||||
					<svg class="inline h-4" width="24" height="24" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-out">
 | 
					            class="bi bi-person-lines-fill"
 | 
				
			||||||
						<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
 | 
					            viewbox="0 0 16 16"
 | 
				
			||||||
						<polyline points="16 17 21 12 16 7"></polyline>
 | 
					          >
 | 
				
			||||||
						<line x1="21" y1="12" x2="9" y2="12"></line>
 | 
					            <path
 | 
				
			||||||
					</svg>
 | 
					              d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"
 | 
				
			||||||
					<span class="sr-only">Ausloggen</span>
 | 
					            />
 | 
				
			||||||
				</a>
 | 
					          </svg>
 | 
				
			||||||
			</div>
 | 
					          <span class="sr-only">Userverwaltung</span>
 | 
				
			||||||
		</div>
 | 
					        </a>
 | 
				
			||||||
	</header>
 | 
					        {% endif %}
 | 
				
			||||||
	<div class="h-8"></div>
 | 
					        <a
 | 
				
			||||||
 | 
					          href="/auth/logout"
 | 
				
			||||||
 | 
					          class="inline-flex justify-center rounded-md bg-primary-600 ml-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <svg
 | 
				
			||||||
 | 
					            class="inline h-4"
 | 
				
			||||||
 | 
					            width="24"
 | 
				
			||||||
 | 
					            height="24"
 | 
				
			||||||
 | 
					            viewbox="0 0 24 24"
 | 
				
			||||||
 | 
					            fill="none"
 | 
				
			||||||
 | 
					            stroke="currentColor"
 | 
				
			||||||
 | 
					            stroke-width="2"
 | 
				
			||||||
 | 
					            stroke-linecap="round"
 | 
				
			||||||
 | 
					            stroke-linejoin="round"
 | 
				
			||||||
 | 
					            class="feather feather-log-out"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
 | 
				
			||||||
 | 
					            <polyline points="16 17 21 12 16 7"></polyline>
 | 
				
			||||||
 | 
					            <line x1="21" y1="12" x2="9" y2="12"></line>
 | 
				
			||||||
 | 
					          </svg>
 | 
				
			||||||
 | 
					          <span class="sr-only">Ausloggen</span>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </header>
 | 
				
			||||||
 | 
					  <div class="h-8"></div>
 | 
				
			||||||
{% endmacro header %}
 | 
					{% endmacro header %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='') %}
 | 
					{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='') %}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user