diff --git a/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json b/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json new file mode 100644 index 0000000..7a3a4db --- /dev/null +++ b/.sqlx/query-b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef.json @@ -0,0 +1,68 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO items (name, parent, class, original_packaging, description)\n SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[])\n RETURNING *", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "parent", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "class", + "type_info": "Uuid" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "short_id", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "original_packaging", + "type_info": "Uuid" + }, + { + "ordinal": 7, + "name": "description", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "VarcharArray", + "UuidArray", + "UuidArray", + "UuidArray", + "VarcharArray" + ] + }, + "nullable": [ + false, + true, + true, + false, + false, + false, + true, + false + ] + }, + "hash": "b54d323092a1dc2e34d3882a06b3cd119362d0baf5effbbd09f969ee31f385ef" +} diff --git a/src/frontend/item.rs b/src/frontend/item.rs index 613279f..f382f4a 100644 --- a/src/frontend/item.rs +++ b/src/frontend/item.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use actix_identity::Identity; -use actix_web::{error, get, post, web, Responder}; +use actix_web::{error, get, post, web, HttpRequest, Responder}; use maud::html; use serde::Deserialize; use sqlx::PgPool; @@ -249,8 +249,14 @@ async fn list(pool: web::Data, user: Identity) -> actix_web::Result usize { + 1 +} + #[derive(Debug, Deserialize)] pub struct NewItemForm { + #[serde(default = "default_quantity")] + pub quantity: usize, pub name: Option, pub parent: Option, pub class: Uuid, @@ -260,6 +266,7 @@ pub struct NewItemForm { #[derive(Debug, Deserialize)] pub struct NewItemFormPrefilled { + pub quantity: Option, pub name: Option, pub parent: Option, pub class: Option, @@ -336,7 +343,10 @@ async fn add_form( ..Default::default() }) - button .btn.btn-primary type="submit" { "Add" } + div .input-group { + button .btn.btn-primary type="submit" { "Add" } + input .form-control.flex-grow-0.w-auto #quantity name="quantity" type="number" value=(form.quantity.unwrap_or(default_quantity())) min="1"; + } } }, )) @@ -344,24 +354,59 @@ async fn add_form( #[post("/items/add")] async fn add_post( + req: HttpRequest, data: web::Form, pool: web::Data, - _user: Identity, + user: Identity, ) -> actix_web::Result { let data = data.into_inner(); - let item = manage::item::add( - &pool, - NewItem { - name: data.name, - class: data.class, - parent: data.parent, - original_packaging: data.original_packaging, - description: data.description, - }, - ) - .await - .map_err(error::ErrorInternalServerError)?; - Ok(web::Redirect::to("/item/".to_owned() + &item.id.to_string()).see_other()) + let new_item = NewItem { + name: data.name, + class: data.class, + parent: data.parent, + original_packaging: data.original_packaging, + description: data.description, + }; + if data.quantity == 1 { + let item = manage::item::add(&pool, new_item) + .await + .map_err(error::ErrorInternalServerError)?; + Ok( + web::Redirect::to("/item/".to_owned() + &item.id.to_string()) + .see_other() + .respond_to(&req) + .map_into_boxed_body(), + ) + } else { + let items = manage::item::add_multiple(&pool, new_item, data.quantity) + .await + .map_err(error::ErrorInternalServerError)?; + Ok(templates::base( + TemplateConfig { + path: "/items/add", + title: Some("Added Items"), + page_title: Some(Box::new("Added Items")), + page_actions: vec![PageActionGroup::generate_labels( + &items.iter().collect::>(), + )], + user: Some(user), + ..Default::default() + }, + html! { + ul { + @for item in items { + li { + a href={ "/item/" (item.id) } { (item.id) } + } + } + } + + a href="/items" { "Back to all items" } + }, + ) + .respond_to(&req) + .map_into_boxed_body()) + } } #[get("/item/{id}/edit")] diff --git a/src/manage/item.rs b/src/manage/item.rs index 4d8108a..74fe901 100644 --- a/src/manage/item.rs +++ b/src/manage/item.rs @@ -23,6 +23,26 @@ pub async fn add(pool: &PgPool, new_item: NewItem) -> Result .await } +pub async fn add_multiple( + pool: &PgPool, + new_item: NewItem, + quantity: usize, +) -> Result, sqlx::Error> { + query_as!( + Item, + r#"INSERT INTO items (name, parent, class, original_packaging, description) + SELECT * FROM UNNEST($1::VARCHAR[], $2::UUID[], $3::UUID[], $4::UUID[], $5::VARCHAR[]) + RETURNING *"#, + &vec![new_item.name; quantity] as &[Option], + &vec![new_item.parent; quantity] as &[Option], + &vec![new_item.class; quantity], + &vec![new_item.original_packaging; quantity] as &[Option], + &vec![new_item.description; quantity] + ) + .fetch_all(pool) + .await +} + pub async fn get(pool: &PgPool, id: Uuid) -> Result { query_as!(Item, "SELECT * FROM items WHERE id = $1", id) .fetch_one(pool)