feat: create new assignment

This commit is contained in:
Max Hohlfeld 2024-05-12 15:23:39 +02:00
parent 74acc02e47
commit 2f5cc33261
6 changed files with 120 additions and 10 deletions

View File

@ -55,13 +55,13 @@ CREATE TABLE event
canceled BOOLEAN NOT NULL DEFAULT false canceled BOOLEAN NOT NULL DEFAULT false
); );
CREATE TABLE assignement CREATE TABLE assignment
( (
eventId INTEGER REFERENCES event (id), eventId INTEGER REFERENCES event (id),
availabillityId INTEGER REFERENCES availabillity (id), availabillityId INTEGER REFERENCES availabillity (id),
function function NOT NULL, function function NOT NULL,
startTime TIME, startTime TIME NOT NULL,
endTime TIME, endTime TIME NOT NULL,
PRIMARY KEY (eventId, availabillityId) PRIMARY KEY (eventId, availabillityId)
); );

View File

@ -2,8 +2,11 @@ use actix_web::{web, HttpResponse, Responder};
use serde::Deserialize; use serde::Deserialize;
use sqlx::PgPool; use sqlx::PgPool;
use crate::models::{Assignment, Event, Function};
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct NewAssignmentsForm { pub struct NewAssignmentsForm {
event: i32,
wachhabender: Option<i32>, wachhabender: Option<i32>,
posten: Vec<i32> posten: Vec<i32>
} }
@ -11,5 +14,41 @@ pub struct NewAssignmentsForm {
#[actix_web::post("/assignments/new")] #[actix_web::post("/assignments/new")]
pub async fn post(pool: web::Data<PgPool>, form: web::Form<NewAssignmentsForm>) -> impl Responder { pub async fn post(pool: web::Data<PgPool>, form: web::Form<NewAssignmentsForm>) -> impl Responder {
let event = Event::read_by_id_including_location(&pool, form.event).await.unwrap(); // TODO: Check if location is needed
if event.voluntary_wachhabender && form.wachhabender.is_some() && form.posten.contains(&form.wachhabender.unwrap()) {
return HttpResponse::BadRequest().body("Wachhabender kann nicht zugleich Posten sein!");
}
let mut joined_ids = form.posten.clone();
if let Some(id) = form.wachhabender {
joined_ids.push(id);
}
for availabillity_id in joined_ids {
let assignments = Assignment::read_by_availabillity(pool.get_ref(), availabillity_id).await.unwrap();
let mut can_be_used = true;
for assignment in assignments {
if event.start_time >= assignment.start_time && event.start_time <= assignment.end_time {
can_be_used = false;
break;
}
}
if !can_be_used {
return HttpResponse::BadRequest().body("availabillity time slot bereits genutzt!");
}
}
if let Some(id) = form.wachhabender {
Assignment::create(pool.get_ref(), id, event.id, Function::Wachhabender, event.start_time, event.end_time).await.unwrap();
}
for id in &form.posten {
Assignment::create(pool.get_ref(), *id, event.id, Function::Posten, event.start_time, event.end_time).await.unwrap();
}
HttpResponse::Ok().finish() HttpResponse::Ok().finish()
} }

View File

@ -1,5 +1,5 @@
use chrono::NaiveTime; use chrono::NaiveTime;
use sqlx::PgPool; use sqlx::{query, PgPool};
use super::Function; use super::Function;
@ -8,12 +8,66 @@ pub struct Assignment {
pub availabillity_id: i32, pub availabillity_id: i32,
pub function: Function, pub function: Function,
pub start_time: NaiveTime, pub start_time: NaiveTime,
pub end_time: NaiveTime pub end_time: NaiveTime,
} }
impl Assignment { impl Assignment {
pub async fn create(pool: &PgPool) -> anyhow::Result<i32> { pub async fn create(
pool: &PgPool,
availabillity_id: i32,
event_id: i32,
function: Function,
start_time: NaiveTime,
end_time: NaiveTime,
) -> anyhow::Result<()> {
query!(
r##"
INSERT INTO assignment (eventId, availabillityId, function, startTime, endTime)
VALUES ($1, $2, $3, $4, $5);
"##,
availabillity_id,
event_id,
function as Function,
start_time,
end_time
)
.execute(pool)
.await?;
Ok(0) Ok(())
}
pub async fn read_by_availabillity(
pool: &PgPool,
availabillity_id: i32,
) -> anyhow::Result<Vec<Assignment>> {
let records = query!(
r##"
SELECT
assignment.eventId,
assignment.availabillityId,
assignment.function AS "function: Function",
assignment.startTime,
assignment.endTime
FROM assignment
WHERE assignment.AvailabillityId = $1;
"##,
availabillity_id
)
.fetch_all(pool)
.await?;
let assignemnets = records
.iter()
.map(|r| Assignment {
event_id: r.eventid,
availabillity_id: r.availabillityid,
function: r.function,
start_time: r.starttime,
end_time: r.endtime,
})
.collect();
Ok(assignemnets)
} }
} }

View File

@ -139,7 +139,7 @@ impl Availabillity {
user_.lastLogin, user_.lastLogin,
user_.receiveNotifications user_.receiveNotifications
FROM availabillity FROM availabillity
LEFT JOIN assignement ON availabillity.Id = assignement.availabillityId LEFT JOIN assignment ON availabillity.Id = assignment.availabillityId
JOIN user_ ON availabillity.userId = user_.id JOIN user_ ON availabillity.userId = user_.id
WHERE availabillity.date = $1; WHERE availabillity.date = $1;
"##, "##,

View File

@ -1,5 +1,5 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use sqlx::{Execute, PgPool}; use sqlx::PgPool;
use super::{Area, Function, Role}; use super::{Area, Function, Role};

View File

@ -3,7 +3,9 @@
{% block content %} {% block content %}
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<form method="post" action="/users/new"> <form method="post" action="/assignments/new">
<input type="hidden" name="event" value="{{ event.id }}" />
<h1 class="title">Planung für {{ event.name }}</h1> <h1 class="title">Planung für {{ event.name }}</h1>
<div class="box"> <div class="box">
{% if event.canceled %}<b>Veranstaltung abgesagt!</b>{% endif %} {% if event.canceled %}<b>Veranstaltung abgesagt!</b>{% endif %}
@ -45,6 +47,7 @@
<div class="field"> <div class="field">
<label class="label">Posten</label> <label class="label">Posten</label>
<p class="help">Mehrfachauswahl mit Strg+Mausklick</p>
<div class="select is-multiple"> <div class="select is-multiple">
<select name="posten" multiple size="{{ available_posten.len() }}"> <select name="posten" multiple size="{{ available_posten.len() }}">
{% for a in available_posten %} {% for a in available_posten %}
@ -79,6 +82,20 @@
</div> </div>
</div> </div>
<div class="field is-horizontal">
<div class="field-label"></div>
<div class="field-body">
<div class="field is-grouped">
<div class="control">
<input class="button is-link" type="submit" value="Erstellen">
</div>
<div class="control">
<a class="button is-link is-light" href="/">Zurück</a>
</div>
</div>
</div>
</div>
</form> </form>
</div> </div>
</section> </section>