239 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			239 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | import React, { useState, useEffect } from 'react'; | ||
|  | import { | ||
|  |     Modal, Box, Typography, TextField, Button, CircularProgress, Alert, | ||
|  |     FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox | ||
|  | } from '@mui/material'; | ||
|  | import type { CanillaDto } from '../../../models/dtos/Distribucion/CanillaDto'; | ||
|  | import type { CreateCanillaDto } from '../../../models/dtos/Distribucion/CreateCanillaDto'; | ||
|  | import type { UpdateCanillaDto } from '../../../models/dtos/Distribucion/UpdateCanillaDto'; | ||
|  | import type { ZonaDto } from '../../../models/dtos/Zonas/ZonaDto'; // Para el dropdown de zonas
 | ||
|  | import type { EmpresaDto } from '../../../models/dtos/Distribucion/EmpresaDto'; // Para el dropdown de empresas
 | ||
|  | import zonaService from '../../../services/Distribucion/zonaService'; | ||
|  | import empresaService from '../../../services/Distribucion/empresaService'; | ||
|  | 
 | ||
|  | const modalStyle = { | ||
|  |     position: 'absolute' as 'absolute', | ||
|  |     top: '50%', | ||
|  |     left: '50%', | ||
|  |     transform: 'translate(-50%, -50%)', | ||
|  |     width: { xs: '90%', sm: 600 }, // Un poco más ancho
 | ||
|  |     bgcolor: 'background.paper', | ||
|  |     border: '2px solid #000', | ||
|  |     boxShadow: 24, | ||
|  |     p: 4, | ||
|  |     maxHeight: '90vh', | ||
|  |     overflowY: 'auto' | ||
|  | }; | ||
|  | 
 | ||
|  | interface CanillaFormModalProps { | ||
|  |   open: boolean; | ||
|  |   onClose: () => void; | ||
|  |   onSubmit: (data: CreateCanillaDto | UpdateCanillaDto, id?: number) => Promise<void>; | ||
|  |   initialData?: CanillaDto | null; | ||
|  |   errorMessage?: string | null; | ||
|  |   clearErrorMessage: () => void; | ||
|  | } | ||
|  | 
 | ||
|  | const CanillaFormModal: React.FC<CanillaFormModalProps> = ({ | ||
|  |   open, | ||
|  |   onClose, | ||
|  |   onSubmit, | ||
|  |   initialData, | ||
|  |   errorMessage, | ||
|  |   clearErrorMessage | ||
|  | }) => { | ||
|  |   const [legajo, setLegajo] = useState<string>(''); // Manejar como string para el TextField
 | ||
|  |   const [nomApe, setNomApe] = useState(''); | ||
|  |   const [parada, setParada] = useState(''); | ||
|  |   const [idZona, setIdZona] = useState<number | string>(''); | ||
|  |   const [accionista, setAccionista] = useState(false); | ||
|  |   const [obs, setObs] = useState(''); | ||
|  |   const [empresa, setEmpresa] = useState<number | string>(0); // 0 para N/A (accionista)
 | ||
|  | 
 | ||
|  |   const [zonas, setZonas] = useState<ZonaDto[]>([]); | ||
|  |   const [empresas, setEmpresas] = useState<EmpresaDto[]>([]); | ||
|  |   const [loading, setLoading] = useState(false); | ||
|  |   const [loadingDropdowns, setLoadingDropdowns] = useState(false); | ||
|  |   const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({}); | ||
|  | 
 | ||
|  |   const isEditing = Boolean(initialData); | ||
|  | 
 | ||
|  |   useEffect(() => { | ||
|  |     const fetchDropdownData = async () => { | ||
|  |         setLoadingDropdowns(true); | ||
|  |         try { | ||
|  |             const [zonasData, empresasData] = await Promise.all([ | ||
|  |                 zonaService.getAllZonas(), // Asume que este servicio devuelve zonas activas
 | ||
|  |                 empresaService.getAllEmpresas() | ||
|  |             ]); | ||
|  |             setZonas(zonasData); | ||
|  |             setEmpresas(empresasData); | ||
|  |         } catch (error) { | ||
|  |             console.error("Error al cargar datos para dropdowns", error); | ||
|  |             setLocalErrors(prev => ({...prev, dropdowns: 'Error al cargar zonas/empresas.'})); | ||
|  |         } finally { | ||
|  |             setLoadingDropdowns(false); | ||
|  |         } | ||
|  |     }; | ||
|  | 
 | ||
|  |     if (open) { | ||
|  |         fetchDropdownData(); | ||
|  |         setLegajo(initialData?.legajo?.toString() || ''); | ||
|  |         setNomApe(initialData?.nomApe || ''); | ||
|  |         setParada(initialData?.parada || ''); | ||
|  |         setIdZona(initialData?.idZona || ''); | ||
|  |         setAccionista(initialData ? initialData.accionista : false); | ||
|  |         setObs(initialData?.obs || ''); | ||
|  |         setEmpresa(initialData ? initialData.empresa : 0); // Si es accionista, empresa es 0
 | ||
|  |         setLocalErrors({}); | ||
|  |         clearErrorMessage(); | ||
|  |     } | ||
|  |   }, [open, initialData, clearErrorMessage]); | ||
|  | 
 | ||
|  | 
 | ||
|  |   const validate = (): boolean => { | ||
|  |     const errors: { [key: string]: string | null } = {}; | ||
|  |     if (!nomApe.trim()) errors.nomApe = 'Nombre y Apellido son obligatorios.'; | ||
|  |     if (!idZona) errors.idZona = 'Debe seleccionar una zona.'; | ||
|  | 
 | ||
|  |     const legajoNum = legajo ? parseInt(legajo, 10) : null; | ||
|  |     if (legajo.trim() && (isNaN(legajoNum!) || legajoNum! < 0)) { | ||
|  |         errors.legajo = 'Legajo debe ser un número positivo o vacío.'; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Lógica de empresa y accionista
 | ||
|  |     if (accionista && empresa !== 0 && empresa !== '') { | ||
|  |         errors.empresa = 'Si es Accionista, la Empresa debe ser N/A (0).'; | ||
|  |     } | ||
|  |     if (!accionista && (empresa === 0 || empresa === '')) { | ||
|  |         errors.empresa = 'Si no es Accionista, debe seleccionar una Empresa.'; | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     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 legajoParsed = legajo.trim() ? parseInt(legajo, 10) : null; | ||
|  | 
 | ||
|  |       const dataToSubmit = { | ||
|  |           legajo: legajoParsed, | ||
|  |           nomApe, | ||
|  |           parada: parada || undefined, | ||
|  |           idZona: Number(idZona), | ||
|  |           accionista, | ||
|  |           obs: obs || undefined, | ||
|  |           empresa: Number(empresa) | ||
|  |       }; | ||
|  | 
 | ||
|  |       if (isEditing && initialData) { | ||
|  |         await onSubmit(dataToSubmit as UpdateCanillaDto, initialData.idCanilla); | ||
|  |       } else { | ||
|  |         await onSubmit(dataToSubmit as CreateCanillaDto); | ||
|  |       } | ||
|  |       onClose(); | ||
|  |     } catch (error: any) { | ||
|  |       console.error("Error en submit de CanillaFormModal:", error); | ||
|  |     } finally { | ||
|  |        setLoading(false); | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   return ( | ||
|  |     <Modal open={open} onClose={onClose}> | ||
|  |       <Box sx={modalStyle}> | ||
|  |         <Typography variant="h6" component="h2" gutterBottom> | ||
|  |           {isEditing ? 'Editar Canillita' : 'Agregar Nuevo Canillita'} | ||
|  |         </Typography> | ||
|  |         <Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}> | ||
|  |             {/* SECCIÓN DE CAMPOS CON BOX Y FLEXBOX */} | ||
|  |             <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}> | ||
|  |                 <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'flex-start' }}> | ||
|  |                     <TextField label="Legajo" value={legajo} type="number" | ||
|  |                         onChange={(e) => {setLegajo(e.target.value); handleInputChange('legajo');}} | ||
|  |                         margin="dense" fullWidth error={!!localErrors.legajo} helperText={localErrors.legajo || ''} | ||
|  |                         disabled={loading} | ||
|  |                         sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} | ||
|  |                     /> | ||
|  |                     <TextField label="Nombre y Apellido" value={nomApe} required | ||
|  |                         onChange={(e) => {setNomApe(e.target.value); handleInputChange('nomApe');}} | ||
|  |                         margin="dense" fullWidth error={!!localErrors.nomApe} helperText={localErrors.nomApe || ''} | ||
|  |                         disabled={loading} autoFocus={!isEditing} | ||
|  |                         sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} | ||
|  |                     /> | ||
|  |                 </Box> | ||
|  |                 <TextField label="Parada (Dirección)" value={parada} | ||
|  |                     onChange={(e) => setParada(e.target.value)} | ||
|  |                     margin="dense" fullWidth multiline rows={2} disabled={loading} | ||
|  |                 /> | ||
|  |                 <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'flex-start', mt: 1 }}> | ||
|  |                     <FormControl fullWidth margin="dense" error={!!localErrors.idZona} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }}> | ||
|  |                         <InputLabel id="zona-select-label" required>Zona</InputLabel> | ||
|  |                         <Select labelId="zona-select-label" label="Zona" value={idZona} | ||
|  |                             onChange={(e) => {setIdZona(e.target.value as number); handleInputChange('idZona');}} | ||
|  |                             disabled={loading || loadingDropdowns} | ||
|  |                         > | ||
|  |                             <MenuItem value="" disabled><em>Seleccione una zona</em></MenuItem> | ||
|  |                             {zonas.map((z) => (<MenuItem key={z.idZona} value={z.idZona}>{z.nombre}</MenuItem>))} | ||
|  |                         </Select> | ||
|  |                         {localErrors.idZona && <Typography color="error" variant="caption">{localErrors.idZona}</Typography>} | ||
|  |                     </FormControl> | ||
|  |                     <FormControl fullWidth margin="dense" error={!!localErrors.empresa} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }}> | ||
|  |                         <InputLabel id="empresa-select-label">Empresa</InputLabel> | ||
|  |                         <Select labelId="empresa-select-label" label="Empresa" value={empresa} | ||
|  |                             onChange={(e) => {setEmpresa(e.target.value as number); handleInputChange('empresa');}} | ||
|  |                             disabled={loading || loadingDropdowns || accionista} | ||
|  |                         > | ||
|  |                             <MenuItem value={0}><em>N/A (Accionista)</em></MenuItem> | ||
|  |                             {empresas.map((e) => (<MenuItem key={e.idEmpresa} value={e.idEmpresa}>{e.nombre}</MenuItem>))} | ||
|  |                         </Select> | ||
|  |                         {localErrors.empresa && <Typography color="error" variant="caption">{localErrors.empresa}</Typography>} | ||
|  |                     </FormControl> | ||
|  |                 </Box> | ||
|  |                 <TextField label="Observaciones" value={obs} | ||
|  |                     onChange={(e) => setObs(e.target.value)} | ||
|  |                     margin="dense" fullWidth multiline rows={2} disabled={loading} sx={{mt:1}} | ||
|  |                 /> | ||
|  |                 <Box sx={{mt:1}}> | ||
|  |                     <FormControlLabel | ||
|  |                         control={<Checkbox checked={accionista} onChange={(e) => { | ||
|  |                             setAccionista(e.target.checked); | ||
|  |                             if (e.target.checked) setEmpresa(0); | ||
|  |                             handleInputChange('accionista'); | ||
|  |                             handleInputChange('empresa'); | ||
|  |                         }} disabled={loading}/>} | ||
|  |                         label="Es Accionista" | ||
|  |                     /> | ||
|  |                 </Box> | ||
|  |             </Box> | ||
|  | 
 | ||
|  |           {errorMessage && <Alert severity="error" sx={{ mt: 2, width: '100%' }}>{errorMessage}</Alert>} | ||
|  |           {localErrors.dropdowns && <Alert severity="warning" sx={{ mt: 1 }}>{localErrors.dropdowns}</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 || loadingDropdowns}> | ||
|  |               {loading ? <CircularProgress size={24} /> : (isEditing ? 'Guardar Cambios' : 'Crear Canillita')} | ||
|  |             </Button> | ||
|  |           </Box> | ||
|  |         </Box> | ||
|  |       </Box> | ||
|  |     </Modal> | ||
|  |   ); | ||
|  | }; | ||
|  | 
 | ||
|  | export default CanillaFormModal; |