Replace sidebar menu with collapsible category tree showing items

This commit is contained in:
2025-11-12 08:36:40 +01:00
parent 8f072292c1
commit 5549c67eb5
96 changed files with 95 additions and 10 deletions
+29
View File
@@ -45,3 +45,32 @@ $twig->addGlobal('app_name', APP_NAME);
// Add translated confirm messages for JS // Add translated confirm messages for JS
$twig->addGlobal('delete_part_confirm', $translator->trans('Are you sure you want to delete this part?')); $twig->addGlobal('delete_part_confirm', $translator->trans('Are you sure you want to delete this part?'));
$twig->addGlobal('delete_category_confirm', $translator->trans('Are you sure you want to delete this category?')); $twig->addGlobal('delete_category_confirm', $translator->trans('Are you sure you want to delete this category?'));
// Build category tree for sidebar
$db = App\Database\Database::getInstance();
$categories = App\Models\Category::getAll($db);
$items = App\Models\Item::getAll($db);
function buildTree($categories, $items, $parentId = null) {
$tree = [];
foreach ($categories as $cat) {
if ($cat['parent_id'] == $parentId) {
$node = [
'id' => $cat['id'],
'name' => $cat['name'],
'children' => buildTree($categories, $items, $cat['id']),
'items' => []
];
foreach ($items as $item) {
if ($item['category_id'] == $cat['id']) {
$node['items'][] = $item;
}
}
$tree[] = $node;
}
}
return $tree;
}
$categoryTree = buildTree($categories, $items);
$twig->addGlobal('category_tree', $categoryTree);
+16
View File
@@ -2,6 +2,22 @@ document.addEventListener('DOMContentLoaded', function() {
const mainContent = document.getElementById('main-content'); const mainContent = document.getElementById('main-content');
const appName = document.querySelector('.navbar-brand').textContent; const appName = document.querySelector('.navbar-brand').textContent;
// Global function for editing item from tree
window.editItem = function(id) {
// Create a mock button to reuse handleEditItem
const mockBtn = { getAttribute: (attr) => attr === 'data-id' ? id : null };
handleEditItem.call(mockBtn);
};
// Function to toggle category children
window.toggleCategory = function(span) {
const li = span.parentElement;
const ul = li.querySelector('ul'); // First ul is children
if (ul) {
ul.style.display = ul.style.display === 'none' ? 'block' : 'none';
}
};
// --- Helper Functions --- // --- Helper Functions ---
function setPageTitle(title) { function setPageTitle(title) {
document.title = `${appName} - ${title}`; document.title = `${appName} - ${title}`;
+26
View File
@@ -0,0 +1,26 @@
.category-tree, .category-tree ul {
list-style: none;
padding-left: 20px;
}
.category-tree li {
margin: 5px 0;
}
.category {
font-weight: bold;
cursor: pointer;
}
.items {
padding-left: 10px;
}
.item-link {
color: #007bff;
text-decoration: none;
}
.item-link:hover {
text-decoration: underline;
}
+23 -9
View File
@@ -1,13 +1,27 @@
{% autoescape %} {% autoescape %}
<ul class="nav flex-column nav-pills"> {% macro render_node(node) %}
<li class="nav-item"> <li>
<a class="nav-link {% if active_page == 'overview' %}active{% endif %}" href="#" data-route="/">{{ trans('Overview') }}</a> <span class="category" onclick="toggleCategory(this)">{{ node.name }}</span>
</li> {% if node.children %}
<li class="nav-item"> <ul style="display: none;">
<a class="nav-link {% if active_page == 'categories' %}active{% endif %}" href="#" data-route="/categories">{{ trans('Categories') }}</a> {% for child in node.children %}
</li> {{ _self.render_node(child) }}
<li class="nav-item"> {% endfor %}
<a class="nav-link {% if active_page == 'parts' %}active{% endif %}" href="#" data-route="/parts">{{ trans('Parts') }}</a> </ul>
{% endif %}
{% if node.items %}
<ul class="items">
{% for item in node.items %}
<li><a href="#" onclick="editItem({{ item.id }})" class="item-link">{{ item.name }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li> </li>
{% endmacro %}
<ul class="category-tree">
{% for node in category_tree %}
{{ _self.render_node(node) }}
{% endfor %}
</ul> </ul>
{% endautoescape %} {% endautoescape %}
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
Vendored Regular → Executable
View File
View File
View File
View File
View File
View File
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
View File
View File
View File
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
View File
View File
View File
View File
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File