274 lines
9.7 KiB
TypeScript
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>
|
|
);
|
|
}
|