Get frontend pages loading data again, remove unused components

This commit is contained in:
2025-03-26 21:47:24 -04:00
parent 8e19e6cd74
commit e5c4f617c5
16 changed files with 2170 additions and 1314 deletions

View File

@@ -97,6 +97,28 @@ router.get('/', async (req, res) => {
const pages = Math.ceil(total / limit);
// Get recent purchase orders
let orderByClause;
if (sortColumn === 'order_date') {
orderByClause = `date ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'vendor_name') {
orderByClause = `vendor ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'total_cost') {
orderByClause = `total_cost ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'total_received') {
orderByClause = `total_received ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'total_items') {
orderByClause = `total_items ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'total_quantity') {
orderByClause = `total_quantity ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'fulfillment_rate') {
orderByClause = `fulfillment_rate ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else if (sortColumn === 'status') {
orderByClause = `status ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
} else {
orderByClause = `date ${sortDirection === 'desc' ? 'DESC' : 'ASC'}`;
}
const { rows: orders } = await pool.query(`
WITH po_totals AS (
SELECT
@@ -128,20 +150,9 @@ router.get('/', async (req, res) => {
total_received,
fulfillment_rate
FROM po_totals
ORDER BY
CASE
WHEN $${paramCounter} = 'order_date' THEN date
WHEN $${paramCounter} = 'vendor_name' THEN vendor
WHEN $${paramCounter} = 'total_cost' THEN total_cost
WHEN $${paramCounter} = 'total_received' THEN total_received
WHEN $${paramCounter} = 'total_items' THEN total_items
WHEN $${paramCounter} = 'total_quantity' THEN total_quantity
WHEN $${paramCounter} = 'fulfillment_rate' THEN fulfillment_rate
WHEN $${paramCounter} = 'status' THEN status
ELSE date
END ${sortDirection === 'desc' ? 'DESC' : 'ASC'}
LIMIT $${paramCounter + 1} OFFSET $${paramCounter + 2}
`, [...params, sortColumn, Number(limit), offset]);
ORDER BY ${orderByClause}
LIMIT $${paramCounter} OFFSET $${paramCounter + 1}
`, [...params, Number(limit), offset]);
// Get unique vendors for filter options
const { rows: vendors } = await pool.query(`
@@ -272,7 +283,7 @@ router.get('/cost-analysis', async (req, res) => {
try {
const pool = req.app.locals.pool;
const [analysis] = await pool.query(`
const { rows: analysis } = await pool.query(`
WITH category_costs AS (
SELECT
c.name as category,
@@ -290,11 +301,11 @@ router.get('/cost-analysis', async (req, res) => {
SELECT
category,
COUNT(DISTINCT pid) as unique_products,
CAST(AVG(cost_price) AS DECIMAL(15,3)) as avg_cost,
CAST(MIN(cost_price) AS DECIMAL(15,3)) as min_cost,
CAST(MAX(cost_price) AS DECIMAL(15,3)) as max_cost,
CAST(STDDEV(cost_price) AS DECIMAL(15,3)) as cost_variance,
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_spend
ROUND(AVG(cost_price)::numeric, 3) as avg_cost,
ROUND(MIN(cost_price)::numeric, 3) as min_cost,
ROUND(MAX(cost_price)::numeric, 3) as max_cost,
ROUND(STDDEV(cost_price)::numeric, 3) as cost_variance,
ROUND(SUM(ordered * cost_price)::numeric, 3) as total_spend
FROM category_costs
GROUP BY category
ORDER BY total_spend DESC
@@ -302,17 +313,37 @@ router.get('/cost-analysis', async (req, res) => {
// Parse numeric values
const parsedAnalysis = {
categories: analysis.map(cat => ({
unique_products: 0,
avg_cost: 0,
min_cost: 0,
max_cost: 0,
cost_variance: 0,
total_spend_by_category: analysis.map(cat => ({
category: cat.category,
unique_products: Number(cat.unique_products) || 0,
avg_cost: Number(cat.avg_cost) || 0,
min_cost: Number(cat.min_cost) || 0,
max_cost: Number(cat.max_cost) || 0,
cost_variance: Number(cat.cost_variance) || 0,
total_spend: Number(cat.total_spend) || 0
}))
};
// Calculate aggregated stats if data exists
if (analysis.length > 0) {
parsedAnalysis.unique_products = analysis.reduce((sum, cat) => sum + Number(cat.unique_products || 0), 0);
// Calculate weighted average cost
const totalProducts = parsedAnalysis.unique_products;
if (totalProducts > 0) {
parsedAnalysis.avg_cost = analysis.reduce((sum, cat) =>
sum + (Number(cat.avg_cost || 0) * Number(cat.unique_products || 0)), 0) / totalProducts;
}
// Find min and max across all categories
parsedAnalysis.min_cost = Math.min(...analysis.map(cat => Number(cat.min_cost || 0)));
parsedAnalysis.max_cost = Math.max(...analysis.map(cat => Number(cat.max_cost || 0)));
// Average variance
parsedAnalysis.cost_variance = analysis.reduce((sum, cat) =>
sum + Number(cat.cost_variance || 0), 0) / analysis.length;
}
res.json(parsedAnalysis);
} catch (error) {
console.error('Error fetching cost analysis:', error);
@@ -325,7 +356,7 @@ router.get('/receiving-status', async (req, res) => {
try {
const pool = req.app.locals.pool;
const [status] = await pool.query(`
const { rows: status } = await pool.query(`
WITH po_totals AS (
SELECT
po_id,
@@ -333,7 +364,7 @@ router.get('/receiving-status', async (req, res) => {
receiving_status,
SUM(ordered) as total_ordered,
SUM(received) as total_received,
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_cost
ROUND(SUM(ordered * cost_price)::numeric, 3) as total_cost
FROM purchase_orders
WHERE status != ${STATUS.CANCELED}
GROUP BY po_id, status, receiving_status
@@ -345,8 +376,8 @@ router.get('/receiving-status', async (req, res) => {
ROUND(
SUM(total_received) / NULLIF(SUM(total_ordered), 0), 3
) as fulfillment_rate,
CAST(SUM(total_cost) AS DECIMAL(15,3)) as total_value,
CAST(AVG(total_cost) AS DECIMAL(15,3)) as avg_cost,
ROUND(SUM(total_cost)::numeric, 3) as total_value,
ROUND(AVG(total_cost)::numeric, 3) as avg_cost,
COUNT(DISTINCT CASE
WHEN receiving_status = ${RECEIVING_STATUS.CREATED} THEN po_id
END) as pending_count,
@@ -364,17 +395,17 @@ router.get('/receiving-status', async (req, res) => {
// Parse numeric values
const parsedStatus = {
order_count: Number(status[0].order_count) || 0,
total_ordered: Number(status[0].total_ordered) || 0,
total_received: Number(status[0].total_received) || 0,
fulfillment_rate: Number(status[0].fulfillment_rate) || 0,
total_value: Number(status[0].total_value) || 0,
avg_cost: Number(status[0].avg_cost) || 0,
order_count: Number(status[0]?.order_count) || 0,
total_ordered: Number(status[0]?.total_ordered) || 0,
total_received: Number(status[0]?.total_received) || 0,
fulfillment_rate: Number(status[0]?.fulfillment_rate) || 0,
total_value: Number(status[0]?.total_value) || 0,
avg_cost: Number(status[0]?.avg_cost) || 0,
status_breakdown: {
pending: Number(status[0].pending_count) || 0,
partial: Number(status[0].partial_count) || 0,
completed: Number(status[0].completed_count) || 0,
canceled: Number(status[0].canceled_count) || 0
pending: Number(status[0]?.pending_count) || 0,
partial: Number(status[0]?.partial_count) || 0,
completed: Number(status[0]?.completed_count) || 0,
canceled: Number(status[0]?.canceled_count) || 0
}
};
@@ -390,7 +421,7 @@ router.get('/order-vs-received', async (req, res) => {
try {
const pool = req.app.locals.pool;
const [quantities] = await pool.query(`
const { rows: quantities } = await pool.query(`
SELECT
p.product_id,
p.title as product,
@@ -403,10 +434,10 @@ router.get('/order-vs-received', async (req, res) => {
COUNT(DISTINCT po.po_id) as order_count
FROM products p
JOIN purchase_orders po ON p.product_id = po.product_id
WHERE po.date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
WHERE po.date >= (CURRENT_DATE - INTERVAL '90 days')
GROUP BY p.product_id, p.title, p.SKU
HAVING order_count > 0
ORDER BY ordered_quantity DESC
HAVING COUNT(DISTINCT po.po_id) > 0
ORDER BY SUM(po.ordered) DESC
LIMIT 20
`);