Allow batch adding items
This commit is contained in:
parent
693fe68797
commit
125627d7d2
|
@ -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"
|
||||||
|
}
|
|
@ -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")]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue