update-handgesteuert #780

Merged
philipp merged 2 commits from update-handgesteuert into staging 2024-10-26 21:06:30 +02:00
8 changed files with 382 additions and 245 deletions
Showing only changes of commit d0b0888a9b - Show all commits

View File

@ -690,18 +690,8 @@ WHERE family_id IS NULL;
} }
pub async fn ergo(db: &SqlitePool) -> Vec<Self> { pub async fn ergo(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!( let ergo = Role::find_by_name(db, "ergo").await.unwrap();
Self, Self::all_with_role(db, &ergo).await
"
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, user_token
FROM user
WHERE deleted = 0 AND dob != '' and weight != '' and sex != ''
ORDER BY name
"
)
.fetch_all(db)
.await
.unwrap()
} }
pub async fn cox(db: &SqlitePool) -> Vec<Self> { pub async fn cox(db: &SqlitePool) -> Vec<Self> {
@ -735,6 +725,19 @@ ORDER BY last_access DESC
.is_ok() .is_ok()
} }
pub async fn update_ergo(&self, db: &SqlitePool, dob: i32, weight: i64, sex: &str) {
sqlx::query!(
"UPDATE user SET dob = ?, weight = ?, sex = ? where id = ?",
dob,
weight,
sex,
self.id
)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a User of a valid id
}
pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) -> Result<(), String> { pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) -> Result<(), String> {
let mut db = db.begin().await.map_err(|e| e.to_string())?; let mut db = db.begin().await.map_err(|e| e.to_string())?;

View File

@ -1,6 +1,6 @@
use std::env; use std::env;
use chrono::Utc; use chrono::{Datelike, Utc};
use rocket::{ use rocket::{
form::Form, form::Form,
fs::TempFile, fs::TempFile,
@ -17,8 +17,7 @@ use sqlx::SqlitePool;
use tera::Context; use tera::Context;
use crate::model::{ use crate::model::{
log::Log, log::Log, notification::Notification, role::Role, user::{AdminUser, User, UserWithDetails}
user::{AdminUser, User, UserWithDetails},
}; };
#[derive(Serialize)] #[derive(Serialize)]
@ -50,7 +49,7 @@ async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
.unwrap(); .unwrap();
Template::render( Template::render(
"ergo.final", "ergo/final",
context!(loggedin_user: &UserWithDetails::from_user(_user.user, db).await, thirty, dozen), context!(loggedin_user: &UserWithDetails::from_user(_user.user, db).await, thirty, dozen),
) )
} }
@ -98,6 +97,19 @@ async fn update(
#[get("/")] #[get("/")]
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template { async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("loggedin_user", &UserWithDetails::from_user(user.clone(), db).await);
if !user.has_role(db, "ergo").await {
return Template::render(
"ergo/missing-data",
context.into_json()
);
}
let users = User::ergo(db).await; let users = User::ergo(db).await;
let thirty = sqlx::query_as!( let thirty = sqlx::query_as!(
@ -116,16 +128,56 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
.await .await
.unwrap(); .unwrap();
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("users", &users); context.insert("users", &users);
context.insert("thirty", &thirty); context.insert("thirty", &thirty);
context.insert("dozen", &dozen); context.insert("dozen", &dozen);
Template::render("ergo", context.into_json()) Template::render("ergo/index", context.into_json())
}
#[derive(FromForm, Debug)]
pub struct UserAdd {
birthyear: i32,
weight: i64,
sex: String,
}
#[post("/set-data", data = "<data>")]
async fn new_user(
db: &State<SqlitePool>,
data: Form<UserAdd>,
user: User,
) -> Flash<Redirect> {
if user.has_role(db, "ergo").await {
return
Flash::error(Redirect::to("/ergo"), "Du hast deine Daten schon eingegeben. Wenn du sie updaten willst, melde dich bitte bei it@rudernlinz.at");
}
// check data
if data.birthyear <1900 || data.birthyear > chrono::Utc::now().year() - 5 {
return
Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geburtsjahr...");
}
if data.weight < 20 || data.weight > 200 {
return
Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Gewicht...");
}
if &data.sex != "f" && &data.sex != "m"{
return
Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geschlecht...");
}
// set data
user.update_ergo(db, data.birthyear, data.weight, &data.sex).await;
// inform all other `ergo` users
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
Notification::create_for_role(db, &ergo, &format!("{} nimmt heuer an der Ergochallenge teil 💪", user.name), "Ergo Challenge", None, None).await;
// add to `ergo` group
user.add_role(db,&ergo).await.unwrap();
Flash::success(Redirect::to("/ergo"), "Du hast deine Daten erfolgreich eingegeben. Viel Spaß beim Schwitzen :-)")
} }
#[derive(FromForm, Debug)] #[derive(FromForm, Debug)]
@ -173,6 +225,9 @@ async fn new_thirty(
) )
.await; .await;
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
Notification::create_for_role(db, &ergo, &format!("{} ist gerade die Dirty Thirty Challenge gefahren 🥵", user.name), "Ergo Challenge", Some("/ergo"), None).await;
Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen") Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
} }
@ -214,11 +269,14 @@ async fn new_dozen(
) )
.await; .await;
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
Notification::create_for_role(db, &ergo, &format!("{} ist gerade die Dirty Dozen Challenge gefahren 🥵", user.name), "Ergo Challenge", Some("/ergo"), None).await;
Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen") Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
} }
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![index, new_thirty, new_dozen, send, reset, update] routes![index, new_thirty, new_dozen, send, reset, update, new_user]
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,6 +1,6 @@
use std::{fs::OpenOptions, io::Write}; use std::{fs::OpenOptions, io::Write};
use chrono::Local; use chrono::{Datelike, Local};
use rocket::{ use rocket::{
catch, catchers, catch, catchers,
fairing::{AdHoc, Fairing, Info, Kind}, fairing::{AdHoc, Fairing, Info, Kind},
@ -63,6 +63,11 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
context.insert("last_trips", &last_trips); context.insert("last_trips", &last_trips);
} }
let date = chrono::Utc::now();
if date.month() <= 3 || date.month() >= 10 {
context.insert("show_quick_ergo_button", "yes");
}
context.insert("achievements", &Achievements::for_user(db, &user).await); context.insert("achievements", &Achievements::for_user(db, &user).await);
context.insert("notifications", &Notification::for_user(db, &user).await); context.insert("notifications", &Notification::for_user(db, &user).await);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await); context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);

View File

@ -1,205 +0,0 @@
{% import "includes/macros" as macros %}
{% extends "base" %}
{% block content %}
<div class="max-w-screen-lg w-full">
<h1 class="h1">Ergo Challenges</h1>
<div class="grid gap-3">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Ergo-Challenge?!</h2>
<div class="text-sm p-3">
<ul class="list-disc ms-2">
<li class="py-1">
<a href="https://rudernlinz.at/termin"
target="_blank"
class="link-primary">Überblick der Challenges</a>
</li>
<li class="py-1">
Eintragung ist jederzeit möglich, alle Daten die bis Sonntag 23:59 hier hochgeladen wurden, werden gesammelt an die Ister Ergo Challenge geschickt
<li class="py-1">
Dienstag + Donnerstag &rarr; gemeinsames Training; bitte um <a href="/" class="link-primary">Anmeldung</a>, damit jeder einen Ergo hat
</li>
<li class="py-1">
Offizielle Ergebnisse: <a href="https://rudernlinz.at/dt"
target="_blank"
style="text-decoration: underline">Dirty Thirty (rudernlinz.at/dt)</a> / <a href="https://rudernlinz.at/dd"
target="_blank"
style="text-decoration: underline">Dirty Dozen (rudernlinz.at/dd)</a>, bei Fehlern direkt mit <a href="mailto:office@ergochallenge.at"
style="text-decoration: underline">Christian (Ister)</a> Kontakt aufnehmen
</li>
<li class="py-1">
<a href="https://cloud.rudernlinz.at/s/m7mPQdwSWscpaXT"
target="_blank"
class="link-primary">Noch mehr Infos zur Ergo-Challenge findest du hier</a>
</li>
</ul>
</div>
<details class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md p-2">
<summary class="cursor-pointer">Deine Daten</summary>
<div class="pt-3">
<p>
Folgende Daten hat der Ruderassistent von dir. Wenn diese nicht mehr aktuell sind, bitte gewünschte Änderungen an Philipp melden (Tel. nr siehe Signal, oder an <a href="mailto:it@rudernlinz.at"
class="text-primary-600 dark:text-primary-200 hover:text-primary-950 hover:dark:text-primary-300 underline"
target="_blank">it@rudernlinz.at</a>).
<br />
<br />
<ul>
<li>Geburtsdatum: {{ loggedin_user.dob }}</li>
<li>Gewicht: {{ loggedin_user.weight }} kg</li>
<li>Geschlecht: {{ loggedin_user.sex }}</li>
</ul>
</p>
</div>
</details>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">
Neuer Eintrag
</h1>
<details class="p-2">
<summary class="cursor-pointer">Dirty Thirty</summary>
<div class="mt-3">
<form action="/ergo/thirty"
class="grid gap-3"
method="post"
enctype="multipart/form-data">
<div>
<label for="user-thirty" class="text-sm text-gray-600 dark:text-gray-100">Ergo-Fahrer</label>
<select name="user" id="user-thirty" class="input rounded-md">
<option disabled="disabled">User auswählen</option>
{% for user in users %}
{% if user.id == loggedin_user.id %}
<option value="{{ user.id }}" selected="selected">{{ user.name }}</option>
{% else %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{{ macros::input(label="Distanz [m]", name="result", required=true, type="number", class="input rounded-md") }}
<div>
<label for="file-thirty" class="text-sm text-gray-600 dark:text-gray-100">Ergebnis-Foto vom Ergo-Display</label>
<input type="file"
id="file-thirty"
name="proof"
class="input rounded-md"
accept="image/*">
</div>
<div class="text-end">
<input type="submit" value="Speichern" class="btn btn-primary btn-fw m-auto" />
</div>
</form>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">Dirty Dozen</summary>
<div class="mt-3">
<form action="/ergo/dozen"
class="grid gap-3"
method="post"
enctype="multipart/form-data">
<div>
<label for="user-dozen" class="text-sm text-gray-600 dark:text-gray-100">Ergo-Fahrer</label>
<select name="user" id="user-dozen" class="input rounded-md">
<option disabled="disabled">User auswählen</option>
{% for user in users %}
{% if user.id == loggedin_user.id %}
<option value="{{ user.id }}" selected="selected">{{ user.name }}</option>
{% else %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{{ macros::input(label="Zeit [hh:mm:ss.s] oder Distanz [m]", name="result", required=true, type="text", class="input rounded-md", pattern="(?:\d+:\d{2}:\d{2}\.\d+|\d{1,2}:\d{2}\.\d+|\d+(\.\d+)?)") }}
<div>
<label for="file-dozen" class="text-sm text-gray-600 dark:text-gray-100">Ergebnis-Foto vom Ergo-Display</label>
<input type="file"
id="file-dozen"
name="proof"
class="input rounded-md"
accept="image/*">
</div>
<div class="text-end">
<input type="submit" value="Speichern" class="btn btn-primary btn-fw m-auto" />
</div>
</form>
</div>
</details>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">Aktuelle Woche</h2>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Thirty <small class="text-gray-600 dark:text-white">({{ thirty | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in thirty %}
<li>
<strong>{{ stat.name }}:</strong> {{ stat.result }}
</li>
{% endfor %}
</ol>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Dozen <small class="text-gray-600 dark:text-white">({{ dozen | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in dozen %}
<li>
<strong>{{ stat.name }}:</strong> {{ stat.result }}
</li>
{% endfor %}
</ol>
</div>
</details>
</div>
{% if "admin" in loggedin_user.roles %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">Update</h2>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Thirty <small class="text-gray-600 dark:text-white">({{ thirty | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in thirty %}
<li>
<form action="/ergo/thirty/user/{{ stat.id }}/new" method="get">
{{ stat.name }}:
<input type="text" value="{{ stat.result }}" name="new" style="color: black" />
<input type="submit" />
</form>
</li>
{% endfor %}
</ol>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Dozen <small class="text-gray-600 dark:text-white">({{ dozen | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in dozen %}
<li>
<form action="/ergo/dozen/user/{{ stat.id }}/new" method="get">
{{ stat.name }}:
<input type="text" value="{{ stat.result }}" name="new" style="color: black" />
<input type="submit" />
</form>
</li>
{% endfor %}
</ol>
</div>
</details>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,230 @@
{% import "includes/macros" as macros %}
{% extends "base" %}
{% block content %}
<div class="max-w-screen-lg w-full">
<h1 class="h1">Ergo Challenges</h1>
<div class="grid gap-3">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Ergo Challenge?!</h2>
<div class="p-3">
<ul class="list-disc ms-2">
<li class="py-1">
<a href="https://rudernlinz.at/termin"
target="_blank"
class="link-primary">Überblick der Challenges</a>
</li>
<li class="py-1">
Eintragung ist jederzeit möglich, alle Daten die bis Sonntag 23:59 hier hochgeladen wurden, werden gesammelt an die Ister Ergo Challenge geschickt
<li class="py-1">
Montag &rarr; gemeinsames Training; bitte um <a href="/planned" class="link-primary">Anmeldung</a>, damit jeder einen Ergo hat
</li>
<li class="py-1">
Offizielle Ergebnisse: <a href="https://rudernlinz.at/dt"
target="_blank"
style="text-decoration: underline">Dirty Thirty (rudernlinz.at/dt)</a> / <a href="https://rudernlinz.at/dd"
target="_blank"
style="text-decoration: underline">Dirty Dozen (rudernlinz.at/dd)</a>, bei Fehlern direkt mit <a href="mailto:office@ergochallenge.at"
style="text-decoration: underline">Christian (Ister)</a> Kontakt aufnehmen
</li>
</ul>
</div>
<details class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md p-2">
<summary class="cursor-pointer"><strong>Um was geht es bei den Ergochallenges?</strong></summary>
<p class="py-2 ">
Der Linzer Verein Ister veranstaltet seit einigen Jahren zwei Challenges im Winter, Dirty Thirty (6x im Winter) und Dirty Dozen (12 Wochen lang).
<ul class="list-decimal ms-4">
<li class="py-1">Bei <strong>Dirty Thirty</strong> geht es darum so viele Kilometer wie möglich in 30 Minuten zu fahren.</li>
<li class="py-1">
Bei <strong>Dirty Dozen</strong> werden jede Woche neue Ziele ausgeschrieben, gestartet wird mit einem Halbmarathon und es geht runter bis auf 100m.
</li>
</ul>
<p class="py-2">Ihr könnt gerne bei allen Challenges mitmachen und es ist möglich jederzeit ein- bzw. auszusteigen. Für alle komplett neuen Teilnehmer würde ich allerdings empfehlen die ersten beiden Dirty Dozen Challenges (Halbmarathon und 16 Kilometer) auszulassen und es am Anfang etwas ruhiger anzugehen. Es steht der Spaß und die Festigung der Technik im Vordergrund, nicht Rekorde.</p>
<strong>Video Tipps 🐞</strong>
<ul class="list-disc ms-3">
<li class="py-1">
<a href="https://www.youtube.com/watch?v=TJsQPV6LNPI"
target="_blank"
style="text-decoration: underline">Intro</a>
</li>
<li class="py-1">
<a href="https://www.youtube.com/watch?v=KOacKLOpWkI"
target="_blank"
style="text-decoration: underline">Schlagaufbau</a>
</li>
<li class="py-1">
<a href="https://www.youtube.com/watch?v=m6VP11EDjcM"
target="_blank"
style="text-decoration: underline">PM5 Monitor</a>
</li>
</ul>
</details>
<details class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md p-2">
<summary class="cursor-pointer"><strong>Deine Daten</strong></summary>
<div class="pt-3">
<p>
Folgende Daten hat der Ruderassistent von dir. Wenn diese nicht mehr aktuell sind, bitte gewünschte Änderungen an Philipp melden (Tel. nr siehe Signal, oder an <a href="mailto:it@rudernlinz.at"
class="text-primary-600 dark:text-primary-200 hover:text-primary-950 hover:dark:text-primary-300 underline"
target="_blank">it@rudernlinz.at</a>).
<br />
<br />
<ul>
<li>Geburtsdatum: {{ loggedin_user.dob }}</li>
<li>Gewicht: {{ loggedin_user.weight }} kg</li>
<li>Geschlecht: {{ loggedin_user.sex }}</li>
</ul>
</p>
</div>
</details>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">
Neuer Eintrag
</h1>
<details class="p-2">
<summary class="cursor-pointer">Dirty Thirty</summary>
<div class="mt-3">
<form action="/ergo/thirty"
class="grid gap-3"
method="post"
enctype="multipart/form-data">
<div>
<label for="user-thirty" class="text-sm text-gray-600 dark:text-gray-100">Ergo-Fahrer</label>
<select name="user" id="user-thirty" class="input rounded-md">
<option disabled="disabled">User auswählen</option>
{% for user in users %}
{% if user.id == loggedin_user.id %}
<option value="{{ user.id }}" selected="selected">{{ user.name }}</option>
{% else %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{{ macros::input(label="Distanz [m]", name="result", required=true, type="number", class="input rounded-md") }}
<div>
<label for="file-thirty" class="text-sm text-gray-600 dark:text-gray-100">Ergebnis-Foto vom Ergo-Display</label>
<input type="file"
id="file-thirty"
name="proof"
class="input rounded-md"
accept="image/*">
</div>
<div class="text-end">
<input type="submit" value="Speichern" class="btn btn-primary btn-fw m-auto" />
</div>
</form>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">Dirty Dozen</summary>
<div class="mt-3">
<form action="/ergo/dozen"
class="grid gap-3"
method="post"
enctype="multipart/form-data">
<div>
<label for="user-dozen" class="text-sm text-gray-600 dark:text-gray-100">Ergo-Fahrer</label>
<select name="user" id="user-dozen" class="input rounded-md">
<option disabled="disabled">User auswählen</option>
{% for user in users %}
{% if user.id == loggedin_user.id %}
<option value="{{ user.id }}" selected="selected">{{ user.name }}</option>
{% else %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{{ macros::input(label="Zeit [hh:mm:ss.s] oder Distanz [m]", name="result", required=true, type="text", class="input rounded-md", pattern="(?:\d+:\d{2}:\d{2}\.\d+|\d{1,2}:\d{2}\.\d+|\d+(\.\d+)?)") }}
<div>
<label for="file-dozen" class="text-sm text-gray-600 dark:text-gray-100">Ergebnis-Foto vom Ergo-Display</label>
<input type="file"
id="file-dozen"
name="proof"
class="input rounded-md"
accept="image/*">
</div>
<div class="text-end">
<input type="submit" value="Speichern" class="btn btn-primary btn-fw m-auto" />
</div>
</form>
</div>
</details>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">Aktuelle Woche</h2>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Thirty <small class="text-gray-600 dark:text-white">({{ thirty | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in thirty %}
<li>
<strong>{{ stat.name }}:</strong> {{ stat.result }}
</li>
{% endfor %}
</ol>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Dozen <small class="text-gray-600 dark:text-white">({{ dozen | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in dozen %}
<li>
<strong>{{ stat.name }}:</strong> {{ stat.result }}
</li>
{% endfor %}
</ol>
</div>
</details>
</div>
{% if "admin" in loggedin_user.roles %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow grid gap-3">
<h2 class="h2">Update</h2>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Thirty <small class="text-gray-600 dark:text-white">({{ thirty | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in thirty %}
<li>
<form action="/ergo/thirty/user/{{ stat.id }}/new" method="get">
{{ stat.name }}:
<input type="text" value="{{ stat.result }}" name="new" style="color: black" />
<input type="submit" />
</form>
</li>
{% endfor %}
</ol>
</div>
</details>
<details class="p-2">
<summary class="cursor-pointer">
Dirty Dozen <small class="text-gray-600 dark:text-white">({{ dozen | length }})</small>
</summary>
<div class="mt-3">
<ol>
{% for stat in dozen %}
<li>
<form action="/ergo/dozen/user/{{ stat.id }}/new" method="get">
{{ stat.name }}:
<input type="text" value="{{ stat.result }}" name="new" style="color: black" />
<input type="submit" />
</form>
</li>
{% endfor %}
</ol>
</div>
</details>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,37 @@
{% import "includes/macros" as macros %}
{% import "includes/forms/boat" as boat %}
{% extends "base" %}
{% block content %}
<div class="max-w-screen-lg w-full dark:text-white">
<h1 class="h1">Ergo Challenge</h1>
<div class="grid ">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<p class="px-3 pt-3">
Schön, dass du heuer bei der Ergo Challenge mitmachen willst!
Dafür benötigen wir 3 Daten: Geburtsjahr, Gewicht und Geschlecht.
{% if loggedin_user.weight %}Wir haben von dir schon Daten, bitte überprüfe (und aktualisiere) diese kurz:{% endif %}
</p>
<form action="/ergo/set-data" method="post" class="grid gap-3 p-3">
{{ macros::input(label="Geburtsjahr [YYYY]", name="birthyear", required=true, type="number", class="input rounded-md", value=loggedin_user.dob) }}
{{ macros::input(label="Gewicht [kg]", name="weight", required=true, type="number", class="input rounded-md", value=loggedin_user.weight) }}
<div>
<label for="sex" class="text-sm text-gray-600 dark:text-gray-100">Geschlecht</label>
<select name="sex" id="sex" class="input rounded-md" required>
<option disabled="disabled"
{% if loggedin_user.sex != 'f' and loggedin_user.sex != 'm' %}selected="selected"{% endif %}>
Geschlecht auswählen
</option>
<option value="f"
{% if loggedin_user.sex == 'f' %}selected="selected"{% endif %}>weiblich</option>
<option value="m"
{% if loggedin_user.sex == 'm' %}selected="selected"{% endif %}>männlich</option>
</select>
<small class="block py-1">Du fühlst dich beim Geschlecht nicht angesprochen? Dann melde dich bitte direkt beim Ergo-Christian, Kontaktmöglichkeit auf der nächsten Seite.</small>
</div>
<input type="submit" class="btn btn-primary" value="Abschicken" />
</form>
</div>
</div>
</div>
{% endblock content %}

View File

@ -6,12 +6,23 @@
<h1 class="h1">Ruder&shy;assistent</h1> <h1 class="h1">Ruder&shy;assistent</h1>
<div class="grid gap-3 my-5"> <div class="grid gap-3 my-5">
<div class="m-auto"> <div class="m-auto">
<a href="/planned" <a href="/planned" style="display:inline-flex;"
class="btn btn-primary flex items-center justify-center"> class="btn btn-primary flex items-center justify-between w-80 max-w-full">
{% include "includes/rowing-icon" %} <span class="text-2xl">🚣‍♀️</span>
<span class="text-xl px-3">Geplante Ausfahrten</span> <span class="text-xl px-3">Geplante Ausfahrten</span>
<span class="text-2xl">🚣‍♂️</span>
</a> </a>
</div> </div>
{% if show_quick_ergo_button %}
<div class="m-auto">
<a href="/ergo" style="display:inline-flex;"
class="btn btn-primary flex items-center justify-between w-80 max-w-full">
<span class="text-2xl">💪</span>
<span class="text-xl px-3">Ergo Challenge</span>
<span class="text-2xl">💪🏿</span>
</a>
</div>
{% endif %}
{% if notifications %} {% if notifications %}
<div id="notification" <div id="notification"
class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
@ -303,6 +314,17 @@
</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"
role="alert">
<h2 class="h2">Ergo</h2>
<div class="p-3">
<ul class="list-none ms-2">
<li class="py-1">
<a href="/ergo" class="block w-100 py-2 hover:text-primary-600">Ergo</a>
</li>
</ul>
</div>
</div>
{% if "schnupper-betreuer" in loggedin_user.roles %} {% if "schnupper-betreuer" in loggedin_user.roles %}
<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 mt-5"
role="alert"> role="alert">
@ -369,19 +391,6 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Ergo</h2>
<div class="p-3">
<ul class="list-none ms-2">
<li class="py-1">
<a href="/ergo" class="block w-100 py-2 hover:text-primary-600">Ergo</a>
</li>
</ul>
</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 mt-5"
role="alert"> role="alert">
<h2 class="h2">Allgemein</h2> <h2 class="h2">Allgemein</h2>