limit-users-to-proper-roles #156
@ -2,18 +2,26 @@ INSERT INTO "role" (name) VALUES ('admin');
|
|||||||
INSERT INTO "role" (name) VALUES ('cox');
|
INSERT INTO "role" (name) VALUES ('cox');
|
||||||
INSERT INTO "role" (name) VALUES ('scheckbuch');
|
INSERT INTO "role" (name) VALUES ('scheckbuch');
|
||||||
INSERT INTO "role" (name) VALUES ('tech');
|
INSERT INTO "role" (name) VALUES ('tech');
|
||||||
|
INSERT INTO "role" (name) VALUES ('Donau Linz');
|
||||||
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
|
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
|
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
|
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(1,5);
|
||||||
INSERT INTO "user" (name, pw) VALUES('rower', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
|
INSERT INTO "user" (name, pw) VALUES('rower', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(2,5);
|
||||||
INSERT INTO "user" (name, pw) VALUES('guest', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$GF6gizbI79Bh0zA9its8S0gram956v+YIV8w8VpwJnQ');
|
INSERT INTO "user" (name, pw) VALUES('guest', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$GF6gizbI79Bh0zA9its8S0gram956v+YIV8w8VpwJnQ');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(3,5);
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(3,3);
|
INSERT INTO "user_role" (user_id, role_id) VALUES(3,3);
|
||||||
INSERT INTO "user" (name, pw) VALUES('cox', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
|
INSERT INTO "user" (name, pw) VALUES('cox', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(4,5);
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(4,2);
|
INSERT INTO "user_role" (user_id, role_id) VALUES(4,2);
|
||||||
INSERT INTO "user" (name) VALUES('new');
|
INSERT INTO "user" (name) VALUES('new');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(5,5);
|
||||||
INSERT INTO "user" (name, pw) VALUES('cox2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
|
INSERT INTO "user" (name, pw) VALUES('cox2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(6,5);
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(6,2);
|
INSERT INTO "user_role" (user_id, role_id) VALUES(6,2);
|
||||||
INSERT INTO "user" (name, pw) VALUES('rower2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
|
INSERT INTO "user" (name, pw) VALUES('rower2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
|
||||||
|
INSERT INTO "user_role" (user_id, role_id) VALUES(7,5);
|
||||||
|
|
||||||
INSERT INTO "trip_details" (planned_starting_time, max_people, day, notes) VALUES('10:00', 2, '1970-01-01', 'trip_details for a planned event');
|
INSERT INTO "trip_details" (planned_starting_time, max_people, day, notes) VALUES('10:00', 2, '1970-01-01', 'trip_details for a planned event');
|
||||||
INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES('test-planned-event', 2, 1);
|
INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES('test-planned-event', 2, 1);
|
||||||
|
@ -264,6 +264,10 @@ ORDER BY departure DESC
|
|||||||
return Err(LogbookCreateError::BoatNotFound);
|
return Err(LogbookCreateError::BoatNotFound);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if boat.amount_seats == 1 && log.rowers.is_empty() {
|
||||||
|
log.rowers = vec![created_by_user.id];
|
||||||
|
}
|
||||||
|
|
||||||
if boat.amount_seats == 1 {
|
if boat.amount_seats == 1 {
|
||||||
log.shipmaster = Some(log.rowers[0]);
|
log.shipmaster = Some(log.rowers[0]);
|
||||||
log.steering_person = Some(log.rowers[0]);
|
log.steering_person = Some(log.rowers[0]);
|
||||||
|
@ -440,23 +440,20 @@ impl<'r> FromRequest<'r> for User {
|
|||||||
Ok(user_id) => {
|
Ok(user_id) => {
|
||||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||||
let Some(user) = User::find_by_id(db, user_id).await else {
|
let Some(user) = User::find_by_id(db, user_id).await else {
|
||||||
return Outcome::Error((Status::Unauthorized, LoginError::UserNotFound));
|
return Outcome::Error((Status::Forbidden, LoginError::UserNotFound));
|
||||||
};
|
};
|
||||||
if user.deleted {
|
if user.deleted {
|
||||||
return Outcome::Error((Status::Unauthorized, LoginError::UserDeleted));
|
return Outcome::Error((Status::Forbidden, LoginError::UserDeleted));
|
||||||
}
|
}
|
||||||
user.logged_in(db).await;
|
user.logged_in(db).await;
|
||||||
|
|
||||||
let mut cookie = Cookie::new("loggedin_user", format!("{}", user.id));
|
let mut cookie = Cookie::new("loggedin_user", format!("{}", user.id));
|
||||||
cookie.set_expires(OffsetDateTime::now_utc() + Duration::weeks(12));
|
cookie.set_expires(OffsetDateTime::now_utc() + Duration::weeks(2));
|
||||||
req.cookies().add_private(cookie);
|
req.cookies().add_private(cookie);
|
||||||
|
|
||||||
Outcome::Success(user)
|
Outcome::Success(user)
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => Outcome::Error((Status::Unauthorized, LoginError::DeserializationError)),
|
||||||
println!("{:?}", user_id.value());
|
|
||||||
Outcome::Error((Status::Unauthorized, LoginError::DeserializationError))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => Outcome::Error((Status::Unauthorized, LoginError::NotLoggedIn)),
|
None => Outcome::Error((Status::Unauthorized, LoginError::NotLoggedIn)),
|
||||||
}
|
}
|
||||||
@ -487,7 +484,7 @@ impl<'r> FromRequest<'r> for TechUser {
|
|||||||
if user.has_role(db, "tech").await {
|
if user.has_role(db, "tech").await {
|
||||||
Outcome::Success(TechUser { user })
|
Outcome::Success(TechUser { user })
|
||||||
} else {
|
} else {
|
||||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
@ -530,7 +527,7 @@ impl<'r> FromRequest<'r> for CoxUser {
|
|||||||
if user.has_role(db, "cox").await {
|
if user.has_role(db, "cox").await {
|
||||||
Outcome::Success(CoxUser { user })
|
Outcome::Success(CoxUser { user })
|
||||||
} else {
|
} else {
|
||||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
@ -555,7 +552,7 @@ impl<'r> FromRequest<'r> for AdminUser {
|
|||||||
if user.has_role(db, "admin").await {
|
if user.has_role(db, "admin").await {
|
||||||
Outcome::Success(AdminUser { user })
|
Outcome::Success(AdminUser { user })
|
||||||
} else {
|
} else {
|
||||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
@ -565,22 +562,65 @@ impl<'r> FromRequest<'r> for AdminUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct NonGuestUser {
|
pub struct AllowedForPlannedTripsUser(pub(crate) User);
|
||||||
pub(crate) user: User,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'r> FromRequest<'r> for NonGuestUser {
|
impl<'r> FromRequest<'r> for AllowedForPlannedTripsUser {
|
||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||||
match User::from_request(req).await {
|
match User::from_request(req).await {
|
||||||
Outcome::Success(user) => {
|
Outcome::Success(user) => {
|
||||||
if !user.has_role(db, "scheckbuch").await {
|
if user.has_role(db, "Donau Linz").await {
|
||||||
Outcome::Success(NonGuestUser { user })
|
Outcome::Success(AllowedForPlannedTripsUser(user))
|
||||||
|
} else if user.has_role(db, "scheckbuch").await {
|
||||||
|
Outcome::Success(AllowedForPlannedTripsUser(user))
|
||||||
} else {
|
} else {
|
||||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
|
Outcome::Forward(f) => Outcome::Forward(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<User> for AllowedForPlannedTripsUser {
|
||||||
|
fn into(self) -> User {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct DonauLinzUser(pub(crate) User);
|
||||||
|
|
||||||
|
impl Into<User> for DonauLinzUser {
|
||||||
|
fn into(self) -> User {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DonauLinzUser {
|
||||||
|
type Target = User;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for DonauLinzUser {
|
||||||
|
type Error = LoginError;
|
||||||
|
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||||
|
match User::from_request(req).await {
|
||||||
|
Outcome::Success(user) => {
|
||||||
|
if user.has_role(db, "Donau Linz").await {
|
||||||
|
Outcome::Success(DonauLinzUser(user))
|
||||||
|
} else {
|
||||||
|
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
model::{
|
model::{
|
||||||
boat::Boat,
|
boat::Boat,
|
||||||
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
|
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
|
||||||
user::{CoxUser, NonGuestUser, TechUser, User, UserWithRoles},
|
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRoles},
|
||||||
},
|
},
|
||||||
tera::log::KioskCookie,
|
tera::log::KioskCookie,
|
||||||
};
|
};
|
||||||
@ -45,7 +45,7 @@ async fn index_kiosk(
|
|||||||
async fn index(
|
async fn index(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
user: NonGuestUser,
|
user: DonauLinzUser,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let boatdamages = BoatDamage::all(db).await;
|
let boatdamages = BoatDamage::all(db).await;
|
||||||
let boats = Boat::all(db).await;
|
let boats = Boat::all(db).await;
|
||||||
@ -59,7 +59,7 @@ async fn index(
|
|||||||
context.insert("boats", &boats);
|
context.insert("boats", &boats);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithRoles::from_user(user.user, db).await,
|
&UserWithRoles::from_user(user.into(), db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("boatdamages", context.into_json())
|
Template::render("boatdamages", context.into_json())
|
||||||
@ -76,13 +76,14 @@ pub struct FormBoatDamageToAdd<'r> {
|
|||||||
async fn create<'r>(
|
async fn create<'r>(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<FormBoatDamageToAdd<'r>>,
|
data: Form<FormBoatDamageToAdd<'r>>,
|
||||||
user: NonGuestUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
|
let user: User = user.into();
|
||||||
let boatdamage_to_add = BoatDamageToAdd {
|
let boatdamage_to_add = BoatDamageToAdd {
|
||||||
boat_id: data.boat_id,
|
boat_id: data.boat_id,
|
||||||
desc: data.desc,
|
desc: data.desc,
|
||||||
lock_boat: data.lock_boat,
|
lock_boat: data.lock_boat,
|
||||||
user_id_created: user.user.id as i32,
|
user_id_created: user.id as i32,
|
||||||
};
|
};
|
||||||
match BoatDamage::create(db, boatdamage_to_add).await {
|
match BoatDamage::create(db, boatdamage_to_add).await {
|
||||||
Ok(_) => Flash::success(
|
Ok(_) => Flash::success(
|
||||||
|
@ -391,7 +391,7 @@ mod test {
|
|||||||
.body("name=cox&password=cox"); // Add the form data to the request body;
|
.body("name=cox&password=cox"); // Add the form data to the request body;
|
||||||
login.dispatch().await;
|
login.dispatch().await;
|
||||||
|
|
||||||
let req = client.get("/join/1");
|
let req = client.get("/planned/join/1");
|
||||||
let _ = req.dispatch().await;
|
let _ = req.dispatch().await;
|
||||||
|
|
||||||
let req = client.get("/cox/join/1");
|
let req = client.get("/cox/join/1");
|
||||||
|
@ -23,7 +23,7 @@ use crate::model::{
|
|||||||
LogbookUpdateError,
|
LogbookUpdateError,
|
||||||
},
|
},
|
||||||
logtype::LogType,
|
logtype::LogType,
|
||||||
user::{NonGuestUser, User, UserWithRoles, UserWithWaterStatus},
|
user::{DonauLinzUser, User, UserWithRoles, UserWithWaterStatus},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct KioskCookie(String);
|
pub struct KioskCookie(String);
|
||||||
@ -44,9 +44,9 @@ impl<'r> FromRequest<'r> for KioskCookie {
|
|||||||
async fn index(
|
async fn index(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
user: NonGuestUser,
|
user: DonauLinzUser,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let boats = Boat::for_user(db, &user.user).await;
|
let boats = Boat::for_user(db, &user).await;
|
||||||
|
|
||||||
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
|
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
|
||||||
User::cox(db)
|
User::cox(db)
|
||||||
@ -78,7 +78,7 @@ async fn index(
|
|||||||
context.insert("logtypes", &logtypes);
|
context.insert("logtypes", &logtypes);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithRoles::from_user(user.user, db).await,
|
&UserWithRoles::from_user(user.into(), db).await,
|
||||||
);
|
);
|
||||||
context.insert("on_water", &on_water);
|
context.insert("on_water", &on_water);
|
||||||
context.insert("distances", &distances);
|
context.insert("distances", &distances);
|
||||||
@ -87,12 +87,12 @@ async fn index(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/show", rank = 2)]
|
#[get("/show", rank = 2)]
|
||||||
async fn show(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
||||||
let logs = Logbook::completed(db).await;
|
let logs = Logbook::completed(db).await;
|
||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
"log.completed",
|
"log.completed",
|
||||||
context!(logs, loggedin_user: &UserWithRoles::from_user(user.user, db).await),
|
context!(logs, loggedin_user: &UserWithRoles::from_user(user.into(), db).await),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,12 +166,12 @@ async fn kiosk(
|
|||||||
async fn create_logbook(
|
async fn create_logbook(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
data: Form<LogToAdd>,
|
data: Form<LogToAdd>,
|
||||||
user: &NonGuestUser,
|
user: &DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
match Logbook::create(
|
match Logbook::create(
|
||||||
db,
|
db,
|
||||||
data.into_inner(),
|
data.into_inner(),
|
||||||
&user.user
|
&user
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -197,14 +197,11 @@ async fn create_logbook(
|
|||||||
async fn create(
|
async fn create(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<LogToAdd>,
|
data: Form<LogToAdd>,
|
||||||
user: NonGuestUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
db,
|
||||||
format!(
|
format!("User {} tries to create log entry={:?}", &user.name, data),
|
||||||
"User {} tries to create log entry={:?}",
|
|
||||||
user.user.name, data
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -238,14 +235,14 @@ async fn create_kiosk(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
create_logbook(db, data, &NonGuestUser { user: creator }).await //TODO: fixme
|
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn home_logbook(
|
async fn home_logbook(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
data: Form<LogToFinalize>,
|
data: Form<LogToFinalize>,
|
||||||
logbook_id: i32,
|
logbook_id: i32,
|
||||||
user: &NonGuestUser,
|
user: &DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
|
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
|
||||||
let Some(logbook) = logbook else {
|
let Some(logbook) = logbook else {
|
||||||
@ -255,7 +252,7 @@ async fn home_logbook(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
match logbook.home(db, &user.user, data.into_inner()).await {
|
match logbook.home(db, &user, data.into_inner()).await {
|
||||||
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
|
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::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 Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
|
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
|
||||||
@ -285,11 +282,11 @@ async fn home_kiosk(
|
|||||||
db,
|
db,
|
||||||
data,
|
data,
|
||||||
logbook_id,
|
logbook_id,
|
||||||
&NonGuestUser {
|
&DonauLinzUser(
|
||||||
user: User::find_by_id(db, logbook.shipmaster as i32)
|
User::find_by_id(db, logbook.shipmaster as i32)
|
||||||
.await
|
.await
|
||||||
.unwrap(), //TODO: fixme
|
.unwrap(),
|
||||||
},
|
), //TODO: fixme
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -299,13 +296,13 @@ async fn home(
|
|||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<LogToFinalize>,
|
data: Form<LogToFinalize>,
|
||||||
logbook_id: i32,
|
logbook_id: i32,
|
||||||
user: NonGuestUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
db,
|
||||||
format!(
|
format!(
|
||||||
"User {} tries to finish log entry {logbook_id} {data:?}",
|
"User {} tries to finish log entry {logbook_id} {data:?}",
|
||||||
user.user.name
|
&user.name
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -314,12 +311,12 @@ async fn home(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<logbook_id>/delete", rank = 2)]
|
#[get("/<logbook_id>/delete", rank = 2)]
|
||||||
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: User) -> Flash<Redirect> {
|
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) -> Flash<Redirect> {
|
||||||
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
||||||
if let Some(logbook) = logbook {
|
if let Some(logbook) = logbook {
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
db,
|
||||||
format!("User {} tries to delete log entry {logbook_id}", user.name),
|
format!("User {} tries to delete log entry {logbook_id}", &user.name),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
match logbook.delete(db, &user).await {
|
match logbook.delete(db, &user).await {
|
||||||
|
262
src/tera/mod.rs
262
src/tera/mod.rs
@ -8,17 +8,12 @@ use rocket::{
|
|||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
routes, Build, FromForm, Rocket, State,
|
routes, Build, FromForm, Rocket, State,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::{tera::Context, Template};
|
use rocket_dyn_templates::Template;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
use tera::Context;
|
||||||
|
|
||||||
use crate::model::{
|
use crate::model::user::{User, UserWithRoles};
|
||||||
log::Log,
|
|
||||||
tripdetails::TripDetails,
|
|
||||||
triptype::TripType,
|
|
||||||
user::{User, UserWithRoles},
|
|
||||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) mod admin;
|
pub(crate) mod admin;
|
||||||
mod auth;
|
mod auth;
|
||||||
@ -27,6 +22,7 @@ mod cox;
|
|||||||
mod ergo;
|
mod ergo;
|
||||||
mod log;
|
mod log;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod planned;
|
||||||
mod stat;
|
mod stat;
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
@ -35,6 +31,16 @@ struct LoginForm<'r> {
|
|||||||
password: &'r str,
|
password: &'r str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||||
|
let mut context = Context::new();
|
||||||
|
if let Some(msg) = flash {
|
||||||
|
context.insert("flash", &msg.into_inner());
|
||||||
|
}
|
||||||
|
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||||
|
Template::render("index", context.into_json())
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/", data = "<login>")]
|
#[post("/", data = "<login>")]
|
||||||
async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String {
|
async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String {
|
||||||
match User::login(db, login.name, login.password).await {
|
match User::login(db, login.name, login.password).await {
|
||||||
@ -43,164 +49,16 @@ async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[catch(401)] //Unauthorized
|
||||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
|
||||||
let mut context = Context::new();
|
|
||||||
|
|
||||||
if user.has_role(db, "cox").await || user.has_role(db, "admin").await {
|
|
||||||
let triptypes = TripType::all(db).await;
|
|
||||||
context.insert("trip_types", &triptypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
let days = user.get_days(db).await;
|
|
||||||
|
|
||||||
if let Some(msg) = flash {
|
|
||||||
context.insert("flash", &msg.into_inner());
|
|
||||||
}
|
|
||||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
|
||||||
context.insert("days", &days);
|
|
||||||
Template::render("index", context.into_json())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/join/<trip_details_id>?<user_note>")]
|
|
||||||
async fn join(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
trip_details_id: i64,
|
|
||||||
user: User,
|
|
||||||
user_note: Option<String>,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
|
||||||
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
|
||||||
};
|
|
||||||
|
|
||||||
match UserTrip::create(db, &user, &trip_details, user_note).await {
|
|
||||||
Ok(_) => {
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"User {} registered for trip_details.id={}",
|
|
||||||
user.name, trip_details_id
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
|
|
||||||
}
|
|
||||||
Err(UserTripError::EventAlreadyFull) => {
|
|
||||||
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
|
|
||||||
}
|
|
||||||
Err(UserTripError::AlreadyRegistered) => {
|
|
||||||
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
|
|
||||||
}
|
|
||||||
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
|
||||||
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
|
|
||||||
}
|
|
||||||
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
|
||||||
Redirect::to("/"),
|
|
||||||
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
|
||||||
),
|
|
||||||
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
|
|
||||||
Redirect::to("/"),
|
|
||||||
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
|
|
||||||
),
|
|
||||||
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
|
|
||||||
Redirect::to("/"),
|
|
||||||
"Du darfst keine Gäste hinzufügen.",
|
|
||||||
),
|
|
||||||
Err(UserTripError::DetailsLocked) => Flash::error(
|
|
||||||
Redirect::to("/"),
|
|
||||||
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/remove/<trip_details_id>/<name>")]
|
|
||||||
async fn remove_guest(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
trip_details_id: i64,
|
|
||||||
user: User,
|
|
||||||
name: String,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
|
||||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
|
||||||
};
|
|
||||||
|
|
||||||
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
|
|
||||||
Ok(_) => {
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"User {} unregistered for trip_details.id={}",
|
|
||||||
user.name, trip_details_id
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
|
||||||
}
|
|
||||||
Err(UserTripDeleteError::DetailsLocked) => {
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"User {} tried to unregister for locked trip_details.id={}",
|
|
||||||
user.name, trip_details_id
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
|
||||||
}
|
|
||||||
Err(UserTripDeleteError::GuestNotParticipating) => {
|
|
||||||
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
|
|
||||||
}
|
|
||||||
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
|
|
||||||
Redirect::to("/"),
|
|
||||||
"Keine Berechtigung um den Gast zu entfernen.",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/remove/<trip_details_id>")]
|
|
||||||
async fn remove(db: &State<SqlitePool>, trip_details_id: i64, user: User) -> Flash<Redirect> {
|
|
||||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
|
||||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
|
||||||
};
|
|
||||||
|
|
||||||
match UserTrip::delete(db, &user, &trip_details, None).await {
|
|
||||||
Ok(_) => {
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"User {} unregistered for trip_details.id={}",
|
|
||||||
user.name, trip_details_id
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
|
||||||
}
|
|
||||||
Err(UserTripDeleteError::DetailsLocked) => {
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"User {} tried to unregister for locked trip_details.id={}",
|
|
||||||
user.name, trip_details_id
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
panic!("Not possible to be here");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[catch(401)] //unauthorized
|
|
||||||
fn unauthorized_error() -> Redirect {
|
fn unauthorized_error() -> Redirect {
|
||||||
Redirect::to("/auth")
|
Redirect::to("/auth")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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.")
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -210,10 +68,11 @@ pub struct Config {
|
|||||||
|
|
||||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
rocket
|
rocket
|
||||||
.mount("/", routes![index, join, remove, remove_guest])
|
.mount("/", routes![index])
|
||||||
.mount("/auth", auth::routes())
|
.mount("/auth", auth::routes())
|
||||||
.mount("/wikiauth", routes![wikiauth])
|
.mount("/wikiauth", routes![wikiauth])
|
||||||
.mount("/log", log::routes())
|
.mount("/log", log::routes())
|
||||||
|
.mount("/planned", planned::routes())
|
||||||
.mount("/ergo", ergo::routes())
|
.mount("/ergo", ergo::routes())
|
||||||
.mount("/stat", stat::routes())
|
.mount("/stat", stat::routes())
|
||||||
.mount("/boatdamage", boatdamage::routes())
|
.mount("/boatdamage", boatdamage::routes())
|
||||||
@ -221,7 +80,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
.mount("/admin", admin::routes())
|
.mount("/admin", admin::routes())
|
||||||
.mount("/", misc::routes())
|
.mount("/", misc::routes())
|
||||||
.mount("/public", FileServer::from("static/"))
|
.mount("/public", FileServer::from("static/"))
|
||||||
.register("/", catchers![unauthorized_error])
|
.register("/", catchers![unauthorized_error, forbidden_error])
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
.attach(AdHoc::config::<Config>())
|
.attach(AdHoc::config::<Config>())
|
||||||
}
|
}
|
||||||
@ -255,7 +114,11 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
|
||||||
assert!(response.into_string().await.unwrap().contains("Ausfahrten"));
|
assert!(response
|
||||||
|
.into_string()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.contains("Ruderassistent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
@ -274,75 +137,6 @@ mod test {
|
|||||||
assert_eq!(response.headers().get("Location").next(), Some("/auth"));
|
assert_eq!(response.headers().get("Location").next(), Some("/auth"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sqlx::test]
|
|
||||||
fn test_join_and_remove() {
|
|
||||||
let db = testdb!();
|
|
||||||
|
|
||||||
let rocket = rocket::build().manage(db.clone());
|
|
||||||
let rocket = crate::tera::config(rocket);
|
|
||||||
|
|
||||||
let client = Client::tracked(rocket).await.unwrap();
|
|
||||||
let login = client
|
|
||||||
.post("/auth")
|
|
||||||
.header(ContentType::Form) // Set the content type to form
|
|
||||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
|
||||||
login.dispatch().await;
|
|
||||||
|
|
||||||
let req = client.get("/join/1");
|
|
||||||
let response = req.dispatch().await;
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
|
||||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
|
||||||
|
|
||||||
let flash_cookie = response
|
|
||||||
.cookies()
|
|
||||||
.get("_flash")
|
|
||||||
.expect("Expected flash cookie");
|
|
||||||
|
|
||||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
|
|
||||||
|
|
||||||
let req = client.get("/remove/1");
|
|
||||||
let response = req.dispatch().await;
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
|
||||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
|
||||||
|
|
||||||
let flash_cookie = response
|
|
||||||
.cookies()
|
|
||||||
.get("_flash")
|
|
||||||
.expect("Expected flash cookie");
|
|
||||||
|
|
||||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sqlx::test]
|
|
||||||
fn test_join_invalid_event() {
|
|
||||||
let db = testdb!();
|
|
||||||
|
|
||||||
let rocket = rocket::build().manage(db.clone());
|
|
||||||
let rocket = crate::tera::config(rocket);
|
|
||||||
|
|
||||||
let client = Client::tracked(rocket).await.unwrap();
|
|
||||||
let login = client
|
|
||||||
.post("/auth")
|
|
||||||
.header(ContentType::Form) // Set the content type to form
|
|
||||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
|
||||||
login.dispatch().await;
|
|
||||||
|
|
||||||
let req = client.get("/join/9999");
|
|
||||||
let response = req.dispatch().await;
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
|
||||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
|
||||||
|
|
||||||
let flash_cookie = response
|
|
||||||
.cookies()
|
|
||||||
.get("_flash")
|
|
||||||
.expect("Expected flash cookie");
|
|
||||||
|
|
||||||
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
fn test_public() {
|
fn test_public() {
|
||||||
let db = testdb!();
|
let db = testdb!();
|
||||||
|
273
src/tera/planned.rs
Normal file
273
src/tera/planned.rs
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
use rocket::{
|
||||||
|
get,
|
||||||
|
request::FlashMessage,
|
||||||
|
response::{Flash, Redirect},
|
||||||
|
routes, Route, State,
|
||||||
|
};
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
use tera::Context;
|
||||||
|
|
||||||
|
use crate::model::{
|
||||||
|
log::Log,
|
||||||
|
tripdetails::TripDetails,
|
||||||
|
triptype::TripType,
|
||||||
|
user::{AllowedForPlannedTripsUser, User, UserWithRoles},
|
||||||
|
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
user: AllowedForPlannedTripsUser,
|
||||||
|
flash: Option<FlashMessage<'_>>,
|
||||||
|
) -> Template {
|
||||||
|
let user: User = user.into();
|
||||||
|
|
||||||
|
let mut context = Context::new();
|
||||||
|
|
||||||
|
if user.has_role(db, "cox").await || user.has_role(db, "admin").await {
|
||||||
|
let triptypes = TripType::all(db).await;
|
||||||
|
context.insert("trip_types", &triptypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let days = user.get_days(db).await;
|
||||||
|
|
||||||
|
if let Some(msg) = flash {
|
||||||
|
context.insert("flash", &msg.into_inner());
|
||||||
|
}
|
||||||
|
context.insert(
|
||||||
|
"loggedin_user",
|
||||||
|
&UserWithRoles::from_user(user.into(), db).await,
|
||||||
|
);
|
||||||
|
context.insert("days", &days);
|
||||||
|
Template::render("planned", context.into_json())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/join/<trip_details_id>?<user_note>")]
|
||||||
|
async fn join(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
trip_details_id: i64,
|
||||||
|
user: AllowedForPlannedTripsUser,
|
||||||
|
user_note: Option<String>,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let user: User = user.into();
|
||||||
|
|
||||||
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
|
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
||||||
|
};
|
||||||
|
|
||||||
|
match UserTrip::create(db, &user, &trip_details, user_note).await {
|
||||||
|
Ok(_) => {
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!(
|
||||||
|
"User {} registered for trip_details.id={}",
|
||||||
|
user.name, trip_details_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
|
||||||
|
}
|
||||||
|
Err(UserTripError::EventAlreadyFull) => {
|
||||||
|
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
|
||||||
|
}
|
||||||
|
Err(UserTripError::AlreadyRegistered) => {
|
||||||
|
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
|
||||||
|
}
|
||||||
|
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
||||||
|
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
|
||||||
|
}
|
||||||
|
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
||||||
|
Redirect::to("/"),
|
||||||
|
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
||||||
|
),
|
||||||
|
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
|
||||||
|
Redirect::to("/"),
|
||||||
|
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
|
||||||
|
),
|
||||||
|
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
|
||||||
|
Redirect::to("/"),
|
||||||
|
"Du darfst keine Gäste hinzufügen.",
|
||||||
|
),
|
||||||
|
Err(UserTripError::DetailsLocked) => Flash::error(
|
||||||
|
Redirect::to("/"),
|
||||||
|
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/remove/<trip_details_id>/<name>")]
|
||||||
|
async fn remove_guest(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
trip_details_id: i64,
|
||||||
|
user: AllowedForPlannedTripsUser,
|
||||||
|
name: String,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let user: User = user.into();
|
||||||
|
|
||||||
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
|
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||||
|
};
|
||||||
|
|
||||||
|
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
|
||||||
|
Ok(_) => {
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!(
|
||||||
|
"User {} unregistered for trip_details.id={}",
|
||||||
|
user.name, trip_details_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||||
|
}
|
||||||
|
Err(UserTripDeleteError::DetailsLocked) => {
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!(
|
||||||
|
"User {} tried to unregister for locked trip_details.id={}",
|
||||||
|
user.name, trip_details_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||||
|
}
|
||||||
|
Err(UserTripDeleteError::GuestNotParticipating) => {
|
||||||
|
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
|
||||||
|
}
|
||||||
|
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
|
||||||
|
Redirect::to("/"),
|
||||||
|
"Keine Berechtigung um den Gast zu entfernen.",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/remove/<trip_details_id>")]
|
||||||
|
async fn remove(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
trip_details_id: i64,
|
||||||
|
user: AllowedForPlannedTripsUser,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let user: User = user.into();
|
||||||
|
|
||||||
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
|
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||||
|
};
|
||||||
|
|
||||||
|
match UserTrip::delete(db, &user, &trip_details, None).await {
|
||||||
|
Ok(_) => {
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!(
|
||||||
|
"User {} unregistered for trip_details.id={}",
|
||||||
|
user.name, trip_details_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||||
|
}
|
||||||
|
Err(UserTripDeleteError::DetailsLocked) => {
|
||||||
|
Log::create(
|
||||||
|
db,
|
||||||
|
format!(
|
||||||
|
"User {} tried to unregister for locked trip_details.id={}",
|
||||||
|
user.name, trip_details_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
panic!("Not possible to be here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![index, join, remove, remove_guest]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use rocket::{
|
||||||
|
http::{ContentType, Status},
|
||||||
|
local::asynchronous::Client,
|
||||||
|
};
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
use crate::testdb;
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
fn test_join_and_remove() {
|
||||||
|
let db = testdb!();
|
||||||
|
|
||||||
|
let rocket = rocket::build().manage(db.clone());
|
||||||
|
let rocket = crate::tera::config(rocket);
|
||||||
|
|
||||||
|
let client = Client::tracked(rocket).await.unwrap();
|
||||||
|
let login = client
|
||||||
|
.post("/auth")
|
||||||
|
.header(ContentType::Form) // Set the content type to form
|
||||||
|
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||||
|
login.dispatch().await;
|
||||||
|
|
||||||
|
let req = client.get("/planned/join/1");
|
||||||
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||||
|
|
||||||
|
let flash_cookie = response
|
||||||
|
.cookies()
|
||||||
|
.get("_flash")
|
||||||
|
.expect("Expected flash cookie");
|
||||||
|
|
||||||
|
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
|
||||||
|
|
||||||
|
let req = client.get("/planned/remove/1");
|
||||||
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||||
|
|
||||||
|
let flash_cookie = response
|
||||||
|
.cookies()
|
||||||
|
.get("_flash")
|
||||||
|
.expect("Expected flash cookie");
|
||||||
|
|
||||||
|
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
fn test_join_invalid_event() {
|
||||||
|
let db = testdb!();
|
||||||
|
|
||||||
|
let rocket = rocket::build().manage(db.clone());
|
||||||
|
let rocket = crate::tera::config(rocket);
|
||||||
|
|
||||||
|
let client = Client::tracked(rocket).await.unwrap();
|
||||||
|
let login = client
|
||||||
|
.post("/auth")
|
||||||
|
.header(ContentType::Form) // Set the content type to form
|
||||||
|
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||||
|
login.dispatch().await;
|
||||||
|
|
||||||
|
let req = client.get("/planned/join/9999");
|
||||||
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
|
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||||
|
|
||||||
|
let flash_cookie = response
|
||||||
|
.cookies()
|
||||||
|
.get("_flash")
|
||||||
|
.expect("Expected flash cookie");
|
||||||
|
|
||||||
|
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
|
||||||
|
}
|
||||||
|
}
|
@ -4,19 +4,19 @@ use sqlx::SqlitePool;
|
|||||||
|
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
stat::{self, Stat},
|
stat::{self, Stat},
|
||||||
user::{NonGuestUser, UserWithRoles},
|
user::{DonauLinzUser, UserWithRoles},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::log::KioskCookie;
|
use super::log::KioskCookie;
|
||||||
|
|
||||||
#[get("/boats?<year>", rank = 2)]
|
#[get("/boats?<year>", rank = 2)]
|
||||||
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
|
||||||
let stat = Stat::boats(db, year).await;
|
let stat = Stat::boats(db, year).await;
|
||||||
let kiosk = false;
|
let kiosk = false;
|
||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
"stat.boats",
|
"stat.boats",
|
||||||
context!(loggedin_user: &UserWithRoles::from_user(user.user, db).await, stat, kiosk),
|
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, kiosk),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,15 +33,15 @@ async fn index_boat_kiosk(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/?<year>", rank = 2)]
|
#[get("/?<year>", rank = 2)]
|
||||||
async fn index(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
|
||||||
let stat = Stat::people(db, year).await;
|
let stat = Stat::people(db, year).await;
|
||||||
let guest_km = Stat::guest(db, year).await;
|
let guest_km = Stat::guest(db, year).await;
|
||||||
let personal = stat::get_personal(db, &user.user).await;
|
let personal = stat::get_personal(db, &user).await;
|
||||||
let kiosk = false;
|
let kiosk = false;
|
||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
"stat.people",
|
"stat.people",
|
||||||
context!(loggedin_user: &UserWithRoles::from_user(user.user, db).await, stat, personal, kiosk, guest_km),
|
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, personal, kiosk, guest_km),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div><!--
|
||||||
<a
|
<a
|
||||||
href="https://wiki.rudernlinz.at/ruderassistent#faq"
|
href="https://wiki.rudernlinz.at/ruderassistent#faq"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -121,7 +121,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="sr-only">Userverwaltung</span>
|
<span class="sr-only">Userverwaltung</span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}-->
|
||||||
<a
|
<a
|
||||||
href="/auth/logout"
|
href="/auth/logout"
|
||||||
class="inline-flex justify-center rounded-md bg-gray-200 ml-1 px-3 py-2 text-sm font-semibold text-primary-950 hover:bg-gray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
class="inline-flex justify-center rounded-md bg-gray-200 ml-1 px-3 py-2 text-sm font-semibold text-primary-950 hover:bg-gray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
||||||
|
@ -3,292 +3,86 @@
|
|||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="max-w-screen-xl w-full grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="max-w-screen-lg w-full">
|
||||||
{% if flash %}
|
{% if flash %}
|
||||||
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<h1 class="h1">Ruderassistent</h1>
|
||||||
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten</h1>
|
|
||||||
|
|
||||||
{% include "includes/buttons" %}
|
|
||||||
|
|
||||||
{% for day in days %}
|
|
||||||
{% set amount_trips = day.planned_events | length + day.trips | length %}
|
|
||||||
{% set_global day_cox_needed = false %}
|
|
||||||
{% if day.planned_events | length > 0 %}
|
|
||||||
{% for planned_event in day.planned_events %}
|
|
||||||
{% if planned_event.cox_needed %}
|
|
||||||
{% set_global day_cox_needed = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="bg-white dark:bg-primary-900 rounded-md flex justify-between flex-col shadow reset-js" style="min-height: 10rem;" data-trips="{{ amount_trips }}" data-month="{{ day.day| date(format='%m') }}" data-coxneeded="{{ day_cox_needed }}">
|
|
||||||
<div>
|
|
||||||
<h2 class="font-bold uppercase tracking-wide text-center rounded-t-md {% if day.is_pinned %} text-white bg-primary-950 {% else %} text-primary-950 dark:text-white bg-gray-200 dark:bg-primary-950 bg-opacity-80 {% endif %} text-lg px-3 py-3 ">{{ day.day| date(format="%d.%m.%Y") }}
|
|
||||||
<small class="inline-block ml-1 text-xs {% if day.is_pinned %} text-gray-200 {% else %} text-gray-500 dark:text-gray-100 {% endif %}">{{ day.day | date(format="%A", locale="de_AT") }}</small>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{% if day.planned_events | length > 0 or day.trips | length > 0 %}
|
|
||||||
<div
|
|
||||||
class="grid grid-cols-1 gap-3 mb-3">
|
|
||||||
|
|
||||||
{# --- START Events --- #}
|
|
||||||
{% if day.planned_events | length > 0 %}
|
|
||||||
{% for planned_event in day.planned_events | sort(attribute="planned_starting_time") %}
|
|
||||||
{% set amount_cur_cox = planned_event.cox | length %}
|
|
||||||
{% set amount_cox_missing = planned_event.planned_amount_cox - amount_cur_cox %}
|
|
||||||
<div class="pt-2 px-3 border-t border-gray-200" style="order: {{ planned_event.planned_starting_time | replace(from=":", to="") }}">
|
|
||||||
<div class="flex justify-between items-center">
|
|
||||||
<div class="mr-1">
|
|
||||||
<strong class="text-primary-900 dark:text-white">
|
|
||||||
{{ planned_event.planned_starting_time }}
|
|
||||||
Uhr
|
|
||||||
</strong>
|
|
||||||
<small class="text-gray-600 dark:text-gray-100">({{ planned_event.name }}{% if planned_event.trip_type %} - {{ planned_event.trip_type.icon | safe }} {{ planned_event.trip_type.name }}{% endif %})</small><br/>
|
|
||||||
|
|
||||||
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>{{ planned_event.planned_starting_time }} Uhr</strong> ({{ planned_event.name }}){% if planned_event.trip_type %}<small class='block'>{{ planned_event.trip_type.desc }}</small>{% endif %}{% if planned_event.notes %}<small class='block'>{{ planned_event.notes }}</small>{% endif %}" data-body="#event{{ planned_event.trip_details_id }}" class="inline-block link-primary mr-3">
|
|
||||||
Details
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-right grid gap-2">
|
|
||||||
{# --- START Row Buttons --- #}
|
|
||||||
{% set_global cur_user_participates = false %}
|
|
||||||
{% for rower in planned_event.rower%}
|
|
||||||
{% if rower.name == loggedin_user.name %}
|
|
||||||
{% set_global cur_user_participates = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if cur_user_participates %}
|
|
||||||
<a href="/remove/{{ planned_event.trip_details_id }}" class="btn btn-attention btn-fw">Abmelden</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if planned_event.max_people > planned_event.rower | length %}
|
|
||||||
{% if cur_user_participates == false %}
|
|
||||||
<a href="/join/{{ planned_event.trip_details_id }}" class="btn btn-primary btn-fw" {% if planned_event.trip_type %} onclick="return confirm('{{ planned_event.trip_type.question }}');" {% endif %}>Mitrudern</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Row Buttons --- #}
|
|
||||||
|
|
||||||
{# --- START Cox Buttons --- #}
|
|
||||||
{% if "cox" in loggedin_user.roles %}
|
|
||||||
{% set_global cur_user_participates = false %}
|
|
||||||
{% for cox in planned_event.cox %}
|
|
||||||
{% if cox.name == loggedin_user.name %}
|
|
||||||
{% set_global cur_user_participates = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if cur_user_participates %}
|
|
||||||
<a href="/cox/remove/{{ planned_event.id }}" class="block btn btn-attention btn-fw">
|
|
||||||
{% include "includes/cox-icon" %}
|
|
||||||
Abmelden
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cox/join/{{ planned_event.id }}" class="block btn {% if amount_cox_missing > 0 %} btn-dark {% else %} btn-gray {% endif %} btn-fw" {% if planned_event.trip_type %} onclick="return confirm('{{ planned_event.trip_type.question }}');" {% endif %}>
|
|
||||||
{% include "includes/cox-icon" %}
|
|
||||||
Steuern
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Cox Buttons --- #}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# --- START Sidebar Content --- #}
|
|
||||||
<div class="hidden">
|
|
||||||
<div
|
|
||||||
id="event{{ planned_event.trip_details_id }}">
|
|
||||||
{# --- START List Coxes --- #}
|
|
||||||
{% if planned_event.planned_amount_cox > 0 %}
|
|
||||||
{% if amount_cox_missing > 0 %}
|
|
||||||
{{ macros::box(participants=planned_event.cox, empty_seats=planned_event.planned_amount_cox - amount_cur_cox, header='Noch benötigte Steuerleute:', text='Keine Steuerleute angemeldet') }}
|
|
||||||
{% else %}
|
|
||||||
{{ macros::box(participants=planned_event.cox, empty_seats="", header='Genügend Steuerleute haben sich angemeldet :-)', text='Keine Steuerleute angemeldet') }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END List Coxes --- #}
|
|
||||||
|
|
||||||
{# --- START List Rowers --- #}
|
|
||||||
{% if planned_event.max_people > 0 %}
|
|
||||||
{% set amount_cur_rower = planned_event.rower | length %}
|
|
||||||
{{ macros::box(participants=planned_event.rower, empty_seats=planned_event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=planned_event.trip_details_id, allow_removing="admin" in loggedin_user.roles) }}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END List Rowers --- #}
|
|
||||||
|
|
||||||
{% if "admin" in loggedin_user.roles %}
|
|
||||||
<form action="/join/{{ planned_event.trip_details_id }}" method="get" />
|
|
||||||
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
|
|
||||||
<input value="Gast hinzufügen" class="btn btn-primary w-full rounded-t-none-important" type="submit"/>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% if planned_event.allow_guests %}
|
<div class="grid gap-3">
|
||||||
<div class="text-primary-900 bg-primary-50 text-center p-1 mb-4">Gäste willkommen!</div>
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
||||||
{% endif %}
|
<h2 class="h2">Allgemein</h2>
|
||||||
|
<div class="text-sm p-3">
|
||||||
|
<ul class="list-disc ms-2">
|
||||||
|
<li class="py-1"><a href="https://rudernlinz.at/termin" target="_blank" class="link-primary">FAQ (extern)</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if "admin" in loggedin_user.roles %}
|
{% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
|
||||||
|
<div class="grid gap-3">
|
||||||
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
||||||
|
<h2 class="h2">Ergo</h2>
|
||||||
|
<div class="text-sm p-3">
|
||||||
|
<ul class="list-disc ms-2">
|
||||||
|
<li class="py-1"><a href="/ergo" class="link-primary">Ergo</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{# --- START Edit Form --- #}
|
{% if "Donau Linz" in loggedin_user.roles %}
|
||||||
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
|
<div class="grid gap-3">
|
||||||
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3>
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
||||||
<form action="/admin/planned-event" method="post" class="grid gap-3">
|
<h2 class="h2">Vereinsmitglieder</h2>
|
||||||
<input type="hidden" name="_method" value="put"/>
|
<div class="text-sm p-3">
|
||||||
<input type="hidden" name="id" value="{{ planned_event.id }}"/>
|
<ul class="list-disc ms-2">
|
||||||
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=planned_event.max_people, min='0') }}
|
<li class="py-1"><a href="/planned" class="link-primary">Geplante Ausfahrten</a></li>
|
||||||
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=planned_event.planned_amount_cox, required=true, min='0') }}
|
<li class="py-1"><a href="/log" class="link-primary">Ausfahrt eintragen</a></li>
|
||||||
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=planned_event.id,checked=planned_event.always_show) }}
|
<li class="py-1"><a href="/log/show" class="link-primary">Logbuch</a></li>
|
||||||
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=planned_event.id,checked=planned_event.is_locked) }}
|
<li class="py-1"><a href="/stat" class="link-primary">Statstik</a></li>
|
||||||
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=planned_event.notes) }}
|
<li class="py-1"><a href="/stat/boats" class="link-primary">Bootsauswertung</a></li>
|
||||||
|
<li class="py-1"><a href="/boatdamage" class="link-primary">Bootsschaden</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<input value="Speichern" class="btn btn-primary" type="submit"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{# --- END Edit Form --- #}
|
|
||||||
|
|
||||||
{# --- START Delete Btn --- #}
|
{% if "scheckbuch" in loggedin_user.roles %}
|
||||||
<div class="text-right">
|
<div class="grid gap-3">
|
||||||
<a href="/admin/planned-event/{{ planned_event.id }}/delete" class="inline-block btn btn-alert">
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
||||||
{% include "includes/delete-icon" %}
|
<h2 class="h2">Scheckbuch</h2>
|
||||||
Termin löschen
|
<div class="text-sm p-3">
|
||||||
</a>
|
<ul class="list-disc ms-2">
|
||||||
</div>
|
<li class="py-1"><a href="/planned" class="link-primary">Geplante Ausfahrten</a></li>
|
||||||
{% endif %}
|
</ul>
|
||||||
{# --- END Delete Btn --- #}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{# --- END Sidebar Content --- #}
|
{% endif %}
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Events --- #}
|
|
||||||
|
|
||||||
{# --- START Trips --- #}
|
{% if "admin" in loggedin_user.roles %}
|
||||||
{% if day.trips | length > 0 %}
|
<div class="grid gap-3">
|
||||||
{% for trip in day.trips | sort(attribute="planned_starting_time") %}
|
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
||||||
<div class="pt-2 px-3 reset-js border-t border-gray-200" style="order: {{ trip.planned_starting_time | replace(from=":", to="") }}" data-coxneeded="false">
|
<h2 class="h2">Admin</h2>
|
||||||
<div class="flex justify-between items-center">
|
<div class="text-sm p-3">
|
||||||
<div class="mr-1">
|
<ul class="list-disc ms-2">
|
||||||
{% if trip.max_people == 0 %}
|
<li class="py-1"><a href="/admin/boat" class="link-primary">Boote</a></li>
|
||||||
<strong class="text-[#f43f5e]">⚠
|
<li class="py-1"><a href="/admin/user" class="link-primary">User</a></li>
|
||||||
{{ trip.planned_starting_time }}
|
<li class="py-1"><a href="/admin/mail" class="link-primary">Mail</a></li>
|
||||||
Uhr</strong>
|
</ul>
|
||||||
<small class="text-[#f43f5e]">(Absage
|
</div>
|
||||||
{{ trip.cox_name }}
|
</div>
|
||||||
{% if trip.trip_type %}
|
</div>
|
||||||
-
|
{% endif %}
|
||||||
{{ trip.trip_type.icon | safe }}{{ trip.trip_type.name }}
|
|
||||||
{% endif %})</small>
|
|
||||||
{% else %}
|
|
||||||
<strong class="text-primary-900 dark:text-white">{{ trip.planned_starting_time }}
|
|
||||||
Uhr</strong>
|
|
||||||
<small class="text-gray-600 dark:text-gray-100">({{ trip.cox_name }}{% if trip.trip_type %} - {{ trip.trip_type.icon | safe }} {{ trip.trip_type.name }}{% endif %})</small>
|
|
||||||
{% endif %}
|
|
||||||
<br/>
|
|
||||||
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>{% if trip.max_people == 0 %}⚠ {% endif %}{{ trip.planned_starting_time }} Uhr</strong> ({{ trip.cox_name }}){% if trip.trip_type %}<small class='block'>{{ trip.trip_type.desc }}</small>{% endif %}{% if trip.notes %}<small class='block'>{{ trip.notes }}</small>{% endif %}" data-body="#trip{{ trip.trip_details_id }}" class="inline-block link-primary mr-3">
|
|
||||||
Details
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{% set_global cur_user_participates = false %}
|
|
||||||
{% for rower in trip.rower %}
|
|
||||||
{% if rower.name == loggedin_user.name %}
|
|
||||||
{% set_global cur_user_participates = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if cur_user_participates %}
|
|
||||||
<a href="/remove/{{ trip.trip_details_id }}" class="btn btn-attention btn-fw">Abmelden</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if trip.max_people > trip.rower | length and trip.cox_id != loggedin_user.id and cur_user_participates == false%}
|
|
||||||
<a href="/join/{{ trip.trip_details_id }}" class="btn btn-primary btn-fw" {% if trip.trip_type %} onclick="return confirm('{{ trip.trip_type.question }}');" {% endif %}>Mitrudern</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{# --- START Sidebar Content --- #}
|
|
||||||
<div class="hidden">
|
|
||||||
<div id="trip{{ trip.trip_details_id }}">
|
|
||||||
{% if trip.max_people == 0 %}
|
|
||||||
{# --- border-[#f43f5e] bg-[#f43f5e] --- #}
|
|
||||||
{{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage') }}
|
|
||||||
{% else %}
|
|
||||||
{% set amount_cur_rower = trip.rower | length %}
|
|
||||||
{{ macros::box(participants=trip.rower, empty_seats=trip.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }}
|
|
||||||
{% if trip.cox_id == loggedin_user.id %}
|
|
||||||
<form action="/join/{{ trip.trip_details_id }}" method="get" />
|
|
||||||
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
|
|
||||||
<input value="Gast hinzufügen" class="btn btn-primary w-full rounded-t-none-important" type="submit"/>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# --- START Edit Form --- #}
|
</div>
|
||||||
{% if trip.cox_id == loggedin_user.id %}
|
|
||||||
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
|
|
||||||
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3>
|
|
||||||
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid gap-3">
|
|
||||||
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=trip.max_people, min='0') }}
|
|
||||||
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=trip.notes) }}
|
|
||||||
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=trip.id,checked=trip.always_show) }}
|
|
||||||
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }}
|
|
||||||
{{ macros::select(label='Typ', name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=trip.trip_type_id) }}
|
|
||||||
|
|
||||||
<input value="Speichern" class="btn btn-primary" type="submit"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% if trip.rower | length == 0 %}
|
|
||||||
<div class="text-right mt-6">
|
|
||||||
<a href="/cox/remove/trip/{{ trip.id }}" class="inline-block btn btn-alert">
|
|
||||||
{% include "includes/delete-icon" %}
|
|
||||||
Termin löschen
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Edit Form --- #}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{# --- END Sidebar Content --- #}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Trips --- #}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# --- START Add Buttons --- #}
|
{% endblock content%}
|
||||||
{% if "admin" in loggedin_user.roles or "cox" in loggedin_user.roles %}
|
|
||||||
<div class="grid {% if "admin" in loggedin_user.roles %} grid-cols-2 {% endif %} text-center">
|
|
||||||
{% if "admin" in loggedin_user.roles %}
|
|
||||||
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>Event</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen" data-day="{{ day.day }}" data-body="#addEventForm" class="relative inline-block w-full bg-primary-900 hover:bg-primary-950 focus:bg-primary-950 dark:bg-primary-950 text-white py-2 rounded-bl-md text-sm font-semibold">
|
|
||||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
|
|
||||||
{% include "includes/plus-icon" %}
|
|
||||||
</span>
|
|
||||||
Event
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if "cox" in loggedin_user.roles %}
|
|
||||||
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>Ausfahrt</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen" data-day="{{ day.day }}" data-body="#sidebarForm" class="relative inline-block w-full py-2 text-primary-900 hover:text-primary-950 dark:bg-primary-600 dark:text-white dark:hover:bg-primary-500 dark:hover:text-white focus:text-primary-950 text-sm font-semibold bg-gray-100 hover:bg-gray-200 focus:bg-gray-200 {% if "admin" in loggedin_user.roles %} rounded-br-md {% else %} rounded-b-md {% endif %}">
|
|
||||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
|
|
||||||
{% include "includes/plus-icon" %}
|
|
||||||
</span>
|
|
||||||
Ausfahrt
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{# --- END Add Buttons --- #}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if "cox" in loggedin_user.roles %}
|
|
||||||
{% include "forms/trip" %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if "admin" in loggedin_user.roles %}
|
|
||||||
{% include "forms/event" %}
|
|
||||||
{% endif %}{% endblock content %}
|
|
||||||
|
294
templates/planned.html.tera
Normal file
294
templates/planned.html.tera
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
{% import "includes/macros" as macros %}
|
||||||
|
|
||||||
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-screen-xl w-full grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{% if flash %}
|
||||||
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten</h1>
|
||||||
|
|
||||||
|
{% include "includes/buttons" %}
|
||||||
|
|
||||||
|
{% for day in days %}
|
||||||
|
{% set amount_trips = day.planned_events | length + day.trips | length %}
|
||||||
|
{% set_global day_cox_needed = false %}
|
||||||
|
{% if day.planned_events | length > 0 %}
|
||||||
|
{% for planned_event in day.planned_events %}
|
||||||
|
{% if planned_event.cox_needed %}
|
||||||
|
{% set_global day_cox_needed = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="bg-white dark:bg-primary-900 rounded-md flex justify-between flex-col shadow reset-js" style="min-height: 10rem;" data-trips="{{ amount_trips }}" data-month="{{ day.day| date(format='%m') }}" data-coxneeded="{{ day_cox_needed }}">
|
||||||
|
<div>
|
||||||
|
<h2 class="font-bold uppercase tracking-wide text-center rounded-t-md {% if day.is_pinned %} text-white bg-primary-950 {% else %} text-primary-950 dark:text-white bg-gray-200 dark:bg-primary-950 bg-opacity-80 {% endif %} text-lg px-3 py-3 ">{{ day.day| date(format="%d.%m.%Y") }}
|
||||||
|
<small class="inline-block ml-1 text-xs {% if day.is_pinned %} text-gray-200 {% else %} text-gray-500 dark:text-gray-100 {% endif %}">{{ day.day | date(format="%A", locale="de_AT") }}</small>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{% if day.planned_events | length > 0 or day.trips | length > 0 %}
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-1 gap-3 mb-3">
|
||||||
|
|
||||||
|
{# --- START Events --- #}
|
||||||
|
{% if day.planned_events | length > 0 %}
|
||||||
|
{% for planned_event in day.planned_events | sort(attribute="planned_starting_time") %}
|
||||||
|
{% set amount_cur_cox = planned_event.cox | length %}
|
||||||
|
{% set amount_cox_missing = planned_event.planned_amount_cox - amount_cur_cox %}
|
||||||
|
<div class="pt-2 px-3 border-t border-gray-200" style="order: {{ planned_event.planned_starting_time | replace(from=":", to="") }}">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div class="mr-1">
|
||||||
|
<strong class="text-primary-900 dark:text-white">
|
||||||
|
{{ planned_event.planned_starting_time }}
|
||||||
|
Uhr
|
||||||
|
</strong>
|
||||||
|
<small class="text-gray-600 dark:text-gray-100">({{ planned_event.name }}{% if planned_event.trip_type %} - {{ planned_event.trip_type.icon | safe }} {{ planned_event.trip_type.name }}{% endif %})</small><br/>
|
||||||
|
|
||||||
|
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>{{ planned_event.planned_starting_time }} Uhr</strong> ({{ planned_event.name }}){% if planned_event.trip_type %}<small class='block'>{{ planned_event.trip_type.desc }}</small>{% endif %}{% if planned_event.notes %}<small class='block'>{{ planned_event.notes }}</small>{% endif %}" data-body="#event{{ planned_event.trip_details_id }}" class="inline-block link-primary mr-3">
|
||||||
|
Details
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-right grid gap-2">
|
||||||
|
{# --- START Row Buttons --- #}
|
||||||
|
{% set_global cur_user_participates = false %}
|
||||||
|
{% for rower in planned_event.rower%}
|
||||||
|
{% if rower.name == loggedin_user.name %}
|
||||||
|
{% set_global cur_user_participates = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if cur_user_participates %}
|
||||||
|
<a href="/planned/remove/{{ planned_event.trip_details_id }}" class="btn btn-attention btn-fw">Abmelden</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if planned_event.max_people > planned_event.rower | length %}
|
||||||
|
{% if cur_user_participates == false %}
|
||||||
|
<a href="/planned/join/{{ planned_event.trip_details_id }}" class="btn btn-primary btn-fw" {% if planned_event.trip_type %} onclick="return confirm('{{ planned_event.trip_type.question }}');" {% endif %}>Mitrudern</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Row Buttons --- #}
|
||||||
|
|
||||||
|
{# --- START Cox Buttons --- #}
|
||||||
|
{% if "cox" in loggedin_user.roles %}
|
||||||
|
{% set_global cur_user_participates = false %}
|
||||||
|
{% for cox in planned_event.cox %}
|
||||||
|
{% if cox.name == loggedin_user.name %}
|
||||||
|
{% set_global cur_user_participates = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if cur_user_participates %}
|
||||||
|
<a href="/cox/remove/{{ planned_event.id }}" class="block btn btn-attention btn-fw">
|
||||||
|
{% include "includes/cox-icon" %}
|
||||||
|
Abmelden
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="/cox/join/{{ planned_event.id }}" class="block btn {% if amount_cox_missing > 0 %} btn-dark {% else %} btn-gray {% endif %} btn-fw" {% if planned_event.trip_type %} onclick="return confirm('{{ planned_event.trip_type.question }}');" {% endif %}>
|
||||||
|
{% include "includes/cox-icon" %}
|
||||||
|
Steuern
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Cox Buttons --- #}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# --- START Sidebar Content --- #}
|
||||||
|
<div class="hidden">
|
||||||
|
<div
|
||||||
|
id="event{{ planned_event.trip_details_id }}">
|
||||||
|
{# --- START List Coxes --- #}
|
||||||
|
{% if planned_event.planned_amount_cox > 0 %}
|
||||||
|
{% if amount_cox_missing > 0 %}
|
||||||
|
{{ macros::box(participants=planned_event.cox, empty_seats=planned_event.planned_amount_cox - amount_cur_cox, header='Noch benötigte Steuerleute:', text='Keine Steuerleute angemeldet') }}
|
||||||
|
{% else %}
|
||||||
|
{{ macros::box(participants=planned_event.cox, empty_seats="", header='Genügend Steuerleute haben sich angemeldet :-)', text='Keine Steuerleute angemeldet') }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END List Coxes --- #}
|
||||||
|
|
||||||
|
{# --- START List Rowers --- #}
|
||||||
|
{% if planned_event.max_people > 0 %}
|
||||||
|
{% set amount_cur_rower = planned_event.rower | length %}
|
||||||
|
{{ macros::box(participants=planned_event.rower, empty_seats=planned_event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=planned_event.trip_details_id, allow_removing="admin" in loggedin_user.roles) }}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END List Rowers --- #}
|
||||||
|
|
||||||
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
<form action="/join/{{ planned_event.trip_details_id }}" method="get" />
|
||||||
|
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
|
||||||
|
<input value="Gast hinzufügen" class="btn btn-primary w-full rounded-t-none-important" type="submit"/>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if planned_event.allow_guests %}
|
||||||
|
<div class="text-primary-900 bg-primary-50 text-center p-1 mb-4">Gäste willkommen!</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
|
||||||
|
{# --- START Edit Form --- #}
|
||||||
|
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
|
||||||
|
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3>
|
||||||
|
<form action="/admin/planned-event" method="post" class="grid gap-3">
|
||||||
|
<input type="hidden" name="_method" value="put"/>
|
||||||
|
<input type="hidden" name="id" value="{{ planned_event.id }}"/>
|
||||||
|
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=planned_event.max_people, min='0') }}
|
||||||
|
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=planned_event.planned_amount_cox, required=true, min='0') }}
|
||||||
|
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=planned_event.id,checked=planned_event.always_show) }}
|
||||||
|
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=planned_event.id,checked=planned_event.is_locked) }}
|
||||||
|
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=planned_event.notes) }}
|
||||||
|
|
||||||
|
<input value="Speichern" class="btn btn-primary" type="submit"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{# --- END Edit Form --- #}
|
||||||
|
|
||||||
|
{# --- START Delete Btn --- #}
|
||||||
|
<div class="text-right">
|
||||||
|
<a href="/admin/planned-event/{{ planned_event.id }}/delete" class="inline-block btn btn-alert">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Termin löschen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Delete Btn --- #}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# --- END Sidebar Content --- #}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Events --- #}
|
||||||
|
|
||||||
|
{# --- START Trips --- #}
|
||||||
|
{% if day.trips | length > 0 %}
|
||||||
|
{% for trip in day.trips | sort(attribute="planned_starting_time") %}
|
||||||
|
<div class="pt-2 px-3 reset-js border-t border-gray-200" style="order: {{ trip.planned_starting_time | replace(from=":", to="") }}" data-coxneeded="false">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div class="mr-1">
|
||||||
|
{% if trip.max_people == 0 %}
|
||||||
|
<strong class="text-[#f43f5e]">⚠
|
||||||
|
{{ trip.planned_starting_time }}
|
||||||
|
Uhr</strong>
|
||||||
|
<small class="text-[#f43f5e]">(Absage
|
||||||
|
{{ trip.cox_name }}
|
||||||
|
{% if trip.trip_type %}
|
||||||
|
-
|
||||||
|
{{ trip.trip_type.icon | safe }}{{ trip.trip_type.name }}
|
||||||
|
{% endif %})</small>
|
||||||
|
{% else %}
|
||||||
|
<strong class="text-primary-900 dark:text-white">{{ trip.planned_starting_time }}
|
||||||
|
Uhr</strong>
|
||||||
|
<small class="text-gray-600 dark:text-gray-100">({{ trip.cox_name }}{% if trip.trip_type %} - {{ trip.trip_type.icon | safe }} {{ trip.trip_type.name }}{% endif %})</small>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>{% if trip.max_people == 0 %}⚠ {% endif %}{{ trip.planned_starting_time }} Uhr</strong> ({{ trip.cox_name }}){% if trip.trip_type %}<small class='block'>{{ trip.trip_type.desc }}</small>{% endif %}{% if trip.notes %}<small class='block'>{{ trip.notes }}</small>{% endif %}" data-body="#trip{{ trip.trip_details_id }}" class="inline-block link-primary mr-3">
|
||||||
|
Details
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% set_global cur_user_participates = false %}
|
||||||
|
{% for rower in trip.rower %}
|
||||||
|
{% if rower.name == loggedin_user.name %}
|
||||||
|
{% set_global cur_user_participates = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if cur_user_participates %}
|
||||||
|
<a href="/remove/{{ trip.trip_details_id }}" class="btn btn-attention btn-fw">Abmelden</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if trip.max_people > trip.rower | length and trip.cox_id != loggedin_user.id and cur_user_participates == false%}
|
||||||
|
<a href="/join/{{ trip.trip_details_id }}" class="btn btn-primary btn-fw" {% if trip.trip_type %} onclick="return confirm('{{ trip.trip_type.question }}');" {% endif %}>Mitrudern</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# --- START Sidebar Content --- #}
|
||||||
|
<div class="hidden">
|
||||||
|
<div id="trip{{ trip.trip_details_id }}">
|
||||||
|
{% if trip.max_people == 0 %}
|
||||||
|
{# --- border-[#f43f5e] bg-[#f43f5e] --- #}
|
||||||
|
{{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage') }}
|
||||||
|
{% else %}
|
||||||
|
{% set amount_cur_rower = trip.rower | length %}
|
||||||
|
{{ macros::box(participants=trip.rower, empty_seats=trip.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }}
|
||||||
|
{% if trip.cox_id == loggedin_user.id %}
|
||||||
|
<form action="/join/{{ trip.trip_details_id }}" method="get" />
|
||||||
|
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
|
||||||
|
<input value="Gast hinzufügen" class="btn btn-primary w-full rounded-t-none-important" type="submit"/>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# --- START Edit Form --- #}
|
||||||
|
{% if trip.cox_id == loggedin_user.id %}
|
||||||
|
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
|
||||||
|
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3>
|
||||||
|
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid gap-3">
|
||||||
|
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=trip.max_people, min='0') }}
|
||||||
|
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=trip.notes) }}
|
||||||
|
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=trip.id,checked=trip.always_show) }}
|
||||||
|
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }}
|
||||||
|
{{ macros::select(label='Typ', name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=trip.trip_type_id) }}
|
||||||
|
|
||||||
|
<input value="Speichern" class="btn btn-primary" type="submit"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% if trip.rower | length == 0 %}
|
||||||
|
<div class="text-right mt-6">
|
||||||
|
<a href="/cox/remove/trip/{{ trip.id }}" class="inline-block btn btn-alert">
|
||||||
|
{% include "includes/delete-icon" %}
|
||||||
|
Termin löschen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Edit Form --- #}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# --- END Sidebar Content --- #}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Trips --- #}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# --- START Add Buttons --- #}
|
||||||
|
{% if "admin" in loggedin_user.roles or "cox" in loggedin_user.roles %}
|
||||||
|
<div class="grid {% if "admin" in loggedin_user.roles %} grid-cols-2 {% endif %} text-center">
|
||||||
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>Event</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen" data-day="{{ day.day }}" data-body="#addEventForm" class="relative inline-block w-full bg-primary-900 hover:bg-primary-950 focus:bg-primary-950 dark:bg-primary-950 text-white py-2 rounded-bl-md text-sm font-semibold">
|
||||||
|
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
|
{% include "includes/plus-icon" %}
|
||||||
|
</span>
|
||||||
|
Event
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if "cox" in loggedin_user.roles %}
|
||||||
|
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>Ausfahrt</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen" data-day="{{ day.day }}" data-body="#sidebarForm" class="relative inline-block w-full py-2 text-primary-900 hover:text-primary-950 dark:bg-primary-600 dark:text-white dark:hover:bg-primary-500 dark:hover:text-white focus:text-primary-950 text-sm font-semibold bg-gray-100 hover:bg-gray-200 focus:bg-gray-200 {% if "admin" in loggedin_user.roles %} rounded-br-md {% else %} rounded-b-md {% endif %}">
|
||||||
|
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
|
{% include "includes/plus-icon" %}
|
||||||
|
</span>
|
||||||
|
Ausfahrt
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{# --- END Add Buttons --- #}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if "cox" in loggedin_user.roles %}
|
||||||
|
{% include "forms/trip" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
{% include "forms/event" %}
|
||||||
|
{% endif %}{% endblock content %}
|
Loading…
Reference in New Issue
Block a user