Allow batch adding items

This commit is contained in:
Simon Bruder 2024-07-16 12:48:19 +02:00
parent 693fe68797
commit 125627d7d2
Signed by: simon
GPG key ID: 347FF8699CDA0776
3 changed files with 149 additions and 16 deletions

View file

@ -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"
}

View file

@ -5,7 +5,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use actix_identity::Identity; 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 maud::html;
use serde::Deserialize; use serde::Deserialize;
use sqlx::PgPool; use sqlx::PgPool;
@ -249,8 +249,14 @@ async fn list(pool: web::Data<PgPool>, user: Identity) -> actix_web::Result<impl
)) ))
} }
fn default_quantity() -> usize {
1
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct NewItemForm { pub struct NewItemForm {
#[serde(default = "default_quantity")]
pub quantity: usize,
pub name: Option<String>, pub name: Option<String>,
pub parent: Option<Uuid>, pub parent: Option<Uuid>,
pub class: Uuid, pub class: Uuid,
@ -260,6 +266,7 @@ pub struct NewItemForm {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct NewItemFormPrefilled { pub struct NewItemFormPrefilled {
pub quantity: Option<usize>,
pub name: Option<String>, pub name: Option<String>,
pub parent: Option<Uuid>, pub parent: Option<Uuid>,
pub class: Option<Uuid>, pub class: Option<Uuid>,
@ -336,7 +343,10 @@ async fn add_form(
..Default::default() ..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")] #[post("/items/add")]
async fn add_post( async fn add_post(
req: HttpRequest,
data: web::Form<NewItemForm>, data: web::Form<NewItemForm>,
pool: web::Data<PgPool>, pool: web::Data<PgPool>,
_user: Identity, user: Identity,
) -> actix_web::Result<impl Responder> { ) -> actix_web::Result<impl Responder> {
let data = data.into_inner(); let data = data.into_inner();
let item = manage::item::add( let new_item = NewItem {
&pool, name: data.name,
NewItem { class: data.class,
name: data.name, parent: data.parent,
class: data.class, original_packaging: data.original_packaging,
parent: data.parent, description: data.description,
original_packaging: data.original_packaging, };
description: data.description, if data.quantity == 1 {
}, let item = manage::item::add(&pool, new_item)
) .await
.await .map_err(error::ErrorInternalServerError)?;
.map_err(error::ErrorInternalServerError)?; Ok(
Ok(web::Redirect::to("/item/".to_owned() + &item.id.to_string()).see_other()) 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::<Vec<&Item>>(),
)],
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")] #[get("/item/{id}/edit")]

View file

@ -23,6 +23,26 @@ pub async fn add(pool: &PgPool, new_item: NewItem) -> Result<Item, sqlx::Error>
.await .await
} }
pub async fn add_multiple(
pool: &PgPool,
new_item: NewItem,
quantity: usize,
) -> Result<Vec<Item>, 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<String>],
&vec![new_item.parent; quantity] as &[Option<Uuid>],
&vec![new_item.class; quantity],
&vec![new_item.original_packaging; quantity] as &[Option<Uuid>],
&vec![new_item.description; quantity]
)
.fetch_all(pool)
.await
}
pub async fn get(pool: &PgPool, id: Uuid) -> Result<Item, sqlx::Error> { pub async fn get(pool: &PgPool, id: Uuid) -> Result<Item, sqlx::Error> {
query_as!(Item, "SELECT * FROM items WHERE id = $1", id) query_as!(Item, "SELECT * FROM items WHERE id = $1", id)
.fetch_one(pool) .fetch_one(pool)