feat: combine availability finished
This commit is contained in:
parent
6995599a81
commit
85fdd41c6b
@ -6,9 +6,10 @@ use sqlx::PgPool;
|
|||||||
|
|
||||||
use crate::endpoints::availability::NewOrEditAvailabilityTemplate;
|
use crate::endpoints::availability::NewOrEditAvailabilityTemplate;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
find_free_time_slots, only_one_availability_exists_and_is_whole_day, Availability, User,
|
find_free_time_slots, only_one_availability_exists_and_is_whole_day, Availability, AvailabilityTime, User
|
||||||
};
|
};
|
||||||
use crate::utils::ApplicationError;
|
use crate::utils::ApplicationError;
|
||||||
|
use crate::{END_OF_DAY, START_OF_DAY};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct AvailabilityNewQuery {
|
struct AvailabilityNewQuery {
|
||||||
@ -27,20 +28,25 @@ pub async fn get(
|
|||||||
Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
|
Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
|
||||||
let free_slots = find_free_time_slots(&availabilities_from_user);
|
let free_slots = find_free_time_slots(&availabilities_from_user);
|
||||||
|
|
||||||
let user_can_create_availabillity =
|
let user_can_create_availabillity = availabilities_from_user.len() == 0
|
||||||
!(only_one_availability_exists_and_is_whole_day(&availabilities_from_user)
|
|| !only_one_availability_exists_and_is_whole_day(&availabilities_from_user)
|
||||||
|| free_slots.len() == 0);
|
|| free_slots.len() > 0;
|
||||||
|
|
||||||
if !user_can_create_availabillity {
|
if !user_can_create_availabillity {
|
||||||
return Ok(HttpResponse::BadRequest().finish());
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let time_selection = if query.whole_day.unwrap_or(true) {
|
||||||
|
AvailabilityTime::WholeDay
|
||||||
|
} else {
|
||||||
|
AvailabilityTime::Temporarily(START_OF_DAY, END_OF_DAY)
|
||||||
|
};
|
||||||
|
|
||||||
let template = NewOrEditAvailabilityTemplate {
|
let template = NewOrEditAvailabilityTemplate {
|
||||||
user: user.into_inner(),
|
user: user.into_inner(),
|
||||||
date: query.date,
|
date: query.date,
|
||||||
whole_day_selected: query.whole_day.unwrap_or(true),
|
time_selection,
|
||||||
id: None,
|
id: None,
|
||||||
time: None,
|
|
||||||
comment: None,
|
comment: None,
|
||||||
slot_suggestions: free_slots,
|
slot_suggestions: free_slots,
|
||||||
};
|
};
|
||||||
|
@ -71,10 +71,15 @@ async fn get(
|
|||||||
|
|
||||||
let availabilities_from_user =
|
let availabilities_from_user =
|
||||||
Availability::read_by_user_and_date(pool.get_ref(), user.id, &date).await?;
|
Availability::read_by_user_and_date(pool.get_ref(), user.id, &date).await?;
|
||||||
|
println!("{availabilities_from_user:#?}");
|
||||||
|
|
||||||
let user_can_create_availabillity =
|
let user_can_create_availabillity = availabilities_from_user.len() == 0
|
||||||
!(only_one_availability_exists_and_is_whole_day(&availabilities_from_user)
|
|| !only_one_availability_exists_and_is_whole_day(&availabilities_from_user)
|
||||||
|| find_free_time_slots(&availabilities_from_user).len() == 0);
|
|| find_free_time_slots(&availabilities_from_user).len() > 0;
|
||||||
|
|
||||||
|
println!("{} || {} || {} = {user_can_create_availabillity}", availabilities_from_user.len() == 0,
|
||||||
|
!only_one_availability_exists_and_is_whole_day(&availabilities_from_user),
|
||||||
|
find_free_time_slots(&availabilities_from_user).len() > 0);
|
||||||
|
|
||||||
let mut events_and_assignments = Vec::new();
|
let mut events_and_assignments = Vec::new();
|
||||||
for e in Event::read_all_by_date_and_area_including_location(
|
for e in Event::read_all_by_date_and_area_including_location(
|
||||||
|
@ -33,8 +33,6 @@ pub async fn get(
|
|||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let whole_day_selected = query.whole_day.unwrap_or(availability.time == AvailabilityTime::WholeDay);
|
|
||||||
|
|
||||||
let suggestions = if let AvailabilityTime::Temporarily(start, end) = availability.time {
|
let suggestions = if let AvailabilityTime::Temporarily(start, end) = availability.time {
|
||||||
let availabilities = Availability::read_by_user_and_date(
|
let availabilities = Availability::read_by_user_and_date(
|
||||||
pool.get_ref(),
|
pool.get_ref(),
|
||||||
@ -51,14 +49,20 @@ pub async fn get(
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let time_selection = if query.whole_day.unwrap_or(availability.time == AvailabilityTime::WholeDay) {
|
||||||
|
AvailabilityTime::WholeDay
|
||||||
|
} else {
|
||||||
|
availability.time.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:?}", availability.time);
|
||||||
let template = NewOrEditAvailabilityTemplate {
|
let template = NewOrEditAvailabilityTemplate {
|
||||||
user: user.into_inner(),
|
user: user.into_inner(),
|
||||||
date: availability.date,
|
date: availability.date,
|
||||||
id: Some(path.id),
|
id: Some(path.id),
|
||||||
time: Some(availability.time),
|
time_selection,
|
||||||
comment: availability.comment.as_deref(),
|
comment: availability.comment.as_deref(),
|
||||||
slot_suggestions: suggestions,
|
slot_suggestions: suggestions,
|
||||||
whole_day_selected
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().body(template.render()?))
|
Ok(HttpResponse::Ok().body(template.render()?))
|
||||||
|
@ -2,7 +2,7 @@ use chrono::{NaiveDate, NaiveTime};
|
|||||||
use rinja::Template;
|
use rinja::Template;
|
||||||
|
|
||||||
use crate::filters;
|
use crate::filters;
|
||||||
use crate::models::{AvailabilityTime, Role, User};
|
use crate::models::{Availability, AvailabilityChangeset, AvailabilityTime, Role, User};
|
||||||
|
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod get_new;
|
pub mod get_new;
|
||||||
@ -17,8 +17,30 @@ struct NewOrEditAvailabilityTemplate<'a> {
|
|||||||
user: User,
|
user: User,
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
id: Option<i32>,
|
id: Option<i32>,
|
||||||
time: Option<AvailabilityTime>,
|
time_selection: AvailabilityTime,
|
||||||
whole_day_selected: bool,
|
|
||||||
comment: Option<&'a str>,
|
comment: Option<&'a str>,
|
||||||
slot_suggestions: Vec<(NaiveTime, NaiveTime)>,
|
slot_suggestions: Vec<(NaiveTime, NaiveTime)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_adjacend_availability<'a>(
|
||||||
|
changeset: &AvailabilityChangeset,
|
||||||
|
availability_id_to_be_updated: Option<i32>,
|
||||||
|
existing_availabilities: &'a [Availability],
|
||||||
|
) -> Option<&'a Availability> {
|
||||||
|
let AvailabilityTime::Temporarily(changeset_start, changeset_end) = changeset.time else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
for a in existing_availabilities
|
||||||
|
.iter()
|
||||||
|
.filter(|a| availability_id_to_be_updated.is_none_or(|id| a.id != id))
|
||||||
|
{
|
||||||
|
if let AvailabilityTime::Temporarily(start, end) = a.time {
|
||||||
|
if start == changeset_end || end == changeset_start {
|
||||||
|
return Some(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use chrono::{NaiveDate, NaiveTime};
|
use chrono::{NaiveDate, NaiveTime};
|
||||||
|
use garde::Validate;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::{Availability, User},
|
endpoints::availability::find_adjacend_availability,
|
||||||
|
models::{Availability, AvailabilityChangeset, AvailabilityContext, AvailabilityTime, User},
|
||||||
utils::{self, ApplicationError},
|
utils::{self, ApplicationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,16 +24,43 @@ pub async fn post(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
form: web::Form<AvailabillityForm>,
|
form: web::Form<AvailabillityForm>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
// TODO: create and validate changeset
|
let existing_availabilities =
|
||||||
//Availability::create(
|
Availability::read_by_user_and_date(pool.get_ref(), user.id, &form.date).await?;
|
||||||
// pool.get_ref(),
|
let context = AvailabilityContext {
|
||||||
// user.id,
|
existing_availabilities: existing_availabilities.clone(),
|
||||||
// form.date,
|
};
|
||||||
// form.from,
|
|
||||||
// form.till,
|
let time = if form.from.is_some() && form.till.is_some() {
|
||||||
// form.comment.clone(),
|
AvailabilityTime::Temporarily(form.from.unwrap(), form.till.unwrap())
|
||||||
//)
|
} else {
|
||||||
//.await?;
|
AvailabilityTime::WholeDay
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut changeset = AvailabilityChangeset {
|
||||||
|
time,
|
||||||
|
comment: form.comment.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = changeset.validate_with(&context) {
|
||||||
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(a) = find_adjacend_availability(&changeset, None, &existing_availabilities) {
|
||||||
|
let (changeset_start, changeset_end) = changeset.time.time_tuple_opt().unwrap();
|
||||||
|
let (adjacent_start, adjacent_end) = changeset.time.time_tuple_opt().unwrap();
|
||||||
|
|
||||||
|
if adjacent_end == changeset_start {
|
||||||
|
changeset.time = AvailabilityTime::Temporarily(adjacent_start, changeset_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjacent_start == changeset_end {
|
||||||
|
changeset.time = AvailabilityTime::Temporarily(changeset_start, adjacent_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
Availability::update(pool.get_ref(), a.id, changeset).await?;
|
||||||
|
} else {
|
||||||
|
Availability::create(pool.get_ref(), user.id, form.date, changeset).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let url = utils::get_return_url_for_date(&form.date);
|
let url = utils::get_return_url_for_date(&form.date);
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
|
@ -3,7 +3,7 @@ use garde::Validate;
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::{availability::post_new::AvailabillityForm, IdPath},
|
endpoints::{availability::{find_adjacend_availability, post_new::AvailabillityForm}, IdPath},
|
||||||
models::{Availability, AvailabilityChangeset, AvailabilityContext, AvailabilityTime, User},
|
models::{Availability, AvailabilityChangeset, AvailabilityContext, AvailabilityTime, User},
|
||||||
utils::{self, ApplicationError},
|
utils::{self, ApplicationError},
|
||||||
};
|
};
|
||||||
@ -44,22 +44,7 @@ pub async fn post(
|
|||||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut adjacent = None;
|
if let Some(a) = find_adjacend_availability(&changeset, Some(availability.id), &existing_availabilities) {
|
||||||
if let AvailabilityTime::Temporarily(changeset_start, changeset_end) = changeset.time {
|
|
||||||
for a in existing_availabilities
|
|
||||||
.iter()
|
|
||||||
.filter(|a| a.id != availability.id)
|
|
||||||
{
|
|
||||||
if let AvailabilityTime::Temporarily(start, end) = a.time {
|
|
||||||
if start == changeset_end || end == changeset_start {
|
|
||||||
adjacent = Some(a);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(a) = adjacent {
|
|
||||||
let (changeset_start, changeset_end) = changeset.time.time_tuple_opt().unwrap();
|
let (changeset_start, changeset_end) = changeset.time.time_tuple_opt().unwrap();
|
||||||
let (adjacent_start, adjacent_end) = changeset.time.time_tuple_opt().unwrap();
|
let (adjacent_start, adjacent_end) = changeset.time.time_tuple_opt().unwrap();
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ fn time_is_not_already_made_available(
|
|||||||
return Err(garde::Error::new("cant create a availability while an other availability for the whole day is already present"));
|
return Err(garde::Error::new("cant create a availability while an other availability for the whole day is already present"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if find_free_time_slots(&context.existing_availabilities).len() == 0 {
|
if context.existing_availabilities.len() > 0 && find_free_time_slots(&context.existing_availabilities).len() == 0 {
|
||||||
return Err(garde::Error::new(
|
return Err(garde::Error::new(
|
||||||
"cant create a availability as every time slot is already filled",
|
"cant create a availability as every time slot is already filled",
|
||||||
));
|
));
|
||||||
@ -71,7 +71,7 @@ pub fn find_free_time_slots(availabilities: &[Availability]) -> Vec<(NaiveTime,
|
|||||||
if times.len() == 0 {
|
if times.len() == 0 {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
//println!("zeiten {times:?}");
|
println!("zeiten {times:?}");
|
||||||
|
|
||||||
times.sort();
|
times.sort();
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<input type="hidden" name="date" value="{{ date }}">
|
<input type="hidden" name="date" value="{{ date }}">
|
||||||
|
{% let whole_day_selected = time_selection == AvailabilityTime::WholeDay %}
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
@ -46,25 +47,21 @@
|
|||||||
<label class="label">Von - Bis</label>
|
<label class="label">Von - Bis</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
{% let times = ("00:00".to_string(), "23:59".to_string()) -%}
|
{% let times = time_selection.time_tuple() -%}
|
||||||
{% if let Some(times) = time -%}
|
|
||||||
{% if let AvailabilityTime::Temporarily(start, end) = times -%}
|
|
||||||
{% let times = (start.to_string(), end.to_string()) -%}
|
|
||||||
{% endif -%}
|
|
||||||
{% endif -%}
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input class="input" type="time" id="from" name="from" value='{{ times.0 }}' {{
|
<input class="input" type="time" id="from" name="from" value='{{ times.0.format("%R") }}' {{
|
||||||
whole_day_selected|cond_show("disabled") }} {{ whole_day_selected|invert|ref|cond_show("required") }}>
|
whole_day_selected|cond_show("disabled") }} {{ whole_day_selected|invert|ref|cond_show("required") }}>
|
||||||
|
{% if slot_suggestions.len() > 0 %}
|
||||||
<p class="help">noch mögliche Zeiträume:</p>
|
<p class="help">noch mögliche Zeiträume:</p>
|
||||||
<div class="tags help">
|
<div class="tags help">
|
||||||
{% for (s, e) in slot_suggestions %}
|
{% for (s, e) in slot_suggestions %}
|
||||||
<span class="tag">{{ s }} - {{ e }}</span>
|
<span class="tag">{{ s }} - {{ e }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input class="input" type="time" id="till" name="till" value='{{ times.1 }}' {{
|
<input class="input" type="time" id="till" name="till" value='{{ times.1.format("%R") }}' {{
|
||||||
whole_day_selected|cond_show("disabled") }} {{ whole_day_selected|invert|ref|cond_show("required") }}>
|
whole_day_selected|cond_show("disabled") }} {{ whole_day_selected|invert|ref|cond_show("required") }}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user