100 lines
3.8 KiB
TypeScript
100 lines
3.8 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { getDb } from "@/lib/db";
|
|
|
|
export async function GET() {
|
|
try {
|
|
const sql = getDb();
|
|
|
|
const [componentStats, productStats, lowStockItems, categoryBreakdown, recentOrders, orderStats] =
|
|
await Promise.all([
|
|
sql(`
|
|
SELECT
|
|
COUNT(*) as total_components,
|
|
COALESCE(SUM(stock_quantity * unit_price), 0) as total_inventory_value,
|
|
COUNT(CASE WHEN stock_quantity <= min_stock_level THEN 1 END) as low_stock_count
|
|
FROM components
|
|
`),
|
|
sql(`
|
|
SELECT
|
|
COUNT(*) as total_products,
|
|
COUNT(CASE WHEN status = 'active' THEN 1 END) as active_products,
|
|
COUNT(CASE WHEN status = 'draft' THEN 1 END) as draft_products
|
|
FROM products
|
|
`),
|
|
sql(`
|
|
SELECT c.*, cat.name as category_name
|
|
FROM components c
|
|
LEFT JOIN categories cat ON c.category_id = cat.id
|
|
WHERE c.stock_quantity <= c.min_stock_level
|
|
ORDER BY (c.stock_quantity::float / GREATEST(c.min_stock_level, 1)) ASC
|
|
LIMIT 20
|
|
`),
|
|
sql(`
|
|
SELECT cat.name, COUNT(c.id) as count, COALESCE(SUM(c.stock_quantity), 0) as total_stock
|
|
FROM categories cat
|
|
LEFT JOIN components c ON cat.id = c.category_id
|
|
GROUP BY cat.id, cat.name
|
|
ORDER BY count DESC
|
|
`),
|
|
sql(`
|
|
SELECT po.id, po.po_number, po.status, po.order_date, po.due_date,
|
|
po.customer_id,
|
|
c.name AS customer_name, c.code AS customer_code,
|
|
COALESCE(SUM(poi.quantity), 0) AS total_ordered,
|
|
COALESCE(SUM(poi.shipped_quantity), 0) AS total_shipped,
|
|
COALESCE(SUM(poi.quantity), 0) - COALESCE(SUM(poi.shipped_quantity), 0) AS total_remaining,
|
|
COALESCE(SUM(poi.quantity * poi.unit_price), 0) AS total_value,
|
|
COUNT(poi.id) AS item_count
|
|
FROM purchase_orders po
|
|
JOIN customers c ON c.id = po.customer_id
|
|
LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id
|
|
WHERE po.status NOT IN ('completed', 'cancelled')
|
|
GROUP BY po.id, po.po_number, po.status, po.order_date, po.due_date, po.customer_id, c.name, c.code
|
|
ORDER BY
|
|
CASE po.status
|
|
WHEN 'in_production' THEN 1
|
|
WHEN 'confirmed' THEN 2
|
|
WHEN 'shipped' THEN 3
|
|
WHEN 'draft' THEN 4
|
|
END,
|
|
po.due_date ASC NULLS LAST,
|
|
po.order_date DESC
|
|
LIMIT 15
|
|
`),
|
|
sql(`
|
|
SELECT
|
|
COUNT(*) AS total_orders,
|
|
COUNT(CASE WHEN status NOT IN ('completed', 'cancelled') THEN 1 END) AS active_orders,
|
|
COUNT(CASE WHEN status = 'in_production' THEN 1 END) AS in_production,
|
|
COUNT(CASE WHEN status = 'confirmed' THEN 1 END) AS confirmed,
|
|
COUNT(CASE WHEN status = 'shipped' THEN 1 END) AS shipped
|
|
FROM purchase_orders
|
|
`),
|
|
]);
|
|
|
|
return NextResponse.json({
|
|
stats: {
|
|
total_components: Number(componentStats[0].total_components),
|
|
total_inventory_value: Number(componentStats[0].total_inventory_value),
|
|
low_stock_count: Number(componentStats[0].low_stock_count),
|
|
total_products: Number(productStats[0].total_products),
|
|
active_products: Number(productStats[0].active_products),
|
|
draft_products: Number(productStats[0].draft_products),
|
|
active_orders: Number(orderStats[0].active_orders),
|
|
in_production_orders: Number(orderStats[0].in_production),
|
|
confirmed_orders: Number(orderStats[0].confirmed),
|
|
shipped_orders: Number(orderStats[0].shipped),
|
|
},
|
|
low_stock_items: lowStockItems,
|
|
category_breakdown: categoryBreakdown,
|
|
recent_orders: recentOrders,
|
|
});
|
|
} catch (error) {
|
|
console.error("[v0] Dashboard API error:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to load dashboard data" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|