Feat Modo Oscuro y Otras Estéticas
This commit is contained in:
		| @@ -9,8 +9,10 @@ import { Tooltip } from 'react-tooltip'; | ||||
| import toast from 'react-hot-toast'; | ||||
| import type { Equipo, Sector, Usuario, UsuarioEquipoDetalle } from '../types/interfaces'; | ||||
| import styles from './SimpleTable.module.css'; | ||||
| import skeletonStyles from './Skeleton.module.css'; // Importamos el estilo del esqueleto | ||||
|  | ||||
| import { equipoService, sectorService, usuarioService } from '../services/apiService'; | ||||
| import { PlusCircle, KeyRound, UserX, Pencil, ArrowUp, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; | ||||
|  | ||||
| import ModalAnadirEquipo from './ModalAnadirEquipo'; | ||||
| import ModalEditarSector from './ModalEditarSector'; | ||||
| @@ -19,6 +21,7 @@ import ModalDetallesEquipo from './ModalDetallesEquipo'; | ||||
| import ModalAnadirDisco from './ModalAnadirDisco'; | ||||
| import ModalAnadirRam from './ModalAnadirRam'; | ||||
| import ModalAnadirUsuario from './ModalAnadirUsuario'; | ||||
| import TableSkeleton from './TableSkeleton'; // Importamos el componente de esqueleto | ||||
|  | ||||
| const SimpleTable = () => { | ||||
|   const [data, setData] = useState<Equipo[]>([]); | ||||
| @@ -317,23 +320,23 @@ const SimpleTable = () => { | ||||
|     } | ||||
|   }; | ||||
|    | ||||
|   // --- DEFINICIÓN DE COLUMNAS CORREGIDA --- | ||||
|   const columns: ColumnDef<Equipo>[] = [ | ||||
|     { header: "ID", accessorKey: "id", enableHiding: true }, | ||||
|     { | ||||
|       header: "Nombre", accessorKey: "hostname", | ||||
|       cell: (info: CellContext<Equipo, any>) => (<button onClick={() => setSelectedEquipo(info.row.original)} className={styles.hostnameButton}>{info.row.original.hostname}</button>) | ||||
|     }, | ||||
|     { header: "IP", accessorKey: "ip" }, | ||||
|     { header: "IP", accessorKey: "ip", id: 'ip' }, | ||||
|     { header: "MAC", accessorKey: "mac", enableHiding: true }, | ||||
|     { header: "Motherboard", accessorKey: "motherboard" }, | ||||
|     { header: "CPU", accessorKey: "cpu" }, | ||||
|     { header: "RAM", accessorKey: "ram_installed" }, | ||||
|     { header: "RAM", accessorKey: "ram_installed", id: 'ram' }, | ||||
|     { header: "Discos", accessorFn: (row: Equipo) => row.discos?.map(d => `${d.mediatype} ${d.size}GB`).join(" | ") || "Sin discos" }, | ||||
|     { header: "OS", accessorKey: "os" }, | ||||
|     { header: "Arquitectura", accessorKey: "architecture" }, | ||||
|     { header: "Arquitectura", accessorKey: "architecture", id: 'arch' }, | ||||
|     { | ||||
|       header: "Usuarios y Claves", | ||||
|       id: 'usuarios', | ||||
|       cell: (info: CellContext<Equipo, any>) => { | ||||
|         const { row } = info; | ||||
|         const usuarios = row.original.usuarios || []; | ||||
| @@ -351,7 +354,7 @@ const SimpleTable = () => { | ||||
|                     className={styles.tableButton} | ||||
|                     data-tooltip-id={`edit-${u.id}`} | ||||
|                   > | ||||
|                     ✏️ | ||||
|                     <KeyRound size={16} /> | ||||
|                     <Tooltip id={`edit-${u.id}`} className={styles.tooltip} place="top">Cambiar Clave</Tooltip> | ||||
|                   </button> | ||||
|  | ||||
| @@ -360,7 +363,7 @@ const SimpleTable = () => { | ||||
|                     className={styles.deleteUserButton} | ||||
|                     data-tooltip-id={`remove-${u.id}`} | ||||
|                   > | ||||
|                     🗑️ | ||||
|                     <UserX size={16} /> | ||||
|                     <Tooltip id={`remove-${u.id}`} className={styles.tooltip} place="top">Eliminar Usuario</Tooltip> | ||||
|                   </button> | ||||
|                 </div> | ||||
| @@ -378,7 +381,7 @@ const SimpleTable = () => { | ||||
|         return ( | ||||
|           <div className={styles.sectorContainer}> | ||||
|             <span className={`${styles.sectorName} ${sector ? styles.sectorNameAssigned : styles.sectorNameUnassigned}`}>{sector?.nombre || 'Asignar'}</span> | ||||
|             <button onClick={() => setModalData(row.original)} className={styles.tableButton} data-tooltip-id={`editSector-${row.original.id}`}>✏️<Tooltip id={`editSector-${row.original.id}`} className={styles.tooltip} place="top">Cambiar Sector</Tooltip></button> | ||||
|             <button onClick={() => setModalData(row.original)} className={styles.tableButton} data-tooltip-id={`editSector-${row.original.id}`}><Pencil size={16} /><Tooltip id={`editSector-${row.original.id}`} className={styles.tooltip} place="top">Cambiar Sector</Tooltip></button> | ||||
|           </div> | ||||
|         ); | ||||
|       } | ||||
| @@ -410,8 +413,18 @@ const SimpleTable = () => { | ||||
|  | ||||
|   if (isLoading) { | ||||
|     return ( | ||||
|       <div style={{ padding: '2rem', textAlign: 'center' }}> | ||||
|         <h2>Cargando Equipos...</h2> | ||||
|       <div> | ||||
|         <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> | ||||
|           <h2>Equipos (...)</h2> | ||||
|           <div className={`${skeletonStyles.skeleton}`} style={{ height: '40px', width: '160px' }}></div> | ||||
|         </div> | ||||
|         <div className={styles.controlsContainer}> | ||||
|           <div className={`${skeletonStyles.skeleton}`} style={{ height: '38px', width: '300px' }}></div> | ||||
|           <div className={`${skeletonStyles.skeleton}`} style={{ height: '38px', width: '200px' }}></div> | ||||
|         </div> | ||||
|         <div><p style={{ fontSize: '12px', fontWeight: 'bold' }}>** La tabla permite ordenar por múltiples columnas manteniendo shift al hacer click en la cabecera. **</p></div> | ||||
|         <div className={`${skeletonStyles.skeleton}`} style={{ height: '54px', marginBottom: '1rem' }}></div> | ||||
|         <TableSkeleton rows={15} columns={11} /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| @@ -420,16 +433,16 @@ const SimpleTable = () => { | ||||
|     <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '1rem 0' }}> | ||||
|       <div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}> | ||||
|         <button onClick={() => table.setPageIndex(0)} disabled={!table.getCanPreviousPage()} className={styles.tableButton}> | ||||
|           {'<<'} | ||||
|           <ChevronsLeft size={18} /> | ||||
|         </button> | ||||
|         <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} className={styles.tableButton}> | ||||
|           {'<'} | ||||
|           <ChevronLeft size={18} /> | ||||
|         </button> | ||||
|         <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()} className={styles.tableButton}> | ||||
|           {'>'} | ||||
|           <ChevronRight size={18} /> | ||||
|         </button> | ||||
|         <button onClick={() => table.setPageIndex(table.getPageCount() - 1)} disabled={!table.getCanNextPage()} className={styles.tableButton}> | ||||
|           {'>>'} | ||||
|           <ChevronsRight size={18} /> | ||||
|         </button> | ||||
|       </div> | ||||
|       <span> | ||||
| @@ -474,8 +487,9 @@ const SimpleTable = () => { | ||||
|         <button | ||||
|           className={`${styles.btn} ${styles.btnPrimary}`} | ||||
|           onClick={() => setIsAddModalOpen(true)} | ||||
|           style={{ display: 'flex', alignItems: 'center', gap: '8px' }} | ||||
|         > | ||||
|           + Añadir Equipo | ||||
|           <PlusCircle size={18} /> Añadir Equipo | ||||
|         </button> | ||||
|       </div> | ||||
|       <div className={styles.controlsContainer}> | ||||
| @@ -495,23 +509,41 @@ const SimpleTable = () => { | ||||
|           <thead> | ||||
|             {table.getHeaderGroups().map(hg => ( | ||||
|               <tr key={hg.id}> | ||||
|                 {hg.headers.map(h => ( | ||||
|                   <th key={h.id} className={styles.th} onClick={h.column.getToggleSortingHandler()}> | ||||
|                     {flexRender(h.column.columnDef.header, h.getContext())} | ||||
|                     {h.column.getIsSorted() && (<span className={styles.sortIndicator}>{h.column.getIsSorted() === 'asc' ? '⇑' : '⇓'}</span>)} | ||||
|                   </th> | ||||
|                 ))} | ||||
|                 {hg.headers.map(h => { | ||||
|                   const classNames = [styles.th]; | ||||
|                   if (h.id === 'ip') classNames.push(styles.thIp); | ||||
|                   if (h.id === 'ram') classNames.push(styles.thRam); | ||||
|                   if (h.id === 'arch') classNames.push(styles.thArch); | ||||
|                   if (h.id === 'usuarios') classNames.push(styles.thUsers); | ||||
|                   if (h.id === 'sector') classNames.push(styles.thSector); | ||||
|  | ||||
|                   return ( | ||||
|                     <th key={h.id} className={classNames.join(' ')} onClick={h.column.getToggleSortingHandler()}> | ||||
|                       {flexRender(h.column.columnDef.header, h.getContext())} | ||||
|                       {h.column.getIsSorted() && (<span className={styles.sortIndicator}>{h.column.getIsSorted() === 'asc' ? '⇑' : '⇓'}</span>)} | ||||
|                     </th> | ||||
|                   ); | ||||
|                 })} | ||||
|               </tr> | ||||
|             ))} | ||||
|           </thead> | ||||
|           <tbody> | ||||
|             {table.getRowModel().rows.map(row => ( | ||||
|               <tr key={row.id} className={styles.tr}> | ||||
|                 {row.getVisibleCells().map(cell => ( | ||||
|                   <td key={cell.id} className={styles.td}> | ||||
|                     {flexRender(cell.column.columnDef.cell, cell.getContext())} | ||||
|                   </td> | ||||
|                 ))} | ||||
|                 {row.getVisibleCells().map(cell => { | ||||
|                    const classNames = [styles.td]; | ||||
|                    if (cell.column.id === 'ip') classNames.push(styles.tdIp); | ||||
|                    if (cell.column.id === 'ram') classNames.push(styles.tdRam); | ||||
|                    if (cell.column.id === 'arch') classNames.push(styles.tdArch); | ||||
|                    if (cell.column.id === 'usuarios') classNames.push(styles.tdUsers); | ||||
|                    if (cell.column.id === 'sector') classNames.push(styles.tdSector); | ||||
|  | ||||
|                   return ( | ||||
|                     <td key={cell.id} className={classNames.join(' ')}> | ||||
|                       {flexRender(cell.column.columnDef.cell, cell.getContext())} | ||||
|                     </td> | ||||
|                   ); | ||||
|                 })} | ||||
|               </tr> | ||||
|             ))} | ||||
|           </tbody> | ||||
| @@ -520,7 +552,7 @@ const SimpleTable = () => { | ||||
|  | ||||
|       {PaginacionControles} | ||||
|  | ||||
|       {showScrollButton && (<button onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} className={styles.scrollToTop} title="Volver arriba">↑</button>)} | ||||
|       {showScrollButton && (<button onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} className={styles.scrollToTop} title="Volver arriba"><ArrowUp size={24} /></button>)} | ||||
|  | ||||
|       {modalData && <ModalEditarSector modalData={modalData} setModalData={setModalData} sectores={sectores} onClose={() => setModalData(null)} onSave={handleSave} />} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user