You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
inventory-app/src/app/item/create.rs

143 lines
5.0 KiB

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};
use axum_htmx::{HxEvent, HxResponseTrigger};
use serde::Deserialize;
use crate::app::routes::AppState;
use crate::db::display_unit::DbDisplayUnit;
use crate::db;
use crate::util::form::field_error::FieldError;
use crate::util::form::field_value::{BoolField, FloatField, StringField};
use crate::util::form::validate_form::{ValidateForm, ValidateFormError};
use crate::util::form::helpers::deserialize_form_checkbox;
use crate::util::form::base_response::BaseResponse;
use crate::util::form::extractors::{FormBase, ValidForm};
#[derive(Template, Debug, Default)]
#[template(path = "item/item-create-form.html")]
pub struct CreateItemFormTemplate {
pub display_units: Vec<DbDisplayUnit>,
pub name: StringField,
pub display_unit_value: StringField,
pub reorder_point: StringField,
pub pims_id: StringField,
pub vetcove_id: StringField,
pub allow_fractional_units: BoolField,
}
#[async_trait]
impl BaseResponse for CreateItemFormTemplate {
type ResponseType = Self;
async fn base_response(state: &AppState) -> Result<Self::ResponseType, AppError> {
let db = &state.db;
let display_units = db::display_unit::get_display_units(&db).await?;
Ok(Self {
display_units,
..Default::default()
})
}
}
#[derive(Deserialize, Debug)]
pub struct CreateItemFormData {
name: String,
display_unit: String,
reorder_point: f64,
pims_id: Option<String>,
vetcove_id: Option<String>,
#[serde(default, deserialize_with = "deserialize_form_checkbox")]
allow_fractional_units: bool,
}
#[async_trait]
impl ValidateForm for FormBase<CreateItemFormData> {
type ValidationErrorResponse = CreateItemFormTemplate;
async fn validate(self, state: &AppState) -> Result<Self, ValidateFormError<Self::ValidationErrorResponse>> {
let display_units = db::display_unit::get_display_units(&state.db).await?;
let name = StringField::new(self.form_data.name.clone())
.invalid_if(|v| v.is_empty(), FieldError::Required);
let display_unit_value = StringField::new(self.form_data.display_unit.clone())
.invalid_if(|v| v.is_empty(), FieldError::Required)
.invalid_if(|v| !display_units.iter().any(
|x| x.abbreviation.eq(&self.form_data.display_unit)),
FieldError::SelectOption)
.clear_value_if_error();
let reorder_point = FloatField::new(self.form_data.reorder_point.clone())
.invalid_if(|v| v.is_nan() || v.is_infinite() || v.is_sign_negative(), FieldError::PositiveNumber)
.invalid_if(|v| !(self.form_data.allow_fractional_units || v.fract() == 0.0), FieldError::WholeNumber)
.map(|v| format!("{:.2}", v));
let pims_id = StringField::new(self.form_data.pims_id.clone().unwrap_or_default())
.invalid_if(|v| v.chars().any(char::is_whitespace), FieldError::ValidIdentifier);
let vetcove_id = StringField::new(self.form_data.vetcove_id.clone().unwrap_or_default())
.invalid_if(|v| !v.chars().all(|c| c.is_ascii_digit()), FieldError::ValidIdentifier);
let allow_fractional_units = BoolField::new(self.form_data.allow_fractional_units);
if name.is_error() ||
display_unit_value.is_error() ||
reorder_point.is_error() ||
pims_id.is_error() ||
vetcove_id.is_error() {
return Err(ValidateFormError::ValidationError(
Self::ValidationErrorResponse {
display_units,
name,
display_unit_value,
reorder_point,
pims_id,
vetcove_id,
allow_fractional_units,
}));
}
Ok(self)
}
}
#[debug_handler]
pub async fn create_item_form_post(
State(state): State<AppState>,
user: SessionUser,
form: ValidForm<FormBase<CreateItemFormData>>,
) -> Result<Response, AppError> {
let form_data = &form.form_data;
let _new_id = db::inventory_item::add_inventory_item(&state.db, &form_data.name, form_data.reorder_point,
form_data.allow_fractional_units, &form_data.display_unit,
&form_data.pims_id, &form_data.vetcove_id,
).await?;
let fresh_form = CreateItemFormTemplate::base_response(&state).await?;
let events = vec![
HxEvent::from("form-submit-success"),
HxEvent::from("new-item"),
];
Ok( (
HxResponseTrigger::normal(events),
fresh_form.into_response()
).into_response() )
}
#[debug_handler]
pub async fn create_item_form_get(
State(state): State<AppState>,
) -> Result<Response, AppError> {
Ok(CreateItemFormTemplate::base_response(&state).await?.into_response())
}

Powered by TurnKey Linux.