diff --git a/frontend/src/components/GestionComponentes.tsx b/frontend/src/components/GestionComponentes.tsx
index 09436ca..95636bc 100644
--- a/frontend/src/components/GestionComponentes.tsx
+++ b/frontend/src/components/GestionComponentes.tsx
@@ -13,6 +13,7 @@ import { Pencil, Trash2 } from 'lucide-react';
import styles from './SimpleTable.module.css';
import { adminService } from '../services/apiService';
import TableSkeleton from './TableSkeleton';
+import { accentInsensitiveFilter } from '../utils/filtering';
// Interfaces
interface TextValue {
@@ -158,23 +159,14 @@ const GestionComponentes = () => {
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
+ filterFns: {
+ accentInsensitive: accentInsensitiveFilter,
+ },
+ globalFilterFn: 'accentInsensitive',
});
- // CORRECCIÓN: Añadimos el tipo React.CSSProperties
- const tableContainerStyle: React.CSSProperties = {
- overflowX: 'auto',
- border: '1px solid #dee2e6',
- borderRadius: '8px',
- marginTop: '1rem'
- };
-
- // CORRECCIÓN: Añadimos el tipo React.CSSProperties
- const tableStyle: React.CSSProperties = {
- minWidth: 'auto'
- };
-
return (
-
+
Gestión de Componentes Maestros ({table.getFilteredRowModel().rows.length})
Unifica valores inconsistentes y elimina registros no utilizados.
** La tabla permite ordenar por múltiples columnas manteniendo shift al hacer click en la cabecera. **
@@ -199,12 +191,12 @@ const GestionComponentes = () => {
{isLoading ? (
-
+
) : (
-
-
+
+
{table.getHeaderGroups().map(headerGroup => (
diff --git a/frontend/src/components/GestionSectores.tsx b/frontend/src/components/GestionSectores.tsx
index 4c62e2d..358c654 100644
--- a/frontend/src/components/GestionSectores.tsx
+++ b/frontend/src/components/GestionSectores.tsx
@@ -1,12 +1,24 @@
// frontend/src/components/GestionSectores.tsx
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useMemo, useCallback } from 'react';
import toast from 'react-hot-toast';
+import {
+ useReactTable,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getSortedRowModel,
+ flexRender,
+ type SortingState,
+ type ColumnDef,
+} from '@tanstack/react-table';
+import { PlusCircle, Pencil, Trash2 } from 'lucide-react';
+import { accentInsensitiveFilter } from '../utils/filtering';
+
import type { Sector } from '../types/interfaces';
import styles from './SimpleTable.module.css';
import ModalSector from './ModalSector';
+import TableSkeleton from './TableSkeleton'; // <-- 1. Importar el esqueleto
import { sectorService } from '../services/apiService';
-import { PlusCircle, Pencil, Trash2 } from 'lucide-react';
const GestionSectores = () => {
const [sectores, setSectores] = useState([]);
@@ -14,10 +26,17 @@ const GestionSectores = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingSector, setEditingSector] = useState(null);
+ // --- 2. Estados para filtro y ordenación ---
+ const [globalFilter, setGlobalFilter] = useState('');
+ const [sorting, setSorting] = useState([]);
+
useEffect(() => {
+ setIsLoading(true); // Aseguramos que se muestre el esqueleto al cargar
sectorService.getAll()
.then(data => {
- setSectores(data);
+ // Ordenar alfabéticamente por defecto
+ const sectoresOrdenados = [...data].sort((a, b) => a.nombre.localeCompare(b.nombre, 'es', { sensitivity: 'base' }));
+ setSectores(sectoresOrdenados);
})
.catch(err => {
toast.error("No se pudieron cargar los sectores.");
@@ -33,36 +52,38 @@ const GestionSectores = () => {
setIsModalOpen(true);
};
- const handleOpenEditModal = (sector: Sector) => {
+ const handleOpenEditModal = useCallback((sector: Sector) => {
setEditingSector(sector);
setIsModalOpen(true);
- };
+ }, []);
const handleSave = async (id: number | null, nombre: string) => {
const isEditing = id !== null;
const toastId = toast.loading(isEditing ? 'Actualizando...' : 'Creando...');
try {
+ let refreshedData;
if (isEditing) {
await sectorService.update(id, nombre);
- setSectores(prev => prev.map(s => s.id === id ? { ...s, nombre } : s));
+ refreshedData = await sectorService.getAll();
toast.success('Sector actualizado.', { id: toastId });
} else {
- const nuevoSector = await sectorService.create(nombre);
- setSectores(prev => [...prev, nuevoSector]);
+ await sectorService.create(nombre);
+ refreshedData = await sectorService.getAll();
toast.success('Sector creado.', { id: toastId });
}
+ const sectoresOrdenados = [...refreshedData].sort((a, b) => a.nombre.localeCompare(b.nombre, 'es', { sensitivity: 'base' }));
+ setSectores(sectoresOrdenados);
setIsModalOpen(false);
} catch (error) {
if (error instanceof Error) toast.error(error.message, { id: toastId });
}
};
- const handleDelete = async (id: number) => {
+ const handleDelete = useCallback(async (id: number) => {
if (!window.confirm("¿Estás seguro de eliminar este sector? Los equipos asociados quedarán sin sector.")) {
return;
}
-
const toastId = toast.loading('Eliminando...');
try {
await sectorService.delete(id);
@@ -71,43 +92,117 @@ const GestionSectores = () => {
} catch (error) {
if (error instanceof Error) toast.error(error.message, { id: toastId });
}
- };
+ }, []);
- if (isLoading) {
- return Cargando sectores...
;
- }
+ // --- 3. Definición de columnas para React Table ---
+ const columns = useMemo[]>(() => [
+ {
+ header: 'Nombre del Sector',
+ accessorKey: 'nombre',
+ },
+ {
+ header: 'Acciones',
+ id: 'acciones',
+ cell: ({ row }) => (
+
+
+
+
+ )
+ }
+ ], [handleOpenEditModal, handleDelete]);
+
+ // --- 4. Instancia de la tabla ---
+ const table = useReactTable({
+ data: sectores,
+ columns,
+ state: { sorting, globalFilter },
+ onSortingChange: setSorting,
+ onGlobalFilterChange: setGlobalFilter,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ filterFns: {
+ accentInsensitive: accentInsensitiveFilter,
+ },
+ globalFilterFn: 'accentInsensitive',
+ });
return (
-
-
Gestión de Sectores
-