Files
GestionIntegralWeb/Frontend/src/components/Modals/Distribucion/ControlDevolucionesFormModal.tsx
dmolinari f46dd82e27
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 6m4s
Fix: Dropdown Focus
2025-07-21 13:00:11 -03:00

201 lines
8.9 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Modal, Box, Typography, TextField, Button, CircularProgress, Alert,
FormControl, InputLabel, Select, MenuItem
} from '@mui/material';
import type { ControlDevolucionesDto } from '../../../models/dtos/Distribucion/ControlDevolucionesDto';
import type { CreateControlDevolucionesDto } from '../../../models/dtos/Distribucion/CreateControlDevolucionesDto';
import type { UpdateControlDevolucionesDto } from '../../../models/dtos/Distribucion/UpdateControlDevolucionesDto';
import type { EmpresaDto } from '../../../models/dtos/Distribucion/EmpresaDto';
import empresaService from '../../../services//Distribucion/empresaService';
const modalStyle = {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: { xs: '90%', sm: 500 },
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
maxHeight: '90vh',
overflowY: 'auto'
};
interface ControlDevolucionesFormModalProps {
open: boolean;
onClose: () => void;
onSubmit: (data: CreateControlDevolucionesDto | UpdateControlDevolucionesDto, idControl?: number) => Promise<void>;
initialData?: ControlDevolucionesDto | null;
errorMessage?: string | null;
clearErrorMessage: () => void;
}
const ControlDevolucionesFormModal: React.FC<ControlDevolucionesFormModalProps> = ({
open,
onClose,
onSubmit,
initialData,
errorMessage,
clearErrorMessage
}) => {
const [idEmpresa, setIdEmpresa] = useState<number | string>('');
const [fecha, setFecha] = useState<string>(new Date().toISOString().split('T')[0]);
const [entrada, setEntrada] = useState<string>('');
const [sobrantes, setSobrantes] = useState<string>('');
const [detalle, setDetalle] = useState('');
const [sinCargo, setSinCargo] = useState<string>('0'); // Default 0
const [empresas, setEmpresas] = useState<EmpresaDto[]>([]);
const [loading, setLoading] = useState(false);
const [loadingEmpresas, setLoadingEmpresas] = useState(false);
const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({});
const isEditing = Boolean(initialData);
useEffect(() => {
const fetchEmpresas = async () => {
setLoadingEmpresas(true);
try {
const data = await empresaService.getAllEmpresas();
setEmpresas(data);
} catch (error) {
console.error("Error al cargar empresas", error);
setLocalErrors(prev => ({...prev, empresas: 'Error al cargar empresas.'}));
} finally {
setLoadingEmpresas(false);
}
};
if (open) {
fetchEmpresas();
setIdEmpresa(initialData?.idEmpresa || '');
setFecha(initialData?.fecha || new Date().toISOString().split('T')[0]);
setEntrada(initialData?.entrada?.toString() || '0');
setSobrantes(initialData?.sobrantes?.toString() || '0');
setDetalle(initialData?.detalle || '');
setSinCargo(initialData?.sinCargo?.toString() || '0');
setLocalErrors({});
clearErrorMessage();
}
}, [open, initialData, clearErrorMessage]);
const validate = (): boolean => {
const errors: { [key: string]: string | null } = {};
if (!idEmpresa) errors.idEmpresa = 'Seleccione una empresa.';
if (!fecha.trim()) errors.fecha = 'La fecha es obligatoria.';
else if (!/^\d{4}-\d{2}-\d{2}$/.test(fecha)) errors.fecha = 'Formato de fecha inválido.';
const entradaNum = parseInt(entrada, 10);
const sobrantesNum = parseInt(sobrantes, 10);
const sinCargoNum = parseInt(sinCargo, 10);
if (entrada.trim() === '' || isNaN(entradaNum) || entradaNum < 0) errors.entrada = 'Entrada debe ser un número >= 0.';
if (sobrantes.trim() === '' || isNaN(sobrantesNum) || sobrantesNum < 0) errors.sobrantes = 'Sobrantes debe ser un número >= 0.';
if (sinCargo.trim() === '' || isNaN(sinCargoNum) || sinCargoNum < 0) errors.sinCargo = 'Sin Cargo debe ser un número >= 0.';
setLocalErrors(errors);
return Object.keys(errors).length === 0;
};
const handleInputChange = (fieldName: string) => {
if (localErrors[fieldName]) setLocalErrors(prev => ({ ...prev, [fieldName]: null }));
if (errorMessage) clearErrorMessage();
};
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
clearErrorMessage();
if (!validate()) return;
setLoading(true);
try {
const commonData = {
entrada: parseInt(entrada, 10),
sobrantes: parseInt(sobrantes, 10),
detalle: detalle || undefined,
sinCargo: parseInt(sinCargo, 10),
};
if (isEditing && initialData) {
const dataToSubmit: UpdateControlDevolucionesDto = { ...commonData };
await onSubmit(dataToSubmit, initialData.idControl);
} else {
const dataToSubmit: CreateControlDevolucionesDto = {
...commonData,
idEmpresa: Number(idEmpresa),
fecha,
};
await onSubmit(dataToSubmit);
}
onClose();
} catch (error: any) {
console.error("Error en submit de ControlDevolucionesFormModal:", error);
} finally {
setLoading(false);
}
};
return (
<Modal open={open} onClose={onClose}>
<Box sx={modalStyle}>
<Typography variant="h6" component="h2" gutterBottom>
{isEditing ? 'Editar Control de Devoluciones' : 'Registrar Control de Devoluciones'}
</Typography>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
<FormControl fullWidth margin="dense" error={!!localErrors.idEmpresa} required>
<InputLabel id="empresa-cd-select-label">Empresa</InputLabel>
<Select labelId="empresa-cd-select-label" label="Empresa" value={idEmpresa}
onChange={(e) => {setIdEmpresa(e.target.value as number); handleInputChange('idEmpresa');}}
disabled={loading || loadingEmpresas || isEditing} autoFocus={!isEditing}
>
<MenuItem value="" disabled><em>Seleccione</em></MenuItem>
{empresas.map((e) => (<MenuItem key={e.idEmpresa} value={e.idEmpresa}>{e.nombre}</MenuItem>))}
</Select>
{localErrors.idEmpresa && <Typography color="error" variant="caption">{localErrors.idEmpresa}</Typography>}
</FormControl>
<TextField label="Fecha" type="date" value={fecha} required
onChange={(e) => {setFecha(e.target.value); handleInputChange('fecha');}}
margin="dense" fullWidth error={!!localErrors.fecha} helperText={localErrors.fecha || ''}
disabled={loading || isEditing} InputLabelProps={{ shrink: true }}
/>
<TextField label="Entrada (Por Remito)" type="number" value={entrada} required
onChange={(e) => {setEntrada(e.target.value); handleInputChange('entrada');}}
margin="dense" fullWidth error={!!localErrors.entrada} helperText={localErrors.entrada || ''}
disabled={loading} inputProps={{min:0}}
/>
<TextField label="Sobrantes" type="number" value={sobrantes} required
onChange={(e) => {setSobrantes(e.target.value); handleInputChange('sobrantes');}}
margin="dense" fullWidth error={!!localErrors.sobrantes} helperText={localErrors.sobrantes || ''}
disabled={loading} inputProps={{min:0}}
/>
<TextField label="Ejemplares Sin Cargo" type="number" value={sinCargo} required
onChange={(e) => {setSinCargo(e.target.value); handleInputChange('sinCargo');}}
margin="dense" fullWidth error={!!localErrors.sinCargo} helperText={localErrors.sinCargo || ''}
disabled={loading} inputProps={{min:0}}
/>
<TextField label="Detalle (Opcional)" value={detalle}
onChange={(e) => setDetalle(e.target.value)}
margin="dense" fullWidth multiline rows={2} disabled={loading}
/>
</Box>
{errorMessage && <Alert severity="error" sx={{ mt: 2, width: '100%' }}>{errorMessage}</Alert>}
{localErrors.empresas && <Alert severity="warning" sx={{ mt: 1 }}>{localErrors.empresas}</Alert>}
<Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
<Button onClick={onClose} color="secondary" disabled={loading}>Cancelar</Button>
<Button type="submit" variant="contained" disabled={loading || loadingEmpresas}>
{loading ? <CircularProgress size={24} /> : (isEditing ? 'Guardar Cambios' : 'Registrar Control')}
</Button>
</Box>
</Box>
</Box>
</Modal>
);
};
export default ControlDevolucionesFormModal;