From 12b7b0f4b2ea7b5318ff9556b64efffb651753df Mon Sep 17 00:00:00 2001 From: Francisco Anfuso Date: Mon, 10 Aug 2026 17:42:02 -0300 Subject: [PATCH] Fase 2 - T2 --- .opencode-rules | 18 ++++ AGENTS.md | 25 +++++ Controllers/RolesController.cs | 101 ++++++++++++++++-- Views/Roles/Disable.cshtml | 18 ++++ Views/Roles/Edit.cshtml | 24 +++++ Views/Roles/Index.cshtml | 80 ++++++++++++-- bin/Debug/net8.0/vita-asistente.dll | Bin 266752 -> 266752 bytes bin/Debug/net8.0/vita-asistente.exe | Bin 151552 -> 151552 bytes bin/Debug/net8.0/vita-asistente.pdb | Bin 82036 -> 82036 bytes obj/Debug/net8.0/apphost.exe | Bin 151552 -> 151552 bytes obj/Debug/net8.0/ref/vita-asistente.dll | Bin 47616 -> 47616 bytes obj/Debug/net8.0/refint/vita-asistente.dll | Bin 47616 -> 47616 bytes obj/Debug/net8.0/rjsmcshtml.dswa.cache.json | 2 +- obj/Debug/net8.0/rjsmrazor.dswa.cache.json | 2 +- .../net8.0/vita-asistente.AssemblyInfo.cs | 2 +- .../vita-asistente.AssemblyInfoInputs.cache | 2 +- ....GeneratedMSBuildEditorConfig.editorconfig | 8 ++ obj/Debug/net8.0/vita-asistente.dll | Bin 266752 -> 266752 bytes obj/Debug/net8.0/vita-asistente.pdb | Bin 82036 -> 82036 bytes plan_desarrollo_sistema_medico.md | 19 ++-- 20 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 .opencode-rules create mode 100644 AGENTS.md create mode 100644 Views/Roles/Disable.cshtml create mode 100644 Views/Roles/Edit.cshtml diff --git a/.opencode-rules b/.opencode-rules new file mode 100644 index 0000000..0e7e418 --- /dev/null +++ b/.opencode-rules @@ -0,0 +1,18 @@ +Actúa como un Ingeniero de Software Principal y Agente Autónomo de Desarrollo. Tu objetivo es cumplir el plan de desarrollo del repositorio de forma iterativa y segura. + +### Contexto del Proyecto +- El proyecto utiliza Git para el control de versiones. +- El estado actual y la descripción general están en `Readme.md`. +- La hoja de ruta y los objetivos están detallados en el archivo del plan de desarrollo. + +### Instrucciones de Operación (Ciclo de Ejecución) +1. ANALIZAR: Lee el plan de desarrollo y compáralo con el código actual para identificar la próxima tarea pendiente. +2. PLANIFICAR: Divide esa tarea en subpasos técnicos sumamente específicos antes de escribir código. +3. EJECUTAR: Modifica o crea los archivos necesarios para completar el subpaso actual. +4. VALIDAR: Asegúrate de que los cambios compilen, no rompan la arquitectura y cumplan con el objetivo. +5. AVANZAR: Una vez completada una tarea, actualiza el archivo del plan de desarrollo marcándola como completada y pasa a la siguiente. + +### Reglas Estrictas +- No intentes resolver todo el plan de desarrollo de un solo golpe; trabaja en tareas atómicas y secuenciales. +- Mantén el código limpio, documentado y modular. +- Si encuentras un bloqueo o ambigüedad en el plan, detén la ejecución y pregunta al usuario. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7e70178 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,25 @@ +# AGENTS.md + +## Tecnología +- ASP.NET Core 8 + MVC con Razor Views. +- MySQL via Pomelo.EntityFrameworkCore.MySql para acceso a datos. +- Identity para gestión de usuarios y roles (Admin, Usuario). + +## Commands +- `dotnet build` - compilar sin restaurar packages. +- `dotnet restore` - restore packages desde nuget.org. +- `dotnet run --urls https://*:5001;http://*:5000` - launch dev server con HTTPS y HTTP. + +## Estructura clave +- Módulos por controller: UsuariosController, AccountController, RolesController. +- Models: ApplicationUser (extiende IdentityUser), ErrorViewModel. +- Data: ApplicationDbContext (EF Core DbContext) en `Data/ApplicationDbContext.cs`. +- Views: Razor views bajo `Views/` folder. + +## Seeds +- Program.cs incluye seeds para roles y usuario admin (`admin@example.com` / "Admin123!"). +- Roles creados: Admin, Usuario. + +## Migraciones +- Usa `dotnet ef migrations add -c ApplicationDbContext`. +- Aplica con `dotnet ef database update`. diff --git a/Controllers/RolesController.cs b/Controllers/RolesController.cs index e0d6013..ee9bd6f 100644 --- a/Controllers/RolesController.cs +++ b/Controllers/RolesController.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; -using System.Linq; +using vita_asistente.Models; namespace vita_asistente.Controllers { @@ -61,13 +61,82 @@ namespace vita_asistente.Controllers return View(); } + // GET: Roles/Edit/5 + public async Task Edit(string id) + { + if (id == null) return NotFound(); + var role = await _roleManager.FindByIdAsync(id); + if (role == null) return NotFound(); + ViewBag.RoleName = role.Name; + return View(role); + } + + // POST: Roles/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(string id, IdentityRole roleActualizado) + { + if (id != roleActualizado.Id) return NotFound(); + + var roleExistente = await _roleManager.FindByIdAsync(id); + if (roleExistente == null) return NotFound(); + + // Actualizar solo el nombre + roleExistente.Name = roleActualizado.Name; + var result = await _roleManager.UpdateAsync(roleExistente); + + if (result.Succeeded) + { + return RedirectToAction(nameof(Index)); + } + foreach (var error in result.Errors) + { + ModelState.AddModelError("", error.Description); + } + return View(roleActualizado); + } + + // GET: Roles/Disable/5 + public async Task Disable(string id) + { + if (id == null) return NotFound(); + var role = await _roleManager.FindByIdAsync(id); + if (role == null) return NotFound(); + return View(role); + } + + // POST: Roles/Disable/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DisableConfirmed(string id) + { + var role = await _roleManager.FindByIdAsync(id); + if (role != null) + { + // En Identity, "deshabilitar" un rol implica eliminarlo + // Pero primero verificamos si tiene usuarios asignados + using var scope = HttpContext.RequestServices.CreateScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + var usersInRole = await userManager.GetUsersInRoleAsync(role.Name); + + if (usersInRole.Any()) + { + ModelState.AddModelError("", "No se puede deshabilitar un rol que tiene usuarios asignados."); + return View("Disable", role); + } + + await _roleManager.DeleteAsync(role); + } + return RedirectToAction(nameof(Index)); + } + // GET: Roles/Delete/5 public async Task Delete(string id) { if (id == null) return NotFound(); - var rol = await _roleManager.FindByIdAsync(id); - if (rol == null) return NotFound(); - return View(rol); + var role = await _roleManager.FindByIdAsync(id); + if (role == null) return NotFound(); + return View(role); } // POST: Roles/Delete/5 @@ -75,10 +144,28 @@ namespace vita_asistente.Controllers [ValidateAntiForgeryToken] public async Task DeleteConfirmed(string id) { - var rol = await _roleManager.FindByIdAsync(id); - if (rol != null) + var role = await _roleManager.FindByIdAsync(id); + if (role != null) { - await _roleManager.DeleteAsync(rol); + // Verificar que no sea el rol "Admin" (o cualquier otro rol esencial) + if (role.Name == "Admin" || role.Name == "Administrador") + { + ModelState.AddModelError("", "No se puede eliminar el rol Admin/Administrador."); + return View(role); + } + + // Verificar que no tenga usuarios asignados + using var scope = HttpContext.RequestServices.CreateScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + var usersInRole = await userManager.GetUsersInRoleAsync(role.Name); + + if (usersInRole.Any()) + { + ModelState.AddModelError("", "No se puede eliminar un rol que tiene usuarios asignados."); + return View(role); + } + + await _roleManager.DeleteAsync(role); } return RedirectToAction(nameof(Index)); } diff --git a/Views/Roles/Disable.cshtml b/Views/Roles/Disable.cshtml new file mode 100644 index 0000000..63a7e88 --- /dev/null +++ b/Views/Roles/Disable.cshtml @@ -0,0 +1,18 @@ +@model IdentityRole + +@{ + ViewData["Title"] = "Deshabilitar rol"; +} + +

Deshabilitar rol: @Model.Name

+ +

¿Está seguro que desea deshabilitar este rol? Esto afectará a los usuarios con este rol.

+ +
+ + @Html.AntiForgeryToken() +
+ + Cancelar +
+
\ No newline at end of file diff --git a/Views/Roles/Edit.cshtml b/Views/Roles/Edit.cshtml new file mode 100644 index 0000000..7998d19 --- /dev/null +++ b/Views/Roles/Edit.cshtml @@ -0,0 +1,24 @@ +@model IdentityRole + +@{ + ViewData["Title"] = "Editar rol"; +} + +

Editar rol: @Model.Name

+ +
+ +
+ + + +
+
+ + Cancelar +
+
+ +section scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} \ No newline at end of file diff --git a/Views/Roles/Index.cshtml b/Views/Roles/Index.cshtml index 9ae2a8c..1d6d4ce 100644 --- a/Views/Roles/Index.cshtml +++ b/Views/Roles/Index.cshtml @@ -11,9 +11,77 @@ @role.Name - Eliminar - - - } - - \ No newline at end of file + Editar + + + + + Deshabilitar + + + + + +