From 4527a4dfc3d48197430c89c2bb5739a502c1598a Mon Sep 17 00:00:00 2001 From: Max Hohlfeld Date: Sun, 11 May 2025 22:31:27 +0200 Subject: [PATCH] feat: event input and display closes #20 --- ...ts__get_edit__inner_produces_template.snap | 337 +++++++------- web/src/endpoints/events/get_edit.rs | 16 +- web/src/endpoints/events/get_new.rs | 19 +- web/src/endpoints/events/mod.rs | 51 ++- web/src/endpoints/events/post_edit.rs | 24 +- web/src/endpoints/events/post_new.rs | 22 +- web/src/filters.rs | 6 +- web/templates/events/new_or_edit.html | 414 +++++++++--------- web/templates/index.html | 10 +- 9 files changed, 465 insertions(+), 434 deletions(-) diff --git a/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap b/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap index 8c72e4a2..7f5cd44a 100644 --- a/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap +++ b/web/snapshots/brass_web__endpoints__events__get_edit__inner_produces_template.snap @@ -5,200 +5,209 @@ snapshot_kind: text ---
-
+

Event 'Vorstellung' bearbeiten

- + - -
-
-
-
-
- -
-
- - -
+ +
+
+
+
+
+ +
+
+
-
-
- - - - -
-
- -
-
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
- +
+
+
+ + +
+
+ +
+
+
+
+
+
-
-
- +
+
+ +
+
+
+
-
-
-
-
- -
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+
+
-
-
- +
+
+ +
+
+
+ +
+ +
+
-
-
- -
- -
- +
+
+ +
+
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+
-
-
- -
-
-
- -
- -
- +
+
+ +
+
+
+
+
+
-
-
- -
-
-
- -
- -
- +
+
+
+
+
+ +
+
+
-
-
- -
-
-
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
- -
-
-
- - +
diff --git a/web/src/endpoints/events/get_edit.rs b/web/src/endpoints/events/get_edit.rs index 2dd39aa4..6f25f0f7 100644 --- a/web/src/endpoints/events/get_edit.rs +++ b/web/src/endpoints/events/get_edit.rs @@ -10,7 +10,7 @@ use crate::utils::test_helper::{ use chrono::{NaiveDate, NaiveTime}; use crate::{ - endpoints::{events::NewEventTemplate, IdPath}, + endpoints::{events::NewOrEditEventTemplate, IdPath}, models::{Assignment, Event, Function, Location, Role, User}, utils::{ApplicationError, TemplateResponse}, }; @@ -37,11 +37,10 @@ pub async fn get( let assignments = Assignment::read_all_by_event(pool.get_ref(), event.id).await?; - let template = NewEventTemplate { + let template = NewOrEditEventTemplate { user: user.into_inner(), date: event.start.date(), locations, - event: Some(event), amount_of_planned_posten: assignments .iter() .filter(|x| x.function == Function::Posten) @@ -52,6 +51,17 @@ pub async fn get( is_wachhabender_planned: assignments .iter() .any(|x| x.function == Function::Wachhabender), + id: Some(event.id), + start: event.start.time(), + end: event.end, + name: Some(event.name), + location: Some(event.location_id), + voluntary_wachhabender: event.voluntary_wachhabender, + voluntary_fuehrungsassistent: event.voluntary_fuehrungsassistent, + amount_of_posten: Some(event.amount_of_posten), + clothing: Some(event.clothing), + canceled: event.canceled, + note: event.note, }; Ok(template.to_response()?) diff --git a/web/src/endpoints/events/get_new.rs b/web/src/endpoints/events/get_new.rs index 5d938bdb..52f580d9 100644 --- a/web/src/endpoints/events/get_new.rs +++ b/web/src/endpoints/events/get_new.rs @@ -1,8 +1,9 @@ use actix_web::{web, Responder}; +use chrono::NaiveTime; use sqlx::PgPool; use crate::{ - endpoints::{events::NewEventTemplate, NaiveDateQuery}, + endpoints::{events::NewOrEditEventTemplate, NaiveDateQuery}, models::{Location, Role, User}, utils::{ApplicationError, TemplateResponse}, }; @@ -23,14 +24,26 @@ pub async fn get( Location::read_by_area(pool.get_ref(), user.area_id).await? }; - let template = NewEventTemplate { + let template = NewOrEditEventTemplate { user: user.into_inner(), date: query.date, locations, - event: None, amount_of_planned_posten: 0, is_fuehrungsassistent_planned: false, is_wachhabender_planned: false, + id: None, + start: NaiveTime::from_hms_opt(18, 0, 0).unwrap(), + end: query + .date + .and_time(NaiveTime::from_hms_opt(23, 00, 00).unwrap()), + name: None, + location: None, + voluntary_wachhabender: false, + voluntary_fuehrungsassistent: false, + amount_of_posten: None, + clothing: None, + canceled: false, + note: None, }; Ok(template.to_response()?) diff --git a/web/src/endpoints/events/mod.rs b/web/src/endpoints/events/mod.rs index 8a2a364e..a4732c1f 100644 --- a/web/src/endpoints/events/mod.rs +++ b/web/src/endpoints/events/mod.rs @@ -1,9 +1,10 @@ -use chrono::Days; use crate::filters; -use chrono::NaiveDate; use askama::Template; +use chrono::{Days, NaiveDateTime}; +use chrono::{NaiveDate, NaiveTime}; +use serde::Deserialize; -use crate::models::{Event, Location, Role, User}; +use crate::models::{Location, Role, User}; pub mod delete; pub mod get_edit; @@ -16,12 +17,52 @@ pub mod put_cancelation; #[derive(Template)] #[cfg_attr(not(test), template(path = "events/new_or_edit.html"))] #[cfg_attr(test, template(path = "events/new_or_edit.html", block = "content"))] -pub struct NewEventTemplate { +pub struct NewOrEditEventTemplate { user: User, date: NaiveDate, locations: Vec, - event: Option, + id: Option, + start: NaiveTime, + end: NaiveDateTime, + name: Option, + location: Option, + voluntary_wachhabender: bool, + voluntary_fuehrungsassistent: bool, + amount_of_posten: Option, + clothing: Option, + canceled: bool, + note: Option, amount_of_planned_posten: usize, is_fuehrungsassistent_planned: bool, is_wachhabender_planned: bool, } + +#[derive(Deserialize)] +pub struct NewOrEditEventForm { + name: String, + date: NaiveDate, + start: NaiveTime, + #[serde(with = "short_date_time_format")] + end: NaiveDateTime, + location: i32, + voluntarywachhabender: Option, + voluntaryfuehrungsassistent: Option, + amount: i16, + clothing: String, + note: Option, +} + +mod short_date_time_format { + use chrono::NaiveDateTime; + use serde::{self, Deserialize, Deserializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + const FORMAT: &'static str = "%Y-%m-%dT%H:%M"; + let s = String::deserialize(deserializer)?; + let dt = NaiveDateTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)?; + Ok(dt) + } +} diff --git a/web/src/endpoints/events/post_edit.rs b/web/src/endpoints/events/post_edit.rs index ef002b36..05c2ba0c 100644 --- a/web/src/endpoints/events/post_edit.rs +++ b/web/src/endpoints/events/post_edit.rs @@ -1,11 +1,10 @@ use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; -use chrono::{Days, NaiveDateTime}; +use chrono::Days; use garde::Validate; -use serde::Deserialize; use sqlx::PgPool; use crate::{ - endpoints::IdPath, + endpoints::{events::NewOrEditEventForm, IdPath}, models::{ Assignment, AssignmentChangeset, Availability, Event, EventChangeset, EventContext, Function, Location, Role, User, @@ -14,24 +13,11 @@ use crate::{ END_OF_DAY, START_OF_DAY, }; -#[derive(Deserialize)] -pub struct EditEventForm { - name: String, - from: NaiveDateTime, - till: NaiveDateTime, - location: i32, - voluntarywachhabender: Option, - voluntaryfuehrungsassistent: Option, - amount: i16, - clothing: String, - note: Option, -} - #[actix_web::post("/events/{id}/edit")] pub async fn post( user: web::ReqData, pool: web::Data, - form: web::Form, + form: web::Form, path: web::Path, ) -> Result { if user.role != Role::Admin && user.role != Role::AreaManager { @@ -61,7 +47,7 @@ pub async fn post( amount_of_posten: form.amount, clothing: form.clothing.clone(), location_id: form.location, - time: (form.from, form.till), + time: (form.date.and_time(form.start), form.end), name: form.name.clone(), note: form .note @@ -161,7 +147,7 @@ pub async fn post( Event::update(pool.get_ref(), event.id, changeset).await?; - let url = utils::get_return_url_for_date(&form.from.date()); + let url = utils::get_return_url_for_date(&form.date); //println!("redirecto to {url}"); Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) diff --git a/web/src/endpoints/events/post_new.rs b/web/src/endpoints/events/post_new.rs index 79cae02d..eb11c268 100644 --- a/web/src/endpoints/events/post_new.rs +++ b/web/src/endpoints/events/post_new.rs @@ -1,32 +1,18 @@ use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; -use chrono::NaiveDateTime; use garde::Validate; -use serde::Deserialize; use sqlx::PgPool; use crate::{ + endpoints::events::NewOrEditEventForm, models::{Event, EventChangeset, Location, Role, User}, utils::{self, ApplicationError}, }; -#[derive(Deserialize)] -pub struct NewEventForm { - name: String, - from: NaiveDateTime, - till: NaiveDateTime, - location: i32, - voluntarywachhabender: Option, - voluntaryfuehrungsassistent: Option, - amount: i16, - clothing: String, - note: Option, -} - #[actix_web::post("/events/new")] pub async fn post( user: web::ReqData, pool: web::Data, - form: web::Form, + form: web::Form, ) -> Result { if user.role != Role::Admin && user.role != Role::AreaManager { return Err(ApplicationError::Unauthorized); @@ -44,7 +30,7 @@ pub async fn post( amount_of_posten: form.amount, clothing: form.clothing.clone(), location_id: form.location, - time: (form.from, form.till), + time: (form.date.and_time(form.start), form.end), name: form.name.clone(), note: form .note @@ -60,7 +46,7 @@ pub async fn post( Event::create(pool.get_ref(), changeset).await?; - let url = utils::get_return_url_for_date(&form.from.date()); + let url = utils::get_return_url_for_date(&form.date); //println!("redirecto to {url}"); Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) diff --git a/web/src/filters.rs b/web/src/filters.rs index f07337ae..7bb8bfbe 100644 --- a/web/src/filters.rs +++ b/web/src/filters.rs @@ -74,11 +74,11 @@ pub fn show_tree(f: &UserFunction) -> askama::Result { } pub fn dt_f(v: &NaiveDateTime) -> askama::Result { - Ok(v.format("%F").to_string()) + Ok(v.format("%Y-%m-%dT%H:%M").to_string()) } -pub fn dt_d(v: &NaiveDateTime) -> askama::Result { - Ok(v.format("%d.%m.%Y").to_string()) +pub fn dt_ff(v: &NaiveDateTime) -> askama::Result { + Ok(v.format("%d.%m.%Y %H:%M").to_string()) } pub fn date_d(v: &NaiveDate) -> askama::Result { diff --git a/web/templates/events/new_or_edit.html b/web/templates/events/new_or_edit.html index 4ee8f2d7..25853caf 100644 --- a/web/templates/events/new_or_edit.html +++ b/web/templates/events/new_or_edit.html @@ -3,246 +3,230 @@ {% block content %}
- {% if let Some(event) = event %} -
-

Event '{{ event.name }}' bearbeiten

+ + {% if let Some(name) = name %} +

Event '{{ name }}' bearbeiten

{% else %} - -

Neues Event anlegen für den {{ date|date_d }}

- {% endif %} +

Neues Event anlegen für den {{ date|date_d }}

+ {% endif %} - {% if let Some(event) = event %} -
-
-
-
-
- -
-
- {% let delete_disabled = amount_of_planned_posten > 0 || is_wachhabender_planned || - is_fuehrungsassistent_planned %} - -
- {% if delete_disabled %} -

- Löschen nicht möglich, da bereits eine Planung existiert. -

- {% endif %} + + + {% if let Some(id) = id %} +
+
+
+
+
+ +
+
+ {% let delete_disabled = amount_of_planned_posten > 0 || is_wachhabender_planned || + is_fuehrungsassistent_planned %} + +
+ {% if delete_disabled %} +

+ Löschen nicht möglich, da bereits eine Planung existiert. +

+ {% endif %} +
+
+
+ {% endif %} + +
+
+ +
+
+
+
+
- {% endif %} +
- {% let disabled = event.is_some() && event.as_ref().unwrap().canceled %} - -
-
- +
+
+ +
+
+
+
-
-
-
- +
+ {% let tomorrow = date.checked_add_days(Days::new(1)).unwrap() %} + +
+
+
+ +
+
+ +
+
+
+
+
+
+
-
-
- -
-
-
- +
+
+ +
+
+
+ {% let wh_disabled = id.is_some() && is_wachhabender_planned %} +
+
-
- + {% if wh_disabled %} +

+ Keine Änderung möglich, da ein Wachhabender bereits eingeplant ist. Diesen zuerst entplanen! +

+ {% endif %} +
+
+
+ +
+
+ +
+
+
+ {% let fa_disabled = id.is_some() && is_fuehrungsassistent_planned %} +
+ +
+ {% if fa_disabled %} +

+ Keine Änderung möglich, da ein Führungsassistent bereits eingeplant ist. Diesen zuerst entplanen! +

+ {% endif %} +
+
+
+ +
+
+ +
+
+
+ {% let posten_planned = id.is_some() && amount_of_planned_posten > 0 %} +
+ +
+ {% if posten_planned %} +

+ Mindestens {{ amount_of_planned_posten }} Posten, da bereits diese Anzahl eingeplant ist. Zum + verringern diese erst entplanen! +

+ {% endif %} +
+
+
+ +
+
+ +
+
+
+
+
+
-
-
- -
-
-
-
-
- -
-
+
+
+ +
+
+
+
+
+
-
-
- -
-
-
- {% let wh_disabled = event.is_some() && is_wachhabender_planned %} -
- -
- {% if wh_disabled %} -

- Keine Änderung möglich, da ein Wachhabender bereits eingeplant ist. Diesen zuerst entplanen! -

- {% endif %} +
+
+
+
+
+ +
+
+
-
-
- -
-
-
- {% let fa_disabled = event.is_some() && is_fuehrungsassistent_planned %} -
- -
- {% if fa_disabled %} -

- Keine Änderung möglich, da ein Führungsassistent bereits eingeplant ist. Diesen zuerst entplanen! -

- {% endif %} -
-
-
- -
-
- -
-
-
- {% let posten_planned = event.is_some() && amount_of_planned_posten > 0 %} -
- -
- {% if posten_planned %} -

- Mindestens {{ amount_of_planned_posten }} Posten, da bereits diese Anzahl eingeplant ist. Zum - verringern - diese erst entplanen! -

- {% endif %} -
-
-
- -
-
- -
-
-
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
- -
-
-
- - +
{% endblock %} diff --git a/web/templates/index.html b/web/templates/index.html index 17f90700..e000b7bc 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -53,7 +53,7 @@

- Events am {{ date.format("%d.%m.%Y") }} + Events am {{ date|date_d }}

{% if user.role == Role::Admin || user.role == Role::AreaManager && (selected_area.is_none() || @@ -106,7 +106,7 @@ {% endif %}
-

Uhrzeit: {{ event.start|dt_t }} Uhr - {{ event.end|dt_f }} Uhr

+

Uhrzeit: {{ event.start|dt_t }} Uhr - {{ event.end|dt_ff }} Uhr

@@ -135,9 +135,11 @@
{% endif %} + {% if wachhabender.is_some() || fuehrungsassistent.is_some() || posten.len() > 0 || vehicle.len() > 0 %}

+ {% endif %} {% if let Some(wh) = wachhabender %}
@@ -178,7 +180,7 @@

- Verfügbarkeiten am {{ date.format("%d.%m.%Y") }} + Verfügbarkeiten am {{ date|date_d }}

{% if selected_area.is_none() || selected_area.unwrap() == user.area_id %} @@ -220,7 +222,7 @@ {{ u.function|show_tree|safe }} - {{ availabillity.start.format("%R") }} bis {{ availabillity.end.format("%d.%m.%Y %R") }} + {{ availabillity.start|dt_t }} Uhr bis {{ availabillity.end|dt_ff }} Uhr {{ availabillity.comment.as_deref().unwrap_or("") }}