| 
									
										
											  
											
												Mejoras integrales en UI, lógica de negocio y auditoría
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.
											
										 
											2025-10-08 13:27:44 -03:00
										 |  |  | // backend/Controllers/SectoresController.cs | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-02 15:08:49 -03:00
										 |  |  | using Dapper; | 
					
						
							|  |  |  | using Inventario.API.Data; | 
					
						
							|  |  |  | using Inventario.API.Models; | 
					
						
							|  |  |  | using Microsoft.AspNetCore.Mvc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Inventario.API.Controllers | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   [ApiController] | 
					
						
							|  |  |  |   [Route("api/[controller]")]
 | 
					
						
							|  |  |  |   public class SectoresController : ControllerBase | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     private readonly DapperContext _context; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public SectoresController(DapperContext context) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       _context = context; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-02 15:32:23 -03:00
										 |  |  |     // --- GET /api/sectores --- | 
					
						
							|  |  |  |     // Método para obtener todos los sectores. | 
					
						
							| 
									
										
										
										
											2025-10-02 15:08:49 -03:00
										 |  |  |     [HttpGet] | 
					
						
							|  |  |  |     public async Task<IActionResult> ConsultarSectores() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       var query = "SELECT Id, Nombre FROM dbo.sectores ORDER BY Nombre;"; | 
					
						
							|  |  |  |       using (var connection = _context.CreateConnection()) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         var sectores = await connection.QueryAsync<Sector>(query); | 
					
						
							|  |  |  |         return Ok(sectores); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-02 15:32:23 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // --- GET /api/sectores/{id} --- | 
					
						
							|  |  |  |     // Método para obtener un único sector por su ID. | 
					
						
							|  |  |  |     [HttpGet("{id}")] | 
					
						
							|  |  |  |     public async Task<IActionResult> ConsultarSectorDetalle(int id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       var query = "SELECT Id, Nombre FROM dbo.sectores WHERE Id = @Id;"; | 
					
						
							|  |  |  |       using (var connection = _context.CreateConnection()) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // QuerySingleOrDefaultAsync es perfecto para obtener un solo registro. | 
					
						
							|  |  |  |         // Devuelve el objeto si lo encuentra, o null si no existe. | 
					
						
							|  |  |  |         var sector = await connection.QuerySingleOrDefaultAsync<Sector>(query, new { Id = id }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (sector == null) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // Si no se encuentra, devolvemos un error HTTP 404 Not Found. | 
					
						
							|  |  |  |           return NotFound(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return Ok(sector); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // --- POST /api/sectores --- | 
					
						
							|  |  |  |     // Método para crear un nuevo sector. | 
					
						
							|  |  |  |     // Nota: Cambiado de /:nombre a un POST estándar que recibe el objeto en el body. Es más RESTful. | 
					
						
							|  |  |  |     [HttpPost] | 
					
						
							|  |  |  |     public async Task<IActionResult> IngresarSector([FromBody] Sector sector) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       var checkQuery = "SELECT COUNT(1) FROM dbo.sectores WHERE Nombre = @Nombre;"; | 
					
						
							|  |  |  |       var insertQuery = "INSERT INTO dbo.sectores (Nombre) VALUES (@Nombre); SELECT CAST(SCOPE_IDENTITY() as int);"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       using (var connection = _context.CreateConnection()) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // Primero, verificamos si ya existe un sector con ese nombre. | 
					
						
							|  |  |  |         var existe = await connection.ExecuteScalarAsync<bool>(checkQuery, new { sector.Nombre }); | 
					
						
							|  |  |  |         if (existe) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // Si ya existe, devolvemos un error HTTP 409 Conflict. | 
					
						
							|  |  |  |           return Conflict($"Ya existe un sector con el nombre '{sector.Nombre}'"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // ExecuteScalarAsync ejecuta la consulta y devuelve el primer valor de la primera fila (el nuevo ID). | 
					
						
							|  |  |  |         var nuevoId = await connection.ExecuteScalarAsync<int>(insertQuery, new { sector.Nombre }); | 
					
						
							|  |  |  |         var nuevoSector = new Sector { Id = nuevoId, Nombre = sector.Nombre }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Devolvemos una respuesta HTTP 201 Created, con la ubicación del nuevo recurso y el objeto creado. | 
					
						
							|  |  |  |         return CreatedAtAction(nameof(ConsultarSectorDetalle), new { id = nuevoId }, nuevoSector); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // --- PUT /api/sectores/{id} --- | 
					
						
							|  |  |  |     // Método para actualizar un sector existente. | 
					
						
							|  |  |  |     [HttpPut("{id}")] | 
					
						
							|  |  |  |     public async Task<IActionResult> ActualizarSector(int id, [FromBody] Sector sector) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       var query = "UPDATE dbo.sectores SET Nombre = @Nombre WHERE Id = @Id;"; | 
					
						
							|  |  |  |       using (var connection = _context.CreateConnection()) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // ExecuteAsync devuelve el número de filas afectadas. | 
					
						
							|  |  |  |         var filasAfectadas = await connection.ExecuteAsync(query, new { Nombre = sector.Nombre, Id = id }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (filasAfectadas == 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // Si no se afectó ninguna fila, significa que el ID no existía. | 
					
						
							|  |  |  |           return NotFound(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Devolvemos HTTP 204 No Content para indicar que la actualización fue exitosa pero no hay nada que devolver. | 
					
						
							|  |  |  |         return NoContent(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // --- DELETE /api/sectores/{id} --- | 
					
						
							|  |  |  |     // Método para eliminar un sector. | 
					
						
							|  |  |  |     [HttpDelete("{id}")] | 
					
						
							|  |  |  |     public async Task<IActionResult> BorrarSector(int id) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
											  
											
												Mejoras integrales en UI, lógica de negocio y auditoría
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.
											
										 
											2025-10-08 13:27:44 -03:00
										 |  |  |         using (var connection = _context.CreateConnection()) | 
					
						
							| 
									
										
										
										
											2025-10-02 15:32:23 -03:00
										 |  |  |         { | 
					
						
							| 
									
										
											  
											
												Mejoras integrales en UI, lógica de negocio y auditoría
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.
											
										 
											2025-10-08 13:27:44 -03:00
										 |  |  |             // 1. VERIFICAR SI EL SECTOR ESTÁ EN USO | 
					
						
							|  |  |  |             var usageQuery = "SELECT COUNT(1) FROM dbo.equipos WHERE sector_id = @Id;"; | 
					
						
							|  |  |  |             var usageCount = await connection.ExecuteScalarAsync<int>(usageQuery, new { Id = id }); | 
					
						
							| 
									
										
										
										
											2025-10-02 15:32:23 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Mejoras integrales en UI, lógica de negocio y auditoría
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.
											
										 
											2025-10-08 13:27:44 -03:00
										 |  |  |             if (usageCount > 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // 2. DEVOLVER HTTP 409 CONFLICT SI ESTÁ EN USO | 
					
						
							|  |  |  |                 return Conflict(new { message = $"No se puede eliminar. Hay {usageCount} equipo(s) asignados a este sector." }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 3. SI NO ESTÁ EN USO, PROCEDER CON LA ELIMINACIÓN | 
					
						
							|  |  |  |             var deleteQuery = "DELETE FROM dbo.sectores WHERE Id = @Id;"; | 
					
						
							|  |  |  |             var filasAfectadas = await connection.ExecuteAsync(deleteQuery, new { Id = id }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (filasAfectadas == 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 return NotFound(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return NoContent(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-10-02 15:32:23 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-02 15:08:49 -03:00
										 |  |  |   } | 
					
						
							|  |  |  | } |