Compare commits
No commits in common. "0e2ef9e256bb663f6170baf991f2a7e3c5f25bfe" and "c9b67f5790c10e38cdc1ed0962e3922f6b7f9f56" have entirely different histories.
0e2ef9e256
...
c9b67f5790
@ -195,21 +195,3 @@ CREATE TABLE IF NOT EXISTS "weather" (
|
|||||||
"wind_gust" FLOAT NOT NULL,
|
"wind_gust" FLOAT NOT NULL,
|
||||||
"rain_mm" FLOAT NOT NULL
|
"rain_mm" FLOAT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "trailer" (
|
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"name" text NOT NULL UNIQUE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "trailer_reservation" (
|
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"trailer_id" INTEGER NOT NULL REFERENCES boat(id),
|
|
||||||
"start_date" DATE NOT NULL,
|
|
||||||
"end_date" DATE NOT NULL,
|
|
||||||
"time_desc" TEXT NOT NULL,
|
|
||||||
"usage" TEXT NOT NULL,
|
|
||||||
"user_id_applicant" INTEGER NOT NULL REFERENCES user(id),
|
|
||||||
"user_id_confirmation" INTEGER REFERENCES user(id),
|
|
||||||
"created_at" datetime not null default CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
|
@ -64,5 +64,3 @@ INSERT INTO "rower" (logbook_id, rower_id) VALUES(3,3);
|
|||||||
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02');
|
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02');
|
||||||
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);
|
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);
|
||||||
INSERT INTO "notification" (user_id, message, category) VALUES (1, 'This is a test notification', 'test-cat');
|
INSERT INTO "notification" (user_id, message, category) VALUES (1, 'This is a test notification', 'test-cat');
|
||||||
INSERT INTO "trailer" (name) VALUES('Großer Hänger');
|
|
||||||
INSERT INTO "trailer" (name) VALUES('Kleiner Hänger');
|
|
||||||
|
@ -25,8 +25,6 @@ pub mod notification;
|
|||||||
pub mod role;
|
pub mod role;
|
||||||
pub mod rower;
|
pub mod rower;
|
||||||
pub mod stat;
|
pub mod stat;
|
||||||
pub mod trailer;
|
|
||||||
pub mod trailerreservation;
|
|
||||||
pub mod trip;
|
pub mod trip;
|
||||||
pub mod tripdetails;
|
pub mod tripdetails;
|
||||||
pub mod triptype;
|
pub mod triptype;
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
use std::ops::DerefMut;
|
|
||||||
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
|
|
||||||
pub struct Trailer {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Trailer {
|
|
||||||
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
|
|
||||||
sqlx::query_as!(Self, "SELECT id, name FROM trailer WHERE id like ?", id)
|
|
||||||
.fetch_one(db)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i32) -> Option<Self> {
|
|
||||||
sqlx::query_as!(Self, "SELECT id, name FROM trailer WHERE id like ?", id)
|
|
||||||
.fetch_one(db.deref_mut())
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
pub async fn all(db: &SqlitePool) -> Vec<Self> {
|
|
||||||
sqlx::query_as!(Self, "SELECT id, name FROM trailer")
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,233 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::{FromRow, SqlitePool};
|
|
||||||
|
|
||||||
use super::log::Log;
|
|
||||||
use super::notification::Notification;
|
|
||||||
use super::role::Role;
|
|
||||||
use super::trailer::Trailer;
|
|
||||||
use super::user::User;
|
|
||||||
use crate::tera::trailerreservation::ReservationEditForm;
|
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct TrailerReservation {
|
|
||||||
pub id: i64,
|
|
||||||
pub trailer_id: i64,
|
|
||||||
pub start_date: NaiveDate,
|
|
||||||
pub end_date: NaiveDate,
|
|
||||||
pub time_desc: String,
|
|
||||||
pub usage: String,
|
|
||||||
pub user_id_applicant: i64,
|
|
||||||
pub user_id_confirmation: Option<i64>,
|
|
||||||
pub created_at: NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct TrailerReservationWithDetails {
|
|
||||||
#[serde(flatten)]
|
|
||||||
reservation: TrailerReservation,
|
|
||||||
trailer: Trailer,
|
|
||||||
user_applicant: User,
|
|
||||||
user_confirmation: Option<User>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TrailerReservationToAdd<'r> {
|
|
||||||
pub trailer: &'r Trailer,
|
|
||||||
pub start_date: NaiveDate,
|
|
||||||
pub end_date: NaiveDate,
|
|
||||||
pub time_desc: &'r str,
|
|
||||||
pub usage: &'r str,
|
|
||||||
pub user_applicant: &'r User,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrailerReservation {
|
|
||||||
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
|
|
||||||
sqlx::query_as!(
|
|
||||||
Self,
|
|
||||||
"SELECT id, trailer_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
|
|
||||||
FROM trailer_reservation
|
|
||||||
WHERE id like ?",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
.fetch_one(db)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn all_future(db: &SqlitePool) -> Vec<TrailerReservationWithDetails> {
|
|
||||||
let trailerreservations = sqlx::query_as!(
|
|
||||||
Self,
|
|
||||||
"
|
|
||||||
SELECT id, trailer_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
|
|
||||||
FROM trailer_reservation
|
|
||||||
WHERE end_date >= CURRENT_DATE ORDER BY end_date
|
|
||||||
"
|
|
||||||
)
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
.unwrap(); //TODO: fixme
|
|
||||||
|
|
||||||
let mut res = Vec::new();
|
|
||||||
for reservation in trailerreservations {
|
|
||||||
let user_confirmation = match reservation.user_id_confirmation {
|
|
||||||
Some(id) => {
|
|
||||||
let user = User::find_by_id(db, id as i32).await;
|
|
||||||
Some(user.unwrap())
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let user_applicant = User::find_by_id(db, reservation.user_id_applicant as i32)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let trailer = Trailer::find_by_id(db, reservation.trailer_id as i32)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
res.push(TrailerReservationWithDetails {
|
|
||||||
reservation,
|
|
||||||
trailer,
|
|
||||||
user_applicant,
|
|
||||||
user_confirmation,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
pub async fn all_future_with_groups(
|
|
||||||
db: &SqlitePool,
|
|
||||||
) -> HashMap<String, Vec<TrailerReservationWithDetails>> {
|
|
||||||
let mut grouped_reservations: HashMap<String, Vec<TrailerReservationWithDetails>> =
|
|
||||||
HashMap::new();
|
|
||||||
|
|
||||||
let reservations = Self::all_future(db).await;
|
|
||||||
for reservation in reservations {
|
|
||||||
let key = format!(
|
|
||||||
"{}-{}-{}-{}-{}",
|
|
||||||
reservation.reservation.start_date,
|
|
||||||
reservation.reservation.end_date,
|
|
||||||
reservation.reservation.time_desc,
|
|
||||||
reservation.reservation.usage,
|
|
||||||
reservation.user_applicant.name
|
|
||||||
);
|
|
||||||
|
|
||||||
grouped_reservations
|
|
||||||
.entry(key)
|
|
||||||
.or_default()
|
|
||||||
.push(reservation);
|
|
||||||
}
|
|
||||||
|
|
||||||
grouped_reservations
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create(
|
|
||||||
db: &SqlitePool,
|
|
||||||
trailerreservation: TrailerReservationToAdd<'_>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
if Self::trailer_reserved_between_dates(
|
|
||||||
db,
|
|
||||||
trailerreservation.trailer,
|
|
||||||
&trailerreservation.start_date,
|
|
||||||
&trailerreservation.end_date,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Err("Hänger in diesem Zeitraum bereits reserviert.".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!("New trailer reservation: {trailerreservation:?}"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"INSERT INTO trailer_reservation(trailer_id, start_date, end_date, time_desc, usage, user_id_applicant) VALUES (?,?,?,?,?,?)",
|
|
||||||
trailerreservation.trailer.id,
|
|
||||||
trailerreservation.start_date,
|
|
||||||
trailerreservation.end_date,
|
|
||||||
trailerreservation.time_desc,
|
|
||||||
trailerreservation.usage,
|
|
||||||
trailerreservation.user_applicant.id,
|
|
||||||
)
|
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let board =
|
|
||||||
User::all_with_role(db, &Role::find_by_name(db, "Vorstand").await.unwrap()).await;
|
|
||||||
for user in board {
|
|
||||||
let date = if trailerreservation.start_date == trailerreservation.end_date {
|
|
||||||
format!("am {}", trailerreservation.start_date)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"von {} bis {}",
|
|
||||||
trailerreservation.start_date, trailerreservation.end_date
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Notification::create(
|
|
||||||
db,
|
|
||||||
&user,
|
|
||||||
&format!(
|
|
||||||
"{} hat eine neue Hängerreservierung für Hänger '{}' {} angelegt. Zeit: {}; Zweck: {}",
|
|
||||||
trailerreservation.user_applicant.name,
|
|
||||||
trailerreservation.trailer.name,
|
|
||||||
date,
|
|
||||||
trailerreservation.time_desc,
|
|
||||||
trailerreservation.usage
|
|
||||||
),
|
|
||||||
"Neue Hängerreservierung",
|
|
||||||
None,None
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn trailer_reserved_between_dates(
|
|
||||||
db: &SqlitePool,
|
|
||||||
trailer: &Trailer,
|
|
||||||
start_date: &NaiveDate,
|
|
||||||
end_date: &NaiveDate,
|
|
||||||
) -> bool {
|
|
||||||
sqlx::query!(
|
|
||||||
"SELECT COUNT(*) AS reservation_count
|
|
||||||
FROM trailer_reservation
|
|
||||||
WHERE trailer_id = ?
|
|
||||||
AND start_date <= ? AND end_date >= ?;",
|
|
||||||
trailer.id,
|
|
||||||
end_date,
|
|
||||||
start_date
|
|
||||||
)
|
|
||||||
.fetch_one(db)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.reservation_count
|
|
||||||
> 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update(&self, db: &SqlitePool, data: ReservationEditForm) {
|
|
||||||
let time_desc = data.time_desc.trim();
|
|
||||||
let usage = data.usage.trim();
|
|
||||||
sqlx::query!(
|
|
||||||
"UPDATE trailer_reservation SET time_desc = ?, usage = ? where id = ?",
|
|
||||||
time_desc,
|
|
||||||
usage,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete(&self, db: &SqlitePool) {
|
|
||||||
sqlx::query!("DELETE FROM trailer_reservation WHERE id=?", self.id)
|
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.unwrap(); //Okay, because we can only create a Boat of a valid id
|
|
||||||
}
|
|
||||||
}
|
|
@ -39,7 +39,6 @@ mod misc;
|
|||||||
mod notification;
|
mod notification;
|
||||||
mod planned;
|
mod planned;
|
||||||
mod stat;
|
mod stat;
|
||||||
pub(crate) mod trailerreservation;
|
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
struct LoginForm<'r> {
|
struct LoginForm<'r> {
|
||||||
@ -201,7 +200,6 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
.mount("/stat", stat::routes())
|
.mount("/stat", stat::routes())
|
||||||
.mount("/boatdamage", boatdamage::routes())
|
.mount("/boatdamage", boatdamage::routes())
|
||||||
.mount("/boatreservation", boatreservation::routes())
|
.mount("/boatreservation", boatreservation::routes())
|
||||||
.mount("/trailerreservation", trailerreservation::routes())
|
|
||||||
.mount("/cox", cox::routes())
|
.mount("/cox", cox::routes())
|
||||||
.mount("/admin", admin::routes())
|
.mount("/admin", admin::routes())
|
||||||
.mount("/board", board::routes())
|
.mount("/board", board::routes())
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
use chrono::NaiveDate;
|
|
||||||
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::{
|
|
||||||
log::Log,
|
|
||||||
trailer::Trailer,
|
|
||||||
trailerreservation::{TrailerReservation, TrailerReservationToAdd},
|
|
||||||
user::{DonauLinzUser, User, UserWithDetails},
|
|
||||||
},
|
|
||||||
tera::log::KioskCookie,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
async fn index_kiosk(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
flash: Option<FlashMessage<'_>>,
|
|
||||||
_kiosk: KioskCookie,
|
|
||||||
) -> Template {
|
|
||||||
let trailerreservations = TrailerReservation::all_future(db).await;
|
|
||||||
|
|
||||||
let mut context = Context::new();
|
|
||||||
if let Some(msg) = flash {
|
|
||||||
context.insert("flash", &msg.into_inner());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.insert("trailerreservations", &trailerreservations);
|
|
||||||
context.insert("trailers", &Trailer::all(db).await);
|
|
||||||
context.insert("user", &User::all(db).await);
|
|
||||||
context.insert("show_kiosk_header", &true);
|
|
||||||
|
|
||||||
Template::render("trailerreservations", context.into_json())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
|
||||||
async fn index(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
flash: Option<FlashMessage<'_>>,
|
|
||||||
user: DonauLinzUser,
|
|
||||||
) -> Template {
|
|
||||||
let trailerreservations = TrailerReservation::all_future(db).await;
|
|
||||||
|
|
||||||
let mut context = Context::new();
|
|
||||||
if let Some(msg) = flash {
|
|
||||||
context.insert("flash", &msg.into_inner());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.insert("trailerreservations", &trailerreservations);
|
|
||||||
context.insert("trailers", &Trailer::all(db).await);
|
|
||||||
context.insert("user", &User::all(db).await);
|
|
||||||
context.insert(
|
|
||||||
"loggedin_user",
|
|
||||||
&UserWithDetails::from_user(user.into(), db).await,
|
|
||||||
);
|
|
||||||
|
|
||||||
Template::render("trailerreservations", context.into_json())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, FromForm)]
|
|
||||||
pub struct FormTrailerReservationToAdd<'r> {
|
|
||||||
pub trailer_id: i64,
|
|
||||||
pub start_date: &'r str,
|
|
||||||
pub end_date: &'r str,
|
|
||||||
pub time_desc: &'r str,
|
|
||||||
pub usage: &'r str,
|
|
||||||
pub user_id_applicant: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/new", data = "<data>", rank = 2)]
|
|
||||||
async fn create<'r>(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
data: Form<FormTrailerReservationToAdd<'r>>,
|
|
||||||
user: DonauLinzUser,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let user_applicant: User = user.into();
|
|
||||||
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let trailerreservation_to_add = TrailerReservationToAdd {
|
|
||||||
trailer: &trailer,
|
|
||||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
|
||||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
|
||||||
time_desc: data.time_desc,
|
|
||||||
usage: data.usage,
|
|
||||||
user_applicant: &user_applicant,
|
|
||||||
};
|
|
||||||
match TrailerReservation::create(db, trailerreservation_to_add).await {
|
|
||||||
Ok(_) => Flash::success(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Reservierung erfolgreich hinzugefügt",
|
|
||||||
),
|
|
||||||
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/new", data = "<data>")]
|
|
||||||
async fn create_from_kiosk<'r>(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
data: Form<FormTrailerReservationToAdd<'r>>,
|
|
||||||
_kiosk: KioskCookie,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let trailerreservation_to_add = TrailerReservationToAdd {
|
|
||||||
trailer: &trailer,
|
|
||||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
|
||||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
|
||||||
time_desc: data.time_desc,
|
|
||||||
usage: data.usage,
|
|
||||||
user_applicant: &user_applicant,
|
|
||||||
};
|
|
||||||
match TrailerReservation::create(db, trailerreservation_to_add).await {
|
|
||||||
Ok(_) => Flash::success(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Reservierung erfolgreich hinzugefügt",
|
|
||||||
),
|
|
||||||
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
|
||||||
pub struct ReservationEditForm {
|
|
||||||
pub(crate) id: i32,
|
|
||||||
pub(crate) time_desc: String,
|
|
||||||
pub(crate) usage: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/", data = "<data>")]
|
|
||||||
async fn update(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
data: Form<ReservationEditForm>,
|
|
||||||
user: User,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let Some(reservation) = TrailerReservation::find_by_id(db, data.id).await else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
format!("Reservation with ID {} does not exist!", data.id),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Not allowed to update reservation (only admins + creator do so).".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!(
|
|
||||||
"{} updated reservation from {reservation:?} to {data:?}",
|
|
||||||
user.name
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
reservation.update(db, data.into_inner()).await;
|
|
||||||
|
|
||||||
Flash::success(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Reservierung erfolgreich bearbeitet",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/<reservation_id>/delete")]
|
|
||||||
async fn delete<'r>(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
reservation_id: i32,
|
|
||||||
user: DonauLinzUser,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let reservation = TrailerReservation::find_by_id(db, reservation_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
|
|
||||||
reservation.delete(db).await;
|
|
||||||
Flash::success(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Reservierung erfolgreich gelöscht",
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Flash::error(
|
|
||||||
Redirect::to("/trailerreservation"),
|
|
||||||
"Nur der Reservierer darf die Reservierung löschen.".to_string(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
|
||||||
routes![
|
|
||||||
index,
|
|
||||||
index_kiosk,
|
|
||||||
create,
|
|
||||||
create_from_kiosk,
|
|
||||||
delete,
|
|
||||||
update
|
|
||||||
]
|
|
||||||
}
|
|
@ -36,7 +36,6 @@
|
|||||||
<a href="/stat/boats" class="px-2">Bootsauswertung</a>
|
<a href="/stat/boats" class="px-2">Bootsauswertung</a>
|
||||||
<a href="/boatdamage" class="px-2">Bootsschaden</a>
|
<a href="/boatdamage" class="px-2">Bootsschaden</a>
|
||||||
<a href="/boatreservation" class="px-2">Bootsreservierung</a>
|
<a href="/boatreservation" class="px-2">Bootsreservierung</a>
|
||||||
<a href="/trailerreservation" class="px-2">Hängerreservierung</a>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -78,8 +78,6 @@
|
|||||||
class="block w-100 py-2 hover:text-primary-600 border-t">Bootsschaden</a>
|
class="block w-100 py-2 hover:text-primary-600 border-t">Bootsschaden</a>
|
||||||
<a href="/boatreservation"
|
<a href="/boatreservation"
|
||||||
class="block w-100 py-2 hover:text-primary-600 border-t">Bootsreservierung</a>
|
class="block w-100 py-2 hover:text-primary-600 border-t">Bootsreservierung</a>
|
||||||
<a href="/trailerreservation"
|
|
||||||
class="block w-100 py-2 hover:text-primary-600 border-t">Hängerreservierung</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
|
{% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
|
||||||
<a href="/ergo" class="block w-100 py-2 hover:text-primary-600 border-t">Ergo</a>
|
<a href="/ergo" class="block w-100 py-2 hover:text-primary-600 border-t">Ergo</a>
|
||||||
|
@ -90,10 +90,6 @@
|
|||||||
<a href="/boatreservation"
|
<a href="/boatreservation"
|
||||||
class="block w-100 py-2 hover:text-primary-600">Bootsreservierung</a>
|
class="block w-100 py-2 hover:text-primary-600">Bootsreservierung</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="py-1">
|
|
||||||
<a href="/trailerreservation"
|
|
||||||
class="block w-100 py-2 hover:text-primary-600">Hängerreservierung</a>
|
|
||||||
</li>
|
|
||||||
<li class="py-1">
|
<li class="py-1">
|
||||||
<a href="/steering" class="block w-100 py-2 hover:text-primary-600">Steuerleute & Co</a>
|
<a href="/steering" class="block w-100 py-2 hover:text-primary-600">Steuerleute & Co</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
{% import "includes/macros" as macros %}
|
|
||||||
{% import "includes/forms/log" as log %}
|
|
||||||
{% extends "base" %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="max-w-screen-lg w-full">
|
|
||||||
<h1 class="h1">Hängerreservierungen</h1>
|
|
||||||
<h2 class="text-md font-bold tracking-wide bg-primary-900 mt-3 p-3 text-white flex justify-between items-center rounded-md">
|
|
||||||
Neue Reservierung
|
|
||||||
<a href="#"
|
|
||||||
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"
|
|
||||||
data-sidebar="true"
|
|
||||||
data-trigger="sidebar"
|
|
||||||
data-header="Neue Reservierung anlegen"
|
|
||||||
data-body="#new-reservation">
|
|
||||||
{% include "includes/plus-icon" %}
|
|
||||||
<span class="sr-only">Neue Reservierung eintragen</span>
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
<div class="hidden">
|
|
||||||
<div id="new-reservation">
|
|
||||||
<form action="/trailerreservation/new" method="post" class="grid gap-3">
|
|
||||||
{{ macros::select(label="Anhänger", data=trailers, name="trailer_id", id="trailer_id", display=["name"], wrapper_class="col-span-4", nonSelectableDefault=" -- Wähle einen Hänger aus ---") }}
|
|
||||||
{% if not loggedin_user %}{{ macros::select(label='Reserviert von', data=user, name='user_id_applicant') }}{% endif %}
|
|
||||||
{{ macros::input(label='Beginn', name='start_date', type='date', required=true, wrapper_class='col-span-4') }}
|
|
||||||
{{ macros::input(label='Ende', name='end_date', type='date', required=true, wrapper_class='col-span-4') }}
|
|
||||||
{{ macros::input(label='Uhrzeit (zB ab 14:00 Uhr, ganztägig, ...)', name='time_desc', type='text', required=true, wrapper_class='col-span-4') }}
|
|
||||||
{{ macros::input(label='Zweck (Wanderfahrt, ...)', name='usage', type='text', required=true, wrapper_class='col-span-4') }}
|
|
||||||
<input type="submit"
|
|
||||||
class="btn btn-primary w-full col-span-4"
|
|
||||||
value="Reservierung eintragen" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="search-wrapper">
|
|
||||||
<label for="name" class="sr-only">Suche</label>
|
|
||||||
<input type="search"
|
|
||||||
name="name"
|
|
||||||
id="filter-js"
|
|
||||||
class="search-bar"
|
|
||||||
placeholder="Suchen nach Namen...">
|
|
||||||
</div>
|
|
||||||
<div id="filter-result-js" class="search-result"></div>
|
|
||||||
{% for reservation in trailerreservations %}
|
|
||||||
{% set allowed_to_edit = false %}
|
|
||||||
{% if loggedin_user %}
|
|
||||||
{% if loggedin_user.id == reservation.user_applicant.id or "admin" in loggedin_user.roles %}
|
|
||||||
{% set allowed_to_edit = true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
<div data-filterable="true"
|
|
||||||
data-filter="{{ reservation.user_applicant.name }} {{ reservation.trailer.name }}"
|
|
||||||
class="w-full border-t bg-white dark:bg-primary-900 text-black dark:text-white p-3">
|
|
||||||
<div class="w-full">
|
|
||||||
<strong>Boot:</strong>
|
|
||||||
{{ reservation.trailer.name }}
|
|
||||||
<br />
|
|
||||||
<strong>Reservierung:</strong>
|
|
||||||
{{ reservation.user_applicant.name }}
|
|
||||||
<br />
|
|
||||||
<strong>Datum:</strong>
|
|
||||||
{{ reservation.start_date }}
|
|
||||||
{% if reservation.end_date != reservation.start_date %}
|
|
||||||
-
|
|
||||||
{{ reservation.end_date }}
|
|
||||||
{% endif %}
|
|
||||||
<br />
|
|
||||||
{% if not allowed_to_edit %}
|
|
||||||
<strong>Uhrzeit:</strong>
|
|
||||||
{{ reservation.time_desc }}
|
|
||||||
<br />
|
|
||||||
<strong>Zweck:</strong>
|
|
||||||
{{ reservation.usage }}
|
|
||||||
{% endif %}
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<form action="/trailerreservation"
|
|
||||||
method="post"
|
|
||||||
class="bg-white dark:bg-primary-900 pt-3 rounded-md w-full">
|
|
||||||
<div class="w-full grid gap-3">
|
|
||||||
<input type="hidden" name="id" value="{{ reservation.id }}" />
|
|
||||||
{{ macros::input(label='Uhrzeit', name='time_desc', id=loop.index, type="text", value=reservation.time_desc, readonly=false) }}
|
|
||||||
{{ macros::input(label='Zweck', name='usage', id=loop.index, type="text", value=reservation.usage, readonly=false) }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-3 text-right">
|
|
||||||
<a href="/trailerreservation/{{ reservation.id }}/delete"
|
|
||||||
class="w-28 btn btn-alert"
|
|
||||||
onclick="return confirm('Willst du diese Reservierung wirklich löschen?');">
|
|
||||||
{% include "includes/delete-icon" %}
|
|
||||||
Löschen
|
|
||||||
</a>
|
|
||||||
<input value="Ändern" type="submit" class="w-28 btn btn-primary ml-1" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
Loading…
Reference in New Issue
Block a user