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:
23
src/migrations/006_subcategories.js
Normal file
23
src/migrations/006_subcategories.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import logger from '../utils/logger.js';
|
||||
|
||||
export default async function migration006(db) {
|
||||
await db.runAsync('BEGIN TRANSACTION');
|
||||
|
||||
await db.runAsync(`CREATE TABLE IF NOT EXISTS subcategories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE,
|
||||
UNIQUE(category_id, name)
|
||||
)`);
|
||||
|
||||
const cols = await db.allAsync('PRAGMA table_info(products)');
|
||||
const hasSubcat = cols.some(c => c.name === 'subcategory_id');
|
||||
if (!hasSubcat) {
|
||||
await db.runAsync(`ALTER TABLE products ADD COLUMN subcategory_id INTEGER REFERENCES subcategories(id) ON DELETE SET NULL`);
|
||||
}
|
||||
|
||||
await db.runAsync('COMMIT');
|
||||
logger.info('Migration 006: subcategories table + products.subcategory_id column');
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import logger from '../utils/logger.js';
|
||||
|
||||
const ALLOWED_TABLES = new Set([
|
||||
'users', 'crypto_wallets', 'transactions', 'products',
|
||||
'purchases', 'locations', 'categories'
|
||||
'purchases', 'locations', 'categories', 'subcategories'
|
||||
]);
|
||||
|
||||
export const checkColumnExists = async (tableName, columnName) => {
|
||||
@@ -40,6 +40,7 @@ export async function runMigrations() {
|
||||
(await import('./003_add_indexes.js')).default,
|
||||
(await import('./004_user_states.js')).default,
|
||||
(await import('./005_audit_log.js')).default,
|
||||
(await import('./006_subcategories.js')).default,
|
||||
];
|
||||
|
||||
for (let i = currentVersion; i < migrations.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user