Feat: Estetico
- Se aplican estilos al resto de controles por uniformidad.
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
// EN: src/components/AdminPanel.tsx
|
// EN: src/components/AdminPanel.tsx
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Typography, AppBar, Toolbar, IconButton, Tabs, Tab } from '@mui/material';
|
import { Box, Typography, AppBar, Toolbar, IconButton, Tabs, Tab, Container } from '@mui/material';
|
||||||
import LogoutIcon from '@mui/icons-material/Logout';
|
import LogoutIcon from '@mui/icons-material/Logout';
|
||||||
|
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
||||||
|
|
||||||
import ContextManager from './ContextManager';
|
import ContextManager from './ContextManager';
|
||||||
import LogsViewer from './LogsViewer';
|
import LogsViewer from './LogsViewer';
|
||||||
import SourceManager from './SourceManager';
|
import SourceManager from './SourceManager';
|
||||||
|
|
||||||
import SystemPromptManager from './SystemPromptManager';
|
import SystemPromptManager from './SystemPromptManager';
|
||||||
|
|
||||||
interface AdminPanelProps {
|
interface AdminPanelProps {
|
||||||
@@ -21,28 +21,57 @@ const AdminPanel: React.FC<AdminPanelProps> = ({ onLogout }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||||
<AppBar position="static">
|
<AppBar
|
||||||
<Toolbar>
|
position="sticky"
|
||||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
elevation={0}
|
||||||
Panel de Administración del Chatbot
|
sx={{
|
||||||
</Typography>
|
background: 'rgba(18, 18, 18, 0.8)',
|
||||||
<IconButton color="inherit" onClick={onLogout} aria-label="Cerrar sesión">
|
backdropFilter: 'blur(12px)',
|
||||||
<LogoutIcon />
|
borderBottom: '1px solid rgba(255,255,255,0.08)'
|
||||||
</IconButton>
|
}}
|
||||||
</Toolbar>
|
>
|
||||||
<Tabs value={currentTab} onChange={handleTabChange} textColor="inherit" indicatorColor="secondary">
|
<Container maxWidth="xl">
|
||||||
<Tab label="Gestor de Contexto" />
|
<Toolbar disableGutters>
|
||||||
<Tab label="Historial de Conversaciones" />
|
<SmartToyIcon sx={{ mr: 2, color: 'primary.main' }} />
|
||||||
<Tab label="Gestor de Fuentes" />
|
<Typography
|
||||||
<Tab label="Gestor de Prompts" />
|
variant="h6"
|
||||||
</Tabs>
|
component="div"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
fontWeight: 700,
|
||||||
|
letterSpacing: '-0.5px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Chatbot Admin
|
||||||
|
</Typography>
|
||||||
|
<IconButton onClick={onLogout} color="error" title="Cerrar sesión">
|
||||||
|
<LogoutIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
<Tabs
|
||||||
|
value={currentTab}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
textColor="inherit"
|
||||||
|
indicatorColor="secondary"
|
||||||
|
sx={{ minHeight: '48px' }}
|
||||||
|
>
|
||||||
|
<Tab label="Contexto" sx={{ fontWeight: 600 }} />
|
||||||
|
<Tab label="Historial" sx={{ fontWeight: 600 }} />
|
||||||
|
<Tab label="Fuentes" sx={{ fontWeight: 600 }} />
|
||||||
|
<Tab label="Prompts / IA" sx={{ fontWeight: 600 }} />
|
||||||
|
</Tabs>
|
||||||
|
</Container>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
{currentTab === 0 && <ContextManager onAuthError={onLogout} />}
|
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||||
{currentTab === 1 && <LogsViewer onAuthError={onLogout} />}
|
<Container maxWidth="xl">
|
||||||
{currentTab === 2 && <SourceManager onAuthError={onLogout} />}
|
{currentTab === 0 && <ContextManager onAuthError={onLogout} />}
|
||||||
{currentTab === 3 && <SystemPromptManager onAuthError={onLogout} />}
|
{currentTab === 1 && <LogsViewer onAuthError={onLogout} />}
|
||||||
|
{currentTab === 2 && <SourceManager onAuthError={onLogout} />}
|
||||||
|
{currentTab === 3 && <SystemPromptManager onAuthError={onLogout} />}
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/AdminPanel.tsx
|
// src/components/ContextManager.tsx
|
||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid';
|
import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid';
|
||||||
import type { GridColDef } from '@mui/x-data-grid';
|
import type { GridColDef } from '@mui/x-data-grid';
|
||||||
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Alert, DialogContentText, TextField } from '@mui/material';
|
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Alert, DialogContentText, TextField, Card, Typography } from '@mui/material';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
@@ -122,90 +122,99 @@ const ContextManager: React.FC<ContextManagerProps> = ({ onAuthError }) => {
|
|||||||
icon={<EditIcon />}
|
icon={<EditIcon />}
|
||||||
label="Editar"
|
label="Editar"
|
||||||
onClick={() => handleOpen(params.row as ContextoItem)}
|
onClick={() => handleOpen(params.row as ContextoItem)}
|
||||||
|
color="inherit"
|
||||||
/>,
|
/>,
|
||||||
<GridActionsCellItem
|
<GridActionsCellItem
|
||||||
icon={<DeleteIcon />}
|
icon={<DeleteIcon />}
|
||||||
label="Eliminar"
|
label="Eliminar"
|
||||||
// Llama a la función que abre el diálogo
|
|
||||||
onClick={() => handleDeleteClick(params.id as number)}
|
onClick={() => handleDeleteClick(params.id as number)}
|
||||||
|
color="inherit"
|
||||||
/>,
|
/>,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 4 }}>
|
<Box>
|
||||||
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||||
<Button startIcon={<AddIcon />} variant="contained" onClick={() => handleOpen()} sx={{ mb: 1 }}>
|
<Typography variant="h4">Gestor de Contexto</Typography>
|
||||||
Añadir Nuevo Item
|
<Button
|
||||||
</Button>
|
startIcon={<AddIcon />}
|
||||||
<Box sx={{ height: 600, width: '100%' }}>
|
variant="contained"
|
||||||
<DataGrid rows={rows} columns={columns} pageSizeOptions={[10, 25, 50, 100]} />
|
color="secondary"
|
||||||
</Box>
|
onClick={() => handleOpen()}
|
||||||
<Box sx={{ p: 4 }}>
|
|
||||||
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
|
||||||
<Button startIcon={<AddIcon />} variant="contained" onClick={() => handleOpen()} sx={{ mb: 1 }}>
|
|
||||||
Añadir Nuevo Item
|
|
||||||
</Button>
|
|
||||||
<Box sx={{ height: 600, width: '100%' }}>
|
|
||||||
<DataGrid rows={rows} columns={columns} pageSizeOptions={[10, 25, 50, 100]} />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* --- DIÁLOGO DE EDICIÓN/CREACIÓN --- */}
|
|
||||||
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
|
|
||||||
<DialogTitle>{isEdit ? 'Editar Item' : 'Añadir Nuevo Item'}</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<TextField
|
|
||||||
margin="dense"
|
|
||||||
label="Clave"
|
|
||||||
fullWidth
|
|
||||||
value={currentRow.clave || ''}
|
|
||||||
disabled={isEdit} // La clave no se puede editar una vez creada
|
|
||||||
onChange={(e) => setCurrentRow({ ...currentRow, clave: e.target.value })}
|
|
||||||
helperText={!isEdit ? "Esta clave no se podrá modificar en el futuro." : ""}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
margin="dense"
|
|
||||||
label="Valor"
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
rows={4}
|
|
||||||
value={currentRow.valor || ''}
|
|
||||||
onChange={(e) => setCurrentRow({ ...currentRow, valor: e.target.value })}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
margin="dense"
|
|
||||||
label="Descripción"
|
|
||||||
fullWidth
|
|
||||||
value={currentRow.descripcion || ''}
|
|
||||||
onChange={(e) => setCurrentRow({ ...currentRow, descripcion: e.target.value })}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleClose}>Cancelar</Button>
|
|
||||||
<Button onClick={handleSave} variant="contained">Guardar</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* --- DIÁLOGO DE CONFIRMACIÓN DE BORRADO --- */}
|
|
||||||
<Dialog
|
|
||||||
open={confirmOpen}
|
|
||||||
onClose={handleConfirmClose}
|
|
||||||
>
|
>
|
||||||
<DialogTitle>Confirmar Eliminación</DialogTitle>
|
Nuevo Item
|
||||||
<DialogContent>
|
</Button>
|
||||||
<DialogContentText>
|
|
||||||
¿Estás seguro de que quieres eliminar este item? Esta acción no se puede deshacer.
|
|
||||||
</DialogContentText>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleConfirmClose}>Cancelar</Button>
|
|
||||||
<Button onClick={handleConfirmDelete} color="error" variant="contained">
|
|
||||||
Eliminar
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<Box sx={{ height: 600, width: '100%' }}>
|
||||||
|
<DataGrid
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
pageSizeOptions={[10, 25, 50, 100]}
|
||||||
|
sx={{ border: 'none' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* --- DIÁLOGO DE EDICIÓN/CREACIÓN --- */}
|
||||||
|
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle>{isEdit ? 'Editar Item' : 'Añadir Nuevo Item'}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
margin="dense"
|
||||||
|
label="Clave"
|
||||||
|
fullWidth
|
||||||
|
value={currentRow.clave || ''}
|
||||||
|
disabled={isEdit} // La clave no se puede editar una vez creada
|
||||||
|
onChange={(e) => setCurrentRow({ ...currentRow, clave: e.target.value })}
|
||||||
|
helperText={!isEdit ? "Esta clave no se podrá modificar en el futuro." : ""}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
margin="dense"
|
||||||
|
label="Valor"
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
value={currentRow.valor || ''}
|
||||||
|
onChange={(e) => setCurrentRow({ ...currentRow, valor: e.target.value })}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
margin="dense"
|
||||||
|
label="Descripción"
|
||||||
|
fullWidth
|
||||||
|
value={currentRow.descripcion || ''}
|
||||||
|
onChange={(e) => setCurrentRow({ ...currentRow, descripcion: e.target.value })}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose}>Cancelar</Button>
|
||||||
|
<Button onClick={handleSave} variant="contained">Guardar</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
{/* --- DIÁLOGO DE CONFIRMACIÓN DE BORRADO --- */}
|
||||||
|
<Dialog
|
||||||
|
open={confirmOpen}
|
||||||
|
onClose={handleConfirmClose}
|
||||||
|
>
|
||||||
|
<DialogTitle>Confirmar Eliminación</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
¿Estás seguro de que quieres eliminar este item? Esta acción no se puede deshacer.
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleConfirmClose}>Cancelar</Button>
|
||||||
|
<Button onClick={handleConfirmDelete} color="error" variant="contained">
|
||||||
|
Eliminar
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// src/components/Login.tsx
|
// src/components/Login.tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Button, TextField, Typography, Paper, Alert } from '@mui/material';
|
import { Box, Button, TextField, Typography, Card, CardContent, Alert } from '@mui/material';
|
||||||
|
import apiClient from '../api/apiClient';
|
||||||
|
|
||||||
interface LoginProps {
|
interface LoginProps {
|
||||||
onLoginSuccess: (token: string) => void;
|
onLoginSuccess: (token: string) => void;
|
||||||
@@ -10,70 +11,94 @@ const Login: React.FC<LoginProps> = ({ onLoginSuccess }) => {
|
|||||||
const [username, setUsername] = React.useState('');
|
const [username, setUsername] = React.useState('');
|
||||||
const [password, setPassword] = React.useState('');
|
const [password, setPassword] = React.useState('');
|
||||||
const [error, setError] = React.useState('');
|
const [error, setError] = React.useState('');
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
const handleLogin = async (event: React.FormEvent) => {
|
const handleLogin = async (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setError('');
|
setError('');
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Usamos la variable de entorno para la URL de la API
|
// Using apiClient ensures consistently handled headers/configs
|
||||||
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/auth/login`, {
|
const response = await apiClient.post('/api/auth/login', { username, password });
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ username, password }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
// Axios returns data directly in response.data
|
||||||
const data = await response.json();
|
if (response.data && response.data.token) {
|
||||||
onLoginSuccess(data.token);
|
onLoginSuccess(response.data.token);
|
||||||
} else {
|
} else {
|
||||||
setError('Usuario o contraseña incorrectos.');
|
setError('Respuesta inesperada del servidor.');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
setError('No se pudo conectar con el servidor. Inténtalo de nuevo más tarde.');
|
console.error(err);
|
||||||
|
if (err.response && err.response.status === 401) {
|
||||||
|
setError('Usuario o contraseña incorrectos.');
|
||||||
|
} else {
|
||||||
|
setError('No se pudo conectar con el servidor.');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
<Box sx={{
|
||||||
<Paper elevation={6} sx={{ padding: 4, width: '100%', maxWidth: 400 }}>
|
display: 'flex',
|
||||||
<Typography variant="h4" component="h1" gutterBottom textAlign="center">
|
justifyContent: 'center',
|
||||||
Iniciar Sesión
|
alignItems: 'center',
|
||||||
</Typography>
|
height: '100vh',
|
||||||
<Typography variant="body2" textAlign="center" sx={{ mb: 3 }}>
|
background: 'radial-gradient(circle at 50% 50%, #1a1a1a 0%, #000 100%)' // Fallback/Enhancement
|
||||||
Gestor de Contexto del Chatbot
|
}}>
|
||||||
</Typography>
|
<Card sx={{ maxWidth: 450, width: '100%', mx: 2 }}>
|
||||||
<form onSubmit={handleLogin}>
|
<CardContent sx={{ p: 4, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
<TextField
|
<Box sx={{ textAlign: 'center', mb: 1 }}>
|
||||||
label="Usuario"
|
<Typography variant="h4" gutterBottom>
|
||||||
variant="outlined"
|
Bienvenido
|
||||||
fullWidth
|
</Typography>
|
||||||
margin="normal"
|
<Typography variant="body2" color="text.secondary">
|
||||||
value={username}
|
Inicia sesión para gestionar el Chatbot
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
</Typography>
|
||||||
autoFocus
|
</Box>
|
||||||
/>
|
|
||||||
<TextField
|
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||||
label="Contraseña"
|
<TextField
|
||||||
type="password"
|
label="Usuario"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="normal"
|
value={username}
|
||||||
value={password}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
autoFocus
|
||||||
/>
|
sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}
|
||||||
{error && <Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>}
|
/>
|
||||||
<Button
|
<TextField
|
||||||
type="submit"
|
label="Contraseña"
|
||||||
variant="contained"
|
type="password"
|
||||||
fullWidth
|
variant="outlined"
|
||||||
size="large"
|
fullWidth
|
||||||
sx={{ mt: 3 }}
|
value={password}
|
||||||
>
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
Entrar
|
sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}
|
||||||
</Button>
|
/>
|
||||||
</form>
|
|
||||||
</Paper>
|
{error && <Alert severity="error">{error}</Alert>}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
fullWidth
|
||||||
|
size="large"
|
||||||
|
disabled={loading}
|
||||||
|
sx={{
|
||||||
|
mt: 1,
|
||||||
|
py: 1.5,
|
||||||
|
background: 'linear-gradient(45deg, #7c4dff 30%, #ff4081 90%)',
|
||||||
|
fontSize: '1rem'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading ? 'Entrando...' : 'Iniciar Sesión'}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { DataGrid, type GridColDef } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef } from '@mui/x-data-grid';
|
||||||
import { Box, Typography, Alert } from '@mui/material';
|
import { Box, Typography, Alert, Card } from '@mui/material';
|
||||||
import apiClient from '../api/apiClient';
|
import apiClient from '../api/apiClient';
|
||||||
|
|
||||||
interface ConversacionLog {
|
interface ConversacionLog {
|
||||||
@@ -66,23 +66,28 @@ const LogsViewer: React.FC<LogsViewerProps> = ({ onAuthError }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 4 }}>
|
<Box>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Box sx={{ mb: 3 }}>
|
||||||
Historial de Conversaciones
|
<Typography variant="h4">Historial de Conversaciones</Typography>
|
||||||
</Typography>
|
|
||||||
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
|
||||||
<Box sx={{ height: 700, width: '100%' }}>
|
|
||||||
<DataGrid
|
|
||||||
rows={logs}
|
|
||||||
columns={columns}
|
|
||||||
pageSizeOptions={[25, 50, 100]}
|
|
||||||
initialState={{
|
|
||||||
sorting: {
|
|
||||||
sortModel: [{ field: 'fecha', sort: 'desc' }],
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<Box sx={{ height: 700, width: '100%' }}>
|
||||||
|
<DataGrid
|
||||||
|
rows={logs}
|
||||||
|
columns={columns}
|
||||||
|
pageSizeOptions={[25, 50, 100]}
|
||||||
|
initialState={{
|
||||||
|
sorting: {
|
||||||
|
sortModel: [{ field: 'fecha', sort: 'desc' }],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={{ border: 'none' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback } from 'react';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid';
|
import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid';
|
||||||
import type { GridColDef } from '@mui/x-data-grid';
|
import type { GridColDef } from '@mui/x-data-grid';
|
||||||
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Alert, TextField, Chip, Switch, FormControlLabel } from '@mui/material';
|
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Alert, TextField, Chip, Switch, FormControlLabel, Card, Typography } from '@mui/material';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
@@ -109,7 +109,12 @@ const SourceManager: React.FC<SourceManagerProps> = ({ onAuthError }) => {
|
|||||||
headerName: 'Activo',
|
headerName: 'Activo',
|
||||||
width: 100,
|
width: 100,
|
||||||
renderCell: (params) => (
|
renderCell: (params) => (
|
||||||
<Chip label={params.value ? 'Sí' : 'No'} color={params.value ? 'success' : 'default'} />
|
<Chip
|
||||||
|
label={params.value ? 'Sí' : 'No'}
|
||||||
|
color={params.value ? 'success' : 'default'}
|
||||||
|
variant={params.value ? 'filled' : 'outlined'}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -121,25 +126,45 @@ const SourceManager: React.FC<SourceManagerProps> = ({ onAuthError }) => {
|
|||||||
icon={<EditIcon />}
|
icon={<EditIcon />}
|
||||||
label="Editar"
|
label="Editar"
|
||||||
onClick={() => handleOpen(params.row as FuenteContexto)}
|
onClick={() => handleOpen(params.row as FuenteContexto)}
|
||||||
|
color="inherit"
|
||||||
/>,
|
/>,
|
||||||
<GridActionsCellItem
|
<GridActionsCellItem
|
||||||
icon={<DeleteIcon />}
|
icon={<DeleteIcon />}
|
||||||
label="Eliminar"
|
label="Eliminar"
|
||||||
onClick={() => handleDeleteClick(params.id as number)}
|
onClick={() => handleDeleteClick(params.id as number)}
|
||||||
|
color="inherit" // Keeping it generic to avoid type errors
|
||||||
/>,
|
/>,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ p: 4 }}>
|
<Box>
|
||||||
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||||
<Button startIcon={<AddIcon />} variant="contained" onClick={() => handleOpen()} sx={{ mb: 2 }}>
|
<Typography variant="h4">Gestor de Fuentes</Typography>
|
||||||
Añadir Nueva Fuente
|
<Button
|
||||||
</Button>
|
startIcon={<AddIcon />}
|
||||||
<Box sx={{ height: 600, width: '100%' }}>
|
variant="contained"
|
||||||
<DataGrid rows={rows} columns={columns} pageSizeOptions={[10, 25, 50]} />
|
color="secondary"
|
||||||
|
onClick={() => handleOpen()}
|
||||||
|
>
|
||||||
|
Nueva Fuente
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<Box sx={{ height: 600, width: '100%' }}>
|
||||||
|
<DataGrid
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
pageSizeOptions={[10, 25, 50]}
|
||||||
|
sx={{ border: 'none' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="md">
|
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="md">
|
||||||
<DialogTitle>{isEdit ? 'Editar Fuente' : 'Añadir Nueva Fuente'}</DialogTitle>
|
<DialogTitle>{isEdit ? 'Editar Fuente' : 'Añadir Nueva Fuente'}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@@ -186,6 +211,7 @@ const SourceManager: React.FC<SourceManagerProps> = ({ onAuthError }) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Fuente activa"
|
label="Fuente activa"
|
||||||
|
sx={{ mt: 2 }}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
Reference in New Issue
Block a user