Funktionierende zu und absage.

This commit is contained in:
2023-02-02 12:56:42 +01:00
parent 87c0a2d541
commit 43d08a014b
9 changed files with 200 additions and 48 deletions
+2 -5
View File
@@ -8,14 +8,11 @@ use log::debug;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::Serialize;
use serde_json::json;
use terminwahl_typen::{
AppointmentSlot, IdType, Nutzer, PlannedAppointment, RequestState, Teacher,
};
use terminwahl_typen::{AppointmentSlot, Nutzer, PlannedAppointment, RequestState, Teacher};
use crate::db::{
self,
read::{get_slot_by_id, get_teacher_by_id},
write::confirm_appointments,
Pool,
};
@@ -26,7 +23,7 @@ pub struct FullAppointment {
}
impl FullAppointment {
async fn get(pool: &Pool, teacher_id: i64, slot_id: i64) -> Result<Self, Box<dyn Error>> {
pub async fn get(pool: &Pool, teacher_id: i64, slot_id: i64) -> Result<Self, Box<dyn Error>> {
Ok(Self {
teacher: get_teacher_by_id(pool, teacher_id)
.await
+1 -1
View File
@@ -76,7 +76,7 @@ pub async fn get_unavailable(db: &Pool) -> Result<HashSet<SlotId>, sqlx::Error>
SlotId,
r#"
SELECT teacher_id, slot_id
FROM `appointments` WHERE datetime(expires) > datetime('now');"#,
FROM `appointments` WHERE datetime(expires) > datetime('now') OR expires is NULL;"#,
)
.fetch_all(db)
.await
+63 -3
View File
@@ -1,7 +1,10 @@
use chrono::{Duration, Local};
use sqlx::query;
use futures::future;
use sqlx::{query, query_as};
use terminwahl_typen::{IdType, Nutzer, PlannedAppointment};
use crate::api::write::FullAppointment;
use super::Pool;
pub async fn save_appointments(
@@ -51,12 +54,69 @@ pub async fn save_nutzer(pool: &Pool, nutzer: &Nutzer) -> Result<IdType, sqlx::E
Ok(db_nutzer.id)
}
pub async fn confirm_appointments(pool: &Pool, validation_key: &str) -> Result<(), sqlx::Error> {
pub async fn confirm_appointments(
pool: &Pool,
validation_key: &str,
) -> Result<Vec<FullAppointment>, sqlx::Error> {
let _ = query!(
"UPDATE appointments SET expires = NULL WHERE validation_key = ?",
validation_key
)
.execute(pool)
.await?;
Ok(())
let appointments = query_as!(
PlannedAppointment,
"Select teacher_id, slot_id from appointments WHERE validation_key = ? and expires is NULL",
validation_key
)
.fetch_all(pool)
.await?;
let full_appointments = future::try_join_all(appointments.into_iter().map(
|PlannedAppointment {
teacher_id,
slot_id,
}| { FullAppointment::get(pool, teacher_id, slot_id) },
))
.await
.expect("Failed to get full list of appointments");
Ok(full_appointments)
}
pub async fn cancel_appointment(
pool: &Pool,
teacher_id: IdType,
slot_id: IdType,
validation_key: &str,
) -> Result<Vec<FullAppointment>, sqlx::Error> {
let _ = query!(
"DELETE FROM appointments WHERE teacher_id = ? AND slot_id = ? AND validation_key = ?",
teacher_id,
slot_id,
validation_key
)
.execute(pool)
.await?;
// Fetch the remaining appointments
let appointments = query_as!(
PlannedAppointment,
"Select teacher_id, slot_id from appointments WHERE validation_key = ? and expires is NULL",
validation_key
)
.fetch_all(pool)
.await?;
// Fetch the teacher names and times
let full_appointments = future::try_join_all(appointments.into_iter().map(
|PlannedAppointment {
teacher_id,
slot_id,
}| { FullAppointment::get(pool, teacher_id, slot_id) },
))
.await
.expect("Failed to get full list of appointments");
Ok(full_appointments)
}
+16
View File
@@ -2,3 +2,19 @@ pub mod api;
pub mod db;
pub mod handlebars_helper;
pub mod views;
pub struct CssPath {
pub path: String,
}
impl Default for CssPath {
fn default() -> Self {
let css_path = glob::glob("terminwahl_front/dist/my_bulma_colors*.css")
.expect("Failed to find css file")
.next()
.expect("Failed to find file")
.expect("Failed to find file");
let css_file = css_path.file_name().unwrap().to_str().unwrap().to_owned();
Self { path: css_file }
}
}
+6 -1
View File
@@ -11,7 +11,7 @@ use handlebars::Handlebars;
use lettre::{transport::smtp::authentication::Credentials, SmtpTransport};
use log::debug;
use std::env;
use terminwahl_back::{api, db, handlebars_helper::TimeOfDate, views};
use terminwahl_back::{api, db, handlebars_helper::TimeOfDate, views, CssPath};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
@@ -58,6 +58,7 @@ async fn main() -> std::io::Result<()> {
.app_data(web::Data::new(pool.clone()))
.app_data(web::Data::new(smtp_pool.clone()))
.app_data(web::Data::new(handlebars.clone()))
.app_data(web::Data::new(CssPath::default()))
.wrap(Logger::default())
.wrap(session_store)
.wrap(error_handlers)
@@ -80,6 +81,10 @@ async fn main() -> std::io::Result<()> {
web::resource("/confirm/{validation_key}")
.route(web::get().to(views::confirm_validation_key)),
)
.service(
web::resource("/cancel/{teacher_id}/{slot_id}/{validation_key}")
.route(web::get().to(views::delete_appointment)),
)
.service(Files::new("/", "./terminwahl_front/dist/").index_file("index.html"))
})
.bind(("127.0.0.1", 8080))?
+39 -14
View File
@@ -1,29 +1,54 @@
use actix_web::{error, web, HttpResponse};
use glob::glob;
use handlebars::Handlebars;
use lettre::SmtpTransport;
use serde_json::json;
use terminwahl_typen::RequestState;
use terminwahl_typen::IdType;
use crate::db::{write::confirm_appointments, Pool};
use crate::{
db::{
write::{cancel_appointment, confirm_appointments},
Pool,
},
CssPath,
};
pub async fn confirm_validation_key(
pool: web::Data<Pool>,
mailer: web::Data<SmtpTransport>,
_mailer: web::Data<SmtpTransport>,
css: web::Data<CssPath>,
handlebars: web::Data<Handlebars<'_>>,
validation_key: web::Path<String>,
) -> Result<HttpResponse, error::Error> {
let css_path = glob("terminwahl_front/dist/my_bulma_colors*.css")
.expect("Failed to find css file")
.next()
.expect("Failed to find file")
.expect("Failed to find file");
let css_file = css_path.file_name().unwrap().to_str();
let data = json!({
"css_file" : css_file,
});
match confirm_appointments(&pool, &validation_key).await {
Ok(_) => Ok(HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap())),
Ok(appointments) => Ok({
let data = json!({
"css_file" : css.path,
"appointments": appointments,
"validation_key": validation_key.into_inner(),
});
HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap())
}),
Err(e) => Err(error::ErrorBadRequest(e)),
}
}
pub async fn delete_appointment(
pool: web::Data<Pool>,
_mailer: web::Data<SmtpTransport>,
handlebars: web::Data<Handlebars<'_>>,
css: web::Data<CssPath>,
path: web::Path<(IdType, IdType, String)>,
) -> Result<HttpResponse, error::Error> {
let (teacher_id, slot_id, validation_key) = path.into_inner();
match cancel_appointment(&pool, teacher_id, slot_id, &validation_key).await {
Ok(appointments) => {
let data = json!({
"css_file" : css.path,
"appointments": appointments,
"validation_key": validation_key,
});
Ok(HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap()))
}
Err(e) => Err(error::ErrorBadRequest(e)),
}
}