Fix and restyle overstockmetrics and topoverstockedproducts, link products to backend
This commit is contained in:
@@ -357,7 +357,7 @@ router.get('/overstock/metrics', async (req, res) => {
|
|||||||
SUM(total_excess_units) as total_excess_units,
|
SUM(total_excess_units) as total_excess_units,
|
||||||
SUM(total_excess_cost) as total_excess_cost,
|
SUM(total_excess_cost) as total_excess_cost,
|
||||||
SUM(total_excess_retail) as total_excess_retail,
|
SUM(total_excess_retail) as total_excess_retail,
|
||||||
CAST(JSON_ARRAYAGG(
|
CONCAT('[', GROUP_CONCAT(
|
||||||
JSON_OBJECT(
|
JSON_OBJECT(
|
||||||
'category', category_name,
|
'category', category_name,
|
||||||
'products', overstocked_products,
|
'products', overstocked_products,
|
||||||
@@ -365,7 +365,7 @@ router.get('/overstock/metrics', async (req, res) => {
|
|||||||
'cost', total_excess_cost,
|
'cost', total_excess_cost,
|
||||||
'retail', total_excess_retail
|
'retail', total_excess_retail
|
||||||
)
|
)
|
||||||
) AS JSON) as category_data
|
), ']') as category_data
|
||||||
FROM (
|
FROM (
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM category_overstock
|
FROM category_overstock
|
||||||
@@ -378,10 +378,17 @@ router.get('/overstock/metrics', async (req, res) => {
|
|||||||
// Format response with explicit type conversion
|
// Format response with explicit type conversion
|
||||||
const response = {
|
const response = {
|
||||||
overstockedProducts: parseInt(rows[0].total_overstocked) || 0,
|
overstockedProducts: parseInt(rows[0].total_overstocked) || 0,
|
||||||
excessUnits: parseInt(rows[0].total_excess_units) || 0,
|
total_excess_units: parseInt(rows[0].total_excess_units) || 0,
|
||||||
excessCost: parseFloat(rows[0].total_excess_cost) || 0,
|
total_excess_cost: parseFloat(rows[0].total_excess_cost) || 0,
|
||||||
excessRetail: parseFloat(rows[0].total_excess_retail) || 0,
|
total_excess_retail: parseFloat(rows[0].total_excess_retail) || 0,
|
||||||
categoryData: rows[0].category_data ? JSON.parse(rows[0].category_data) : []
|
category_data: rows[0].category_data ?
|
||||||
|
JSON.parse(rows[0].category_data).map(obj => ({
|
||||||
|
category: obj.category,
|
||||||
|
products: parseInt(obj.products) || 0,
|
||||||
|
units: parseInt(obj.units) || 0,
|
||||||
|
cost: parseFloat(obj.cost) || 0,
|
||||||
|
retail: parseFloat(obj.retail) || 0
|
||||||
|
})) : []
|
||||||
};
|
};
|
||||||
|
|
||||||
res.json(response);
|
res.json(response);
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import { Package, Layers, DollarSign, ShoppingCart } from "lucide-react"
|
|||||||
|
|
||||||
interface OverstockMetricsData {
|
interface OverstockMetricsData {
|
||||||
overstockedProducts: number
|
overstockedProducts: number
|
||||||
overstockedUnits: number
|
total_excess_units: number
|
||||||
overstockedCost: number
|
total_excess_cost: number
|
||||||
overstockedRetail: number
|
total_excess_retail: number
|
||||||
overstockByCategory: {
|
category_data: {
|
||||||
category: string
|
category: string
|
||||||
products: number
|
products: number
|
||||||
units: number
|
units: number
|
||||||
cost: number
|
cost: number
|
||||||
|
retail: number
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,28 +42,28 @@ export function OverstockMetrics() {
|
|||||||
<Package className="h-4 w-4 text-muted-foreground" />
|
<Package className="h-4 w-4 text-muted-foreground" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Overstocked Products</p>
|
<p className="text-sm font-medium text-muted-foreground">Overstocked Products</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold">{data?.overstockedProducts.toLocaleString() || 0}</p>
|
<p className="text-lg font-bold">{data?.overstockedProducts.toLocaleString() || 0}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline justify-between">
|
<div className="flex items-baseline justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Layers className="h-4 w-4 text-muted-foreground" />
|
<Layers className="h-4 w-4 text-muted-foreground" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Overstocked Units</p>
|
<p className="text-sm font-medium text-muted-foreground">Overstocked Units</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold">{data?.overstockedUnits.toLocaleString() || 0}</p>
|
<p className="text-lg font-bold">{data?.total_excess_units.toLocaleString() || 0}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline justify-between">
|
<div className="flex items-baseline justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Overstocked Cost</p>
|
<p className="text-sm font-medium text-muted-foreground">Overstocked Cost</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold">{formatCurrency(data?.overstockedCost || 0)}</p>
|
<p className="text-lg font-bold">{formatCurrency(data?.total_excess_cost || 0)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline justify-between">
|
<div className="flex items-baseline justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ShoppingCart className="h-4 w-4 text-muted-foreground" />
|
<ShoppingCart className="h-4 w-4 text-muted-foreground" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Overstocked Retail</p>
|
<p className="text-sm font-medium text-muted-foreground">Overstocked Retail</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold">{formatCurrency(data?.overstockedRetail || 0)}</p>
|
<p className="text-lg font-bold">{formatCurrency(data?.total_excess_retail || 0)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ interface OverstockedProduct {
|
|||||||
product_id: number
|
product_id: number
|
||||||
SKU: string
|
SKU: string
|
||||||
title: string
|
title: string
|
||||||
|
stock_quantity: number
|
||||||
overstocked_amt: number
|
overstocked_amt: number
|
||||||
excess_cost: number
|
excess_cost: number
|
||||||
excess_retail: number
|
excess_retail: number
|
||||||
days_of_inventory: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TopOverstockedProducts() {
|
export function TopOverstockedProducts() {
|
||||||
@@ -38,9 +38,10 @@ export function TopOverstockedProducts() {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Product</TableHead>
|
<TableHead>Product</TableHead>
|
||||||
<TableHead className="text-right">Units</TableHead>
|
<TableHead className="text-right">Current Stock</TableHead>
|
||||||
<TableHead className="text-right">Cost</TableHead>
|
<TableHead className="text-right">Overstock Amt</TableHead>
|
||||||
<TableHead className="text-right">Days</TableHead>
|
<TableHead className="text-right">Overstock Cost</TableHead>
|
||||||
|
<TableHead className="text-right">Overstock Retail</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -48,10 +49,20 @@ export function TopOverstockedProducts() {
|
|||||||
<TableRow key={product.product_id}>
|
<TableRow key={product.product_id}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">{product.title}</p>
|
<a
|
||||||
|
href={`https://backend.acherryontop.com/product/${product.product_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="font-medium hover:underline"
|
||||||
|
>
|
||||||
|
{product.title}
|
||||||
|
</a>
|
||||||
<p className="text-sm text-muted-foreground">{product.SKU}</p>
|
<p className="text-sm text-muted-foreground">{product.SKU}</p>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className="text-right">
|
||||||
|
{product.stock_quantity.toLocaleString()}
|
||||||
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
{product.overstocked_amt.toLocaleString()}
|
{product.overstocked_amt.toLocaleString()}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -59,7 +70,7 @@ export function TopOverstockedProducts() {
|
|||||||
{formatCurrency(product.excess_cost)}
|
{formatCurrency(product.excess_cost)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
{product.days_of_inventory}
|
{formatCurrency(product.excess_retail)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -49,7 +49,14 @@ export function TopReplenishProducts() {
|
|||||||
<TableRow key={product.product_id}>
|
<TableRow key={product.product_id}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">{product.title}</p>
|
<a
|
||||||
|
href={`https://backend.acherryontop.com/product/${product.product_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="font-medium hover:underline"
|
||||||
|
>
|
||||||
|
{product.title}
|
||||||
|
</a>
|
||||||
<p className="text-sm text-muted-foreground">{product.SKU}</p>
|
<p className="text-sm text-muted-foreground">{product.SKU}</p>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
Reference in New Issue
Block a user