From a6b12d9bf2a4597e612e36f22f29927df233353b Mon Sep 17 00:00:00 2001 From: Max Hohlfeld Date: Wed, 7 May 2025 19:52:42 +0200 Subject: [PATCH] feat: redesign availability input --- web/src/endpoints/availability/get_new.rs | 16 +- web/src/endpoints/availability/get_update.rs | 11 +- web/src/endpoints/availability/mod.rs | 17 +- web/src/endpoints/availability/post_new.rs | 21 +-- web/src/endpoints/availability/post_update.rs | 9 +- web/src/filters.rs | 34 +++- web/templates/availability/new_or_edit.html | 170 ++++++++++-------- 7 files changed, 173 insertions(+), 105 deletions(-) diff --git a/web/src/endpoints/availability/get_new.rs b/web/src/endpoints/availability/get_new.rs index 6e708336..6a5c5142 100644 --- a/web/src/endpoints/availability/get_new.rs +++ b/web/src/endpoints/availability/get_new.rs @@ -1,12 +1,10 @@ use actix_web::{web, HttpResponse, Responder}; -use chrono::NaiveDate; +use chrono::{Days, NaiveDate, NaiveTime}; use serde::Deserialize; use sqlx::PgPool; use crate::endpoints::availability::NewOrEditAvailabilityTemplate; -use crate::models::{ - find_free_date_time_slots, Availability, User, -}; +use crate::models::{find_free_date_time_slots, Availability, User}; use crate::utils::{ApplicationError, TemplateResponse}; #[derive(Deserialize)] @@ -24,8 +22,8 @@ pub async fn get( Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?; let slot_suggestions = find_free_date_time_slots(&availabilities_from_user); - let user_can_create_availabillity = availabilities_from_user.is_empty() - || !slot_suggestions.is_empty(); + let user_can_create_availabillity = + availabilities_from_user.is_empty() || !slot_suggestions.is_empty(); if !user_can_create_availabillity { return Ok(HttpResponse::BadRequest().finish()); @@ -34,11 +32,13 @@ pub async fn get( let template = NewOrEditAvailabilityTemplate { user: user.into_inner(), date: query.date, + enddate: None, id: None, - start: None, - end: None, + start: Some(NaiveTime::from_hms_opt(10, 0, 0).unwrap()), + end: Some(NaiveTime::from_hms_opt(20, 0, 0).unwrap()), comment: None, slot_suggestions, + datetomorrow: query.date.checked_add_days(Days::new(1)).unwrap(), }; Ok(template.to_response()?) diff --git a/web/src/endpoints/availability/get_update.rs b/web/src/endpoints/availability/get_update.rs index 27950430..a394a9b1 100644 --- a/web/src/endpoints/availability/get_update.rs +++ b/web/src/endpoints/availability/get_update.rs @@ -1,4 +1,5 @@ use actix_web::{web, HttpResponse, Responder}; +use chrono::Days; use sqlx::PgPool; use crate::{ @@ -33,11 +34,17 @@ pub async fn get( let template = NewOrEditAvailabilityTemplate { user: user.into_inner(), date: availability.start.date(), + enddate: Some(availability.end.date()), id: Some(path.id), start: Some(availability.start.time()), - end: Some(availability.end), + end: Some(availability.end.time()), comment: availability.comment.as_deref(), - slot_suggestions + slot_suggestions, + datetomorrow: availability + .start + .date() + .checked_add_days(Days::new(1)) + .unwrap(), }; Ok(template.to_response()?) diff --git a/web/src/endpoints/availability/mod.rs b/web/src/endpoints/availability/mod.rs index 4ad94b8d..f5a26726 100644 --- a/web/src/endpoints/availability/mod.rs +++ b/web/src/endpoints/availability/mod.rs @@ -1,5 +1,7 @@ +use crate::filters; use askama::Template; -use chrono::{Days, NaiveDate, NaiveDateTime, NaiveTime}; +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +use serde::Deserialize; use crate::models::{Availability, AvailabilityChangeset, Role, User}; @@ -15,11 +17,22 @@ pub mod post_update; struct NewOrEditAvailabilityTemplate<'a> { user: User, date: NaiveDate, + enddate: Option, id: Option, start: Option, - end: Option, + end: Option, comment: Option<&'a str>, slot_suggestions: Vec<(NaiveDateTime, NaiveDateTime)>, + datetomorrow: NaiveDate +} + +#[derive(Deserialize)] +pub struct AvailabillityForm { + pub startdate: NaiveDate, + pub enddate: NaiveDate, + pub starttime: NaiveTime, + pub endtime: NaiveTime, + pub comment: Option, } fn find_adjacend_availability<'a>( diff --git a/web/src/endpoints/availability/post_new.rs b/web/src/endpoints/availability/post_new.rs index d8712880..7915e4df 100644 --- a/web/src/endpoints/availability/post_new.rs +++ b/web/src/endpoints/availability/post_new.rs @@ -1,23 +1,13 @@ use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; -use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; use garde::Validate; -use serde::Deserialize; use sqlx::PgPool; use crate::{ - endpoints::availability::find_adjacend_availability, + endpoints::availability::{find_adjacend_availability, AvailabillityForm}, models::{Availability, AvailabilityChangeset, AvailabilityContext, User}, utils::{self, ApplicationError}, }; -#[derive(Deserialize)] -pub struct AvailabillityForm { - pub date: NaiveDate, - pub from: NaiveTime, - pub till: NaiveDateTime, - pub comment: Option, -} - #[actix_web::post("/availabillity/new")] pub async fn post( user: web::ReqData, @@ -25,13 +15,16 @@ pub async fn post( form: web::Form, ) -> Result { let existing_availabilities = - Availability::read_by_user_and_date(pool.get_ref(), user.id, &form.date).await?; + Availability::read_by_user_and_date(pool.get_ref(), user.id, &form.startdate).await?; let context = AvailabilityContext { existing_availabilities: existing_availabilities.clone(), }; + let start = form.startdate.and_time(form.starttime); + let end = form.enddate.and_time(form.endtime); + let mut changeset = AvailabilityChangeset { - time: (form.date.and_time(form.from), form.till), + time: (start, end), comment: form.comment.clone(), }; @@ -55,7 +48,7 @@ pub async fn post( Availability::create(pool.get_ref(), user.id, changeset).await?; } - let url = utils::get_return_url_for_date(&form.date); + let url = utils::get_return_url_for_date(&form.startdate); Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) .insert_header(("HX-LOCATION", url)) diff --git a/web/src/endpoints/availability/post_update.rs b/web/src/endpoints/availability/post_update.rs index 27e058e6..0129f27b 100644 --- a/web/src/endpoints/availability/post_update.rs +++ b/web/src/endpoints/availability/post_update.rs @@ -4,7 +4,7 @@ use sqlx::PgPool; use crate::{ endpoints::{ - availability::{find_adjacend_availability, post_new::AvailabillityForm}, + availability::{find_adjacend_availability, AvailabillityForm}, IdPath, }, models::{Availability, AvailabilityChangeset, AvailabilityContext, User}, @@ -37,8 +37,11 @@ pub async fn post( existing_availabilities: existing_availabilities.clone(), }; + let start = form.startdate.and_time(form.starttime); + let end = form.enddate.and_time(form.endtime); + let mut changeset = AvailabilityChangeset { - time: (form.date.and_time(form.from), form.till), + time: (start, end), comment: form.comment.clone(), }; @@ -65,7 +68,7 @@ pub async fn post( Availability::update(pool.get_ref(), availability.id, changeset).await?; } - let url = utils::get_return_url_for_date(&form.date); + let url = utils::get_return_url_for_date(&form.startdate); Ok(HttpResponse::Found() .insert_header((LOCATION, url.clone())) .insert_header(("HX-LOCATION", url)) diff --git a/web/src/filters.rs b/web/src/filters.rs index 252785b0..c4e00543 100644 --- a/web/src/filters.rs +++ b/web/src/filters.rs @@ -1,4 +1,6 @@ -use chrono::{NaiveDate, NaiveDateTime}; +use std::fmt::Display; + +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; use maud::html; use crate::models::UserFunction; @@ -21,7 +23,10 @@ pub fn cond_show(show: &bool, text: &str) -> askama::Result { } } -pub fn insert_value(option: &Option) -> askama::Result { +pub fn insert_value(option: &Option) -> askama::Result +where + T: Display, +{ if let Some(val) = option { let s = format!(r#"value="{val}""#); return Ok(s); @@ -30,6 +35,15 @@ pub fn insert_value(option: &Option) -> askama::Result { Ok(String::new()) } +pub fn insert_time_value(option: &Option) -> askama::Result { + if let Some(val) = option { + let s = val.format(r#"value="%H:%M""#).to_string(); + return Ok(s); + } + + Ok(String::new()) +} + pub fn is_some_and_eq(option: &Option, other: &T) -> askama::Result where T: Eq, @@ -71,6 +85,22 @@ pub fn date_d(v: &NaiveDate) -> askama::Result { Ok(v.format("%d.%m.%Y").to_string()) } +pub fn date_c(v: &NaiveDate) -> askama::Result { + Ok(v.format("%d.%m").to_string()) +} + +pub fn time(v: &NaiveTime) -> askama::Result { + Ok(v.format("%H:%M").to_string()) +} + +pub fn time_opt(v: &Option, default: &str) -> askama::Result { + if let Some(t) = v { + return time(t); + } + + Ok(default.to_string()) +} + pub fn dt_t(v: &NaiveDateTime) -> askama::Result { Ok(v.format("%R").to_string()) } diff --git a/web/templates/availability/new_or_edit.html b/web/templates/availability/new_or_edit.html index 94d190d1..b5dda845 100644 --- a/web/templates/availability/new_or_edit.html +++ b/web/templates/availability/new_or_edit.html @@ -3,87 +3,109 @@ {% block content %}
- {% if id.is_some() %} -
-

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

- {% else %} - -

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

- {% endif %} + {% let is_edit = id.is_some() %} + +

{% if is_edit %}Bearbeite{% else %}Neue{% endif %} Vefügbarkeit für den {{ date|date_d }}

- - {% let time = "%R" %} + + -
-
- -
-
-
- - {% if slot_suggestions.len() > 0 %} -

noch mögliche Zeiträume:

-
- {% for (s, e) in slot_suggestions %} - {{ s.format(time) }} - {{ e.format(time) }} - {% endfor %} -
- {% endif %} +
+
+ +
+
+
+ + {% if slot_suggestions.len() > 0 %} +

noch mögliche Zeiträume:

+
+ {% for (s, e) in slot_suggestions %} + {{ *s|dt_t }} - {{ *e|dt_t }} + {% endfor %}
-
- + {% endif %} +
+
+ +
+
+
+ +
+
+ +
+
+
+ {% let is_overnight = enddate.is_some() && enddate.as_ref().unwrap() == datetomorrow|ref %} + +
+
+ +
+
+
+ + +
+
+ +
+
+
+
+ +
+

+ verfügbar von {{ date|date_c }} {{ start|time_opt("10:00")|safe }} Uhr + bis {{ enddate.as_ref().unwrap_or(date)|date_c }} + {{ end|time_opt("20:00")|safe }} Uhr +

+
+
+
+ +
+
+
+
+
+ +
+
+
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
- -
-
-
- - +
{% endblock %}