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

174 lines
5.8 KiB
Rust

use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
use chrono::{NaiveDate, NaiveTime};
use garde::Validate;
use serde::Deserialize;
use sqlx::PgPool;
use crate::{
endpoints::IdPath,
models::{
Assignment, AssignmentChangeset, Availability, Event, EventChangeset,
EventContext, Function, Location, Role, User,
},
utils::{self, ApplicationError},
};
#[derive(Deserialize)]
pub struct EditEventForm {
name: String,
date: NaiveDate,
from: NaiveTime,
till: NaiveTime,
location: i32,
voluntarywachhabender: Option<bool>,
voluntaryfuehrungsassistent: Option<bool>,
amount: i16,
clothing: String,
note: Option<String>,
}
#[actix_web::post("/events/{id}/edit")]
pub async fn post(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
form: web::Form<EditEventForm>,
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 {
date: form.date,
amount_of_posten: form.amount,
clothing: form.clothing.clone(),
location_id: form.location,
time: (form.from, form.till),
name: form.name.clone(),
note: form
.note
.clone()
.and_then(|n| if !n.is_empty() { Some(n) } else { None }),
voluntary_wachhabender: form.voluntarywachhabender.unwrap_or(false),
voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent.unwrap_or(false),
};
let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
let mut common_time = (
NaiveTime::parse_from_str("00:00", "%R").unwrap(),
NaiveTime::parse_from_str("23:59", "%R").unwrap(),
);
for assignment in &assignments_for_event {
let availability = Availability::read_by_id(pool.get_ref(), assignment.availabillity_id)
.await?
.unwrap();
let all_assignments =
Assignment::read_all_by_availabillity(pool.get_ref(), assignment.availabillity_id)
.await?;
if all_assignments.len() == 1 {
// TODO: refactor
if availability.start.time() > common_time.0 {
common_time.0 = availability.start.time();
}
if availability.end.time() < common_time.1 {
common_time.1 = availability.end.time();
}
} else {
// TODO: refactor
let mut slots = vec![(availability.start.time(), availability.end.time())];
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_time && s.1 <= a.end_time);
slots = rest;
let fit = fit.first().unwrap();
if fit.0 != a.start_time {
slots.push((fit.0, a.start_time));
}
if fit.1 != a.end_time {
slots.push((a.end_time, fit.1));
}
}
let slot = slots
.into_iter()
.find(|s| s.0 >= assignment.start_time && s.1 <= assignment.end_time)
.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.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_time != changeset.time.0 || event.end_time != 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.availabillity_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())
}