Improve the frontend 3

This commit is contained in:
Urtzi Alfaro
2025-10-30 21:08:07 +01:00
parent 36217a2729
commit 63f5c6d512
184 changed files with 21512 additions and 7442 deletions

View File

@@ -0,0 +1,126 @@
import React from 'react';
import { Card } from '@components/ui';
import { MOQSettings } from '@services/types/settings';
import { Input } from '@components/ui/Input';
interface MOQSettingsCardProps {
settings: MOQSettings;
onChange: (settings: MOQSettings) => void;
disabled?: boolean;
}
const MOQSettingsCard: React.FC<MOQSettingsCardProps> = ({
settings,
onChange,
disabled = false,
}) => {
const handleNumberChange = (field: keyof MOQSettings, value: string) => {
const numValue = value === '' ? 0 : Number(value);
onChange({
...settings,
[field]: numValue,
});
};
const handleToggleChange = (field: keyof MOQSettings, value: boolean) => {
onChange({
...settings,
[field]: value,
});
};
return (
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-6">
Configuración de MOQ (Cantidad Mínima de Pedido)
</h3>
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Consolidation Window Days */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Días de Ventana de Consolidación (1-30)
</label>
<Input
type="number"
min="1"
max="30"
value={settings.consolidation_window_days}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('consolidation_window_days', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Min Batch Size */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Tamaño Mínimo de Lote (0.1-1000)
</label>
<Input
type="number"
min="0.1"
max="1000"
step="0.1"
value={settings.min_batch_size}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('min_batch_size', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Max Batch Size */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Tamaño Máximo de Lote (1-10000)
</label>
<Input
type="number"
min="1"
max="10000"
step="1"
value={settings.max_batch_size}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('max_batch_size', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
</div>
{/* Toggle Options */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<input
type="checkbox"
id="allow_early_ordering"
checked={settings.allow_early_ordering}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleChange('allow_early_ordering', e.target.checked)}
disabled={disabled}
className="rounded border-[var(--border-primary)]"
/>
<label htmlFor="allow_early_ordering" className="text-sm text-[var(--text-secondary)]">
Permitir Pedido Anticipado
</label>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="enable_batch_optimization"
checked={settings.enable_batch_optimization}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleChange('enable_batch_optimization', e.target.checked)}
disabled={disabled}
className="rounded border-[var(--border-primary)]"
/>
<label htmlFor="enable_batch_optimization" className="text-sm text-[var(--text-secondary)]">
Habilitar Optimización de Lotes
</label>
</div>
</div>
</div>
</Card>
);
};
export default MOQSettingsCard;

View File

@@ -0,0 +1,158 @@
import React from 'react';
import { Card } from '@components/ui';
import { ReplenishmentSettings } from '@services/types/settings';
import { Slider } from '@components/ui/Slider';
import { Input } from '@components/ui/Input';
interface ReplenishmentSettingsCardProps {
settings: ReplenishmentSettings;
onChange: (settings: ReplenishmentSettings) => void;
disabled?: boolean;
}
const ReplenishmentSettingsCard: React.FC<ReplenishmentSettingsCardProps> = ({
settings,
onChange,
disabled = false,
}) => {
const handleNumberChange = (field: keyof ReplenishmentSettings, value: string) => {
const numValue = value === '' ? 0 : Number(value);
onChange({
...settings,
[field]: numValue,
});
};
const handleToggleChange = (field: keyof ReplenishmentSettings, value: boolean) => {
onChange({
...settings,
[field]: value,
});
};
return (
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-6">
Planeamiento de Reposición
</h3>
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Projection Horizon Days */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Días de Proyección (1-30)
</label>
<Input
type="number"
min="1"
max="30"
value={settings.projection_horizon_days}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('projection_horizon_days', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Service Level */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Nivel de Servicio ({(settings.service_level * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.service_level]}
onValueChange={([value]: number[]) => handleNumberChange('service_level', value.toString())}
disabled={disabled}
/>
</div>
{/* Buffer Days */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Días de Buffer (0-14)
</label>
<Input
type="number"
min="0"
max="14"
value={settings.buffer_days}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('buffer_days', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Demand Forecast Days */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Días de Previsión de Demanda (1-90)
</label>
<Input
type="number"
min="1"
max="90"
value={settings.demand_forecast_days}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('demand_forecast_days', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Min Order Quantity */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Cantidad Mínima de Pedido (0.1-1000)
</label>
<Input
type="number"
min="0.1"
max="100"
step="0.1"
value={settings.min_order_quantity}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('min_order_quantity', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Max Order Quantity */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Cantidad Máxima de Pedido (1-1000)
</label>
<Input
type="number"
min="1"
max="10000"
step="1"
value={settings.max_order_quantity}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('max_order_quantity', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
</div>
{/* Enable Auto Replenishment Toggle */}
<div className="flex items-center gap-2">
<input
type="checkbox"
id="enable_auto_replenishment"
checked={settings.enable_auto_replenishment}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleChange('enable_auto_replenishment', e.target.checked)}
disabled={disabled}
className="rounded border-[var(--border-primary)]"
/>
<label htmlFor="enable_auto_replenishment" className="text-sm text-[var(--text-secondary)]">
Habilitar Reposición Automática
</label>
</div>
</div>
</Card>
);
};
export default ReplenishmentSettingsCard;

View File

@@ -0,0 +1,130 @@
import React from 'react';
import { Card } from '@components/ui';
import { SafetyStockSettings } from '@services/types/settings';
import { Slider } from '@components/ui/Slider';
import { Input } from '@components/ui/Input';
import { Select } from '@components/ui/Select';
interface SafetyStockSettingsCardProps {
settings: SafetyStockSettings;
onChange: (settings: SafetyStockSettings) => void;
disabled?: boolean;
}
const SafetyStockSettingsCard: React.FC<SafetyStockSettingsCardProps> = ({
settings,
onChange,
disabled = false,
}) => {
const handleNumberChange = (field: keyof SafetyStockSettings, value: string) => {
const numValue = value === '' ? 0 : Number(value);
onChange({
...settings,
[field]: numValue,
});
};
const handleStringChange = (field: keyof SafetyStockSettings, value: any) => {
const stringValue = typeof value === 'object' && value !== null ? value[0] : value;
onChange({
...settings,
[field]: stringValue.toString(),
});
};
return (
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-6">
Configuración de Stock de Seguridad
</h3>
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Service Level */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Nivel de Servicio ({(settings.service_level * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.service_level]}
onValueChange={([value]: number[]) => handleNumberChange('service_level', value.toString())}
disabled={disabled}
/>
</div>
{/* Method */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Método de Cálculo
</label>
<Select
value={settings.method}
onChange={(value) => handleStringChange('method', value)}
disabled={disabled}
options={[
{ value: 'statistical', label: 'Estadístico (Z×σ×√L)' },
{ value: 'fixed_percentage', label: 'Porcentaje Fijo (20%)' },
]}
/>
</div>
{/* Min Safety Stock */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Stock de Seguridad Mínimo (0-1000)
</label>
<Input
type="number"
min="0"
max="1000"
step="0.1"
value={settings.min_safety_stock}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('min_safety_stock', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Max Safety Stock */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Stock de Seguridad Máximo (0-1000)
</label>
<Input
type="number"
min="0"
max="1000"
step="0.1"
value={settings.max_safety_stock}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('max_safety_stock', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Reorder Point Calculation */}
<div className="space-y-2 md:col-span-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Método de Punto de Reorden
</label>
<Select
value={settings.reorder_point_calculation}
onChange={(value) => handleStringChange('reorder_point_calculation', value)}
disabled={disabled}
options={[
{ value: 'safety_stock_plus_lead_time_demand', label: 'Stock de Seguridad + Demanda de Tiempo de Entrega' },
{ value: 'safety_stock_only', label: 'Solo Stock de Seguridad' },
{ value: 'fixed_quantity', label: 'Cantidad Fija' },
]}
/>
</div>
</div>
</div>
</Card>
);
};
export default SafetyStockSettingsCard;

View File

@@ -0,0 +1,152 @@
import React from 'react';
import { Card } from '@components/ui';
import { SupplierSelectionSettings } from '@services/types/settings';
import { Slider } from '@components/ui/Slider';
import { Input } from '@components/ui/Input';
interface SupplierSelectionSettingsCardProps {
settings: SupplierSelectionSettings;
onChange: (settings: SupplierSelectionSettings) => void;
disabled?: boolean;
}
const SupplierSelectionSettingsCard: React.FC<SupplierSelectionSettingsCardProps> = ({
settings,
onChange,
disabled = false,
}) => {
const handleNumberChange = (field: keyof SupplierSelectionSettings, value: string) => {
const numValue = value === '' ? 0 : Number(value);
onChange({
...settings,
[field]: numValue,
});
};
const handleToggleChange = (field: keyof SupplierSelectionSettings, value: boolean) => {
onChange({
...settings,
[field]: value,
});
};
return (
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-6">
Configuración de Selección de Proveedores
</h3>
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Price Weight */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Peso del Precio ({(settings.price_weight * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.price_weight]}
onValueChange={([value]: number[]) => handleNumberChange('price_weight', value.toString())}
disabled={disabled}
/>
</div>
{/* Lead Time Weight */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Peso del Tiempo de Entrega ({(settings.lead_time_weight * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.lead_time_weight]}
onValueChange={([value]: number[]) => handleNumberChange('lead_time_weight', value.toString())}
disabled={disabled}
/>
</div>
{/* Quality Weight */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Peso de la Calidad ({(settings.quality_weight * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.quality_weight]}
onValueChange={([value]: number[]) => handleNumberChange('quality_weight', value.toString())}
disabled={disabled}
/>
</div>
{/* Reliability Weight */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Peso de la Confiabilidad ({(settings.reliability_weight * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.reliability_weight]}
onValueChange={([value]: number[]) => handleNumberChange('reliability_weight', value.toString())}
disabled={disabled}
/>
</div>
{/* Diversification Threshold */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Umbral de Diversificación (0-1000)
</label>
<Input
type="number"
min="0"
max="1000"
value={settings.diversification_threshold}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleNumberChange('diversification_threshold', e.target.value)}
disabled={disabled}
className="w-full"
/>
</div>
{/* Max Single Percentage */}
<div className="space-y-2">
<label className="text-sm font-medium text-[var(--text-secondary)]">
Máximo % para Proveedor Único ({(settings.max_single_percentage * 100).toFixed(0)}%)
</label>
<Slider
min={0}
max={1}
step={0.01}
value={[settings.max_single_percentage]}
onValueChange={([value]: number[]) => handleNumberChange('max_single_percentage', value.toString())}
disabled={disabled}
/>
</div>
</div>
{/* Enable Supplier Score Optimization Toggle */}
<div className="flex items-center gap-2">
<input
type="checkbox"
id="enable_supplier_score_optimization"
checked={settings.enable_supplier_score_optimization}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleChange('enable_supplier_score_optimization', e.target.checked)}
disabled={disabled}
className="rounded border-[var(--border-primary)]"
/>
<label htmlFor="enable_supplier_score_optimization" className="text-sm text-[var(--text-secondary)]">
Habilitar Optimización por Puntuación de Proveedores
</label>
</div>
</div>
</Card>
);
};
export default SupplierSelectionSettingsCard;