Este commit introduce una serie de mejoras significativas en toda la aplicación, abordando la experiencia de usuario, la consistencia de los datos, la robustez del backend y la implementación de un historial de cambios completo. ✨ **Funcionalidades y Mejoras (Features & Enhancements)** * **Historial de Auditoría Completo:** * Se implementa el registro en el historial para todas las acciones CRUD manuales: creación de equipos, adición y eliminación de discos, RAM y usuarios. * Los cambios de campos simples (IP, Hostname, etc.) ahora también se registran detalladamente. * **Consistencia de Datos Mejorada:** * **RAM:** La selección de RAM en el modal de "Añadir RAM" y la vista de "Administración" ahora agrupan los módulos por especificaciones (Fabricante, Tamaño, Velocidad), eliminando las entradas duplicadas causadas por diferentes `part_number`. * **Arquitectura:** El campo de edición para la arquitectura del equipo se ha cambiado de un input de texto a un selector con las opciones fijas "32 bits" y "64 bits". * **Experiencia de Usuario (UX) Optimizada:** * El botón de "Wake On Lan" (WOL) ahora se deshabilita visualmente si el equipo no tiene una dirección MAC registrada. * Se corrige el apilamiento de modales: los sub-modales (Añadir Disco/RAM/Usuario) ahora siempre aparecen por encima del modal principal de detalles y bloquean su cierre. * El historial de cambios se actualiza en tiempo real en la interfaz después de añadir o eliminar un componente, sin necesidad de cerrar y reabrir el modal. 🐛 **Correcciones (Bug Fixes)** * **Actualización de Estado en Vivo:** Al añadir/eliminar un módulo de RAM, los campos "RAM Instalada" y "Última Actualización" ahora se recalculan en el backend y se actualizan instantáneamente en el frontend. * **Historial de Sectores Legible:** Se corrige el registro del historial para que al cambiar un sector se guarde el *nombre* del sector (ej. "Técnica") en lugar de su ID numérico. * **Formulario de Edición:** El dropdown de "Sector" en el modo de edición ahora preselecciona correctamente el sector asignado actualmente al equipo. * **Error Crítico al Añadir RAM:** Se soluciona un error del servidor (`Sequence contains more than one element`) que ocurría al añadir manualmente un tipo de RAM que ya existía con múltiples `part_number`. Se reemplazó `QuerySingleOrDefaultAsync` por `QueryFirstOrDefaultAsync` para mayor robustez. * **Eliminación Segura:** Se impide la eliminación de un sector si este tiene equipos asignados, protegiendo la integridad de los datos. ♻️ **Refactorización (Refactoring)** * **Servicio de API Centralizado:** Toda la lógica de llamadas `fetch` del frontend ha sido extraída de los componentes y centralizada en un único servicio (`apiService.ts`), mejorando drásticamente la mantenibilidad y organización del código. * **Optimización de Renders:** Se ha optimizado el rendimiento de los modales mediante el uso del hook `useCallback` para memorizar funciones que se pasan como props. * **Nulabilidad en C#:** Se han resuelto múltiples advertencias de compilación (`CS8620`) en el backend al especificar explícitamente los tipos de referencia anulables (`string?`), mejorando la seguridad de tipos del código.
		
			
				
	
	
		
			75 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			75 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useState, useEffect } from 'react';
 | |
| 
 | |
| // --- Interfaces de Props más robustas usando una unión discriminada ---
 | |
| type AutocompleteInputProps = {
 | |
|   value: string;
 | |
|   onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
 | |
|   name: string;
 | |
|   placeholder?: string;
 | |
|   className?: string;
 | |
| } & ( // Esto crea una unión: o es estático o es dinámico
 | |
|   | {
 | |
|       mode: 'static';
 | |
|       fetchSuggestions: () => Promise<string[]>; // No necesita 'query'
 | |
|     }
 | |
|   | {
 | |
|       mode: 'dynamic';
 | |
|       fetchSuggestions: (query: string) => Promise<string[]>; // Necesita 'query'
 | |
|     }
 | |
| );
 | |
| 
 | |
| const AutocompleteInput: React.FC<AutocompleteInputProps> = (props) => {
 | |
|   const { value, onChange, name, placeholder, className } = props;
 | |
|   const [suggestions, setSuggestions] = useState<string[]>([]);
 | |
|   const dataListId = `suggestions-for-${name}`;
 | |
| 
 | |
|   // --- Lógica para el modo ESTÁTICO ---
 | |
|   // Se ejecuta UNA SOLA VEZ cuando el componente se monta
 | |
|   useEffect(() => {
 | |
|     if (props.mode === 'static') {
 | |
|       props.fetchSuggestions()
 | |
|         .then(setSuggestions)
 | |
|         .catch(err => console.error(`Error fetching static suggestions for ${name}:`, err));
 | |
|     }
 | |
|   // La lista de dependencias asegura que solo se ejecute si estas props cambian (lo cual no harán)
 | |
|   }, [props.mode, props.fetchSuggestions, name]); 
 | |
| 
 | |
|   // --- Lógica para el modo DINÁMICO ---
 | |
|   // Se ejecuta cada vez que el usuario escribe, con un debounce
 | |
|   useEffect(() => {
 | |
|     if (props.mode === 'dynamic') {
 | |
|       if (value.length < 2) {
 | |
|         setSuggestions([]);
 | |
|         return;
 | |
|       }
 | |
|       const handler = setTimeout(() => {
 | |
|         props.fetchSuggestions(value)
 | |
|           .then(setSuggestions)
 | |
|           .catch(err => console.error(`Error fetching dynamic suggestions for ${name}:`, err));
 | |
|       }, 300);
 | |
|       return () => clearTimeout(handler);
 | |
|     }
 | |
|   }, [value, props.mode, props.fetchSuggestions, name]);
 | |
| 
 | |
|   return (
 | |
|     <>
 | |
|       <input
 | |
|         type="text"
 | |
|         name={name}
 | |
|         value={value}
 | |
|         onChange={onChange}
 | |
|         placeholder={placeholder}
 | |
|         className={className}
 | |
|         list={dataListId}
 | |
|         autoComplete="off"
 | |
|       />
 | |
|       <datalist id={dataListId}>
 | |
|         {suggestions.map((suggestion, index) => (
 | |
|           <option key={index} value={suggestion} />
 | |
|         ))}
 | |
|       </datalist>
 | |
|     </>
 | |
|   );
 | |
| };
 | |
| 
 | |
| export default AutocompleteInput; |