// EN: src/components/SourceManager.tsx import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import { DataGrid, GridActionsCellItem } 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 EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; import AddIcon from '@mui/icons-material/Add'; import apiClient from '../api/apiClient'; interface FuenteContexto { id: number; nombre: string; url: string; descripcionParaIA: string; activo: boolean; } interface SourceManagerProps { onAuthError: () => void; } const SourceManager: React.FC = ({ onAuthError }) => { const [rows, setRows] = useState([]); const [open, setOpen] = useState(false); const [isEdit, setIsEdit] = useState(false); const [currentRow, setCurrentRow] = useState>({}); const [error, setError] = useState(null); const [confirmOpen, setConfirmOpen] = useState(false); const [itemToDelete, setItemToDelete] = useState(null); const fetchData = useCallback(async () => { try { // --- ENDPOINT --- const response = await apiClient.get('/api/admin/fuentes'); setRows(response.data); } catch (err) { setError('No se pudieron cargar las fuentes de contexto.'); if (axios.isAxiosError(err) && err.response?.status === 401) { onAuthError(); } } }, [onAuthError]); useEffect(() => { fetchData(); }, [fetchData]); const handleOpen = (item?: FuenteContexto) => { if (item) { setIsEdit(true); setCurrentRow(item); } else { // --- ESTADO INICIAL --- setIsEdit(false); setCurrentRow({ nombre: '', url: '', descripcionParaIA: '', activo: true }); } setOpen(true); }; const handleClose = () => setOpen(false); const handleSave = async () => { try { if (isEdit) { await apiClient.put(`/api/admin/fuentes/${currentRow.id}`, currentRow); } else { await apiClient.post('/api/admin/fuentes', currentRow); } fetchData(); handleClose(); } catch (err) { setError('Error al guardar la fuente.'); } }; const handleDeleteClick = (id: number) => { setItemToDelete(id); setConfirmOpen(true); }; const handleConfirmClose = () => { setConfirmOpen(false); setItemToDelete(null); }; const handleConfirmDelete = async () => { if (itemToDelete !== null) { try { await apiClient.delete(`/api/admin/fuentes/${itemToDelete}`); fetchData(); } catch (err) { setError('Error al eliminar la fuente.'); } finally { handleConfirmClose(); } } }; // --- DEFINICIÓN DE COLUMNAS --- const columns: GridColDef[] = [ { field: 'nombre', headerName: 'Nombre', width: 200 }, { field: 'url', headerName: 'URL', width: 350 }, { field: 'descripcionParaIA', headerName: 'Descripción para IA', flex: 1 }, { field: 'activo', headerName: 'Activo', width: 100, renderCell: (params) => ( ), }, { field: 'actions', type: 'actions', width: 100, getActions: (params) => [ } label="Editar" onClick={() => handleOpen(params.row as FuenteContexto)} />, } label="Eliminar" onClick={() => handleDeleteClick(params.id as number)} />, ], }, ]; return ( {error && {error}} {isEdit ? 'Editar Fuente' : 'Añadir Nueva Fuente'} setCurrentRow({ ...currentRow, nombre: e.target.value })} helperText="Un nombre corto y descriptivo (ej: FAQs de Suscripción)." /> setCurrentRow({ ...currentRow, url: e.target.value })} helperText="La URL completa de la página que el bot debe leer." /> setCurrentRow({ ...currentRow, descripcionParaIA: e.target.value })} helperText="¡Crucial! Describe en una frase para qué sirve esta fuente. Ej: 'Usar para responder preguntas sobre cómo registrarse, iniciar sesión o sobre el registro'." /> setCurrentRow({ ...currentRow, activo: e.target.checked })} /> } label="Fuente activa" /> Confirmar Eliminación ¿Estás seguro de que quieres eliminar este item? Esta acción no se puede deshacer. ); }; export default SourceManager;