Merge pull request 'single-user-edit-page' (#970) from single-user-edit-page into staging
Reviewed-on: #970
This commit is contained in:
commit
5296b6a6c1
@ -50,11 +50,16 @@ function editReadOnlyField() {
|
|||||||
Array.prototype.forEach.call(editBtns, (btn: HTMLButtonElement) => {
|
Array.prototype.forEach.call(editBtns, (btn: HTMLButtonElement) => {
|
||||||
btn.addEventListener("click", function () {
|
btn.addEventListener("click", function () {
|
||||||
let wrapper = btn.parentElement;
|
let wrapper = btn.parentElement;
|
||||||
let input = wrapper?.querySelector('input');
|
let input = <HTMLInputElement> wrapper?.querySelector('input.input'),
|
||||||
|
select = <HTMLSelectElement> wrapper?.querySelector('select.input'),
|
||||||
|
attribute = 'readonly';
|
||||||
|
|
||||||
wrapper?.classList.toggle('editable')
|
if(select) attribute = 'disabled';
|
||||||
input?.toggleAttribute('readonly');
|
let element = input ? input : select;
|
||||||
if(!input?.hasAttribute('readonly')) input?.focus();
|
|
||||||
|
element?.toggleAttribute(attribute);
|
||||||
|
if(!element?.hasAttribute(attribute)) element?.focus();
|
||||||
|
wrapper?.classList.toggle('editable');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
70
src/lib.rs
70
src/lib.rs
@ -1,5 +1,7 @@
|
|||||||
#![allow(clippy::blocks_in_conditions)]
|
#![allow(clippy::blocks_in_conditions)]
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
|
||||||
#[cfg(feature = "rowing-tera")]
|
#[cfg(feature = "rowing-tera")]
|
||||||
@ -22,6 +24,74 @@ pub(crate) const FOERDERND: i64 = 8500;
|
|||||||
pub(crate) const SCHECKBUCH: i64 = 3000;
|
pub(crate) const SCHECKBUCH: i64 = 3000;
|
||||||
pub(crate) const EINSCHREIBGEBUEHR: i64 = 3000;
|
pub(crate) const EINSCHREIBGEBUEHR: i64 = 3000;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct NonEmptyString(String);
|
||||||
|
|
||||||
|
impl NonEmptyString {
|
||||||
|
pub fn new(s: String) -> Option<Self> {
|
||||||
|
if s.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(NonEmptyString(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement Deref to allow automatic dereferencing to &str
|
||||||
|
impl Deref for NonEmptyString {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This allows &NonEmptyString to be converted to &str
|
||||||
|
impl AsRef<str> for NonEmptyString {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This allows NonEmptyString to be converted to String with .into()
|
||||||
|
impl From<NonEmptyString> for String {
|
||||||
|
fn from(s: NonEmptyString) -> Self {
|
||||||
|
s.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for NonEmptyString {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
if s.is_empty() {
|
||||||
|
Err("String cannot be empty")
|
||||||
|
} else {
|
||||||
|
Ok(NonEmptyString(s.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for NonEmptyString {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
if s.is_empty() {
|
||||||
|
Err("String cannot be empty")
|
||||||
|
} else {
|
||||||
|
Ok(NonEmptyString(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! testdb {
|
macro_rules! testdb {
|
||||||
|
@ -42,12 +42,20 @@ impl User {
|
|||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
updated_by: &ManageUserUser,
|
updated_by: &ManageUserUser,
|
||||||
new_phone: &str,
|
new_phone: &str,
|
||||||
) -> Result<(), String> {
|
) {
|
||||||
let new_phone = new_phone.trim();
|
let new_phone = new_phone.trim();
|
||||||
|
|
||||||
let query = if new_phone.is_empty() {
|
let query = if new_phone.is_empty() {
|
||||||
|
if self.phone.is_none() {
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
sqlx::query!("UPDATE user SET phone = NULL where id = ?", self.id)
|
sqlx::query!("UPDATE user SET phone = NULL where id = ?", self.id)
|
||||||
} else {
|
} else {
|
||||||
|
if let Some(old_phone) = &self.phone {
|
||||||
|
if old_phone == new_phone {
|
||||||
|
return; //nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
sqlx::query!("UPDATE user SET phone = ? where id = ?", new_phone, self.id)
|
sqlx::query!("UPDATE user SET phone = ? where id = ?", new_phone, self.id)
|
||||||
};
|
};
|
||||||
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
||||||
@ -58,8 +66,6 @@ impl User {
|
|||||||
None => format!("{updated_by} has added a phone number for {self}: {new_phone}")
|
None => format!("{updated_by} has added a phone number for {self}: {new_phone}")
|
||||||
};
|
};
|
||||||
Log::create(db, msg).await;
|
Log::create(db, msg).await;
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn update_address(
|
pub(crate) async fn update_address(
|
||||||
@ -67,12 +73,20 @@ impl User {
|
|||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
updated_by: &ManageUserUser,
|
updated_by: &ManageUserUser,
|
||||||
new_address: &str,
|
new_address: &str,
|
||||||
) -> Result<(), String> {
|
) {
|
||||||
let new_address = new_address.trim();
|
let new_address = new_address.trim();
|
||||||
|
|
||||||
let query = if new_address.is_empty() {
|
let query = if new_address.is_empty() {
|
||||||
|
if !self.address.is_none() {
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id)
|
sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id)
|
||||||
} else {
|
} else {
|
||||||
|
if let Some(old_address) = &self.address {
|
||||||
|
if old_address == new_address {
|
||||||
|
return; //nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE user SET address = ? where id = ?",
|
"UPDATE user SET address = ? where id = ?",
|
||||||
new_address,
|
new_address,
|
||||||
@ -87,8 +101,6 @@ impl User {
|
|||||||
None => format!("{updated_by} has added an address for {self}: {new_address}")
|
None => format!("{updated_by} has added an address for {self}: {new_address}")
|
||||||
};
|
};
|
||||||
Log::create(db, msg).await;
|
Log::create(db, msg).await;
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn update_nickname(
|
pub(crate) async fn update_nickname(
|
||||||
@ -313,6 +325,9 @@ impl User {
|
|||||||
if self.has_membership_pdf(db).await {
|
if self.has_membership_pdf(db).await {
|
||||||
return Err(format!("User {self} hat bereits eine Beitrittserklärung."));
|
return Err(format!("User {self} hat bereits eine Beitrittserklärung."));
|
||||||
}
|
}
|
||||||
|
if membership_pdf.len() == 0 {
|
||||||
|
return Err(format!("Keine Beitrittserklärung mitgeschickt."));
|
||||||
|
}
|
||||||
|
|
||||||
let mut stream = membership_pdf.open().await.unwrap();
|
let mut stream = membership_pdf.open().await.unwrap();
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::ScheckbuchUser;
|
use super::ScheckbuchUser;
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
logbook::{Logbook, LogbookWithBoatAndRowers},
|
logbook::{Logbook, LogbookWithBoatAndRowers},
|
||||||
role::Role,
|
|
||||||
user::User,
|
user::User,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
use std::{
|
use std::{fmt::Display, ops::DerefMut};
|
||||||
fmt::Display,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
|
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::{
|
use rocket::{
|
||||||
async_trait,
|
|
||||||
http::{Cookie, Status},
|
http::{Cookie, Status},
|
||||||
request::{self, FromRequest, Outcome},
|
request::{FromRequest, Outcome},
|
||||||
time::{Duration, OffsetDateTime},
|
time::{Duration, OffsetDateTime},
|
||||||
tokio::io::AsyncReadExt,
|
tokio::io::AsyncReadExt,
|
||||||
Request,
|
Request,
|
||||||
@ -35,6 +32,7 @@ use scheckbuch::ScheckbuchUser;
|
|||||||
mod basic;
|
mod basic;
|
||||||
mod fee;
|
mod fee;
|
||||||
pub(crate) mod member;
|
pub(crate) mod member;
|
||||||
|
pub(crate) mod regular;
|
||||||
pub(crate) mod scheckbuch;
|
pub(crate) mod scheckbuch;
|
||||||
|
|
||||||
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
@ -119,10 +117,7 @@ impl User {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.has_role(db, "Donau Linz").await {
|
if self.has_role(db, "schnupperant").await {
|
||||||
self.send_welcome_mail_full_member(db, mail, smtp_pw)
|
|
||||||
.await?;
|
|
||||||
} else if self.has_role(db, "schnupperant").await {
|
|
||||||
self.send_welcome_mail_schnupper(db, mail, smtp_pw).await?;
|
self.send_welcome_mail_schnupper(db, mail, smtp_pw).await?;
|
||||||
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, self).await {
|
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, self).await {
|
||||||
scheckbuch.notify(db, mail, smtp_pw).await?;
|
scheckbuch.notify(db, mail, smtp_pw).await?;
|
||||||
@ -182,57 +177,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_welcome_mail_full_member(
|
|
||||||
&self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
mail: &str,
|
|
||||||
smtp_pw: &str,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
// 2 things to do:
|
|
||||||
// 1. Send mail to user
|
|
||||||
Mail::send_single(
|
|
||||||
db,
|
|
||||||
mail,
|
|
||||||
"Willkommen im ASKÖ Ruderverein Donau Linz!",
|
|
||||||
format!(
|
|
||||||
"Hallo {0},
|
|
||||||
|
|
||||||
herzlich willkommen im ASKÖ Ruderverein Donau Linz! Wir freuen uns sehr, dich als neues Mitglied in unserem Verein begrüßen zu dürfen.
|
|
||||||
|
|
||||||
Um dir den Einstieg zu erleichtern, findest du in unserem Handbuch alle wichtigen Informationen über unseren Verein: https://rudernlinz.at/book. Bei weiteren Fragen stehen dir die Adressen info@rudernlinz.at (für allgemeine Fragen) und it@rudernlinz.at (bei technischen Fragen) jederzeit zur Verfügung.
|
|
||||||
|
|
||||||
Du kannst auch gerne unserer Signal-Gruppe beitreten, um auf dem Laufenden zu bleiben und dich mit anderen Mitgliedern auszutauschen: https://signal.group/#CjQKICFrq6zSsRHxrucS3jEcQn6lknEXacAykwwLV3vNLKxPEhA17jxz7cpjfu3JZokLq1TH
|
|
||||||
|
|
||||||
Für die Organisation unserer Ausfahrten nutzen wir app.rudernlinz.at. Logge dich einfach mit deinem Namen ('{0}' ohne Anführungszeichen) ein, beim ersten Mal kannst du das Passwortfeld leer lassen. Unter 'Geplante Ausfahrten' kannst du dich jederzeit zu den Ausfahrten anmelden.
|
|
||||||
|
|
||||||
Beim nächsten Treffen im Verein, erinnere jemand vom Vorstand (https://rudernlinz.at/unser-verein/vorstand/) bitte daran, deinen Fingerabdruck zu registrieren, damit du Zugang zum Bootshaus erhältst.
|
|
||||||
|
|
||||||
Damit du dich noch mehr verbunden fühlst (:-)), haben wir im Bootshaus ein WLAN für Vereinsmitglieder 'ASKÖ Ruderverein Donau Linz' eingerichtet. Das Passwort dafür lautet 'donau1921' (ohne Anführungszeichen). Bitte gib das Passwort an keine vereinsfremden Personen weiter.
|
|
||||||
|
|
||||||
Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln!
|
|
||||||
|
|
||||||
Riemen- & Dollenbruch
|
|
||||||
ASKÖ Ruderverein Donau Linz", self.name),
|
|
||||||
smtp_pw,
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
// 2. Notify all coxes
|
|
||||||
Notification::create_for_steering_people(
|
|
||||||
db,
|
|
||||||
&format!(
|
|
||||||
"Liebe Steuerberechtigte, seit {} gibt es ein neues Mitglied: {}",
|
|
||||||
self.member_since_date.clone().unwrap(),
|
|
||||||
self.name
|
|
||||||
),
|
|
||||||
"Neues Vereinsmitglied",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn amount_boats(&self, db: &SqlitePool) -> i64 {
|
pub async fn amount_boats(&self, db: &SqlitePool) -> i64 {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT COUNT(*) as count FROM boat WHERE owner = ?",
|
"SELECT COUNT(*) as count FROM boat WHERE owner = ?",
|
||||||
@ -904,7 +848,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
|||||||
impl<'r> FromRequest<'r> for User {
|
impl<'r> FromRequest<'r> for User {
|
||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||||
match req.cookies().get_private("loggedin_user") {
|
match req.cookies().get_private("loggedin_user") {
|
||||||
Some(user_id) => match user_id.value().parse::<i32>() {
|
Some(user_id) => match user_id.value().parse::<i32>() {
|
||||||
Ok(user_id) => {
|
Ok(user_id) => {
|
||||||
@ -939,7 +883,7 @@ macro_rules! special_user {
|
|||||||
pub(crate) user: User,
|
pub(crate) user: User,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for $name {
|
impl std::ops::Deref for $name {
|
||||||
type Target = User;
|
type Target = User;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.user
|
&self.user
|
||||||
@ -953,20 +897,20 @@ macro_rules! special_user {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'r> FromRequest<'r> for $name {
|
impl<'r> rocket::request::FromRequest<'r> for $name {
|
||||||
type Error = LoginError;
|
type Error = crate::model::user::LoginError;
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r rocket::request::Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||||
match User::from_request(req).await {
|
match User::from_request(req).await {
|
||||||
Outcome::Success(user) => {
|
rocket::request::Outcome::Success(user) => {
|
||||||
if special_user!(@check_roles user, db, $($role)*) {
|
if special_user!(@check_roles user, db, $($role)*) {
|
||||||
Outcome::Success($name { user })
|
rocket::request::Outcome::Success($name { user })
|
||||||
} else {
|
} else {
|
||||||
Outcome::Forward(Status::Forbidden)
|
rocket::request::Outcome::Forward(rocket::http::Status::Forbidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
rocket::request::Outcome::Error(f) => rocket::request::Outcome::Error(f),
|
||||||
Outcome::Forward(f) => Outcome::Forward(f),
|
rocket::request::Outcome::Forward(f) => rocket::request::Outcome::Forward(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1007,8 +951,10 @@ special_user!(TechUser, +"tech");
|
|||||||
special_user!(ErgoUser, +"ergo");
|
special_user!(ErgoUser, +"ergo");
|
||||||
special_user!(SteeringUser, +"cox", +"Bootsführer");
|
special_user!(SteeringUser, +"cox", +"Bootsführer");
|
||||||
special_user!(AdminUser, +"admin");
|
special_user!(AdminUser, +"admin");
|
||||||
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch");
|
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
|
||||||
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
|
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO:
|
||||||
|
// remove ->
|
||||||
|
// RegularUser
|
||||||
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
|
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
|
||||||
special_user!(VorstandUser, +"admin", +"Vorstand");
|
special_user!(VorstandUser, +"admin", +"Vorstand");
|
||||||
special_user!(EventUser, +"manage_events");
|
special_user!(EventUser, +"manage_events");
|
||||||
|
73
src/model/user/regular.rs
Normal file
73
src/model/user/regular.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use super::User;
|
||||||
|
use crate::{
|
||||||
|
model::{mail::Mail, notification::Notification},
|
||||||
|
special_user,
|
||||||
|
};
|
||||||
|
use rocket::async_trait;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(RegularUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
|
||||||
|
|
||||||
|
impl RegularUser {
|
||||||
|
pub(crate) async fn notify(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
|
||||||
|
self.notify_coxes_about_new_regular(db).await;
|
||||||
|
self.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_welcome_mail_to_user(
|
||||||
|
&self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let Some(mail) = &self.mail else {
|
||||||
|
return Err(format!(
|
||||||
|
"Couldn't send welcome mail, as the user {self} has no mail..."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Mail::send_single(
|
||||||
|
db,
|
||||||
|
mail,
|
||||||
|
"Willkommen im ASKÖ Ruderverein Donau Linz!",
|
||||||
|
format!(
|
||||||
|
"Hallo {0},
|
||||||
|
|
||||||
|
herzlich willkommen im ASKÖ Ruderverein Donau Linz! Wir freuen uns sehr, dich als neues Mitglied in unserem Verein begrüßen zu dürfen.
|
||||||
|
|
||||||
|
Um dir den Einstieg zu erleichtern, findest du in unserem Handbuch alle wichtigen Informationen über unseren Verein: https://rudernlinz.at/book. Bei weiteren Fragen stehen dir die Adressen info@rudernlinz.at (für allgemeine Fragen) und it@rudernlinz.at (bei technischen Fragen) jederzeit zur Verfügung.
|
||||||
|
|
||||||
|
Du kannst auch gerne unserer Signal-Gruppe beitreten, um auf dem Laufenden zu bleiben und dich mit anderen Mitgliedern auszutauschen: https://signal.group/#CjQKICFrq6zSsRHxrucS3jEcQn6lknEXacAykwwLV3vNLKxPEhA17jxz7cpjfu3JZokLq1TH
|
||||||
|
|
||||||
|
Für die Organisation unserer Ausfahrten nutzen wir app.rudernlinz.at. Logge dich einfach mit deinem Namen ('{0}' ohne Anführungszeichen) ein, beim ersten Mal kannst du das Passwortfeld leer lassen. Unter 'Geplante Ausfahrten' kannst du dich jederzeit zu den Ausfahrten anmelden.
|
||||||
|
|
||||||
|
Beim nächsten Treffen im Verein, erinnere jemand vom Vorstand (https://rudernlinz.at/unser-verein/vorstand/) bitte daran, deinen Fingerabdruck zu registrieren, damit du Zugang zum Bootshaus erhältst.
|
||||||
|
|
||||||
|
Damit du dich noch mehr verbunden fühlst (:-)), haben wir im Bootshaus ein WLAN für Vereinsmitglieder 'ASKÖ Ruderverein Donau Linz' eingerichtet. Das Passwort dafür lautet 'donau1921' (ohne Anführungszeichen). Bitte gib das Passwort an keine vereinsfremden Personen weiter.
|
||||||
|
|
||||||
|
Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln!
|
||||||
|
|
||||||
|
Riemen- & Dollenbruch
|
||||||
|
ASKÖ Ruderverein Donau Linz", self.name),
|
||||||
|
smtp_pw,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn notify_coxes_about_new_regular(&self, db: &SqlitePool) {
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, seit {} gibt es ein neues Mitglied: {}",
|
||||||
|
self.member_since_date.clone().unwrap(),
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neues Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
use super::member::Member;
|
use super::regular::RegularUser;
|
||||||
use super::{ManageUserUser, User};
|
use super::{ManageUserUser, User};
|
||||||
use crate::model::role::Role;
|
use crate::model::role::Role;
|
||||||
use crate::model::user::LoginError;
|
use crate::NonEmptyString;
|
||||||
use crate::tera::admin::user::ScheckToRegularForm;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{mail::Mail, notification::Notification},
|
model::{mail::Mail, notification::Notification},
|
||||||
special_user, SCHECKBUCH,
|
special_user, SCHECKBUCH,
|
||||||
@ -10,13 +9,7 @@ use crate::{
|
|||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use rocket::async_trait;
|
use rocket::async_trait;
|
||||||
use rocket::fs::TempFile;
|
use rocket::fs::TempFile;
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::request;
|
|
||||||
use rocket::request::FromRequest;
|
|
||||||
use rocket::request::Outcome;
|
|
||||||
use rocket::Request;
|
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
special_user!(ScheckbuchUser, +"scheckbuch");
|
special_user!(ScheckbuchUser, +"scheckbuch");
|
||||||
|
|
||||||
@ -24,11 +17,12 @@ impl ScheckbuchUser {
|
|||||||
pub(crate) async fn convert_to_regular_user(
|
pub(crate) async fn convert_to_regular_user(
|
||||||
self,
|
self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
changed_by: &ManageUserUser,
|
changed_by: &ManageUserUser,
|
||||||
member_since: &NaiveDate,
|
member_since: &NaiveDate,
|
||||||
birthdate: &NaiveDate,
|
birthdate: &NaiveDate,
|
||||||
phone: &str,
|
phone: NonEmptyString,
|
||||||
address: &str,
|
address: NonEmptyString,
|
||||||
membership_pdf: &TempFile<'_>,
|
membership_pdf: &TempFile<'_>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Set data
|
// Set data
|
||||||
@ -36,9 +30,9 @@ impl ScheckbuchUser {
|
|||||||
self.user
|
self.user
|
||||||
.update_member_since(db, changed_by, member_since)
|
.update_member_since(db, changed_by, member_since)
|
||||||
.await;
|
.await;
|
||||||
self.user.update_phone(db, changed_by, phone).await?;
|
|
||||||
self.user.update_address(db, changed_by, address).await?;
|
self.user.update_phone(db, changed_by, &phone).await;
|
||||||
self.user.update_address(db, changed_by, address).await?;
|
self.user.update_address(db, changed_by, &address).await;
|
||||||
self.user
|
self.user
|
||||||
.add_membership_pdf(db, changed_by, membership_pdf)
|
.add_membership_pdf(db, changed_by, membership_pdf)
|
||||||
.await?;
|
.await?;
|
||||||
@ -50,25 +44,13 @@ impl ScheckbuchUser {
|
|||||||
self.user.add_role(db, changed_by, ®ular).await?;
|
self.user.add_role(db, changed_by, ®ular).await?;
|
||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
todo!() // Continue here
|
let regular = RegularUser::new(db, &self.user).await.unwrap();
|
||||||
|
regular.notify(db, smtp_pw).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
//async fn from(user: User, db: &SqlitePool, mail: &str, smtp_pw: &str) -> Result<(), String> {
|
// TODO: make private
|
||||||
// if user.has_role(db, "scheckbuch").await {
|
|
||||||
// return Err("User is already a scheckbuch".into());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO: do we allow e.g. DonauLinz to scheckbuch?
|
|
||||||
|
|
||||||
// let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
|
||||||
// user.add_role(db, &scheckbuch).await.unwrap();
|
|
||||||
|
|
||||||
// // TODO: remove all other `membership_type` roles
|
|
||||||
// let new_user = Self::new(db, &user).await.unwrap();
|
|
||||||
|
|
||||||
// new_user.notify(db, mail, smtp_pw).await
|
|
||||||
//}
|
|
||||||
|
|
||||||
pub(crate) async fn notify(
|
pub(crate) async fn notify(
|
||||||
&self,
|
&self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
|
@ -390,13 +390,11 @@ async fn update_phone(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
match user.update_phone(db, &admin, &data.phone).await {
|
user.update_phone(db, &admin, &data.phone).await;
|
||||||
Ok(_) => Flash::success(
|
Flash::success(
|
||||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||||
"Telefonnummer erfolgreich geändert",
|
"Telefonnummer erfolgreich geändert",
|
||||||
),
|
)
|
||||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
@ -418,13 +416,12 @@ async fn update_address(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
match user.update_address(db, &admin, &data.address).await {
|
user.update_address(db, &admin, &data.address).await;
|
||||||
Ok(_) => Flash::success(
|
|
||||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
Flash::success(
|
||||||
"Adresse erfolgreich geändert",
|
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||||
),
|
"Adresse erfolgreich geändert",
|
||||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
@ -831,6 +828,7 @@ async fn scheckbook_to_regular(
|
|||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<ScheckToRegularForm<'_>>,
|
data: Form<ScheckToRegularForm<'_>>,
|
||||||
admin: ManageUserUser,
|
admin: ManageUserUser,
|
||||||
|
config: &State<Config>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let Some(user) = User::find_by_id(db, id).await else {
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
@ -842,13 +840,19 @@ async fn scheckbook_to_regular(
|
|||||||
let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
||||||
return Flash::error(
|
return Flash::error(
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
format!("Datum {} ist nicht im YYYY-MM-DD Format", &data.birthdate),
|
format!(
|
||||||
|
"Geburtsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||||
|
&data.birthdate
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else {
|
let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else {
|
||||||
return Flash::error(
|
return Flash::error(
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
format!("Datum {} ist nicht im YYYY-MM-DD Format", &data.birthdate),
|
format!(
|
||||||
|
"Beitrittsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||||
|
&data.birthdate
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -859,14 +863,28 @@ async fn scheckbook_to_regular(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let Ok(phone) = data.phone.clone().try_into() else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
"Vereinsmitglied braucht eine Telefonnummer",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let Ok(address) = data.address.clone().try_into() else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
"Vereinsmitglied braucht eine Adresse",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
match user
|
match user
|
||||||
.convert_to_regular_user(
|
.convert_to_regular_user(
|
||||||
db,
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
&admin,
|
&admin,
|
||||||
&member_since,
|
&member_since,
|
||||||
&birthdate,
|
&birthdate,
|
||||||
&data.phone,
|
phone,
|
||||||
&data.address,
|
address,
|
||||||
&data.membership_pdf,
|
&data.membership_pdf,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -4,323 +4,329 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="max-w-screen-lg w-full">
|
<div class="max-w-screen-lg w-full">
|
||||||
<h1 class="h1">{{ user.name }}</h1>
|
<h1 class="h1">{{ user.name }}</h1>
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="grid sm:grid-cols-2 gap-3">
|
||||||
role="alert">
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||||
<h2 class="h2">Grunddaten</h2>
|
role="alert">
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<h2 class="h2">
|
||||||
<div class="py-3">
|
Grunddaten
|
||||||
{% if user.last_access %}
|
<br />
|
||||||
Zuletzt eingeloggt am {{ user.last_access | date(format="%d. %m. %Y") }}
|
<small class="inline-block text-xs text-gray-500 dark:text-gray-100 ">
|
||||||
{% else %}
|
{% if user.last_access %}
|
||||||
{{ user.name }} hat sich noch nie eingeloggt.
|
Zuletzt eingeloggt am {{ user.last_access | date(format="%d. %m. %Y") }}
|
||||||
|
{% else %}
|
||||||
|
{{ user.name }} hat sich noch nie eingeloggt.
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
</h2>
|
||||||
|
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
|
<div class="py-3 grid gap-3">
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-mail" method="post">
|
||||||
|
{{ macros::inputgroup(label='Mailadresse', name='mail', type="text", value=user.mail, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-phone" method="post">
|
||||||
|
{{ macros::inputgroup(label='Telefonnummer', name='phone', type="text", value=user.phone, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-nickname" method="post">
|
||||||
|
{{ macros::inputgroup(label='Spitzname', name='nickname', type="text", value=user.nickname, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<span>Notizen: to be replaced with activity :-)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||||
|
role="alert">
|
||||||
|
<h2 class="h2">
|
||||||
|
Mitgliedschaft
|
||||||
|
<br />
|
||||||
|
<small class="inline-block text-xs text-gray-500 dark:text-gray-100 ">
|
||||||
|
{% if "SchnupperInterest" in member %}
|
||||||
|
Interessiert am Schnupperkurs
|
||||||
|
{% elif "Schnupperant" in member %}
|
||||||
|
Beim nächsten Schnupperkurs angemeldet
|
||||||
|
{% elif "Scheckbuch" in member %}
|
||||||
|
{% set logbook = member["Scheckbuch"] %}
|
||||||
|
Scheckbuch (Ausfahrten: {{ logbook | length }})
|
||||||
|
{% elif "Regular" in member %}
|
||||||
|
Reguläres Vereinsmitglied
|
||||||
|
{% elif "Foerdernd" in member %}
|
||||||
|
Förderndes Vereinsmitglied
|
||||||
|
{% elif "Unterstuetzend" in member %}
|
||||||
|
Unterstützendes Vereinsmitglied
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
</h2>
|
||||||
|
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
|
{% if is_clubmember %}
|
||||||
|
<div class="py-3 grid gap-3">
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-member-since" method="post">
|
||||||
|
{{ macros::inputgroup(label='Mitglied seit', name='member_since', type="date", value=user.member_since_date, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-birthdate" method="post">
|
||||||
|
{{ macros::inputgroup(label='Geburtsdatum', name='birthdate', type="date", value=user.birthdate, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-address" method="post">
|
||||||
|
{{ macros::inputgroup(label='Adresse', name='address', type="text", value=user.address, readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
<form action="/admin/user/{{ user.id }}/change-family" method="post">
|
||||||
|
{{ macros::selectgroup(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen', readonly=not allowed_to_edit) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="py-3">
|
||||||
|
{% if user.membership_pdf %}
|
||||||
|
<a href="/admin/user/{{ user.id }}/membership" class="link link-primary">Beitrittserklärung herunterladen</a>
|
||||||
|
{% else %}
|
||||||
|
⚠️ Aktuell gibt's keine Beitrittserklärung 😢
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
Das kannst du hier ändern ⤵️
|
||||||
|
<form action="/admin/user/{{ user.id }}/add-membership-pdf"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="grid gap-3">
|
||||||
|
<fieldset>
|
||||||
|
{{ macros::input(label='Neue Beitrittserklärung hochladen', name='membership_pdf', type="file", accept='application/pdf') }}
|
||||||
|
</fieldset>
|
||||||
|
<input value="Hochladen" type="submit" class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<div class="py-3">
|
||||||
|
<div class="mt-3 text-right">
|
||||||
|
<a href="/admin/user/{{ user.id }}/delete"
|
||||||
|
class="btn btn-alert"
|
||||||
|
onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Mitglied ist ausgetreten
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% elif "Scheckbuch" in member %}
|
||||||
|
<div class="grid gap-3 pb-3">
|
||||||
|
{% for log in logbook %}
|
||||||
|
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=false) }}
|
||||||
|
{% endfor %}
|
||||||
|
<button type="button"
|
||||||
|
onclick="document.getElementById('call-for-action').showModal()"
|
||||||
|
class="btn btn-primary">Zu Vereinsmitglied umwandeln</button>
|
||||||
|
</div>
|
||||||
|
<dialog id="call-for-action"
|
||||||
|
class="max-w-screen-sm w-full dark:bg-primary-600 dark:text-white rounded-md"
|
||||||
|
onclick="document.getElementById('call-for-action').close()">
|
||||||
|
<div onclick="event.stopPropagation();" class="p-3">
|
||||||
|
<button type="button"
|
||||||
|
onclick="document.getElementById('call-for-action').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">
|
||||||
|
<form action="/admin/user/{{ user.id }}/scheckbook-to-regular"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="grid gap-3">
|
||||||
|
Type: Select -> normales Mitglied, förderndes Mitglied, unterstützendes Mitglied
|
||||||
|
{{ macros::input(label='Mitglied seit', name='member_since', type="date", value=now() | date(), required=true) }}
|
||||||
|
{{ macros::input(label='Geburtsdatum', name='birthdate', type="date", value=user.birthdate, required=true) }}
|
||||||
|
{{ macros::input(label='Telefonnummer', name='phone', type="text", value=user.phone, required=true) }}
|
||||||
|
{{ macros::input(label='Adresse', name='address', type="text", value=user.address, required=true) }}
|
||||||
|
{{ macros::input(label='Beitrittserklärung', name='membership_pdf', type="file", accept='application/pdf', required=true) }}
|
||||||
|
<input value="Als neues, reguläres Mitglied anlegen"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="py-3">
|
</div>
|
||||||
<ul class="grid gap-3">
|
{% if is_clubmember %}
|
||||||
<li>
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||||
<form action="/admin/user/{{ user.id }}/change-mail" method="post">
|
role="alert">
|
||||||
{{ macros::inputgroup(label='Mailadresse', name='mail', type="text", value=user.mail, readonly=not allowed_to_edit) }}
|
<h2 class="h2">Rollen</h2>
|
||||||
</form>
|
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
</li>
|
<div class="py-3">
|
||||||
<li>
|
<ul>
|
||||||
<form action="/admin/user/{{ user.id }}/change-phone" method="post">
|
{% for role in user.proper_roles -%}
|
||||||
{{ macros::inputgroup(label='Telefonnummer', name='phone', type="text", value=user.phone, readonly=not allowed_to_edit) }}
|
{% if not role.cluster and not role.hide_in_lists %}
|
||||||
</form>
|
<li class="flex my-2 w-full justify-between items-center hover:bg-gray-100">
|
||||||
</li>
|
<span>
|
||||||
<li>
|
<strong>
|
||||||
<form action="/admin/user/{{ user.id }}/change-nickname" method="post">
|
|
||||||
{{ macros::inputgroup(label='Spitzname', name='nickname', type="text", value=user.nickname, readonly=not allowed_to_edit) }}
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li>Notizen: to be replaced with activity :-)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="py-3">
|
|
||||||
Rollen:
|
|
||||||
<ul class="list-disc ms-4">
|
|
||||||
{% for role in user.proper_roles -%}
|
|
||||||
{% if not role.cluster and not role.hide_in_lists %}
|
|
||||||
<li>
|
|
||||||
<strong>
|
|
||||||
{% if role.formatted_name %}
|
|
||||||
{{ role.formatted_name }}
|
|
||||||
{% else %}
|
|
||||||
{{ role.name }}
|
|
||||||
{% endif %}
|
|
||||||
</strong> {{ role.desc }}
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<a href="/admin/user/{{ user.id }}/remove-role/{{ role.id }}"
|
|
||||||
onclick="return confirm('Willst du die Rolle \'{{ role.name }}\' von {{ user.name }} wirklich entfernen?');">🗑️</a>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<details>
|
|
||||||
<summary>+ Rolle</summary>
|
|
||||||
<form action="/admin/user/{{ user.id }}/add-role" method="post">
|
|
||||||
<fieldset>
|
|
||||||
<select name="role_id">
|
|
||||||
{% for role in roles %}
|
|
||||||
{% if not role.cluster and role not in user.proper_roles and not role.hide_in_lists %}
|
|
||||||
<option value="{{ role.id }}">
|
|
||||||
{% if role.formatted_name %}
|
{% if role.formatted_name %}
|
||||||
{{ role.formatted_name }}
|
{{ role.formatted_name }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ role.name }}
|
{{ role.name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if role.desc %}({{ role.desc }}){% endif %}
|
</strong>
|
||||||
</option>
|
<br />
|
||||||
|
<small>{{ role.desc }}</small>
|
||||||
|
</span>
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<a href="/admin/user/{{ user.id }}/remove-role/{{ role.id }}"
|
||||||
|
onclick="return confirm('Willst du die Rolle \'{{ role.name }}\' von {{ user.name }} wirklich entfernen?');">🗑️</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
</li>
|
||||||
</select>
|
|
||||||
<input value="Rolle hinzufügen" type="submit" class="btn btn-primary ml-1" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if supposed_to_pay %}
|
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">💸</h2>
|
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
|
||||||
<div class="py-3">
|
|
||||||
{% if fee %}
|
|
||||||
<div>
|
|
||||||
<strong>{{ fee.name }}</strong>
|
|
||||||
<span class="block">{{ fee.sum_in_cents / 100 }}€</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{% for p in fee.parts %}
|
|
||||||
{{ p.0 }} ({{ p.1 / 100 }}€)
|
|
||||||
{% if not loop.last %}+{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% if "paid" in user.roles %}
|
|
||||||
✅ bezahlt
|
|
||||||
{% else %}
|
|
||||||
❌ Zahlung ausständig
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
{% if "paid" in user.roles %}
|
|
||||||
✅ {{ member | keys }} hat schon bezahlt
|
|
||||||
{% else %}
|
|
||||||
❌
|
|
||||||
{% for key, value in member %}
|
|
||||||
{% if loop.first %}{{ key }}{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
hat noch nicht bezahlt
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if is_clubmember %}
|
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">Vereinsmitglied</h2>
|
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
|
||||||
<div class="py-3">
|
|
||||||
<ul class="grid gap-3">
|
|
||||||
<li>
|
|
||||||
<form action="/admin/user/{{ user.id }}/change-member-since" method="post">
|
|
||||||
{{ macros::inputgroup(label='Mitglied seit', name='member_since', type="date", value=user.member_since_date, readonly=not allowed_to_edit) }}
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<form action="/admin/user/{{ user.id }}/change-birthdate" method="post">
|
|
||||||
{{ macros::inputgroup(label='Geburtsdatum', name='birthdate', type="date", value=user.birthdate, readonly=not allowed_to_edit) }}
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<form action="/admin/user/{{ user.id }}/change-address" method="post">
|
|
||||||
{{ macros::inputgroup(label='Adresse', name='address', type="text", value=user.address, readonly=not allowed_to_edit) }}
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Familie:
|
|
||||||
{% for family in families %}
|
|
||||||
{% if user.family_id == family.id %}{{ family.names }}{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<details>
|
|
||||||
<summary>✏️</summary>
|
|
||||||
<form action="/admin/user/{{ user.id }}/change-family" method="post">
|
|
||||||
{{ macros::select(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen') }}
|
|
||||||
<input value="Ändern" type="submit" class="btn btn-primary ml-1" />
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="py-3">
|
|
||||||
{% if user.membership_pdf %}
|
|
||||||
<a href="/admin/user/{{ user.id }}/membership"
|
|
||||||
class="text-black dark:text-white">Beitrittserklärung</a>
|
|
||||||
{% else %}
|
|
||||||
⚠️ Aktuell gibt's keine Beitrittserklärung 😢
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
Das kannst du hier ändern ⤵️
|
|
||||||
<form action="/admin/user/{{ user.id }}/add-membership-pdf"
|
|
||||||
method="post"
|
|
||||||
enctype="multipart/form-data">
|
|
||||||
<fieldset>
|
|
||||||
{{ macros::input(label='Neue Beitrittserklärung hochladen', name='membership_pdf', type="file", accept='application/pdf') }}
|
|
||||||
<input value="Hochladen" type="submit" class="btn btn-primary ml-1" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<div class="py-3">
|
|
||||||
<div class="mt-3 text-right">
|
|
||||||
<a href="/admin/user/{{ user.id }}/delete"
|
|
||||||
class="btn btn-alert"
|
|
||||||
onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');">
|
|
||||||
{% include "includes/delete-icon" %}
|
|
||||||
Mitglied ist ausgetreten
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% 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">Mitgliedstyp</h2>
|
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
|
||||||
<div class="py-3">
|
|
||||||
{{ user.name }}
|
|
||||||
{% if "SchnupperInterest" in member %}
|
|
||||||
ist interessiert am Schnupperkurs.
|
|
||||||
{% elif "Schnupperant" in member %}
|
|
||||||
ist beim nächsten Schnupperkurs angemeldet.
|
|
||||||
{% elif "Scheckbuch" in member %}
|
|
||||||
{% set logbook = member["Scheckbuch"] %}
|
|
||||||
hat ein Scheckbuch und {{ logbook | length }} Ausfahrten absolviert.
|
|
||||||
<details>
|
|
||||||
<summary>Ausfahrten</summary>
|
|
||||||
{% for log in logbook %}
|
|
||||||
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=false) }}
|
|
||||||
{% endfor %}
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Zu reguläres Vereinsmitglied umwandeln</summary>
|
|
||||||
<form action="/admin/user/{{ user.id }}/scheckbook-to-regular"
|
|
||||||
method="post"
|
|
||||||
enctype="multipart/form-data">
|
|
||||||
{{ macros::input(label='Mitglied seit', name='member_since', type="date", value=now() | date()) }}
|
|
||||||
{{ macros::input(label='Geburtsdatum', name='birthdate', type="date", value=user.birthdate) }}
|
|
||||||
{{ macros::input(label='Telefonnummer', name='phone', type="text", value=user.phone) }}
|
|
||||||
{{ macros::input(label='Adresse', name='address', type="text", value=user.address) }}
|
|
||||||
{{ macros::input(label='Beitrittserklärung', name='membership_pdf', type="file", accept='application/pdf') }}
|
|
||||||
<input value="Als neues, reguläres Mitglied anlegen"
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-primary ml-1" />
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
{% elif "Regular" in member %}
|
|
||||||
ist ein reguläres Vereinsmitglied.
|
|
||||||
{% elif "Foerdernd" in member %}
|
|
||||||
ist ein förderndes Vereinsmitglied.
|
|
||||||
{% elif "Unterstuetzend" in member %}
|
|
||||||
ist ein unterstützendes Vereinsmitglied.
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">Aktivität von und mit {{ user.name }}</h2>
|
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
|
||||||
<div class="py-3">
|
|
||||||
<ul class="list-disc ms-4">
|
|
||||||
<li>Passwort zurückgesetzt am/um X</li>
|
|
||||||
<li>Am X beigetreten.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">TODO</h2>
|
|
||||||
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
|
||||||
<span class="text-black dark:text-white cursor-pointer">
|
|
||||||
<span class="font-bold">
|
|
||||||
{{ user.name }}
|
|
||||||
{% if not user.last_access and allowed_to_edit and user.mail %}
|
|
||||||
<form action="/admin/user"
|
|
||||||
method="post"
|
|
||||||
enctype="multipart/form-data"
|
|
||||||
class="inline">
|
|
||||||
• <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
|
||||||
href="/admin/user/{{ user.id }}/send-welcome-mail"
|
|
||||||
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<form action="/admin/user"
|
|
||||||
method="post"
|
|
||||||
enctype="multipart/form-data"
|
|
||||||
class="w-full mt-2">
|
|
||||||
{% if user.pw %}
|
|
||||||
<a class="block my-1 font-normal text-[#f43f5e] dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
|
||||||
href="/admin/user/{{ user.id }}/reset-pw"
|
|
||||||
onclick="return confirm('Willst du wirklich das Passwort zurücksetzen?');">Passwort zurücksetzen</a>
|
|
||||||
{% endif %}
|
|
||||||
<div class="w-full grid gap-3 mt-3">
|
|
||||||
<input type="hidden" name="id" value="{{ user.id }}" />
|
|
||||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
|
||||||
{% for cluster, cluster_roles in roles | group_by(attribute="cluster") %}
|
|
||||||
<label for="cluster_{{ loop.index }}">{{ cluster }}</label>
|
|
||||||
{# Determine the initially selected role within the cluster #}
|
|
||||||
{% set_global selected_role_id = "none" %}
|
|
||||||
{% for role in cluster_roles %}
|
|
||||||
{% if selected_role_id == "none" and role.name in user.roles %}
|
|
||||||
{% set_global selected_role_id = role.id %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# Set default name to the selected role ID or first role if none selected #}
|
</ul>
|
||||||
<select id="cluster_{{ loop.index }}"
|
{% if allowed_to_edit %}
|
||||||
{% if selected_role_id == 'none' %} {% else %} name="roles[{{ selected_role_id }}]" {% endif %}
|
<details>
|
||||||
{% if allowed_to_edit == false %}disabled{% endif %}
|
<summary>+ Rolle</summary>
|
||||||
onchange=" if (this.value === '') { this.removeAttribute('name'); } else { this.name = 'roles[' + this.options[this.selectedIndex].getAttribute('data-role-id') + ']'; }">
|
<form action="/admin/user/{{ user.id }}/add-role" method="post">
|
||||||
<option value=""
|
<fieldset>
|
||||||
data-role-id="none"
|
<select name="role_id">
|
||||||
{% if selected_role_id == 'none' %}selected="selected"{% endif %}>
|
{% for role in roles %}
|
||||||
None
|
{% if not role.cluster and role not in user.proper_roles and not role.hide_in_lists %}
|
||||||
</option>
|
<option value="{{ role.id }}">
|
||||||
{% for role in cluster_roles %}
|
{% if role.formatted_name %}
|
||||||
<option value="on"
|
{{ role.formatted_name }}
|
||||||
data-role-id="{{ role.id }}"
|
{% else %}
|
||||||
{% if role.id == selected_role_id %}selected="selected"{% endif %}>
|
{{ role.name }}
|
||||||
{{ role.name }}
|
{% endif %}
|
||||||
</option>
|
{% if role.desc %}({{ role.desc }}){% endif %}
|
||||||
{% endfor %}
|
</option>
|
||||||
</select>
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input value="Rolle hinzufügen" type="submit" class="btn btn-primary ml-1" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if supposed_to_pay %}
|
||||||
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||||
|
role="alert">
|
||||||
|
<h2 class="h2">💸-Beitrag</h2>
|
||||||
|
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
|
<div class="py-3">
|
||||||
|
{% if fee %}
|
||||||
|
<div>
|
||||||
|
<strong>{{ fee.name }}</strong>
|
||||||
|
<span class="block">{{ fee.sum_in_cents / 100 }}€</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% for p in fee.parts %}
|
||||||
|
{{ p.0 }} ({{ p.1 / 100 }}€)
|
||||||
|
{% if not loop.last %}+{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if "paid" in user.roles %}
|
||||||
|
✅ bezahlt
|
||||||
|
{% else %}
|
||||||
|
❌ Zahlung ausständig
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% if "paid" in user.roles %}
|
||||||
|
✅ {{ member | keys }} hat schon bezahlt
|
||||||
|
{% else %}
|
||||||
|
❌
|
||||||
|
{% for key, value in member %}
|
||||||
|
{% if loop.first %}{{ key }}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
hat noch nicht bezahlt
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% 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">Aktivität von und mit {{ user.name }}</h2>
|
||||||
|
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
|
<div class="py-3">
|
||||||
|
<ul class="list-disc ms-4">
|
||||||
|
<li>Passwort zurückgesetzt am/um X</li>
|
||||||
|
<li>Am X beigetreten.</li>
|
||||||
|
</ul>
|
||||||
|
</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 mt-5"
|
role="alert">
|
||||||
role="alert">
|
<h2 class="h2">TODO</h2>
|
||||||
<h2 class="h2">Ergo-Challenge</h2>
|
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<span class="text-black dark:text-white cursor-pointer">
|
||||||
<div class="py-3">
|
<span class="font-bold">
|
||||||
{{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
{{ user.name }}
|
||||||
{{ macros::input(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
{% if not user.last_access and allowed_to_edit and user.mail %}
|
||||||
{{ macros::input(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
|
<form action="/admin/user"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="inline">
|
||||||
|
• <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
||||||
|
href="/admin/user/{{ user.id }}/send-welcome-mail"
|
||||||
|
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<form action="/admin/user"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="w-full mt-2">
|
||||||
|
{% if user.pw %}
|
||||||
|
<a class="block my-1 font-normal text-[#f43f5e] dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
||||||
|
href="/admin/user/{{ user.id }}/reset-pw"
|
||||||
|
onclick="return confirm('Willst du wirklich das Passwort zurücksetzen?');">Passwort zurücksetzen</a>
|
||||||
|
{% endif %}
|
||||||
|
<div class="w-full grid gap-3 mt-3">
|
||||||
|
<input type="hidden" name="id" value="{{ user.id }}" />
|
||||||
|
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
|
{% for cluster, cluster_roles in roles | group_by(attribute="cluster") %}
|
||||||
|
<label for="cluster_{{ loop.index }}">{{ cluster }}</label>
|
||||||
|
{# Determine the initially selected role within the cluster #}
|
||||||
|
{% set_global selected_role_id = "none" %}
|
||||||
|
{% for role in cluster_roles %}
|
||||||
|
{% if selected_role_id == "none" and role.name in user.roles %}
|
||||||
|
{% set_global selected_role_id = role.id %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{# Set default name to the selected role ID or first role if none selected #}
|
||||||
|
<select id="cluster_{{ loop.index }}"
|
||||||
|
{% if selected_role_id == 'none' %} {% else %} name="roles[{{ selected_role_id }}]" {% endif %}
|
||||||
|
{% if allowed_to_edit == false %}disabled{% endif %}
|
||||||
|
onchange=" if (this.value === '') { this.removeAttribute('name'); } else { this.name = 'roles[' + this.options[this.selectedIndex].getAttribute('data-role-id') + ']'; }">
|
||||||
|
<option value=""
|
||||||
|
data-role-id="none"
|
||||||
|
{% if selected_role_id == 'none' %}selected="selected"{% endif %}>
|
||||||
|
None
|
||||||
|
</option>
|
||||||
|
{% for role in cluster_roles %}
|
||||||
|
<option value="on"
|
||||||
|
data-role-id="{{ role.id }}"
|
||||||
|
{% if role.id == selected_role_id %}selected="selected"{% endif %}>
|
||||||
|
{{ role.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
|
<div class="py-3">
|
||||||
|
{{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
||||||
|
{{ macros::input(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
||||||
|
{{ macros::input(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -174,10 +174,9 @@ function setChoiceByLabel(choicesInstance, label) {
|
|||||||
{% if autofocus %}autofocus{% endif %}
|
{% if autofocus %}autofocus{% endif %}
|
||||||
{% if accept %}accept="{{ accept }}"{% endif %}
|
{% if accept %}accept="{{ accept }}"{% endif %}
|
||||||
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
||||||
{% if readonly %}readonly{% endif %}/>
|
{% if readonly %}readonly{% endif %} />
|
||||||
</div>
|
</div>
|
||||||
{% endmacro input %}
|
{% endmacro input %}
|
||||||
|
|
||||||
{% macro inputgroup(label, name, type, required=false, class='', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false, accept='') %}
|
{% macro inputgroup(label, name, type, required=false, class='', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false, accept='') %}
|
||||||
<div class="{{ wrapper_class }}">
|
<div class="{{ wrapper_class }}">
|
||||||
<label for="{{ name }}"
|
<label for="{{ name }}"
|
||||||
@ -185,29 +184,77 @@ function setChoiceByLabel(choicesInstance, label) {
|
|||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input {% if type=='datetime-local' %}onclick='if (!this.value) setCurrentdate(this)'{% endif %}
|
<input {% if type=='datetime-local' %}onclick='if (!this.value) setCurrentdate(this)'{% endif %}
|
||||||
{% if id %} id="{{ id }}" {% else %} id="{{ name }}" {% endif %}
|
{% if id %} id="{{ id }}" {% else %} id="{{ name }}" {% endif %}
|
||||||
name="{{ name }}"
|
name="{{ name }}"
|
||||||
type="{{ type }}"
|
type="{{ type }}"
|
||||||
{% if required %}required{% endif %}
|
{% if required %}required{% endif %}
|
||||||
value="{{ value }}"
|
value="{{ value }}"
|
||||||
class="input {% if readonly %}rounded-md{% else %}rounded-l-md{% endif %} {{ class }}"
|
class="input {% if readonly %}rounded-md{% else %}rounded-l-md{% endif %} {{ class }}"
|
||||||
placeholder="{% if hide_label %}{{ label }}{% endif %}"
|
placeholder="{% if hide_label %}{{ label }}{% endif %}"
|
||||||
{% if min is defined %}min="{{ min }}"{% endif %}
|
{% if min is defined %}min="{{ min }}"{% endif %}
|
||||||
{% if autofocus %}autofocus{% endif %}
|
{% if autofocus %}autofocus{% endif %}
|
||||||
{% if accept %}accept="{{ accept }}"{% endif %}
|
{% if accept %}accept="{{ accept }}"{% endif %}
|
||||||
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
||||||
readonly/>
|
readonly />
|
||||||
{% if allowed_to_edit %}
|
{% if allowed_to_edit %}
|
||||||
<button type="button" class="btn btn-primary rounded-l-none-important edit-js">Ändern</button>
|
<button type="button" class="btn btn-dark rounded-l-none-important edit-js">{% include "includes/pencil" %}</button>
|
||||||
<input value="x" type="reset" class="edit-js btn btn-alert btn-hidden rounded-none-important"/>
|
<input value="x"
|
||||||
<input value="💾" type="submit" class="btn btn-primary btn-hidden rounded-l-none-important" />
|
type="reset"
|
||||||
{% endif %}
|
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
||||||
|
<input value="💾"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary btn-hidden rounded-l-none-important" />
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro inputgroup %}
|
{% endmacro inputgroup %}
|
||||||
|
{% macro selectgroup(label, data, name='trip_type', default='', id='', selected_id='', display='', extras='', class='', wrapper_class='', required=false, show_seats=false, new_last_entry='', nonSelectableDefault=false, only_ergo=false, readonly=false) %}
|
||||||
|
<div class="{{ wrapper_class }}">
|
||||||
|
<label for="{{ name }}" class="text-sm text-gray-600 dark:text-gray-100">{{ label }}</label>
|
||||||
|
{% if display == '' %}
|
||||||
|
{% set display = ["name"] %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="input-group">
|
||||||
|
<select name="{{ name }}"
|
||||||
|
{% if id %} id="{{ id }}" {% else %} id="{{ name }}" {% endif %}
|
||||||
|
class="input {% if readonly %}rounded-md{% else %}rounded-l-md{% endif %} {{ class }}"
|
||||||
|
{% if required %}required="required"{% endif %}
|
||||||
|
disabled>
|
||||||
|
{% if default %}<option selected value>{{ default }}</option>{% endif %}
|
||||||
|
{% if nonSelectableDefault %}<option disabled selected value>{{ nonSelectableDefault }}</option>{% endif %}
|
||||||
|
{% for d in data %}
|
||||||
|
<option value="{{ d.id }}"
|
||||||
|
{% if only_ergo and d.id!=4 %}disabled{% endif %}
|
||||||
|
{% if d.id == selected_id %}selected{% endif %}
|
||||||
|
{% if extras != '' %} {% for extra in extras %} {% if extra != 'on_water' and d[extra] %} data- {{ extra }}={{ d[extra] }} {% else %} {% if d[extra] %}disabled{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if show_seats %} data-custom-properties='{"amount_seats": {{ d["amount_seats"] }}, "owner": "{{ d["owner"] }}", "default_destination": "{{ d["default_destination"] }}", "boat_in_ottensheim": {{ d["location_id"] == 2 }}, "boat_reserved_today": {{ d["reserved_today"] }}, "convert_handoperated_possible": {{ d["convert_handoperated_possible"] }}, "default_handoperated": {{ d["default_shipmaster_only_steering"] }}}' {% endif %}>
|
||||||
|
{% for displa in display -%}
|
||||||
|
{%- if d[displa] -%}
|
||||||
|
{{- d[displa] -}}
|
||||||
|
{%- else -%}
|
||||||
|
{{- displa -}}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor %}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
{% if new_last_entry %}<option value="-1">{{ new_last_entry }}</option>{% endif %}
|
||||||
|
</select>
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<button type="button" class="btn btn-dark rounded-l-none-important edit-js">{% include "includes/pencil" %}</button>
|
||||||
|
<input value="x"
|
||||||
|
type="reset"
|
||||||
|
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
||||||
|
<input value="💾"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary btn-hidden rounded-l-none-important" />
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro selectgroup %}
|
||||||
{% macro checkbox(label, name, id='', checked=false, class='', disabled=false, readonly=false) %}
|
{% macro checkbox(label, name, id='', checked=false, class='', disabled=false, readonly=false) %}
|
||||||
<label for="{{ name }}{{ id }}"
|
<label for="{{ name }}{{ id }}"
|
||||||
class="flex items-center cursor-pointer text-black dark:text-white hover:text-gray-900 dark:hover:text-gray-100 {{ class }}">
|
class="flex items-center cursor-pointer text-black dark:text-white hover:text-gray-900 dark:hover:text-gray-100 {{ class }}">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user