Frontend fixes - dashboard, products, forecasting
This commit is contained in:
@@ -584,7 +584,7 @@ router.get('/best-sellers', async (req, res) => {
|
||||
GROUP BY c.cat_id, c.name
|
||||
)
|
||||
SELECT
|
||||
cat_id as category_id,
|
||||
cat_id,
|
||||
name,
|
||||
units_sold,
|
||||
revenue,
|
||||
@@ -604,28 +604,30 @@ router.get('/best-sellers', async (req, res) => {
|
||||
|
||||
// Format response with explicit type conversion
|
||||
const formattedProducts = products.map(p => ({
|
||||
...p,
|
||||
pid: p.pid,
|
||||
sku: p.sku,
|
||||
title: p.title,
|
||||
units_sold: parseInt(p.units_sold) || 0,
|
||||
revenue: parseFloat(p.revenue) || 0,
|
||||
profit: parseFloat(p.profit) || 0,
|
||||
growth_rate: parseFloat(p.growth_rate) || 0
|
||||
revenue: p.revenue.toString(),
|
||||
profit: p.profit.toString(),
|
||||
growth_rate: p.growth_rate.toString()
|
||||
}));
|
||||
|
||||
const formattedBrands = brands.map(b => ({
|
||||
brand: b.brand,
|
||||
units_sold: parseInt(b.units_sold) || 0,
|
||||
revenue: parseFloat(b.revenue) || 0,
|
||||
profit: parseFloat(b.profit) || 0,
|
||||
growth_rate: parseFloat(b.growth_rate) || 0
|
||||
revenue: b.revenue.toString(),
|
||||
profit: b.profit.toString(),
|
||||
growth_rate: b.growth_rate.toString()
|
||||
}));
|
||||
|
||||
const formattedCategories = categories.map(c => ({
|
||||
category_id: c.cat_id,
|
||||
cat_id: c.cat_id,
|
||||
name: c.name,
|
||||
units_sold: parseInt(c.units_sold) || 0,
|
||||
revenue: parseFloat(c.revenue) || 0,
|
||||
profit: parseFloat(c.profit) || 0,
|
||||
growth_rate: parseFloat(c.growth_rate) || 0
|
||||
revenue: c.revenue.toString(),
|
||||
profit: c.profit.toString(),
|
||||
growth_rate: c.growth_rate.toString()
|
||||
}));
|
||||
|
||||
res.json({
|
||||
|
||||
@@ -172,7 +172,7 @@ router.get('/', async (req, res) => {
|
||||
p.pid,
|
||||
COALESCE(
|
||||
(SELECT overstock_days FROM stock_thresholds st
|
||||
WHERE st.cat_id IN (
|
||||
WHERE st.category_id IN (
|
||||
SELECT pc.cat_id
|
||||
FROM product_categories pc
|
||||
WHERE pc.pid = p.pid
|
||||
@@ -181,7 +181,7 @@ router.get('/', async (req, res) => {
|
||||
ORDER BY st.vendor IS NULL
|
||||
LIMIT 1),
|
||||
(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)
|
||||
ORDER BY st.vendor IS NULL
|
||||
LIMIT 1),
|
||||
@@ -651,11 +651,10 @@ router.get('/:id/time-series', async (req, res) => {
|
||||
const [monthlySales] = await pool.query(`
|
||||
SELECT
|
||||
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,
|
||||
CAST(SUM(price * quantity) AS DECIMAL(15,3)) as revenue,
|
||||
CAST(SUM((price - cost_price) * quantity) AS DECIMAL(15,3)) as profit
|
||||
FROM order_items
|
||||
CAST(SUM(price * quantity) AS DECIMAL(15,3)) as revenue
|
||||
FROM orders
|
||||
WHERE pid = ?
|
||||
AND canceled = false
|
||||
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),
|
||||
units_sold: parseInt(month.units_sold),
|
||||
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
|
||||
const [recentOrders] = await pool.query(`
|
||||
SELECT
|
||||
DATE_FORMAT(date, '%Y-%m-%d') as date,
|
||||
order_id,
|
||||
order_number,
|
||||
quantity,
|
||||
price,
|
||||
discount,
|
||||
tax,
|
||||
shipping
|
||||
FROM order_items
|
||||
shipping,
|
||||
customer_name as customer,
|
||||
status
|
||||
FROM orders
|
||||
WHERE pid = ?
|
||||
AND canceled = false
|
||||
ORDER BY date DESC
|
||||
@@ -711,7 +712,7 @@ router.get('/:id/time-series', async (req, res) => {
|
||||
END as lead_time_days
|
||||
FROM purchase_orders
|
||||
WHERE pid = ?
|
||||
AND status != ${PurchaseOrderStatus.CANCELED}
|
||||
AND status != ${PurchaseOrderStatus.Canceled}
|
||||
ORDER BY date DESC
|
||||
LIMIT 10
|
||||
`, [id]);
|
||||
|
||||
@@ -18,9 +18,10 @@ interface Product {
|
||||
interface Category {
|
||||
cat_id: number;
|
||||
name: string;
|
||||
total_revenue: string;
|
||||
total_profit: string;
|
||||
total_units: number;
|
||||
units_sold: number;
|
||||
revenue: string;
|
||||
profit: string;
|
||||
growth_rate: string;
|
||||
}
|
||||
|
||||
interface BestSellerBrand {
|
||||
@@ -159,9 +160,9 @@ export function BestSellers() {
|
||||
{data?.categories.map((category) => (
|
||||
<TableRow key={category.cat_id}>
|
||||
<TableCell>{category.name}</TableCell>
|
||||
<TableCell className="text-right">{category.total_units}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(Number(category.total_revenue))}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(Number(category.total_profit))}</TableCell>
|
||||
<TableCell className="text-right">{category.units_sold}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(Number(category.revenue))}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(Number(category.profit))}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
||||
@@ -205,8 +205,8 @@ export function ProductDetail({ productId, onClose }: ProductDetailProps) {
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">{product?.title || 'Loading...'}</h2>
|
||||
<p className="text-sm text-muted-foreground">{product?.SKU || ''}</p>
|
||||
<VaulDrawer.Title className="text-xl font-semibold">{product?.title || 'Loading...'}</VaulDrawer.Title>
|
||||
<VaulDrawer.Description className="text-sm text-muted-foreground">{product?.SKU || ''}</VaulDrawer.Description>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="ghost" size="icon" onClick={onClose}>
|
||||
|
||||
@@ -67,12 +67,14 @@ export default function Forecasting() {
|
||||
avgTotalSold: Number(item.avgTotalSold) || 0,
|
||||
products: item.products?.map((p: any) => ({
|
||||
pid: p.pid,
|
||||
name: p.title,
|
||||
title: p.title,
|
||||
sku: p.sku,
|
||||
stock_quantity: Number(p.stock_quantity) || 0,
|
||||
total_sold: Number(p.total_sold) || 0,
|
||||
avg_price: Number(p.avg_price) || 0,
|
||||
first_received_date: p.first_received_date,
|
||||
daily_sales_avg: Number(p.daily_sales_avg) || 0,
|
||||
forecast_units: Number(p.forecast_units) || 0,
|
||||
forecast_revenue: Number(p.forecast_revenue) || 0,
|
||||
confidence_level: Number(p.confidence_level) || 0
|
||||
}))
|
||||
}));
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user