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) => {
|
||||
btn.addEventListener("click", function () {
|
||||
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')
|
||||
input?.toggleAttribute('readonly');
|
||||
if(!input?.hasAttribute('readonly')) input?.focus();
|
||||
if(select) attribute = 'disabled';
|
||||
let element = input ? input : select;
|
||||
|
||||
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)]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub mod model;
|
||||
|
||||
#[cfg(feature = "rowing-tera")]
|
||||
@ -22,6 +24,74 @@ pub(crate) const FOERDERND: i64 = 8500;
|
||||
pub(crate) const SCHECKBUCH: 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)]
|
||||
#[macro_export]
|
||||
macro_rules! testdb {
|
||||
|
@ -42,12 +42,20 @@ impl User {
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
new_phone: &str,
|
||||
) -> Result<(), String> {
|
||||
) {
|
||||
let new_phone = new_phone.trim();
|
||||
|
||||
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)
|
||||
} 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)
|
||||
};
|
||||
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}")
|
||||
};
|
||||
Log::create(db, msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn update_address(
|
||||
@ -67,12 +73,20 @@ impl User {
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
new_address: &str,
|
||||
) -> Result<(), String> {
|
||||
) {
|
||||
let new_address = new_address.trim();
|
||||
|
||||
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)
|
||||
} else {
|
||||
if let Some(old_address) = &self.address {
|
||||
if old_address == new_address {
|
||||
return; //nothing to do
|
||||
}
|
||||
}
|
||||
sqlx::query!(
|
||||
"UPDATE user SET address = ? where id = ?",
|
||||
new_address,
|
||||
@ -87,8 +101,6 @@ impl User {
|
||||
None => format!("{updated_by} has added an address for {self}: {new_address}")
|
||||
};
|
||||
Log::create(db, msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn update_nickname(
|
||||
@ -313,6 +325,9 @@ impl User {
|
||||
if self.has_membership_pdf(db).await {
|
||||
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 buffer = Vec::new();
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::ScheckbuchUser;
|
||||
use crate::model::{
|
||||
logbook::{Logbook, LogbookWithBoatAndRowers},
|
||||
role::Role,
|
||||
user::User,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -1,15 +1,12 @@
|
||||
use std::{
|
||||
fmt::Display,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
use std::{fmt::Display, ops::DerefMut};
|
||||
|
||||
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use log::info;
|
||||
use rocket::async_trait;
|
||||
use rocket::{
|
||||
async_trait,
|
||||
http::{Cookie, Status},
|
||||
request::{self, FromRequest, Outcome},
|
||||
request::{FromRequest, Outcome},
|
||||
time::{Duration, OffsetDateTime},
|
||||
tokio::io::AsyncReadExt,
|
||||
Request,
|
||||
@ -35,6 +32,7 @@ use scheckbuch::ScheckbuchUser;
|
||||
mod basic;
|
||||
mod fee;
|
||||
pub(crate) mod member;
|
||||
pub(crate) mod regular;
|
||||
pub(crate) mod scheckbuch;
|
||||
|
||||
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
@ -119,10 +117,7 @@ impl User {
|
||||
));
|
||||
};
|
||||
|
||||
if self.has_role(db, "Donau Linz").await {
|
||||
self.send_welcome_mail_full_member(db, mail, smtp_pw)
|
||||
.await?;
|
||||
} else if self.has_role(db, "schnupperant").await {
|
||||
if self.has_role(db, "schnupperant").await {
|
||||
self.send_welcome_mail_schnupper(db, mail, smtp_pw).await?;
|
||||
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, self).await {
|
||||
scheckbuch.notify(db, mail, smtp_pw).await?;
|
||||
@ -182,57 +177,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
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 {
|
||||
sqlx::query!(
|
||||
"SELECT COUNT(*) as count FROM boat WHERE owner = ?",
|
||||
@ -904,7 +848,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
impl<'r> FromRequest<'r> for User {
|
||||
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") {
|
||||
Some(user_id) => match user_id.value().parse::<i32>() {
|
||||
Ok(user_id) => {
|
||||
@ -939,7 +883,7 @@ macro_rules! special_user {
|
||||
pub(crate) user: User,
|
||||
}
|
||||
|
||||
impl Deref for $name {
|
||||
impl std::ops::Deref for $name {
|
||||
type Target = User;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.user
|
||||
@ -953,20 +897,20 @@ macro_rules! special_user {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> FromRequest<'r> for $name {
|
||||
type Error = LoginError;
|
||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
impl<'r> rocket::request::FromRequest<'r> for $name {
|
||||
type Error = crate::model::user::LoginError;
|
||||
async fn from_request(req: &'r rocket::request::Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||
match User::from_request(req).await {
|
||||
Outcome::Success(user) => {
|
||||
rocket::request::Outcome::Success(user) => {
|
||||
if special_user!(@check_roles user, db, $($role)*) {
|
||||
Outcome::Success($name { user })
|
||||
rocket::request::Outcome::Success($name { user })
|
||||
} else {
|
||||
Outcome::Forward(Status::Forbidden)
|
||||
rocket::request::Outcome::Forward(rocket::http::Status::Forbidden)
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
Outcome::Forward(f) => Outcome::Forward(f),
|
||||
rocket::request::Outcome::Error(f) => rocket::request::Outcome::Error(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!(SteeringUser, +"cox", +"Bootsführer");
|
||||
special_user!(AdminUser, +"admin");
|
||||
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch");
|
||||
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
|
||||
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
|
||||
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO:
|
||||
// remove ->
|
||||
// RegularUser
|
||||
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
|
||||
special_user!(VorstandUser, +"admin", +"Vorstand");
|
||||
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 crate::model::role::Role;
|
||||
use crate::model::user::LoginError;
|
||||
use crate::tera::admin::user::ScheckToRegularForm;
|
||||
use crate::NonEmptyString;
|
||||
use crate::{
|
||||
model::{mail::Mail, notification::Notification},
|
||||
special_user, SCHECKBUCH,
|
||||
@ -10,13 +9,7 @@ use crate::{
|
||||
use chrono::NaiveDate;
|
||||
use rocket::async_trait;
|
||||
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 std::ops::Deref;
|
||||
|
||||
special_user!(ScheckbuchUser, +"scheckbuch");
|
||||
|
||||
@ -24,11 +17,12 @@ impl ScheckbuchUser {
|
||||
pub(crate) async fn convert_to_regular_user(
|
||||
self,
|
||||
db: &SqlitePool,
|
||||
smtp_pw: &str,
|
||||
changed_by: &ManageUserUser,
|
||||
member_since: &NaiveDate,
|
||||
birthdate: &NaiveDate,
|
||||
phone: &str,
|
||||
address: &str,
|
||||
phone: NonEmptyString,
|
||||
address: NonEmptyString,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<(), String> {
|
||||
// Set data
|
||||
@ -36,9 +30,9 @@ impl ScheckbuchUser {
|
||||
self.user
|
||||
.update_member_since(db, changed_by, member_since)
|
||||
.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.update_phone(db, changed_by, &phone).await;
|
||||
self.user.update_address(db, changed_by, &address).await;
|
||||
self.user
|
||||
.add_membership_pdf(db, changed_by, membership_pdf)
|
||||
.await?;
|
||||
@ -50,25 +44,13 @@ impl ScheckbuchUser {
|
||||
self.user.add_role(db, changed_by, ®ular).await?;
|
||||
|
||||
// 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> {
|
||||
// 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
|
||||
//}
|
||||
|
||||
// TODO: make private
|
||||
pub(crate) async fn notify(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
|
@ -390,13 +390,11 @@ async fn update_phone(
|
||||
);
|
||||
};
|
||||
|
||||
match user.update_phone(db, &admin, &data.phone).await {
|
||||
Ok(_) => Flash::success(
|
||||
user.update_phone(db, &admin, &data.phone).await;
|
||||
Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Telefonnummer erfolgreich geändert",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
@ -418,13 +416,12 @@ async fn update_address(
|
||||
);
|
||||
};
|
||||
|
||||
match user.update_address(db, &admin, &data.address).await {
|
||||
Ok(_) => Flash::success(
|
||||
user.update_address(db, &admin, &data.address).await;
|
||||
|
||||
Flash::success(
|
||||
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)]
|
||||
@ -831,6 +828,7 @@ async fn scheckbook_to_regular(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<ScheckToRegularForm<'_>>,
|
||||
admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
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 {
|
||||
return Flash::error(
|
||||
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 {
|
||||
return Flash::error(
|
||||
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
|
||||
.convert_to_regular_user(
|
||||
db,
|
||||
&config.smtp_pw,
|
||||
&admin,
|
||||
&member_since,
|
||||
&birthdate,
|
||||
&data.phone,
|
||||
&data.address,
|
||||
phone,
|
||||
address,
|
||||
&data.membership_pdf,
|
||||
)
|
||||
.await
|
||||
|
@ -4,50 +4,171 @@
|
||||
{% block content %}
|
||||
<div class="max-w-screen-lg w-full">
|
||||
<h1 class="h1">{{ user.name }}</h1>
|
||||
<div class="grid sm:grid-cols-2 gap-3">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||
role="alert">
|
||||
<h2 class="h2">Grunddaten</h2>
|
||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
<h2 class="h2">
|
||||
Grunddaten
|
||||
<br />
|
||||
<small class="inline-block text-xs text-gray-500 dark:text-gray-100 ">
|
||||
{% if user.last_access %}
|
||||
Zuletzt eingeloggt am {{ user.last_access | date(format="%d. %m. %Y") }}
|
||||
{% else %}
|
||||
{{ user.name }} hat sich noch nie eingeloggt.
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="py-3">
|
||||
<ul class="grid gap-3">
|
||||
<li>
|
||||
</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>
|
||||
</li>
|
||||
<li>
|
||||
<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>
|
||||
</li>
|
||||
<li>
|
||||
<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>
|
||||
<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">
|
||||
Rollen:
|
||||
<ul class="list-disc ms-4">
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
{% 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">Rollen</h2>
|
||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
<ul>
|
||||
{% for role in user.proper_roles -%}
|
||||
{% if not role.cluster and not role.hide_in_lists %}
|
||||
<li>
|
||||
<li class="flex my-2 w-full justify-between items-center hover:bg-gray-100">
|
||||
<span>
|
||||
<strong>
|
||||
{% if role.formatted_name %}
|
||||
{{ role.formatted_name }}
|
||||
{% else %}
|
||||
{{ role.name }}
|
||||
{% endif %}
|
||||
</strong> {{ role.desc }}
|
||||
</strong>
|
||||
<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>
|
||||
@ -83,10 +204,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</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">💸</h2>
|
||||
<h2 class="h2">💸-Beitrag</h2>
|
||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
{% if fee %}
|
||||
@ -120,123 +242,6 @@
|
||||
</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>
|
||||
@ -325,4 +330,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@ -174,10 +174,9 @@ function setChoiceByLabel(choicesInstance, label) {
|
||||
{% if autofocus %}autofocus{% endif %}
|
||||
{% if accept %}accept="{{ accept }}"{% endif %}
|
||||
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
||||
{% if readonly %}readonly{% endif %}/>
|
||||
{% if readonly %}readonly{% endif %} />
|
||||
</div>
|
||||
{% endmacro input %}
|
||||
|
||||
{% 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 }}">
|
||||
<label for="{{ name }}"
|
||||
@ -197,17 +196,65 @@ function setChoiceByLabel(choicesInstance, label) {
|
||||
{% if autofocus %}autofocus{% endif %}
|
||||
{% if accept %}accept="{{ accept }}"{% endif %}
|
||||
{% if pattern %}pattern="{{ pattern }}"{% endif %}
|
||||
readonly/>
|
||||
readonly />
|
||||
{% if allowed_to_edit %}
|
||||
<button type="button" class="btn btn-primary rounded-l-none-important edit-js">Ändern</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" />
|
||||
<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 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) %}
|
||||
<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 }}">
|
||||
|
Loading…
x
Reference in New Issue
Block a user