Link new settings pages to new tables for config

This commit is contained in:
2025-01-12 16:01:34 -05:00
parent 8172323954
commit a6781205a3
8 changed files with 270 additions and 6 deletions

View File

@@ -35,7 +35,12 @@ const CORE_TABLES = [
// Config tables that must be created // Config tables that must be created
const CONFIG_TABLES = [ const CONFIG_TABLES = [
'stock_thresholds' 'stock_thresholds',
'lead_time_thresholds',
'sales_velocity_config',
'abc_classification_config',
'safety_stock_config',
'turnover_config'
]; ];
// Split SQL into individual statements // Split SQL into individual statements

View File

@@ -26,7 +26,12 @@ const METRICS_TABLES = [
// Config tables that must exist // Config tables that must exist
const CONFIG_TABLES = [ const CONFIG_TABLES = [
'stock_thresholds' 'stock_thresholds',
'lead_time_thresholds',
'sales_velocity_config',
'abc_classification_config',
'safety_stock_config',
'turnover_config'
]; ];
// Core tables that must exist // Core tables that must exist

View File

@@ -0,0 +1,172 @@
const express = require('express');
const router = express.Router();
// Debug middleware
router.use((req, res, next) => {
console.log(`[Config Route] ${req.method} ${req.path}`);
next();
});
// Get all configuration values
router.get('/', async (req, res) => {
const pool = req.app.locals.pool;
try {
console.log('[Config Route] Fetching configuration values...');
const [stockThresholds] = await pool.query('SELECT * FROM stock_thresholds WHERE id = 1');
console.log('[Config Route] Stock thresholds:', stockThresholds);
const [leadTimeThresholds] = await pool.query('SELECT * FROM lead_time_thresholds WHERE id = 1');
console.log('[Config Route] Lead time thresholds:', leadTimeThresholds);
const [salesVelocityConfig] = await pool.query('SELECT * FROM sales_velocity_config WHERE id = 1');
console.log('[Config Route] Sales velocity config:', salesVelocityConfig);
const [abcConfig] = await pool.query('SELECT * FROM abc_classification_config WHERE id = 1');
console.log('[Config Route] ABC config:', abcConfig);
const [safetyStockConfig] = await pool.query('SELECT * FROM safety_stock_config WHERE id = 1');
console.log('[Config Route] Safety stock config:', safetyStockConfig);
const [turnoverConfig] = await pool.query('SELECT * FROM turnover_config WHERE id = 1');
console.log('[Config Route] Turnover config:', turnoverConfig);
const response = {
stockThresholds: stockThresholds[0],
leadTimeThresholds: leadTimeThresholds[0],
salesVelocityConfig: salesVelocityConfig[0],
abcConfig: abcConfig[0],
safetyStockConfig: safetyStockConfig[0],
turnoverConfig: turnoverConfig[0]
};
console.log('[Config Route] Sending response:', response);
res.json(response);
} catch (error) {
console.error('[Config Route] Error fetching configuration:', error);
res.status(500).json({ error: 'Failed to fetch configuration', details: error.message });
}
});
// Update stock thresholds
router.put('/stock-thresholds/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { critical_days, reorder_days, overstock_days, low_stock_threshold, min_reorder_quantity } = req.body;
const [result] = await pool.query(
`UPDATE stock_thresholds
SET critical_days = ?,
reorder_days = ?,
overstock_days = ?,
low_stock_threshold = ?,
min_reorder_quantity = ?
WHERE id = ?`,
[critical_days, reorder_days, overstock_days, low_stock_threshold, min_reorder_quantity, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating stock thresholds:', error);
res.status(500).json({ error: 'Failed to update stock thresholds' });
}
});
// Update lead time thresholds
router.put('/lead-time-thresholds/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { target_days, warning_days, critical_days } = req.body;
const [result] = await pool.query(
`UPDATE lead_time_thresholds
SET target_days = ?,
warning_days = ?,
critical_days = ?
WHERE id = ?`,
[target_days, warning_days, critical_days, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating lead time thresholds:', error);
res.status(500).json({ error: 'Failed to update lead time thresholds' });
}
});
// Update sales velocity config
router.put('/sales-velocity/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { daily_window_days, weekly_window_days, monthly_window_days } = req.body;
const [result] = await pool.query(
`UPDATE sales_velocity_config
SET daily_window_days = ?,
weekly_window_days = ?,
monthly_window_days = ?
WHERE id = ?`,
[daily_window_days, weekly_window_days, monthly_window_days, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating sales velocity config:', error);
res.status(500).json({ error: 'Failed to update sales velocity config' });
}
});
// Update ABC classification config
router.put('/abc-classification/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { a_threshold, b_threshold, classification_period_days } = req.body;
const [result] = await pool.query(
`UPDATE abc_classification_config
SET a_threshold = ?,
b_threshold = ?,
classification_period_days = ?
WHERE id = ?`,
[a_threshold, b_threshold, classification_period_days, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating ABC classification config:', error);
res.status(500).json({ error: 'Failed to update ABC classification config' });
}
});
// Update safety stock config
router.put('/safety-stock/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { coverage_days, service_level } = req.body;
const [result] = await pool.query(
`UPDATE safety_stock_config
SET coverage_days = ?,
service_level = ?
WHERE id = ?`,
[coverage_days, service_level, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating safety stock config:', error);
res.status(500).json({ error: 'Failed to update safety stock config' });
}
});
// Update turnover config
router.put('/turnover/:id', async (req, res) => {
const pool = req.app.locals.pool;
try {
const { calculation_period_days, target_rate } = req.body;
const [result] = await pool.query(
`UPDATE turnover_config
SET calculation_period_days = ?,
target_rate = ?
WHERE id = ?`,
[calculation_period_days, target_rate, req.params.id]
);
res.json({ success: true });
} catch (error) {
console.error('[Config Route] Error updating turnover config:', error);
res.status(500).json({ error: 'Failed to update turnover config' });
}
});
// Export the router
module.exports = router;

View File

@@ -12,6 +12,7 @@ const ordersRouter = require('./routes/orders');
const csvRouter = require('./routes/csv'); const csvRouter = require('./routes/csv');
const analyticsRouter = require('./routes/analytics'); const analyticsRouter = require('./routes/analytics');
const purchaseOrdersRouter = require('./routes/purchase-orders'); const purchaseOrdersRouter = require('./routes/purchase-orders');
const configRouter = require('./routes/config');
// Get the absolute path to the .env file // Get the absolute path to the .env file
const envPath = path.resolve(process.cwd(), '.env'); const envPath = path.resolve(process.cwd(), '.env');
@@ -83,6 +84,7 @@ app.use('/api/orders', ordersRouter);
app.use('/api/csv', csvRouter); app.use('/api/csv', csvRouter);
app.use('/api/analytics', analyticsRouter); app.use('/api/analytics', analyticsRouter);
app.use('/api/purchase-orders', purchaseOrdersRouter); app.use('/api/purchase-orders', purchaseOrdersRouter);
app.use('/api/config', configRouter);
// Basic health check route // Basic health check route
app.get('/health', (req, res) => { app.get('/health', (req, res) => {

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -25,6 +25,24 @@ export function CalculationSettings() {
monthly_window_days: 90 monthly_window_days: 90
}); });
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to load configuration');
}
const data = await response.json();
setSalesVelocityConfig(data.salesVelocityConfig);
} catch (error) {
toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
loadConfig();
}, []);
const handleUpdateSalesVelocityConfig = async () => { const handleUpdateSalesVelocityConfig = async () => {
try { try {
const response = await fetch(`${config.apiUrl}/config/sales-velocity/1`, { const response = await fetch(`${config.apiUrl}/config/sales-velocity/1`, {

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -114,6 +114,29 @@ export function Configuration() {
target_rate: 1.0 target_rate: 1.0
}); });
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to load configuration');
}
const data = await response.json();
setStockThresholds(data.stockThresholds);
setLeadTimeThresholds(data.leadTimeThresholds);
setSalesVelocityConfig(data.salesVelocityConfig);
setAbcConfig(data.abcConfig);
setSafetyStockConfig(data.safetyStockConfig);
setTurnoverConfig(data.turnoverConfig);
} catch (error) {
toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
loadConfig();
}, []);
const handleUpdateStockThresholds = async () => { const handleUpdateStockThresholds = async () => {
try { try {
const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, { const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, {

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -55,6 +55,26 @@ export function PerformanceMetrics() {
target_rate: 1.0 target_rate: 1.0
}); });
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to load configuration');
}
const data = await response.json();
setLeadTimeThresholds(data.leadTimeThresholds);
setAbcConfig(data.abcConfig);
setTurnoverConfig(data.turnoverConfig);
} catch (error) {
toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
loadConfig();
}, []);
const handleUpdateLeadTimeThresholds = async () => { const handleUpdateLeadTimeThresholds = async () => {
try { try {
const response = await fetch(`${config.apiUrl}/config/lead-time-thresholds/1`, { const response = await fetch(`${config.apiUrl}/config/lead-time-thresholds/1`, {

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -45,6 +45,25 @@ export function StockManagement() {
service_level: 95.0 service_level: 95.0
}); });
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to load configuration');
}
const data = await response.json();
setStockThresholds(data.stockThresholds);
setSafetyStockConfig(data.safetyStockConfig);
} catch (error) {
toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
loadConfig();
}, []);
const handleUpdateStockThresholds = async () => { const handleUpdateStockThresholds = async () => {
try { try {
const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, { const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, {