Add development documentation and enhance SPA navigation with category/item detail views
- Add DEVELOPMENT.md with complete LXC environment setup guide - Implement dynamic category tree with item navigation in sidebar - Add category detail view with items list via AJAX - Add item edit form with delete functionality - Enhance SPA routing to support query parameters for categories/items - Update Bootstrap styling with icons and improved navigation - Include SQLite database in repository for development
This commit is contained in:
@@ -13,6 +13,7 @@ use App\Router;
|
||||
use App\Controllers\ItemController;
|
||||
use App\Controllers\CategoryController;
|
||||
use App\Database;
|
||||
use App\Models\Category;
|
||||
|
||||
// Initialize Database (ensures tables exist)
|
||||
Database::getInstance();
|
||||
@@ -40,25 +41,58 @@ $router->addRoute('GET', '/print/{id:\d+}', [ItemController::class, 'printQR']);
|
||||
// These routes return only the Twig block content, not the full layout.
|
||||
$router->addRoute('GET', '/api/items', [ItemController::class, 'listItems']);
|
||||
$router->addRoute('GET', '/api/items/{id:\d+}', [ItemController::class, 'getItem']);
|
||||
$router->addRoute('GET', '/api/categories', [CategoryController::class, 'listCategories']);
|
||||
$router->addRoute('GET', '/api/categories/list', [CategoryController::class, 'listCategoriesJson']);
|
||||
$router->addRoute('GET', '/api/categories/{id}', [CategoryController::class, 'getCategory']);
|
||||
$router->addRoute('GET', '/api/parts', [ItemController::class, 'renderAddForm']);
|
||||
|
||||
// Categories with optional filter
|
||||
$router->addRoute('GET', '/api/categories', function() {
|
||||
$categoryId = $_GET['category'] ?? null;
|
||||
if ($categoryId) {
|
||||
// Show category details with items
|
||||
$controller = new CategoryController();
|
||||
$controller->showCategory($categoryId);
|
||||
} else {
|
||||
// Show all categories
|
||||
$controller = new CategoryController();
|
||||
$controller->listCategories();
|
||||
}
|
||||
});
|
||||
|
||||
// Parts with optional item filter
|
||||
$router->addRoute('GET', '/api/parts', function() {
|
||||
$itemId = $_GET['item'] ?? null;
|
||||
if ($itemId) {
|
||||
// Show item details/edit form
|
||||
$controller = new ItemController();
|
||||
$controller->editItem($itemId);
|
||||
} else {
|
||||
// Show add form
|
||||
$controller = new ItemController();
|
||||
$controller->renderAddForm();
|
||||
}
|
||||
});
|
||||
$router->addRoute('GET', '/api/tree', function() {
|
||||
try {
|
||||
$db = App\Database\Database::getInstance();
|
||||
$categories = App\Models\Category::getAll($db);
|
||||
$db = Database::getInstance();
|
||||
$categories = Category::getAll($db);
|
||||
|
||||
function build_category_tree($categories, $parentId = null, &$visited = [], $depth = 0, &$nodeCount = 0) {
|
||||
function build_category_tree($categories, $db, $parentId = null, $visited = [], $depth = 0, $nodeCount = 0) {
|
||||
if ($depth > 5 || $nodeCount > 100) return [];
|
||||
$tree = [];
|
||||
foreach ($categories as $cat) {
|
||||
if ($cat['parent_id'] == $parentId && !in_array($cat['id'], $visited)) {
|
||||
$visited[] = $cat['id'];
|
||||
|
||||
// Get items for this category
|
||||
$itemsStmt = $db->prepare('SELECT id, name FROM items WHERE category_id = :category_id ORDER BY name');
|
||||
$itemsStmt->execute([':category_id' => $cat['id']]);
|
||||
$items = $itemsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$node = [
|
||||
'id' => $cat['id'],
|
||||
'name' => $cat['name'],
|
||||
'children' => build_category_tree($categories, $cat['id'], $visited, $depth + 1, $nodeCount)
|
||||
'items' => $items,
|
||||
'children' => build_category_tree($categories, $db, $cat['id'], $visited, $depth + 1, $nodeCount)
|
||||
];
|
||||
$tree[] = $node;
|
||||
$nodeCount++;
|
||||
@@ -72,21 +106,39 @@ $router->addRoute('GET', '/api/tree', function() {
|
||||
if ($depth > 5) return '';
|
||||
$html = '';
|
||||
foreach ($nodes as $node) {
|
||||
$html .= '<li>';
|
||||
$html .= '<span class="category" onclick="toggleCategory(this)">' . htmlspecialchars($node['name']) . '</span>';
|
||||
$html .= '<li class="nav-item">';
|
||||
$html .= '<a class="nav-link category-link" href="#" data-route="/categories?category=' . $node['id'] . '">';
|
||||
$html .= '<i class="bi bi-folder"></i> ' . htmlspecialchars($node['name']);
|
||||
$html .= '</a>';
|
||||
|
||||
// Add items as sub-items
|
||||
if (!empty($node['items'])) {
|
||||
$html .= '<ul class="nav flex-column nav-pills ms-2">';
|
||||
foreach ($node['items'] as $item) {
|
||||
$html .= '<li class="nav-item">';
|
||||
$html .= '<a class="nav-link item-link" href="#" data-route="/parts?item=' . $item['id'] . '">';
|
||||
$html .= '<i class="bi bi-file-earmark"></i> ' . htmlspecialchars($item['name']);
|
||||
$html .= '</a>';
|
||||
$html .= '</li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
}
|
||||
|
||||
// Add subcategories
|
||||
if (!empty($node['children'])) {
|
||||
$html .= '<ul style="display: none;">';
|
||||
$html .= '<ul class="nav flex-column nav-pills ms-2">';
|
||||
$html .= render_category_tree($node['children'], $depth + 1);
|
||||
$html .= '</ul>';
|
||||
}
|
||||
|
||||
$html .= '</li>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
$nodeCount = 0;
|
||||
$categoryTree = build_category_tree($categories, null, $visited = [], 0, $nodeCount);
|
||||
$html = '<ul class="category-tree">' . render_category_tree($categoryTree) . '</ul>';
|
||||
$categoryTree = build_category_tree($categories, $db, null, [], 0, $nodeCount);
|
||||
$html = '<ul class="nav flex-column nav-pills">' . render_category_tree($categoryTree) . '</ul>';
|
||||
if (strlen($html) > 10000) {
|
||||
$html = '<ul class="nav flex-column nav-pills"><li class="nav-item"><a class="nav-link" href="#" data-route="/">Overview</a></li><li class="nav-item"><a class="nav-link" href="#" data-route="/categories">Categories</a></li><li class="nav-item"><a class="nav-link" href="#" data-route="/parts">Parts</a></li></ul>';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user