use crate::error::AppError; use crate::session::SessionUser; use askama::Template; use askama_axum::{IntoResponse, Response}; use axum::extract::State; use axum::{async_trait, debug_handler, Form}; use serde::Deserialize; use crate::app::routes::AppState; use crate::db::display_unit::DbDisplayUnit; use crate::db; use crate::util::extract::form_helpers::form_checkbox_is_checked; use crate::util::extract::htmx_form_data::{HtmxFormData, HtmxFormDataError}; use crate::util::extract::validated_form::ValidatedForm; #[derive(Template, Debug)] #[template(path = "item/item-create-form.html")] pub struct CreateItemFormTemplate { pub display_units: Vec, pub name_value: String, pub name_error: &'static str, pub display_unit_value: String, pub display_unit_error: &'static str, pub reorder_point_value: String, pub reorder_point_error: &'static str, pub pims_id_value: String, pub pims_id_error: &'static str, pub vetcove_id_value: String, pub vetcove_id_error: &'static str, pub allow_fractional_units_value: bool, } impl CreateItemFormTemplate { pub fn clear_inputs(&mut self) { self.name_value.clear(); self.display_unit_value.clear(); self.reorder_point_value.clear(); self.pims_id_value.clear(); self.vetcove_id_value.clear(); self.allow_fractional_units_value = false; } } #[derive(Deserialize, Debug)] pub struct CreateItemFormData { name: String, display_unit: String, reorder_point: f64, allow_fractional_units: Option, pims_id: Option, vetcove_id: Option, } #[async_trait] impl HtmxFormData for CreateItemFormData { type FormTemplate = CreateItemFormTemplate; async fn validate(self, state: &AppState) -> Result> { let mut base = Self::base_template(&state).await?; let display_units = &base.display_units; let allow_fractional_units = form_checkbox_is_checked(&self.allow_fractional_units); let name_error = if self.name.is_empty() { "Please provide a name" } else { "" }; let display_unit_error = if self.display_unit.is_empty() { "Please provide a display unit" } else if !display_units.iter().any(|x| x.abbreviation.eq(&self.display_unit)){ "Invalid display unit" } else { "" }; let reorder_point_error = if self.reorder_point.is_nan() || self.reorder_point.is_infinite() || self.reorder_point.is_sign_negative() { "Provide a positive number" } else if !(allow_fractional_units || self.reorder_point.fract() == 0.0) { "Fractional units not allowed" } else { "" }; let pims_id_error = if let Some(pims_id) = &self.pims_id { if pims_id.chars().any(char::is_whitespace) { "Invalid PIMS id" } else { "" } } else { "" }; let vetcove_id_error = if let Some(vetcove_id) = &self.vetcove_id { if !vetcove_id.chars().all(|c| c.is_ascii_digit()) { "Invalid Vectcove id" } else { "" } } else { "" }; if !(name_error.is_empty() && display_unit_error.is_empty() && reorder_point_error.is_empty() && pims_id_error.is_empty() && vetcove_id_error.is_empty()) { base.name_value = self.name; base.name_error = name_error; base.display_unit_value = self.display_unit; base.display_unit_error = display_unit_error; base.reorder_point_value = format!("{:.2}", self.reorder_point); base.reorder_point_error = reorder_point_error; base.pims_id_value = self.pims_id.as_deref().unwrap_or_default().to_string(); base.pims_id_error = pims_id_error; base.vetcove_id_value = self.vetcove_id.as_deref().unwrap_or_default().to_string(); base.vetcove_id_error = vetcove_id_error; base.allow_fractional_units_value = allow_fractional_units; return Err(HtmxFormDataError::ValidationError(base)); } Ok(self) } async fn base_template(state: &AppState) -> anyhow::Result { let db = &state.db; let display_units = db::display_unit::get_display_units(&db).await?; Ok(Self::FormTemplate { display_units, name_value: "".to_owned(), name_error: "", display_unit_value: "".to_owned(), display_unit_error: "", reorder_point_value: "".to_owned(), reorder_point_error: "", pims_id_value: "".to_owned(), pims_id_error: "", vetcove_id_value: "".to_owned(), vetcove_id_error: "", allow_fractional_units_value: false, }) } } #[debug_handler] pub async fn create_item_form_post( State(state): State, user: SessionUser, form_data: ValidatedForm, ) -> Result { let allow_fractional_units = form_checkbox_is_checked(&form_data.allow_fractional_units); let _new_id = db::inventory_item::add_inventory_item(&state.db, &form_data.name, form_data.reorder_point, allow_fractional_units, &form_data.display_unit, &form_data.pims_id, &form_data.vetcove_id, ).await?; Ok(CreateItemFormData::base_template(&state).await?.into_response()) } #[debug_handler] pub async fn create_item_form_get( State(state): State, ) -> Result { Ok(CreateItemFormData::base_template(&state).await?.into_response()) }