update
This commit is contained in:
@@ -9,10 +9,10 @@ impl Home {
|
|||||||
let doc: Markup = html!(
|
let doc: Markup = html!(
|
||||||
div id="home" class={"p-8" "max-w-xl"} {
|
div id="home" class={"p-8" "max-w-xl"} {
|
||||||
p {
|
p {
|
||||||
a href="/inventory" { "Inventory" }
|
a href="/inventory/" { "Inventory" }
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
a href="/trips" { "Trips" }
|
a href="/trips/" { "Trips" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -162,6 +162,15 @@ impl InventoryItemList {
|
|||||||
@if items.is_empty() {
|
@if items.is_empty() {
|
||||||
p ."text-lg" ."text-center" ."py-5" ."text-gray-400" { "[Empty]" }
|
p ."text-lg" ."text-center" ."py-5" ."text-gray-400" { "[Empty]" }
|
||||||
} @else {
|
} @else {
|
||||||
|
@if let Some(edit_item) = edit_item {
|
||||||
|
form
|
||||||
|
name="edit-item"
|
||||||
|
id="edit-item"
|
||||||
|
action=(format!("/inventory/item/{edit_item}/edit"))
|
||||||
|
target="_self"
|
||||||
|
method="post"
|
||||||
|
{}
|
||||||
|
}
|
||||||
table
|
table
|
||||||
."table"
|
."table"
|
||||||
."table-auto"
|
."table-auto"
|
||||||
@@ -179,7 +188,36 @@ impl InventoryItemList {
|
|||||||
tbody {
|
tbody {
|
||||||
@for item in items {
|
@for item in items {
|
||||||
@if edit_item.map_or(false, |edit_item| edit_item == item.id) {
|
@if edit_item.map_or(false, |edit_item| edit_item == item.id) {
|
||||||
tr { td { (item.name.clone()) " is being edited" }}
|
tr ."h-10" {
|
||||||
|
td ."border" ."p-2" ."bg-blue-100" {
|
||||||
|
input ."w-full"
|
||||||
|
type="text"
|
||||||
|
id="edit-item-name"
|
||||||
|
name="edit-item-name"
|
||||||
|
form="edit-item"
|
||||||
|
value=(item.name)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
td ."border" ."p-2" ."bg-blue-100" {
|
||||||
|
input ."w-full"
|
||||||
|
type="number"
|
||||||
|
id="edit-item-weight"
|
||||||
|
name="edit-item-weight"
|
||||||
|
form="edit-item"
|
||||||
|
value=(item.weight)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
td ."border" ."p-2" ."bg-green-100" {
|
||||||
|
button type="submit" form="edit-item" {
|
||||||
|
span ."mdi" ."mdi-content-save" ."text-xl" {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td ."border" ."p-2" ."bg-red-100" {
|
||||||
|
a href=(format!("/inventory/item/{id}/cancel", id = item.id)) {
|
||||||
|
span ."mdi" ."mdi-cancel" ."text-xl" {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} @else {
|
} @else {
|
||||||
tr ."h-10" ."even:bg-gray-100" ."hover:bg-purple-100" {
|
tr ."h-10" ."even:bg-gray-100" ."hover:bg-purple-100" {
|
||||||
td ."border" ."p-0" {
|
td ."border" ."p-0" {
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ impl Root {
|
|||||||
}} { "Trips" }
|
}} { "Trips" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div hx-boost="true" {
|
// div hx-boost="true" {
|
||||||
|
div {
|
||||||
(body)
|
(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ use sqlx::{
|
|||||||
error::DatabaseError,
|
error::DatabaseError,
|
||||||
query,
|
query,
|
||||||
sqlite::{SqliteConnectOptions, SqliteError, SqlitePoolOptions},
|
sqlite::{SqliteConnectOptions, SqliteError, SqlitePoolOptions},
|
||||||
Pool, Sqlite,
|
Pool, Row, Sqlite,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use futures::TryFutureExt;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use uuid::{uuid, Uuid};
|
use uuid::{uuid, Uuid};
|
||||||
|
|
||||||
@@ -85,6 +86,8 @@ async fn main() -> Result<(), sqlx::Error> {
|
|||||||
.route("/inventory/item/", post(inventory_item_create))
|
.route("/inventory/item/", post(inventory_item_create))
|
||||||
.route("/inventory/category/:id", get(inventory_active))
|
.route("/inventory/category/:id", get(inventory_active))
|
||||||
.route("/inventory/item/:id/delete", get(inventory_item_delete))
|
.route("/inventory/item/:id/delete", get(inventory_item_delete))
|
||||||
|
.route("/inventory/item/:id/edit", post(inventory_item_edit))
|
||||||
|
.route("/inventory/item/:id/cancel", get(inventory_item_cancel))
|
||||||
// .route(
|
// .route(
|
||||||
// "/inventory/category/:id/items",
|
// "/inventory/category/:id/items",
|
||||||
// post(htmx_inventory_category_items),
|
// post(htmx_inventory_category_items),
|
||||||
@@ -293,7 +296,7 @@ async fn inventory_item_delete(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> Result<Redirect, (StatusCode, String)> {
|
) -> Result<Redirect, (StatusCode, String)> {
|
||||||
query(
|
let results = query(
|
||||||
"DELETE FROM inventoryitems
|
"DELETE FROM inventoryitems
|
||||||
WHERE id = ?",
|
WHERE id = ?",
|
||||||
)
|
)
|
||||||
@@ -302,6 +305,12 @@ async fn inventory_item_delete(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
|
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
|
||||||
|
|
||||||
|
if results.rows_affected() == 0 {
|
||||||
|
Err((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
format!("item with id {id} not found", id = id),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
Ok(Redirect::to(
|
Ok(Redirect::to(
|
||||||
headers
|
headers
|
||||||
.get("referer")
|
.get("referer")
|
||||||
@@ -318,6 +327,7 @@ async fn inventory_item_delete(
|
|||||||
})?,
|
})?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// async fn htmx_inventory_category_items(
|
// async fn htmx_inventory_category_items(
|
||||||
// Path(id): Path<String>,
|
// Path(id): Path<String>,
|
||||||
@@ -360,3 +370,44 @@ async fn inventory_item_delete(
|
|||||||
// ),
|
// ),
|
||||||
// ))
|
// ))
|
||||||
// }
|
// }
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct EditItem {
|
||||||
|
#[serde(rename = "edit-item-name")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "edit-item-weight")]
|
||||||
|
weight: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inventory_item_edit(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
Form(edit_item): Form<EditItem>,
|
||||||
|
) -> Result<Redirect, (StatusCode, String)> {
|
||||||
|
let id = Item::update(&state.database_pool, id, &edit_item.name, edit_item.weight)
|
||||||
|
.await
|
||||||
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
format!("item with id {id} not found", id = id),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(&format!("/inventory/category/{id}", id = id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inventory_item_cancel(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Redirect, (StatusCode, String)> {
|
||||||
|
let id = Item::find(&state.database_pool, id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
format!("item with id {id} not found", id = id),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(&format!(
|
||||||
|
"/inventory/category/{id}",
|
||||||
|
id = id.category_id
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use sqlx::sqlite::SqlitePoolOptions;
|
use sqlx::sqlite::SqlitePoolOptions;
|
||||||
|
|
||||||
|
use futures::TryFutureExt;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -164,3 +165,60 @@ impl TryFrom<SqliteRow> for Item {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub async fn find(pool: &sqlx::Pool<sqlx::Sqlite>, id: Uuid) -> Result<Option<Item>, Error> {
|
||||||
|
let item: Result<Result<Item, Error>, sqlx::Error> = sqlx::query(
|
||||||
|
"SELECT * FROM inventoryitems AS item
|
||||||
|
WHERE item.id = ?",
|
||||||
|
)
|
||||||
|
.bind(id.to_string())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.map_ok(std::convert::TryInto::try_into)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match item {
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => Ok(None),
|
||||||
|
_ => Err(e.into()),
|
||||||
|
},
|
||||||
|
Ok(v) => Ok(Some(v?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update(
|
||||||
|
pool: &sqlx::Pool<sqlx::Sqlite>,
|
||||||
|
id: Uuid,
|
||||||
|
name: &str,
|
||||||
|
weight: u32,
|
||||||
|
) -> Result<Option<Uuid>, Error> {
|
||||||
|
let id: Result<Result<Uuid, Error>, sqlx::Error> = sqlx::query(
|
||||||
|
"UPDATE inventoryitems AS item
|
||||||
|
SET
|
||||||
|
name = ?,
|
||||||
|
weight = ?
|
||||||
|
WHERE item.id = ?
|
||||||
|
RETURNING inventoryitems.category_id AS id
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.bind(name)
|
||||||
|
.bind(weight)
|
||||||
|
.bind(id.to_string())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.map_ok(|row| {
|
||||||
|
let id: &str = row.try_get("id")?;
|
||||||
|
let uuid: Result<Uuid, uuid::Error> = Uuid::try_parse(id);
|
||||||
|
let uuid: Result<Uuid, Error> = uuid.map_err(|e| e.into());
|
||||||
|
uuid
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match id {
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => Ok(None),
|
||||||
|
_ => Err(e.into()),
|
||||||
|
},
|
||||||
|
Ok(v) => Ok(Some(v?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user