// SPDX-FileCopyrightText: 2024 Simon Bruder // // SPDX-License-Identifier: AGPL-3.0-or-later use actix_web::{error, get, post, web, Responder}; use maud::html; use sqlx::PgPool; use uuid::Uuid; use super::templates::{self, forms, TemplateConfig}; use crate::manage; use crate::models::*; pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(show_item_class) .service(list_item_classes) .service(add_item_class) .service(add_item_class_post) .service(edit_item_class) .service(edit_item_class_post); } #[get("/item-class/{id}")] async fn show_item_class( pool: web::Data, path: web::Path, ) -> actix_web::Result { let id = path.into_inner(); let item_class = manage::item_class::get(&pool, id) .await .map_err(error::ErrorInternalServerError)?; // TODO: Once async closures are stable, use map_or on item_class.parent instead let parent = match item_class.parent { Some(id) => manage::item_class::get(&pool, id) .await .map(Some) .map_err(error::ErrorInternalServerError)?, None => None, }; let children = manage::item_class::children(&pool, id) .await .map_err(error::ErrorInternalServerError)?; let mut title = item_class.name.clone(); title.push_str(" – Item Details"); Ok(templates::base( TemplateConfig { path: &format!("/item-class/{}", item_class.id), title: Some(&title), page_title: Some(Box::new(item_class.name.clone())), page_actions: vec![ (templates::helpers::PageAction { href: format!("/item-class/{}/edit", item_class.id), name: "Edit".to_string(), }), ], ..Default::default() }, html! { table .table { tr { th { "UUID" } td { (item_class.id) } } tr { th { "Name" } td { (item_class.name) } } @if let Some(parent) = parent { tr { th { "Parent" } td { a href={ "/item-class/" (parent.id) } { (parent.name) } } } } } @if !children.is_empty() { h3 .mt-4 { "Children" } ul { @for child in children { li { a href={ "/item-class/" (child.id) } { (child.name) } } } } } }, )) } #[get("/item-classes")] async fn list_item_classes(pool: web::Data) -> actix_web::Result { let item_classes_ids = sqlx::query_scalar!("SELECT id FROM item_classes ORDER BY created_at") .fetch_all(pool.as_ref()) .await .map_err(error::ErrorInternalServerError)?; let item_classes = manage::item_class::get_all_as_map(&pool) .await .map_err(error::ErrorInternalServerError)?; Ok(templates::base( TemplateConfig { path: "/item-classes", title: Some("Item Class List"), page_title: Some(Box::new("Item Class List")), page_actions: vec![ (templates::helpers::PageAction { href: "/item-classes/add".to_string(), name: "Add".to_string(), }), ], ..Default::default() }, html! { table .table { thead { tr { th { "Name" } th { "Parents" } } } tbody { @for item_class in item_classes_ids { @let item_class = item_classes.get(&item_class).unwrap(); tr { td { a href={ "/item-class/" (item_class.id) } { (item_class.name) } } td { @if let Some(parent) = item_class.parent { @let parent = item_classes.get(&parent).unwrap(); a href={ "/item-class/" (parent.id) } { (parent.name) } } @else { "-" } } } } } } }, )) } #[get("/item-classes/add")] async fn add_item_class(form: web::Query) -> actix_web::Result { Ok(templates::base( TemplateConfig { path: "/items-classes/add", title: Some("Add Item Class"), page_title: Some(Box::new("Add Item Class")), ..Default::default() }, html! { form method="POST" { (forms::InputGroup { r#type: forms::InputType::Text, name: "name", title: "Name", required: true, value: form.name.clone(), ..Default::default() }) (forms::InputGroup { r#type: forms::InputType::Text, name: "parent", title: "Parent", disabled: form.parent.is_none(), value: form.parent.map(|id| id.to_string()), ..Default::default() }) button .btn.btn-primary type="submit" { "Add" } } }, )) } #[post("/item-classes/add")] async fn add_item_class_post( data: web::Form, pool: web::Data, ) -> actix_web::Result { let item = manage::item_class::add(&pool, data.into_inner()) .await .map_err(error::ErrorInternalServerError)?; Ok(web::Redirect::to("/item-class/".to_owned() + &item.id.to_string()).see_other()) } #[get("/item-class/{id}/edit")] async fn edit_item_class( pool: web::Data, path: web::Path, ) -> actix_web::Result { let id = path.into_inner(); let item_class = manage::item_class::get(&pool, id) .await .map_err(error::ErrorInternalServerError)?; let mut title = item_class.name.clone(); title.push_str(" – Item Details"); Ok(templates::base( TemplateConfig { path: &format!("/items-class/{}/add", id), title: Some(&title), page_title: Some(Box::new(item_class.name.clone())), ..Default::default() }, html! { form method="POST" { (forms::InputGroup { r#type: forms::InputType::Text, name: "id", title: "UUID", disabled: true, required: true, value: Some(item_class.id.to_string()), }) (forms::InputGroup { r#type: forms::InputType::Text, name: "name", title: "Name", required: true, value: Some(item_class.name), ..Default::default() }) (forms::InputGroup { r#type: forms::InputType::Text, name: "parent", title: "Parent", disabled: item_class.parent.is_none(), value: item_class.parent.map(|id| id.to_string()), ..Default::default() }) button .btn.btn-primary type="submit" { "Edit" } } }, )) } #[post("/item-class/{id}/edit")] async fn edit_item_class_post( pool: web::Data, path: web::Path, data: web::Form, ) -> actix_web::Result { let id = path.into_inner(); let item_class = manage::item_class::update(&pool, id, data.into_inner()) .await .map_err(error::ErrorInternalServerError)?; Ok(web::Redirect::to("/item-class/".to_owned() + &item_class.id.to_string()).see_other()) }