Compare commits

..

No commits in common. "38a30e1898b38d04c4670040ad5d852d8cdbe555" and "b42540ac2f6f3aab0c175aeaed0f1b8adfdfb8aa" have entirely different histories.

17 changed files with 156 additions and 532 deletions

View File

@ -1,114 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n event.id AS eventId,\n event.startTimestamp,\n event.endTimestamp,\n event.name,\n event.locationId,\n event.voluntaryWachhabender,\n event.voluntaryFuehrungsassistent,\n event.amountOfPosten,\n event.clothing,\n event.canceled,\n event.note,\n location.id,\n location.name AS locationName,\n location.areaId AS locationAreaId,\n clothing.id AS clothingId,\n clothing.name AS clothingName\n FROM event\n JOIN location ON event.locationId = location.id\n JOIN clothing ON event.clothing = clothing.id\n WHERE starttimestamp::date >= $1\n AND starttimestamp::date <= $2\n AND location.areaId = $3;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "eventid",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "starttimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 2,
"name": "endtimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "name",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "locationid",
"type_info": "Int4"
},
{
"ordinal": 5,
"name": "voluntarywachhabender",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "voluntaryfuehrungsassistent",
"type_info": "Bool"
},
{
"ordinal": 7,
"name": "amountofposten",
"type_info": "Int2"
},
{
"ordinal": 8,
"name": "clothing",
"type_info": "Int4"
},
{
"ordinal": 9,
"name": "canceled",
"type_info": "Bool"
},
{
"ordinal": 10,
"name": "note",
"type_info": "Text"
},
{
"ordinal": 11,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 12,
"name": "locationname",
"type_info": "Text"
},
{
"ordinal": 13,
"name": "locationareaid",
"type_info": "Int4"
},
{
"ordinal": 14,
"name": "clothingid",
"type_info": "Int4"
},
{
"ordinal": 15,
"name": "clothingname",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Date",
"Date",
"Int4"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
false
]
},
"hash": "10b4b80f351b66ac5e778a3031288ac5dc66efd0a66b38b7e30f4c954df91bdf"
}

View File

@ -1,48 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\n FROM availability\n WHERE availability.userId = $1\n AND availability.starttimestamp::date >= $2\n AND availability.endtimestamp::date <= $3;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "userid",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "starttimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "endtimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "comment",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int4",
"Date",
"Date"
]
},
"nullable": [
false,
false,
false,
false,
true
]
},
"hash": "f60053118df6a791d31fa258ee3737881f8f97ca41cbebd92eb22c967292d2ee"
}

View File

@ -341,46 +341,6 @@ impl Availability {
Ok(availabilities) Ok(availabilities)
} }
pub async fn read_by_user_and_daterange(
pool: &PgPool,
user_id: i32,
date_range: (&NaiveDate, &NaiveDate),
) -> Result<Vec<Availability>> {
let records = query!(
r##"
SELECT
availability.id,
availability.userId,
availability.startTimestamp,
availability.endTimestamp,
availability.comment
FROM availability
WHERE availability.userId = $1
AND availability.starttimestamp::date >= $2
AND availability.endtimestamp::date <= $3;
"##,
user_id,
date_range.0,
date_range.1
)
.fetch_all(pool)
.await?;
let availabilities = records
.iter()
.map(|r| Availability {
id: r.id,
user_id: r.userid,
user: None,
start: r.starttimestamp.naive_utc(),
end: r.endtimestamp.naive_utc(),
comment: r.comment.clone(),
})
.collect();
Ok(availabilities)
}
pub async fn find_adjacent_by_time_for_user( pub async fn find_adjacent_by_time_for_user(
pool: &PgPool, pool: &PgPool,
start: &NaiveDateTime, start: &NaiveDateTime,

View File

@ -2,11 +2,9 @@ use chrono::{Days, NaiveDateTime};
use sqlx::PgPool; use sqlx::PgPool;
use super::Availability; use super::Availability;
use crate::{ use crate::{validation::{
END_OF_DAY, START_OF_DAY, start_date_time_lies_before_end_date_time, AsyncValidate, AsyncValidateError
models::Assignment, }, END_OF_DAY, START_OF_DAY};
validation::{AsyncValidate, AsyncValidateError, start_date_time_lies_before_end_date_time},
};
pub struct AvailabilityChangeset { pub struct AvailabilityChangeset {
pub time: (NaiveDateTime, NaiveDateTime), pub time: (NaiveDateTime, NaiveDateTime),
@ -16,7 +14,7 @@ pub struct AvailabilityChangeset {
pub struct AvailabilityContext<'a> { pub struct AvailabilityContext<'a> {
pub pool: &'a PgPool, pub pool: &'a PgPool,
pub user_id: i32, pub user_id: i32,
pub availability: Option<i32>, pub availability_to_get_edited: Option<i32>,
} }
impl<'a> AsyncValidate<'a> for AvailabilityChangeset { impl<'a> AsyncValidate<'a> for AvailabilityChangeset {
@ -30,69 +28,48 @@ impl<'a> AsyncValidate<'a> for AvailabilityChangeset {
Availability::read_by_user_and_date(context.pool, context.user_id, &self.time.0.date()) Availability::read_by_user_and_date(context.pool, context.user_id, &self.time.0.date())
.await?; .await?;
start_date_time_lies_before_end_date_time(&self.time.0, &self.time.1)?; if let Some(existing) = context.availability_to_get_edited {
if let Some(availability) = context.availability {
existing_availabilities = existing_availabilities existing_availabilities = existing_availabilities
.into_iter() .into_iter()
.filter(|a| a.id != availability) .filter(|a| a.id != existing)
.collect(); .collect();
time_is_not_already_assigned(&self.time, availability, context.pool).await?;
} }
if !existing_availabilities.is_empty() { time_is_not_already_made_available(&self.time, &existing_availabilities)?;
time_is_not_already_made_available(&self.time, &existing_availabilities)?; start_date_time_lies_before_end_date_time(&self.time.0, &self.time.1)?;
}
Ok(()) Ok(())
} }
} }
fn time_is_not_already_made_available( fn time_is_not_already_made_available(
(start, end): &(NaiveDateTime, NaiveDateTime), value: &(NaiveDateTime, NaiveDateTime),
existing_availabilities: &Vec<Availability>, existing_availabilities: &Vec<Availability>,
) -> Result<(), AsyncValidateError> { ) -> Result<(), AsyncValidateError> {
if existing_availabilities.is_empty() {
return Ok(());
}
let free_slots = find_free_date_time_slots(existing_availabilities); let free_slots = find_free_date_time_slots(existing_availabilities);
if free_slots.is_empty() { if free_slots.is_empty() {
return Err(AsyncValidateError::new( return Err(AsyncValidateError::new(
"Verfügbarkeit kann nicht erstellt werden, da bereits alle Zeiträume verfügbar gemacht wurden.", "cant create a availability as every time slot is already filled",
)); ));
} }
let free_block_found_for_start = free_slots.iter().any(|s| s.0 <= *start && s.1 >= *start); let free_block_found_for_start = free_slots.iter().any(|s| s.0 <= value.0 && s.1 >= value.0);
let free_block_found_for_end = free_slots.iter().any(|s| s.0 <= *end && s.1 >= *end); let free_block_found_for_end = free_slots.iter().any(|s| s.0 <= value.1 && s.1 >= value.1);
let is_already_present_as_is = existing_availabilities
.iter()
.any(|a| a.start == *start && a.end == a.end);
if !free_block_found_for_start || !free_block_found_for_end || is_already_present_as_is { if !free_block_found_for_start || !free_block_found_for_end {
return Err(AsyncValidateError::new( return Err(AsyncValidateError::new(
"Verfügbarkeit kann nicht erstellt werden, da eine vorhandene Verfügbarkeit überschnitten würde.", "cant create availability as there exists already a availability with the desired time",
)); ));
} }
Ok(()) Ok(())
} }
async fn time_is_not_already_assigned(
(start, end): &(NaiveDateTime, NaiveDateTime),
availability: i32,
pool: &PgPool,
) -> Result<(), AsyncValidateError> {
let existing_assignments = Assignment::read_all_by_availability(pool, availability).await?;
for a in existing_assignments {
if a.start < *start || a.end > *end {
return Err(AsyncValidateError::new(
"Verfügbarkeitszeit kann nicht verkleinert werden, da bereits eine Planung für diese Zeit existiert.",
));
}
}
Ok(())
}
pub fn find_free_date_time_slots( pub fn find_free_date_time_slots(
availabilities: &[Availability], availabilities: &[Availability],
) -> Vec<(NaiveDateTime, NaiveDateTime)> { ) -> Vec<(NaiveDateTime, NaiveDateTime)> {

View File

@ -95,73 +95,6 @@ impl Event {
Ok(events) Ok(events)
} }
pub async fn read_all_by_daterange_and_area_including_location(
pool: &PgPool,
date_range: (&NaiveDate, &NaiveDate),
area_id: i32,
) -> Result<Vec<Event>> {
let records = query!(
r#"
SELECT
event.id AS eventId,
event.startTimestamp,
event.endTimestamp,
event.name,
event.locationId,
event.voluntaryWachhabender,
event.voluntaryFuehrungsassistent,
event.amountOfPosten,
event.clothing,
event.canceled,
event.note,
location.id,
location.name AS locationName,
location.areaId AS locationAreaId,
clothing.id AS clothingId,
clothing.name AS clothingName
FROM event
JOIN location ON event.locationId = location.id
JOIN clothing ON event.clothing = clothing.id
WHERE starttimestamp::date >= $1
AND starttimestamp::date <= $2
AND location.areaId = $3;
"#,
date_range.0,
date_range.1,
area_id
)
.fetch_all(pool)
.await?;
let events = records
.into_iter()
.map(|record| Event {
id: record.eventid,
start: record.starttimestamp.naive_utc(),
end: record.endtimestamp.naive_utc(),
name: record.name.to_string(),
location_id: record.locationid,
location: Some(Location {
id: record.locationid,
name: record.locationname.to_string(),
area_id: record.locationareaid,
area: None,
}),
voluntary_wachhabender: record.voluntarywachhabender,
voluntary_fuehrungsassistent: record.voluntaryfuehrungsassistent,
amount_of_posten: record.amountofposten,
clothing: Clothing {
id: record.clothingid,
name: record.clothingname,
},
canceled: record.canceled,
note: record.note,
})
.collect();
Ok(events)
}
pub async fn read_by_id_including_location(pool: &PgPool, id: i32) -> Result<Option<Event>> { pub async fn read_by_id_including_location(pool: &PgPool, id: i32) -> Result<Option<Event>> {
let record = query!( let record = query!(
r#" r#"

View File

@ -5,8 +5,8 @@ mod validation_trait;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
pub use email::email_is_valid; pub use email::email_is_valid;
pub use error::AsyncValidateError; pub use error::AsyncValidateError;
use sqlx::PgPool;
pub use validation_trait::AsyncValidate; pub use validation_trait::AsyncValidate;
use sqlx::PgPool;
pub struct DbContext<'a> { pub struct DbContext<'a> {
pub pool: &'a PgPool, pub pool: &'a PgPool,
@ -24,7 +24,7 @@ pub fn start_date_time_lies_before_end_date_time(
) -> Result<(), AsyncValidateError> { ) -> Result<(), AsyncValidateError> {
if start >= end { if start >= end {
return Err(AsyncValidateError::new( return Err(AsyncValidateError::new(
"Ende kann nicht vor dem Beginn liegen.", "endtime can't lie before starttime",
)); ));
} }

View File

@ -1,158 +0,0 @@
use actix_web::{web, HttpResponse, Responder};
use askama::Template;
use chrono::{NaiveDate, Utc};
use serde::Deserialize;
use sqlx::PgPool;
use crate::{
filters,
utils::{
event_planning_template::generate_vehicles_assigned_and_available,
ApplicationError,
DateTimeFormat::{DayMonthYearHourMinute, HourMinute, WeekdayDayMonthYear},
TemplateResponse,
},
};
use brass_db::models::{
find_free_date_time_slots, Area, Assignment, Availability, Event, Function, Role, User, Vehicle,
};
#[derive(Deserialize)]
pub struct CalendarQuery {
date: Option<NaiveDate>,
area: Option<i32>,
}
#[derive(Template)]
#[template(path = "calendar.html")]
struct CalendarTemplate {
user: User,
user_can_create_availability: bool,
date: NaiveDate,
selected_area: Option<i32>,
areas: Vec<Area>,
events_and_assignments: Vec<(
Event,
Vec<String>,
Option<String>,
Option<String>,
Vec<Vehicle>,
)>,
availabilities: Vec<Availability>,
}
#[actix_web::get("/calendar")]
async fn get(
user: web::ReqData<User>,
pool: web::Data<PgPool>,
query: web::Query<CalendarQuery>,
) -> Result<impl Responder, ApplicationError> {
let date = match query.date {
Some(given_date) => given_date,
None => Utc::now().date_naive(),
};
let areas = Area::read_all(pool.get_ref()).await?;
let selected_area = match query.area {
Some(id) => {
if !areas.iter().any(|a| a.id == id) {
return Ok(HttpResponse::BadRequest().finish());
}
Some(id)
}
None => None,
};
let availabilities = Availability::read_by_date_and_area_including_user(
pool.get_ref(),
date,
query.area.unwrap_or(user.area_id),
)
.await?;
let availabilities_from_user =
Availability::read_by_user_and_date(pool.get_ref(), user.id, &date).await?;
//println!("{availabilities_from_user:#?}");
let user_can_create_availability = availabilities_from_user.is_empty()
|| !find_free_date_time_slots(&availabilities_from_user).is_empty();
//println!("{} || {} || {} = {user_can_create_availability}", availabilities_from_user.is_empty(),
// !only_one_availability_exists_and_is_whole_day(&availabilities_from_user),
// !find_free_time_slots(&availabilities_from_user).is_empty());
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);
let (assigned_vehicle, _) = generate_vehicles_assigned_and_available(&pool, &e).await?;
events_and_assignments.push((
e,
posten
.into_iter()
.map(|p| {
availabilities
.iter()
.find(|a| a.id == p.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
})
.collect(),
fuehrungsassistent.first().map(|fa| {
availabilities
.iter()
.find(|a| a.id == fa.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
}),
wachhabender.first().map(|wh| {
availabilities
.iter()
.find(|a| a.id == wh.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
}),
assigned_vehicle,
));
}
let template = CalendarTemplate {
user: user.into_inner(),
user_can_create_availability,
date,
selected_area,
areas,
events_and_assignments,
availabilities,
};
Ok(template.to_response()?)
}

View File

@ -1,23 +1,43 @@
use actix_web::{web, Responder}; use actix_web::{web, HttpResponse, Responder};
use askama::Template; use askama::Template;
use chrono::{Months, Utc}; use chrono::{NaiveDate, Utc};
use serde::Deserialize;
use sqlx::PgPool; use sqlx::PgPool;
use crate::{ use crate::{
filters, filters,
utils::{ utils::{
event_planning_template::generate_vehicles_assigned_and_available,
ApplicationError, ApplicationError,
DateTimeFormat::{DayMonthYearHourMinute, HourMinute, WeekdayDayMonthYear}, DateTimeFormat::{DayMonthYearHourMinute, HourMinute, WeekdayDayMonthYear},
TemplateResponse, TemplateResponse,
}, },
}; };
use brass_db::models::{Assignment, Availability, Event, Function, Role, User}; use brass_db::models::{
find_free_date_time_slots, Area, Assignment, Availability, Event, Function, Role, User, Vehicle,
};
#[derive(Deserialize)]
pub struct CalendarQuery {
date: Option<NaiveDate>,
area: Option<i32>,
}
#[derive(Template)] #[derive(Template)]
#[template(path = "overview.html")] #[template(path = "index.html")]
struct OverviewTemplate { struct CalendarTemplate {
user: User, user: User,
events: Vec<Event>, user_can_create_availability: bool,
date: NaiveDate,
selected_area: Option<i32>,
areas: Vec<Area>,
events_and_assignments: Vec<(
Event,
Vec<String>,
Option<String>,
Option<String>,
Vec<Vehicle>,
)>,
availabilities: Vec<Availability>, availabilities: Vec<Availability>,
} }
@ -25,24 +45,112 @@ struct OverviewTemplate {
async fn get( async fn get(
user: web::ReqData<User>, user: web::ReqData<User>,
pool: web::Data<PgPool>, pool: web::Data<PgPool>,
query: web::Query<CalendarQuery>,
) -> Result<impl Responder, ApplicationError> { ) -> Result<impl Responder, ApplicationError> {
let today = Utc::now().date_naive(); let date = match query.date {
let next_month = today.checked_add_months(Months::new(1)).unwrap(); Some(given_date) => given_date,
None => Utc::now().date_naive(),
};
let events = Event::read_all_by_daterange_and_area_including_location( let areas = Area::read_all(pool.get_ref()).await?;
let selected_area = match query.area {
Some(id) => {
if !areas.iter().any(|a| a.id == id) {
return Ok(HttpResponse::BadRequest().finish());
}
Some(id)
}
None => None,
};
let availabilities = Availability::read_by_date_and_area_including_user(
pool.get_ref(), pool.get_ref(),
(&today, &next_month), date,
user.area_id, query.area.unwrap_or(user.area_id),
) )
.await?; .await?;
let availabilities = let availabilities_from_user =
Availability::read_by_user_and_daterange(pool.get_ref(), user.id, (&today, &next_month)) Availability::read_by_user_and_date(pool.get_ref(), user.id, &date).await?;
.await?; //println!("{availabilities_from_user:#?}");
let template = OverviewTemplate { let user_can_create_availability = availabilities_from_user.is_empty()
|| !find_free_date_time_slots(&availabilities_from_user).is_empty();
//println!("{} || {} || {} = {user_can_create_availability}", availabilities_from_user.is_empty(),
// !only_one_availability_exists_and_is_whole_day(&availabilities_from_user),
// !find_free_time_slots(&availabilities_from_user).is_empty());
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);
let (assigned_vehicle, _) = generate_vehicles_assigned_and_available(&pool, &e).await?;
events_and_assignments.push((
e,
posten
.into_iter()
.map(|p| {
availabilities
.iter()
.find(|a| a.id == p.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
})
.collect(),
fuehrungsassistent.first().map(|fa| {
availabilities
.iter()
.find(|a| a.id == fa.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
}),
wachhabender.first().map(|wh| {
availabilities
.iter()
.find(|a| a.id == wh.availability_id)
.unwrap()
.user
.as_ref()
.unwrap()
.name
.clone()
}),
assigned_vehicle,
));
}
let template = CalendarTemplate {
user: user.into_inner(), user: user.into_inner(),
events, user_can_create_availability,
date,
selected_area,
areas,
events_and_assignments,
availabilities, availabilities,
}; };

View File

@ -9,7 +9,6 @@ use crate::{
use brass_db::models::{Role, User}; use brass_db::models::{Role, User};
pub mod delete; pub mod delete;
pub mod get_calendar;
pub mod get_new; pub mod get_new;
pub mod get_overview; pub mod get_overview;
pub mod get_update; pub mod get_update;

View File

@ -1,5 +1,4 @@
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
use maud::html;
use sqlx::PgPool; use sqlx::PgPool;
use crate::{ use crate::{
@ -23,7 +22,7 @@ pub async fn post(
let context = AvailabilityContext { let context = AvailabilityContext {
pool: pool.get_ref(), pool: pool.get_ref(),
user_id: user.id, user_id: user.id,
availability: None, availability_to_get_edited: None,
}; };
let mut changeset = AvailabilityChangeset { let mut changeset = AvailabilityChangeset {
@ -32,13 +31,7 @@ pub async fn post(
}; };
if let Err(e) = changeset.validate_with_context(&context).await { if let Err(e) = changeset.validate_with_context(&context).await {
let error_message = html! { return Ok(HttpResponse::BadRequest().body(e.to_string()));
svg class="icon is-small" {
use href="/static/feather-sprite.svg#alert-triangle" {}
}
" " (e)
};
return Ok(HttpResponse::UnprocessableEntity().body(error_message.into_string()));
}; };
if let Some(a) = if let Some(a) =

View File

@ -1,5 +1,4 @@
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
use maud::html;
use sqlx::PgPool; use sqlx::PgPool;
use crate::{ use crate::{
@ -32,7 +31,7 @@ pub async fn post(
let context = AvailabilityContext { let context = AvailabilityContext {
pool: pool.get_ref(), pool: pool.get_ref(),
user_id: user.id, user_id: user.id,
availability: Some(availability.id), availability_to_get_edited: Some(availability.id),
}; };
let mut changeset = AvailabilityChangeset { let mut changeset = AvailabilityChangeset {
@ -41,13 +40,7 @@ pub async fn post(
}; };
if let Err(e) = changeset.validate_with_context(&context).await { if let Err(e) = changeset.validate_with_context(&context).await {
let error_message = html! { return Ok(HttpResponse::BadRequest().body(e.to_string()));
svg class="icon is-small" {
use href="/static/feather-sprite.svg#alert-triangle" {}
}
" " (e)
};
return Ok(HttpResponse::UnprocessableEntity().body(error_message.into_string()));
}; };
if let Some(a) = Availability::find_adjacent_by_time_for_user( if let Some(a) = Availability::find_adjacent_by_time_for_user(

View File

@ -56,11 +56,10 @@ pub fn init(cfg: &mut ServiceConfig) {
cfg.service(availability::delete::delete); cfg.service(availability::delete::delete);
cfg.service(availability::get_new::get); cfg.service(availability::get_new::get);
cfg.service(availability::get_calendar::get); cfg.service(availability::get_overview::get);
cfg.service(availability::get_update::get); cfg.service(availability::get_update::get);
cfg.service(availability::post_new::post); cfg.service(availability::post_new::post);
cfg.service(availability::post_update::post); cfg.service(availability::post_update::post);
cfg.service(availability::get_overview::get);
cfg.service(events::put_cancelation::put_cancel); cfg.service(events::put_cancelation::put_cancel);
cfg.service(events::put_cancelation::put_uncancel); cfg.service(events::put_cancelation::put_uncancel);

View File

@ -4,8 +4,7 @@
<section class="section"> <section class="section">
<div class="container"> <div class="container">
{% let is_edit = id.is_some() %} {% let is_edit = id.is_some() %}
<form hx-post="/availability/{% if is_edit %}edit/{{ id.unwrap() }}{% else %}new{% endif %}" hx-target="body" <form method="post" action="/availability/{% if is_edit %}edit/{{ id.unwrap() }}{% else %}new{% endif %}">
hx-target-422="#error">
<h1 class="title">{% if is_edit %}Bearbeite{% else %}Neue{% endif %} Vefügbarkeit für {{ <h1 class="title">{% if is_edit %}Bearbeite{% else %}Neue{% endif %} Vefügbarkeit für {{
date|fmt_date(WeekdayDayMonthYear) }}</h1> date|fmt_date(WeekdayDayMonthYear) }}</h1>
@ -19,7 +18,7 @@
<div class="field-body"> <div class="field-body">
<div class="field"> <div class="field">
<input class="input" type="time" name="starttime" required {{ start|insert_time_value|safe }} <input class="input" type="time" name="starttime" required {{ start|insert_time_value|safe }}
_="on change put the value of me into #st then put '' into #error"> _="on change put the value of me into #st">
{% if slot_suggestions.len() > 0 %} {% if slot_suggestions.len() > 0 %}
<p class="help">noch mögliche Zeiträume:</p> <p class="help">noch mögliche Zeiträume:</p>
<div class="tags help"> <div class="tags help">
@ -33,7 +32,7 @@
</div> </div>
<div class="field"> <div class="field">
<input class="input" type="time" name="endtime" required {{ end|insert_time_value|safe }} <input class="input" type="time" name="endtime" required {{ end|insert_time_value|safe }}
_='on change put the value of me into #et then put "" into #error _='on change put the value of me into #et
then if (value of the previous <input/>) is greater than (value of me) then if (value of the previous <input/>) is greater than (value of me)
then set the value of #enddate to "{{ datetomorrow }}" then set the value of #enddate to "{{ datetomorrow }}"
then put "{{ datetomorrow|fmt_date(WeekdayDayMonth) }}" into #ed then put "{{ datetomorrow|fmt_date(WeekdayDayMonth) }}" into #ed
@ -54,15 +53,13 @@
<label class="radio"> <label class="radio">
<input type="radio" name="isovernight" {{ is_overnight|invert|ref|cond_show("checked")|safe }} <input type="radio" name="isovernight" {{ is_overnight|invert|ref|cond_show("checked")|safe }}
_='on click set the value of #enddate to "{{ date }}" _='on click set the value of #enddate to "{{ date }}"
then put "{{ date|fmt_date(WeekdayDayMonth) }}" into #ed then put "{{ date|fmt_date(WeekdayDayMonth) }}" into #ed'>
then put "" into #error'>
am selben Tag am selben Tag
</label> </label>
<label class="radio ml-3"> <label class="radio ml-3">
<input type="radio" id="radionextday" name="isovernight" {{ is_overnight|cond_show("checked")|safe }} <input type="radio" id="radionextday" name="isovernight" {{ is_overnight|cond_show("checked")|safe }}
_='on click set the value of #enddate to "{{ datetomorrow }}" _='on click set the value of #enddate to "{{ datetomorrow }}"
then put "{{ datetomorrow|fmt_date(WeekdayDayMonth) }}" into #ed then put "{{ datetomorrow|fmt_date(WeekdayDayMonth) }}" into #ed'>
then put "" into #error'>
am Tag darauf am Tag darauf
</label> </label>
</div> </div>
@ -93,7 +90,6 @@
<span id="et">{{ end_time|fmt_time(HourMinute) }}</span> <span id="et">{{ end_time|fmt_time(HourMinute) }}</span>
Uhr Uhr
</p> </p>
<p id="error" class="help is-danger"></p>
</div> </div>
</div> </div>
</div> </div>
@ -103,7 +99,7 @@
<div class="field-body"> <div class="field-body">
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-success" _="on click put '' into #error"> <button class="button is-success">
<svg class="icon"> <svg class="icon">
<use href="/static/feather-sprite.svg#check-circle" /> <use href="/static/feather-sprite.svg#check-circle" />
</svg> </svg>

View File

@ -18,10 +18,6 @@
<div class="navbar-menu" id="navMenu"> <div class="navbar-menu" id="navMenu">
<div hx-boost="true" class="navbar-start"> <div hx-boost="true" class="navbar-start">
<a href="/" class="navbar-item"> <a href="/" class="navbar-item">
Übersicht
</a>
<a href="/calendar" class="navbar-item">
Kalender Kalender
</a> </a>

View File

@ -1,9 +0,0 @@
{% extends "nav.html" %}
{% block content %}
<section id="progress" class="section">
<div class="container">
Übersicht
</div>
</section>
{% endblock %}

View File

@ -3,8 +3,7 @@
{% block content %} {% block content %}
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<form hx-post="/users/{% if let Some(id) = id %}edit/{{ id }}{% else %}new{% endif %}" hx-target="body" <form hx-post="/users/{% if let Some(id) = id %}edit/{{ id }}{% else %}new{% endif %}" hx-target-422="find p">
hx-target-422="find p">
<h1 class="title"> <h1 class="title">
{% if let Some(name) = name %}Nutzer '{{ name }}' bearbeiten{% else %}Neuen Nutzer anlegen{% endif %} {% if let Some(name) = name %}Nutzer '{{ name }}' bearbeiten{% else %}Neuen Nutzer anlegen{% endif %}
</h1> </h1>