add boatdamages functionality
This commit is contained in:
		| @@ -5,6 +5,7 @@ CREATE TABLE IF NOT EXISTS "user" ( | |||||||
| 	"is_cox" boolean NOT NULL DEFAULT FALSE, | 	"is_cox" boolean NOT NULL DEFAULT FALSE, | ||||||
| 	"is_admin" boolean NOT NULL DEFAULT FALSE, | 	"is_admin" boolean NOT NULL DEFAULT FALSE, | ||||||
| 	"is_guest" boolean NOT NULL DEFAULT TRUE, | 	"is_guest" boolean NOT NULL DEFAULT TRUE, | ||||||
|  | 	"is_tech" boolean NOT NULL DEFAULT FALSE, | ||||||
| 	"deleted" boolean NOT NULL DEFAULT FALSE, | 	"deleted" boolean NOT NULL DEFAULT FALSE, | ||||||
| 	"last_access" DATETIME | 	"last_access" DATETIME | ||||||
| ); | ); | ||||||
| @@ -112,11 +113,11 @@ CREATE TABLE IF NOT EXISTS "boat_damage" ( | |||||||
| 	"boat_id" INTEGER NOT NULL REFERENCES boat(id),  | 	"boat_id" INTEGER NOT NULL REFERENCES boat(id),  | ||||||
| 	"desc" text not null, | 	"desc" text not null, | ||||||
| 	"user_id_created" INTEGER NOT NULL REFERENCES user(id),  | 	"user_id_created" INTEGER NOT NULL REFERENCES user(id),  | ||||||
| 	"created_at" text not null default CURRENT_TIMESTAMP, | 	"created_at" datetime not null default CURRENT_TIMESTAMP, | ||||||
| 	"user_id_fixed" INTEGER REFERENCES user(id),  -- none: not fixed yet | 	"user_id_fixed" INTEGER REFERENCES user(id),  -- none: not fixed yet | ||||||
| 	"fixed_at" text, | 	"fixed_at" datetime, | ||||||
| 	"user_id_verified" INTEGER REFERENCES user(id),  | 	"user_id_verified" INTEGER REFERENCES user(id),  | ||||||
| 	"verified_at" text, | 	"verified_at" datetime, | ||||||
| 	"lock_boat" boolean not null default false -- if true: noone can use the boat  | 	"lock_boat" boolean not null default false -- if true: noone can use the boat  | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										167
									
								
								src/model/boatdamage.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/model/boatdamage.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | use crate::model::{boat::Boat, user::User}; | ||||||
|  | use chrono::NaiveDateTime; | ||||||
|  | use rocket::serde::{Deserialize, Serialize}; | ||||||
|  | use rocket::FromForm; | ||||||
|  | use sqlx::{FromRow, SqlitePool}; | ||||||
|  |  | ||||||
|  | #[derive(FromRow, Debug, Serialize, Deserialize)] | ||||||
|  | pub struct BoatDamage { | ||||||
|  |     pub id: i64, | ||||||
|  |     pub boat_id: i64, | ||||||
|  |     pub desc: String, | ||||||
|  |     pub user_id_created: i64, | ||||||
|  |     pub created_at: NaiveDateTime, | ||||||
|  |     pub user_id_fixed: Option<i64>, | ||||||
|  |     pub fixed_at: Option<NaiveDateTime>, | ||||||
|  |     pub user_id_verified: Option<i64>, | ||||||
|  |     pub verified_at: Option<NaiveDateTime>, | ||||||
|  |     pub lock_boat: bool, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromRow, Debug, Serialize, Deserialize)] | ||||||
|  | pub struct BoatDamageWithDetails { | ||||||
|  |     #[serde(flatten)] | ||||||
|  |     boat_damage: BoatDamage, | ||||||
|  |     user_created: User, | ||||||
|  |     user_fixed: Option<User>, | ||||||
|  |     user_verified: Option<User>, | ||||||
|  |     boat: Boat, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct BoatDamageToAdd<'r> { | ||||||
|  |     pub boat_id: i64, | ||||||
|  |     pub desc: &'r str, | ||||||
|  |     pub user_id_created: i32, | ||||||
|  |     pub lock_boat: bool, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromForm)] | ||||||
|  | pub struct BoatDamageFixed<'r> { | ||||||
|  |     pub desc: &'r str, | ||||||
|  |     pub user_id_fixed: i32, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromForm)] | ||||||
|  | pub struct BoatDamageVerified<'r> { | ||||||
|  |     pub desc: &'r str, | ||||||
|  |     pub user_id_verified: i32, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl BoatDamage { | ||||||
|  |     pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> { | ||||||
|  |         sqlx::query_as!( | ||||||
|  |             Self, | ||||||
|  |             "SELECT id, boat_id, desc, user_id_created, created_at, user_id_fixed, fixed_at, user_id_verified, verified_at, lock_boat  | ||||||
|  |              FROM boat_damage | ||||||
|  |              WHERE id like ?", | ||||||
|  |             id | ||||||
|  |         ) | ||||||
|  |         .fetch_one(db) | ||||||
|  |         .await | ||||||
|  |         .ok() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn all(db: &SqlitePool) -> Vec<BoatDamageWithDetails> { | ||||||
|  |         let boatdamages = sqlx::query_as!( | ||||||
|  |             BoatDamage, | ||||||
|  |             " | ||||||
|  | SELECT id, boat_id, desc, user_id_created, created_at, user_id_fixed, fixed_at, user_id_verified, verified_at, lock_boat  | ||||||
|  | FROM boat_damage | ||||||
|  | ORDER BY created_at DESC | ||||||
|  |         " | ||||||
|  |         ) | ||||||
|  |         .fetch_all(db) | ||||||
|  |         .await | ||||||
|  |         .unwrap(); //TODO: fixme | ||||||
|  |  | ||||||
|  |         let mut res = Vec::new(); | ||||||
|  |         for boat_damage in boatdamages { | ||||||
|  |             let user_fixed = match boat_damage.user_id_fixed { | ||||||
|  |                 Some(id) => { | ||||||
|  |                     let user = User::find_by_id(db, id as i32).await; | ||||||
|  |                     Some(user.unwrap()) | ||||||
|  |                 } | ||||||
|  |                 None => None, | ||||||
|  |             }; | ||||||
|  |             let user_verified = match boat_damage.user_id_verified { | ||||||
|  |                 Some(id) => { | ||||||
|  |                     let user = User::find_by_id(db, id as i32).await; | ||||||
|  |                     Some(user.unwrap()) | ||||||
|  |                 } | ||||||
|  |                 None => None, | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             res.push(BoatDamageWithDetails { | ||||||
|  |                 boat: Boat::find_by_id(db, boat_damage.boat_id as i32) | ||||||
|  |                     .await | ||||||
|  |                     .unwrap(), | ||||||
|  |                 user_created: User::find_by_id(db, boat_damage.user_id_created as i32) | ||||||
|  |                     .await | ||||||
|  |                     .unwrap(), | ||||||
|  |                 user_fixed, | ||||||
|  |                 user_verified, | ||||||
|  |                 boat_damage, | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |         res | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn create(db: &SqlitePool, boatdamage: BoatDamageToAdd<'_>) -> Result<(), String> { | ||||||
|  |         sqlx::query!( | ||||||
|  |             "INSERT INTO boat_damage(boat_id, desc, user_id_created, lock_boat) VALUES (?,?,?, ?)", | ||||||
|  |             boatdamage.boat_id, | ||||||
|  |             boatdamage.desc, | ||||||
|  |             boatdamage.user_id_created, | ||||||
|  |             boatdamage.lock_boat | ||||||
|  |         ) | ||||||
|  |         .execute(db) | ||||||
|  |         .await | ||||||
|  |         .map_err(|e| e.to_string())?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn fixed(&self, db: &SqlitePool, boat: BoatDamageFixed<'_>) -> Result<(), String> { | ||||||
|  |         sqlx::query!( | ||||||
|  |             "UPDATE boat_damage SET desc=?, user_id_fixed=?, fixed_at=CURRENT_TIMESTAMP WHERE id=?", | ||||||
|  |             boat.desc, | ||||||
|  |             boat.user_id_fixed, | ||||||
|  |             self.id | ||||||
|  |         ) | ||||||
|  |         .execute(db) | ||||||
|  |         .await | ||||||
|  |         .map_err(|e| e.to_string())?; | ||||||
|  |  | ||||||
|  |         let user = User::find_by_id(db, boat.user_id_fixed).await.unwrap(); | ||||||
|  |         if user.is_tech { | ||||||
|  |             return self | ||||||
|  |                 .verified( | ||||||
|  |                     db, | ||||||
|  |                     BoatDamageVerified { | ||||||
|  |                         desc: boat.desc, | ||||||
|  |                         user_id_verified: user.id as i32, | ||||||
|  |                     }, | ||||||
|  |                 ) | ||||||
|  |                 .await; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn verified( | ||||||
|  |         &self, | ||||||
|  |         db: &SqlitePool, | ||||||
|  |         boat: BoatDamageVerified<'_>, | ||||||
|  |     ) -> Result<(), String> { | ||||||
|  |         //TODO: Check if user is allowed to verify | ||||||
|  |  | ||||||
|  |         sqlx::query!( | ||||||
|  |             "UPDATE boat_damage SET desc=?, user_id_verified=?, verified_at=CURRENT_TIMESTAMP WHERE id=?", | ||||||
|  |             boat.desc, | ||||||
|  |             boat.user_id_verified, | ||||||
|  |             self.id | ||||||
|  |         ) | ||||||
|  |         .execute(db) | ||||||
|  |         .await.map_err(|e| e.to_string())?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ use self::{ | |||||||
| }; | }; | ||||||
|  |  | ||||||
| pub mod boat; | pub mod boat; | ||||||
|  | pub mod boatdamage; | ||||||
| pub mod location; | pub mod location; | ||||||
| pub mod log; | pub mod log; | ||||||
| pub mod logbook; | pub mod logbook; | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ impl Rower { | |||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             User, |             User, | ||||||
|             " |             " | ||||||
| SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access | SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech | ||||||
| FROM user | FROM user | ||||||
| WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?) | WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?) | ||||||
|         ", |         ", | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ pub struct User { | |||||||
|     pub is_cox: bool, |     pub is_cox: bool, | ||||||
|     pub is_admin: bool, |     pub is_admin: bool, | ||||||
|     pub is_guest: bool, |     pub is_guest: bool, | ||||||
|  |     pub is_tech: bool, | ||||||
|     pub deleted: bool, |     pub deleted: bool, | ||||||
|     pub last_access: Option<chrono::NaiveDateTime>, |     pub last_access: Option<chrono::NaiveDateTime>, | ||||||
| } | } | ||||||
| @@ -41,6 +42,7 @@ pub enum LoginError { | |||||||
|     NotLoggedIn, |     NotLoggedIn, | ||||||
|     NotAnAdmin, |     NotAnAdmin, | ||||||
|     NotACox, |     NotACox, | ||||||
|  |     NotATech, | ||||||
|     NoPasswordSet(User), |     NoPasswordSet(User), | ||||||
|     DeserializationError, |     DeserializationError, | ||||||
| } | } | ||||||
| @@ -73,7 +75,7 @@ impl User { | |||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             Self, |             Self, | ||||||
|             " |             " | ||||||
| SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access | SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech | ||||||
| FROM user  | FROM user  | ||||||
| WHERE id like ? | WHERE id like ? | ||||||
|         ", |         ", | ||||||
| @@ -88,7 +90,7 @@ WHERE id like ? | |||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             Self, |             Self, | ||||||
|             " |             " | ||||||
| SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access | SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech | ||||||
| FROM user  | FROM user  | ||||||
| WHERE name like ? | WHERE name like ? | ||||||
|         ", |         ", | ||||||
| @@ -130,7 +132,7 @@ WHERE name like ? | |||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             Self, |             Self, | ||||||
|             " |             " | ||||||
| SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access | SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech | ||||||
| FROM user | FROM user | ||||||
| WHERE deleted = 0 | WHERE deleted = 0 | ||||||
| ORDER BY last_access DESC | ORDER BY last_access DESC | ||||||
| @@ -145,7 +147,7 @@ ORDER BY last_access DESC | |||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             Self, |             Self, | ||||||
|             " |             " | ||||||
| SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access | SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech | ||||||
| FROM user | FROM user | ||||||
| WHERE deleted = 0 AND is_cox=true | WHERE deleted = 0 AND is_cox=true | ||||||
| ORDER BY last_access DESC | ORDER BY last_access DESC | ||||||
| @@ -167,12 +169,20 @@ ORDER BY last_access DESC | |||||||
|         .is_ok() |         .is_ok() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub async fn update(&self, db: &SqlitePool, is_cox: bool, is_admin: bool, is_guest: bool) { |     pub async fn update( | ||||||
|  |         &self, | ||||||
|  |         db: &SqlitePool, | ||||||
|  |         is_cox: bool, | ||||||
|  |         is_admin: bool, | ||||||
|  |         is_guest: bool, | ||||||
|  |         is_tech: bool, | ||||||
|  |     ) { | ||||||
|         sqlx::query!( |         sqlx::query!( | ||||||
|             "UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ? where id = ?", |             "UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ?, is_tech = ? where id = ?", | ||||||
|             is_cox, |             is_cox, | ||||||
|             is_admin, |             is_admin, | ||||||
|             is_guest, |             is_guest, | ||||||
|  |             is_tech, | ||||||
|             self.id |             self.id | ||||||
|         ) |         ) | ||||||
|         .execute(db) |         .execute(db) | ||||||
| @@ -325,6 +335,46 @@ impl<'r> FromRequest<'r> for User { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub struct TechUser { | ||||||
|  |     pub(crate) user: User, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Deref for TechUser { | ||||||
|  |     type Target = User; | ||||||
|  |  | ||||||
|  |     fn deref(&self) -> &Self::Target { | ||||||
|  |         &self.user | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<User> for TechUser { | ||||||
|  |     type Error = LoginError; | ||||||
|  |  | ||||||
|  |     fn try_from(user: User) -> Result<Self, Self::Error> { | ||||||
|  |         if user.is_tech { | ||||||
|  |             Ok(TechUser { user }) | ||||||
|  |         } else { | ||||||
|  |             Err(LoginError::NotATech) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[async_trait] | ||||||
|  | impl<'r> FromRequest<'r> for TechUser { | ||||||
|  |     type Error = LoginError; | ||||||
|  |  | ||||||
|  |     async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { | ||||||
|  |         match User::from_request(req).await { | ||||||
|  |             Outcome::Success(user) => match user.try_into() { | ||||||
|  |                 Ok(user) => Outcome::Success(user), | ||||||
|  |                 Err(_) => Outcome::Failure((Status::Unauthorized, LoginError::NotACox)), | ||||||
|  |             }, | ||||||
|  |             Outcome::Failure(f) => Outcome::Failure(f), | ||||||
|  |             Outcome::Forward(f) => Outcome::Forward(f), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| pub struct CoxUser { | pub struct CoxUser { | ||||||
|     user: User, |     user: User, | ||||||
| } | } | ||||||
| @@ -469,7 +519,7 @@ mod test { | |||||||
|         let pool = testdb!(); |         let pool = testdb!(); | ||||||
|  |  | ||||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); |         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||||
|         user.update(&pool, false, false, false).await; |         user.update(&pool, false, false, false, false).await; | ||||||
|  |  | ||||||
|         let user = User::find_by_id(&pool, 1).await.unwrap(); |         let user = User::find_by_id(&pool, 1).await.unwrap(); | ||||||
|         assert_eq!(user.is_admin, false); |         assert_eq!(user.is_admin, false); | ||||||
|   | |||||||
| @@ -63,6 +63,7 @@ struct UserEditForm { | |||||||
|     is_guest: bool, |     is_guest: bool, | ||||||
|     is_cox: bool, |     is_cox: bool, | ||||||
|     is_admin: bool, |     is_admin: bool, | ||||||
|  |     is_tech: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[post("/user", data = "<data>")] | #[post("/user", data = "<data>")] | ||||||
| @@ -73,13 +74,13 @@ async fn update( | |||||||
| ) -> Flash<Redirect> { | ) -> Flash<Redirect> { | ||||||
|     let user = User::find_by_id(db, data.id).await; |     let user = User::find_by_id(db, data.id).await; | ||||||
|     let Some(user) = user else { |     let Some(user) = user else { | ||||||
|             return Flash::error( |         return Flash::error( | ||||||
|                 Redirect::to("/admin/user"), |             Redirect::to("/admin/user"), | ||||||
|                 format!("User with ID {} does not exist!", data.id), |             format!("User with ID {} does not exist!", data.id), | ||||||
|             ) |         ); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     user.update(db, data.is_cox, data.is_admin, data.is_guest) |     user.update(db, data.is_cox, data.is_admin, data.is_guest, data.is_tech) | ||||||
|         .await; |         .await; | ||||||
|  |  | ||||||
|     Flash::success(Redirect::to("/admin/user"), "Successfully updated user") |     Flash::success(Redirect::to("/admin/user"), "Successfully updated user") | ||||||
|   | |||||||
							
								
								
									
										114
									
								
								src/tera/boatdamage.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/tera/boatdamage.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | |||||||
|  | use rocket::{ | ||||||
|  |     form::Form, | ||||||
|  |     get, post, | ||||||
|  |     request::FlashMessage, | ||||||
|  |     response::{Flash, Redirect}, | ||||||
|  |     routes, FromForm, Route, State, | ||||||
|  | }; | ||||||
|  | use rocket_dyn_templates::Template; | ||||||
|  | use sqlx::SqlitePool; | ||||||
|  | use tera::Context; | ||||||
|  |  | ||||||
|  | use crate::model::{ | ||||||
|  |     boat::Boat, | ||||||
|  |     boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified}, | ||||||
|  |     user::{CoxUser, TechUser, User}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #[get("/")] | ||||||
|  | async fn index(db: &State<SqlitePool>, flash: Option<FlashMessage<'_>>, user: User) -> Template { | ||||||
|  |     let boatdamages = BoatDamage::all(db).await; | ||||||
|  |     let boats = Boat::all(db).await; | ||||||
|  |  | ||||||
|  |     let mut context = Context::new(); | ||||||
|  |     if let Some(msg) = flash { | ||||||
|  |         context.insert("flash", &msg.into_inner()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     context.insert("boatdamages", &boatdamages); | ||||||
|  |     context.insert("boats", &boats); | ||||||
|  |     context.insert("loggedin_user", &user); | ||||||
|  |  | ||||||
|  |     Template::render("boatdamages", context.into_json()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromForm)] | ||||||
|  | pub struct FormBoatDamageToAdd<'r> { | ||||||
|  |     pub boat_id: i64, | ||||||
|  |     pub desc: &'r str, | ||||||
|  |     pub lock_boat: bool, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[post("/", data = "<data>")] | ||||||
|  | async fn create<'r>( | ||||||
|  |     db: &State<SqlitePool>, | ||||||
|  |     data: Form<FormBoatDamageToAdd<'r>>, | ||||||
|  |     coxuser: CoxUser, | ||||||
|  | ) -> Flash<Redirect> { | ||||||
|  |     let boatdamage_to_add = BoatDamageToAdd { | ||||||
|  |         boat_id: data.boat_id, | ||||||
|  |         desc: data.desc, | ||||||
|  |         lock_boat: data.lock_boat, | ||||||
|  |         user_id_created: coxuser.id as i32, | ||||||
|  |     }; | ||||||
|  |     match BoatDamage::create(db, boatdamage_to_add).await { | ||||||
|  |         Ok(_) => Flash::success( | ||||||
|  |             Redirect::to("/boatdamage"), | ||||||
|  |             "Ausfahrt erfolgreich hinzugefügt", | ||||||
|  |         ), | ||||||
|  |         Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromForm)] | ||||||
|  | pub struct FormBoatDamageFixed<'r> { | ||||||
|  |     pub desc: &'r str, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[post("/<boatdamage_id>/fixed", data = "<data>")] | ||||||
|  | async fn fixed<'r>( | ||||||
|  |     db: &State<SqlitePool>, | ||||||
|  |     data: Form<FormBoatDamageFixed<'r>>, | ||||||
|  |     boatdamage_id: i32, | ||||||
|  |     coxuser: CoxUser, | ||||||
|  | ) -> Flash<Redirect> { | ||||||
|  |     let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix | ||||||
|  |     let boatdamage_fixed = BoatDamageFixed { | ||||||
|  |         desc: data.desc, | ||||||
|  |         user_id_fixed: coxuser.id as i32, | ||||||
|  |     }; | ||||||
|  |     match boatdamage.fixed(db, boatdamage_fixed).await { | ||||||
|  |         Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Successfully fixed the boat."), | ||||||
|  |         Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(FromForm)] | ||||||
|  | pub struct FormBoatDamageVerified<'r> { | ||||||
|  |     pub desc: &'r str, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[post("/<boatdamage_id>/verified", data = "<data>")] | ||||||
|  | async fn verified<'r>( | ||||||
|  |     db: &State<SqlitePool>, | ||||||
|  |     data: Form<FormBoatDamageFixed<'r>>, | ||||||
|  |     boatdamage_id: i32, | ||||||
|  |     techuser: TechUser, | ||||||
|  | ) -> Flash<Redirect> { | ||||||
|  |     let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix | ||||||
|  |     let boatdamage_verified = BoatDamageVerified { | ||||||
|  |         desc: data.desc, | ||||||
|  |         user_id_verified: techuser.id as i32, | ||||||
|  |     }; | ||||||
|  |     match boatdamage.verified(db, boatdamage_verified).await { | ||||||
|  |         Ok(_) => Flash::success( | ||||||
|  |             Redirect::to("/boatdamage"), | ||||||
|  |             "Successfully verified the boat.", | ||||||
|  |         ), | ||||||
|  |         Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn routes() -> Vec<Route> { | ||||||
|  |     routes![index, create, fixed, verified] | ||||||
|  | } | ||||||
| @@ -21,6 +21,7 @@ use crate::model::{ | |||||||
|  |  | ||||||
| mod admin; | mod admin; | ||||||
| mod auth; | mod auth; | ||||||
|  | mod boatdamage; | ||||||
| mod cox; | mod cox; | ||||||
| mod log; | mod log; | ||||||
| mod misc; | mod misc; | ||||||
| @@ -120,6 +121,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> { | |||||||
|         .mount("/auth", auth::routes()) |         .mount("/auth", auth::routes()) | ||||||
|         .mount("/log", log::routes()) |         .mount("/log", log::routes()) | ||||||
|         .mount("/stat", stat::routes()) |         .mount("/stat", stat::routes()) | ||||||
|  |         .mount("/boatdamage", boatdamage::routes()) | ||||||
|         .mount("/cox", cox::routes()) |         .mount("/cox", cox::routes()) | ||||||
|         .mount("/admin", admin::routes()) |         .mount("/admin", admin::routes()) | ||||||
|         .mount("/", misc::routes()) |         .mount("/", misc::routes()) | ||||||
|   | |||||||
| @@ -51,6 +51,7 @@ | |||||||
| 						<div class="grid md:grid-cols-3"> | 						<div class="grid md:grid-cols-3"> | ||||||
| 							{{ macros::checkbox(label='Gast', name='is_guest', id=loop.index , checked=user.is_guest) }} | 							{{ macros::checkbox(label='Gast', name='is_guest', id=loop.index , checked=user.is_guest) }} | ||||||
| 							{{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }} | 							{{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }} | ||||||
|  | 							{{ macros::checkbox(label='Technical', name='is_tech', id=loop.index , checked=user.is_tech) }} | ||||||
| 							{{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }} | 							{{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }} | ||||||
| 						</div> | 						</div> | ||||||
| 						{% if user.pw %} | 						{% if user.pw %} | ||||||
|   | |||||||
							
								
								
									
										59
									
								
								templates/boatdamages.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								templates/boatdamages.html.tera
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | {% import "includes/macros" as macros %} | ||||||
|  | {% import "includes/forms/log" as log %} | ||||||
|  |  | ||||||
|  | {% extends "base" %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  |  | ||||||
|  | <div class="w-full"> | ||||||
|  |   <h1 class="h1">Bootschäden</h1> | ||||||
|  |  | ||||||
|  |   {% if flash %} | ||||||
|  |     {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} | ||||||
|  |   {% endif %} | ||||||
|  |   <h1>Neuen Schaden eintragen</h1> | ||||||
|  |   <form action="/boatdamage" method="post"> | ||||||
|  |   	{{ log::boat_select(only_ones=false) }} | ||||||
|  |   	{{ macros::input(label='Beschreibung des Schadens', name='desc', type='text', required=true) }} | ||||||
|  | 	<input type="submit" value="Schaden eintragen" /> | ||||||
|  |   </form> | ||||||
|  |  | ||||||
|  |    | ||||||
|  |  | ||||||
|  |   <hr /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   {% for boatdamage in boatdamages %} | ||||||
|  |   	Boat: {{ boatdamage.boat.name }} | ||||||
|  | 	Desc: {{ boatdamage.desc }} | ||||||
|  | 	Schaden eingetragen von {{ boatdamage.user_created.name }} am/um {{ boatdamage.created_at | date(format='%d. %m. %Y %H:%M') }} | ||||||
|  | 	{% if boatdamage.is_lock %} | ||||||
|  | 		Boot gesperrt | ||||||
|  | 	{% endif %} | ||||||
|  | 	{% if boatdamage.fixed_at %} | ||||||
|  | 		Repariert von {{ boatdamage.user_fixed.name }} am/um {{ boatdamage.fixed_at | date(format='%d. %m. %Y %H:%M') }} | ||||||
|  | 	{% elif loggedin_user.is_cox %} | ||||||
|  | 		<form action="/boatdamage/{{ boatdamage.id }}/fixed" method="post"> | ||||||
|  | 			<input type="text" name="desc" value="{{ boatdamage.desc }}" /> | ||||||
|  | 			{% if loggedin_user.is_tech %} | ||||||
|  | 				<input type="submit" value="Ich hab's repariert + verifiziert" /> | ||||||
|  | 			{% else %} | ||||||
|  | 				<input type="submit" value="Ich hab's repariert" /> | ||||||
|  | 			{% endif %} | ||||||
|  | 		</form> | ||||||
|  | 	{% endif %} | ||||||
|  | 	{% if boatdamage.verified_at %} | ||||||
|  | 		Verifziert von {{ boatdamage.user_verified.name }} am/um {{ boatdamage.verified_at | date(format='%d. %m. %Y %H:%M') }} | ||||||
|  | 	{% elif loggedin_user.is_tech and boatdamage.fixed_at %} | ||||||
|  | 		<form action="/boatdamage/{{ boatdamage.id }}/verified" method="post"> | ||||||
|  | 			<input type="text" name="desc" value="{{ boatdamage.desc }}" /> | ||||||
|  | 			<input type="submit" value="Ich hab's verifiziert" /> | ||||||
|  | 		</form> | ||||||
|  | 	{% endif %} | ||||||
|  | 	<hr /> | ||||||
|  |   {% endfor %} | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | {% include "dynamics/sidebar" %} | ||||||
|  | {% endblock content%} | ||||||
| @@ -13,6 +13,13 @@ | |||||||
| 					{% include "includes/question-icon" %} | 					{% include "includes/question-icon" %} | ||||||
| 					<span class="sr-only">FAQs</span> | 					<span class="sr-only">FAQs</span> | ||||||
| 				</a> | 				</a> | ||||||
|  | 				{% if loggedin_user.is_admin%} | ||||||
|  | 					<a href="/boatdamage" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 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"> | ||||||
|  | 						Bootsschaden	 | ||||||
|  | 						<span class="sr-only">Bootsschaden</span> | ||||||
|  | 					</a> | ||||||
|  | 					 | ||||||
|  | 				{% endif %} | ||||||
| 				{% if loggedin_user.is_admin %} | 				{% if loggedin_user.is_admin %} | ||||||
| 					<a href="/stat" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 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"> | 					<a href="/stat" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 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"> | ||||||
| 						STATS | 						STATS | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user