feat: delete event and list assignments in overview

This commit is contained in:
Max Hohlfeld 2025-01-17 22:44:17 +01:00
parent 0bb5f54f16
commit ee9c4b1ac1
10 changed files with 175 additions and 18 deletions

View File

@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM event WHERE id = $1;",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": []
},
"hash": "4bfce344fc6f0bfbb2b460677ce3eba3793ecf697355e81012e721357ba351b9"
}

View File

@ -1,4 +1,8 @@
use crate::{filters, utils::ApplicationError};
use crate::{
filters,
models::{Assignment, Function},
utils::ApplicationError,
};
use actix_web::{web, HttpResponse, Responder};
use chrono::{NaiveDate, Utc};
use rinja::Template;
@ -20,7 +24,7 @@ struct CalendarTemplate {
date: NaiveDate,
selected_area: Option<i32>,
areas: Vec<Area>,
events: Vec<Event>,
events_and_assignments: Vec<(Event, Vec<String>, Option<String>, Option<String>)>,
availabillities: Vec<Availabillity>,
}
@ -48,13 +52,6 @@ async fn get(
None => None,
};
let events = Event::read_all_by_date_and_area_including_location(
pool.get_ref(),
date,
query.area.unwrap_or(user.area_id),
)
.await?;
let availabillities = Availabillity::read_by_date_and_area_including_user(
pool.get_ref(),
date,
@ -62,12 +59,74 @@ async fn get(
)
.await?;
let mut events_and_assignments = Vec::new();
for e in Event::read_all_by_date_and_area_including_location(
pool.get_ref(),
date,
query.area.unwrap_or(user.area_id),
)
.await?
.into_iter()
{
let assignments = Assignment::read_all_by_event(pool.get_ref(), e.id).await?;
let (posten, rest): (Vec<Assignment>, Vec<Assignment>) = assignments
.into_iter()
.partition(|a| a.function == Function::Posten);
let (wachhabender, fuehrungsassistent): (Vec<Assignment>, Vec<Assignment>) = rest
.into_iter()
.partition(|a| a.function == Function::Wachhabender);
events_and_assignments.push((
e,
posten
.into_iter()
.map(|p| {
availabillities
.iter()
.find(|a| a.id == p.availabillity_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
})
.collect(),
fuehrungsassistent.first().and_then(|fa| {
Some(
availabillities
.iter()
.find(|a| a.id == fa.availabillity_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone(),
)
}),
wachhabender.first().and_then(|wh| {
Some(
availabillities
.iter()
.find(|a| a.id == wh.availabillity_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone(),
)
}),
));
}
let template = CalendarTemplate {
user: user.into_inner(),
date,
selected_area,
areas,
events,
events_and_assignments,
availabillities,
};

View File

@ -0,0 +1,40 @@
use actix_web::{web, HttpResponse, Responder};
use sqlx::PgPool;
use crate::{
endpoints::IdPath,
models::{Assignment, Event, Role, User},
utils::{self, ApplicationError},
};
#[actix_web::delete("/events/{id}")]
pub async fn delete(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
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 user.role != Role::Admin && user.area_id != event.location.as_ref().unwrap().area_id {
return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area"));
}
let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
if assignments_for_event.len() > 0 {
return Ok(HttpResponse::BadRequest().body("Can't delete event when people are assigned"));
}
Event::delete(pool.get_ref(), event.id).await?;
let url = utils::get_return_url_for_date(&event.date);
Ok(HttpResponse::Ok()
.insert_header(("HX-LOCATION", url))
.finish())
}

View File

@ -4,11 +4,12 @@ use rinja::Template;
use crate::models::{Event, Location, Role, User};
pub mod delete;
pub mod get_edit;
pub mod get_new;
pub mod get_plan;
pub mod post_new;
pub mod post_edit;
pub mod post_new;
pub mod put_cancelation;
#[derive(Template)]

View File

@ -1,4 +1,3 @@
use actix_http::header::LOCATION;
use actix_web::{web, HttpResponse, Responder};
use sqlx::PgPool;
@ -49,7 +48,6 @@ async fn handle_set_event_cancelation_to(
}
let url = utils::get_return_url_for_date(&event.date);
println!("redirecto to {url}");
Ok(HttpResponse::Ok()
.insert_header(("HX-LOCATION", url))
.finish())

View File

@ -63,6 +63,7 @@ pub fn init(cfg: &mut ServiceConfig) {
cfg.service(events::get_plan::get);
cfg.service(events::get_edit::get);
cfg.service(events::post_edit::post);
cfg.service(events::delete::delete);
cfg.service(assignment::post_new::post);
cfg.service(assignment::delete::delete);

View File

@ -16,7 +16,7 @@ impl Assignment {
pool: &PgPool,
event_id: i32,
availabillity_id: i32,
changeset: AssignmentChangeset
changeset: AssignmentChangeset,
) -> Result<()> {
query!(
r##"

View File

@ -160,7 +160,21 @@ impl Event {
}
pub async fn update_cancelation(pool: &PgPool, id: i32, canceled: bool) -> Result<()> {
query!("UPDATE event SET canceled = $1 WHERE id = $2;", canceled, id).execute(pool).await?;
query!(
"UPDATE event SET canceled = $1 WHERE id = $2;",
canceled,
id
)
.execute(pool)
.await?;
Ok(())
}
pub async fn delete(pool: &PgPool, id: i32) -> Result<()> {
query!("DELETE FROM event WHERE id = $1;", id)
.execute(pool)
.await?;
Ok(())
}

View File

@ -30,13 +30,21 @@
</button>
</div>
<div class="control">
<button class="button is-danger" type="button">
{% let delete_disabled = amount_of_planned_posten > 0 || is_wachhabender_planned ||
is_fuehrungsassistent_planned %}
<button class="button is-danger" type="button" hx-delete="/events/{{ event.id }}" {{
delete_disabled|cond_show("disabled") }} hx-trigger="confirmed">
<svg class="icon">
<use href="/static/feather-sprite.svg#x-circle" />
</svg>
<span>Löschen</span>
</button>
</div>
{% if delete_disabled %}
<p class="help is-align-content-center">
Löschen nicht möglich, da bereits eine Planung existiert.
</p>
{% endif %}
</div>
</div>
</div>

View File

@ -69,12 +69,12 @@
{% endif %}
</div>
{% if events.len() == 0 %}
{% if events_and_assignments.len() == 0 %}
<div class="box">
<h5 class="subtitle is-5">keine Events geplant</h5>
</div>
{% else %}
{% for event in events %}
{% for (event, posten, fuehrungsassistent, wachhabender) in events_and_assignments %}
<div class="box">
<div class="fixed-grid has-1-cols-mobile">
<div class="grid content">
@ -135,6 +135,28 @@
</div>
{% endif %}
<div class="cell is-col-span-2">
<hr class="my-1">
</div>
{% if let Some(wh) = wachhabender %}
<div class="cell">
<p><b>Wachhabender geplant:</b> {{ wh }}</p>
</div>
{% endif %}
{% if let Some(fa) = fuehrungsassistent %}
<div class="cell">
<p><b>fuehrungsassistent geplant:</b> {{ fa }}</p>
</div>
{% endif %}
{% if posten.len() > 0 %}
<div class="cell is-col-span-2">
<p><b>Posten:</b> {{ posten.join(", ")}}</p>
</div>
{% endif %}
</div>
</div>
</div>