Fix vendor and category page loading issues

This commit is contained in:
2025-01-16 19:53:47 -05:00
parent 87dc6b27f4
commit 3f966ceac3
4 changed files with 58 additions and 34 deletions

View File

@@ -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),

View File

@@ -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),

View File

@@ -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>

View File

@@ -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 && (