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, pool: web::Data, form: web::Form, path: web::Path, ) -> Result { 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()) }