From c9aefdc756d45feffb0a72ec389c7289c239a4b6 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 16 Jan 2025 14:21:33 -0500 Subject: [PATCH] Fixes for reset and calculate scripts --- inventory-server/scripts/calculate-metrics.js | 412 ++++++++---------- inventory-server/scripts/reset-metrics.js | 27 +- inventory-server/src/routes/vendors.js | 9 +- inventory/tsconfig.tsbuildinfo | 2 +- 4 files changed, 195 insertions(+), 255 deletions(-) diff --git a/inventory-server/scripts/calculate-metrics.js b/inventory-server/scripts/calculate-metrics.js index e7c7011..13ac227 100644 --- a/inventory-server/scripts/calculate-metrics.js +++ b/inventory-server/scripts/calculate-metrics.js @@ -256,53 +256,195 @@ async function calculateVendorMetrics(connection, startTime, totalProducts) { percentage: '70' }); + // Calculate vendor performance metrics await connection.query(` INSERT INTO vendor_metrics ( vendor, - last_calculated_at, avg_lead_time_days, on_time_delivery_rate, order_fill_rate, total_orders, total_late_orders, total_purchase_value, - avg_order_value + avg_order_value, + active_products, + total_products, + total_revenue, + avg_margin_percent, + status ) SELECT - po.vendor, - NOW() as last_calculated_at, - AVG( - CASE - WHEN po.received_date IS NOT NULL - THEN DATEDIFF(po.received_date, po.date) - ELSE NULL - END - ) as avg_lead_time_days, - ( - COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) * 100.0 / - NULLIF(COUNT(*), 0) - ) as on_time_delivery_rate, - ( - SUM(CASE WHEN po.received >= po.ordered THEN 1 ELSE 0 END) * 100.0 / - NULLIF(COUNT(*), 0) - ) as order_fill_rate, - COUNT(*) as total_orders, - COUNT(CASE WHEN po.received_date > po.expected_date THEN 1 END) as total_late_orders, - SUM(po.ordered * po.cost_price) as total_purchase_value, - AVG(po.ordered * po.cost_price) as avg_order_value - FROM purchase_orders po - WHERE po.status = 'closed' - AND po.date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) - GROUP BY po.vendor + vd.vendor, + -- Lead time metrics + AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days, + -- On-time delivery rate + (COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) / COUNT(*)) * 100 as on_time_delivery_rate, + -- Order fill rate + (SUM(po.received) / SUM(po.ordered)) * 100 as order_fill_rate, + COUNT(DISTINCT po.po_id) as total_orders, + COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as total_late_orders, + SUM(po.cost_price * po.ordered) as total_purchase_value, + AVG(po.cost_price * po.ordered) as avg_order_value, + -- Product counts + COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, + COUNT(DISTINCT p.product_id) as total_products, + -- Financial metrics + SUM(o.price * o.quantity) as total_revenue, + AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent, + vd.status + FROM vendor_details vd + LEFT JOIN products p ON vd.vendor = p.vendor + LEFT JOIN purchase_orders po ON p.product_id = po.product_id + LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false + GROUP BY vd.vendor, vd.status ON DUPLICATE KEY UPDATE - last_calculated_at = VALUES(last_calculated_at), avg_lead_time_days = VALUES(avg_lead_time_days), on_time_delivery_rate = VALUES(on_time_delivery_rate), order_fill_rate = VALUES(order_fill_rate), total_orders = VALUES(total_orders), total_late_orders = VALUES(total_late_orders), total_purchase_value = VALUES(total_purchase_value), - avg_order_value = VALUES(avg_order_value) + avg_order_value = VALUES(avg_order_value), + active_products = VALUES(active_products), + total_products = VALUES(total_products), + total_revenue = VALUES(total_revenue), + avg_margin_percent = VALUES(avg_margin_percent), + status = VALUES(status), + last_calculated_at = CURRENT_TIMESTAMP + `); + + // Calculate vendor time-based metrics + await connection.query(` + INSERT INTO vendor_time_metrics ( + vendor, + year, + month, + total_orders, + late_orders, + avg_lead_time_days, + total_purchase_value, + total_revenue, + avg_margin_percent + ) + SELECT + vd.vendor, + YEAR(po.date) as year, + MONTH(po.date) as month, + COUNT(DISTINCT po.po_id) as total_orders, + COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as late_orders, + AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days, + SUM(po.cost_price * po.ordered) as total_purchase_value, + SUM(o.price * o.quantity) as total_revenue, + AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent + FROM vendor_details vd + LEFT JOIN products p ON vd.vendor = p.vendor + LEFT JOIN purchase_orders po ON p.product_id = po.product_id + LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false + WHERE po.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) + GROUP BY vd.vendor, YEAR(po.date), MONTH(po.date) + ON DUPLICATE KEY UPDATE + total_orders = VALUES(total_orders), + late_orders = VALUES(late_orders), + avg_lead_time_days = VALUES(avg_lead_time_days), + total_purchase_value = VALUES(total_purchase_value), + total_revenue = VALUES(total_revenue), + avg_margin_percent = VALUES(avg_margin_percent) + `); +} + +async function calculateCategoryMetrics(connection, startTime, totalProducts) { + outputProgress({ + status: 'running', + operation: 'Calculating category metrics', + current: Math.floor(totalProducts * 0.85), + total: totalProducts, + elapsed: formatElapsedTime(startTime), + remaining: estimateRemaining(startTime, Math.floor(totalProducts * 0.85), totalProducts), + rate: calculateRate(startTime, Math.floor(totalProducts * 0.85)), + percentage: '85' + }); + + // Calculate category performance metrics + await connection.query(` + INSERT INTO category_metrics ( + category_id, + product_count, + active_products, + total_value, + avg_margin, + turnover_rate, + growth_rate, + status + ) + SELECT + c.id as category_id, + COUNT(DISTINCT p.product_id) as product_count, + COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, + SUM(p.stock_quantity * p.cost_price) as total_value, + AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin, + -- Turnover rate calculation + SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate, + -- Growth rate calculation (comparing current month to previous month) + (( + SUM(CASE WHEN o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) - + SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) + ) / NULLIF( + SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END), + 0 + ) * 100) as growth_rate, + c.status + FROM categories c + LEFT JOIN product_categories pc ON c.id = pc.category_id + LEFT JOIN products p ON pc.product_id = p.product_id + LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false + GROUP BY c.id, c.status + ON DUPLICATE KEY UPDATE + product_count = VALUES(product_count), + active_products = VALUES(active_products), + total_value = VALUES(total_value), + avg_margin = VALUES(avg_margin), + turnover_rate = VALUES(turnover_rate), + growth_rate = VALUES(growth_rate), + status = VALUES(status), + last_calculated_at = CURRENT_TIMESTAMP + `); + + // Calculate category time-based metrics + await connection.query(` + INSERT INTO category_time_metrics ( + category_id, + year, + month, + product_count, + active_products, + total_value, + total_revenue, + avg_margin, + turnover_rate + ) + SELECT + c.id as category_id, + YEAR(o.date) as year, + MONTH(o.date) as month, + COUNT(DISTINCT p.product_id) as product_count, + COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, + SUM(p.stock_quantity * p.cost_price) as total_value, + SUM(o.price * o.quantity) as total_revenue, + AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin, + SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate + FROM categories c + LEFT JOIN product_categories pc ON c.id = pc.category_id + LEFT JOIN products p ON pc.product_id = p.product_id + LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false + WHERE o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) + GROUP BY c.id, YEAR(o.date), MONTH(o.date) + ON DUPLICATE KEY UPDATE + product_count = VALUES(product_count), + active_products = VALUES(active_products), + total_value = VALUES(total_value), + total_revenue = VALUES(total_revenue), + avg_margin = VALUES(avg_margin), + turnover_rate = VALUES(turnover_rate) `); } @@ -547,193 +689,6 @@ async function calculateCategorySalesMetrics(connection, startTime, totalProduct `); } -// Add new functions for vendor and category metrics -async function calculateVendorMetrics(connection) { - console.log('Calculating vendor metrics...'); - - // Calculate vendor performance metrics - await connection.query(` - INSERT INTO vendor_metrics ( - vendor, - avg_lead_time_days, - on_time_delivery_rate, - order_fill_rate, - total_orders, - total_late_orders, - total_purchase_value, - avg_order_value, - active_products, - total_products, - total_revenue, - avg_margin_percent, - status - ) - SELECT - vd.vendor, - -- Lead time metrics - AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days, - -- On-time delivery rate - (COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) / COUNT(*)) * 100 as on_time_delivery_rate, - -- Order fill rate - (SUM(po.received) / SUM(po.ordered)) * 100 as order_fill_rate, - COUNT(DISTINCT po.po_id) as total_orders, - COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as total_late_orders, - SUM(po.cost_price * po.ordered) as total_purchase_value, - AVG(po.cost_price * po.ordered) as avg_order_value, - -- Product counts - COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, - COUNT(DISTINCT p.product_id) as total_products, - -- Financial metrics - SUM(o.price * o.quantity) as total_revenue, - AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent, - vd.status - FROM vendor_details vd - LEFT JOIN products p ON vd.vendor = p.vendor - LEFT JOIN purchase_orders po ON p.product_id = po.product_id - LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false - GROUP BY vd.vendor, vd.status - ON DUPLICATE KEY UPDATE - avg_lead_time_days = VALUES(avg_lead_time_days), - on_time_delivery_rate = VALUES(on_time_delivery_rate), - order_fill_rate = VALUES(order_fill_rate), - total_orders = VALUES(total_orders), - total_late_orders = VALUES(total_late_orders), - total_purchase_value = VALUES(total_purchase_value), - avg_order_value = VALUES(avg_order_value), - active_products = VALUES(active_products), - total_products = VALUES(total_products), - total_revenue = VALUES(total_revenue), - avg_margin_percent = VALUES(avg_margin_percent), - status = VALUES(status), - last_calculated_at = CURRENT_TIMESTAMP - `); - - // Calculate vendor time-based metrics - await connection.query(` - INSERT INTO vendor_time_metrics ( - vendor, - year, - month, - total_orders, - late_orders, - avg_lead_time_days, - total_purchase_value, - total_revenue, - avg_margin_percent - ) - SELECT - vd.vendor, - YEAR(po.date) as year, - MONTH(po.date) as month, - COUNT(DISTINCT po.po_id) as total_orders, - COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as late_orders, - AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days, - SUM(po.cost_price * po.ordered) as total_purchase_value, - SUM(o.price * o.quantity) as total_revenue, - AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent - FROM vendor_details vd - LEFT JOIN products p ON vd.vendor = p.vendor - LEFT JOIN purchase_orders po ON p.product_id = po.product_id - LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false - WHERE po.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) - GROUP BY vd.vendor, YEAR(po.date), MONTH(po.date) - ON DUPLICATE KEY UPDATE - total_orders = VALUES(total_orders), - late_orders = VALUES(late_orders), - avg_lead_time_days = VALUES(avg_lead_time_days), - total_purchase_value = VALUES(total_purchase_value), - total_revenue = VALUES(total_revenue), - avg_margin_percent = VALUES(avg_margin_percent) - `); -} - -async function calculateCategoryMetrics(connection) { - console.log('Calculating category metrics...'); - - // Calculate category performance metrics - await connection.query(` - INSERT INTO category_metrics ( - category_id, - product_count, - active_products, - total_value, - avg_margin, - turnover_rate, - growth_rate, - status - ) - SELECT - c.id as category_id, - COUNT(DISTINCT p.product_id) as product_count, - COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, - SUM(p.stock_quantity * p.cost_price) as total_value, - AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin, - -- Turnover rate calculation - SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate, - -- Growth rate calculation (comparing current month to previous month) - (( - SUM(CASE WHEN o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) - - SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) - ) / NULLIF( - SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END), - 0 - ) * 100) as growth_rate, - c.status - FROM categories c - LEFT JOIN product_categories pc ON c.id = pc.category_id - LEFT JOIN products p ON pc.product_id = p.product_id - LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false - GROUP BY c.id, c.status - ON DUPLICATE KEY UPDATE - product_count = VALUES(product_count), - active_products = VALUES(active_products), - total_value = VALUES(total_value), - avg_margin = VALUES(avg_margin), - turnover_rate = VALUES(turnover_rate), - growth_rate = VALUES(growth_rate), - status = VALUES(status), - last_calculated_at = CURRENT_TIMESTAMP - `); - - // Calculate category time-based metrics - await connection.query(` - INSERT INTO category_time_metrics ( - category_id, - year, - month, - product_count, - active_products, - total_value, - total_revenue, - avg_margin, - turnover_rate - ) - SELECT - c.id as category_id, - YEAR(o.date) as year, - MONTH(o.date) as month, - COUNT(DISTINCT p.product_id) as product_count, - COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products, - SUM(p.stock_quantity * p.cost_price) as total_value, - SUM(o.price * o.quantity) as total_revenue, - AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin, - SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate - FROM categories c - LEFT JOIN product_categories pc ON c.id = pc.category_id - LEFT JOIN products p ON pc.product_id = p.product_id - LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false - WHERE o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) - GROUP BY c.id, YEAR(o.date), MONTH(o.date) - ON DUPLICATE KEY UPDATE - product_count = VALUES(product_count), - active_products = VALUES(active_products), - total_value = VALUES(total_value), - total_revenue = VALUES(total_revenue), - avg_margin = VALUES(avg_margin), - turnover_rate = VALUES(turnover_rate) - `); -} - // Update the main calculation function to include the new metrics async function calculateMetrics() { let pool; @@ -1339,27 +1294,13 @@ async function calculateMetrics() { }); await calculateLeadTimeMetrics(connection, startTime, totalProducts); - // Add category sales metrics calculation - if (isCancelled) { - throw new Error('Operation cancelled'); - } + // Calculate category metrics + await calculateCategoryMetrics(connection, startTime, totalProducts); + + // Calculate category sales metrics await calculateCategorySalesMetrics(connection, startTime, totalProducts); // Calculate ABC classification - if (isCancelled) { - throw new Error('Operation cancelled'); - } - outputProgress({ - status: 'running', - operation: 'Starting ABC classification', - current: Math.floor(totalProducts * 0.7), - total: totalProducts, - elapsed: formatElapsedTime(startTime), - remaining: estimateRemaining(startTime, Math.floor(totalProducts * 0.7), totalProducts), - rate: calculateRate(startTime, Math.floor(totalProducts * 0.7)), - percentage: '70' - }); - const [abcConfig] = await connection.query('SELECT a_threshold, b_threshold FROM abc_classification_config WHERE id = 1'); const abcThresholds = abcConfig[0] || { a_threshold: 20, b_threshold: 50 }; @@ -1388,9 +1329,6 @@ async function calculateMetrics() { `, [abcThresholds.a_threshold, abcThresholds.b_threshold]); // Calculate time-based aggregates - if (isCancelled) { - throw new Error('Operation cancelled'); - } outputProgress({ status: 'running', operation: 'Starting time-based aggregates calculation', diff --git a/inventory-server/scripts/reset-metrics.js b/inventory-server/scripts/reset-metrics.js index 09e1aa8..d31b78e 100644 --- a/inventory-server/scripts/reset-metrics.js +++ b/inventory-server/scripts/reset-metrics.js @@ -22,6 +22,9 @@ const METRICS_TABLES = [ 'product_metrics', 'product_time_aggregates', 'vendor_metrics', + 'vendor_time_metrics', + 'category_metrics', + 'category_time_metrics', 'category_sales_metrics' ]; @@ -48,19 +51,17 @@ async function resetMetrics() { connection = await mysql.createConnection(dbConfig); await connection.beginTransaction(); - // Reset existing metrics tables - await connection.query('TRUNCATE TABLE temp_sales_metrics'); - await connection.query('TRUNCATE TABLE temp_purchase_metrics'); - await connection.query('TRUNCATE TABLE product_metrics'); - await connection.query('TRUNCATE TABLE product_time_aggregates'); - - // Reset vendor metrics tables - await connection.query('TRUNCATE TABLE vendor_metrics'); - await connection.query('TRUNCATE TABLE vendor_time_metrics'); - - // Reset category metrics tables - await connection.query('TRUNCATE TABLE category_metrics'); - await connection.query('TRUNCATE TABLE category_time_metrics'); + // Reset all metrics tables + for (const table of METRICS_TABLES) { + console.log(`Truncating table: ${table}`); + try { + await connection.query(`TRUNCATE TABLE ${table}`); + console.log(`Successfully truncated: ${table}`); + } catch (err) { + console.error(`Error truncating ${table}:`, err.message); + throw err; + } + } await connection.commit(); console.log('All metrics tables reset successfully'); diff --git a/inventory-server/src/routes/vendors.js b/inventory-server/src/routes/vendors.js index 0c97685..8688a77 100644 --- a/inventory-server/src/routes/vendors.js +++ b/inventory-server/src/routes/vendors.js @@ -19,7 +19,7 @@ router.get('/', async (req, res) => { const params = []; if (search) { - whereConditions.push('(p.vendor LIKE ? OR vm.contact_name LIKE ?)'); + whereConditions.push('(p.vendor LIKE ? OR vd.contact_name LIKE ?)'); params.push(`%${search}%`, `%${search}%`); } @@ -61,9 +61,9 @@ router.get('/', async (req, res) => { const [vendors] = await pool.query(` SELECT p.vendor as name, - vm.contact_name, - vm.email, - vm.phone, + vd.contact_name, + vd.email, + vd.phone, vm.status, vm.avg_lead_time_days, vm.on_time_delivery_rate, @@ -72,6 +72,7 @@ router.get('/', async (req, res) => { COUNT(DISTINCT p.product_id) as active_products FROM products p LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor + LEFT JOIN vendor_details vd ON p.vendor = vd.vendor ${whereClause} GROUP BY p.vendor ORDER BY ${sortColumn} ${sortDirection} diff --git a/inventory/tsconfig.tsbuildinfo b/inventory/tsconfig.tsbuildinfo index 78c9cfe..ee8a216 100644 --- a/inventory/tsconfig.tsbuildinfo +++ b/inventory/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/dashboard.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/types/products.ts"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/auth/requireauth.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/forecasting/columns.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toggle-group.tsx","./src/components/ui/toggle.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/categories.tsx","./src/pages/dashboard.tsx","./src/pages/forecasting.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/pages/vendors.tsx","./src/routes/forecasting.tsx","./src/types/products.ts"],"errors":true,"version":"5.6.3"} \ No newline at end of file