Add backend changes and update scripts

This commit is contained in:
2025-01-16 00:47:29 -05:00
parent 1ea02b8938
commit 7ec587595a
7 changed files with 589 additions and 174 deletions

View File

@@ -0,0 +1,120 @@
const express = require('express');
const router = express.Router();
// Get categories with pagination, filtering, and sorting
router.get('/', async (req, res) => {
const pool = req.app.locals.pool;
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 50;
const offset = (page - 1) * limit;
const search = req.query.search || '';
const parent = req.query.parent || 'all';
const performance = req.query.performance || 'all';
const sortColumn = req.query.sortColumn || 'name';
const sortDirection = req.query.sortDirection || 'asc';
// Build the WHERE clause based on filters
const whereConditions = [];
const params = [];
if (search) {
whereConditions.push('(c.name LIKE ? OR c.description LIKE ?)');
params.push(`%${search}%`, `%${search}%`);
}
if (parent !== 'all') {
if (parent === 'none') {
whereConditions.push('c.parent_category IS NULL');
} else {
whereConditions.push('c.parent_category = ?');
params.push(parent);
}
}
if (performance !== 'all') {
switch (performance) {
case 'high_growth':
whereConditions.push('cm.growth_rate >= 20');
break;
case 'growing':
whereConditions.push('cm.growth_rate >= 5 AND cm.growth_rate < 20');
break;
case 'stable':
whereConditions.push('cm.growth_rate >= -5 AND cm.growth_rate < 5');
break;
case 'declining':
whereConditions.push('cm.growth_rate < -5');
break;
}
}
const whereClause = whereConditions.length > 0
? 'WHERE ' + whereConditions.join(' AND ')
: '';
// Get total count for pagination
const [countResult] = await pool.query(`
SELECT COUNT(DISTINCT c.id) as total
FROM categories c
LEFT JOIN category_metrics cm ON c.id = cm.category_id
${whereClause}
`, params);
// Get parent categories for filter dropdown
const [parentCategories] = await pool.query(`
SELECT DISTINCT parent_category
FROM categories
WHERE parent_category IS NOT NULL
ORDER BY parent_category
`);
// Get categories with metrics
const [categories] = await pool.query(`
SELECT
c.id as category_id,
c.name,
c.description,
c.parent_category,
cm.product_count,
cm.total_value,
cm.avg_margin,
cm.turnover_rate,
cm.growth_rate,
cm.status
FROM categories c
LEFT JOIN category_metrics cm ON c.id = cm.category_id
${whereClause}
ORDER BY ${sortColumn} ${sortDirection}
LIMIT ? OFFSET ?
`, [...params, limit, offset]);
// Get overall stats
const [stats] = await pool.query(`
SELECT
COUNT(DISTINCT c.id) as totalCategories,
COUNT(DISTINCT CASE WHEN cm.status = 'active' THEN c.id END) as activeCategories,
SUM(cm.total_value) as totalValue,
AVG(cm.avg_margin) as avgMargin,
AVG(cm.growth_rate) as avgGrowth
FROM categories c
LEFT JOIN category_metrics cm ON c.id = cm.category_id
`);
res.json({
categories,
parentCategories: parentCategories.map(p => p.parent_category),
stats: stats[0],
pagination: {
total: countResult[0].total,
pages: Math.ceil(countResult[0].total / limit),
current: page,
}
});
} catch (error) {
console.error('Error fetching categories:', error);
res.status(500).json({ error: 'Failed to fetch categories' });
}
});
module.exports = router;

View File

@@ -0,0 +1,108 @@
const express = require('express');
const router = express.Router();
// Get vendors with pagination, filtering, and sorting
router.get('/', async (req, res) => {
const pool = req.app.locals.pool;
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 50;
const offset = (page - 1) * limit;
const search = req.query.search || '';
const status = req.query.status || 'all';
const performance = req.query.performance || 'all';
const sortColumn = req.query.sortColumn || 'name';
const sortDirection = req.query.sortDirection || 'asc';
// Build the WHERE clause based on filters
const whereConditions = [];
const params = [];
if (search) {
whereConditions.push('(p.vendor LIKE ? OR vm.contact_name LIKE ?)');
params.push(`%${search}%`, `%${search}%`);
}
if (status !== 'all') {
whereConditions.push('vm.status = ?');
params.push(status);
}
if (performance !== 'all') {
switch (performance) {
case 'excellent':
whereConditions.push('vm.order_fill_rate >= 95');
break;
case 'good':
whereConditions.push('vm.order_fill_rate >= 85 AND vm.order_fill_rate < 95');
break;
case 'fair':
whereConditions.push('vm.order_fill_rate >= 75 AND vm.order_fill_rate < 85');
break;
case 'poor':
whereConditions.push('vm.order_fill_rate < 75');
break;
}
}
const whereClause = whereConditions.length > 0
? 'WHERE ' + whereConditions.join(' AND ')
: '';
// Get total count for pagination
const [countResult] = await pool.query(`
SELECT COUNT(DISTINCT p.vendor) as total
FROM products p
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
${whereClause}
`, params);
// Get vendors with metrics
const [vendors] = await pool.query(`
SELECT
p.vendor as name,
vm.contact_name,
vm.email,
vm.phone,
vm.status,
vm.avg_lead_time_days,
vm.on_time_delivery_rate,
vm.order_fill_rate,
vm.total_orders,
COUNT(DISTINCT p.product_id) as active_products
FROM products p
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
${whereClause}
GROUP BY p.vendor
ORDER BY ${sortColumn} ${sortDirection}
LIMIT ? OFFSET ?
`, [...params, limit, offset]);
// Get overall stats
const [stats] = await pool.query(`
SELECT
COUNT(DISTINCT p.vendor) as totalVendors,
COUNT(DISTINCT CASE WHEN vm.status = 'active' THEN p.vendor END) as activeVendors,
AVG(vm.avg_lead_time_days) as avgLeadTime,
AVG(vm.order_fill_rate) as avgFillRate,
AVG(vm.on_time_delivery_rate) as avgOnTimeDelivery
FROM products p
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
`);
res.json({
vendors,
stats: stats[0],
pagination: {
total: countResult[0].total,
pages: Math.ceil(countResult[0].total / limit),
current: page,
}
});
} catch (error) {
console.error('Error fetching vendors:', error);
res.status(500).json({ error: 'Failed to fetch vendors' });
}
});
module.exports = router;