single-user-edit-page #971
@ -5,7 +5,8 @@
|
|||||||
.input-group {
|
.input-group {
|
||||||
@apply flex;
|
@apply flex;
|
||||||
|
|
||||||
input[readonly] {
|
input[readonly],
|
||||||
|
select[disabled] {
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,4 +10,8 @@
|
|||||||
&-white {
|
&-white {
|
||||||
@apply text-white hover:text-primary-100 underline;
|
@apply text-white hover:text-primary-100 underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-no-underline {
|
||||||
|
@apply no-underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,6 +225,15 @@ CREATE TABLE IF NOT EXISTS "distance" (
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "activity" (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
relevant_for TEXT NOT NULL, -- e.g. user_id=123;trip_id=456
|
||||||
|
keep_until DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS prevent_multiple_roles_same_cluster
|
CREATE TRIGGER IF NOT EXISTS prevent_multiple_roles_same_cluster
|
||||||
BEFORE INSERT ON user_role
|
BEFORE INSERT ON user_role
|
||||||
BEGIN
|
BEGIN
|
||||||
|
54
src/model/activity.rs
Normal file
54
src/model/activity.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
|
#[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Activity {
|
||||||
|
pub id: i64,
|
||||||
|
pub created_at: NaiveDateTime,
|
||||||
|
pub text: String,
|
||||||
|
pub relevant_for: String,
|
||||||
|
pub keep_until: Option<NaiveDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Activity {
|
||||||
|
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"SELECT id, created_at, text, relevant_for, keep_until FROM activity WHERE id like ?",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub async fn create_with_tx(
|
||||||
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
|
text: &str,
|
||||||
|
relevant_for: &str,
|
||||||
|
keep_until: Option<NaiveDateTime>,
|
||||||
|
) {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO activity(text, relevant_for, keep_until) VALUES (?, ?, ?)",
|
||||||
|
text,
|
||||||
|
relevant_for,
|
||||||
|
keep_until
|
||||||
|
)
|
||||||
|
.execute(db.deref_mut())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create(
|
||||||
|
db: &SqlitePool,
|
||||||
|
text: &str,
|
||||||
|
relevant_for: &str,
|
||||||
|
keep_until: Option<NaiveDateTime>,
|
||||||
|
) {
|
||||||
|
let mut tx = db.begin().await.unwrap();
|
||||||
|
Self::create_with_tx(&mut tx, text, relevant_for, keep_until).await;
|
||||||
|
tx.commit().await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
@ -79,7 +79,9 @@ impl Mail {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Send the email
|
// Send the email
|
||||||
mailer.send(&email).unwrap();
|
if let Err(e) = mailer.send(&email) {
|
||||||
|
Log::create_with_tx(db, format!("Mail nicht versandt: {e:?}")).await;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ use self::{
|
|||||||
use boatreservation::{BoatReservation, BoatReservationWithDetails};
|
use boatreservation::{BoatReservation, BoatReservationWithDetails};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub mod activity;
|
||||||
pub mod boat;
|
pub mod boat;
|
||||||
pub mod boatdamage;
|
pub mod boatdamage;
|
||||||
pub mod boathouse;
|
pub mod boathouse;
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
// TODO: put back in `src/model/user/mod.rs` once that is cleaned up
|
// TODO: put back in `src/model/user/mod.rs` once that is cleaned up
|
||||||
|
|
||||||
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
|
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
|
||||||
use crate::model::{family::Family, log::Log, mail::valid_mails, role::Role};
|
use crate::model::{activity::Activity, family::Family, log::Log, mail::valid_mails, role::Role};
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use rocket::{fs::TempFile, tokio::io::AsyncReadExt};
|
use rocket::{fs::TempFile, tokio::io::AsyncReadExt};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
|
pub(crate) async fn add_note(
|
||||||
|
&self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
updated_by: &ManageUserUser,
|
||||||
|
note: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let note = note.trim();
|
||||||
|
|
||||||
|
Activity::create(db, note, "relevant_for", None).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn update_mail(
|
pub(crate) async fn update_mail(
|
||||||
&self,
|
&self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
|
162
src/model/user/clubmember.rs
Normal file
162
src/model/user/clubmember.rs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
use super::User;
|
||||||
|
use crate::{
|
||||||
|
model::{log::Log, notification::Notification, role::Role, user::ManageUserUser},
|
||||||
|
special_user,
|
||||||
|
};
|
||||||
|
use rocket::async_trait;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(ClubMemberUser, +"Donau Linz", +"Förderndes Mitglied", +"Unterstützend");
|
||||||
|
|
||||||
|
impl ClubMemberUser {
|
||||||
|
async fn add_membership_role(&self, db: &SqlitePool, role: &Role) {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO user_role(user_id, role_id) VALUES (?, ?)",
|
||||||
|
self.id,
|
||||||
|
role.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_membership_role(&self, db: &SqlitePool) {
|
||||||
|
let role = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM user_role WHERE user_id = ? and role_id = ?",
|
||||||
|
self.id,
|
||||||
|
role.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let role = Role::find_by_name(db, "Unterstützend").await.unwrap();
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM user_role WHERE user_id = ? and role_id = ?",
|
||||||
|
self.id,
|
||||||
|
role.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let role = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM user_role WHERE user_id = ? and role_id = ?",
|
||||||
|
self.id,
|
||||||
|
role.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
async fn new_membership_role(&self, db: &SqlitePool, role: &str) -> Result<(), String> {
|
||||||
|
let role = Role::find_by_name(db, role).await.unwrap();
|
||||||
|
self.remove_membership_role(db).await;
|
||||||
|
self.add_membership_role(db, &role).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_regular(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
modified_by: &ManageUserUser,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if self.has_role(db, "Donau Linz").await {
|
||||||
|
return Err(format!("User {self} ist bereits reguläres Mitglied."));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_membership_role(db, "Donau Linz").await?;
|
||||||
|
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} hat upgegraded und ist nun ein neues reguläres Mitglied. 🎉",
|
||||||
|
self.name,
|
||||||
|
),
|
||||||
|
"Neues Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!("{modified_by} has moved user {self} to regular membership."),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_unterstuetzend(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
modified_by: &ManageUserUser,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if self.has_role(db, "Unterstützend").await {
|
||||||
|
return Err(format!("User {self} ist bereits unterstützendes Mitglied."));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_membership_role(db, "Unterstützend").await?;
|
||||||
|
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, der Mitgliedstatus von {} hat sich geändert auf 'Unterstützendes Mitglied'.",
|
||||||
|
self.name,
|
||||||
|
),
|
||||||
|
"Neues unterstützendes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!("{modified_by} has moved user {self} to unterstützend membership."),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_foerdernd(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
modified_by: &ManageUserUser,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if self.has_role(db, "Förderndes Mitglied").await {
|
||||||
|
return Err(format!("User {self} ist bereits förderndes Mitglied."));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_membership_role(db, "Förderndes Mitglied").await?;
|
||||||
|
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, der Mitgliedstatus von {} hat sich geändert auf 'Förderndes Mitglied'.",
|
||||||
|
self.name,
|
||||||
|
),
|
||||||
|
"Neues förderndes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!("{modified_by} has moved user {self} to fördernd membership."),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
40
src/model/user/foerdernd.rs
Normal file
40
src/model/user/foerdernd.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use super::User;
|
||||||
|
use crate::{model::mail::Mail, special_user};
|
||||||
|
use rocket::async_trait;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(FoerderndUser, +"Förderndes Mitglied");
|
||||||
|
|
||||||
|
impl FoerderndUser {
|
||||||
|
pub(crate) 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Riemen- & Dollenbruch
|
||||||
|
ASKÖ Ruderverein Donau Linz", self.name),
|
||||||
|
smtp_pw,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -8,14 +8,12 @@ use rocket::{
|
|||||||
http::{Cookie, Status},
|
http::{Cookie, Status},
|
||||||
request::{FromRequest, Outcome},
|
request::{FromRequest, Outcome},
|
||||||
time::{Duration, OffsetDateTime},
|
time::{Duration, OffsetDateTime},
|
||||||
tokio::io::AsyncReadExt,
|
|
||||||
Request,
|
Request,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
family::Family,
|
|
||||||
log::Log,
|
log::Log,
|
||||||
logbook::Logbook,
|
logbook::Logbook,
|
||||||
mail::Mail,
|
mail::Mail,
|
||||||
@ -26,14 +24,19 @@ use super::{
|
|||||||
tripdetails::TripDetails,
|
tripdetails::TripDetails,
|
||||||
Day,
|
Day,
|
||||||
};
|
};
|
||||||
use crate::{tera::admin::user::UserEditForm, AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD};
|
use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD;
|
||||||
use scheckbuch::ScheckbuchUser;
|
use scheckbuch::ScheckbuchUser;
|
||||||
|
|
||||||
mod basic;
|
mod basic;
|
||||||
|
pub(crate) mod clubmember;
|
||||||
mod fee;
|
mod fee;
|
||||||
|
pub(crate) mod foerdernd;
|
||||||
pub(crate) mod member;
|
pub(crate) mod member;
|
||||||
pub(crate) mod regular;
|
pub(crate) mod regular;
|
||||||
pub(crate) mod scheckbuch;
|
pub(crate) mod scheckbuch;
|
||||||
|
pub(crate) mod schnupperant;
|
||||||
|
pub(crate) mod schnupperinterest;
|
||||||
|
pub(crate) mod unterstuetzend;
|
||||||
|
|
||||||
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -120,7 +123,7 @@ impl User {
|
|||||||
if self.has_role(db, "schnupperant").await {
|
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, smtp_pw).await?;
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Could not send welcome mail, because user {} is not in Donau Linz or scheckbuch or schnupperant group",
|
"Could not send welcome mail, because user {} is not in Donau Linz or scheckbuch or schnupperant group",
|
||||||
@ -478,68 +481,6 @@ ORDER BY last_access DESC
|
|||||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) -> Result<(), String> {
|
|
||||||
let mut db = db.begin().await.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut family_id = data.family_id;
|
|
||||||
|
|
||||||
if family_id.is_some_and(|x| x == -1) {
|
|
||||||
family_id = Some(Family::insert_tx(&mut db).await)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.has_membership_pdf_tx(&mut db).await {
|
|
||||||
if let Some(membership_pdf) = data.membership_pdf {
|
|
||||||
let mut stream = membership_pdf.open().await.unwrap();
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
stream.read_to_end(&mut buffer).await.unwrap();
|
|
||||||
sqlx::query!(
|
|
||||||
"UPDATE user SET membership_pdf = ? where id = ?",
|
|
||||||
buffer,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
.execute(db.deref_mut())
|
|
||||||
.await
|
|
||||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"UPDATE user SET dob = ?, weight = ?, sex = ?, member_since_date=?, birthdate=?, mail=?, nickname=?, notes=?, phone=?, address=?, family_id = ? where id = ?",
|
|
||||||
data.dob,
|
|
||||||
data.weight,
|
|
||||||
data.sex,
|
|
||||||
data.member_since_date,
|
|
||||||
data.birthdate,
|
|
||||||
data.mail,
|
|
||||||
data.nickname,
|
|
||||||
data.notes,
|
|
||||||
data.phone,
|
|
||||||
data.address,
|
|
||||||
family_id,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
.execute(db.deref_mut())
|
|
||||||
.await
|
|
||||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
|
||||||
|
|
||||||
// handle roles
|
|
||||||
sqlx::query!("DELETE FROM user_role WHERE user_id = ?", self.id)
|
|
||||||
.execute(db.deref_mut())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for role_id in data.roles.into_keys() {
|
|
||||||
let role = Role::find_by_id_tx(&mut db, role_id.parse::<i32>().unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
self.add_role_tx(&mut db, &role).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit().await.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_end_mail_scheckbuch(
|
async fn send_end_mail_scheckbuch(
|
||||||
&self,
|
&self,
|
||||||
db: &mut Transaction<'_, Sqlite>,
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
@ -1010,9 +951,7 @@ impl UserWithMembershipPdf {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashMap;
|
use crate::testdb;
|
||||||
|
|
||||||
use crate::{tera::admin::user::UserEditForm, testdb};
|
|
||||||
|
|
||||||
use super::User;
|
use super::User;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
@ -1073,38 +1012,6 @@ mod test {
|
|||||||
assert_eq!(User::create(&pool, "admin".into()).await, false);
|
assert_eq!(User::create(&pool, "admin".into()).await, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sqlx::test]
|
|
||||||
fn test_update() {
|
|
||||||
let pool = testdb!();
|
|
||||||
|
|
||||||
let user = User::find_by_id(&pool, 1).await.unwrap();
|
|
||||||
user.update(
|
|
||||||
&pool,
|
|
||||||
UserEditForm {
|
|
||||||
id: 1,
|
|
||||||
dob: None,
|
|
||||||
weight: None,
|
|
||||||
sex: Some("m".into()),
|
|
||||||
roles: HashMap::new(),
|
|
||||||
member_since_date: None,
|
|
||||||
birthdate: None,
|
|
||||||
mail: None,
|
|
||||||
nickname: None,
|
|
||||||
notes: None,
|
|
||||||
phone: None,
|
|
||||||
address: None,
|
|
||||||
family_id: None,
|
|
||||||
membership_pdf: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = User::find_by_id(&pool, 1).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(user.sex, Some("m".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
fn succ_login_with_test_db() {
|
fn succ_login_with_test_db() {
|
||||||
let pool = testdb!();
|
let pool = testdb!();
|
||||||
|
@ -1,22 +1,12 @@
|
|||||||
use super::User;
|
use super::User;
|
||||||
use crate::{
|
use crate::{model::mail::Mail, special_user};
|
||||||
model::{mail::Mail, notification::Notification},
|
|
||||||
special_user,
|
|
||||||
};
|
|
||||||
use rocket::async_trait;
|
use rocket::async_trait;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
special_user!(RegularUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
|
special_user!(RegularUser, +"Donau Linz");
|
||||||
|
|
||||||
impl RegularUser {
|
impl RegularUser {
|
||||||
pub(crate) async fn notify(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
|
pub(crate) async fn send_welcome_mail_to_user(
|
||||||
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,
|
&self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
smtp_pw: &str,
|
smtp_pw: &str,
|
||||||
@ -55,19 +45,4 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
|||||||
|
|
||||||
Ok(())
|
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,4 +1,6 @@
|
|||||||
|
use super::foerdernd::FoerderndUser;
|
||||||
use super::regular::RegularUser;
|
use super::regular::RegularUser;
|
||||||
|
use super::unterstuetzend::UnterstuetzendUser;
|
||||||
use super::{ManageUserUser, User};
|
use super::{ManageUserUser, User};
|
||||||
use crate::model::role::Role;
|
use crate::model::role::Role;
|
||||||
use crate::NonEmptyString;
|
use crate::NonEmptyString;
|
||||||
@ -14,10 +16,9 @@ use sqlx::SqlitePool;
|
|||||||
special_user!(ScheckbuchUser, +"scheckbuch");
|
special_user!(ScheckbuchUser, +"scheckbuch");
|
||||||
|
|
||||||
impl ScheckbuchUser {
|
impl ScheckbuchUser {
|
||||||
pub(crate) async fn convert_to_regular_user(
|
async fn set_data_for_clubmember(
|
||||||
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,
|
||||||
@ -25,7 +26,6 @@ impl ScheckbuchUser {
|
|||||||
address: NonEmptyString,
|
address: NonEmptyString,
|
||||||
membership_pdf: &TempFile<'_>,
|
membership_pdf: &TempFile<'_>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Set data
|
|
||||||
self.user.update_birthdate(db, changed_by, birthdate).await;
|
self.user.update_birthdate(db, changed_by, birthdate).await;
|
||||||
self.user
|
self.user
|
||||||
.update_member_since(db, changed_by, member_since)
|
.update_member_since(db, changed_by, member_since)
|
||||||
@ -37,6 +37,30 @@ impl ScheckbuchUser {
|
|||||||
.add_membership_pdf(db, changed_by, membership_pdf)
|
.add_membership_pdf(db, changed_by, membership_pdf)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub(crate) async fn convert_to_regular_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Change roles
|
// Change roles
|
||||||
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||||
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||||
@ -45,33 +69,146 @@ impl ScheckbuchUser {
|
|||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
let regular = RegularUser::new(db, &self.user).await.unwrap();
|
let regular = RegularUser::new(db, &self.user).await.unwrap();
|
||||||
regular.notify(db, smtp_pw).await?;
|
regular.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} hatte ein Scheckbuch und ist nun seit {} es ein neues reguläres Mitglied. 🎉",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn convert_to_unterstuetzend_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// Set data
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Change roles
|
||||||
|
let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &scheckbook).await?;
|
||||||
|
self.user.add_role(db, changed_by, &unterstuetzend).await?;
|
||||||
|
|
||||||
|
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
|
||||||
|
unterstuetzend
|
||||||
|
.send_welcome_mail_to_user(db, smtp_pw)
|
||||||
|
.await?;
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} es ein neues unterstützendes Mitglied.",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues unterstützendes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn convert_to_foerdernd_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// Set data
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Change roles
|
||||||
|
let unterstuetzend = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &scheckbook).await?;
|
||||||
|
self.user.add_role(db, changed_by, &unterstuetzend).await?;
|
||||||
|
|
||||||
|
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
|
||||||
|
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, {} hatte ein Scheckbuch und ist nun seit {} es ein neues förderndes Mitglied.",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues förderndes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make private
|
// TODO: make private
|
||||||
pub(crate) async fn notify(
|
pub(crate) async fn notify(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
|
||||||
&self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
mail: &str,
|
|
||||||
smtp_pw: &str,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
self.send_welcome_mail_to_user(db, mail, smtp_pw).await?;
|
|
||||||
self.notify_coxes_about_new_scheckbuch(db).await;
|
self.notify_coxes_about_new_scheckbuch(db).await;
|
||||||
|
self.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_welcome_mail_to_user(
|
pub(crate) async fn send_welcome_mail_to_user(
|
||||||
&self,
|
&self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
mail: &str,
|
|
||||||
smtp_pw: &str,
|
smtp_pw: &str,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let Some(mail) = &self.mail else {
|
||||||
|
return Err(
|
||||||
|
"Kann Mail nicht versenden, weil der User keine Mailadresse hinterlegt hat.".into(),
|
||||||
|
);
|
||||||
|
};
|
||||||
Mail::send_single(
|
Mail::send_single(
|
||||||
db,
|
db,
|
||||||
mail,
|
&mail,
|
||||||
"ASKÖ Ruderverein Donau Linz | Dein Scheckbuch wartet auf Dich",
|
"ASKÖ Ruderverein Donau Linz | Dein Scheckbuch wartet auf Dich",
|
||||||
format!(
|
format!(
|
||||||
"Hallo {0},
|
"Hallo {0},
|
||||||
|
319
src/model/user/schnupperant.rs
Normal file
319
src/model/user/schnupperant.rs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
use super::foerdernd::FoerderndUser;
|
||||||
|
use super::regular::RegularUser;
|
||||||
|
use super::scheckbuch::ScheckbuchUser;
|
||||||
|
use super::unterstuetzend::UnterstuetzendUser;
|
||||||
|
use super::{ManageUserUser, User};
|
||||||
|
use crate::model::role::Role;
|
||||||
|
use crate::NonEmptyString;
|
||||||
|
use crate::{
|
||||||
|
model::{mail::Mail, notification::Notification},
|
||||||
|
special_user,
|
||||||
|
};
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use rocket::async_trait;
|
||||||
|
use rocket::fs::TempFile;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(SchnupperantUser, +"schnupperant");
|
||||||
|
|
||||||
|
impl SchnupperantUser {
|
||||||
|
async fn set_data_for_clubmember(
|
||||||
|
&self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
self.user.update_birthdate(db, changed_by, birthdate).await;
|
||||||
|
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
|
||||||
|
.add_membership_pdf(db, changed_by, membership_pdf)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub(crate) async fn convert_to_regular_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Change roles
|
||||||
|
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &scheckbook).await?;
|
||||||
|
self.user.add_role(db, changed_by, ®ular).await?;
|
||||||
|
|
||||||
|
// Notify
|
||||||
|
let regular = RegularUser::new(db, &self.user).await.unwrap();
|
||||||
|
regular.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} nahm an unserem Schnupperkurs teil und ist nun seit {} ein neues reguläres Mitglied. 🎉",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_scheckbook(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
smtp_pw: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &schnupperant).await?;
|
||||||
|
self.user.add_role(db, changed_by, &scheckbook).await?;
|
||||||
|
|
||||||
|
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
|
||||||
|
self.add_role(db, &changed_by, &no_einschreibgebuehr)
|
||||||
|
.await
|
||||||
|
.expect("role doesn't have a group");
|
||||||
|
}
|
||||||
|
|
||||||
|
let scheckbook = ScheckbuchUser::new(db, &self.user).await.unwrap();
|
||||||
|
scheckbook.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} hat unseren Schnupperkurs absolviert und nun ein Scheckbuch. Wie immer, freuen wir uns wenn du uns beim A+F Rudern unterstützt oder selber Ausfahrten ausschreibst. Bitte beachte, dass Scheckbuch-Personen nur Ausfahrten sehen, bei denen 'Scheckbuch-Anmeldungen erlauben' ausgewählt wurde.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neues Scheckbuch",
|
||||||
|
None,None
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_schnupperinterest(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let schnupperinterest = Role::find_by_name(db, "schnupper-interessierte")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &schnupperant).await?;
|
||||||
|
self.user
|
||||||
|
.add_role(db, changed_by, &schnupperinterest)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&role,
|
||||||
|
&format!(
|
||||||
|
"Lieber Schnupperbetreuer, {} hat sich vom Schnupperkurs abgemeldet.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Schnupperkurs Abmeldung",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn convert_to_unterstuetzend_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// Set data
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Change roles
|
||||||
|
let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &scheckbook).await?;
|
||||||
|
self.user.add_role(db, changed_by, &unterstuetzend).await?;
|
||||||
|
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
|
||||||
|
self.add_role(db, &changed_by, &no_einschreibgebuehr)
|
||||||
|
.await
|
||||||
|
.expect("role doesn't have a group");
|
||||||
|
}
|
||||||
|
|
||||||
|
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
|
||||||
|
unterstuetzend
|
||||||
|
.send_welcome_mail_to_user(db, smtp_pw)
|
||||||
|
.await?;
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, {} nahm am Schnupperkurs teil und ist nun seit {} es ein neues unterstützendes Mitglied.",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues unterstützendes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn convert_to_foerdernd_user(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
smtp_pw: &str,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
member_since: &NaiveDate,
|
||||||
|
birthdate: &NaiveDate,
|
||||||
|
phone: NonEmptyString,
|
||||||
|
address: NonEmptyString,
|
||||||
|
membership_pdf: &TempFile<'_>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// Set data
|
||||||
|
self.set_data_for_clubmember(
|
||||||
|
db,
|
||||||
|
changed_by,
|
||||||
|
member_since,
|
||||||
|
birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
membership_pdf,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Change roles
|
||||||
|
let unterstuetzend = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
self.user.remove_role(db, changed_by, &scheckbook).await?;
|
||||||
|
self.user.add_role(db, changed_by, &unterstuetzend).await?;
|
||||||
|
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
|
||||||
|
self.add_role(db, &changed_by, &no_einschreibgebuehr)
|
||||||
|
.await
|
||||||
|
.expect("role doesn't have a group");
|
||||||
|
}
|
||||||
|
|
||||||
|
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
|
||||||
|
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&vorstand,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, {} nahm am Schnupperkurs teil und ist nun seit {} es ein neues förderndes Mitglied.",
|
||||||
|
self.name,
|
||||||
|
self.member_since_date.clone().unwrap()
|
||||||
|
),
|
||||||
|
"Neues förderndes Vereinsmitglied",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make private
|
||||||
|
pub(crate) async fn notify(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
|
||||||
|
self.notify_coxes_about_new_scheckbuch(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 mail, because user {self} has no mail"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
Mail::send_single(
|
||||||
|
db,
|
||||||
|
&mail,
|
||||||
|
"ASKÖ Ruderverein Donau Linz | Anmeldung Schnupperkurs",
|
||||||
|
format!(
|
||||||
|
"Hallo {0},
|
||||||
|
|
||||||
|
es freut uns sehr, dich bei unserem Schnupperkurs willkommen heißen zu dürfen. Detaillierte Informationen folgen noch, ich werde sie dir ein paar Tage vor dem Termin zusenden.
|
||||||
|
|
||||||
|
Riemen- & Dollenbruch,
|
||||||
|
ASKÖ Ruderverein Donau Linz", self.name),
|
||||||
|
smtp_pw,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn notify_coxes_about_new_scheckbuch(&self, db: &SqlitePool) {
|
||||||
|
if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&role,
|
||||||
|
&format!(
|
||||||
|
"Lieber Schnupperbetreuer, {} hat sich zum Schnupperkurs angemeldet.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neuer Schnupperant",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
103
src/model/user/schnupperinterest.rs
Normal file
103
src/model/user/schnupperinterest.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use super::scheckbuch::ScheckbuchUser;
|
||||||
|
use super::schnupperant::SchnupperantUser;
|
||||||
|
use super::{ManageUserUser, User};
|
||||||
|
use crate::model::role::Role;
|
||||||
|
use crate::{model::notification::Notification, special_user};
|
||||||
|
use rocket::async_trait;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(SchnupperInterestUser, +"schnupper-interessierte");
|
||||||
|
|
||||||
|
impl SchnupperInterestUser {
|
||||||
|
pub(crate) async fn move_to_scheckbook(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
smtp_pw: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let schnupperinterest = Role::find_by_name(db, "schnupper-interessierte")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||||
|
self.user
|
||||||
|
.remove_role(db, changed_by, &schnupperinterest)
|
||||||
|
.await?;
|
||||||
|
self.user.add_role(db, changed_by, &scheckbook).await?;
|
||||||
|
|
||||||
|
let scheckbook = ScheckbuchUser::new(db, &self.user).await.unwrap();
|
||||||
|
scheckbook.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||||
|
|
||||||
|
Notification::create_for_steering_people(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} wollte unseren Schnupperkurs absolviert und nun ein Scheckbuch. Wie immer, freuen wir uns wenn du uns beim A+F Rudern unterstützt oder selber Ausfahrten ausschreibst. Bitte beachte, dass Scheckbuch-Personen nur Ausfahrten sehen, bei denen 'Scheckbuch-Anmeldungen erlauben' ausgewählt wurde.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neues Scheckbuch",
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn move_to_schnupperant(
|
||||||
|
self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
changed_by: &ManageUserUser,
|
||||||
|
smtp_pw: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let schnupperinterest = Role::find_by_name(db, "schnupper-interessierte")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||||
|
self.user
|
||||||
|
.remove_role(db, changed_by, &schnupperinterest)
|
||||||
|
.await?;
|
||||||
|
self.user.add_role(db, changed_by, &schnupperant).await?;
|
||||||
|
|
||||||
|
let schnupperant = SchnupperantUser::new(db, &self.user).await.unwrap();
|
||||||
|
schnupperant.notify(db, smtp_pw).await?;
|
||||||
|
|
||||||
|
if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&role,
|
||||||
|
&format!(
|
||||||
|
"Lieber Schnupperbetreuer, {} hat sich zum Schnupperkurs angemeldet.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neuer Schnupper-Interessierte:r",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn notify(&self, db: &SqlitePool) -> Result<(), String> {
|
||||||
|
self.notify_schnupperbetreuer_about_new_interest(db).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn notify_schnupperbetreuer_about_new_interest(&self, db: &SqlitePool) {
|
||||||
|
if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await {
|
||||||
|
Notification::create_for_role(
|
||||||
|
db,
|
||||||
|
&role,
|
||||||
|
&format!(
|
||||||
|
"Lieber Schnupperbetreuer, {} hat Interesse zum Schnupperkurs bekundet.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Neuer Schnupper-Interessierte:r",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/model/user/unterstuetzend.rs
Normal file
40
src/model/user/unterstuetzend.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use super::User;
|
||||||
|
use crate::{model::mail::Mail, special_user};
|
||||||
|
use rocket::async_trait;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
special_user!(UnterstuetzendUser, +"Unterstützend");
|
||||||
|
|
||||||
|
impl UnterstuetzendUser {
|
||||||
|
pub(crate) 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Riemen- & Dollenbruch
|
||||||
|
ASKÖ Ruderverein Donau Linz", self.name),
|
||||||
|
smtp_pw,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{
|
model::{
|
||||||
family::Family,
|
family::Family,
|
||||||
@ -7,9 +5,10 @@ use crate::{
|
|||||||
logbook::Logbook,
|
logbook::Logbook,
|
||||||
role::Role,
|
role::Role,
|
||||||
user::{
|
user::{
|
||||||
member::Member, scheckbuch::ScheckbuchUser, AdminUser, AllowedToEditPaymentStatusUser,
|
clubmember::ClubMemberUser, member::Member, scheckbuch::ScheckbuchUser,
|
||||||
ManageUserUser, User, UserWithDetails, UserWithMembershipPdf,
|
schnupperant::SchnupperantUser, schnupperinterest::SchnupperInterestUser, AdminUser,
|
||||||
UserWithRolesAndMembershipPdf, VorstandUser,
|
AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
|
||||||
|
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tera::Config,
|
tera::Config,
|
||||||
@ -300,49 +299,6 @@ async fn delete(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Fla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
|
||||||
pub struct UserEditForm<'a> {
|
|
||||||
pub(crate) id: i32,
|
|
||||||
pub(crate) dob: Option<String>,
|
|
||||||
pub(crate) weight: Option<String>,
|
|
||||||
pub(crate) sex: Option<String>,
|
|
||||||
pub(crate) roles: HashMap<String, String>,
|
|
||||||
pub(crate) member_since_date: Option<String>,
|
|
||||||
pub(crate) birthdate: Option<String>,
|
|
||||||
pub(crate) mail: Option<String>,
|
|
||||||
pub(crate) nickname: Option<String>,
|
|
||||||
pub(crate) notes: Option<String>,
|
|
||||||
pub(crate) phone: Option<String>,
|
|
||||||
pub(crate) address: Option<String>,
|
|
||||||
pub(crate) family_id: Option<i64>,
|
|
||||||
pub(crate) membership_pdf: Option<TempFile<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/user", data = "<data>", format = "multipart/form-data")]
|
|
||||||
async fn update(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
data: Form<UserEditForm<'_>>,
|
|
||||||
admin: ManageUserUser,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let user = User::find_by_id(db, data.id).await;
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!("{} updated user from {user:?} to {data:?}", admin.user.name),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let Some(user) = user else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to("/admin/user"),
|
|
||||||
format!("User with ID {} does not exist!", data.id),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
match user.update(db, data.into_inner()).await {
|
|
||||||
Ok(_) => Flash::success(Redirect::to("/admin/user"), "Successfully updated user"),
|
|
||||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
pub struct MailUpdateForm {
|
pub struct MailUpdateForm {
|
||||||
mail: String,
|
mail: String,
|
||||||
@ -371,6 +327,34 @@ async fn update_mail(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm, Debug)]
|
||||||
|
pub struct AddNoteForm {
|
||||||
|
note: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/user/<id>/add-note", data = "<data>")]
|
||||||
|
async fn add_note(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
data: Form<AddNoteForm>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
match user.add_note(db, &admin, &data.note).await {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||||
|
"Notiz hinzugefügt",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
pub struct PhoneUpdateForm {
|
pub struct PhoneUpdateForm {
|
||||||
phone: String,
|
phone: String,
|
||||||
@ -711,11 +695,11 @@ async fn create(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
//#[derive(FromForm, Debug)]
|
||||||
struct UserAddScheckbuchForm<'r> {
|
//struct UserAddScheckbuchForm<'r> {
|
||||||
name: &'r str,
|
// name: &'r str,
|
||||||
mail: &'r str,
|
// mail: &'r str,
|
||||||
}
|
//}
|
||||||
|
|
||||||
//#[post("/user/new/scheckbuch", data = "<data>")]
|
//#[post("/user/new/scheckbuch", data = "<data>")]
|
||||||
//async fn create_scheckbuch(
|
//async fn create_scheckbuch(
|
||||||
@ -764,58 +748,128 @@ struct UserAddScheckbuchForm<'r> {
|
|||||||
// Flash::success(Redirect::to("/admin/user/scheckbuch"), format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
|
// Flash::success(Redirect::to("/admin/user/scheckbuch"), format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//#[get("/user/move/schnupperant/<id>/to/scheckbuch")]
|
#[derive(FromForm, Debug)]
|
||||||
//async fn schnupper_to_scheckbuch(
|
pub struct SchnupperantToRegularForm<'a> {
|
||||||
// db: &State<SqlitePool>,
|
membertype: String,
|
||||||
// id: i32,
|
member_since: String,
|
||||||
// admin: SchnupperBetreuerUser,
|
birthdate: String,
|
||||||
// config: &State<Config>,
|
phone: String,
|
||||||
//) -> Flash<Redirect> {
|
address: String,
|
||||||
// let Some(user) = User::find_by_id(db, id).await else {
|
membership_pdf: TempFile<'a>,
|
||||||
// return Flash::error(
|
}
|
||||||
// Redirect::to("/admin/schnupper"),
|
|
||||||
// "user id not found".to_string(),
|
#[post("/user/<id>/schnupperant-to-regular", data = "<data>")]
|
||||||
// );
|
async fn schnupperant_to_regular(
|
||||||
// };
|
db: &State<SqlitePool>,
|
||||||
//
|
data: Form<SchnupperantToRegularForm<'_>>,
|
||||||
// if !user.has_role(db, "schnupperant").await {
|
admin: ManageUserUser,
|
||||||
// return Flash::error(
|
config: &State<Config>,
|
||||||
// Redirect::to("/admin/schnupper"),
|
id: i32,
|
||||||
// "kein schnupperant...".to_string(),
|
) -> Flash<Redirect> {
|
||||||
// );
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
// }
|
return Flash::error(
|
||||||
//
|
Redirect::to("/admin/user"),
|
||||||
// let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
format!("User with ID {} does not exist!", id),
|
||||||
// let paid = Role::find_by_name(db, "paid").await.unwrap();
|
);
|
||||||
// user.remove_role(db, &schnupperant).await;
|
};
|
||||||
// user.remove_role(db, &paid).await;
|
let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
||||||
//
|
return Flash::error(
|
||||||
// let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
// user.add_role(db, &scheckbuch)
|
format!(
|
||||||
// .await
|
"Geburtsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||||
// .expect("just removed 'schnupperant' thus can't have a role with that group");
|
&data.birthdate
|
||||||
//
|
),
|
||||||
// if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
|
);
|
||||||
// user.add_role(db, &no_einschreibgebuehr)
|
};
|
||||||
// .await
|
let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else {
|
||||||
// .expect("role doesn't have a group");
|
return Flash::error(
|
||||||
// }
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
//
|
format!(
|
||||||
// user.send_welcome_email(db, &config.smtp_pw).await.unwrap();
|
"Beitrittsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||||
//
|
&data.birthdate
|
||||||
// Log::create(
|
),
|
||||||
// db,
|
);
|
||||||
// format!(
|
};
|
||||||
// "{} created new scheckbuch (from schnupperant): {}",
|
|
||||||
// admin.name, user.name
|
let Some(user) = SchnupperantUser::new(db, &user).await else {
|
||||||
// ),
|
return Flash::error(
|
||||||
// )
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
// .await;
|
"User ist kein Schnupperant",
|
||||||
// Flash::success(Redirect::to("/admin/schnupper"), format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap()))
|
);
|
||||||
//}
|
};
|
||||||
|
|
||||||
|
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",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let response = match &*data.membertype {
|
||||||
|
"regular" => {
|
||||||
|
user.convert_to_regular_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
"unterstuetzend" => {
|
||||||
|
user.convert_to_unterstuetzend_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
"foerdernd" => {
|
||||||
|
user.convert_to_foerdernd_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
"Membertype gibts ned",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
pub struct ScheckToRegularForm<'a> {
|
pub struct ScheckToRegularForm<'a> {
|
||||||
|
membertype: String,
|
||||||
member_since: String,
|
member_since: String,
|
||||||
birthdate: String,
|
birthdate: String,
|
||||||
phone: String,
|
phone: String,
|
||||||
@ -875,20 +929,220 @@ async fn scheckbook_to_regular(
|
|||||||
"Vereinsmitglied braucht eine Adresse",
|
"Vereinsmitglied braucht eine Adresse",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
let response = match &*data.membertype {
|
||||||
|
"regular" => {
|
||||||
|
user.convert_to_regular_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
"unterstuetzend" => {
|
||||||
|
user.convert_to_unterstuetzend_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
"foerdernd" => {
|
||||||
|
user.convert_to_foerdernd_user(
|
||||||
|
db,
|
||||||
|
&config.smtp_pw,
|
||||||
|
&admin,
|
||||||
|
&member_since,
|
||||||
|
&birthdate,
|
||||||
|
phone,
|
||||||
|
address,
|
||||||
|
&data.membership_pdf,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
"Membertype gibts ned",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match user
|
match response {
|
||||||
.convert_to_regular_user(
|
Ok(_) => Flash::success(
|
||||||
db,
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
&config.smtp_pw,
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
&admin,
|
),
|
||||||
&member_since,
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
&birthdate,
|
}
|
||||||
phone,
|
}
|
||||||
address,
|
|
||||||
&data.membership_pdf,
|
#[derive(FromForm, Debug)]
|
||||||
)
|
pub struct ChangeMembertypeForm {
|
||||||
.await
|
membertype: String,
|
||||||
{
|
}
|
||||||
|
|
||||||
|
#[post("/user/<id>/change-membertype", data = "<data>")]
|
||||||
|
async fn change_membertype(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
data: Form<ChangeMembertypeForm>,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = ClubMemberUser::new(&db, &user).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User {user} ist kein Vereinsmitglied"),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = match &*data.membertype {
|
||||||
|
"regular" => user.move_to_regular(db, &admin).await,
|
||||||
|
"unterstuetzend" => user.move_to_unterstuetzend(db, &admin).await,
|
||||||
|
"foerdernd" => user.move_to_foerdernd(db, &admin).await,
|
||||||
|
_ => {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{{ id }}")),
|
||||||
|
format!("Membertype gibt's ned"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/user/<id>/schnupperant-to-scheckbuch")]
|
||||||
|
async fn schnupperant_to_scheckbook(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
config: &State<Config>,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = SchnupperantUser::new(&db, &user).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
format!("User {user} ist kein Schnupperant"),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
match user.move_to_scheckbook(db, &admin, &config.smtp_pw).await {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/user/<id>/schnupperinterest-to-schnupperant")]
|
||||||
|
async fn schnupperinterest_to_schnupperant(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
config: &State<Config>,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = SchnupperInterestUser::new(&db, &user).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
format!("User {user} ist kein Schnupperinteressierter"),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
match user.move_to_schnupperant(db, &admin, &config.smtp_pw).await {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/user/<id>/schnupperant-to-schnupperinterest")]
|
||||||
|
async fn schnupperant_to_schnupperinterest(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = SchnupperantUser::new(&db, &user).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
format!("User {user} ist kein Schnupperant"),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
match user.move_to_schnupperinterest(db, &admin).await {
|
||||||
|
Ok(_) => Flash::success(
|
||||||
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
|
"Mitgliedstyp umgewandelt.",
|
||||||
|
),
|
||||||
|
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[get("/user/<id>/schnupperinterest-to-scheckbuch")]
|
||||||
|
async fn schnupperinterest_to_scheckbuch(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: ManageUserUser,
|
||||||
|
config: &State<Config>,
|
||||||
|
id: i32,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let Some(user) = User::find_by_id(db, id).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to("/admin/user"),
|
||||||
|
format!("User with ID {} does not exist!", id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = SchnupperInterestUser::new(&db, &user).await else {
|
||||||
|
return Flash::error(
|
||||||
|
Redirect::to(format!("/admin/user/{id}")),
|
||||||
|
format!("User {user} ist kein Schnupperinteressierter"),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
match user.move_to_scheckbook(db, &admin, &config.smtp_pw).await {
|
||||||
Ok(_) => Flash::success(
|
Ok(_) => Flash::success(
|
||||||
Redirect::to(format!("/admin/user/{}", id)),
|
Redirect::to(format!("/admin/user/{}", id)),
|
||||||
"Mitgliedstyp umgewandelt und Infos versendet",
|
"Mitgliedstyp umgewandelt und Infos versendet",
|
||||||
@ -903,10 +1157,8 @@ pub fn routes() -> Vec<Route> {
|
|||||||
index_admin,
|
index_admin,
|
||||||
view,
|
view,
|
||||||
resetpw,
|
resetpw,
|
||||||
update,
|
|
||||||
create,
|
create,
|
||||||
//create_scheckbuch,
|
//create_scheckbuch,
|
||||||
//schnupper_to_scheckbuch,
|
|
||||||
delete,
|
delete,
|
||||||
fees,
|
fees,
|
||||||
fees_paid,
|
fees_paid,
|
||||||
@ -923,8 +1175,15 @@ pub fn routes() -> Vec<Route> {
|
|||||||
update_family,
|
update_family,
|
||||||
add_membership_pdf,
|
add_membership_pdf,
|
||||||
add_role,
|
add_role,
|
||||||
|
add_note,
|
||||||
remove_role,
|
remove_role,
|
||||||
//
|
//
|
||||||
scheckbook_to_regular,
|
scheckbook_to_regular,
|
||||||
|
schnupperant_to_regular,
|
||||||
|
schnupperant_to_scheckbook,
|
||||||
|
schnupperinterest_to_schnupperant,
|
||||||
|
schnupperant_to_schnupperinterest,
|
||||||
|
schnupperinterest_to_scheckbuch,
|
||||||
|
change_membertype,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,11 @@ UPDATE role SET desc='Es können Logbucheinträge im Nachhinein hinzugefügt wer
|
|||||||
UPDATE role SET desc='Erlaubt den Login auf der Wordpress-Website um zB Artikel zu schreiben.' WHERE name='allow_website_login';
|
UPDATE role SET desc='Erlaubt den Login auf der Wordpress-Website um zB Artikel zu schreiben.' WHERE name='allow_website_login';
|
||||||
UPDATE role SET desc='Muss nur den halben Rennruderbeitrag bezahlen (da zB erst in der 2. Jahreshälfte dazugestoßen wurde)' WHERE name='half-rennrudern';
|
UPDATE role SET desc='Muss nur den halben Rennruderbeitrag bezahlen (da zB erst in der 2. Jahreshälfte dazugestoßen wurde)' WHERE name='half-rennrudern';
|
||||||
UPDATE role SET desc='Muss keinen Rennruderbeitrag bezahlen, obwohl man in Rennruder-Gruppe ist.' WHERE name='renntrainer';
|
UPDATE role SET desc='Muss keinen Rennruderbeitrag bezahlen, obwohl man in Rennruder-Gruppe ist.' WHERE name='renntrainer';
|
||||||
|
|
||||||
|
CREATE TABLE activity (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
relevant_for TEXT NOT NULL, -- e.g. user_id=123;trip_id=456
|
||||||
|
keep_until DATETIME -- OPTIONAL field
|
||||||
|
);
|
||||||
|
@ -67,115 +67,21 @@
|
|||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<div data-filterable="true"
|
<div data-filterable="true"
|
||||||
data-filter="{{ user.name }} {% for role in roles %} {% if role.name in user.roles %} yes-role:{{ role.name }} {% else %} no-role:{{ role.name }} {% endif %} role-{{ role }} {% endfor %} {% if user.membership_pdf %}has-membership-pdf{% else %}has-no-membership-pdf{% endif %}"
|
data-filter="{{ user.name }} {% for role in roles %} {% if role.name in user.roles %} yes-role:{{ role.name }} {% else %} no-role:{{ role.name }} {% endif %} role-{{ role }} {% endfor %} {% if user.membership_pdf %}has-membership-pdf{% else %}has-no-membership-pdf{% endif %}"
|
||||||
class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative flex justify-between items-center">
|
||||||
<details class="block dark:text-white w-full">
|
<span class="text-black dark:text-white">
|
||||||
<summary>
|
<span class="font-bold">
|
||||||
<span class="text-black dark:text-white cursor-pointer">
|
{{ user.name }}
|
||||||
<span class="font-bold">
|
{% if user.last_access %}• ⏳ {{ user.last_access | date }}{% endif %}
|
||||||
{{ user.name }}
|
</span>
|
||||||
{% if not user.last_access and allowed_to_edit and user.mail %}
|
<small class="block text-gray-600 dark:text-gray-100">
|
||||||
<form action="/admin/user"
|
{% for role in user.roles -%}
|
||||||
method="post"
|
{{ role }}
|
||||||
enctype="multipart/form-data"
|
{%- if not loop.last %},
|
||||||
class="inline">
|
{% endif -%}
|
||||||
• <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
{% endfor %}
|
||||||
href="/admin/user/{{ user.id }}/send-welcome-mail"
|
</small>
|
||||||
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
|
</span>
|
||||||
</form>
|
<a href="/admin/user/{{ user.id }}" class="btn btn-dark ml-3">{% include "includes/pencil" %}</a>
|
||||||
{% endif %}
|
|
||||||
{% if user.last_access %}• ⏳ {{ user.last_access | date }}{% endif %}
|
|
||||||
</span>
|
|
||||||
<small class="block text-gray-600 dark:text-gray-100">
|
|
||||||
{% for role in user.roles -%}
|
|
||||||
{{ role }}
|
|
||||||
{%- if not loop.last %},
|
|
||||||
{% endif -%}
|
|
||||||
{% endfor %}
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
</summary>
|
|
||||||
<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 }}">✏️</a>
|
|
||||||
<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 %}
|
|
||||||
{% for role in roles %}
|
|
||||||
{% if not role.cluster %}
|
|
||||||
{{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<hr class="sm:col-span-2 lg:col-span-4 my-3" />
|
|
||||||
{% if user.membership_pdf %}
|
|
||||||
<a href="/admin/user/{{ user.id }}/membership"
|
|
||||||
class="text-black dark:text-white">Beitrittserklärung herunterladen</a>
|
|
||||||
{% else %}
|
|
||||||
{{ macros::input(label='Beitrittserklärung', name='membership_pdf', id=loop.index, type="file", readonly=allowed_to_edit == false, accept='application/pdf') }}
|
|
||||||
{% endif %}
|
|
||||||
{{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Mitglied seit', name='member_since_date', id=loop.index, type="text", value=user.member_since_date, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Geburtsdatum', name='birthdate', id=loop.index, type="text", value=user.birthdate, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Mail', name='mail', id=loop.index, type="text", value=user.mail, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Nickname', name='nickname', id=loop.index, type="text", value=user.nickname, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Notizen', name='notes', id=loop.index, type="text", value=user.notes, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Telefon', name='phone', id=loop.index, type="text", value=user.phone, readonly=allowed_to_edit == false) }}
|
|
||||||
{{ macros::input(label='Adresse', name='address', id=loop.index, type="text", value=user.address, readonly=allowed_to_edit == false) }}
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
{{ 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') }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<div class="mt-3 text-right">
|
|
||||||
<a href="/admin/user/{{ user.id }}/delete"
|
|
||||||
class="w-28 btn btn-alert"
|
|
||||||
onclick="return confirm('Wirklich löschen?');">
|
|
||||||
{% include "includes/delete-icon" %}
|
|
||||||
Löschen
|
|
||||||
</a>
|
|
||||||
<input value="Ändern" type="submit" class="w-28 btn btn-primary ml-1" />
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="max-w-screen-lg w-full">
|
<div class="max-w-screen-lg w-full">
|
||||||
|
{% if "admin" in loggedin_user.roles or "Vorstand" in loggedin_user.roles %}
|
||||||
|
<a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a>
|
||||||
|
{% endif %}
|
||||||
<h1 class="h1">{{ user.name }}</h1>
|
<h1 class="h1">{{ user.name }}</h1>
|
||||||
<div class="grid sm:grid-cols-2 gap-3">
|
<div class="grid sm:grid-cols-2 gap-3">
|
||||||
<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">
|
|
||||||
<h2 class="h2">
|
<h2 class="h2">
|
||||||
Grunddaten
|
Grunddaten
|
||||||
<br />
|
<br />
|
||||||
@ -14,11 +16,11 @@
|
|||||||
{% if user.last_access %}
|
{% if user.last_access %}
|
||||||
Zuletzt eingeloggt am {{ user.last_access | date(format="%d. %m. %Y") }}
|
Zuletzt eingeloggt am {{ user.last_access | date(format="%d. %m. %Y") }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ user.name }} hat sich noch nie eingeloggt.
|
App-Boykott 😢
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</small>
|
</small>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
<div class="py-3 grid gap-3">
|
<div class="py-3 grid gap-3">
|
||||||
<form action="/admin/user/{{ user.id }}/change-mail" method="post">
|
<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) }}
|
{{ macros::inputgroup(label='Mailadresse', name='mail', type="text", value=user.mail, readonly=not allowed_to_edit) }}
|
||||||
@ -29,12 +31,23 @@
|
|||||||
<form action="/admin/user/{{ user.id }}/change-nickname" method="post">
|
<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) }}
|
{{ macros::inputgroup(label='Spitzname', name='nickname', type="text", value=user.nickname, readonly=not allowed_to_edit) }}
|
||||||
</form>
|
</form>
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<form action="/admin/user/{{ user.id }}/new-note" method="post">
|
||||||
|
{{ macros::inputgroup(label='Neue Notiz', name='note', type="text") }}
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
<span>Notizen: to be replaced with activity :-)</span>
|
<span>Notizen: to be replaced with activity :-)</span>
|
||||||
|
{% if user.pw and allowed_to_edit %}
|
||||||
|
<div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">
|
<h2 class="h2">
|
||||||
Mitgliedschaft
|
Mitgliedschaft
|
||||||
<br />
|
<br />
|
||||||
@ -55,7 +68,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</small>
|
</small>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<div class="mx-3">
|
||||||
{% if is_clubmember %}
|
{% if is_clubmember %}
|
||||||
<div class="py-3 grid gap-3">
|
<div class="py-3 grid gap-3">
|
||||||
<form action="/admin/user/{{ user.id }}/change-member-since" method="post">
|
<form action="/admin/user/{{ user.id }}/change-member-since" method="post">
|
||||||
@ -73,7 +86,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
{% if user.membership_pdf %}
|
{% if user.membership_pdf %}
|
||||||
<a href="/admin/user/{{ user.id }}/membership" class="link link-primary">Beitrittserklärung herunterladen</a>
|
<a href="/admin/user/{{ user.id }}/membership"
|
||||||
|
class="link link-primary link-no-underline">Beitrittserklärung herunterladen ↓</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
⚠️ Aktuell gibt's keine Beitrittserklärung 😢
|
⚠️ Aktuell gibt's keine Beitrittserklärung 😢
|
||||||
{% if allowed_to_edit %}
|
{% if allowed_to_edit %}
|
||||||
@ -93,6 +107,9 @@
|
|||||||
{% if allowed_to_edit %}
|
{% if allowed_to_edit %}
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
<div class="mt-3 text-right">
|
<div class="mt-3 text-right">
|
||||||
|
<button type="button"
|
||||||
|
onclick="document.getElementById('change-member-type').showModal()"
|
||||||
|
class="btn btn-dark">Mitgliedsstatus ändern</button>
|
||||||
<a href="/admin/user/{{ user.id }}/delete"
|
<a href="/admin/user/{{ user.id }}/delete"
|
||||||
class="btn btn-alert"
|
class="btn btn-alert"
|
||||||
onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');">
|
onclick="return confirm('Ist {{ user.name }} wirklich aus dem Verein ausgetreten?');">
|
||||||
@ -101,115 +118,241 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<dialog id="change-member-type"
|
||||||
|
class="max-w-screen-sm w-full dark:bg-primary-600 dark:text-white rounded-md"
|
||||||
|
onclick="document.getElementById('change-member-type').close()">
|
||||||
|
<div onclick="event.stopPropagation();" class="p-3">
|
||||||
|
<button type="button"
|
||||||
|
onclick="document.getElementById('change-member-type').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 }}/change-membertype"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="grid gap-3">
|
||||||
|
<div>
|
||||||
|
<label for="membertype" class="text-sm text-gray-600 dark:text-gray-100">Mitgliedsstatus</label>
|
||||||
|
<select name="membertype" id="membertype" class="input rounded-md ">
|
||||||
|
<option selected="" value="regular">Reguläres Vereinsmitglied</option>
|
||||||
|
<option value="unterstuetzend">Unterstützendes Vereinsmitglied</option>
|
||||||
|
<option value="foerdernd">Förderndes Vereinsmitglied</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input value="Ändern" type="submit" class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif "Scheckbuch" in member %}
|
{% elif "Scheckbuch" in member %}
|
||||||
<div class="grid gap-3 pb-3">
|
{% if allowed_to_edit %}
|
||||||
{% for log in logbook %}
|
<div class="grid gap-3 pb-3">
|
||||||
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=false) }}
|
<div class="max-h-60 overflow-y-scroll">
|
||||||
{% endfor %}
|
{% for log in logbook %}
|
||||||
<button type="button"
|
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=false) }}
|
||||||
onclick="document.getElementById('call-for-action').showModal()"
|
{% endfor %}
|
||||||
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>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
{% endif %}
|
||||||
|
{% elif "SchnupperInterest" in member %}
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/schnupperinterest-to-scheckbuch"
|
||||||
|
class="btn btn-dark"
|
||||||
|
onclick="return confirm('Willst du \'{{ user.name }}\' wirklich auf ein Scheckbuch umwandeln?');">In Scheckbuch umwandeln</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/schnupperinterest-to-schnupperant"
|
||||||
|
class="btn btn-dark"
|
||||||
|
onclick="return confirm('Hat sich \'{{ user.name }}\' wirklich zum Kurs angemeldet?');">Zum Schnupperkurs angemeldet</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/delete"
|
||||||
|
class="btn btn-alert"
|
||||||
|
onclick="return confirm('Ist {{ user.name }} wirklich nicht mehr am Schnupperkurs interessiert?');">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Daten löschen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% elif "Schnupperant" in member %}
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/schnupperant-to-schnupperinterest"
|
||||||
|
class="btn btn-dark"
|
||||||
|
onclick="return confirm('Hat sich \'{{ user.name }}\' wirklich vom Schnupperkurs abgemeldet?');">Vom Kurs abgemeldet</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/schnupperant-to-scheckbuch"
|
||||||
|
class="btn btn-dark"
|
||||||
|
onclick="return confirm('Willst du \'{{ user.name }}\' wirklich auf ein Scheckbuch umwandeln?');">In Scheckbuch umwandeln</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/delete"
|
||||||
|
class="btn btn-alert"
|
||||||
|
onclick="return confirm('Ist {{ user.name }} wirklich nicht mehr am Schnupperkurs interessiert?');">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Daten löschen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if "Scheckbuch" in member or "Schnupperant" in member %}
|
||||||
|
{% if allowed_to_edit %}
|
||||||
|
<div class="grid gap-3 pb-3 mt-3">
|
||||||
|
<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">
|
||||||
|
{% if "Scheckbuch" in member %}
|
||||||
|
{% set action = "scheckbook-to-regular" %}
|
||||||
|
{% elif "Schnupperant" in member %}
|
||||||
|
{% set action = "schnupperant-to-regular" %}
|
||||||
|
{% endif %}
|
||||||
|
<form action="/admin/user/{{ user.id }}/{{ action }}"
|
||||||
|
method="post"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
class="grid gap-3">
|
||||||
|
<div>
|
||||||
|
<label for="membertype" class="text-sm text-gray-600 dark:text-gray-100">Mitgliedstyp</label>
|
||||||
|
<select name="membertype" id="membertype" class="input rounded-md ">
|
||||||
|
<option selected="" value="regular">Reguläres Vereinsmitglied</option>
|
||||||
|
<option value="unterstuetzend">Unterstützendes Vereinsmitglied</option>
|
||||||
|
<option value="foerdernd">Förderndes Vereinsmitglied</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{ 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>
|
||||||
|
<div class="grid pt-3">
|
||||||
|
<a href="/admin/user/{{ user.id }}/delete"
|
||||||
|
class="btn btn-alert"
|
||||||
|
onclick="return confirm('Willst du die Daten von {{ user.name }} wirklich? Seine restlichen Scheckbuch-Ausfahrten entfallen damit...');">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Daten löschen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if is_clubmember %}
|
{% if is_clubmember %}
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">Rollen</h2>
|
<h2 class="h2">Rollen</h2>
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<div>
|
||||||
<div class="py-3">
|
<ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full">
|
||||||
<ul>
|
{% for role in user.proper_roles -%}
|
||||||
{% for role in user.proper_roles -%}
|
{% if not role.cluster and not role.hide_in_lists %}
|
||||||
{% if not role.cluster and not role.hide_in_lists %}
|
<li class="flex w-full justify-between items-center p-3 {% if allowed_to_edit %}hover:bg-gray-100 dark:hover:bg-primary-950{% endif %}">
|
||||||
<li class="flex my-2 w-full justify-between items-center hover:bg-gray-100">
|
<span>
|
||||||
<span>
|
<strong>
|
||||||
<strong>
|
{% if role.formatted_name %}
|
||||||
{% if role.formatted_name %}
|
{{ role.formatted_name }}
|
||||||
{{ role.formatted_name }}
|
{% else %}
|
||||||
{% else %}
|
{{ role.name }}
|
||||||
{{ role.name }}
|
{% endif %}
|
||||||
{% endif %}
|
</strong>
|
||||||
</strong>
|
<br />
|
||||||
<br />
|
<small>{{ role.desc }}</small>
|
||||||
<small>{{ role.desc }}</small>
|
</span>
|
||||||
</span>
|
{% if allowed_to_edit %}
|
||||||
{% if allowed_to_edit %}
|
<a href="/admin/user/{{ user.id }}/remove-role/{{ role.id }}"
|
||||||
<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>
|
||||||
onclick="return confirm('Willst du die Rolle \'{{ role.name }}\' von {{ user.name }} wirklich entfernen?');">🗑️</a>
|
{% endif %}
|
||||||
{% endif %}
|
</li>
|
||||||
</li>
|
{% endif %}
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
{% endfor %}
|
</ul>
|
||||||
</ul>
|
{% if allowed_to_edit %}
|
||||||
{% if allowed_to_edit %}
|
<div class="m-3">
|
||||||
<details>
|
<button type="button"
|
||||||
<summary>+ Rolle</summary>
|
onclick="document.getElementById('role-modal').showModal()"
|
||||||
<form action="/admin/user/{{ user.id }}/add-role" method="post">
|
class="btn btn-primary w-full">Rolle hinzufügen</button>
|
||||||
<fieldset>
|
</div>
|
||||||
<select name="role_id">
|
<dialog id="role-modal"
|
||||||
{% for role in roles %}
|
class="max-w-screen-sm w-full dark:bg-primary-600 dark:text-white rounded-md"
|
||||||
{% if not role.cluster and role not in user.proper_roles and not role.hide_in_lists %}
|
onclick="document.getElementById('role-modal').close()">
|
||||||
<option value="{{ role.id }}">
|
<div onclick="event.stopPropagation();" class="p-3">
|
||||||
{% if role.formatted_name %}
|
<button type="button"
|
||||||
{{ role.formatted_name }}
|
onclick="document.getElementById('role-modal').close()"
|
||||||
{% else %}
|
title="Schließen"
|
||||||
{{ role.name }}
|
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">
|
||||||
{% endif %}
|
<svg class="inline h-5 w-5"
|
||||||
{% if role.desc %}({{ role.desc }}){% endif %}
|
width="16"
|
||||||
</option>
|
height="16"
|
||||||
{% endif %}
|
fill="currentColor"
|
||||||
{% endfor %}
|
viewBox="0 0 16 16">
|
||||||
</select>
|
<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>
|
||||||
<input value="Rolle hinzufügen" type="submit" class="btn btn-primary ml-1" />
|
</svg>
|
||||||
</fieldset>
|
</button>
|
||||||
</form>
|
<div class="mt-8">
|
||||||
</details>
|
<form action="/admin/user/{{ user.id }}/add-role"
|
||||||
{% endif %}
|
method="post"
|
||||||
</div>
|
class="grid gap-3">
|
||||||
|
<div>
|
||||||
|
<label for="role_id" class="text-sm text-gray-600 dark:text-gray-100">Rollen</label>
|
||||||
|
<select name="role_id" id="role_id" class="input rounded-md ">
|
||||||
|
{% 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 %}
|
||||||
|
{{ role.formatted_name }}
|
||||||
|
{% else %}
|
||||||
|
{{ role.name }}
|
||||||
|
{% endif %}
|
||||||
|
</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input value="Rolle hinzufügen" type="submit" class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if supposed_to_pay %}
|
{% if supposed_to_pay %}
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">💸-Beitrag</h2>
|
<h2 class="h2">💸-Beitrag</h2>
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
{% if fee %}
|
{% if fee %}
|
||||||
<div>
|
<div>
|
||||||
@ -242,10 +385,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
role="alert">
|
<h2 class="h2">Aktivitäten</h2>
|
||||||
<h2 class="h2">Aktivität von und mit {{ user.name }}</h2>
|
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
<ul class="list-disc ms-4">
|
<ul class="list-disc ms-4">
|
||||||
<li>Passwort zurückgesetzt am/um X</li>
|
<li>Passwort zurückgesetzt am/um X</li>
|
||||||
@ -254,8 +396,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
role="alert">
|
|
||||||
<h2 class="h2">TODO</h2>
|
<h2 class="h2">TODO</h2>
|
||||||
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
||||||
<span class="text-black dark:text-white cursor-pointer">
|
<span class="text-black dark:text-white cursor-pointer">
|
||||||
@ -277,11 +418,6 @@
|
|||||||
method="post"
|
method="post"
|
||||||
enctype="multipart/form-data"
|
enctype="multipart/form-data"
|
||||||
class="w-full mt-2">
|
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">
|
<div class="w-full grid gap-3 mt-3">
|
||||||
<input type="hidden" name="id" value="{{ user.id }}" />
|
<input type="hidden" name="id" value="{{ user.id }}" />
|
||||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
@ -318,10 +454,9 @@
|
|||||||
</form>
|
</form>
|
||||||
</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">
|
|
||||||
<h2 class="h2">Ergo-Challenge</h2>
|
<h2 class="h2">Ergo-Challenge</h2>
|
||||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
{{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
{{ 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='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
||||||
|
@ -198,7 +198,10 @@ function setChoiceByLabel(choicesInstance, label) {
|
|||||||
{% 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-dark rounded-l-none-important edit-js">{% include "includes/pencil" %}</button>
|
<button type="button" class="btn btn-dark rounded-l-none-important edit-js">
|
||||||
|
{% include "includes/pencil" %}
|
||||||
|
<span class="sr-only">Bearbeiten</span>
|
||||||
|
</button>
|
||||||
<input value="x"
|
<input value="x"
|
||||||
type="reset"
|
type="reset"
|
||||||
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
||||||
@ -244,7 +247,10 @@ function setChoiceByLabel(choicesInstance, label) {
|
|||||||
{% if new_last_entry %}<option value="-1">{{ new_last_entry }}</option>{% endif %}
|
{% if new_last_entry %}<option value="-1">{{ new_last_entry }}</option>{% endif %}
|
||||||
</select>
|
</select>
|
||||||
{% if allowed_to_edit %}
|
{% if allowed_to_edit %}
|
||||||
<button type="button" class="btn btn-dark rounded-l-none-important edit-js">{% include "includes/pencil" %}</button>
|
<button type="button" class="btn btn-dark rounded-l-none-important edit-js">
|
||||||
|
{% include "includes/pencil" %}
|
||||||
|
<span class="sr-only">Bearbeiten</span>
|
||||||
|
</button>
|
||||||
<input value="x"
|
<input value="x"
|
||||||
type="reset"
|
type="reset"
|
||||||
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
class="edit-js btn btn-alert btn-hidden rounded-none-important" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user