196 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			196 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | // src/pages/Distribucion/GestionarDistribuidoresPage.tsx
 | ||
|  | import React, { useState, useEffect, useCallback } from 'react'; | ||
|  | import { | ||
|  |     Box, Typography, TextField, Button, Paper, IconButton, Menu, MenuItem, | ||
|  |     Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination, | ||
|  |     CircularProgress, Alert | ||
|  | } from '@mui/material'; | ||
|  | import AddIcon from '@mui/icons-material/Add'; | ||
|  | import MoreVertIcon from '@mui/icons-material/MoreVert'; | ||
|  | import distribuidorService from '../../services/Distribucion/distribuidorService'; | ||
|  | import type { DistribuidorDto } from '../../models/dtos/Distribucion/DistribuidorDto'; | ||
|  | import type { CreateDistribuidorDto } from '../../models/dtos/Distribucion/CreateDistribuidorDto'; | ||
|  | import type { UpdateDistribuidorDto } from '../../models/dtos/Distribucion/UpdateDistribuidorDto'; | ||
|  | import DistribuidorFormModal from '../../components/Modals/Distribucion/DistribuidorFormModal'; | ||
|  | import { usePermissions } from '../../hooks/usePermissions'; | ||
|  | import axios from 'axios'; | ||
|  | 
 | ||
|  | 
 | ||
|  | const GestionarDistribuidoresPage: React.FC = () => { | ||
|  |   const [distribuidores, setDistribuidores] = useState<DistribuidorDto[]>([]); | ||
|  |   const [loading, setLoading] = useState(true); | ||
|  |   const [error, setError] = useState<string | null>(null); | ||
|  |   const [filtroNombre, setFiltroNombre] = useState(''); | ||
|  |   const [filtroNroDoc, setFiltroNroDoc] = useState(''); | ||
|  | 
 | ||
|  |   const [modalOpen, setModalOpen] = useState(false); | ||
|  |   const [editingDistribuidor, setEditingDistribuidor] = useState<DistribuidorDto | null>(null); | ||
|  |   const [apiErrorMessage, setApiErrorMessage] = useState<string | null>(null); | ||
|  | 
 | ||
|  |   const [page, setPage] = useState(0); | ||
|  |   const [rowsPerPage, setRowsPerPage] = useState(5); | ||
|  |   const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); | ||
|  |   const [selectedDistribuidorRow, setSelectedDistribuidorRow] = useState<DistribuidorDto | null>(null); | ||
|  | 
 | ||
|  |   const { tienePermiso, isSuperAdmin } = usePermissions(); | ||
|  | 
 | ||
|  |   const puedeVer = isSuperAdmin || tienePermiso("DG001"); | ||
|  |   const puedeCrear = isSuperAdmin || tienePermiso("DG002"); | ||
|  |   const puedeModificar = isSuperAdmin || tienePermiso("DG003"); | ||
|  |   const puedeEliminar = isSuperAdmin || tienePermiso("DG005"); | ||
|  | 
 | ||
|  |   const cargarDistribuidores = useCallback(async () => { | ||
|  |     if (!puedeVer) { | ||
|  |       setError("No tiene permiso para ver esta sección."); | ||
|  |       setLoading(false); | ||
|  |       return; | ||
|  |     } | ||
|  |     setLoading(true); setError(null); setApiErrorMessage(null); | ||
|  |     try { | ||
|  |       const data = await distribuidorService.getAllDistribuidores(filtroNombre, filtroNroDoc); | ||
|  |       setDistribuidores(data); | ||
|  |     } catch (err) { | ||
|  |       console.error(err); setError('Error al cargar los distribuidores.'); | ||
|  |     } finally { setLoading(false); } | ||
|  |   }, [filtroNombre, filtroNroDoc, puedeVer]); | ||
|  | 
 | ||
|  |   useEffect(() => { cargarDistribuidores(); }, [cargarDistribuidores]); | ||
|  | 
 | ||
|  |   const handleOpenModal = (distribuidor?: DistribuidorDto) => { | ||
|  |     setEditingDistribuidor(distribuidor || null); setApiErrorMessage(null); setModalOpen(true); | ||
|  |   }; | ||
|  |   const handleCloseModal = () => { | ||
|  |     setModalOpen(false); setEditingDistribuidor(null); | ||
|  |   }; | ||
|  | 
 | ||
|  |   const handleSubmitModal = async (data: CreateDistribuidorDto | UpdateDistribuidorDto, id?: number) => { | ||
|  |     setApiErrorMessage(null); | ||
|  |     try { | ||
|  |       if (id && editingDistribuidor) { | ||
|  |         await distribuidorService.updateDistribuidor(id, data as UpdateDistribuidorDto); | ||
|  |       } else { | ||
|  |         await distribuidorService.createDistribuidor(data as CreateDistribuidorDto); | ||
|  |       } | ||
|  |       cargarDistribuidores(); | ||
|  |     } catch (err: any) { | ||
|  |       const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al guardar el distribuidor.'; | ||
|  |       setApiErrorMessage(message); throw err; | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   const handleDelete = async (id: number) => { | ||
|  |     if (window.confirm(`¿Está seguro de eliminar este distribuidor (ID: ${id})?`)) { | ||
|  |        setApiErrorMessage(null); | ||
|  |        try { | ||
|  |         await distribuidorService.deleteDistribuidor(id); | ||
|  |         cargarDistribuidores(); | ||
|  |       } catch (err: any) { | ||
|  |          const message = axios.isAxiosError(err) && err.response?.data?.message ? err.response.data.message : 'Error al eliminar el distribuidor.'; | ||
|  |          setApiErrorMessage(message); | ||
|  |       } | ||
|  |     } | ||
|  |     handleMenuClose(); | ||
|  |   }; | ||
|  | 
 | ||
|  |   const handleMenuOpen = (event: React.MouseEvent<HTMLElement>, distribuidor: DistribuidorDto) => { | ||
|  |     setAnchorEl(event.currentTarget); setSelectedDistribuidorRow(distribuidor); | ||
|  |   }; | ||
|  |   const handleMenuClose = () => { | ||
|  |     setAnchorEl(null); setSelectedDistribuidorRow(null); | ||
|  |   }; | ||
|  | 
 | ||
|  |   const handleChangePage = (_event: unknown, newPage: number) => setPage(newPage); | ||
|  |   const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
|  |     setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); | ||
|  |   }; | ||
|  |   const displayData = distribuidores.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); | ||
|  | 
 | ||
|  |   if (!loading && !puedeVer) { | ||
|  |       return <Box sx={{ p: 2 }}><Alert severity="error">{error || "No tiene permiso."}</Alert></Box>; | ||
|  |   } | ||
|  | 
 | ||
|  |   return ( | ||
|  |     <Box sx={{ p: 2 }}> | ||
|  |       <Typography variant="h4" gutterBottom>Gestionar Distribuidores</Typography> | ||
|  |       <Paper sx={{ p: 2, mb: 2 }}> | ||
|  |          <Box sx={{ display: 'flex', gap: 2, mb: 2, flexWrap: 'wrap' }}> | ||
|  |             <TextField | ||
|  |                 label="Filtrar por Nombre" | ||
|  |                 variant="outlined" | ||
|  |                 size="small" | ||
|  |                 value={filtroNombre} | ||
|  |                 onChange={(e) => setFiltroNombre(e.target.value)} | ||
|  |                 sx={{ flexGrow: 1, minWidth: '200px' }} | ||
|  |             /> | ||
|  |             <TextField | ||
|  |                 label="Filtrar por Nro. Doc." | ||
|  |                 variant="outlined" | ||
|  |                 size="small" | ||
|  |                 value={filtroNroDoc} | ||
|  |                 onChange={(e) => setFiltroNroDoc(e.target.value)} | ||
|  |                 sx={{ flexGrow: 1, minWidth: '200px' }} | ||
|  |             /> | ||
|  |             {/* <Button variant="contained" onClick={cargarDistribuidores} size="small">Buscar</Button> */} | ||
|  |          </Box> | ||
|  |          {puedeCrear && ( | ||
|  |           <Box sx={{ display: 'flex', justifyContent: 'flex-end', mb: 2 }}> | ||
|  |           <Button variant="contained" startIcon={<AddIcon />} onClick={() => handleOpenModal()} sx={{ mb: 2 }}>Agregar Distribuidor</Button> | ||
|  |           </Box> | ||
|  |           )} | ||
|  |       </Paper> | ||
|  | 
 | ||
|  |       {loading && <Box sx={{ display: 'flex', justifyContent: 'center', my: 2 }}><CircularProgress /></Box>} | ||
|  |       {error && !loading && <Alert severity="error" sx={{my: 2}}>{error}</Alert>} | ||
|  |       {apiErrorMessage && <Alert severity="error" sx={{my: 2}}>{apiErrorMessage}</Alert>} | ||
|  | 
 | ||
|  |       {!loading && !error && puedeVer && ( | ||
|  |          <TableContainer component={Paper}> | ||
|  |            <Table size="small"> | ||
|  |              <TableHead><TableRow> | ||
|  |                  <TableCell>Nombre</TableCell><TableCell>Nro. Doc.</TableCell> | ||
|  |                  <TableCell>Contacto</TableCell><TableCell>Zona</TableCell> | ||
|  |                  <TableCell>Teléfono</TableCell><TableCell>Localidad</TableCell> | ||
|  |                  {(puedeModificar || puedeEliminar) && <TableCell align="right">Acciones</TableCell>} | ||
|  |              </TableRow></TableHead> | ||
|  |              <TableBody> | ||
|  |                {displayData.length === 0 ? ( | ||
|  |                   <TableRow><TableCell colSpan={7} align="center">No se encontraron distribuidores.</TableCell></TableRow> | ||
|  |                ) : ( | ||
|  |                  displayData.map((d) => ( | ||
|  |                      <TableRow key={d.idDistribuidor} hover> | ||
|  |                      <TableCell>{d.nombre}</TableCell><TableCell>{d.nroDoc}</TableCell> | ||
|  |                      <TableCell>{d.contacto || '-'}</TableCell><TableCell>{d.nombreZona || '-'}</TableCell> | ||
|  |                      <TableCell>{d.telefono || '-'}</TableCell><TableCell>{d.localidad || '-'}</TableCell> | ||
|  |                      {(puedeModificar || puedeEliminar) && ( | ||
|  |                         <TableCell align="right"> | ||
|  |                             <IconButton onClick={(e) => handleMenuOpen(e, d)} disabled={!puedeModificar && !puedeEliminar}><MoreVertIcon /></IconButton> | ||
|  |                         </TableCell> | ||
|  |                      )} | ||
|  |                      </TableRow> | ||
|  |                  )))} | ||
|  |              </TableBody> | ||
|  |            </Table> | ||
|  |            <TablePagination | ||
|  |              rowsPerPageOptions={[5, 10, 25]} component="div" count={distribuidores.length} | ||
|  |              rowsPerPage={rowsPerPage} page={page} onPageChange={handleChangePage} | ||
|  |              onRowsPerPageChange={handleChangeRowsPerPage} labelRowsPerPage="Filas por página:" | ||
|  |            /> | ||
|  |          </TableContainer> | ||
|  |        )} | ||
|  | 
 | ||
|  |       <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> | ||
|  |         {puedeModificar && (<MenuItem onClick={() => { handleOpenModal(selectedDistribuidorRow!); handleMenuClose(); }}>Modificar</MenuItem>)} | ||
|  |         {puedeEliminar && (<MenuItem onClick={() => handleDelete(selectedDistribuidorRow!.idDistribuidor)}>Eliminar</MenuItem>)} | ||
|  |         {(!puedeModificar && !puedeEliminar) && <MenuItem disabled>Sin acciones</MenuItem>} | ||
|  |       </Menu> | ||
|  | 
 | ||
|  |       <DistribuidorFormModal | ||
|  |         open={modalOpen} onClose={handleCloseModal} onSubmit={handleSubmitModal} | ||
|  |         initialData={editingDistribuidor} errorMessage={apiErrorMessage} | ||
|  |         clearErrorMessage={() => setApiErrorMessage(null)} | ||
|  |       /> | ||
|  |     </Box> | ||
|  |   ); | ||
|  | }; | ||
|  | 
 | ||
|  | export default GestionarDistribuidoresPage; |