feat: unified Catalog page with Location→Category→Subcategory→Product tree

- New /catalog page with tree view: Location (🌍) → Category (📂) → Subcategory (📁) → Product
- Add/delete locations, categories, subcategories, products from one page
- JS-powered subcategory dropdown filtered by category
- Sticky sidebar with Add Location/Category/Product forms
- Responsive grid layout (tree + forms side by side, stacks on mobile)
- Navigation simplified: Catalog replaces separate Locations/Categories/Products
- Old routes still accessible for backward compatibility
- Subcategories table migration (006_subcategories.js)
- subcategory_id column added to products table
- Seed data includes subcategories (VPN, Accounts, Hardware, etc.)
This commit is contained in:
NW
2026-06-22 21:12:05 +01:00
parent 2012435370
commit c7bf3f132c
17 changed files with 693 additions and 69 deletions

View File

@@ -0,0 +1,36 @@
import { Router } from 'express';
import db from '../../config/database.js';
import { renderLocationList } from '../views/locations.js';
const router = Router();
router.get('/', async (req, res) => {
const locations = await db.allAsync(`SELECT l.*,
(SELECT COUNT(*) FROM categories WHERE location_id = l.id) as category_count,
(SELECT COUNT(*) FROM products WHERE location_id = l.id) as product_count
FROM locations l ORDER BY l.country, l.city, l.district`);
res.send(renderLocationList(locations));
});
router.post('/', async (req, res) => {
const { country, city, district } = req.body;
await db.runAsync(
'INSERT INTO locations (country, city, district) VALUES (?, ?, ?)',
[country, city, district || '']
);
res.redirect('/locations');
});
router.post('/:id/delete', async (req, res) => {
const count = await db.getAsync(
'SELECT COUNT(*) as cnt FROM categories WHERE location_id = ?',
[req.params.id]
);
if (count && count.cnt > 0) {
return res.redirect('/locations?error=Cannot+delete+location+with+categories');
}
await db.runAsync('DELETE FROM locations WHERE id = ?', [req.params.id]);
res.redirect('/locations');
});
export default router;