li7y/src/frontend/item_class.rs

262 lines
8.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
//
// 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<PgPool>,
path: web::Path<Uuid>,
) -> actix_web::Result<impl Responder> {
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",
}),
],
..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<PgPool>) -> actix_web::Result<impl Responder> {
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",
name: "Add",
}),
],
..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<NewItemClassForm>) -> actix_web::Result<impl Responder> {
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<NewItemClass>,
pool: web::Data<PgPool>,
) -> actix_web::Result<impl Responder> {
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<PgPool>,
path: web::Path<Uuid>,
) -> actix_web::Result<impl Responder> {
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<PgPool>,
path: web::Path<Uuid>,
data: web::Form<NewItemClass>,
) -> actix_web::Result<impl Responder> {
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())
}