diff --git a/src/calendar/delete_availabillity.rs b/src/calendar/delete_availabillity.rs new file mode 100644 index 00000000..51f3b35a --- /dev/null +++ b/src/calendar/delete_availabillity.rs @@ -0,0 +1,28 @@ +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use serde::Deserialize; +use sqlx::PgPool; + +use crate::models::availabillity::Availabillity; + +#[derive(Deserialize)] +pub struct AvailabillityPath { + pub id: i32 +} + +#[actix_web::delete("/availabillity/delete/{id}")] +pub async fn delete_availabillity(user: Identity, pool: web::Data, path: web::Path) -> impl Responder { + if let Ok(current_user_id) = user.id() { + let current_user_id: i32 = current_user_id.parse().unwrap(); + + if let Ok(availabillity_in_db) = Availabillity::read_by_id(pool.get_ref(), path.id).await { + if availabillity_in_db.user_id == current_user_id { + if let Ok(_) = Availabillity::delete(pool.get_ref(), availabillity_in_db.id).await { + return HttpResponse::NoContent().finish(); + } + } + } + } + + return HttpResponse::BadRequest().finish(); +} diff --git a/src/calendar/mod.rs b/src/calendar/mod.rs index 62851439..47351a5a 100644 --- a/src/calendar/mod.rs +++ b/src/calendar/mod.rs @@ -1,5 +1,7 @@ pub mod routes; mod get_availabillity_new; mod post_availabillity; +mod delete_availabillity; +mod update_availabillity; pub use routes::init; diff --git a/src/calendar/post_availabillity.rs b/src/calendar/post_availabillity.rs index 64b94361..570c7963 100644 --- a/src/calendar/post_availabillity.rs +++ b/src/calendar/post_availabillity.rs @@ -8,10 +8,10 @@ use crate::models::{availabillity::Availabillity, user::User}; #[derive(Deserialize)] pub struct AvailabillityForm { - date: NaiveDate, - start_time: Option, - end_time: Option, - comment: Option + pub date: NaiveDate, + pub from: Option, + pub till: Option, + pub comment: Option } #[actix_web::post("/availabillity/new")] @@ -24,7 +24,7 @@ pub async fn post_availabillity( .await .unwrap(); - if let Ok(_) = Availabillity::create(pool.get_ref(), current_user.id, form.date, form.start_time, form.end_time, form.comment.clone()).await { + if let Ok(_) = Availabillity::create(pool.get_ref(), current_user.id, form.date, form.from, form.till, form.comment.clone()).await { HttpResponse::Found() .insert_header((LOCATION, "/")) .finish() diff --git a/src/calendar/routes.rs b/src/calendar/routes.rs index e0d74aca..a3818f46 100644 --- a/src/calendar/routes.rs +++ b/src/calendar/routes.rs @@ -1,20 +1,23 @@ use actix_identity::Identity; -use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; +use actix_web::{web, HttpResponse, Responder}; use askama::Template; use chrono::{NaiveDate, Utc}; use serde::Deserialize; use sqlx::PgPool; use crate::models::{ - area::Area, availabillity::Availabillity, event::Event, role::Role, user::User, + area::Area, availabillity::Availabillity, event::Event, function, role::Role, user::User }; -use super::{get_availabillity_new::get_availabillity_new, post_availabillity::post_availabillity}; +use super::{delete_availabillity::delete_availabillity, get_availabillity_new::get_availabillity_new, post_availabillity::post_availabillity, update_availabillity::{get_update_availabillity, post_update_availabillity}}; pub fn init(cfg: &mut web::ServiceConfig) { cfg.service(get_index); cfg.service(get_availabillity_new); cfg.service(post_availabillity); + cfg.service(delete_availabillity); + cfg.service(get_update_availabillity); + cfg.service(post_update_availabillity); } #[derive(Deserialize)] @@ -26,12 +29,15 @@ pub struct CalendarQuery { #[template(path = "index.html")] struct CalendarTemplate { user_role: Role, + current_user_id: i32, date: NaiveDate, area: Area, events: Vec, availabillities: Vec, } +type Function = function::Function; + #[actix_web::get("/")] async fn get_index( user: Identity, @@ -49,16 +55,13 @@ async fn get_index( .await .unwrap(); let events = Event::read_by_date(pool.get_ref(), date).await.unwrap(); - let mut availabillities = Availabillity::read_by_date(pool.get_ref(), date) + let availabillities = Availabillity::read_by_date_including_user(pool.get_ref(), date) .await .unwrap(); - for avl in availabillities.iter_mut() { - avl.load_user(pool.get_ref()).await.unwrap() - } - let template = CalendarTemplate { user_role: current_user.role, + current_user_id: current_user.id, date, area, events, diff --git a/src/calendar/update_availabillity.rs b/src/calendar/update_availabillity.rs new file mode 100644 index 00000000..82590866 --- /dev/null +++ b/src/calendar/update_availabillity.rs @@ -0,0 +1,110 @@ +use actix_identity::Identity; +use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; +use askama::Template; +use askama_actix::TemplateToResponse; +use chrono::{NaiveDate, NaiveTime}; +use sqlx::PgPool; + +use crate::{calendar::post_availabillity::AvailabillityForm, models::{availabillity::Availabillity, role::Role, user::User}}; + +use super::delete_availabillity::AvailabillityPath; + +#[derive(Template)] +#[template(path = "availabillity_edit.html")] +pub struct AvailabillityEditTemplate { + user_role: Role, + date: NaiveDate, + id: i32, + start_time: String, + end_time: String, + has_time: bool, + comment: String, +} + +#[actix_web::get("/availabillity/edit/{id}")] +pub async fn get_update_availabillity( + user: Identity, + pool: web::Data, + path: web::Path, +) -> impl Responder { + let current_user = User::read_by_id(pool.as_ref(), user.id().unwrap().parse().unwrap()) + .await + .unwrap(); + + if let Ok(availabillity) = Availabillity::read_by_id(pool.get_ref(), path.id).await { + if availabillity.user_id == user.id().unwrap().parse::().unwrap() { + let start_time = availabillity + .start_time + .unwrap_or(NaiveTime::from_hms_opt(0, 0, 0).unwrap()) + .format("%R") + .to_string(); + + let end_time = availabillity + .end_time + .unwrap_or(NaiveTime::from_hms_opt(23, 59, 0).unwrap()) + .format("%R") + .to_string(); + + let has_time = availabillity.start_time.is_some() && availabillity.end_time.is_some(); + + let comment = availabillity.comment.unwrap_or(String::new()); + + let template = AvailabillityEditTemplate { + user_role: current_user.role, + date: availabillity.date, + id: path.id, + start_time, + end_time, + has_time, + comment + }; + + return template.to_response(); + } + } + + HttpResponse::BadRequest().body("Availabillity with this id doesn't exist.") +} + +#[actix_web::post("/availabillity/edit/{id}")] +pub async fn post_update_availabillity( + user: Identity, + pool: web::Data, + path: web::Path, + form: web::Form +) -> impl Responder { + let current_user = User::read_by_id(pool.as_ref(), user.id().unwrap().parse().unwrap()) + .await + .unwrap(); + + if let Ok(mut availabillity) = Availabillity::read_by_id(pool.get_ref(), path.id).await { + if availabillity.user_id == user.id().unwrap().parse::().unwrap() { + let mut has_changed = false; + + if availabillity.start_time != form.from { + availabillity.start_time = form.from; + has_changed = true; + } + + if availabillity.end_time != form.till { + availabillity.end_time = form.till; + has_changed = true; + } + + if availabillity.comment != form.comment { + availabillity.comment = form.comment.clone(); + has_changed = true; + } + + if has_changed { + if let Ok(_) = Availabillity::update(pool.get_ref(), path.id, &availabillity).await { + return HttpResponse::Found() + .insert_header((LOCATION, "/")) + .finish(); + } + } + } + } + + HttpResponse::BadRequest().body("Fehler beim erstellen") +} diff --git a/src/models/availabillity.rs b/src/models/availabillity.rs index 549fd3c5..cb73b915 100644 --- a/src/models/availabillity.rs +++ b/src/models/availabillity.rs @@ -1,7 +1,7 @@ use chrono::{NaiveDate, NaiveTime}; use sqlx::{query, PgPool}; -use super::user::User; +use super::{function::Function, role::Role, user::User}; pub struct Availabillity { pub id: i32, @@ -56,10 +56,106 @@ impl Availabillity { Ok(availabillities) } - pub async fn load_user(&mut self, pool: &PgPool) -> anyhow::Result<()> { - let user = User::read_by_id(pool, self.user_id).await?; - self.user = Some(user); + pub async fn read_by_date_including_user( + pool: &PgPool, + date: NaiveDate, + ) -> anyhow::Result> { + let records = query!( + r##" + SELECT + availabillity.id, + availabillity.userId, + availabillity.date, + availabillity.startTime, + availabillity.endTime, + availabillity.comment, + user_.name, + user_.email, + user_.password, + user_.salt, + user_.role AS "role: Role", + user_.function AS "function: Function", + user_.areaId, + user_.locked, + user_.lastLogin, + user_.receiveNotifications + FROM availabillity + JOIN user_ ON availabillity.userId = user_.id + WHERE availabillity.date = $1; + "##, + date + ) + .fetch_all(pool) + .await?; + + let availabillities = records + .iter() + .map(|r| Availabillity { + id: r.id, + user_id: r.userid, + user: Some(User { + id: r.userid, + name: r.name.clone(), + email: r.email.clone(), + password: r.password.clone(), + salt: r.salt.clone(), + role: r.role.clone(), + function: r.function.clone(), + area_id: r.areaid, + locked: r.locked, + last_login: r.lastlogin, + receive_notifications: r.receivenotifications, + }), + date: r.date, + start_time: r.starttime, + end_time: r.endtime, + comment: r.comment.clone(), + }) + .collect(); + + Ok(availabillities) + } + + pub async fn read_by_id(pool: &PgPool, id: i32) -> anyhow::Result { + let record = query!("SELECT * FROM availabillity WHERE id = $1", id) + .fetch_one(pool) + .await?; + + let availabillity = Availabillity { + id: record.id, + user_id: record.userid, + user: None, + date: record.date, + start_time: record.starttime, + end_time: record.endtime, + comment: record.comment.clone(), + }; + + Ok(availabillity) + } + + pub async fn update( + pool: &PgPool, + id: i32, + updated_availabillity: &Availabillity, + ) -> anyhow::Result<()> { + query!( + "UPDATE availabillity SET startTime = $1, endTime = $2, comment = $3 WHERE id = $4", + updated_availabillity.start_time, + updated_availabillity.end_time, + updated_availabillity.comment, + id + ) + .execute(pool) + .await?; Ok(()) } + + pub async fn delete(pool: &PgPool, id: i32) -> anyhow::Result<()> { + query!("DELETE FROM availabillity WHERE id = $1", id) + .execute(pool) + .await?; + Ok(()) + } } diff --git a/templates/availabillity_edit.html b/templates/availabillity_edit.html new file mode 100644 index 00000000..c0a32cbb --- /dev/null +++ b/templates/availabillity_edit.html @@ -0,0 +1,117 @@ +{% extends "nav.html" %} + +{% block content %} +
+
+
+

Bearbeite Vefügbarkeit für den {{ date.format("%d.%m.%Y") }}

+ + + +
+
+ +
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+ Zurück +
+
+
+
+ +
+
+
+ + +{% endblock %} diff --git a/templates/availabillity_new.html b/templates/availabillity_new.html index 5eef5d85..dc62dc9c 100644 --- a/templates/availabillity_new.html +++ b/templates/availabillity_new.html @@ -33,10 +33,10 @@
- +
- +
@@ -48,7 +48,7 @@
- +
diff --git a/templates/index.html b/templates/index.html index 36a21a94..dca41661 100644 --- a/templates/index.html +++ b/templates/index.html @@ -62,19 +62,74 @@
keine Verfügbarkeiten eingetragen
{% else %} - {% for availabillity in availabillities %} - {% let user = availabillity.user.as_ref().unwrap() %} -
-

{{ user.name }}

-

{{ user.function }}

- {% if availabillity.start_time.is_some() && availabillity.end_time.is_some() %} -

{{ availabillity.start_time.as_ref().unwrap() }}

- {% endif %} + + + + + + + + + + + + {% for availabillity in availabillities %} + {% let user = availabillity.user.as_ref().unwrap() %} + + + + + + + + {% endfor %} + +
NameFunktionZeitraumKommentar
{{ user.name }} + {% match user.function %} + {% when Function::Posten %} + Posten + {% when Function::Wachhabender %} + Wachhabender + {% else %} + {% endmatch %} + + {% if availabillity.start_time.is_some() && availabillity.end_time.is_some() %} + {{ availabillity.start_time.as_ref().unwrap().format("%R") }} bis {{ availabillity.end_time.as_ref().unwrap().format("%R") }} + {% else %} + ganztägig + {% endif %} + + {{ availabillity.comment.as_deref().unwrap_or("") }} + + {% if availabillity.user_id == current_user_id %} +
+ Bearbeiten + +
+ {% endif %} +
- {% endfor %} {% endif %} + + {% endblock %}