Fase 2: Creación del frontend con React, Vite y MUI. Implementada tabla de titulares con funcionalidad de arrastrar y soltar.
This commit is contained in:
119
frontend/src/components/TablaTitulares.tsx
Normal file
119
frontend/src/components/TablaTitulares.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
// frontend/src/components/TablaTitulares.tsx
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Chip, IconButton
|
||||
} from '@mui/material';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
|
||||
import type { Titular } from '../types';
|
||||
import * as api from '../services/apiService';
|
||||
|
||||
// Componente para una fila de tabla "arrastrable"
|
||||
const SortableRow = ({ titular }: { titular: Titular }) => {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: titular.id });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
const getChipColor = (tipo: Titular['tipo']) => {
|
||||
if (tipo === 'Edited') return 'warning';
|
||||
if (tipo === 'Manual') return 'info';
|
||||
return 'success';
|
||||
};
|
||||
|
||||
return (
|
||||
<TableRow ref={setNodeRef} style={style} {...attributes} {...listeners}>
|
||||
<TableCell>...</TableCell> {/* Handle para arrastrar */}
|
||||
<TableCell>{titular.texto}</TableCell>
|
||||
<TableCell>
|
||||
<Chip label={titular.tipo || 'Scraped'} color={getChipColor(titular.tipo)} size="small" />
|
||||
</TableCell>
|
||||
<TableCell>{titular.fuente}</TableCell>
|
||||
<TableCell>
|
||||
<IconButton size="small" onClick={() => console.log('Eliminar:', titular.id)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Componente principal de la tabla
|
||||
const TablaTitulares = () => {
|
||||
const [titulares, setTitulares] = useState<Titular[]>([]);
|
||||
|
||||
// Sensores para dnd-kit: reaccionar a clics de puntero
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
const cargarTitulares = async () => {
|
||||
try {
|
||||
const data = await api.obtenerTitulares();
|
||||
setTitulares(data);
|
||||
} catch (error) {
|
||||
console.error("Error al cargar titulares:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
cargarTitulares();
|
||||
}, []);
|
||||
|
||||
const handleDragEnd = (event: any) => {
|
||||
const { active, over } = event;
|
||||
if (active.id !== over.id) {
|
||||
setTitulares((items) => {
|
||||
const oldIndex = items.findIndex((item) => item.id === active.id);
|
||||
const newIndex = items.findIndex((item) => item.id === over.id);
|
||||
const newArray = arrayMove(items, oldIndex, newIndex);
|
||||
|
||||
// Creamos el payload para la API
|
||||
const payload = newArray.map((item, index) => ({
|
||||
id: item.id,
|
||||
nuevoOrden: index
|
||||
}));
|
||||
|
||||
// Llamada a la API en segundo plano
|
||||
api.actualizarOrdenTitulares(payload).catch(err => {
|
||||
console.error("Error al reordenar:", err);
|
||||
// Opcional: revertir el estado si la API falla
|
||||
});
|
||||
|
||||
return newArray;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext items={titulares.map(t => t.id)} strategy={verticalListSortingStrategy}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{ width: 50 }}></TableCell> {/* Celda para el drag handle */}
|
||||
<TableCell>Texto del Titular</TableCell>
|
||||
<TableCell>Tipo</TableCell>
|
||||
<TableCell>Fuente</TableCell>
|
||||
<TableCell>Acciones</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{titulares.map((titular) => (
|
||||
<SortableRow key={titular.id} titular={titular} />
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default TablaTitulares;
|
||||
Reference in New Issue
Block a user