update
This commit is contained in:
@@ -14,15 +14,17 @@ impl Inventory {
|
|||||||
div id="pkglist-item-manager" {
|
div id="pkglist-item-manager" {
|
||||||
div ."p-8" ."grid" ."grid-cols-4" ."gap-3" {
|
div ."p-8" ."grid" ."grid-cols-4" ."gap-3" {
|
||||||
div ."col-span-2" {
|
div ."col-span-2" {
|
||||||
({<InventoryCategoryList as Into<Markup>>::into(InventoryCategoryList::build(&categories))})
|
(InventoryCategoryList::build(&state, &categories).into_markup())
|
||||||
}
|
}
|
||||||
div ."col-span-2" {
|
div ."col-span-2" {
|
||||||
h1 ."text-2xl" ."mb-5" ."text-center" { "Items" }
|
h1 ."text-2xl" ."mb-5" ."text-center" { "Items" }
|
||||||
@if state.active_category_id.is_some() {
|
@if let Some(active_category_id) = state.active_category_id {
|
||||||
({<InventoryItemList as Into<Markup>>::into(InventoryItemList::build(categories.iter().find(|category| category.active).unwrap().items(), state.edit_item))})
|
(InventoryItemList::build(&state, categories.iter().find(|category| category.id == active_category_id)
|
||||||
|
.ok_or(Error::NotFoundError { description: format!("no category with id {}", active_category_id) })?
|
||||||
|
.items())
|
||||||
|
.into_markup())
|
||||||
}
|
}
|
||||||
({<InventoryNewItemForm as Into<Markup>>::into(InventoryNewItemForm::build(&state, &categories))})
|
(InventoryNewItemForm::build(&state, &categories).into_markup())
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +45,7 @@ pub struct InventoryCategoryList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InventoryCategoryList {
|
impl InventoryCategoryList {
|
||||||
pub fn build(categories: &Vec<Category>) -> Self {
|
pub fn build(state: &ClientState, categories: &Vec<Category>) -> Self {
|
||||||
let biggest_category_weight: u32 = categories
|
let biggest_category_weight: u32 = categories
|
||||||
.iter()
|
.iter()
|
||||||
.map(Category::total_weight)
|
.map(Category::total_weight)
|
||||||
@@ -68,20 +70,20 @@ impl InventoryCategoryList {
|
|||||||
}
|
}
|
||||||
thead ."bg-gray-200" {
|
thead ."bg-gray-200" {
|
||||||
tr ."h-10" {
|
tr ."h-10" {
|
||||||
th ."border" ."p-2" { "Name" }
|
th ."border" ."p-2" ."w-3/5" { "Name" }
|
||||||
th ."border" ."p-2" { "Weight" }
|
th ."border" ."p-2" { "Weight" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
@for category in categories {
|
@for category in categories {
|
||||||
tr class={@if category.active {
|
tr class={@if state.active_category_id.map_or(false, |id| category.id == id) {
|
||||||
"h-10 hover:bg-purple-100 m-3 h-full outline outline-2 outline-indigo-300"
|
"h-10 hover:bg-purple-100 m-3 h-full outline outline-2 outline-indigo-300 pointer-events-none"
|
||||||
} @else {
|
} @else {
|
||||||
"h-10 hover:bg-purple-100 m-3 h-full"
|
"h-10 hover:bg-purple-100 m-3 h-full"
|
||||||
}} {
|
}} {
|
||||||
|
|
||||||
td
|
td
|
||||||
class=@if category.active {
|
class=@if state.active_category_id.map_or(false, |id| category.id == id) {
|
||||||
"border p-0 m-0 font-bold"
|
"border p-0 m-0 font-bold"
|
||||||
} @else {
|
} @else {
|
||||||
"border p-0 m-0"
|
"border p-0 m-0"
|
||||||
@@ -142,6 +144,10 @@ impl InventoryCategoryList {
|
|||||||
|
|
||||||
Self { doc }
|
Self { doc }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_markup(self) -> Markup {
|
||||||
|
self.doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InventoryCategoryList> for Markup {
|
impl From<InventoryCategoryList> for Markup {
|
||||||
@@ -155,14 +161,14 @@ pub struct InventoryItemList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InventoryItemList {
|
impl InventoryItemList {
|
||||||
pub fn build(items: &Vec<Item>, edit_item: Option<Uuid>) -> Self {
|
pub fn build(state: &ClientState, items: &Vec<Item>) -> Self {
|
||||||
let biggest_item_weight: u32 = items.iter().map(|item| item.weight).max().unwrap_or(1);
|
let biggest_item_weight: u32 = items.iter().map(|item| item.weight).max().unwrap_or(1);
|
||||||
let doc = html!(
|
let doc = html!(
|
||||||
div #items {
|
div #items {
|
||||||
@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 {
|
@if let Some(edit_item) = state.edit_item {
|
||||||
form
|
form
|
||||||
name="edit-item"
|
name="edit-item"
|
||||||
id="edit-item"
|
id="edit-item"
|
||||||
@@ -174,6 +180,7 @@ impl InventoryItemList {
|
|||||||
table
|
table
|
||||||
."table"
|
."table"
|
||||||
."table-auto"
|
."table-auto"
|
||||||
|
.table-fixed
|
||||||
."border-collapse"
|
."border-collapse"
|
||||||
."border-spacing-0"
|
."border-spacing-0"
|
||||||
."border"
|
."border"
|
||||||
@@ -181,16 +188,18 @@ impl InventoryItemList {
|
|||||||
{
|
{
|
||||||
thead ."bg-gray-200" {
|
thead ."bg-gray-200" {
|
||||||
tr ."h-10" {
|
tr ."h-10" {
|
||||||
th ."border" ."p-2" { "Name" }
|
th ."border" ."p-2" ."w-3/5" { "Name" }
|
||||||
th ."border" ."p-2" { "Weight" }
|
th ."border" ."p-2" { "Weight" }
|
||||||
|
th ."border" ."p-2" ."w-10" {}
|
||||||
|
th ."border" ."p-2" ."w-10" {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
@for item in items {
|
@for item in items {
|
||||||
@if edit_item.map_or(false, |edit_item| edit_item == item.id) {
|
@if state.edit_item.map_or(false, |edit_item| edit_item == item.id) {
|
||||||
tr ."h-10" {
|
tr ."h-10" {
|
||||||
td ."border" ."p-2" ."bg-blue-100" {
|
td ."border" ."bg-blue-300" ."px-2" ."py-0" .flex {
|
||||||
input ."w-full"
|
input ."block" ."w-full" ."bg-blue-100"
|
||||||
type="text"
|
type="text"
|
||||||
id="edit-item-name"
|
id="edit-item-name"
|
||||||
name="edit-item-name"
|
name="edit-item-name"
|
||||||
@@ -198,23 +207,29 @@ impl InventoryItemList {
|
|||||||
value=(item.name)
|
value=(item.name)
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
td ."border" ."p-2" ."bg-blue-100" {
|
td ."border" ."bg-blue-300" ."px-2" ."py-0" {
|
||||||
input ."w-full"
|
// div ."h-full" ."w-full" {
|
||||||
type="number"
|
// input ."block" ."w-full" ."bg-blue-100"
|
||||||
id="edit-item-weight"
|
// type="number"
|
||||||
name="edit-item-weight"
|
// id="edit-item-weight"
|
||||||
form="edit-item"
|
// name="edit-item-weight"
|
||||||
value=(item.weight)
|
// form="edit-item"
|
||||||
{}
|
// value=(item.weight)
|
||||||
|
// {}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
td ."border" ."p-2" ."bg-green-100" {
|
td ."border-none" ."bg-green-100" ."hover:bg-green-200" .flex ."p-0" {
|
||||||
button type="submit" form="edit-item" {
|
div .aspect-square .w-full .h-full .flex {
|
||||||
span ."mdi" ."mdi-content-save" ."text-xl" {}
|
button type="submit" form="edit-item" .m-auto .w-full .h-full {
|
||||||
|
span ."mdi" ."mdi-content-save" ."text-xl" .m-auto {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
td ."border" ."p-2" ."bg-red-100" {
|
td ."border-none" ."bg-red-100" ."hover:bg-red-200" ."p-0" {
|
||||||
a href=(format!("/inventory/item/{id}/cancel", id = item.id)) {
|
div .aspect-square .flex .w-full .h-full {
|
||||||
span ."mdi" ."mdi-cancel" ."text-xl" {}
|
a href=(format!("/inventory/item/{id}/cancel", id = item.id)) .flex .m-auto .w-full .h-full {
|
||||||
|
span ."mdi" ."mdi-cancel" ."text-xl" .m-auto {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,10 +262,12 @@ impl InventoryItemList {
|
|||||||
."w-8"
|
."w-8"
|
||||||
."text-center"
|
."text-center"
|
||||||
{
|
{
|
||||||
a href = (format!("?edit_item={id}", id = item.id))
|
div .aspect-square .flex .w-full .h-full {
|
||||||
{
|
a href = (format!("?edit_item={id}", id = item.id)) ."m-auto"
|
||||||
button {
|
{
|
||||||
span ."mdi" ."mdi-pencil" ."text-xl" {}
|
button {
|
||||||
|
span ."mdi" ."mdi-pencil" ."text-xl" {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,10 +279,12 @@ impl InventoryItemList {
|
|||||||
."w-8"
|
."w-8"
|
||||||
."text-center"
|
."text-center"
|
||||||
{
|
{
|
||||||
a href = (format!("/inventory/item/{id}/delete", id = item.id))
|
div .aspect-square .flex .w-full .h-full {
|
||||||
{
|
a href = (format!("/inventory/item/{id}/delete", id = item.id)) ."m-auto"
|
||||||
button {
|
{
|
||||||
span ."mdi" ."mdi-delete" ."text-xl" {}
|
button {
|
||||||
|
span ."mdi" ."mdi-delete" ."text-xl" {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +299,10 @@ impl InventoryItemList {
|
|||||||
|
|
||||||
Self { doc }
|
Self { doc }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_markup(self) -> Markup {
|
||||||
|
self.doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InventoryItemList> for Markup {
|
impl From<InventoryItemList> for Markup {
|
||||||
@@ -363,17 +386,12 @@ impl InventoryNewItemForm {
|
|||||||
."rounded"
|
."rounded"
|
||||||
."focus:outline-none"
|
."focus:outline-none"
|
||||||
."focus:bg-white"
|
."focus:bg-white"
|
||||||
."focus:border-purple-500" {
|
."focus:border-purple-500"
|
||||||
|
autocomplete="off" // https://stackoverflow.com/a/10096033
|
||||||
|
{
|
||||||
@for category in categories {
|
@for category in categories {
|
||||||
@if state.active_category_id.map_or(false, |id| id == category.id) {
|
option value=(category.id) selected[state.active_category_id.map_or(false, |id| id == category.id)] {
|
||||||
|
(category.name)
|
||||||
option value=(category.id) selected="true" {
|
|
||||||
(category.name)
|
|
||||||
}
|
|
||||||
} @else {
|
|
||||||
option value=(category.id) {
|
|
||||||
(category.name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,6 +411,10 @@ impl InventoryNewItemForm {
|
|||||||
|
|
||||||
Self { doc }
|
Self { doc }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_markup(self) -> Markup {
|
||||||
|
self.doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InventoryNewItemForm> for Markup {
|
impl From<InventoryNewItemForm> for Markup {
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ impl Root {
|
|||||||
}} { "Trips" }
|
}} { "Trips" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// div hx-boost="true" {
|
div hx-boost="true" {
|
||||||
div {
|
|
||||||
(body)
|
(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,12 +169,6 @@ async fn inventory(
|
|||||||
.populate_items(&state.database_pool)
|
.populate_items(&state.database_pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Html::from(e.to_string())))?;
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Html::from(e.to_string())))?;
|
||||||
|
|
||||||
if let Some(active_id) = active_id {
|
|
||||||
if category.id == active_id {
|
|
||||||
category.active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -183,7 +177,12 @@ async fn inventory(
|
|||||||
Root::build(
|
Root::build(
|
||||||
Inventory::build(state.client_state, categories)
|
Inventory::build(state.client_state, categories)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Html::from(e.to_string())))?
|
.map_err(|e| match e {
|
||||||
|
Error::NotFoundError { description } => {
|
||||||
|
(StatusCode::NOT_FOUND, Html::from(description))
|
||||||
|
}
|
||||||
|
_ => (StatusCode::INTERNAL_SERVER_ERROR, Html::from(e.to_string())),
|
||||||
|
})?
|
||||||
.into(),
|
.into(),
|
||||||
&TopLevelPage::Inventory,
|
&TopLevelPage::Inventory,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use futures::TryStreamExt;
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
SqlError { description: String },
|
SqlError { description: String },
|
||||||
UuidError { description: String },
|
UuidError { description: String },
|
||||||
|
NotFoundError { description: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@@ -23,6 +24,9 @@ impl fmt::Display for Error {
|
|||||||
Self::UuidError { description } => {
|
Self::UuidError { description } => {
|
||||||
write!(f, "UUID error: {description}")
|
write!(f, "UUID error: {description}")
|
||||||
}
|
}
|
||||||
|
Self::NotFoundError { description } => {
|
||||||
|
write!(f, "Not found: {description}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,12 +82,12 @@ impl TryFrom<SqliteRow> for Trip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Category {
|
pub struct Category {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
items: Option<Vec<Item>>,
|
items: Option<Vec<Item>>,
|
||||||
pub active: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SqliteRow> for Category {
|
impl TryFrom<SqliteRow> for Category {
|
||||||
@@ -99,7 +103,6 @@ impl TryFrom<SqliteRow> for Category {
|
|||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
description: description.to_string(),
|
description: description.to_string(),
|
||||||
items: None,
|
items: None,
|
||||||
active: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +141,7 @@ impl<'a> Category {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user