Files
inventory/inventory/src/components/settings/PerformanceMetrics.tsx

274 lines
9.7 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { toast } from "sonner";
import config from '../../config';
import { Table, TableBody, TableCell, TableHeader, TableRow } from "@/components/ui/table";
interface LeadTimeThreshold {
id: number;
cat_id: number | null;
vendor: string | null;
target_days: number;
warning_days: number;
critical_days: number;
}
interface ABCClassificationConfig {
id: number;
cat_id: number | null;
vendor: string | null;
a_threshold: number;
b_threshold: number;
classification_period_days: number;
}
interface TurnoverConfig {
id: number;
cat_id: number | null;
vendor: string | null;
calculation_period_days: number;
target_rate: number;
}
export function PerformanceMetrics() {
const [leadTimeThresholds, setLeadTimeThresholds] = useState<LeadTimeThreshold>({
id: 1,
cat_id: null,
vendor: null,
target_days: 14,
warning_days: 21,
critical_days: 30
});
const [abcConfigs, setAbcConfigs] = useState<ABCClassificationConfig[]>([]);
const [turnoverConfigs, setTurnoverConfigs] = useState<TurnoverConfig[]>([]);
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);
setAbcConfigs(data.abcConfigs);
setTurnoverConfigs(data.turnoverConfigs);
} catch (error) {
toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
loadConfig();
}, []);
const handleUpdateLeadTimeThresholds = async () => {
try {
const response = await fetch(`${config.apiUrl}/config/lead-time-thresholds/1`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(leadTimeThresholds)
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Failed to update lead time thresholds');
}
toast.success('Lead time thresholds updated successfully');
} catch (error) {
toast.error(`Failed to update thresholds: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
const handleUpdateABCConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config/abc-classification/1`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(abcConfigs)
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Failed to update ABC classification configuration');
}
toast.success('ABC classification configuration updated successfully');
} catch (error) {
toast.error(`Failed to update configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
const handleUpdateTurnoverConfig = async () => {
try {
const response = await fetch(`${config.apiUrl}/config/turnover/1`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(turnoverConfigs)
});
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Failed to update turnover configuration');
}
toast.success('Turnover configuration updated successfully');
} catch (error) {
toast.error(`Failed to update configuration: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
function getCategoryName(_cat_id: number): import("react").ReactNode {
throw new Error('Function not implemented.');
}
return (
<div className="max-w-[700px] space-y-4">
{/* Lead Time Thresholds Card */}
<Card>
<CardHeader>
<CardTitle>Lead Time Thresholds</CardTitle>
<CardDescription>Configure lead time thresholds for vendor performance</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="grid grid-cols-3 gap-4">
<div>
<Label htmlFor="target-days">Target Days</Label>
<Input
id="target-days"
type="number"
min="1"
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={leadTimeThresholds.target_days}
onChange={(e) => setLeadTimeThresholds(prev => ({
...prev,
target_days: parseInt(e.target.value) || 1
}))}
/>
</div>
<div>
<Label htmlFor="warning-days">Warning Days</Label>
<Input
id="warning-days"
type="number"
min="1"
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={leadTimeThresholds.warning_days}
onChange={(e) => setLeadTimeThresholds(prev => ({
...prev,
warning_days: parseInt(e.target.value) || 1
}))}
/>
</div>
<div>
<Label htmlFor="critical-days-lead">Critical Days</Label>
<Input
id="critical-days-lead"
type="number"
min="1"
className="[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={leadTimeThresholds.critical_days}
onChange={(e) => setLeadTimeThresholds(prev => ({
...prev,
critical_days: parseInt(e.target.value) || 1
}))}
/>
</div>
</div>
<Button onClick={handleUpdateLeadTimeThresholds}>
Update Lead Time Thresholds
</Button>
</div>
</CardContent>
</Card>
{/* ABC Classification Card */}
<Card>
<CardHeader>
<CardTitle>ABC Classification</CardTitle>
<CardDescription>Configure ABC classification parameters</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Table>
<TableHeader>
<TableRow>
<TableCell>Category</TableCell>
<TableCell>Vendor</TableCell>
<TableCell className="text-right">A Threshold</TableCell>
<TableCell className="text-right">B Threshold</TableCell>
<TableCell className="text-right">Period Days</TableCell>
</TableRow>
</TableHeader>
<TableBody>
{abcConfigs.map((config) => (
<TableRow key={`${config.cat_id}-${config.vendor}`}>
<TableCell>{config.cat_id ? getCategoryName(config.cat_id) : 'Global'}</TableCell>
<TableCell>{config.vendor || 'All Vendors'}</TableCell>
<TableCell className="text-right">{config.a_threshold}%</TableCell>
<TableCell className="text-right">{config.b_threshold}%</TableCell>
<TableCell className="text-right">{config.classification_period_days}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Button onClick={handleUpdateABCConfig}>
Update ABC Classification
</Button>
</div>
</CardContent>
</Card>
{/* Turnover Configuration Card */}
<Card>
<CardHeader>
<CardTitle>Turnover Rate</CardTitle>
<CardDescription>Configure turnover rate calculations</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Table>
<TableHeader>
<TableRow>
<TableCell>Category</TableCell>
<TableCell>Vendor</TableCell>
<TableCell className="text-right">Period Days</TableCell>
<TableCell className="text-right">Target Rate</TableCell>
</TableRow>
</TableHeader>
<TableBody>
{turnoverConfigs.map((config) => (
<TableRow key={`${config.cat_id}-${config.vendor}`}>
<TableCell>{config.cat_id ? getCategoryName(config.cat_id) : 'Global'}</TableCell>
<TableCell>{config.vendor || 'All Vendors'}</TableCell>
<TableCell className="text-right">{config.calculation_period_days}</TableCell>
<TableCell className="text-right">{config.target_rate.toFixed(2)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Button onClick={handleUpdateTurnoverConfig}>
Update Turnover Configuration
</Button>
</div>
</CardContent>
</Card>
</div>
);
}