Frontend fixes - dashboard, products, forecasting

This commit is contained in:
2025-01-28 14:26:44 -05:00
parent 7e341a152c
commit b1f252bea8
5 changed files with 40 additions and 34 deletions

View File

@@ -584,7 +584,7 @@ router.get('/best-sellers', async (req, res) => {
GROUP BY c.cat_id, c.name GROUP BY c.cat_id, c.name
) )
SELECT SELECT
cat_id as category_id, cat_id,
name, name,
units_sold, units_sold,
revenue, revenue,
@@ -604,28 +604,30 @@ router.get('/best-sellers', async (req, res) => {
// Format response with explicit type conversion // Format response with explicit type conversion
const formattedProducts = products.map(p => ({ const formattedProducts = products.map(p => ({
...p, pid: p.pid,
sku: p.sku,
title: p.title,
units_sold: parseInt(p.units_sold) || 0, units_sold: parseInt(p.units_sold) || 0,
revenue: parseFloat(p.revenue) || 0, revenue: p.revenue.toString(),
profit: parseFloat(p.profit) || 0, profit: p.profit.toString(),
growth_rate: parseFloat(p.growth_rate) || 0 growth_rate: p.growth_rate.toString()
})); }));
const formattedBrands = brands.map(b => ({ const formattedBrands = brands.map(b => ({
brand: b.brand, brand: b.brand,
units_sold: parseInt(b.units_sold) || 0, units_sold: parseInt(b.units_sold) || 0,
revenue: parseFloat(b.revenue) || 0, revenue: b.revenue.toString(),
profit: parseFloat(b.profit) || 0, profit: b.profit.toString(),
growth_rate: parseFloat(b.growth_rate) || 0 growth_rate: b.growth_rate.toString()
})); }));
const formattedCategories = categories.map(c => ({ const formattedCategories = categories.map(c => ({
category_id: c.cat_id, cat_id: c.cat_id,
name: c.name, name: c.name,
units_sold: parseInt(c.units_sold) || 0, units_sold: parseInt(c.units_sold) || 0,
revenue: parseFloat(c.revenue) || 0, revenue: c.revenue.toString(),
profit: parseFloat(c.profit) || 0, profit: c.profit.toString(),
growth_rate: parseFloat(c.growth_rate) || 0 growth_rate: c.growth_rate.toString()
})); }));
res.json({ res.json({

View File

@@ -172,7 +172,7 @@ router.get('/', async (req, res) => {
p.pid, p.pid,
COALESCE( COALESCE(
(SELECT overstock_days FROM stock_thresholds st (SELECT overstock_days FROM stock_thresholds st
WHERE st.cat_id IN ( WHERE st.category_id IN (
SELECT pc.cat_id SELECT pc.cat_id
FROM product_categories pc FROM product_categories pc
WHERE pc.pid = p.pid WHERE pc.pid = p.pid
@@ -181,7 +181,7 @@ router.get('/', async (req, res) => {
ORDER BY st.vendor IS NULL ORDER BY st.vendor IS NULL
LIMIT 1), LIMIT 1),
(SELECT overstock_days FROM stock_thresholds st (SELECT overstock_days FROM stock_thresholds st
WHERE st.cat_id IS NULL WHERE st.category_id IS NULL
AND (st.vendor = p.vendor OR st.vendor IS NULL) AND (st.vendor = p.vendor OR st.vendor IS NULL)
ORDER BY st.vendor IS NULL ORDER BY st.vendor IS NULL
LIMIT 1), LIMIT 1),
@@ -651,11 +651,10 @@ router.get('/:id/time-series', async (req, res) => {
const [monthlySales] = await pool.query(` const [monthlySales] = await pool.query(`
SELECT SELECT
DATE_FORMAT(date, '%Y-%m') as month, DATE_FORMAT(date, '%Y-%m') as month,
COUNT(DISTINCT order_id) as order_count, COUNT(DISTINCT order_number) as order_count,
SUM(quantity) as units_sold, SUM(quantity) as units_sold,
CAST(SUM(price * quantity) AS DECIMAL(15,3)) as revenue, CAST(SUM(price * quantity) AS DECIMAL(15,3)) as revenue
CAST(SUM((price - cost_price) * quantity) AS DECIMAL(15,3)) as profit FROM orders
FROM order_items
WHERE pid = ? WHERE pid = ?
AND canceled = false AND canceled = false
GROUP BY DATE_FORMAT(date, '%Y-%m') GROUP BY DATE_FORMAT(date, '%Y-%m')
@@ -669,20 +668,22 @@ router.get('/:id/time-series', async (req, res) => {
order_count: parseInt(month.order_count), order_count: parseInt(month.order_count),
units_sold: parseInt(month.units_sold), units_sold: parseInt(month.units_sold),
revenue: parseFloat(month.revenue), revenue: parseFloat(month.revenue),
profit: parseFloat(month.profit) profit: 0 // Set to 0 since we don't have cost data in orders table
})); }));
// Get recent orders // Get recent orders
const [recentOrders] = await pool.query(` const [recentOrders] = await pool.query(`
SELECT SELECT
DATE_FORMAT(date, '%Y-%m-%d') as date, DATE_FORMAT(date, '%Y-%m-%d') as date,
order_id, order_number,
quantity, quantity,
price, price,
discount, discount,
tax, tax,
shipping shipping,
FROM order_items customer_name as customer,
status
FROM orders
WHERE pid = ? WHERE pid = ?
AND canceled = false AND canceled = false
ORDER BY date DESC ORDER BY date DESC
@@ -711,7 +712,7 @@ router.get('/:id/time-series', async (req, res) => {
END as lead_time_days END as lead_time_days
FROM purchase_orders FROM purchase_orders
WHERE pid = ? WHERE pid = ?
AND status != ${PurchaseOrderStatus.CANCELED} AND status != ${PurchaseOrderStatus.Canceled}
ORDER BY date DESC ORDER BY date DESC
LIMIT 10 LIMIT 10
`, [id]); `, [id]);

View File

@@ -18,9 +18,10 @@ interface Product {
interface Category { interface Category {
cat_id: number; cat_id: number;
name: string; name: string;
total_revenue: string; units_sold: number;
total_profit: string; revenue: string;
total_units: number; profit: string;
growth_rate: string;
} }
interface BestSellerBrand { interface BestSellerBrand {
@@ -159,9 +160,9 @@ export function BestSellers() {
{data?.categories.map((category) => ( {data?.categories.map((category) => (
<TableRow key={category.cat_id}> <TableRow key={category.cat_id}>
<TableCell>{category.name}</TableCell> <TableCell>{category.name}</TableCell>
<TableCell className="text-right">{category.total_units}</TableCell> <TableCell className="text-right">{category.units_sold}</TableCell>
<TableCell className="text-right">{formatCurrency(Number(category.total_revenue))}</TableCell> <TableCell className="text-right">{formatCurrency(Number(category.revenue))}</TableCell>
<TableCell className="text-right">{formatCurrency(Number(category.total_profit))}</TableCell> <TableCell className="text-right">{formatCurrency(Number(category.profit))}</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>

View File

@@ -205,8 +205,8 @@ export function ProductDetail({ productId, onClose }: ProductDetailProps) {
</div> </div>
)} )}
<div> <div>
<h2 className="text-xl font-semibold">{product?.title || 'Loading...'}</h2> <VaulDrawer.Title className="text-xl font-semibold">{product?.title || 'Loading...'}</VaulDrawer.Title>
<p className="text-sm text-muted-foreground">{product?.SKU || ''}</p> <VaulDrawer.Description className="text-sm text-muted-foreground">{product?.SKU || ''}</VaulDrawer.Description>
</div> </div>
</div> </div>
<Button variant="ghost" size="icon" onClick={onClose}> <Button variant="ghost" size="icon" onClick={onClose}>

View File

@@ -67,12 +67,14 @@ export default function Forecasting() {
avgTotalSold: Number(item.avgTotalSold) || 0, avgTotalSold: Number(item.avgTotalSold) || 0,
products: item.products?.map((p: any) => ({ products: item.products?.map((p: any) => ({
pid: p.pid, pid: p.pid,
name: p.title, title: p.title,
sku: p.sku, sku: p.sku,
stock_quantity: Number(p.stock_quantity) || 0, stock_quantity: Number(p.stock_quantity) || 0,
total_sold: Number(p.total_sold) || 0, total_sold: Number(p.total_sold) || 0,
avg_price: Number(p.avg_price) || 0, daily_sales_avg: Number(p.daily_sales_avg) || 0,
first_received_date: p.first_received_date, forecast_units: Number(p.forecast_units) || 0,
forecast_revenue: Number(p.forecast_revenue) || 0,
confidence_level: Number(p.confidence_level) || 0
})) }))
})); }));
}, },