brass/web/src/endpoints/events/post_edit.rs

157 lines
5.3 KiB
Rust

use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
use chrono::Days;
use garde::Validate;
use sqlx::PgPool;
use crate::{
endpoints::{events::NewOrEditEventForm, IdPath},
utils::{self, ApplicationError},
END_OF_DAY, START_OF_DAY,
};
use brass_db::models::{
Assignment, AssignmentChangeset, Availability, Event, EventChangeset, EventContext, Function,
Location, Role, User,
};
#[actix_web::post("/events/{id}/edit")]
pub async fn post(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
form: web::Form<NewOrEditEventForm>,
path: web::Path<IdPath>,
) -> Result<impl Responder, ApplicationError> {
if user.role != Role::Admin && user.role != Role::AreaManager {
return Err(ApplicationError::Unauthorized);
}
let Some(event) = Event::read_by_id_including_location(pool.get_ref(), path.id).await? else {
return Ok(HttpResponse::NotFound().finish());
};
if event.location_id != form.location {
let Some(location) = Location::read_by_id(pool.get_ref(), form.location).await? else {
return Ok(HttpResponse::BadRequest().body("Location does not exist"));
};
if user.role != Role::Admin && user.area_id != location.area_id {
return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area"));
}
if event.location.as_ref().unwrap().area_id != location.area_id {
return Ok(HttpResponse::BadRequest()
.body("Can't change to a location outside of previous location area"));
}
}
let changeset = EventChangeset {
amount_of_posten: form.amount,
clothing: form.clothing,
location_id: form.location,
time: (form.date.and_time(form.start), form.end),
name: form.name.clone(),
note: form
.note
.clone()
.and_then(|n| if !n.is_empty() { Some(n) } else { None }),
voluntary_wachhabender: form.voluntarywachhabender,
voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent,
};
let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
let start = event.start.date();
let end = event.start.date().checked_add_days(Days::new(1)).unwrap();
let mut common_time = (start.and_time(START_OF_DAY), end.and_time(END_OF_DAY));
for assignment in &assignments_for_event {
let availability = Availability::read_by_id(pool.get_ref(), assignment.availability_id)
.await?
.unwrap();
let all_assignments =
Assignment::read_all_by_availability(pool.get_ref(), assignment.availability_id)
.await?;
if all_assignments.len() == 1 {
if availability.start > common_time.0 {
common_time.0 = availability.start;
}
if availability.end < common_time.1 {
common_time.1 = availability.end;
}
} else {
let mut slots = vec![(availability.start, availability.end)];
for a in all_assignments
.iter()
.filter(|x| x.event_id != assignment.event_id)
{
let (fit, rest) = slots
.into_iter()
.partition(|s| s.0 >= a.start && s.1 <= a.end);
slots = rest;
let fit = fit.first().unwrap();
if fit.0 != a.start {
slots.push((fit.0, a.start));
}
if fit.1 != a.end {
slots.push((a.end, fit.1));
}
}
let slot = slots
.into_iter()
.find(|s| s.0 >= assignment.start && s.1 <= assignment.end)
.unwrap();
if slot.0 > common_time.0 {
common_time.0 = slot.0;
}
if slot.1 < common_time.1 {
common_time.1 = slot.1;
}
}
}
let context = Some(EventContext {
date_in_db: event.start.date(),
common_min_max_available_time: common_time,
// safe as max amount of posten can be only 100
amount_of_assigned_posten: assignments_for_event
.iter()
.filter(|a| a.function == Function::Posten)
.count() as i16,
wachhabender_assigned: assignments_for_event
.iter()
.any(|a| a.function == Function::Wachhabender),
fuehrungsassistent_assigned: assignments_for_event
.iter()
.any(|a| a.function == Function::Fuehrungsassistent),
});
if let Err(e) = changeset.validate_with(&context) {
return Ok(HttpResponse::BadRequest().body(e.to_string()));
};
if event.start != changeset.time.0 || event.end != changeset.time.1 {
for a in assignments_for_event {
let c = AssignmentChangeset {
function: a.function,
time: changeset.time,
};
Assignment::update(pool.get_ref(), a.event_id, a.availability_id, c).await?;
}
}
Event::update(pool.get_ref(), event.id, changeset).await?;
let url = utils::get_return_url_for_date(&form.date);
//println!("redirecto to {url}");
Ok(HttpResponse::Found()
.insert_header((LOCATION, url.clone()))
.insert_header(("HX-LOCATION", url))
.finish())
}