diff --git a/Cargo.lock b/Cargo.lock index 042c8e45..36536747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,6 +372,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "askama_actix" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b0dd17cfe203b00ba3853a89fba459ecf24c759b738b244133330607c78e55" +dependencies = [ + "actix-web", + "askama", +] + [[package]] name = "askama_derive" version = "0.12.1" @@ -635,6 +645,7 @@ dependencies = [ "anyhow", "argon2", "askama", + "askama_actix", "chrono", "dotenv", "serde", @@ -714,6 +725,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.0", ] diff --git a/Cargo.toml b/Cargo.toml index 933aba04..6ae46af8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,13 @@ edition = "2021" [dependencies] sqlx = { version = "0.6", features = ["runtime-async-std-rustls", "postgres", "chrono"] } actix-web = { version = "4" } -askama = "0.12.0" -serde = { version = "1.0.164", features = [ "derive"]} +askama = { version = "0.12.0", features = ["with-actix-web"] } +serde = { version = "1.0.164", features = ["derive"] } argon2 = { version = "0.5.0", features = [ "std"]} anyhow = "1.0.71" dotenv = "0.15.0" actix-session = { version = "0.7.2", features = ["cookie-session"] } actix-identity = "0.5.2" -chrono = "0.4.33" +chrono = { version = "0.4.33", features = ["serde"] } actix-files = "0.6.5" +askama_actix = "0.14.0" diff --git a/src/calendar/get_availabillity_new.rs b/src/calendar/get_availabillity_new.rs new file mode 100644 index 00000000..1814a735 --- /dev/null +++ b/src/calendar/get_availabillity_new.rs @@ -0,0 +1,41 @@ +use actix_identity::Identity; +use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; +use askama::Template; +use askama_actix::TemplateToResponse; +use chrono::NaiveDate; +use serde::Deserialize; +use sqlx::PgPool; + +use crate::models::{role::Role, user::User}; + +#[derive(Template)] +#[template(path = "availabillity_new.html")] +struct AvailabillityNewTemplate { + user_role: Role, + date: NaiveDate +} + +#[derive(Deserialize)] +pub struct AvailabillityNewQuery { + date: NaiveDate, +} + +#[actix_web::get("/availabillity/new")] +pub async fn get_availabillity_new( + user: Option, + pool: web::Data, + query: web::Query, + ) -> impl Responder { + + if let Some(user) = user { + let current_user = User::read_by_id(pool.as_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); + + let template = AvailabillityNewTemplate { user_role: current_user.role, date: query.date }; + + template.to_response() + } else { + HttpResponse::PermanentRedirect() + .insert_header((LOCATION, "/login")) + .finish() + } +} diff --git a/src/calendar/mod.rs b/src/calendar/mod.rs index dee9bfb0..77f537af 100644 --- a/src/calendar/mod.rs +++ b/src/calendar/mod.rs @@ -1,3 +1,4 @@ pub mod routes; +mod get_availabillity_new; pub use routes::init; diff --git a/src/calendar/routes.rs b/src/calendar/routes.rs index 82f0def2..24f418b5 100644 --- a/src/calendar/routes.rs +++ b/src/calendar/routes.rs @@ -1,26 +1,66 @@ use actix_identity::Identity; use actix_web::{http::header::LOCATION, web, HttpResponse, Responder}; use askama::Template; +use chrono::{NaiveDate, Utc}; +use serde::Deserialize; use sqlx::PgPool; -use crate::models::{role::Role, user::User}; +use crate::models::{ + area::Area, availabillity::Availabillity, event::Event, role::Role, user::User, +}; + +use super::get_availabillity_new::get_availabillity_new; + pub fn init(cfg: &mut web::ServiceConfig) { cfg.service(get_index); + cfg.service(get_availabillity_new); +} + +#[derive(Deserialize)] +pub struct CalendarQuery { + date: Option, } #[derive(Template)] #[template(path = "index.html")] struct CalendarTemplate { user_role: Role, + date: NaiveDate, + area: Area, + events: Vec, + availabillities: Vec, } #[actix_web::get("/")] -async fn get_index(user: Option, pool: web::Data) -> impl Responder { +async fn get_index( + user: Option, + pool: web::Data, + query: web::Query, +) -> impl Responder { if let Some(user) = user { - let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()).await.unwrap(); + let current_user = User::read_by_id(pool.get_ref(), user.id().unwrap().parse().unwrap()) + .await + .unwrap(); + let date = match query.date { + Some(given_date) => given_date, + None => Utc::now().date_naive(), + }; + let area = Area::read_by_id(pool.get_ref(), current_user.area_id) + .await + .unwrap(); + let events = Event::read_by_date(pool.get_ref(), date).await.unwrap(); + let availabillities = Availabillity::read_by_date(pool.get_ref(), date) + .await + .unwrap(); - let template = CalendarTemplate{ user_role: current_user.role }; + let template = CalendarTemplate { + user_role: current_user.role, + date, + area, + events, + availabillities, + }; HttpResponse::Ok().body(template.render().unwrap()) } else { diff --git a/src/models/area.rs b/src/models/area.rs index c3ab2130..928f1eb1 100644 --- a/src/models/area.rs +++ b/src/models/area.rs @@ -12,8 +12,8 @@ impl Area { Ok(result.id) } - pub async fn read_by_id(pool: &PgPool, id: i32) -> anyhow::Result> { - let record = query_as!(Area, "SELECT * FROM area WHERE id = $1", id).fetch_optional(pool).await?; + pub async fn read_by_id(pool: &PgPool, id: i32) -> anyhow::Result { + let record = query_as!(Area, "SELECT * FROM area WHERE id = $1", id).fetch_one(pool).await?; Ok(record) } diff --git a/src/models/availabillity.rs b/src/models/availabillity.rs index e69de29b..c40a21ce 100644 --- a/src/models/availabillity.rs +++ b/src/models/availabillity.rs @@ -0,0 +1,40 @@ +use chrono::{NaiveDate, NaiveTime}; +use sqlx::{query, PgPool}; + +pub struct Availabillity { + pub id: i32, + pub user_id: i32, + pub date: NaiveDate, + pub start_time: Option, + pub end_time: Option +} + +impl Availabillity { + pub async fn create(pool: &PgPool, user_id: i32, date: NaiveDate, start_time: Option, end_time: Option) -> anyhow::Result { + let mut result = match (start_time, end_time) { + (Some(start_time), Some(end_time)) => query!("INSERT INTO availabillity (userId, date, startTime, endTime) VALUES ($1, $2, $3, $4) RETURNING id;", user_id, date, start_time, end_time).fetch_one(pool).await?.id, + (_, _) => query!("INSERT INTO availabillity (userId, date) VALUES ($1, $2) RETURNING id;", user_id, date).fetch_one(pool).await?.id + }; + + Ok(result) + } + + pub async fn read_by_date(pool: &PgPool, date: NaiveDate) -> anyhow::Result> { + let records = query!("SELECT * FROM availabillity WHERE date = $1", date) + .fetch_all(pool) + .await?; + + let availabillities = records + .iter() + .map(|a| Availabillity { + id: a.id, + user_id: a.userid, + date: a.date, + start_time: a.starttime, + end_time: a.endtime + }) + .collect(); + + Ok(availabillities) + } +} diff --git a/src/models/location.rs b/src/models/location.rs index 96966aa9..20092119 100644 --- a/src/models/location.rs +++ b/src/models/location.rs @@ -1,9 +1,38 @@ +use sqlx::{query, PgPool}; + pub struct Location { pub id: i32, pub name: String, - pub areaId: i32 + pub area_id: i32, } impl Location { + pub async fn create(pool: &PgPool, name: &str, area_id: i32) -> anyhow::Result { + let result = query!( + "INSERT INTO location (name, areaId) VALUES ($1, $2) RETURNING id;", + name, + area_id + ) + .fetch_one(pool) + .await?; + Ok(result.id) + } + + pub async fn read_by_area(pool: &PgPool, area_id: i32) -> anyhow::Result> { + let records = query!("SELECT * FROM location 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, + }) + .collect(); + + Ok(locations) + } } diff --git a/templates/availabillity_new.html b/templates/availabillity_new.html new file mode 100644 index 00000000..c6bca6a2 --- /dev/null +++ b/templates/availabillity_new.html @@ -0,0 +1,110 @@ +{% extends "nav.html" %} + +{% block content %} +
+
+
+

Neue Vefügbarkeit für den {{ date.format("%d.%m.%Y") }}

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+ Zurück +
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/templates/base.html b/templates/base.html index ae95a0ca..dc084dc6 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,7 +5,7 @@ Brass - Brasiwa Leipzig - + diff --git a/templates/index.html b/templates/index.html index 6b4aced0..275d0bef 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,12 +1,74 @@ {% extends "nav.html" %} - + {% block content %}
-
-

- Vefügbarkeit für |datum| -

- +
+
+
+ +
+
+ +
+
+ +
-
+ + + +
+
+
+
+

+ Events am {{ date.format("%d.%m.%Y") }} +

+
+ +
+ + {% if events.len() == 0 %} +
+
keine Events geplant
+
+ {% else %} + {% for event in events %} +
+
{{ event.name }}
+
+ {% endfor %} + {% endif %} +
+
+ +
+
+
+
+

+ Verfügbarkeiten am {{ date.format("%d.%m.%Y") }} +

+
+ +
+ + {% if events.len() == 0 %} +
+
keine Verfügbarkeiten eingetragen
+
+ {% else %} + {% for availabillity in availabillities %} +
+
{{ availabillity.user_id }}
+
+ {% endfor %} + {% endif %} + +
+
{% endblock %}