Fix vendor and category page loading issues
This commit is contained in:
@@ -94,17 +94,29 @@ router.get('/', async (req, res) => {
|
||||
SELECT
|
||||
COUNT(DISTINCT c.id) as totalCategories,
|
||||
COUNT(DISTINCT CASE WHEN cm.status = 'active' THEN c.id END) as activeCategories,
|
||||
SUM(cm.total_value) as totalValue,
|
||||
AVG(cm.avg_margin) as avgMargin,
|
||||
AVG(cm.growth_rate) as avgGrowth
|
||||
COALESCE(SUM(cm.total_value), 0) as totalValue,
|
||||
COALESCE(ROUND(AVG(NULLIF(cm.avg_margin, 0)), 1), 0) as avgMargin,
|
||||
COALESCE(ROUND(AVG(NULLIF(cm.growth_rate, 0)), 1), 0) as avgGrowth
|
||||
FROM categories c
|
||||
LEFT JOIN category_metrics cm ON c.id = cm.category_id
|
||||
`);
|
||||
|
||||
res.json({
|
||||
categories,
|
||||
categories: categories.map(cat => ({
|
||||
...cat,
|
||||
product_count: parseInt(cat.product_count || 0),
|
||||
total_value: parseFloat(cat.total_value || 0),
|
||||
avg_margin: parseFloat(cat.avg_margin || 0),
|
||||
turnover_rate: parseFloat(cat.turnover_rate || 0),
|
||||
growth_rate: parseFloat(cat.growth_rate || 0)
|
||||
})),
|
||||
parentCategories: parentCategories.map(p => p.parent_category),
|
||||
stats: stats[0],
|
||||
stats: {
|
||||
...stats[0],
|
||||
totalValue: parseFloat(stats[0].totalValue || 0),
|
||||
avgMargin: parseFloat(stats[0].avgMargin || 0),
|
||||
avgGrowth: parseFloat(stats[0].avgGrowth || 0)
|
||||
},
|
||||
pagination: {
|
||||
total: countResult[0].total,
|
||||
pages: Math.ceil(countResult[0].total / limit),
|
||||
|
||||
@@ -84,16 +84,28 @@ router.get('/', async (req, res) => {
|
||||
SELECT
|
||||
COUNT(DISTINCT p.vendor) as totalVendors,
|
||||
COUNT(DISTINCT CASE WHEN vm.status = 'active' THEN p.vendor END) as activeVendors,
|
||||
AVG(vm.avg_lead_time_days) as avgLeadTime,
|
||||
AVG(vm.order_fill_rate) as avgFillRate,
|
||||
AVG(vm.on_time_delivery_rate) as avgOnTimeDelivery
|
||||
COALESCE(ROUND(AVG(NULLIF(vm.avg_lead_time_days, 0)), 1), 0) as avgLeadTime,
|
||||
COALESCE(ROUND(AVG(NULLIF(vm.order_fill_rate, 0)), 1), 0) as avgFillRate,
|
||||
COALESCE(ROUND(AVG(NULLIF(vm.on_time_delivery_rate, 0)), 1), 0) as avgOnTimeDelivery
|
||||
FROM products p
|
||||
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
|
||||
`);
|
||||
|
||||
res.json({
|
||||
vendors,
|
||||
stats: stats[0],
|
||||
vendors: vendors.map(vendor => ({
|
||||
...vendor,
|
||||
avg_lead_time_days: parseFloat(vendor.avg_lead_time_days || 0),
|
||||
on_time_delivery_rate: parseFloat(vendor.on_time_delivery_rate || 0),
|
||||
order_fill_rate: parseFloat(vendor.order_fill_rate || 0),
|
||||
total_orders: parseInt(vendor.total_orders || 0),
|
||||
active_products: parseInt(vendor.active_products || 0)
|
||||
})),
|
||||
stats: {
|
||||
...stats[0],
|
||||
avgLeadTime: parseFloat(stats[0].avgLeadTime || 0),
|
||||
avgFillRate: parseFloat(stats[0].avgFillRate || 0),
|
||||
avgOnTimeDelivery: parseFloat(stats[0].avgOnTimeDelivery || 0)
|
||||
},
|
||||
pagination: {
|
||||
total: countResult[0].total,
|
||||
pages: Math.ceil(countResult[0].total / limit),
|
||||
|
||||
@@ -97,9 +97,9 @@ export function Categories() {
|
||||
<CardTitle className="text-sm font-medium">Total Categories</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.totalCategories ?? "..."}</div>
|
||||
<div className="text-2xl font-bold">{data?.stats?.totalCategories ?? "..."}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{data?.stats.activeCategories ?? "..."} active
|
||||
{data?.stats?.activeCategories ?? "..."} active
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -109,7 +109,7 @@ export function Categories() {
|
||||
<CardTitle className="text-sm font-medium">Total Value</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.totalValue ? formatCurrency(data.stats.totalValue) : "..."}</div>
|
||||
<div className="text-2xl font-bold">{data?.stats?.totalValue ? formatCurrency(data.stats.totalValue) : "..."}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Inventory value
|
||||
</p>
|
||||
@@ -121,7 +121,7 @@ export function Categories() {
|
||||
<CardTitle className="text-sm font-medium">Avg Margin</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.avgMargin?.toFixed(1) ?? "..."}%</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgMargin === 'number' ? data.stats.avgMargin.toFixed(1) : "..."}%</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Across all categories
|
||||
</p>
|
||||
@@ -133,7 +133,7 @@ export function Categories() {
|
||||
<CardTitle className="text-sm font-medium">Avg Growth</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.avgGrowth?.toFixed(1) ?? "..."}%</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgGrowth === 'number' ? data.stats.avgGrowth.toFixed(1) : "..."}%</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Year over year
|
||||
</p>
|
||||
@@ -203,21 +203,21 @@ export function Categories() {
|
||||
Loading categories...
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : data?.categories.map((category: Category) => (
|
||||
) : data?.categories?.map((category: Category) => (
|
||||
<TableRow key={category.category_id}>
|
||||
<TableCell>
|
||||
<div className="font-medium">{category.name}</div>
|
||||
<div className="text-sm text-muted-foreground">{category.description}</div>
|
||||
</TableCell>
|
||||
<TableCell>{category.parent_category || "—"}</TableCell>
|
||||
<TableCell>{category.product_count.toLocaleString()}</TableCell>
|
||||
<TableCell>{formatCurrency(category.total_value)}</TableCell>
|
||||
<TableCell>{category.avg_margin.toFixed(1)}%</TableCell>
|
||||
<TableCell>{category.turnover_rate.toFixed(1)}x</TableCell>
|
||||
<TableCell>{category.product_count?.toLocaleString() ?? 0}</TableCell>
|
||||
<TableCell>{formatCurrency(category.total_value ?? 0)}</TableCell>
|
||||
<TableCell>{typeof category.avg_margin === 'number' ? category.avg_margin.toFixed(1) : "0.0"}%</TableCell>
|
||||
<TableCell>{typeof category.turnover_rate === 'number' ? category.turnover_rate.toFixed(1) : "0.0"}x</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
{category.growth_rate.toFixed(1)}%
|
||||
{getPerformanceBadge(category.growth_rate)}
|
||||
{typeof category.growth_rate === 'number' ? category.growth_rate.toFixed(1) : "0.0"}%
|
||||
{getPerformanceBadge(category.growth_rate ?? 0)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{category.status}</TableCell>
|
||||
|
||||
@@ -89,9 +89,9 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Total Vendors</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.totalVendors ?? "..."}</div>
|
||||
<div className="text-2xl font-bold">{data?.stats?.totalVendors ?? "..."}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{data?.stats.activeVendors ?? "..."} active
|
||||
{data?.stats?.activeVendors ?? "..."} active
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -101,7 +101,7 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Avg Lead Time</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.avgLeadTime?.toFixed(1) ?? "..."} days</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgLeadTime === 'number' ? data.stats.avgLeadTime.toFixed(1) : "..."} days</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Across all vendors
|
||||
</p>
|
||||
@@ -113,7 +113,7 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Fill Rate</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.avgFillRate?.toFixed(1) ?? "..."}%</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgFillRate === 'number' ? data.stats.avgFillRate.toFixed(1) : "..."}%</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Average order fill rate
|
||||
</p>
|
||||
@@ -125,7 +125,7 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">On-Time Delivery</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{data?.stats.avgOnTimeDelivery?.toFixed(1) ?? "..."}%</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgOnTimeDelivery === 'number' ? data.stats.avgOnTimeDelivery.toFixed(1) : "..."}%</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Average on-time rate
|
||||
</p>
|
||||
@@ -194,7 +194,7 @@ export function Vendors() {
|
||||
Loading vendors...
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : data?.vendors.map((vendor: Vendor) => (
|
||||
) : data?.vendors?.map((vendor: Vendor) => (
|
||||
<TableRow key={vendor.vendor_id}>
|
||||
<TableCell className="font-medium">{vendor.name}</TableCell>
|
||||
<TableCell>
|
||||
@@ -202,16 +202,16 @@ export function Vendors() {
|
||||
<div className="text-sm text-muted-foreground">{vendor.email}</div>
|
||||
</TableCell>
|
||||
<TableCell>{vendor.status}</TableCell>
|
||||
<TableCell>{vendor.avg_lead_time_days.toFixed(1)} days</TableCell>
|
||||
<TableCell>{vendor.on_time_delivery_rate.toFixed(1)}%</TableCell>
|
||||
<TableCell>{typeof vendor.avg_lead_time_days === 'number' ? vendor.avg_lead_time_days.toFixed(1) : "0.0"} days</TableCell>
|
||||
<TableCell>{typeof vendor.on_time_delivery_rate === 'number' ? vendor.on_time_delivery_rate.toFixed(1) : "0.0"}%</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
{vendor.order_fill_rate.toFixed(1)}%
|
||||
{getPerformanceBadge(vendor.order_fill_rate)}
|
||||
{typeof vendor.order_fill_rate === 'number' ? vendor.order_fill_rate.toFixed(1) : "0.0"}%
|
||||
{getPerformanceBadge(vendor.order_fill_rate ?? 0)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{vendor.total_orders.toLocaleString()}</TableCell>
|
||||
<TableCell>{vendor.active_products.toLocaleString()}</TableCell>
|
||||
<TableCell>{vendor.total_orders?.toLocaleString() ?? 0}</TableCell>
|
||||
<TableCell>{vendor.active_products?.toLocaleString() ?? 0}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{!isLoading && !data?.vendors.length && (
|
||||
|
||||
Reference in New Issue
Block a user