use std::collections::HashMap; use cfg_if::cfg_if; use chrono::Weekday; use leptos::*; use validator::Validate; use crate::backend::data::{ApiResponse, ClosingTime, DayHour, WeekHours}; use crate::components::data_form::ForValidation; cfg_if! { if #[cfg(feature = "ssr")] { use crate::error::AppError; use chrono::Local; pub async fn hours_for_day(day: Weekday) -> Result, AppError> { use crate::backend::get_pool; use crate::backend::data::OpeningHour; let pool = get_pool().await?; let hours = sqlx::query_as::<_, OpeningHour>("SELECT * FROM opening_hour WHERE day = $1") .bind(day.num_days_from_monday() as i32) .fetch_all(&pool) .await?; Ok(hours.into_iter().map(|h| { DayHour::new(h.from, h.to, h.discount)}).collect()) } async fn purge_closing_days() -> Result<(), AppError> { use crate::backend::get_pool; let pool = get_pool().await?; sqlx::query("DELETE FROM closing_time WHERE to_date < $1") .bind(Local::now()) .execute(&pool) .await?; Ok(()) } }} #[server] pub async fn get_hours() -> Result>, ServerFnError> { use crate::backend::get_pool; use crate::backend::data::OpeningHour; let pool = get_pool().await?; let hours = sqlx::query_as::<_, OpeningHour>("SELECT * FROM opening_hour").fetch_all(&pool).await?; let mut ret: HashMap> = hours.into_iter().fold(HashMap::new(), |mut map, v| { map.entry(Weekday::try_from(v.day as u8).unwrap_or(Weekday::Mon)) .and_modify(|h| h.push(DayHour::new(v.from, v.to, v.discount))) .or_insert(vec![DayHour::new(v.from, v.to, v.discount)]); map }); for d in 0..6 { if let None = ret.get(&Weekday::try_from(d as u8).unwrap()) { ret.insert(Weekday::try_from(d as u8).unwrap(), Vec::new()); } } Ok(ret) } #[server] pub async fn get_hours_for_day(day: Weekday) -> Result, ServerFnError> { Ok(hours_for_day(day).await?) } #[server] pub async fn update_hours(hours: WeekHours) -> Result, ServerFnError> { use crate::perm_check; use crate::backend::get_pool; use crate::backend::data::DayHours; perm_check!(is_admin); let hr = DayHours::try_new(hours.hours())?; let pool = get_pool().await?; let day = hours.day(); let mut tx = pool.begin().await?; sqlx::query("DELETE FROM opening_hour WHERE day = $1") .bind(day.num_days_from_monday() as i32) .execute(&mut *tx) .await?; for h in hr.hours() { sqlx::query(r#"INSERT INTO opening_hour(day, "from", "to", discount) VALUES($1, $2, $3, $4)"#) .bind(day.num_days_from_monday() as i32) .bind(h.from()) .bind(h.to()) .bind(h.discount()) .execute(&mut *tx) .await?; } tx.commit().await?; Ok(ApiResponse::Data(())) } impl ForValidation for UpdateHours { fn entity(&self) -> &dyn Validate { &self.hours } } #[server] pub async fn get_closing_time() -> Result, ServerFnError> { use crate::backend::get_pool; use sqlx::{Error, query_as}; let pool = get_pool().await?; let ct = query_as::<_, ClosingTime>("SELECT * FROM closing_time ORDER BY from_date") .fetch_one(&pool) .await; Ok(if let Err(ref e) = ct { if matches!(e, Error::RowNotFound) { None } else { Some(ct?) } } else { Some(ct?) }) } #[server] pub async fn get_closing_times() -> Result, ServerFnError> { use crate::backend::get_pool; use sqlx::query_as; let pool = get_pool().await?; purge_closing_days().await?; Ok(query_as::<_, ClosingTime>("SELECT * FROM closing_time ORDER BY from_date").fetch_all(&pool).await?) } #[server] pub async fn insert_closing_time(time: ClosingTime) -> Result, ServerFnError> { use crate::perm_check; use crate::backend::get_pool; perm_check!(is_admin); let pool = get_pool().await?; sqlx::query("INSERT INTO closing_time(from_date, to_date) VALUES($1, $2)") .bind(time.from_date) .bind(time.to_date) .execute(&pool) .await?; Ok(ApiResponse::Data(())) } impl ForValidation for InsertClosingTime { fn entity(&self) -> &dyn Validate { &self.time } } #[server] pub async fn delete_closing_time(id: i32) -> Result, ServerFnError> { use crate::perm_check; use crate::backend::get_pool; perm_check!(is_admin); let pool = get_pool().await?; sqlx::query("DELETE FROM closing_time WHERE id = $1") .bind(id) .execute(&pool) .await?; Ok(ApiResponse::Data(())) }