feat: add function to db and event

This commit is contained in:
Max Hohlfeld 2024-12-06 21:30:38 +01:00
parent 7bf892207a
commit 58bcee940f
17 changed files with 113 additions and 28 deletions

View File

@ -49,7 +49,8 @@ CREATE TABLE event
endTime TIME NOT NULL,
name TEXT NOT NULL,
locationId INTEGER NOT NULL REFERENCES location (id) ON DELETE CASCADE,
voluntaryWachhabender BOOLEAN NOT NULL,
voluntaryWachhabender BOOLEAN NOT NULL,
voluntaryFuehrungsassistent BOOLEAN NOT NULL,
amountOfPosten SMALLINT NOT NULL CHECK (amountOfPosten >= 0),
clothing TEXT NOT NULL,
note TEXT,

View File

@ -1,5 +1,3 @@
use std::ops::AddAssign;
use actix_web::{web, HttpResponse, Responder};
use askama_actix::TemplateToResponse;
use serde::Deserialize;
@ -69,7 +67,7 @@ pub async fn post(
let a = Assignment::count_by_event_and_function(pool.get_ref(), event.id, function).await?;
let event_already_has_enough_assignments_for_function = match function {
Function::Posten => a >= event.amount_of_posten as i64,
Function::Fuehrungsassistent => event.voluntary_wachhabender && a >= 1,
Function::Fuehrungsassistent => event.voluntary_fuehrungsassistent && a >= 1,
Function::Wachhabender => event.voluntary_wachhabender && a >= 1,
};

View File

@ -13,6 +13,7 @@ pub struct NewEventForm {
till: NaiveTime,
location: i32,
voluntarywachhabender: Option<bool>,
voluntaryfuehrungsassistent: Option<bool>,
amount: i16,
clothing: String,
note: Option<String>
@ -35,7 +36,8 @@ pub async fn post(
&form.till,
&form.name,
form.location,
form.voluntarywachhabender.is_some() && form.voluntarywachhabender.unwrap(),
form.voluntarywachhabender.unwrap_or(false),
form.voluntaryfuehrungsassistent.unwrap_or(false),
form.amount,
&form.clothing,
form.note.as_ref()

View File

@ -11,6 +11,7 @@ mod location;
mod user;
mod imprint;
mod vehicle;
mod vehicle_assignment;
#[derive(Deserialize)]
pub struct IdPath {

View File

@ -3,17 +3,22 @@ use askama::Template;
use askama_actix::TemplateToResponse;
use sqlx::PgPool;
use crate::models::{Area, Function, Role, User};
use crate::{
filters,
models::{Area, Role, User},
};
#[derive(Template)]
#[template(path = "user/profile.html")]
struct ProfileTemplate {
user: User
user: User,
}
#[actix_web::get("/profile")]
pub async fn get(user: web::ReqData<User>, pool: web::Data<PgPool>) -> impl Responder {
let area = Area::read_by_id(pool.get_ref(), user.area_id).await.unwrap();
let area = Area::read_by_id(pool.get_ref(), user.area_id)
.await
.unwrap();
let mut user = user.into_inner();
user.area = area;

View File

@ -0,0 +1 @@
mod post_new;

View File

@ -0,0 +1,36 @@
use actix_web::{web, HttpResponse, Responder};
use sqlx::PgPool;
use crate::{models::{Event, Role, User, Vehicle}, utils::ApplicationError};
pub struct VehicleAssignmentQuery {
event: i32,
vehicle: i32
}
#[actix_web::post("/vehicle-assignment")]
pub async fn post_new(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
query: web::Query<VehicleAssignmentQuery>
) -> Result<impl Responder, ApplicationError> {
let Some(event) = Event::read_by_id_including_location(pool.get_ref(), query.event).await?
else {
return Ok(HttpResponse::NotFound().finish());
};
let user_is_admin_or_area_manager_of_event_area = user.role == Role::Admin
|| (user.role == Role::AreaManager
&& user.area_id == event.location.as_ref().unwrap().area_id);
if !user_is_admin_or_area_manager_of_event_area {
return Err(ApplicationError::Unauthorized);
}
let Some(vehicle) = Vehicle::read(pool.get_ref(), query.vehicle).await? else {
return Ok(HttpResponse::NotFound().finish());
};
}

View File

@ -13,6 +13,7 @@ pub struct Event {
pub location_id: i32,
pub location: Option<Location>,
pub voluntary_wachhabender: bool,
pub voluntary_fuehrungsassistent: bool,
pub amount_of_posten: i16,
pub clothing: String,
pub canceled: bool,
@ -28,15 +29,16 @@ impl Event {
name: &String,
location_id: i32,
voluntary_wachhabender: bool,
voluntary_fuehrungsassistent: bool,
amount_of_posten: i16,
clothing: &String,
note: Option<&String>,
) -> Result<()> {
query!(r#"
INSERT INTO event (date, startTime, endTime, name, locationId, voluntaryWachhabender, amountOfPosten, clothing, note)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
INSERT INTO event (date, startTime, endTime, name, locationId, voluntaryWachhabender, voluntaryFuehrungsassistent, amountOfPosten, clothing, note)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
"#,
date, start_time, end_time, name, location_id, voluntary_wachhabender, amount_of_posten, clothing, note).execute(pool).await?;
date, start_time, end_time, name, location_id, voluntary_wachhabender, voluntary_fuehrungsassistent, amount_of_posten, clothing, note).execute(pool).await?;
Ok(())
}
@ -56,6 +58,7 @@ impl Event {
event.name,
event.locationId,
event.voluntaryWachhabender,
event.voluntaryFuehrungsassistent,
event.amountOfPosten,
event.clothing,
event.canceled,
@ -90,6 +93,7 @@ impl Event {
area: None,
}),
voluntary_wachhabender: record.voluntarywachhabender,
voluntary_fuehrungsassistent: record.voluntaryfuehrungsassistent,
amount_of_posten: record.amountofposten,
clothing: record.clothing.to_string(),
canceled: record.canceled,
@ -111,6 +115,7 @@ impl Event {
event.name,
event.locationId,
event.voluntaryWachhabender,
event.voluntaryFuehrungsassistent,
event.amountOfPosten,
event.clothing,
event.canceled,
@ -142,6 +147,7 @@ impl Event {
area: None,
}),
voluntary_wachhabender: record.voluntarywachhabender,
voluntary_fuehrungsassistent: record.voluntaryfuehrungsassistent,
amount_of_posten: record.amountofposten,
clothing: record.clothing.to_string(),
canceled: record.canceled,

View File

@ -28,6 +28,7 @@ impl TryFrom<u8> for Function {
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Function::Posten),
5 => Ok(Function::Fuehrungsassistent),
10 => Ok(Function::Wachhabender),
_ => Err(ApplicationError::UnsupportedEnumValue {
value: value.to_string(),

View File

@ -36,6 +36,26 @@ impl Vehicle {
Ok(vehicles)
}
pub async fn read_all_assignable_for_event(pool: &PgPool, event_id: i32) -> Result<Vec<Vehicle>> {
let records = query!(r#"
SELECT *
FROM vehicle
;"#).fetch_all(pool).await?;
let vehicles = records
.into_iter()
.map(|v| Vehicle {
id: v.id,
radio_call_name: v.radiocallname,
station: v.station,
})
.collect();
Ok(vehicles)
}
pub async fn read(pool: &PgPool, id: i32) -> Result<Option<Vehicle>> {
let record = query!("SELECT * FROM vehicle WHERE id = $1;", id)
.fetch_optional(pool)

View File

@ -73,8 +73,7 @@ pub async fn generate_status_whether_staff_is_required(
.count()
< event.amount_of_posten as usize;
// TODO change to Fuehrungsassistent
let further_fuehrungsassistent_required = event.voluntary_wachhabender
let further_fuehrungsassistent_required = event.voluntary_fuehrungsassistent
&& existing_assignments_for_event
.iter()
.all(|a| a.function != Function::Fuehrungsassistent);

View File

@ -70,6 +70,21 @@
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Führungsassistent durch FF gestellt?</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<label class="checkbox">
<input class="checkbox" type="checkbox" name="voluntaryfuehrungsassistent" value="true">
</label>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzahl der Posten</label>

View File

@ -31,7 +31,7 @@
</div>
<div class="cell">
<p><b>Führungsassistent:</b> {% if event.voluntary_wachhabender %}FF{% else %}BF{% endif %}</p>
<p><b>Führungsassistent:</b> {% if event.voluntary_fuehrungsassistent %}FF{% else %}BF{% endif %}</p>
</div>
<div class="cell is-col-span-2">
@ -51,13 +51,6 @@
<div class="box">
<h5 class="title is-5">Einteilung Personal</h5>
<div class="icon-text">
<svg class="icon has-text-success">
<use href="/static/feather-sprite.svg#check" />
</svg>
<span>Alle Plätze verplant!</span>
</div>
{% include "plan_personal_table.html" %}
</div>

View File

@ -1,3 +1,12 @@
{% if !further_posten_required && !further_fuehrungsassistent_required && !further_wachhabender_required %}
<div class="icon-text">
<svg class="icon has-text-success">
<use href="/static/feather-sprite.svg#check" />
</svg>
<span>Alle Plätze verplant!</span>
</div>
{% endif %}
<table class="table is-fullwidth">
<thead>
<tr>

View File

@ -69,6 +69,8 @@
<div class="select is-fullwidth">
<select name="function">
<option value="1" {{ function|is_some_and_eq(1|as_ref)|cond_show("selected") }}>Posten</option>
<option value="5" {{ function|is_some_and_eq(5|as_ref)|cond_show("selected") }}>Führungsassistent
</option>
<option value="10" {{ function|is_some_and_eq(10|as_ref)|cond_show("selected") }}>Wachhabender
</option>
</select>

View File

@ -59,6 +59,8 @@
{% match u.function %}
{% when Function::Posten %}
<span class="tag is-primary is-light">Posten</span>
{% when Function::Fuehrungsassistent %}
<span class="tag is-primary is-light">Führungsassistent</span>
{% when Function::Wachhabender %}
<span class="tag is-primary">Wachhabender</span>
{% else %}

View File

@ -62,13 +62,7 @@
<div class="field-body">
<div class="field">
<div class="control">
{% match user.function %}
{% when Function::Posten %}
<span class="tag is-primary is-light">Posten</span>
{% when Function::Wachhabender %}
<span class="tag is-primary">Wachhabender</span>
{% else %}
{% endmatch %}
{{ user.function|show_tree|safe }}
</div>
</div>
</div>