Show parent hierarchy in item list and details
This commit is contained in:
parent
9138865b48
commit
b736eba0a0
|
@ -27,6 +27,7 @@ struct ItemDetails {
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
item: Item,
|
item: Item,
|
||||||
item_class: ItemClass,
|
item_class: ItemClass,
|
||||||
|
parents: Vec<Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/item/{id}")]
|
#[get("/item/{id}")]
|
||||||
|
@ -45,10 +46,15 @@ async fn show_item(
|
||||||
.await
|
.await
|
||||||
.map_err(error::ErrorInternalServerError)?;
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
|
let parents = manage::item::get_parents_details(&mut pool.get().await.unwrap(), item.id)
|
||||||
|
.await
|
||||||
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
Ok(ItemDetails {
|
Ok(ItemDetails {
|
||||||
req,
|
req,
|
||||||
item,
|
item,
|
||||||
item_class,
|
item_class,
|
||||||
|
parents,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +62,12 @@ async fn show_item(
|
||||||
#[template(path = "item_list.html")]
|
#[template(path = "item_list.html")]
|
||||||
struct ItemList {
|
struct ItemList {
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
items: Vec<Item>,
|
// Both a Vec and a HashMap are used to have both the natural order,
|
||||||
|
// as well as arbitrary access capabilities.
|
||||||
|
item_list: Vec<Item>,
|
||||||
|
items: HashMap<Uuid, Item>,
|
||||||
item_classes: HashMap<Uuid, ItemClass>,
|
item_classes: HashMap<Uuid, ItemClass>,
|
||||||
|
item_tree: HashMap<Uuid, Vec<Uuid>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/items")]
|
#[get("/items")]
|
||||||
|
@ -65,7 +75,11 @@ async fn list_items(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
pool: web::Data<DbPool>,
|
pool: web::Data<DbPool>,
|
||||||
) -> actix_web::Result<impl Responder> {
|
) -> actix_web::Result<impl Responder> {
|
||||||
let items = manage::item::get_all(&mut pool.get().await.unwrap())
|
let item_list = manage::item::get_all(&mut pool.get().await.unwrap())
|
||||||
|
.await
|
||||||
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
|
let items = manage::item::get_all_as_map(&mut pool.get().await.unwrap())
|
||||||
.await
|
.await
|
||||||
.map_err(error::ErrorInternalServerError)?;
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
|
@ -73,10 +87,16 @@ async fn list_items(
|
||||||
.await
|
.await
|
||||||
.map_err(error::ErrorInternalServerError)?;
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
|
let item_tree = manage::item::get_all_parents(&mut pool.get().await.unwrap())
|
||||||
|
.await
|
||||||
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
|
|
||||||
Ok(ItemList {
|
Ok(ItemList {
|
||||||
req,
|
req,
|
||||||
|
item_list,
|
||||||
items,
|
items,
|
||||||
item_classes,
|
item_classes,
|
||||||
|
item_tree,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
use diesel::sql_types;
|
||||||
use diesel_async::pg::AsyncPgConnection;
|
use diesel_async::pg::AsyncPgConnection;
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -37,6 +38,16 @@ pub async fn get_all(conn: &mut AsyncPgConnection) -> Result<Vec<Item>, diesel::
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_as_map(
|
||||||
|
conn: &mut AsyncPgConnection,
|
||||||
|
) -> Result<HashMap<Uuid, Item>, diesel::result::Error> {
|
||||||
|
Ok(get_all(conn)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| (i.id, i))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
conn: &mut AsyncPgConnection,
|
conn: &mut AsyncPgConnection,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
@ -91,3 +102,23 @@ pub async fn get_all_parents(
|
||||||
.collect::<HashMap<Uuid, Vec<Uuid>>>()
|
.collect::<HashMap<Uuid, Vec<Uuid>>>()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_parents_details(
|
||||||
|
conn: &mut AsyncPgConnection,
|
||||||
|
id: Uuid,
|
||||||
|
) -> Result<Vec<Item>, diesel::result::Error> {
|
||||||
|
define_sql_function!(fn unnest(a: sql_types::Array<sql_types::Uuid>) -> sql_types::Uuid);
|
||||||
|
|
||||||
|
schema::items::table
|
||||||
|
.filter(
|
||||||
|
schema::items::id.eq_any(
|
||||||
|
schema::item_tree::table
|
||||||
|
.filter(schema::item_tree::id.eq(id))
|
||||||
|
.select(unnest(schema::item_tree::parents))
|
||||||
|
.into_boxed(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.select(Item::as_select())
|
||||||
|
.load(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
|
@ -39,3 +39,4 @@ diesel::table! {
|
||||||
diesel::joinable!(items -> item_classes (class));
|
diesel::joinable!(items -> item_classes (class));
|
||||||
|
|
||||||
diesel::allow_tables_to_appear_in_same_query!(item_classes, items);
|
diesel::allow_tables_to_appear_in_same_query!(item_classes, items);
|
||||||
|
diesel::allow_tables_to_appear_in_same_query!(items, item_tree);
|
||||||
|
|
|
@ -36,10 +36,15 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Parent
|
Parents
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
{% if let Some(parent) = item.parent %}{{ parent }}{% else %}-{% endif %}
|
<ol class="breadcrumb mb-0">
|
||||||
|
{%- for parent in parents %}
|
||||||
|
<li class="breadcrumb-item"><a href="/item/{{ parent.id }}">{{ parent.name }}</a></li>
|
||||||
|
{%- endfor %}
|
||||||
|
<li class="breadcrumb-item active">{{ item.name }}</li>
|
||||||
|
</ol>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -15,14 +15,28 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Class</th>
|
<th>Class</th>
|
||||||
|
<th>Parents</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in items -%}
|
{% for item in item_list -%}
|
||||||
{% let class = item_classes.get(item.class).unwrap() %}
|
{% let class = item_classes.get(item.class).unwrap() %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/item/{{ item.id }}">{{ item.name }}</a></td>
|
<td><a href="/item/{{ item.id }}">{{ item.name }}</a></td>
|
||||||
<td><a href="/item-class/{{ class.id }}">{{ class.name }}</a></td>
|
<td><a href="/item-class/{{ class.id }}">{{ class.name }}</a></td>
|
||||||
|
<td>
|
||||||
|
<ol class="breadcrumb mb-0">
|
||||||
|
{%- let parents = item_tree.get(item.id).unwrap() -%}
|
||||||
|
{%- if parents.len() > 3 %}
|
||||||
|
<li class="breadcrumb-item">…</li>
|
||||||
|
{%- endif %}
|
||||||
|
{%- for parent in parents.iter().rev().take(3).rev() %}
|
||||||
|
{%- let parent = items.get(parent).unwrap() %}
|
||||||
|
<li class="breadcrumb-item"><a href="/item/{{ parent.id }}">{{ parent.name }}</a></li>
|
||||||
|
{%- endfor %}
|
||||||
|
<li class="breadcrumb-item active">{{ item.name }}</li>
|
||||||
|
</ol>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
Loading…
Reference in a new issue