Merge pull request 'boatshouse' (#257) from boatshouse into staging
Reviewed-on: #257
This commit is contained in:
@ -181,6 +181,41 @@ ORDER BY amount_seats DESC
|
||||
Self::boats_to_details(db, boats).await
|
||||
}
|
||||
|
||||
pub async fn all_for_boatshouse(db: &SqlitePool) -> Vec<BoatWithDetails> {
|
||||
let boats = sqlx::query_as!(
|
||||
Boat,
|
||||
"
|
||||
SELECT
|
||||
b.id,
|
||||
b.name,
|
||||
b.amount_seats,
|
||||
b.location_id,
|
||||
b.owner,
|
||||
b.year_built,
|
||||
b.boatbuilder,
|
||||
b.default_shipmaster_only_steering,
|
||||
b.default_destination,
|
||||
b.skull,
|
||||
b.external
|
||||
FROM
|
||||
boat AS b
|
||||
LEFT JOIN
|
||||
boathouse AS bh ON b.id = bh.boat_id
|
||||
WHERE
|
||||
b.external = false
|
||||
AND b.location_id = (SELECT id FROM location WHERE name = 'Linz')
|
||||
AND bh.id IS NULL -- This ensures the boat does not have an entry in the boathouse table
|
||||
ORDER BY
|
||||
b.name DESC;
|
||||
"
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap(); //TODO: fixme
|
||||
|
||||
Self::boats_to_details(db, boats).await
|
||||
}
|
||||
|
||||
pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<BoatWithDetails> {
|
||||
if user.has_role(db, "admin").await {
|
||||
return Self::all(db).await;
|
||||
|
90
src/model/boathouse.rs
Normal file
90
src/model/boathouse.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, SqlitePool};
|
||||
|
||||
use crate::tera::board::boathouse::FormBoathouseToAdd;
|
||||
|
||||
use super::boat::Boat;
|
||||
|
||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
||||
pub struct Boathouse {
|
||||
pub id: i64,
|
||||
pub boat_id: i64,
|
||||
pub aisle: String,
|
||||
pub side: String,
|
||||
pub level: i64,
|
||||
}
|
||||
|
||||
impl Boathouse {
|
||||
pub async fn get(db: &SqlitePool) -> HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 4]>> {
|
||||
let mut ret: HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 4]>> = HashMap::new();
|
||||
|
||||
let mut mountain = HashMap::new();
|
||||
mountain.insert("mountain", [None, None, None, None]);
|
||||
mountain.insert("water", [None, None, None, None]);
|
||||
ret.insert("mountain-aisle", mountain);
|
||||
|
||||
let mut middle = HashMap::new();
|
||||
middle.insert("mountain", [None, None, None, None]);
|
||||
middle.insert("water", [None, None, None, None]);
|
||||
ret.insert("middle-aisle", middle);
|
||||
|
||||
let mut water = HashMap::new();
|
||||
water.insert("mountain", [None, None, None, None]);
|
||||
water.insert("water", [None, None, None, None]);
|
||||
ret.insert("water-aisle", water);
|
||||
|
||||
let boathouses = sqlx::query_as!(
|
||||
Boathouse,
|
||||
"SELECT id, boat_id, aisle, side, level FROM boathouse"
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap(); //TODO: fixme
|
||||
|
||||
for boathouse in boathouses {
|
||||
let aisle = ret
|
||||
.get_mut(format!("{}-aisle", boathouse.aisle).as_str())
|
||||
.unwrap();
|
||||
let side = aisle.get_mut(boathouse.side.as_str()).unwrap();
|
||||
|
||||
side[boathouse.level as usize] = Some((
|
||||
boathouse.id,
|
||||
Boat::find_by_id(db, boathouse.boat_id as i32)
|
||||
.await
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub async fn create(db: &SqlitePool, data: FormBoathouseToAdd) -> Result<(), String> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO boathouse(boat_id, aisle, side, level) VALUES (?,?,?,?)",
|
||||
data.boat_id,
|
||||
data.aisle,
|
||||
data.side,
|
||||
data.level
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
|
||||
sqlx::query_as!(Self, "SELECT * FROM boathouse WHERE id like ?", id)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub async fn delete(&self, db: &SqlitePool) {
|
||||
sqlx::query!("DELETE FROM boathouse WHERE id=?", self.id)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a Boat of a valid id
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use self::{
|
||||
|
||||
pub mod boat;
|
||||
pub mod boatdamage;
|
||||
pub mod boathouse;
|
||||
pub mod family;
|
||||
pub mod location;
|
||||
pub mod log;
|
||||
|
85
src/tera/board/boathouse.rs
Normal file
85
src/tera/board/boathouse.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use crate::model::{
|
||||
boat::Boat,
|
||||
boathouse::Boathouse,
|
||||
user::{AdminUser, UserWithRoles, VorstandUser},
|
||||
};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/boathouse")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
let boats = Boat::all_for_boatshouse(db).await;
|
||||
context.insert("boats", &boats);
|
||||
|
||||
let boathouse = Boathouse::get(db).await;
|
||||
context.insert("boathouse", &boathouse);
|
||||
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(admin.into(), db).await,
|
||||
);
|
||||
|
||||
Template::render("board/boathouse", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoathouseToAdd {
|
||||
pub boat_id: i32,
|
||||
pub aisle: String,
|
||||
pub side: String,
|
||||
pub level: i32,
|
||||
}
|
||||
#[post("/boathouse", data = "<data>")]
|
||||
async fn new<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoathouseToAdd>,
|
||||
_admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
match Boathouse::create(db, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/board/boathouse"), "Boot hinzugefügt"),
|
||||
Err(e) => Flash::error(Redirect::to("/board/boathouse"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/boathouse/<boathouse_id>/delete")]
|
||||
async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boathouse_id: i32) -> Flash<Redirect> {
|
||||
let boat = Boathouse::find_by_id(db, boathouse_id).await;
|
||||
match boat {
|
||||
Some(boat) => {
|
||||
boat.delete(db).await;
|
||||
Flash::success(Redirect::to("/board/boathouse"), "Bootsplatz gelöscht")
|
||||
}
|
||||
None => Flash::error(Redirect::to("/board/boathouse"), "Boatplace does not exist"),
|
||||
}
|
||||
}
|
||||
//#[post("/boat/new", data = "<data>")]
|
||||
//async fn create(
|
||||
// db: &State<SqlitePool>,
|
||||
// data: Form<BoatToAdd<'_>>,
|
||||
// _admin: AdminUser,
|
||||
//) -> Flash<Redirect> {
|
||||
// match Boat::create(db, data.into_inner()).await {
|
||||
// Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot hinzugefügt"),
|
||||
// Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
|
||||
// }
|
||||
//}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, new, delete]
|
||||
}
|
9
src/tera/board/mod.rs
Normal file
9
src/tera/board/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use rocket::Route;
|
||||
|
||||
pub mod boathouse;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut ret = Vec::new();
|
||||
ret.append(&mut boathouse::routes());
|
||||
ret
|
||||
}
|
@ -21,6 +21,7 @@ use crate::model::user::{User, UserWithRoles};
|
||||
|
||||
pub(crate) mod admin;
|
||||
mod auth;
|
||||
pub(crate) mod board;
|
||||
mod boatdamage;
|
||||
mod cox;
|
||||
mod ergo;
|
||||
@ -89,6 +90,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
.mount("/boatdamage", boatdamage::routes())
|
||||
.mount("/cox", cox::routes())
|
||||
.mount("/admin", admin::routes())
|
||||
.mount("/board", board::routes())
|
||||
.mount("/", misc::routes())
|
||||
.mount("/public", FileServer::from("static/"))
|
||||
.register("/", catchers![unauthorized_error, forbidden_error])
|
||||
|
Reference in New Issue
Block a user