Compare commits
17 Commits
improve-lo
...
bd2686fa9c
Author | SHA1 | Date | |
---|---|---|---|
bd2686fa9c | |||
495ee666cd | |||
5296b6a6c1 | |||
49e657ab54 | |||
25bbaca0d3 | |||
26038eabe4 | |||
57acd92e7c | |||
c136c60e62 | |||
a5e90ea014 | |||
f0f3909239 | |||
1438bbe3a8 | |||
a910cd745d | |||
6265440288 | |||
3baed66ebc | |||
499ce06438 | |||
67e5277c62 | |||
ce154bf060 |
@@ -5,7 +5,3 @@
|
||||
.h2 {
|
||||
@apply font-bold uppercase tracking-wide text-center rounded-t-md text-primary-950 dark:text-white bg-gray-200 dark:bg-primary-950 bg-opacity-80 text-lg px-3 py-3;
|
||||
}
|
||||
|
||||
.h3 {
|
||||
@apply text-center text-xl uppercase tracking-wide font-bold text-primary-900 dark:text-white;
|
||||
}
|
@@ -11,10 +11,6 @@
|
||||
@apply text-white hover:text-primary-100 underline;
|
||||
}
|
||||
|
||||
&-black {
|
||||
@apply text-black hover:text-primary-950 dark:text-white hover:dark:text-primary-300 underline;
|
||||
}
|
||||
|
||||
&-no-underline {
|
||||
@apply no-underline;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ use rot::rest;
|
||||
use rot::tera;
|
||||
use rot::{scheduled, tera::Config};
|
||||
|
||||
use sqlx::{ConnectOptions, pool::PoolOptions, sqlite::SqliteConnectOptions};
|
||||
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use super::{role::Role, user::User};
|
||||
use super::user::User;
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||
@@ -14,47 +14,6 @@ pub struct Activity {
|
||||
pub keep_until: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
pub struct ActivityBuilder {
|
||||
text: String,
|
||||
relevant_for: String,
|
||||
keep_until: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl ActivityBuilder {
|
||||
#[must_use]
|
||||
pub fn new(text: &str) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
relevant_for: String::new(),
|
||||
keep_until: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn relevant_for_user(self, user: &User) -> Self {
|
||||
Self {
|
||||
relevant_for: format!("{}user-{};", self.relevant_for, user.id),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn relevant_for_role(self, role: &Role) -> Self {
|
||||
Self {
|
||||
relevant_for: format!("{}role-{};", self.relevant_for, role.id),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save(self, db: &SqlitePool) {
|
||||
Activity::create(db, &self.text, &self.relevant_for, self.keep_until).await;
|
||||
}
|
||||
|
||||
pub async fn save_tx(self, db: &mut Transaction<'_, Sqlite>) {
|
||||
Activity::create_with_tx(db, &self.text, &self.relevant_for, self.keep_until).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Activity {
|
||||
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
@@ -66,7 +25,7 @@ impl Activity {
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
pub(super) async fn create_with_tx(
|
||||
pub async fn create_with_tx(
|
||||
db: &mut Transaction<'_, Sqlite>,
|
||||
text: &str,
|
||||
relevant_for: &str,
|
||||
@@ -83,7 +42,7 @@ impl Activity {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(super) async fn create(
|
||||
pub async fn create(
|
||||
db: &SqlitePool,
|
||||
text: &str,
|
||||
relevant_for: &str,
|
||||
|
@@ -2,8 +2,8 @@ use std::ops::DerefMut;
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
use itertools::Itertools;
|
||||
use rocket::FromForm;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use rocket::FromForm;
|
||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||
|
||||
use crate::model::boathouse::Boathouse;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use crate::model::{boat::Boat, user::User};
|
||||
use chrono::NaiveDateTime;
|
||||
use rocket::FromForm;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use rocket::FromForm;
|
||||
use sqlx::{FromRow, SqlitePool};
|
||||
|
||||
use super::log::Log;
|
||||
|
@@ -2,8 +2,8 @@ use std::io::Write;
|
||||
|
||||
use chrono::{Duration, NaiveDate, NaiveTime};
|
||||
use ics::{
|
||||
ICalendar,
|
||||
properties::{DtEnd, DtStart, Summary},
|
||||
ICalendar,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use sqlx::{FromRow, Row, SqlitePool};
|
||||
@@ -578,11 +578,6 @@ mod test {
|
||||
|
||||
let today = Local::now().date_naive().format("%Y%m%d").to_string();
|
||||
let actual = Event::get_ics_feed(&pool).await;
|
||||
assert_eq!(
|
||||
format!(
|
||||
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:event-1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:{today}T100000\r\nDTEND:{today}T130000\r\nSUMMARY:test-planned-event \r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"
|
||||
),
|
||||
actual
|
||||
);
|
||||
assert_eq!(format!("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:event-1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:{today}T100000\r\nDTEND:{today}T130000\r\nSUMMARY:test-planned-event \r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"), actual);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use serde::Serialize;
|
||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction, sqlite::SqliteQueryResult};
|
||||
use sqlx::{sqlite::SqliteQueryResult, FromRow, Sqlite, SqlitePool, Transaction};
|
||||
|
||||
use super::user::User;
|
||||
|
||||
|
@@ -823,13 +823,7 @@ ORDER BY departure DESC
|
||||
if difference > Duration::hours(1) {
|
||||
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
|
||||
let logbook = LogbookWithBoatAndRowers::from(db, self.clone()).await;
|
||||
let mut msg = format!(
|
||||
"{} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: Schiffsführer: {}, Steuerperson: {}, Abfahrt: {}",
|
||||
user.name,
|
||||
logbook.steering_user.name,
|
||||
logbook.steering_user.name,
|
||||
logbook.logbook.departure.format("%Y-%m-%d %H:%M")
|
||||
);
|
||||
let mut msg = format!("{} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: Schiffsführer: {}, Steuerperson: {}, Abfahrt: {}", user.name, logbook.steering_user.name, logbook.steering_user.name, logbook.logbook.departure.format("%Y-%m-%d %H:%M"));
|
||||
if let Some(destination) = logbook.logbook.destination {
|
||||
msg.push_str(&format!(", Ziel: {}", destination));
|
||||
} else {
|
||||
|
@@ -1,15 +1,15 @@
|
||||
use std::{error::Error, fs};
|
||||
|
||||
use lettre::{
|
||||
Address, Message, SmtpTransport, Transport,
|
||||
message::{Attachment, MultiPart, SinglePart, header::ContentType},
|
||||
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||
transport::smtp::authentication::Credentials,
|
||||
Address, Message, SmtpTransport, Transport,
|
||||
};
|
||||
use sqlx::{Sqlite, SqlitePool, Transaction};
|
||||
|
||||
use crate::tera::admin::mail::MailToSend;
|
||||
|
||||
use super::{activity::ActivityBuilder, family::Family, log::Log, role::Role, user::User};
|
||||
use super::{family::Family, log::Log, role::Role, user::User};
|
||||
|
||||
pub struct Mail {}
|
||||
|
||||
@@ -253,12 +253,6 @@ Der Vorstand");
|
||||
|
||||
// Send the email
|
||||
mailer.send(&email).unwrap();
|
||||
ActivityBuilder::new(&format!(
|
||||
"{user} hat die Info-Mail bzgl. Gebühren gesendet bekommen."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,12 +369,6 @@ Der Vorstand");
|
||||
|
||||
// Send the email
|
||||
mailer.send(&email).unwrap();
|
||||
ActivityBuilder::new(&format!(
|
||||
"{user} hat die Mahn-Mail bzgl. Gebühren gesendet bekommen."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use std::io::Write;
|
||||
|
||||
use ics::{ICalendar, components::Property};
|
||||
use ics::{components::Property, ICalendar};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{event::Event, trip::Trip, user::User};
|
||||
|
@@ -1,6 +1,5 @@
|
||||
use std::{cmp::Ordering, fmt::Display, ops::DerefMut};
|
||||
use std::{fmt::Display, ops::DerefMut};
|
||||
|
||||
use super::{activity::ActivityBuilder, user::AdminUser};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||
|
||||
@@ -14,30 +13,6 @@ pub struct Role {
|
||||
pub(crate) cluster: Option<String>,
|
||||
}
|
||||
|
||||
// Implement PartialEq to compare roles based only on id
|
||||
impl PartialEq for Role {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Eq to indicate that equality is reflexive
|
||||
impl Eq for Role {}
|
||||
|
||||
// Implement PartialOrd if you need to sort or compare roles
|
||||
impl PartialOrd for Role {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.id.cmp(&other.id))
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Ord if you need total ordering (for sorting)
|
||||
impl Ord for Role {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.id.cmp(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Role {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
@@ -55,27 +30,6 @@ impl Role {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn all_cluster(db: &SqlitePool, cluster: &str) -> Vec<Role> {
|
||||
sqlx::query_as!(
|
||||
Role,
|
||||
r#"SELECT id,
|
||||
CASE WHEN formatted_name IS NOT NULL AND formatted_name != ''
|
||||
THEN formatted_name
|
||||
ELSE name
|
||||
END AS "name!: String",
|
||||
'' as formatted_name,
|
||||
desc,
|
||||
hide_in_lists,
|
||||
cluster
|
||||
FROM role
|
||||
WHERE cluster = ?"#,
|
||||
cluster
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(db: &SqlitePool, name: i32) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
@@ -105,6 +59,21 @@ WHERE id like ?
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub async fn find_by_cluster_tx(db: &mut Transaction<'_, Sqlite>, name: i32) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
"
|
||||
SELECT id, name, formatted_name, desc, hide_in_lists, cluster
|
||||
FROM role
|
||||
WHERE cluster = ?
|
||||
",
|
||||
name
|
||||
)
|
||||
.fetch_one(db.deref_mut())
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
@@ -135,30 +104,6 @@ WHERE name like ?
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &AdminUser,
|
||||
formatted_name: &str,
|
||||
desc: &str,
|
||||
) -> Result<(), String> {
|
||||
sqlx::query!(
|
||||
"UPDATE role SET formatted_name=?, desc=? WHERE id=?",
|
||||
formatted_name,
|
||||
desc,
|
||||
self.id
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat Rolle {self} von {self:#?} auf FORMATTED_NAME={formatted_name}, DESC={desc} aktualisiert."
|
||||
)).relevant_for_role(self).save(db).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn names_from_role(&self, db: &SqlitePool) -> Vec<String> {
|
||||
let query = format!(
|
||||
"SELECT u.name
|
||||
|
@@ -567,11 +567,9 @@ mod test {
|
||||
|
||||
let last_notification = &Notification::for_user(&pool, &cox).await[0];
|
||||
|
||||
assert!(
|
||||
last_notification
|
||||
.message
|
||||
.starts_with("cox2 hat eine Ausfahrt zur selben Zeit")
|
||||
);
|
||||
assert!(last_notification
|
||||
.message
|
||||
.starts_with("cox2 hat eine Ausfahrt zur selben Zeit"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
|
@@ -1,10 +1,7 @@
|
||||
// TODO: put back in `src/model/user/mod.rs` once that is cleaned up
|
||||
|
||||
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
|
||||
use crate::model::{
|
||||
activity::ActivityBuilder, family::Family, mail::valid_mails, notification::Notification,
|
||||
role::Role,
|
||||
};
|
||||
use crate::model::{activity::Activity, family::Family, log::Log, mail::valid_mails, role::Role};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{fs::TempFile, tokio::io::AsyncReadExt};
|
||||
use sqlx::SqlitePool;
|
||||
@@ -19,10 +16,13 @@ impl User {
|
||||
) -> Result<(), String> {
|
||||
let note = note.trim();
|
||||
|
||||
ActivityBuilder::new(&format!("({updated_by}) {note}"))
|
||||
.relevant_for_user(user)
|
||||
.save(db)
|
||||
.await;
|
||||
Activity::create(
|
||||
db,
|
||||
&format!("({updated_by}) {note}"),
|
||||
&format!("user-{};", user.id),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -47,20 +47,12 @@ impl User {
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.mail {
|
||||
Some(old_mail) => {
|
||||
format!(
|
||||
"{updated_by} hat die Mail-Adresse von {self} von {old_mail} auf {new_mail} geändert."
|
||||
)
|
||||
}
|
||||
None => {
|
||||
format!("{updated_by} eine neue Mail-Adresse für {self} hinzugefügt: {new_mail}")
|
||||
}
|
||||
Some(old_mail) => format!(
|
||||
"{updated_by} has changed the mail address of {self} from {old_mail} to {new_mail}"
|
||||
),
|
||||
None => format!("{updated_by} has added a mail address for {self}: {new_mail}"),
|
||||
};
|
||||
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -89,21 +81,11 @@ impl User {
|
||||
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.phone {
|
||||
Some(old_phone) if new_phone.is_empty() => format!(
|
||||
"{updated_by} hat die Telefonnummer von {self} entfernt (alte Nummer: {old_phone})"
|
||||
),
|
||||
Some(old_phone) => format!(
|
||||
"{updated_by} hat die Telefonnummer von {self} von {old_phone} auf {new_phone} geändert."
|
||||
),
|
||||
None => format!(
|
||||
"{updated_by} hat eine neue Telefonnummer für {self} hinzugefügt: {new_phone}"
|
||||
),
|
||||
Some(old_phone) if new_phone.is_empty() => format!("{updated_by} has removed the phone number of {self} (old number: {old_phone})"),
|
||||
Some(old_phone) => format!("{updated_by} has changed the phone number of {self} from {old_phone} to {new_phone}"),
|
||||
None => format!("{updated_by} has added a phone number for {self}: {new_phone}")
|
||||
};
|
||||
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_address(
|
||||
@@ -115,7 +97,7 @@ impl User {
|
||||
let new_address = new_address.trim();
|
||||
|
||||
let query = if new_address.is_empty() {
|
||||
if self.address.is_none() {
|
||||
if !self.address.is_none() {
|
||||
return; // nothing to do
|
||||
}
|
||||
sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id)
|
||||
@@ -134,19 +116,11 @@ impl User {
|
||||
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.address {
|
||||
Some(old_address) if new_address.is_empty() => format!(
|
||||
"{updated_by} hat die Adresse von {self} entfernt (alte Adresse: {old_address})"
|
||||
),
|
||||
Some(old_address) => format!(
|
||||
"{updated_by} hat die Adresse von {self} von {old_address} auf {new_address} geändert."
|
||||
),
|
||||
None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}"),
|
||||
Some(old_address) if new_address.is_empty() => format!("{updated_by} has removed the address of {self} (old address: {old_address})"),
|
||||
Some(old_address) => format!("{updated_by} has changed the address of {self} from {old_address} to {new_address}"),
|
||||
None => format!("{updated_by} has added an address for {self}: {new_address}")
|
||||
};
|
||||
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_nickname(
|
||||
@@ -169,20 +143,11 @@ impl User {
|
||||
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.nickname {
|
||||
Some(old_nickname) if new_nickname.is_empty() => format!(
|
||||
"{updated_by} hat den Sitznamen von {self} entfernt (alter Spitzname: {old_nickname})"
|
||||
),
|
||||
Some(old_nickname) => format!(
|
||||
"{updated_by} hat den Spitznamen von {self} von {old_nickname} auf {new_nickname} geändert."
|
||||
),
|
||||
None => format!(
|
||||
"{updated_by} hat einen neuen Spitznamen für {self} hinzugefügt: {new_nickname}"
|
||||
),
|
||||
Some(old_nickname) if new_nickname.is_empty() => format!("{updated_by} has removed the nickname of {self} (old nickname: {old_nickname})"),
|
||||
Some(old_nickname) => format!("{updated_by} has changed the nickname of {self} from {old_nickname} to {new_nickname}"),
|
||||
None => format!("{updated_by} has added a nickname for {self}: {new_nickname}")
|
||||
};
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -203,18 +168,10 @@ impl User {
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.member_since_date {
|
||||
Some(old_member_since_date) => format!(
|
||||
"{updated_by} hat das Beitrittsdatum von {self} von {old_member_since_date} auf {new_member_since_date} geändert."
|
||||
),
|
||||
None => format!(
|
||||
"{updated_by} hat ein neues Beitrittsdatum für {self} hinzugefügt: {new_member_since_date}"
|
||||
),
|
||||
Some(old_member_since_date) => format!("{updated_by} has changed the member_since date of {self} from {old_member_since_date} to {new_member_since_date}"),
|
||||
None => format!("{updated_by} has added a member_since_date for {self}: {new_member_since_date}")
|
||||
};
|
||||
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_birthdate(
|
||||
@@ -232,19 +189,11 @@ impl User {
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.birthdate {
|
||||
Some(old_birthdate) => format!(
|
||||
"{updated_by} hat das Geburtsdatum von {self} von {old_birthdate} auf {new_birthdate} geändert."
|
||||
),
|
||||
None => {
|
||||
format!("{updated_by} hat ein Geburtsdatum für {self} hinzugefügt: {new_birthdate}")
|
||||
}
|
||||
let msg = match &self.birthdate{
|
||||
Some(old_birthdate) => format!("{updated_by} has changed the birthdate of {self} from {old_birthdate} to {new_birthdate}"),
|
||||
None => format!("{updated_by} has added a birthdate for {self}: {new_birthdate}")
|
||||
};
|
||||
|
||||
ActivityBuilder::new(&msg)
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_family(
|
||||
@@ -263,134 +212,20 @@ impl User {
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap();
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat {self} zu einer Familie hinzugefügt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
} else {
|
||||
sqlx::query!("UPDATE user SET family_id = NULL where id = ?", self.id)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap();
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat die Familienzugehörigkeit von {self} gelöscht."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
};
|
||||
|
||||
Family::clean_families_without_members(db).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn change_skill(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
skill: Option<Role>,
|
||||
) -> Result<(), String> {
|
||||
let old_skill = self.skill(db).await;
|
||||
|
||||
let member = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||
let cox = Role::find_by_name(db, "cox").await.unwrap();
|
||||
let bootsfuehrer = Role::find_by_name(db, "Bootsführer").await.unwrap();
|
||||
|
||||
match (old_skill, skill) {
|
||||
(None, new) if new == Some(cox.clone()) => {
|
||||
self.add_role(db, updated_by, &cox).await?;
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&member,
|
||||
&format!(
|
||||
"Liebes Vereinsmitglied, {self} ist ab sofort Steuerperson 🎉 Hip hip ...!"
|
||||
),
|
||||
"Neue Steuerperson",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht"))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
(old, new) if old == Some(cox.clone()) && new == Some(bootsfuehrer.clone()) => {
|
||||
self.remove_role(db, updated_by, &cox).await?;
|
||||
self.add_role(db, updated_by, &bootsfuehrer).await?;
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&member,
|
||||
&format!(
|
||||
"Liebes Vereinsmitglied, {self} ist ab sofort Bootsführer:in 🎉 Hip hip ...!"
|
||||
),
|
||||
"Neue:r Bootsführer:in",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("{updated_by} hat {self} zum Bootsführer gemacht"))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
(old, None) => {
|
||||
if let Some(old) = old {
|
||||
self.remove_role(db, updated_by, &old).await?;
|
||||
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&vorstand,
|
||||
&format!("Lieber Vorstand, {self} ist ab kein {old} mehr."),
|
||||
"Steuerperson --",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitlgied gemacht (keine Steuerperson/Schiffsführer mehr)"))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
(old, new) => return Err(format!("Not allowed to change from {old:?} to {new:?}")),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn change_financial(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
financial: Option<Role>,
|
||||
) -> Result<(), String> {
|
||||
let mut new = String::new();
|
||||
let mut old = String::new();
|
||||
|
||||
if let Some(old_financial) = self.financial(db).await {
|
||||
self.remove_role(db, updated_by, &old_financial).await?;
|
||||
old.push_str(&old_financial.name);
|
||||
} else {
|
||||
old.push_str("Keine Ermäßigung");
|
||||
}
|
||||
|
||||
if let Some(new_financial) = financial {
|
||||
self.add_role(db, updated_by, &new_financial).await?;
|
||||
new.push_str(&new_financial.name);
|
||||
} else {
|
||||
new.push_str("Keine Ermäßigung");
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert"
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} hat die Familie von {self} aktualisiert."),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_role(
|
||||
@@ -400,9 +235,7 @@ impl User {
|
||||
role: &Role,
|
||||
) -> Result<(), String> {
|
||||
if !self.has_role(db, &role.name).await {
|
||||
return Err(format!(
|
||||
"Kann Rolle {role} von User {self} nicht entfernen, da der User die Rolle gar nicht hat"
|
||||
));
|
||||
return Err(format!("Kann Rolle {role} von User {self} nicht entfernen, da der User die Rolle gar nicht hat"));
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
@@ -414,14 +247,11 @@ impl User {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if !role.hide_in_lists && role.cluster.is_none() {
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat die Rolle {role} von {self} entfernt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has removed role {role} from user {self}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -442,11 +272,10 @@ impl User {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has set that user {self} has NOT paid the fee (yet)"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
pub(crate) async fn has_paid(
|
||||
@@ -465,11 +294,10 @@ impl User {
|
||||
.await
|
||||
.expect("paid role has no group");
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has set that user {self} has paid the fee (yet)"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -480,9 +308,7 @@ impl User {
|
||||
role: &Role,
|
||||
) -> Result<(), String> {
|
||||
if self.has_role(db, &role.name).await {
|
||||
return Err(format!(
|
||||
"Kann Rolle {role} von User {self} nicht hinzufügen, da der User die Rolle schon hat"
|
||||
));
|
||||
return Err(format!("Kann Rolle {role} von User {self} nicht hinzufügen, da der User die Rolle schon hat"));
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
@@ -501,14 +327,11 @@ impl User {
|
||||
)
|
||||
})?;
|
||||
|
||||
if !role.hide_in_lists && role.cluster.is_none() {
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has added role {role} to user {self}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -523,7 +346,7 @@ impl User {
|
||||
return Err(format!("User {self} hat bereits eine Beitrittserklärung."));
|
||||
}
|
||||
if membership_pdf.len() == 0 {
|
||||
return Err("Keine Beitrittserklärung mitgeschickt.".to_string());
|
||||
return Err(format!("Keine Beitrittserklärung mitgeschickt."));
|
||||
}
|
||||
|
||||
let mut stream = membership_pdf.open().await.unwrap();
|
||||
@@ -538,11 +361,10 @@ impl User {
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has added the membership pdf for user {self}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
|
@@ -1,8 +1,6 @@
|
||||
use super::User;
|
||||
use crate::{
|
||||
model::{
|
||||
activity::ActivityBuilder, notification::Notification, role::Role, user::ManageUserUser,
|
||||
},
|
||||
model::{log::Log, notification::Notification, role::Role, user::ManageUserUser},
|
||||
special_user,
|
||||
};
|
||||
use rocket::async_trait;
|
||||
@@ -83,11 +81,10 @@ impl ClubMemberUser {
|
||||
)
|
||||
.await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{modified_by} hat {self} zu einem regulären hochgestuft."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{modified_by} has moved user {self} to regular membership."),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@@ -119,11 +116,10 @@ impl ClubMemberUser {
|
||||
.await;
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{modified_by} hat {self} zu einem unterstützenden Mitglied gemacht."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{modified_by} has moved user {self} to unterstützend membership."),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@@ -155,11 +151,10 @@ impl ClubMemberUser {
|
||||
.await;
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{modified_by} hat {self} zu ein förderndes Mitglied gemacht."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("{modified_by} has moved user {self} to fördernd membership."),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use super::User;
|
||||
use crate::{
|
||||
BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR,
|
||||
RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND, model::family::Family,
|
||||
model::family::Family, BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO,
|
||||
FOERDERND, REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND,
|
||||
};
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use serde::Serialize;
|
||||
|
@@ -1,17 +1,10 @@
|
||||
use super::{ManageUserUser, User, regular::ClubMember};
|
||||
use crate::{
|
||||
NonEmptyString,
|
||||
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
|
||||
special_user,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{async_trait, fs::TempFile};
|
||||
use super::User;
|
||||
use crate::{model::mail::Mail, special_user};
|
||||
use rocket::async_trait;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
special_user!(FoerderndUser, +"Förderndes Mitglied");
|
||||
|
||||
impl ClubMember for FoerderndUser {}
|
||||
|
||||
impl FoerderndUser {
|
||||
pub(crate) async fn send_welcome_mail_to_user(
|
||||
&self,
|
||||
@@ -42,60 +35,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
smtp_pw,
|
||||
).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"User {self} hat die Info-Mail bzgl. neues förderndes Mitglied (Handbuch und WLAN Infos) an {mail} gesendet bekommen"
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
smtp_pw: &str,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
financial: Option<Role>,
|
||||
birthdate: &NaiveDate,
|
||||
member_since: &NaiveDate,
|
||||
phone: NonEmptyString,
|
||||
address: NonEmptyString,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
|
||||
let user = Self::create_member(
|
||||
db,
|
||||
created_by,
|
||||
&role,
|
||||
name,
|
||||
mail,
|
||||
financial,
|
||||
birthdate,
|
||||
member_since,
|
||||
phone,
|
||||
address,
|
||||
membership_pdf,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.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, es gibt ein neues förderndes Mitglied: {user}"),
|
||||
"Neues unterstützendes Vereinsmitglied",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -36,19 +36,19 @@ impl Member {
|
||||
}
|
||||
|
||||
pub(crate) fn is_club_member(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Member::Regular(_) | Member::Foerdernd(_) | Member::Unterstuetzend(_)
|
||||
)
|
||||
match self {
|
||||
Member::Regular(_) | Member::Foerdernd(_) | Member::Unterstuetzend(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub(crate) fn supposed_to_pay(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
match self {
|
||||
Member::Schnupperant(_)
|
||||
| Member::Scheckbuch(_)
|
||||
| Member::Regular(_)
|
||||
| Member::Foerdernd(_)
|
||||
| Member::Unterstuetzend(_)
|
||||
)
|
||||
| Member::Scheckbuch(_)
|
||||
| Member::Regular(_)
|
||||
| Member::Foerdernd(_)
|
||||
| Member::Unterstuetzend(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ use rocket::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||
|
||||
use super::activity::ActivityBuilder;
|
||||
use super::{
|
||||
log::Log,
|
||||
logbook::Logbook,
|
||||
@@ -113,6 +112,74 @@ impl User {
|
||||
self.has_role_tx(db, "cox").await || self.has_role_tx(db, "Bootsführer").await
|
||||
}
|
||||
|
||||
pub async fn send_welcome_email(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
|
||||
let Some(mail) = &self.mail else {
|
||||
return Err(format!(
|
||||
"Could not send welcome mail, because user {} has no email address",
|
||||
self.name
|
||||
));
|
||||
};
|
||||
|
||||
if self.has_role(db, "schnupperant").await {
|
||||
self.send_welcome_mail_schnupper(db, mail, smtp_pw).await?;
|
||||
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, self).await {
|
||||
scheckbuch.notify(db, smtp_pw).await?;
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Could not send welcome mail, because user {} is not in Donau Linz or scheckbuch or schnupperant group",
|
||||
self.name
|
||||
));
|
||||
}
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("Willkommensemail wurde an {} versandt", self.name),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_welcome_mail_schnupper(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
mail: &str,
|
||||
smtp_pw: &str,
|
||||
) -> Result<(), String> {
|
||||
// 2 things to do:
|
||||
// 1. Send mail to user
|
||||
Mail::send_single(
|
||||
db,
|
||||
mail,
|
||||
"Schnupperrudern beim ASKÖ Ruderverein Donau Linz",
|
||||
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?;
|
||||
|
||||
// 2. Notify all coxes
|
||||
let coxes = Role::find_by_name(db, "schnupper-betreuer").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&coxes,
|
||||
&format!(
|
||||
"Liebe Schnupper-Betreuer, {} nimmt am Schnupperkurs teil.",
|
||||
self.name
|
||||
),
|
||||
"Neue(r) Schnupperteilnehmer:in ",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn amount_boats(&self, db: &SqlitePool) -> i64 {
|
||||
sqlx::query!(
|
||||
"SELECT COUNT(*) as count FROM boat WHERE owner = ?",
|
||||
@@ -192,40 +259,6 @@ impl User {
|
||||
.into_iter().map(|r| r.name).collect()
|
||||
}
|
||||
|
||||
pub async fn financial(&self, db: &SqlitePool) -> Option<Role> {
|
||||
sqlx::query_as!(
|
||||
Role,
|
||||
"
|
||||
SELECT r.id, r.name, r.formatted_name, r.desc, r.hide_in_lists, r.cluster
|
||||
FROM role r
|
||||
JOIN user_role ur ON r.id = ur.role_id
|
||||
WHERE ur.user_id = ?
|
||||
AND r.cluster = 'financial';
|
||||
",
|
||||
self.id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn skill(&self, db: &SqlitePool) -> Option<Role> {
|
||||
sqlx::query_as!(
|
||||
Role,
|
||||
"
|
||||
SELECT r.id, r.name, r.formatted_name, r.desc, r.hide_in_lists, r.cluster
|
||||
FROM role r
|
||||
JOIN user_role ur ON r.id = ur.role_id
|
||||
WHERE ur.user_id = ?
|
||||
AND r.cluster = 'skill';
|
||||
",
|
||||
self.id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn real_roles(&self, db: &SqlitePool) -> Vec<Role> {
|
||||
sqlx::query_as!(
|
||||
Role,
|
||||
@@ -419,6 +452,22 @@ ORDER BY last_access DESC
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn create(db: &SqlitePool, name: &str) -> bool {
|
||||
let name = name.trim();
|
||||
sqlx::query!("INSERT INTO USER(name) VALUES (?)", name)
|
||||
.execute(db)
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub async fn create_with_mail(db: &SqlitePool, name: &str, mail: &str) -> bool {
|
||||
let name = name.trim();
|
||||
sqlx::query!("INSERT INTO USER(name, mail) VALUES (?, ?)", name, mail)
|
||||
.execute(db)
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub async fn update_ergo(&self, db: &SqlitePool, dob: i32, weight: i64, sex: &str) {
|
||||
sqlx::query!(
|
||||
"UPDATE user SET dob = ?, weight = ?, sex = ? where id = ?",
|
||||
@@ -458,7 +507,29 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
smtp_pw,
|
||||
).await?;
|
||||
|
||||
ActivityBuilder::new(&format!("User {self} hat eine Mail bekommen, dass seine 5 Ausfahrten mit der heutigen Ausfahrt aufgebraucht sind, und dass der nächste Schritt eine Vereinsmitgliedschaft wäre (inkl. Links zu Beitrittserklärung + Info, dass sie an info@ geschickt werden soll.")).relevant_for_user(self).save_tx(db).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_role_tx(
|
||||
&self,
|
||||
db: &mut Transaction<'_, Sqlite>,
|
||||
role: &Role,
|
||||
) -> Result<(), String> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO user_role(user_id, role_id) VALUES (?, ?)",
|
||||
self.id,
|
||||
role.id
|
||||
)
|
||||
.execute(db.deref_mut())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
format!(
|
||||
"User already has a role in the cluster '{}'",
|
||||
role.cluster
|
||||
.clone()
|
||||
.expect("db trigger can't activate on empty string")
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -505,11 +576,10 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
};
|
||||
|
||||
if user.deleted {
|
||||
ActivityBuilder::new(&format!(
|
||||
"User {user} wollte sich einloggen, klappte jedoch nicht weil er gelöscht wurde."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
Log::create(
|
||||
db,
|
||||
format!("User ({name}) already deleted (tried to login)."),
|
||||
)
|
||||
.await;
|
||||
return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has
|
||||
//been deleted
|
||||
@@ -520,12 +590,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
if password_hash == user_pw {
|
||||
return Ok(user);
|
||||
}
|
||||
ActivityBuilder::new(&format!(
|
||||
"User {user} wollte sich einloggen, hat jedoch das falsche Passwort angegeben."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
Log::create(db, format!("User {name} supplied the wrong PW")).await;
|
||||
Err(LoginError::InvalidAuthenticationCombo)
|
||||
} else {
|
||||
info!("User {name} has no PW set");
|
||||
@@ -538,12 +603,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
// TODO: add responsible person
|
||||
ActivityBuilder::new(&format!("Passwort von User {self} wurde zurückgesetzt."))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_pw(&self, db: &SqlitePool, pw: &str) {
|
||||
@@ -552,12 +611,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
ActivityBuilder::new(&format!(
|
||||
"Passwort von User {self} wurde erfolgreich geändert."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
|
||||
fn get_hashed_pw(pw: &str) -> String {
|
||||
@@ -577,21 +630,13 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
ActivityBuilder::new(&format!("User {self} hat sich eingeloggt."))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn delete(&self, db: &SqlitePool, deleted_by: &ManageUserUser) {
|
||||
pub async fn delete(&self, db: &SqlitePool) {
|
||||
sqlx::query!("UPDATE user SET deleted=1 WHERE id=?", self.id)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
ActivityBuilder::new(&format!("User {self} wurde von {deleted_by} gelöscht."))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn get_days(&self, db: &SqlitePool) -> Vec<Day> {
|
||||
@@ -683,10 +728,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
None,None
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("5 Scheckbuchausfahrten von {self} wurden mit der heutigen Ausfahrt aufgebraucht. Info-Mail wurde an {self} geschickt + alle Steuerberechtigten informiert, dass wir pot. ein neues Mitglied haben"))
|
||||
.relevant_for_user(self)
|
||||
.save_tx(db)
|
||||
.await;
|
||||
}
|
||||
a if a > 5 => {
|
||||
let board = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
|
||||
@@ -701,10 +742,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
None,None
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("{self} hat nun bereits die {amount_trips}. seiner 5 Scheckbuchausfahrten absolviert. Vorstand wurde via Notification informiert."))
|
||||
.relevant_for_user(self)
|
||||
.save_tx(db)
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -724,12 +761,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
None,None
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!(
|
||||
"{self} hat das heurige Fahrtenabzeichen geschafft! Der Vorstand + {self} wurde via Notification informiert."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save_tx(db)
|
||||
.await;
|
||||
|
||||
Notification::create_with_tx(db, self, "Mit deiner letzten Ausfahrt hast du nun alle Anforderungen für das heurige Fahrtenzeichen erfüllt. Gratuliere! 🎉", "Fahrtenabzeichen geschafft", None, None).await;
|
||||
}
|
||||
@@ -748,10 +779,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
None,None
|
||||
)
|
||||
.await;
|
||||
ActivityBuilder::new(&format!("{self} hat den Äquatorpreis in {level} geschafft! Der Vorstand + {self} wurde via Notification informiert."))
|
||||
.relevant_for_user(self)
|
||||
.save_tx(db)
|
||||
.await;
|
||||
|
||||
Notification::create_with_tx(db, self, &format!("Mit deiner letzten Ausfahrt erfüllst du nun alle Anforderungen für den Äquatorpreis in {level}. Gratuliere! 🎉"), "Äquatorpreis", None, None).await;
|
||||
}
|
||||
@@ -812,7 +839,7 @@ macro_rules! special_user {
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> rocket::request::FromRequest<'r> for $name {
|
||||
type Error = $crate::model::user::LoginError;
|
||||
type Error = crate::model::user::LoginError;
|
||||
async fn from_request(req: &'r rocket::request::Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||
match User::from_request(req).await {
|
||||
@@ -971,6 +998,20 @@ mod test {
|
||||
assert_eq!(res.len(), 4);
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_succ_create() {
|
||||
let pool = testdb!();
|
||||
|
||||
assert_eq!(User::create(&pool, "new-user-name".into()).await, true);
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_duplicate_name_create() {
|
||||
let pool = testdb!();
|
||||
|
||||
assert_eq!(User::create(&pool, "admin".into()).await, false);
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn succ_login_with_test_db() {
|
||||
let pool = testdb!();
|
||||
|
@@ -1,67 +1,10 @@
|
||||
use super::{ManageUserUser, User};
|
||||
use crate::{
|
||||
NonEmptyString,
|
||||
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
|
||||
special_user,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt};
|
||||
use super::User;
|
||||
use crate::{model::mail::Mail, special_user};
|
||||
use rocket::async_trait;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
special_user!(RegularUser, +"Donau Linz");
|
||||
|
||||
pub trait ClubMember {
|
||||
async fn create_member(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
role: &Role,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
financial: Option<Role>,
|
||||
birthdate: &NaiveDate,
|
||||
member_since: &NaiveDate,
|
||||
phone: NonEmptyString,
|
||||
address: NonEmptyString,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<User, String> {
|
||||
if membership_pdf.len() == 0 {
|
||||
return Err("Keine Beitrittserklärung mitgeschickt.".to_string());
|
||||
}
|
||||
|
||||
let mut stream = membership_pdf.open().await.unwrap();
|
||||
let mut buffer = Vec::new();
|
||||
stream.read_to_end(&mut buffer).await.unwrap();
|
||||
|
||||
let name = name.as_str();
|
||||
let phone = phone.as_str();
|
||||
let address = address.as_str();
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO user(name, member_since_date, birthdate, mail, phone, address, membership_pdf)
|
||||
VALUES (?,?,?,?,?,?,?)",
|
||||
name, member_since, birthdate, mail, phone, address,buffer
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let user = User::find_by_name(db, name).await.unwrap();
|
||||
user.change_financial(db, created_by, financial).await?;
|
||||
user.add_role(db, created_by, role).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{created_by} hat Mitglied {user} mit der Rolle {role} angelegt."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClubMember for RegularUser {}
|
||||
|
||||
impl RegularUser {
|
||||
pub(crate) async fn send_welcome_mail_to_user(
|
||||
&self,
|
||||
@@ -100,55 +43,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
smtp_pw,
|
||||
).await?;
|
||||
|
||||
ActivityBuilder::new(&format!("Willkommensmail für {self} wurde an {mail} verschickt (Handbuch, Signal-Gruppe, App-Info, Fingerprint, WLAN)."))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
smtp_pw: &str,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
financial: Option<Role>,
|
||||
birthdate: &NaiveDate,
|
||||
member_since: &NaiveDate,
|
||||
phone: NonEmptyString,
|
||||
address: NonEmptyString,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||
let user = Self::create_member(
|
||||
db,
|
||||
created_by,
|
||||
&role,
|
||||
name,
|
||||
mail,
|
||||
financial,
|
||||
birthdate,
|
||||
member_since,
|
||||
phone,
|
||||
address,
|
||||
membership_pdf,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||
|
||||
Notification::create_for_steering_people(
|
||||
db,
|
||||
&format!("Liebe Steuerberechtigte, es gibt ein neues Mitglied: {user} 🎉"),
|
||||
"Neues Vereinsmitglied",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -2,13 +2,11 @@ use super::foerdernd::FoerderndUser;
|
||||
use super::regular::RegularUser;
|
||||
use super::unterstuetzend::UnterstuetzendUser;
|
||||
use super::{ManageUserUser, User};
|
||||
use crate::NonEmptyString;
|
||||
use crate::model::activity::ActivityBuilder;
|
||||
use crate::model::role::Role;
|
||||
use crate::NonEmptyString;
|
||||
use crate::{
|
||||
SCHECKBUCH,
|
||||
model::{mail::Mail, notification::Notification},
|
||||
special_user,
|
||||
special_user, SCHECKBUCH,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::async_trait;
|
||||
@@ -85,13 +83,6 @@ impl ScheckbuchUser {
|
||||
)
|
||||
.await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat den Scheckbuch-User {self} auf ein reguläres Mitglied upgegraded! Die Steuerpersonen wurden via Notification informiert."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -143,10 +134,6 @@ impl ScheckbuchUser {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActivityBuilder::new(&format!("{changed_by} hat den Scheckbuch-User {self} auf ein unterstützendes Mitglied upgegraded!"))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -197,12 +184,6 @@ impl ScheckbuchUser {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat den Scheckbuch-User {self} auf ein förderndes Mitglied upgegraded!"
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -212,13 +193,6 @@ impl ScheckbuchUser {
|
||||
self.notify_coxes_about_new_scheckbuch(db).await;
|
||||
self.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{self} hat eine Info-Mail bekommen (Erklärung Scheckbuch, Ruderapp) und alle Steuerberechtigten wurden informiert."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -234,7 +208,7 @@ impl ScheckbuchUser {
|
||||
};
|
||||
Mail::send_single(
|
||||
db,
|
||||
mail,
|
||||
&mail,
|
||||
"ASKÖ Ruderverein Donau Linz | Dein Scheckbuch wartet auf Dich",
|
||||
format!(
|
||||
"Hallo {0},
|
||||
@@ -267,38 +241,4 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
smtp_pw: &str,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||
|
||||
let name = name.as_str();
|
||||
sqlx::query!(
|
||||
"INSERT INTO user(name, mail)
|
||||
VALUES (?,?)",
|
||||
name,
|
||||
mail
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let user = User::find_by_name(db, name).await.unwrap();
|
||||
user.add_role(db, created_by, &role).await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.notify(db, smtp_pw).await?;
|
||||
|
||||
ActivityBuilder::new(&format!("{created_by} hat Scheckbuch {user} angelegt."))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,10 @@
|
||||
use super::foerdernd::FoerderndUser;
|
||||
use super::regular::RegularUser;
|
||||
use super::scheckbuch::ScheckbuchUser;
|
||||
use super::schnupperinterest::SchnupperInterestUser;
|
||||
use super::unterstuetzend::UnterstuetzendUser;
|
||||
use super::{ManageUserUser, User};
|
||||
use crate::NonEmptyString;
|
||||
use crate::model::activity::ActivityBuilder;
|
||||
use crate::model::role::Role;
|
||||
use crate::NonEmptyString;
|
||||
use crate::{
|
||||
model::{mail::Mail, notification::Notification},
|
||||
special_user,
|
||||
@@ -86,13 +84,6 @@ impl SchnupperantUser {
|
||||
)
|
||||
.await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat den Schnupperant {self} auf ein reguläres Mitglied upgegraded!"
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -108,13 +99,13 @@ impl SchnupperantUser {
|
||||
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)
|
||||
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.notify(db, smtp_pw).await?;
|
||||
scheckbook.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||
|
||||
Notification::create_for_steering_people(
|
||||
db,
|
||||
@@ -127,13 +118,6 @@ impl SchnupperantUser {
|
||||
)
|
||||
.await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat dem ehemaligen Schnupperant {self} nun ein Scheckbuch gegeben"
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -151,9 +135,6 @@ impl SchnupperantUser {
|
||||
.add_role(db, changed_by, &schnupperinterest)
|
||||
.await?;
|
||||
|
||||
let schnupperinterest = SchnupperInterestUser::new(db, &self.user).await.unwrap();
|
||||
schnupperinterest.notify(db).await?;
|
||||
|
||||
if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await {
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
@@ -169,13 +150,6 @@ impl SchnupperantUser {
|
||||
.await;
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat dem eigentlichen Schnupperanten {self} wieder auf die 'Interessierten'-Liste zurückgegeben."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -208,7 +182,7 @@ impl SchnupperantUser {
|
||||
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)
|
||||
self.add_role(db, &changed_by, &no_einschreibgebuehr)
|
||||
.await
|
||||
.expect("role doesn't have a group");
|
||||
}
|
||||
@@ -233,13 +207,6 @@ impl SchnupperantUser {
|
||||
.await;
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat den Schnupperant {self} auf ein unterstützendes Mitglied upgegraded!"
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -272,7 +239,7 @@ impl SchnupperantUser {
|
||||
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)
|
||||
self.add_role(db, &changed_by, &no_einschreibgebuehr)
|
||||
.await
|
||||
.expect("role doesn't have a group");
|
||||
}
|
||||
@@ -295,13 +262,6 @@ impl SchnupperantUser {
|
||||
.await;
|
||||
}
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{changed_by} hat den Schnupperant {self} auf ein förderndes Mitglied upgegraded!"
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -310,13 +270,6 @@ impl SchnupperantUser {
|
||||
self.notify_coxes_about_new_scheckbuch(db).await;
|
||||
self.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{self} hat eine Mail bekommen (Inhalt: wir freuen uns auf ihn + senden detailliertere Infos später zu) und die Schnupperbetreuer wurden via Notification informiert."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -332,7 +285,7 @@ impl SchnupperantUser {
|
||||
};
|
||||
Mail::send_single(
|
||||
db,
|
||||
mail,
|
||||
&mail,
|
||||
"ASKÖ Ruderverein Donau Linz | Anmeldung Schnupperkurs",
|
||||
format!(
|
||||
"Hallo {0},
|
||||
@@ -363,40 +316,4 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
smtp_pw: &str,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||
|
||||
let name = name.as_str();
|
||||
sqlx::query!(
|
||||
"INSERT INTO user(name, mail)
|
||||
VALUES (?,?)",
|
||||
name,
|
||||
mail
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let user = User::find_by_name(db, name).await.unwrap();
|
||||
user.add_role(db, created_by, &role).await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.notify(db, smtp_pw).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{created_by} hat {user} zur fixen Schnupperkurs-Anmeldung hinzugefügt."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,6 @@
|
||||
use super::scheckbuch::ScheckbuchUser;
|
||||
use super::schnupperant::SchnupperantUser;
|
||||
use super::{ManageUserUser, User};
|
||||
use crate::NonEmptyString;
|
||||
use crate::model::activity::ActivityBuilder;
|
||||
use crate::model::role::Role;
|
||||
use crate::{model::notification::Notification, special_user};
|
||||
use rocket::async_trait;
|
||||
@@ -27,7 +25,7 @@ impl SchnupperInterestUser {
|
||||
self.user.add_role(db, changed_by, &scheckbook).await?;
|
||||
|
||||
let scheckbook = ScheckbuchUser::new(db, &self.user).await.unwrap();
|
||||
scheckbook.notify(db, smtp_pw).await?;
|
||||
scheckbook.send_welcome_mail_to_user(db, smtp_pw).await?;
|
||||
|
||||
Notification::create_for_steering_people(
|
||||
db,
|
||||
@@ -41,13 +39,6 @@ impl SchnupperInterestUser {
|
||||
)
|
||||
.await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"Der Schnupperinteressierte {self} hat sich (ohne Schnupperkurs) doch gleich direkt für ein Scheckbuch entschieden. {changed_by} hat dieses eingerichtet."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -83,12 +74,6 @@ impl SchnupperInterestUser {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ActivityBuilder::new(&format!(
|
||||
"Der Schnupperinteressierte {self} hat sich zum Schnupperkurs angemeldet."
|
||||
))
|
||||
.relevant_for_user(&self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -96,13 +81,6 @@ impl SchnupperInterestUser {
|
||||
pub(crate) async fn notify(&self, db: &SqlitePool) -> Result<(), String> {
|
||||
self.notify_schnupperbetreuer_about_new_interest(db).await;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"Der Schnupperbetreuer hat eine Info via Notification bekommen, dass {self} Interesse an einen Schnupperkurs hat."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -122,41 +100,4 @@ impl SchnupperInterestUser {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "schnupper-interessierte")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let name = name.as_str();
|
||||
sqlx::query!(
|
||||
"INSERT INTO user(name, mail)
|
||||
VALUES (?,?)",
|
||||
name,
|
||||
mail
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let user = User::find_by_name(db, name).await.unwrap();
|
||||
user.add_role(db, created_by, &role).await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.notify(db).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{created_by} hat Schnupper-Interessierten {user} angelegt."
|
||||
))
|
||||
.relevant_for_user(&user)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,10 @@
|
||||
use super::{ManageUserUser, User, regular::ClubMember};
|
||||
use crate::{
|
||||
NonEmptyString,
|
||||
model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
|
||||
special_user,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{async_trait, fs::TempFile};
|
||||
use super::User;
|
||||
use crate::{model::mail::Mail, special_user};
|
||||
use rocket::async_trait;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
special_user!(UnterstuetzendUser, +"Unterstützend");
|
||||
|
||||
impl ClubMember for UnterstuetzendUser {}
|
||||
|
||||
impl UnterstuetzendUser {
|
||||
pub(crate) async fn send_welcome_mail_to_user(
|
||||
&self,
|
||||
@@ -42,60 +35,6 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
smtp_pw,
|
||||
).await?;
|
||||
|
||||
ActivityBuilder::new(&format!(
|
||||
"{self} hat eine Mail an {mail} bekommen, mit Infos dass er/sie nun ein unterstützendes Mitglied ist (Handbuch, WLAN)."
|
||||
))
|
||||
.relevant_for_user(self)
|
||||
.save(db)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
db: &SqlitePool,
|
||||
created_by: &ManageUserUser,
|
||||
smtp_pw: &str,
|
||||
name: NonEmptyString,
|
||||
mail: &str,
|
||||
financial: Option<Role>,
|
||||
birthdate: &NaiveDate,
|
||||
member_since: &NaiveDate,
|
||||
phone: NonEmptyString,
|
||||
address: NonEmptyString,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<(), String> {
|
||||
let role = Role::find_by_name(db, "Unterstützend").await.unwrap();
|
||||
let user = Self::create_member(
|
||||
db,
|
||||
created_by,
|
||||
&role,
|
||||
name,
|
||||
mail,
|
||||
financial,
|
||||
birthdate,
|
||||
member_since,
|
||||
phone,
|
||||
address,
|
||||
membership_pdf,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let user = Self::new(db, &user).await.unwrap();
|
||||
user.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, es gibt ein neues unterstützendes Mitglied: {user}"),
|
||||
"Neues unterstützendes Vereinsmitglied",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use rocket::{Build, FromForm, Rocket, State, form::Form, post, routes};
|
||||
use rocket::{form::Form, post, routes, Build, FromForm, Rocket, State};
|
||||
use serde_json::json;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
|
@@ -96,9 +96,7 @@ struct DailyWeather {
|
||||
}
|
||||
|
||||
fn fetch(api_key: &str) -> Result<Data, String> {
|
||||
let url = format!(
|
||||
"https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}"
|
||||
);
|
||||
let url = format!("https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}");
|
||||
|
||||
match ureq::get(&url).call() {
|
||||
Ok(mut response) => {
|
||||
|
@@ -5,14 +5,13 @@ use crate::model::{
|
||||
user::{User, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{
|
||||
Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/boat")]
|
||||
@@ -246,11 +245,9 @@ mod test {
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
assert!(
|
||||
Boat::find_by_name(&db, "completely-new-boat".into())
|
||||
.await
|
||||
.is_none()
|
||||
);
|
||||
assert!(Boat::find_by_name(&db, "completely-new-boat".into())
|
||||
.await
|
||||
.is_none());
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
|
@@ -1,9 +1,8 @@
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post, put,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use sqlx::SqlitePool;
|
||||
@@ -33,8 +32,8 @@ async fn create(
|
||||
|
||||
let trip_details_id = TripDetails::create(db, data.tripdetails).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc. we
|
||||
//just created
|
||||
//the object
|
||||
//just created
|
||||
//the object
|
||||
|
||||
Event::create(
|
||||
db,
|
||||
|
@@ -1,9 +1,9 @@
|
||||
use rocket::form::Form;
|
||||
use rocket::fs::TempFile;
|
||||
use rocket::response::{Flash, Redirect};
|
||||
use rocket::{FromForm, post};
|
||||
use rocket::{Route, State, get, request::FlashMessage, routes};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket::{post, FromForm};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::log::Log;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use csv::ReaderBuilder;
|
||||
use rocket::{FromForm, Route, State, form::Form, get, post, routes};
|
||||
use rocket_dyn_templates::{Template, context};
|
||||
use rocket::{form::Form, get, post, routes, FromForm, Route, State};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::{
|
||||
@@ -12,7 +12,6 @@ pub mod boat;
|
||||
pub mod event;
|
||||
pub mod mail;
|
||||
pub mod notification;
|
||||
pub mod role;
|
||||
pub mod schnupper;
|
||||
pub mod user;
|
||||
|
||||
@@ -82,7 +81,6 @@ pub fn routes() -> Vec<Route> {
|
||||
ret.append(&mut notification::routes());
|
||||
ret.append(&mut mail::routes());
|
||||
ret.append(&mut event::routes());
|
||||
ret.append(&mut role::routes());
|
||||
ret.append(&mut routes![rss, show_rss, show_list, list]);
|
||||
ret
|
||||
}
|
||||
|
@@ -6,14 +6,13 @@ use crate::model::{
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/notification")]
|
||||
|
@@ -1,64 +0,0 @@
|
||||
use crate::model::{
|
||||
role::Role,
|
||||
user::{AdminUser, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/role")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let roles = Role::all(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("roles", &roles);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(admin.user, db).await,
|
||||
);
|
||||
|
||||
Template::render("admin/role", context.into_json())
|
||||
}
|
||||
#[derive(FromForm)]
|
||||
pub struct RoleToUpdate<'r> {
|
||||
pub formatted_name: &'r str,
|
||||
pub desc: &'r str,
|
||||
}
|
||||
|
||||
#[post("/role/<role_id>", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<RoleToUpdate<'_>>,
|
||||
role_id: i32,
|
||||
admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
let role = Role::find_by_id(db, role_id).await;
|
||||
let Some(role) = role else {
|
||||
return Flash::error(Redirect::to("/admin/role"), "Role does not exist!");
|
||||
};
|
||||
|
||||
match role
|
||||
.update(db, &admin, &data.formatted_name, &data.desc)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Flash::success(Redirect::to("/admin/role"), "Rolle bearbeitet"),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/role"), e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, update]
|
||||
}
|
@@ -3,8 +3,8 @@ use crate::model::{
|
||||
user::{SchnupperBetreuerUser, User, UserWithDetails},
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use rocket::{Route, State, get, request::FlashMessage, routes};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/schnupper")]
|
||||
|
@@ -4,13 +4,11 @@ use crate::{
|
||||
family::Family,
|
||||
log::Log,
|
||||
logbook::Logbook,
|
||||
mail::valid_mails,
|
||||
role::Role,
|
||||
user::{
|
||||
clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member,
|
||||
regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser,
|
||||
schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser,
|
||||
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
|
||||
clubmember::ClubMemberUser, member::Member, scheckbuch::ScheckbuchUser,
|
||||
schnupperant::SchnupperantUser, schnupperinterest::SchnupperInterestUser, AdminUser,
|
||||
AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
|
||||
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||
},
|
||||
},
|
||||
@@ -66,7 +64,6 @@ async fn index(
|
||||
let allowed_to_edit = ManageUserUser::new(db, &user).await.is_some();
|
||||
|
||||
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
||||
let financial = Role::all_cluster(db, "financial").await;
|
||||
|
||||
let roles = Role::all(db).await;
|
||||
let families = Family::all_with_members(db).await;
|
||||
@@ -78,7 +75,6 @@ async fn index(
|
||||
context.insert("allowed_to_edit", &allowed_to_edit);
|
||||
context.insert("users", &users);
|
||||
context.insert("roles", &roles);
|
||||
context.insert("financial", &financial);
|
||||
context.insert("families", &families);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
|
||||
@@ -99,7 +95,6 @@ async fn index_admin(
|
||||
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
||||
|
||||
let user: User = user.user;
|
||||
let financial = Role::all_cluster(db, "financial").await;
|
||||
let allowed_to_edit = ManageUserUser::new(db, &user).await.is_some();
|
||||
|
||||
let roles = Role::all(db).await;
|
||||
@@ -112,7 +107,6 @@ async fn index_admin(
|
||||
context.insert("allowed_to_edit", &allowed_to_edit);
|
||||
context.insert("users", &users);
|
||||
context.insert("roles", &roles);
|
||||
context.insert("financial", &financial);
|
||||
context.insert("families", &families);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
|
||||
@@ -132,20 +126,10 @@ async fn view(
|
||||
format!("User mit ID {} gibts ned", user),
|
||||
));
|
||||
};
|
||||
if user.name == "Externe Steuerperson" {
|
||||
return Err(Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Diese besondere Person kannst du dir leider nicht anschauen, mein lieber neugieriger Ruderant!"
|
||||
));
|
||||
}
|
||||
|
||||
let member = Member::from(db, user.clone()).await;
|
||||
let fee = user.fee(db).await;
|
||||
let activities = Activity::for_user(db, &user).await;
|
||||
let financial = Role::all_cluster(db, "financial").await;
|
||||
let user_financial = user.financial(db).await;
|
||||
let skill = Role::all_cluster(db, "skill").await;
|
||||
let user_skill = user.skill(db).await;
|
||||
|
||||
let user = UserWithRolesAndMembershipPdf::from_user(db, user).await;
|
||||
|
||||
@@ -164,10 +148,6 @@ async fn view(
|
||||
context.insert("is_clubmember", &member.is_club_member());
|
||||
context.insert("supposed_to_pay", &member.supposed_to_pay());
|
||||
context.insert("fee", &fee);
|
||||
context.insert("skill", &skill);
|
||||
context.insert("user_skill", &user_skill);
|
||||
context.insert("financial", &financial);
|
||||
context.insert("user_financial", &user_financial);
|
||||
context.insert("member", &member);
|
||||
context.insert("activities", &activities);
|
||||
context.insert("roles", &roles);
|
||||
@@ -266,6 +246,26 @@ async fn fees_paid(
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/user/<user>/send-welcome-mail")]
|
||||
async fn send_welcome_mail(
|
||||
db: &State<SqlitePool>,
|
||||
_admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
user: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, user).await else {
|
||||
return Flash::error(Redirect::to("/admin/user"), "User does not exist");
|
||||
};
|
||||
|
||||
match user.send_welcome_email(db, &config.smtp_pw).await {
|
||||
Ok(()) => Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("Willkommens-Email wurde an {} versandt.", user.name),
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/user/<user>/reset-pw")]
|
||||
async fn resetpw(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Flash<Redirect> {
|
||||
let user = User::find_by_id(db, user).await;
|
||||
@@ -292,7 +292,7 @@ async fn delete(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Fla
|
||||
Log::create(db, format!("{} deleted user: {user:?}", admin.user.name)).await;
|
||||
match user {
|
||||
Some(user) => {
|
||||
user.delete(db, &admin).await;
|
||||
user.delete(db).await;
|
||||
Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("Benutzer {} gelöscht", user.name),
|
||||
@@ -456,86 +456,6 @@ async fn update_family(
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct ChangeSkillForm {
|
||||
skill_id: String,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-skill", data = "<data>")]
|
||||
async fn change_skill(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<ChangeSkillForm>,
|
||||
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 skill = if data.skill_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let Ok(skill_id) = data.skill_id.parse() else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
"Skill_id is not a number",
|
||||
);
|
||||
};
|
||||
Role::find_by_id(db, skill_id).await
|
||||
};
|
||||
|
||||
match user.change_skill(db, &admin, skill).await {
|
||||
Ok(()) => Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Skill erfolgreich geändert",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct ChangeFinancialForm {
|
||||
financial_id: String,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-financial", data = "<data>")]
|
||||
async fn change_financial(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<ChangeFinancialForm>,
|
||||
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 financial = if data.financial_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let Ok(financial_id) = data.financial_id.parse() else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
"Finacial_id is not a number",
|
||||
);
|
||||
};
|
||||
Role::find_by_id(db, financial_id).await
|
||||
};
|
||||
|
||||
match user.change_financial(db, &admin, financial).await {
|
||||
Ok(()) => Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Ermäßigung erfolgreich geändert",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddMembershipPDFForm<'a> {
|
||||
membership_pdf: TempFile<'a>,
|
||||
@@ -752,6 +672,32 @@ async fn download_membership_pdf(
|
||||
(ContentType::PDF, user.membership_pdf.unwrap())
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct UserAddForm<'r> {
|
||||
name: &'r str,
|
||||
}
|
||||
|
||||
#[post("/user/new", data = "<data>")]
|
||||
async fn create(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<UserAddForm<'_>>,
|
||||
admin: ManageUserUser,
|
||||
) -> Flash<Redirect> {
|
||||
if User::create(db, data.name).await {
|
||||
Log::create(
|
||||
db,
|
||||
format!("{} created new user: {data:?}", admin.user.name),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/admin/user"), "Successfully created user")
|
||||
} else {
|
||||
Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User {} already exists", data.name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//#[derive(FromForm, Debug)]
|
||||
//struct UserAddScheckbuchForm<'r> {
|
||||
// name: &'r str,
|
||||
@@ -911,7 +857,7 @@ async fn schnupperant_to_regular(
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
"Membertype gibts ned",
|
||||
);
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1030,7 +976,7 @@ async fn scheckbook_to_regular(
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
"Membertype gibts ned",
|
||||
);
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1062,7 +1008,7 @@ async fn change_membertype(
|
||||
);
|
||||
};
|
||||
|
||||
let Some(user) = ClubMemberUser::new(db, &user).await else {
|
||||
let Some(user) = ClubMemberUser::new(&db, &user).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User {user} ist kein Vereinsmitglied"),
|
||||
@@ -1075,9 +1021,9 @@ async fn change_membertype(
|
||||
"foerdernd" => user.move_to_foerdernd(db, &admin).await,
|
||||
_ => {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
"Membertype gibt's ned",
|
||||
);
|
||||
Redirect::to(format!("/admin/user/{{ id }}")),
|
||||
format!("Membertype gibt's ned"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1104,7 +1050,7 @@ async fn schnupperant_to_scheckbook(
|
||||
);
|
||||
};
|
||||
|
||||
let Some(user) = SchnupperantUser::new(db, &user).await else {
|
||||
let Some(user) = SchnupperantUser::new(&db, &user).await else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
format!("User {user} ist kein Schnupperant"),
|
||||
@@ -1134,7 +1080,7 @@ async fn schnupperinterest_to_schnupperant(
|
||||
);
|
||||
};
|
||||
|
||||
let Some(user) = SchnupperInterestUser::new(db, &user).await else {
|
||||
let Some(user) = SchnupperInterestUser::new(&db, &user).await else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
format!("User {user} ist kein Schnupperinteressierter"),
|
||||
@@ -1163,7 +1109,7 @@ async fn schnupperant_to_schnupperinterest(
|
||||
);
|
||||
};
|
||||
|
||||
let Some(user) = SchnupperantUser::new(db, &user).await else {
|
||||
let Some(user) = SchnupperantUser::new(&db, &user).await else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
format!("User {user} ist kein Schnupperant"),
|
||||
@@ -1192,7 +1138,7 @@ async fn schnupperinterest_to_scheckbuch(
|
||||
);
|
||||
};
|
||||
|
||||
let Some(user) = SchnupperInterestUser::new(db, &user).await else {
|
||||
let Some(user) = SchnupperInterestUser::new(&db, &user).await else {
|
||||
return Flash::error(
|
||||
Redirect::to(format!("/admin/user/{id}")),
|
||||
format!("User {user} ist kein Schnupperinteressierter"),
|
||||
@@ -1208,236 +1154,21 @@ async fn schnupperinterest_to_scheckbuch(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddClubMemberForm<'a> {
|
||||
name: String,
|
||||
mail: String,
|
||||
financial_id: String,
|
||||
membertype: String,
|
||||
member_since: String,
|
||||
birthdate: String,
|
||||
phone: String,
|
||||
address: String,
|
||||
membership_pdf: TempFile<'a>,
|
||||
}
|
||||
|
||||
#[post("/user/new/clubmember", data = "<data>")]
|
||||
async fn add_club_member(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<AddClubMemberForm<'_>>,
|
||||
admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
) -> Flash<Redirect> {
|
||||
if !valid_mails(&data.mail) {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!(
|
||||
"{} ist kein gültiges Format für eine Mailadresse",
|
||||
&data.mail
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let financial = if data.financial_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let Ok(financial_id) = data.financial_id.parse() else {
|
||||
return Flash::error(Redirect::to("/admin/user"), "Finacial_id is not a number");
|
||||
};
|
||||
Role::find_by_id(db, financial_id).await
|
||||
};
|
||||
|
||||
let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/"),
|
||||
format!(
|
||||
"Geburtsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||
&data.birthdate
|
||||
),
|
||||
);
|
||||
};
|
||||
let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!(
|
||||
"Beitrittsdatum {} ist nicht im YYYY-MM-DD Format",
|
||||
&data.birthdate
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
let Ok(phone) = data.phone.clone().try_into() else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Vereinsmitglied braucht eine Telefonnummer",
|
||||
);
|
||||
};
|
||||
let Ok(address) = data.address.clone().try_into() else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Vereinsmitglied braucht eine Adresse",
|
||||
);
|
||||
};
|
||||
let Ok(name) = data.name.clone().try_into() else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Vereinsmitglied braucht einen Namen",
|
||||
);
|
||||
};
|
||||
let response = match &*data.membertype {
|
||||
"regular" => {
|
||||
RegularUser::create(
|
||||
db,
|
||||
&admin,
|
||||
&config.smtp_pw,
|
||||
name,
|
||||
&data.mail,
|
||||
financial,
|
||||
&birthdate,
|
||||
&member_since,
|
||||
phone,
|
||||
address,
|
||||
&data.membership_pdf,
|
||||
)
|
||||
.await
|
||||
}
|
||||
"unterstuetzend" => {
|
||||
UnterstuetzendUser::create(
|
||||
db,
|
||||
&admin,
|
||||
&config.smtp_pw,
|
||||
name,
|
||||
&data.mail,
|
||||
financial,
|
||||
&birthdate,
|
||||
&member_since,
|
||||
phone,
|
||||
address,
|
||||
&data.membership_pdf,
|
||||
)
|
||||
.await
|
||||
}
|
||||
"foerdernd" => {
|
||||
FoerderndUser::create(
|
||||
db,
|
||||
&admin,
|
||||
&config.smtp_pw,
|
||||
name,
|
||||
&data.mail,
|
||||
financial,
|
||||
&birthdate,
|
||||
&member_since,
|
||||
phone,
|
||||
address,
|
||||
&data.membership_pdf,
|
||||
)
|
||||
.await
|
||||
}
|
||||
_ => return Flash::error(Redirect::to("/admin/user"), "Membertype gibts ned"),
|
||||
};
|
||||
|
||||
match response {
|
||||
Ok(_) => Flash::success(Redirect::to("/admin/user"), "Mitglied erfolgreich erstellt"),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddScheckbuchForm {
|
||||
name: String,
|
||||
mail: String,
|
||||
}
|
||||
|
||||
#[post("/user/new/scheckbuch", data = "<data>")]
|
||||
async fn add_scheckbuch(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<AddScheckbuchForm>,
|
||||
admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
) -> Flash<Redirect> {
|
||||
if !valid_mails(&data.mail) {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!(
|
||||
"{} ist kein gültiges Format für eine Mailadresse",
|
||||
&data.mail
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let Ok(name) = data.name.clone().try_into() else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Scheckbuch braucht einen Namen",
|
||||
);
|
||||
};
|
||||
match ScheckbuchUser::create(db, &admin, &config.smtp_pw, name, &data.mail).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
"Scheckbuch erfolgreich erstellt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddSchnupperForm {
|
||||
name: String,
|
||||
mail: String,
|
||||
schnupper_type: String,
|
||||
}
|
||||
|
||||
#[post("/user/new/schnupper", data = "<data>")]
|
||||
async fn add_schnupper(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<AddSchnupperForm>,
|
||||
admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
) -> Flash<Redirect> {
|
||||
if !valid_mails(&data.mail) {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!(
|
||||
"{} ist kein gültiges Format für eine Mailadresse",
|
||||
&data.mail
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let Ok(name) = data.name.clone().try_into() else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
"Schnupperer braucht einen Namen",
|
||||
);
|
||||
};
|
||||
let response = match &*data.schnupper_type {
|
||||
"schnupperInterested" => SchnupperInterestUser::create(db, &admin, name, &data.mail).await,
|
||||
"schnupperant" => {
|
||||
SchnupperantUser::create(db, &admin, &config.smtp_pw, name, &data.mail).await
|
||||
}
|
||||
_ => return Flash::error(Redirect::to("/admin/user"), "Schnuppertyp gibts ned"),
|
||||
};
|
||||
match response {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
"Schnupperer erfolgreich erstellt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
index,
|
||||
index_admin,
|
||||
view,
|
||||
resetpw,
|
||||
create,
|
||||
//create_scheckbuch,
|
||||
delete,
|
||||
fees,
|
||||
fees_paid,
|
||||
scheckbuch,
|
||||
download_membership_pdf,
|
||||
// Updates
|
||||
send_welcome_mail,
|
||||
//
|
||||
update_mail,
|
||||
update_phone,
|
||||
update_nickname,
|
||||
@@ -1445,13 +1176,11 @@ pub fn routes() -> Vec<Route> {
|
||||
update_birthdate,
|
||||
update_address,
|
||||
update_family,
|
||||
change_skill,
|
||||
change_financial,
|
||||
add_membership_pdf,
|
||||
add_role,
|
||||
add_note,
|
||||
remove_role,
|
||||
// Moves
|
||||
//
|
||||
scheckbook_to_regular,
|
||||
schnupperant_to_regular,
|
||||
schnupperant_to_scheckbook,
|
||||
@@ -1459,9 +1188,5 @@ pub fn routes() -> Vec<Route> {
|
||||
schnupperant_to_schnupperinterest,
|
||||
schnupperinterest_to_scheckbuch,
|
||||
change_membertype,
|
||||
// Add
|
||||
add_club_member,
|
||||
add_scheckbuch,
|
||||
add_schnupper,
|
||||
]
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
use rocket::{
|
||||
FromForm, Request, Route, State,
|
||||
form::Form,
|
||||
get,
|
||||
http::{Cookie, CookieJar},
|
||||
@@ -9,8 +8,9 @@ use rocket::{
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
time::{Duration, OffsetDateTime},
|
||||
FromForm, Request, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{Template, context, tera};
|
||||
use rocket_dyn_templates::{context, tera, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{
|
||||
@@ -73,10 +73,7 @@ async fn login(
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
return Flash::error(
|
||||
Redirect::to("/auth"),
|
||||
"Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Kontaktiere unseren Schriftführer oder schreibe eine Mail an info@rudernlinz.at!",
|
||||
);
|
||||
return Flash::error(Redirect::to("/auth"), "Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Kontaktiere unseren Schriftführer oder schreibe eine Mail an info@rudernlinz.at!");
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -3,8 +3,8 @@ use crate::model::{
|
||||
role::Role,
|
||||
user::{User, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{Route, State, get, request::FlashMessage, routes};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/achievement")]
|
||||
|
@@ -4,14 +4,13 @@ use crate::model::{
|
||||
user::{AdminUser, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{Template, tera::Context};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/boathouse")]
|
||||
|
@@ -1,10 +1,9 @@
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
|
@@ -1,11 +1,10 @@
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
|
@@ -1,9 +1,8 @@
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
@@ -23,7 +22,7 @@ async fn create_ergo(
|
||||
) -> Flash<Redirect> {
|
||||
let trip_details_id = TripDetails::create(db, data.into_inner()).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
|
||||
//created
|
||||
//created
|
||||
Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix
|
||||
|
||||
//Log::create(
|
||||
@@ -46,7 +45,7 @@ async fn create(
|
||||
) -> Flash<Redirect> {
|
||||
let trip_details_id = TripDetails::create(db, data.into_inner()).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
|
||||
//created
|
||||
//created
|
||||
Trip::new_own(db, &cox, trip_details).await; //TODO: fix
|
||||
|
||||
//Log::create(
|
||||
@@ -138,10 +137,9 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: SteeringUser)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/planned"), "Danke für's helfen!")
|
||||
}
|
||||
Err(CoxHelpError::CanceledEvent) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Die Ausfahrt wurde leider abgesagt...",
|
||||
),
|
||||
Err(CoxHelpError::CanceledEvent) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Ausfahrt wurde leider abgesagt...")
|
||||
}
|
||||
Err(CoxHelpError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du hilfst bereits aus!")
|
||||
}
|
||||
@@ -149,10 +147,9 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: SteeringUser)
|
||||
Redirect::to("/planned"),
|
||||
"Du hast dich bereits als Ruderer angemeldet!",
|
||||
),
|
||||
Err(CoxHelpError::DetailsLocked) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.",
|
||||
),
|
||||
Err(CoxHelpError::DetailsLocked) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Flash::error(Redirect::to("/planned"), "Event gibt's nicht")
|
||||
@@ -200,10 +197,9 @@ async fn remove(
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(TripHelpDeleteError::DetailsLocked) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!",
|
||||
),
|
||||
Err(TripHelpDeleteError::DetailsLocked) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!")
|
||||
}
|
||||
Err(TripHelpDeleteError::CoxNotHelping) => {
|
||||
Flash::error(Redirect::to("/planned"), "Steuermann hilft nicht aus...")
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ use std::env;
|
||||
|
||||
use chrono::Utc;
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
fs::TempFile,
|
||||
get,
|
||||
@@ -10,9 +9,9 @@ use rocket::{
|
||||
post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{Template, context};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use serde::Serialize;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
145
src/tera/log.rs
145
src/tera/log.rs
@@ -110,13 +110,10 @@ async fn index(
|
||||
#[get("/show", rank = 3)]
|
||||
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
||||
let logs = Logbook::completed(db).await;
|
||||
let boats = Boat::all(db).await;
|
||||
let users = User::all(db).await;
|
||||
let logtypes = LogType::all(db).await;
|
||||
|
||||
Template::render(
|
||||
"log.completed",
|
||||
context!(logs, boats, users, logtypes, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
|
||||
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -218,77 +215,31 @@ async fn create_logbook(
|
||||
user: &DonauLinzUser,
|
||||
smtp_pw: &str,
|
||||
) -> Flash<Redirect> {
|
||||
match Logbook::create(db, data.into_inner(), user, smtp_pw).await {
|
||||
Ok(msg) => Flash::success(
|
||||
Redirect::to("/log"),
|
||||
format!("Ausfahrt erfolgreich hinzugefügt{msg}"),
|
||||
),
|
||||
Err(LogbookCreateError::BoatAlreadyOnWater) => {
|
||||
Flash::error(Redirect::to("/log"), "Boot schon am Wasser")
|
||||
}
|
||||
Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!("Ruderer {} schon am Wasser", rower.name),
|
||||
),
|
||||
Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"), "Boot gesperrt"),
|
||||
Err(LogbookCreateError::BoatNotFound) => {
|
||||
Flash::error(Redirect::to("/log"), "Boot gibt's ned")
|
||||
}
|
||||
Err(LogbookCreateError::TooManyRowers(expected, actual)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!(
|
||||
"Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)"
|
||||
),
|
||||
),
|
||||
Err(LogbookCreateError::RowerCreateError(rower, e)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!("Fehler bei Ruderer {rower}: {e}"),
|
||||
),
|
||||
Err(LogbookCreateError::ArrivalNotAfterDeparture) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Ankunftszeit kann nicht vor der Abfahrtszeit sein",
|
||||
),
|
||||
Err(LogbookCreateError::UserNotAllowedToUseBoat) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Schiffsführer darf dieses Boot nicht verwenden",
|
||||
),
|
||||
Err(LogbookCreateError::SteeringPersonNotInRowers) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Steuerperson nicht in Liste der Ruderer!",
|
||||
),
|
||||
Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Schiffsführer nicht in Liste der Ruderer!",
|
||||
),
|
||||
Err(LogbookCreateError::NotYourEntry) => {
|
||||
Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!")
|
||||
}
|
||||
Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Ankunftszeit gesetzt aber nicht Distanz + Strecke",
|
||||
),
|
||||
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten an den Vorstand (info@rudernlinz.at).",
|
||||
),
|
||||
Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Handsteuer-Status dieses Boots kann nicht verändert werden.",
|
||||
),
|
||||
Err(LogbookCreateError::TooFast(km, min)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!(
|
||||
"KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut."
|
||||
),
|
||||
),
|
||||
Err(LogbookCreateError::AlreadyFinalized) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Logbucheintrag wurde bereits abgeschlossen.",
|
||||
),
|
||||
Err(LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!",
|
||||
),
|
||||
match Logbook::create(
|
||||
db,
|
||||
data.into_inner(),
|
||||
user, smtp_pw
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(msg) => Flash::success(Redirect::to("/log"), format!("Ausfahrt erfolgreich hinzugefügt{msg}")),
|
||||
Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Boot schon am Wasser"),
|
||||
Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)),
|
||||
Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"),"Boot gesperrt"),
|
||||
Err(LogbookCreateError::BoatNotFound) => Flash::error(Redirect::to("/log"), "Boot gibt's ned"),
|
||||
Err(LogbookCreateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
|
||||
Err(LogbookCreateError::RowerCreateError(rower, e)) => Flash::error(Redirect::to("/log"), format!("Fehler bei Ruderer {rower}: {e}")),
|
||||
Err(LogbookCreateError::ArrivalNotAfterDeparture) => Flash::error(Redirect::to("/log"), "Ankunftszeit kann nicht vor der Abfahrtszeit sein"),
|
||||
Err(LogbookCreateError::UserNotAllowedToUseBoat) => Flash::error(Redirect::to("/log"), "Schiffsführer darf dieses Boot nicht verwenden"),
|
||||
Err(LogbookCreateError::SteeringPersonNotInRowers) => Flash::error(Redirect::to("/log"), "Steuerperson nicht in Liste der Ruderer!"),
|
||||
Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"),
|
||||
Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"),
|
||||
Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"),
|
||||
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten an den Vorstand (info@rudernlinz.at)."),
|
||||
Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(Redirect::to("/log"), "Handsteuer-Status dieses Boots kann nicht verändert werden."),
|
||||
Err(LogbookCreateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
|
||||
Err(LogbookCreateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."),
|
||||
Err(LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,13 +312,7 @@ async fn update(
|
||||
let data = data.into_inner();
|
||||
|
||||
let Some(logbook) = Logbook::find_by_id(db, data.id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!(
|
||||
"Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt",
|
||||
data.id
|
||||
),
|
||||
);
|
||||
return Flash::error(Redirect::to("/log"), format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id));
|
||||
};
|
||||
|
||||
match logbook.update(db, data.clone(), &user.user).await {
|
||||
@@ -408,36 +353,14 @@ async fn home_logbook(
|
||||
);
|
||||
};
|
||||
|
||||
match logbook.home(db, user, data.into_inner(), smtp_pw).await {
|
||||
match logbook.home(db,user, data.into_inner(), smtp_pw).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
|
||||
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!(
|
||||
"Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)"
|
||||
),
|
||||
),
|
||||
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten dem Vorstand an info@rudernlinz.at.",
|
||||
),
|
||||
Err(LogbookUpdateError::TooFast(km, min)) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!(
|
||||
"KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut."
|
||||
),
|
||||
),
|
||||
Err(LogbookUpdateError::AlreadyFinalized) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Logbucheintrag wurde bereits abgeschlossen.",
|
||||
),
|
||||
Err(LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!",
|
||||
),
|
||||
Err(LogbookUpdateError::BoatAlreadyOnWater) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
"Das Boot war in diesem Zeitraum schon am Wasser. Bitte überprüfe das Datum und die Zeit.",
|
||||
),
|
||||
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
|
||||
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten dem Vorstand an info@rudernlinz.at."),
|
||||
Err(LogbookUpdateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
|
||||
Err(LogbookUpdateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."),
|
||||
Err(LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"),
|
||||
Err(LogbookUpdateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Das Boot war in diesem Zeitraum schon am Wasser. Bitte überprüfe das Datum und die Zeit."),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to("/log"),
|
||||
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use rocket::{Route, State, get, http::ContentType, routes};
|
||||
use rocket::{get, http::ContentType, routes, Route, State};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{event::Event, personal::cal::get_personal_cal, user::User};
|
||||
|
@@ -2,7 +2,7 @@ use std::{fs::OpenOptions, io::Write};
|
||||
|
||||
use chrono::{Datelike, Local};
|
||||
use rocket::{
|
||||
Build, Data, FromForm, Request, Rocket, State, catch, catchers,
|
||||
catch, catchers,
|
||||
fairing::{AdHoc, Fairing, Info, Kind},
|
||||
form::Form,
|
||||
fs::FileServer,
|
||||
@@ -13,6 +13,7 @@ use rocket::{
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
time::{Duration, OffsetDateTime},
|
||||
Build, Data, FromForm, Request, Rocket, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use serde::Deserialize;
|
||||
@@ -20,7 +21,6 @@ use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
SCHECKBUCH,
|
||||
model::{
|
||||
logbook::Logbook,
|
||||
notification::Notification,
|
||||
@@ -28,6 +28,7 @@ use crate::{
|
||||
role::Role,
|
||||
user::{User, UserWithDetails},
|
||||
},
|
||||
SCHECKBUCH,
|
||||
};
|
||||
|
||||
pub(crate) mod admin;
|
||||
@@ -201,10 +202,7 @@ async fn blogpost_unpublished(
|
||||
|
||||
#[catch(403)] //forbidden
|
||||
fn forbidden_error() -> Flash<Redirect> {
|
||||
Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.",
|
||||
)
|
||||
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
|
||||
}
|
||||
|
||||
struct Usage {}
|
||||
@@ -330,13 +328,11 @@ mod test {
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
||||
assert!(
|
||||
response
|
||||
.into_string()
|
||||
.await
|
||||
.unwrap()
|
||||
.contains("Ruderassistent")
|
||||
);
|
||||
assert!(response
|
||||
.into_string()
|
||||
.await
|
||||
.unwrap()
|
||||
.contains("Ruderassistent"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use rocket::{
|
||||
Route, State, get,
|
||||
get,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, Route, State,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
|
@@ -1,15 +1,14 @@
|
||||
use rocket::{
|
||||
Route, State, get,
|
||||
get,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD,
|
||||
model::{
|
||||
log::Log,
|
||||
tripdetails::TripDetails,
|
||||
@@ -17,6 +16,7 @@ use crate::{
|
||||
user::{AllowedForPlannedTripsUser, User, UserWithDetails},
|
||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||
},
|
||||
AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD,
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
@@ -81,15 +81,14 @@ async fn join(
|
||||
),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
}else{
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} registered the guest '{}' for trip_details.id={}",
|
||||
user.name, registered_user, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
).await;
|
||||
}
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich angemeldet!")
|
||||
}
|
||||
@@ -99,10 +98,9 @@ async fn join(
|
||||
Err(UserTripError::AlreadyRegistered) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du nimmst bereits teil!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegisteredAsCox) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Du hilfst bereits als Steuerperson aus!",
|
||||
),
|
||||
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du hilfst bereits als Steuerperson aus!")
|
||||
}
|
||||
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
||||
@@ -162,10 +160,7 @@ async fn remove_guest(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!",
|
||||
)
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!")
|
||||
}
|
||||
Err(UserTripDeleteError::GuestNotParticipating) => {
|
||||
Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.")
|
||||
@@ -216,10 +211,7 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
|
||||
)
|
||||
Flash::error(Redirect::to("/planned"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(UserTripDeleteError::NotVisibleToUser) => {
|
||||
Log::create(
|
||||
@@ -231,10 +223,7 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...",
|
||||
)
|
||||
Flash::error(Redirect::to("/planned"), "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...")
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Not possible to be here");
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use rocket::{Route, State, get, routes};
|
||||
use rocket_dyn_templates::{Template, context};
|
||||
use rocket::{get, routes, Route, State};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{
|
||||
|
@@ -1,11 +1,10 @@
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
|
@@ -3,3 +3,33 @@ INSERT INTO user(name) VALUES('Marie');
|
||||
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz'));
|
||||
INSERT INTO user(name) VALUES('Philipp');
|
||||
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz'));
|
||||
|
||||
|
||||
ALTER TABLE role ADD COLUMN formatted_name text;
|
||||
ALTER TABLE role ADD COLUMN desc text;
|
||||
ALTER TABLE role ADD COLUMN hide_in_lists BOOLEAN NOT NULL DEFAULT false;
|
||||
UPDATE role SET hide_in_lists=true WHERE name='paid';
|
||||
UPDATE role SET hide_in_lists=true WHERE name='ergo';
|
||||
UPDATE role SET desc='Can do ANYTHING.' WHERE name='admin';
|
||||
UPDATE role SET desc='Kann Ausfahrten ausschreiben und kann alle Boote die in Linz lagern verwenden.', formatted_name='Steuerperson' WHERE name='cox';
|
||||
UPDATE role SET desc='Darf reparierte Bootschäden verifizieren und wird über Bootsschäden informiert.', formatted_name='Bootsreparateur' WHERE name='tech';
|
||||
UPDATE role SET desc = null WHERE name='Rechnungsprüfer';
|
||||
UPDATE role SET desc='Darf Boote die in Ottensheim lagern verwenden.' WHERE name='Rennrudern';
|
||||
UPDATE role SET desc='Haben zahlreiche Berechtigungen, siehe den Vorstand-Block im Menü.' WHERE name='Vorstand';
|
||||
UPDATE role SET desc='Können Events ausschreiben und bearbeiten.', formatted_name='Eventmanager' WHERE name='manage_events';
|
||||
UPDATE role SET desc='Sieht Details zum Schnupperkurs (Teilnehmer, Bezahlstatus, ...)' WHERE name='schnupper-betreuer';
|
||||
UPDATE role SET desc=null WHERE name='kassier';
|
||||
UPDATE role SET desc=null WHERE name='schriftfuehrer';
|
||||
UPDATE role SET desc='Entfernt bei der Gebührenberechnung die Einschreibgebühr.' WHERE name='no-einschreibgebuehr';
|
||||
UPDATE role SET desc='Es können Logbucheinträge im Nachhinein hinzugefügt werden. Idealerweise diese Rolle nur kurzfristig vergeben.' WHERE name='allow-retroactive-logbookentries';
|
||||
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 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
|
||||
);
|
||||
|
@@ -1,37 +0,0 @@
|
||||
{% import "includes/macros" as macros %}
|
||||
{% import "includes/forms/boat" as boat %}
|
||||
{% extends "base" %}
|
||||
{% block content %}
|
||||
<div class="max-w-screen-lg w-full dark:text-white">
|
||||
<h1 class="h1">Rolle</h1>
|
||||
<div class="grid ">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
|
||||
role="alert">
|
||||
<h2 class="h2">Rolle</h2>
|
||||
{% for role in roles %}
|
||||
<div data-filterable="true"
|
||||
data-filter="{{ role.name }}"
|
||||
class="w-full border-t">
|
||||
<form action="/admin/role/{{ role.id }}"
|
||||
data-filterable="true"
|
||||
method="post"
|
||||
class="bg-white dark:bg-primary-900 p-4 w-full">
|
||||
<div class="w-full">
|
||||
<input type="hidden" name="id" value="{{ role.id }}" />
|
||||
<div class="font-bold mb-1 text-black dark:text-white">
|
||||
{{ role.name }}
|
||||
<br />
|
||||
</div>
|
||||
<div class="grid md:grid-cols-3 gap-3">
|
||||
{{ macros::input(label='Formatierter Name', name='formatted_name', type='text', value=role.formatted_name) }}
|
||||
{{ macros::input(label='Beschreibung', name='desc', type='text', value=role.desc) }}
|
||||
<input value="Ändern" type="submit" class="w-28 btn btn-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
@@ -8,136 +8,25 @@
|
||||
<summary class="px-3 cursor-pointer text-md font-bold text-primary-950 dark:text-white">
|
||||
Neue Person hinzufügen
|
||||
</summary>
|
||||
|
||||
<div class="grid sm:grid-cols-3 gap-3 mt-3">
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-clubuser').showModal()"
|
||||
class="btn btn-primary">Vereinsmitglied</button>
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-scheckbuch').showModal()"
|
||||
class="btn btn-dark">Scheckbuch</button>
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-schnupperkurs').showModal()"
|
||||
class="btn btn-dark">Schnupperkurs</button>
|
||||
|
||||
|
||||
</div>
|
||||
<dialog id="add-clubuser"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
onclick="document.getElementById('add-clubuser').close()">
|
||||
<div onclick="event.stopPropagation();" class="p-3">
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-clubuser').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">
|
||||
<h2 class="h3 mb-3">Neues Vereinsmitglied</h2>
|
||||
<form action="/admin/user/new/clubmember"
|
||||
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='Name', name='name', type="text", required=true) }}
|
||||
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }}
|
||||
{{ macros::select(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung") }}
|
||||
{{ macros::input(label='Mitglied seit', name='member_since', type="date", value=now() | date(), required=true) }}
|
||||
{{ macros::input(label='Geburtsdatum', name='birthdate', type="date", required=true) }}
|
||||
{{ macros::input(label='Telefonnummer', name='phone', type="text", required=true) }}
|
||||
{{ macros::input(label='Adresse', name='address', type="text", required=true) }}
|
||||
{{ macros::input(label='Beitrittserklärung', name='membership_pdf', type="file", accept='application/pdf', required=true) }}
|
||||
<input value="Neues Vereinsmitglied anlegen"
|
||||
type="submit"
|
||||
class="btn btn-primary" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="add-scheckbuch"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
onclick="document.getElementById('add-scheckbuch').close()">
|
||||
<div onclick="event.stopPropagation();" class="p-3">
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-scheckbuch').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">
|
||||
<h2 class="h3 mb-3">Neues Scheckbuch</h2>
|
||||
<form action="/admin/user/new/scheckbuch"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
class="grid gap-3">
|
||||
{{ macros::input(label='Name', name='name', type="text", required=true) }}
|
||||
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }}
|
||||
<input value="Neues Scheckbuch anlegen"
|
||||
type="submit"
|
||||
class="btn btn-primary" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="add-schnupperkurs"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
onclick="document.getElementById('add-schnupperkurs').close()">
|
||||
<div onclick="event.stopPropagation();" class="p-3">
|
||||
<button type="button"
|
||||
onclick="document.getElementById('add-schnupperkurs').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/new/schnupper"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
class="grid gap-3">
|
||||
<h2 class="h3 mb-3">Neuer Schnupperant</h2>
|
||||
|
||||
<form action="/admin/user/new"
|
||||
onsubmit="return confirm('Willst du wirklich einen neuen Benutzer anlegen?');"
|
||||
method="post"
|
||||
class="flex mt-4 rounded-md sm:flex items-end justify-between">
|
||||
<div class="w-full">
|
||||
<div>
|
||||
<label for="schnupper_type" class="text-sm text-gray-600 dark:text-gray-100">Typ</label>
|
||||
<select name="schnupper_type" id="schnupper_type" class="input rounded-md ">
|
||||
<option value="schnupperInterested">Interessiert am Schnupperkurs</option>
|
||||
<option value="schnupperant">Fixe Schnupperkurs-Anmeldung</option>
|
||||
</select>
|
||||
<label for="name" class="sr-only">Name</label>
|
||||
<input type="text"
|
||||
name="name"
|
||||
class="input rounded-md w-100"
|
||||
placeholder="Name" />
|
||||
</div>
|
||||
{{ macros::input(label='Name', name='name', type="text", required=true) }}
|
||||
{{ macros::input(label='Mailadresse', name='mail', type="email", required=true, placeholder='user@mail.at') }}
|
||||
{{ macros::select(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung") }}
|
||||
<input value="Hinzufügen" type="submit" class="btn btn-primary" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
<div class="text-right ml-3">
|
||||
<input value="Hinzufügen"
|
||||
type="submit"
|
||||
class="w-28 mt-2 sm:mt-0 rounded-md bg-primary-500 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer" />
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
<!-- START filterBar -->
|
||||
|
@@ -7,8 +7,8 @@
|
||||
<a href="/admin/user" class="link link-primary link-no-underline">← Userverwaltung</a>
|
||||
{% endif %}
|
||||
<h1 class="h1">{{ user.name }}</h1>
|
||||
<div class="grid sm:grid-cols-2 gap-8 my-8">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<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">
|
||||
<h2 class="h2">
|
||||
Grunddaten
|
||||
<br />
|
||||
@@ -31,13 +31,6 @@
|
||||
<form action="/admin/user/{{ user.id }}/change-nickname" method="post">
|
||||
{{ macros::inputgroup(label='Spitzname', name='nickname', type="text", value=user.nickname, readonly=not allowed_to_edit) }}
|
||||
</form>
|
||||
<form action="/admin/user/{{ user.id }}/change-financial" method="post">
|
||||
{% if user_financial %}
|
||||
{{ macros::selectgroup(label="Finanzielles", data=financial, selected_id=user_financial.id, name='financial_id', display=['name'], default="Keine Ermäßigung", readonly=not allowed_to_edit) }}
|
||||
{% else %}
|
||||
{{ macros::selectgroup(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung", readonly=not allowed_to_edit) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if allowed_to_edit %}
|
||||
<form action="/admin/user/{{ user.id }}/new-note" method="post">
|
||||
{{ macros::inputgroup(label='Neue Notiz', name='note', type="text") }}
|
||||
@@ -53,7 +46,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">
|
||||
Mitgliedschaft
|
||||
<br />
|
||||
@@ -86,13 +79,6 @@
|
||||
<form action="/admin/user/{{ user.id }}/change-address" method="post">
|
||||
{{ macros::inputgroup(label='Adresse', name='address', type="text", value=user.address, readonly=not allowed_to_edit) }}
|
||||
</form>
|
||||
<form action="/admin/user/{{ user.id }}/change-skill" method="post">
|
||||
{% if user_skill %}
|
||||
{{ macros::selectgroup(label="Steuererlaubnis", data=skill, selected_id=user_skill.id, name='skill_id', display=['name'], default="Keine Steuerberechtigung", readonly=not allowed_to_edit) }}
|
||||
{% else %}
|
||||
{{ macros::selectgroup(label="Steuererlaubnis", data=skill, name='skill_id', display=['name'], default="Keine Steuerberechtigung", readonly=not allowed_to_edit) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
<form action="/admin/user/{{ user.id }}/change-family" method="post">
|
||||
{{ macros::selectgroup(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen', readonly=not allowed_to_edit) }}
|
||||
</form>
|
||||
@@ -132,7 +118,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<dialog id="change-member-type"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
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"
|
||||
@@ -175,27 +161,19 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<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 %}
|
||||
{% elif "SchnupperInterest" in member %}
|
||||
{% if allowed_to_edit %}
|
||||
<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 }}/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"
|
||||
@@ -235,7 +213,7 @@
|
||||
class="btn btn-primary">Zu Vereinsmitglied umwandeln</button>
|
||||
</div>
|
||||
<dialog id="call-for-action"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
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"
|
||||
@@ -280,12 +258,20 @@
|
||||
</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 %}
|
||||
</div>
|
||||
</div>
|
||||
{% if is_clubmember %}
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">Rollen</h2>
|
||||
<div>
|
||||
<ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full">
|
||||
@@ -318,7 +304,7 @@
|
||||
class="btn btn-primary w-full">Rolle hinzufügen</button>
|
||||
</div>
|
||||
<dialog id="role-modal"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-600 dark:text-white rounded-md"
|
||||
onclick="document.getElementById('role-modal').close()">
|
||||
<div onclick="event.stopPropagation();" class="p-3">
|
||||
<button type="button"
|
||||
@@ -363,7 +349,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if supposed_to_pay %}
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">💸-Beitrag</h2>
|
||||
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
@@ -385,9 +371,7 @@
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if "paid" in user.roles %}
|
||||
✅ {% for key, value in member %}
|
||||
{% if loop.first %}{{ key }}{% endif %}
|
||||
{% endfor %} hat schon bezahlt
|
||||
✅ {{ member | keys }} hat schon bezahlt
|
||||
{% else %}
|
||||
❌
|
||||
{% for key, value in member %}
|
||||
@@ -400,7 +384,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">Aktivitäten</h2>
|
||||
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
@@ -414,13 +398,71 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">TODO</h2>
|
||||
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
|
||||
<span class="text-black dark:text-white cursor-pointer">
|
||||
<span class="font-bold">
|
||||
{{ user.name }}
|
||||
{% if not user.last_access and allowed_to_edit and user.mail %}
|
||||
<form action="/admin/user"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
class="inline">
|
||||
• <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
||||
href="/admin/user/{{ user.id }}/send-welcome-mail"
|
||||
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
|
||||
</form>
|
||||
{% endif %}
|
||||
</span>
|
||||
</span>
|
||||
<form action="/admin/user"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
class="w-full mt-2">
|
||||
<div class="w-full grid gap-3 mt-3">
|
||||
<input type="hidden" name="id" value="{{ user.id }}" />
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
{% for cluster, cluster_roles in roles | group_by(attribute="cluster") %}
|
||||
<label for="cluster_{{ loop.index }}">{{ cluster }}</label>
|
||||
{# Determine the initially selected role within the cluster #}
|
||||
{% set_global selected_role_id = "none" %}
|
||||
{% for role in cluster_roles %}
|
||||
{% if selected_role_id == "none" and role.name in user.roles %}
|
||||
{% set_global selected_role_id = role.id %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# Set default name to the selected role ID or first role if none selected #}
|
||||
<select id="cluster_{{ loop.index }}"
|
||||
{% if selected_role_id == 'none' %} {% else %} name="roles[{{ selected_role_id }}]" {% endif %}
|
||||
{% if allowed_to_edit == false %}disabled{% endif %}
|
||||
onchange=" if (this.value === '') { this.removeAttribute('name'); } else { this.name = 'roles[' + this.options[this.selectedIndex].getAttribute('data-role-id') + ']'; }">
|
||||
<option value=""
|
||||
data-role-id="none"
|
||||
{% if selected_role_id == 'none' %}selected="selected"{% endif %}>
|
||||
None
|
||||
</option>
|
||||
{% for role in cluster_roles %}
|
||||
<option value="on"
|
||||
data-role-id="{{ role.id }}"
|
||||
{% if role.id == selected_role_id %}selected="selected"{% endif %}>
|
||||
{{ role.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||
<h2 class="h2">Ergo-Challenge</h2>
|
||||
<div class="mx-3">
|
||||
<div class="grid gap-3 pb-3 mt-3">
|
||||
{{ macros::inputgroup(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
||||
{{ macros::inputgroup(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
||||
{{ macros::inputgroup(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
|
||||
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
{{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
|
||||
{{ macros::input(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
|
||||
{{ macros::input(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -183,6 +183,8 @@
|
||||
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative"
|
||||
data-filterable="true"
|
||||
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
|
||||
<details>
|
||||
<summary style="list-style: none;">
|
||||
{% if log.logtype and not hide_type %}
|
||||
<div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold">
|
||||
{% if log.logtype == 1 %}
|
||||
@@ -197,15 +199,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
|
||||
{% if allowed_to_edit %}
|
||||
<a href="#"
|
||||
onclick="document.getElementById('change-{{ log.id }}').showModal()"
|
||||
class="link link-black font-bold">{{ log.boat.name }}</a>
|
||||
{% else %}
|
||||
<strong class="text-black dark:text-white">
|
||||
{{ log.boat.name }}
|
||||
</strong>
|
||||
{% endif %}
|
||||
<strong class="text-black dark:text-white">{{ log.boat.name }}</strong>
|
||||
<small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
|
||||
{% if log.shipmaster_only_steering %}
|
||||
- handgesteuert
|
||||
@@ -258,65 +252,35 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if allowed_to_edit %}
|
||||
<dialog id="change-{{ log.id }}"
|
||||
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
|
||||
onclick="document.getElementById('change-{{ log.id }}').close()">
|
||||
<div onclick="event.stopPropagation();" class="p-3">
|
||||
<button type="button"
|
||||
onclick="document.getElementById('change-{{ log.id }}').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">
|
||||
<h2 class="h3">Eintrag '{{ log.boat.name }}' ändern </h2>
|
||||
<p class="text-center mb-3">ID: {{ log.id }}</p>
|
||||
<form action="/log/update" method="post" class="grid gap-3">
|
||||
</summary>
|
||||
{% if allowed_to_edit %}
|
||||
<form action="/log/update" method="post">
|
||||
<input type="hidden" name="id" value="{{ log.id }}" />
|
||||
<input type="hidden" name="boat_id" value="{{ log.boat_id }}" />
|
||||
<input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" />
|
||||
<input type="hidden"
|
||||
name="steering_person"
|
||||
value="{{ log.steering_person }}" />
|
||||
{{ macros::select(label="Boot", data=boats, name="boat_id", id="boat_id{{ log.id }}", selected_id=log.boat.id ,display=["name", " (","amount_seats", " x)"]) }}
|
||||
{{ macros::select(label="Schiffsführer", data=log.rowers, name="shipmaster", id="shipmaster{{ log.id }}", selected_id=log.shipmaster_user.id) }}
|
||||
|
||||
|
||||
{{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }}
|
||||
<div>
|
||||
<label for="departure" class=" text-sm text-gray-600 dark:text-white ">
|
||||
Abfahrt
|
||||
</label>
|
||||
<input type="datetime-local" class="input rounded-md" name="departure" value="{{ log.departure }}" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="arrival" class=" text-sm text-gray-600 dark:text-white ">
|
||||
Ankunft
|
||||
</label>
|
||||
<input type="datetime-local" class="input rounded-md" name="arrival" value="{{ log.arrival }}" />
|
||||
</div>
|
||||
|
||||
Handgesteuert:
|
||||
<input type="checkbox"
|
||||
name="shipmaster_only_steering"
|
||||
{% if log.shipmaster_only_steering %}checked="checked"{% endif %} />
|
||||
<input type="datetime-local" name="departure" value="{{ log.departure }}" />
|
||||
<input type="datetime-local" name="arrival" value="{{ log.arrival }}" />
|
||||
<input type="hidden" name="destination" value="{{ log.destination }}" />
|
||||
<input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" />
|
||||
<input type="hidden" name="comments" value="{{ log.comments }}" />
|
||||
<input type="hidden" name="logtype" value="{{ log.logtype }}" />
|
||||
<input type="submit" class="btn btn-primary" value="Updaten" />
|
||||
<input type="submit" value="Updaten" />
|
||||
</form>
|
||||
<a href="/log/{{ log.id }}/delete"
|
||||
class="w-28 btn btn-alert mt-3"
|
||||
class="w-28 btn btn-alert"
|
||||
onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');">
|
||||
{% include "includes/delete-icon" %}
|
||||
Löschen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</details>
|
||||
</div>
|
||||
{% endmacro show_old %}
|
||||
{% macro home(log) %}
|
||||
|
@@ -156,7 +156,7 @@ function setChoiceByLabel(choicesInstance, label) {
|
||||
</header>
|
||||
<div class="h-8"></div>
|
||||
{% endmacro header %}
|
||||
{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false, accept='', placeholder='') %}
|
||||
{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false, accept='') %}
|
||||
<div class="{{ wrapper_class }}">
|
||||
<label for="{{ name }}"
|
||||
class="{% if hide_label %} sr-only {% else %} text-sm text-gray-600 dark:text-white {% endif %}">
|
||||
@@ -169,7 +169,7 @@ function setChoiceByLabel(choicesInstance, label) {
|
||||
{% if required %}required{% endif %}
|
||||
value="{{ value }}"
|
||||
class="input {{ class }}"
|
||||
placeholder="{% if hide_label %}{{ label }}{% endif %}{% if placeholder %}{{ placeholder }}{% endif %}"
|
||||
placeholder="{% if hide_label %}{{ label }}{% endif %}"
|
||||
{% if min is defined %}min="{{ min }}"{% endif %}
|
||||
{% if autofocus %}autofocus{% endif %}
|
||||
{% if accept %}accept="{{ accept }}"{% endif %}
|
||||
|
@@ -431,9 +431,6 @@
|
||||
<li class="py-1">
|
||||
<a href="/admin/rss" class="block w-100 py-2 hover:text-primary-600">Logs</a>
|
||||
</li>
|
||||
<li class="py-1">
|
||||
<a href="/admin/role" class="block w-100 py-2 hover:text-primary-600">Rollen</a>
|
||||
</li>
|
||||
<li class="py-1">
|
||||
<a href="/admin/list" class="block w-100 py-2 hover:text-primary-600">Fingerabdruck-Liste überprüfen</a>
|
||||
</li>
|
||||
|
Reference in New Issue
Block a user