Fix all regressions and get everything loading again
This commit is contained in:
@@ -137,47 +137,107 @@ router.get('/vendors', async (req, res) => {
|
||||
try {
|
||||
const pool = req.app.locals.pool;
|
||||
|
||||
console.log('Fetching vendor performance data...');
|
||||
|
||||
// First check if we have any vendors with sales
|
||||
const [checkData] = await pool.query(`
|
||||
SELECT COUNT(DISTINCT p.vendor) as vendor_count,
|
||||
COUNT(DISTINCT o.order_number) as order_count
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
WHERE p.vendor IS NOT NULL
|
||||
`);
|
||||
|
||||
console.log('Vendor data check:', checkData[0]);
|
||||
|
||||
// Get vendor performance metrics
|
||||
const [performance] = await pool.query(`
|
||||
WITH monthly_sales AS (
|
||||
SELECT
|
||||
p.vendor,
|
||||
SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END) as current_month,
|
||||
SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
AND o.date < DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END) as previous_month
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
WHERE p.vendor IS NOT NULL
|
||||
AND o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
GROUP BY p.vendor
|
||||
)
|
||||
SELECT
|
||||
p.vendor,
|
||||
SUM(o.price * o.quantity) as salesVolume,
|
||||
ROUND(
|
||||
COALESCE(ROUND(
|
||||
(SUM(o.price * o.quantity - p.cost_price * o.quantity) /
|
||||
NULLIF(SUM(o.price * o.quantity), 0)) * 100, 1
|
||||
) as profitMargin,
|
||||
ROUND(
|
||||
), 0) as profitMargin,
|
||||
COALESCE(ROUND(
|
||||
SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0), 1
|
||||
) as stockTurnover,
|
||||
COUNT(DISTINCT p.product_id) as productCount
|
||||
), 0) as stockTurnover,
|
||||
COUNT(DISTINCT p.product_id) as productCount,
|
||||
ROUND(
|
||||
((ms.current_month / NULLIF(ms.previous_month, 0)) - 1) * 100,
|
||||
1
|
||||
) as growth
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
AND p.vendor IS NOT NULL
|
||||
GROUP BY p.vendor
|
||||
HAVING salesVolume > 0
|
||||
LEFT JOIN monthly_sales ms ON p.vendor = ms.vendor
|
||||
WHERE p.vendor IS NOT NULL
|
||||
AND o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.vendor, ms.current_month, ms.previous_month
|
||||
ORDER BY salesVolume DESC
|
||||
LIMIT 10
|
||||
`);
|
||||
|
||||
console.log('Performance data:', performance);
|
||||
|
||||
// Get vendor comparison data
|
||||
const [comparison] = await pool.query(`
|
||||
SELECT
|
||||
p.vendor,
|
||||
ROUND(SUM(o.price * o.quantity) / NULLIF(COUNT(DISTINCT p.product_id), 0), 2) as salesPerProduct,
|
||||
ROUND(AVG((o.price - p.cost_price) / NULLIF(o.price, 0) * 100), 1) as averageMargin,
|
||||
COALESCE(ROUND(SUM(o.price * o.quantity) / NULLIF(COUNT(DISTINCT p.product_id), 0), 2), 0) as salesPerProduct,
|
||||
COALESCE(ROUND(AVG((o.price - p.cost_price) / NULLIF(o.price, 0) * 100), 1), 0) as averageMargin,
|
||||
COUNT(DISTINCT p.product_id) as size
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
AND p.vendor IS NOT NULL
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id AND o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
WHERE p.vendor IS NOT NULL
|
||||
GROUP BY p.vendor
|
||||
HAVING salesPerProduct > 0
|
||||
ORDER BY salesPerProduct DESC
|
||||
LIMIT 20
|
||||
`);
|
||||
|
||||
res.json({ performance, comparison });
|
||||
console.log('Comparison data:', comparison);
|
||||
|
||||
// Get vendor sales trends
|
||||
const [trends] = await pool.query(`
|
||||
SELECT
|
||||
p.vendor,
|
||||
DATE_FORMAT(o.date, '%b %Y') as month,
|
||||
COALESCE(SUM(o.price * o.quantity), 0) as sales
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
WHERE p.vendor IS NOT NULL
|
||||
AND o.date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
GROUP BY
|
||||
p.vendor,
|
||||
DATE_FORMAT(o.date, '%b %Y'),
|
||||
DATE_FORMAT(o.date, '%Y-%m')
|
||||
ORDER BY
|
||||
p.vendor,
|
||||
DATE_FORMAT(o.date, '%Y-%m')
|
||||
`);
|
||||
|
||||
console.log('Trends data:', trends);
|
||||
|
||||
res.json({ performance, comparison, trends });
|
||||
} catch (error) {
|
||||
console.error('Error fetching vendor performance:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch vendor performance' });
|
||||
@@ -382,22 +442,33 @@ router.get('/categories', async (req, res) => {
|
||||
|
||||
// Get category performance metrics
|
||||
const [performance] = await pool.query(`
|
||||
WITH monthly_sales AS (
|
||||
SELECT
|
||||
c.name,
|
||||
SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END) as current_month,
|
||||
SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
AND o.date < DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END) as previous_month
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
GROUP BY c.name
|
||||
)
|
||||
SELECT
|
||||
c.name as category,
|
||||
SUM(o.price * o.quantity) as revenue,
|
||||
SUM(o.price * o.quantity - p.cost_price * o.quantity) as profit,
|
||||
ROUND(
|
||||
((SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END) /
|
||||
NULLIF(SUM(CASE
|
||||
WHEN o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
AND o.date < DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
THEN o.price * o.quantity
|
||||
ELSE 0
|
||||
END), 0)) - 1) * 100,
|
||||
((ms.current_month / NULLIF(ms.previous_month, 0)) - 1) * 100,
|
||||
1
|
||||
) as growth,
|
||||
COUNT(DISTINCT p.product_id) as productCount
|
||||
@@ -405,8 +476,9 @@ router.get('/categories', async (req, res) => {
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
LEFT JOIN monthly_sales ms ON c.name = ms.name
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
GROUP BY c.name
|
||||
GROUP BY c.name, ms.current_month, ms.previous_month
|
||||
HAVING revenue > 0
|
||||
ORDER BY revenue DESC
|
||||
LIMIT 10
|
||||
@@ -428,7 +500,27 @@ router.get('/categories', async (req, res) => {
|
||||
LIMIT 6
|
||||
`);
|
||||
|
||||
res.json({ performance, distribution });
|
||||
// Get category sales trends
|
||||
const [trends] = await pool.query(`
|
||||
SELECT
|
||||
c.name as category,
|
||||
DATE_FORMAT(o.date, '%b %Y') as month,
|
||||
SUM(o.price * o.quantity) as sales
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
GROUP BY
|
||||
c.name,
|
||||
DATE_FORMAT(o.date, '%b %Y'),
|
||||
DATE_FORMAT(o.date, '%Y-%m')
|
||||
ORDER BY
|
||||
c.name,
|
||||
DATE_FORMAT(o.date, '%Y-%m')
|
||||
`);
|
||||
|
||||
res.json({ performance, distribution, trends });
|
||||
} catch (error) {
|
||||
console.error('Error fetching category performance:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch category performance' });
|
||||
|
||||
@@ -287,6 +287,7 @@ router.get('/inventory/low-stock', async (req, res) => {
|
||||
router.get('/vendors/metrics', async (req, res) => {
|
||||
const pool = req.app.locals.pool;
|
||||
try {
|
||||
console.log('Fetching vendor metrics...');
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
vendor,
|
||||
@@ -300,18 +301,74 @@ router.get('/vendors/metrics', async (req, res) => {
|
||||
FROM vendor_metrics
|
||||
ORDER BY on_time_delivery_rate DESC
|
||||
`);
|
||||
res.json(rows.map(row => ({
|
||||
console.log('Found vendor metrics:', rows.length, 'rows');
|
||||
console.log('First row sample:', rows[0]);
|
||||
|
||||
const mappedRows = rows.map(row => ({
|
||||
...row,
|
||||
avg_lead_time_days: parseFloat(row.avg_lead_time_days || 0),
|
||||
on_time_delivery_rate: parseFloat(row.on_time_delivery_rate || 0),
|
||||
order_fill_rate: parseFloat(row.order_fill_rate || 0),
|
||||
total_purchase_value: parseFloat(row.total_purchase_value || 0),
|
||||
avg_order_value: parseFloat(row.avg_order_value || 0)
|
||||
})));
|
||||
}));
|
||||
console.log('First mapped row sample:', mappedRows[0]);
|
||||
|
||||
res.json(mappedRows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching vendor metrics:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch vendor metrics' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get trending products
|
||||
router.get('/products/trending', async (req, res) => {
|
||||
const pool = req.app.locals.pool;
|
||||
try {
|
||||
// First check if we have any data
|
||||
const [checkData] = await pool.query(`
|
||||
SELECT COUNT(*) as count,
|
||||
MAX(total_revenue) as max_revenue,
|
||||
MAX(daily_sales_avg) as max_daily_sales,
|
||||
COUNT(DISTINCT product_id) as products_with_metrics
|
||||
FROM product_metrics
|
||||
WHERE total_revenue > 0 OR daily_sales_avg > 0
|
||||
`);
|
||||
console.log('Product metrics stats:', checkData[0]);
|
||||
|
||||
if (checkData[0].count === 0) {
|
||||
console.log('No products with metrics found');
|
||||
return res.json([]);
|
||||
}
|
||||
|
||||
// Get trending products
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
p.product_id,
|
||||
p.sku,
|
||||
p.title,
|
||||
COALESCE(pm.daily_sales_avg, 0) as daily_sales_avg,
|
||||
COALESCE(pm.weekly_sales_avg, 0) as weekly_sales_avg,
|
||||
CASE
|
||||
WHEN pm.weekly_sales_avg > 0 AND pm.daily_sales_avg > 0
|
||||
THEN ((pm.daily_sales_avg - pm.weekly_sales_avg) / pm.weekly_sales_avg) * 100
|
||||
ELSE 0
|
||||
END as growth_rate,
|
||||
COALESCE(pm.total_revenue, 0) as total_revenue
|
||||
FROM products p
|
||||
INNER JOIN product_metrics pm ON p.product_id = pm.product_id
|
||||
WHERE (pm.total_revenue > 0 OR pm.daily_sales_avg > 0)
|
||||
AND p.visible = true
|
||||
ORDER BY growth_rate DESC
|
||||
LIMIT 50
|
||||
`);
|
||||
|
||||
console.log('Trending products:', rows);
|
||||
res.json(rows);
|
||||
} catch (error) {
|
||||
console.error('Error fetching trending products:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch trending products' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -25,7 +25,7 @@ export function TrendingProducts() {
|
||||
const { data: products } = useQuery<TrendingProduct[]>({
|
||||
queryKey: ["trending-products"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${config.apiUrl}/dashboard/products/trending`)
|
||||
const response = await fetch(`${config.apiUrl}/products/trending`)
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch trending products")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user