feat: get edit event

This commit is contained in:
Max Hohlfeld 2025-01-04 21:56:08 +01:00
parent 8688215f17
commit e58ab878b6
10 changed files with 447 additions and 112 deletions

View File

@ -0,0 +1,46 @@
{
"db_name": "PostgreSQL",
"query": "SELECT location.id AS locationId, location.name, location.areaId, area.id, area.name AS areaName FROM location JOIN area ON location.areaId = area.id WHERE areaId = $1;",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "locationid",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "areaid",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 4,
"name": "areaname",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
false,
false,
false,
false,
false
]
},
"hash": "70850ec3f7c519c1fc104fead6a44d07ba76023567bc6ea0eec2267d1c592479"
}

1
Cargo.lock generated
View File

@ -1337,6 +1337,7 @@ version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "661cb0601b5f4050d1e65452c5b0ea555c0b3e88fb5ed7855906adc6c42523ef"
dependencies = [
"chrono",
"deunicode",
"rand",
]

View File

@ -41,4 +41,4 @@ change-detection = "1.2.0"
[dev-dependencies]
insta = "1.41.1"
fake = "3.0.1"
fake = { version = "3.0.1", features = ["chrono"]}

View File

@ -0,0 +1,158 @@
---
source: web/src/endpoints/events/get_edit.rs
expression: body
snapshot_kind: text
---
<section class="section">
<div class="container">
<form method="post" action="/events/1/edit">
<h1 class="title">Event 'Vorstellung' bearbeiten</h1>
<input type="hidden" name="date" value="2025-01-01">
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsname</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="name" placeholder="Wave Gotik Treffen" required value="Vorstellung" />
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Startzeit - Endzeit</label>
</div>
<div class="field-body">
<div class="field">
<input class="input" type="time" id="from" name="from" required value="08:00" />
</div>
<div class="field">
<input class="input" type="time" id="till" name="till" required value="10:00" />
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsort</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="location" required>
<option value="1">Hauptbahnhof - (Leipzig Ost)
selected
</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Wachhabender 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="voluntarywachhabender" value="true" >
</label>
</div>
</div>
</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>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" type="number" name="amount"
min="1" max="100" required
value="2" />
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzugsordnung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="clothing" placeholder="Tuchuniform" required
value="Tuchuniform" />
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anmerkung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="note" />
</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" hx-boost="true" href="/?date=2025-01-01">Zurück</a>
</div>
</div>
</div>
</div>
</form>
</div>
</section>

View File

@ -26,7 +26,7 @@ pub async fn get(
return Ok(HttpResponse::NotFound().finish());
};
if availabillity.user_id == user.id {
if availabillity.user_id != user.id {
return Err(ApplicationError::Unauthorized);
}

View File

@ -1,7 +1,15 @@
use actix_web::{web, HttpResponse, Responder};
use brass_macros::db_test;
use rinja::Template;
use sqlx::PgPool;
#[cfg(test)]
use crate::utils::test_helper::{
assert_snapshot, read_body, test_get, DbTestContext, RequestConfig, StatusCode,
};
#[cfg(test)]
use chrono::{NaiveDate, NaiveTime};
use crate::{
endpoints::{events::NewEventTemplate, IdPath},
models::{Assignment, Event, Function, Location, Role, User},
@ -22,11 +30,11 @@ pub async fn get(
return Ok(HttpResponse::NotFound().finish());
};
let locations = if user.role == Role::Admin {
Location::read_all_including_area(pool.get_ref()).await?
} else {
Location::read_by_area(pool.get_ref(), user.area_id).await?
};
let locations = Location::read_by_area_including_area(
pool.get_ref(),
event.location.as_ref().unwrap().area_id,
)
.await?;
let assignments = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
@ -49,3 +57,62 @@ pub async fn get(
Ok(HttpResponse::Ok().body(template.render()?))
}
#[db_test]
async fn produces_template(context: &DbTestContext) {
//Location::create(&context.db_pool, &Industry().fake::<String>(), 1)
// .await
// .unwrap();
//
//let date: NaiveDate = Date().fake();
//let time: NaiveTime = Time().fake();
//let words: Vec<String> = Words(3..5).fake();
//Event::create(
// &context.db_pool,
// &date,
// &time,
// &time,
// &words.join(" "),
// 1,
// false,
// false,
// 2,
// &Word().fake(),
// None,
//)
//.await
//.unwrap();
Location::create(&context.db_pool, "Hauptbahnhof", 1)
.await
.unwrap();
Event::create(
&context.db_pool,
&NaiveDate::parse_from_str("2025-01-01", "%F").unwrap(),
&NaiveTime::parse_from_str("08:00", "%R").unwrap(),
&NaiveTime::parse_from_str("10:00", "%R").unwrap(),
&"Vorstellung".to_string(),
1,
false,
false,
2,
&"Tuchuniform".to_string(),
None,
)
.await
.unwrap();
let app = context.app().await;
let config = RequestConfig {
uri: "/events/1/edit".to_string(),
role: Role::Admin,
function: crate::models::Function::Posten,
user_area: 1,
};
let response = test_get(&context.db_pool, app, &config).await;
assert_eq!(StatusCode::OK, response.status());
let body = read_body(response).await;
assert_snapshot!(body);
}

View File

@ -1,3 +1,4 @@
use crate::filters;
use chrono::NaiveDate;
use rinja::Template;
@ -9,7 +10,8 @@ pub mod get_plan;
pub mod post_new;
#[derive(Template)]
#[template(path = "events/new_or_edit.html")]
#[cfg_attr(not(test), template(path = "events/new_or_edit.html"))]
#[cfg_attr(test, template(path = "events/new_or_edit.html", block = "content"))]
pub struct NewEventTemplate {
user: User,
date: NaiveDate,

View File

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

View File

@ -43,6 +43,27 @@ impl Location {
Ok(locations)
}
pub async fn read_by_area_including_area(pool: &PgPool, area_id: i32) -> Result<Vec<Location>> {
let records = query!("SELECT location.id AS locationId, location.name, location.areaId, area.id, area.name AS areaName FROM location JOIN area ON location.areaId = area.id WHERE areaId = $1;", area_id)
.fetch_all(pool)
.await?;
let locations = records
.iter()
.map(|lr| Location {
id: lr.id,
name: lr.name.to_string(),
area_id: lr.areaid,
area: Some(Area {
id: lr.id,
name: lr.areaname.to_string()
}),
})
.collect();
Ok(locations)
}
pub async fn read_all(pool: &PgPool) -> Result<Vec<Location>> {
let records = query!("SELECT * FROM location").fetch_all(pool).await?;

View File

@ -3,142 +3,181 @@
{% block content %}
<section class="section">
<div class="container">
<form method="post" action="/events/new">
<h1 class="title">Neues Event anlegen für den {{ date.format("%d.%m.%Y") }}</h1>
{% if let Some(event) = event %}
<form method="post" action="/events/{{ event.id }}/edit">
<h1 class="title">Event '{{ event.name }}' bearbeiten</h1>
{% else %}
<form method="post" action="/events/new">
<h1 class="title">Neues Event anlegen für den {{ date.format("%d.%m.%Y") }}</h1>
{% endif %}
<input type="hidden" name="date" value="{{ date }}">
<input type="hidden" name="date" value="{{ date }}">
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsname</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="name" placeholder="Wave Gotik Treffen" required />
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsname</label>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Startzeit - Endzeit</label>
</div>
<div class="field-body">
<div class="field">
<input class="input" type="time" id="from" name="from" value="00:00" required>
</div>
<div class="field">
<input class="input" type="time" id="till" name="till" value="23:59" required>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsort</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="location" required>
{% for location in locations %}
<option value="{{ location.id }}">{{ location.name }}{% if user.role == Role::Admin %} - ({{
location.area.as_ref().unwrap().name }}){% endif %}</option>
{% endfor %}
</select>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="name" placeholder="Wave Gotik Treffen" required {% if let Some(event)=event
%} value="{{ event.name }}" {% endif %} />
</div>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Wachhabender 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="voluntarywachhabender" value="true">
</label>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Startzeit - Endzeit</label>
</div>
<div class="field-body">
{% let f = "%H:%M" %}
<div class="field">
<input class="input" type="time" id="from" name="from" required value="{% if let Some(event)=event
%}{{ event.start_time.format(f) }}{% else %}0:00{% endif %}" />
</div>
<div class="field">
<input class="input" type="time" id="till" name="till" required value="{% if let Some(event)=event
%}{{ event.end_time.format(f) }}{% else %}23:59{% endif %}" />
</div>
</div>
</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 class="field is-horizontal">
<div class="field-label">
<label class="label">Veranstaltungsort</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="location" required>
{% for location in locations %}
<option value="{{ location.id }}">{{ location.name }}{% if user.role == Role::Admin %} - ({{
location.area.as_ref().unwrap().name }}){% endif %}
{% if let Some(event) = event %}{{ (event.location_id == location.id)|cond_show("selected") }}{%
endif %}
</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzahl der Posten</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" type="number" name="amount" min="1" max="100" required />
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Wachhabender durch FF gestellt?</label>
</div>
<div class="field-body">
<div class="field is-narrow">
{% let wh_disabled = event.is_some() && is_wachhabender_planned %}
<div class="control">
<label class="checkbox">
<input class="checkbox" type="checkbox" name="voluntarywachhabender" value="true" {{
wh_disabled|cond_show("disabled")}} {% if let Some(event)=event %} {{
event.voluntary_wachhabender|cond_show("checked") }} {% endif %}>
</label>
</div>
{% if wh_disabled %}
<p class="help">
Keine Änderung möglich, da ein Wachhabender bereits eingeplant ist. Diesen zuerst entplanen!
</p>
{% endif %}
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzugsordnung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="clothing" placeholder="Tuchuniform" required />
<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">
{% let fa_disabled = event.is_some() && is_fuehrungsassistent_planned %}
<div class="control">
<label class="checkbox">
<input class="checkbox" type="checkbox" name="voluntaryfuehrungsassistent" value="true" {{
fa_disabled|cond_show("disabled") }} {% if let Some(event)=event %} {{
event.voluntary_fuehrungsassistent|cond_show("checked") }} {% endif %}>
</label>
</div>
{% if fa_disabled %}
<p class="help">
Keine Änderung möglich, da ein Führungsassistent bereits eingeplant ist. Diesen zuerst entplanen!
</p>
{% endif %}
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anmerkung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="note" placeholder="" />
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzahl der Posten</label>
</div>
<div class="field-body">
<div class="field">
{% let posten_planned = event.is_some() && amount_of_planned_posten > 0 %}
<div class="control">
<input class="input" type="number" name="amount"
min="{% if posten_planned %}{{ amount_of_planned_posten }}{% else %}1{% endif %}" max="100" required
{% if let Some(event)=event %} value="{{ event.amount_of_posten }}" {% endif %} />
</div>
{% if posten_planned %}
<p class="help">
Mindestens {{ amount_of_planned_posten }} Posten, da bereits diese Anzahl eingeplant ist. Zum verringern
diese erst entplanen!
</p>
{% endif %}
</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" hx-boost="true" href="/?date={{ date }}">Zurück</a>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anzugsordnung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="clothing" placeholder="Tuchuniform" required {% if let Some(event)=event %}
value="{{ event.clothing }}" {% endif %} />
</div>
</div>
</div>
</div>
</div>
</form>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Anmerkung</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input class="input" name="note" {% if let Some(event)=event %} {{ event.note|insert_value }} {% endif
%} />
</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" hx-boost="true" href="/?date={{ date }}">Zurück</a>
</div>
</div>
</div>
</div>
</form>
</div>
</section>
{% endblock %}