in base.html. `category_groups` is a two-level list of top-level
+ categories, each `{ name, slug, children: [{ name, slug }] }`. A category
+ with children is expandable (accordion); one without is a plain link.
+ Active state is set client-side by markActiveNav() via data-nav +
+ aria-current; groups auto-expand when the current page is the category or
+ one of its subcategories. #}
{{ t(key="categories", lang=lang | default(value='sk')) }}
@@ -12,16 +15,46 @@
{{ t(key="all-products", lang=lang | default(value='sk')) }}
- {% for item in category_tree %}
+ {% for group in category_groups %}
+ {% if group.children | length > 0 %}
+
+
+
+
+ {% else %}
-
- {% if item.depth > 0 %}↳ {% endif %}
- {{ item.name }}
+
+ {{ group.name }}
+ {% endif %}
{% endfor %}
-{% if category_tree | length == 0 %}
+{% if category_groups | length == 0 %}
{{ t(key="shop-empty", lang=lang | default(value='sk')) }}
{% endif %}
diff --git a/src/controllers/shop.rs b/src/controllers/shop.rs
index 669c82f..c32453b 100644
--- a/src/controllers/shop.rs
+++ b/src/controllers/shop.rs
@@ -51,7 +51,7 @@ async fn category_sidebar(
&v,
"shop/_sidebar.html",
json!({
- "category_tree": view::sidebar_rows(&categories::tree(&published)),
+ "category_groups": view::sidebar_groups(&published),
"lang": current_lang(&jar),
}),
)
diff --git a/src/views/shop.rs b/src/views/shop.rs
index aae0fa2..e73ab76 100644
--- a/src/views/shop.rs
+++ b/src/views/shop.rs
@@ -45,12 +45,24 @@ pub fn product_form(product: &products::Model, image: Option) -> Value {
})
}
-/// Depth-ordered `{ name, slug, depth }` rows for the storefront sidebar,
-/// rendered as an indented flat list.
-pub fn sidebar_rows(tree: &[(categories::Model, usize)]) -> Vec {
- tree.iter()
- .map(|(category, depth)| {
- json!({ "name": category.name, "slug": category.slug, "depth": depth })
+/// Two-level grouping for the storefront sidebar menu: each top-level category
+/// as `{ name, slug, children: [{ name, slug }] }`, with its direct
+/// subcategories nested under `children` (empty when the category has none).
+/// Siblings are ordered by position then name on both levels.
+pub fn sidebar_groups(categories: &[categories::Model]) -> Vec {
+ let mut top: Vec<&categories::Model> = categories
+ .iter()
+ .filter(|c| c.parent_id.is_none())
+ .collect();
+ top.sort_by(|a, b| a.position.cmp(&b.position).then_with(|| a.name.cmp(&b.name)));
+
+ top.into_iter()
+ .map(|category| {
+ let children: Vec = crate::models::categories::children_of(categories, category.id)
+ .into_iter()
+ .map(|child| json!({ "name": child.name, "slug": child.slug }))
+ .collect();
+ json!({ "name": category.name, "slug": category.slug, "children": children })
})
.collect()
}