F1 - Autentcacion - Completa
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\",
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\views\\usuarios\\manageroles.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:views\\usuarios\\manageroles.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\views\\roles\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:views\\roles\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 3,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cde3}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "Program.cs",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Program.cs",
|
||||
"RelativeDocumentMoniker": "Program.cs",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Program.cs",
|
||||
"RelativeToolTip": "Program.cs",
|
||||
"ViewState": "AgIAAD8AAAAAAAAAAAAAAFUAAAABAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-08-10T18:18:15.097Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "Index.cshtml",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Roles\\Index.cshtml",
|
||||
"RelativeDocumentMoniker": "Views\\Roles\\Index.cshtml",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Roles\\Index.cshtml",
|
||||
"RelativeToolTip": "Views\\Roles\\Index.cshtml",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAEAAAAZAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||
"WhenOpened": "2026-08-10T18:16:34.827Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ManageRoles.cshtml",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"RelativeDocumentMoniker": "Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"RelativeToolTip": "Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABIAAABCAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||
"WhenOpened": "2026-08-10T18:15:38.813Z",
|
||||
"EditorCaption": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\",
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\views\\usuarios\\manageroles.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:views\\usuarios\\manageroles.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|c:\\users\\franc\\source\\repos\\vita-asistente\\views\\roles\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||
"RelativeMoniker": "D:0:0:{DF7AD1BB-7DD9-4CE5-857C-CDA25577352C}|vita-asistente.csproj|solutionrelative:views\\roles\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 3,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cde3}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "Program.cs",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Program.cs",
|
||||
"RelativeDocumentMoniker": "Program.cs",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Program.cs",
|
||||
"RelativeToolTip": "Program.cs",
|
||||
"ViewState": "AgIAAD8AAAAAAAAAAAAAAFUAAAABAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-08-10T18:18:15.097Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "Index.cshtml",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Roles\\Index.cshtml",
|
||||
"RelativeDocumentMoniker": "Views\\Roles\\Index.cshtml",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Roles\\Index.cshtml",
|
||||
"RelativeToolTip": "Views\\Roles\\Index.cshtml",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAEAAAAZAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||
"WhenOpened": "2026-08-10T18:16:34.827Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ManageRoles.cshtml",
|
||||
"DocumentMoniker": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"RelativeDocumentMoniker": "Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"ToolTip": "C:\\Users\\franc\\source\\repos\\Vita-Asistente\\Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"RelativeToolTip": "Views\\Usuarios\\ManageRoles.cshtml",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABIAAABCAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||
"WhenOpened": "2026-08-10T18:15:38.813Z",
|
||||
"EditorCaption": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,152 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using vita_asistente.Models;
|
||||
using vita_asistente.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace vita_asistente.Controllers
|
||||
{
|
||||
public class AccountController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public AccountController(UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
// GET: /Account/Register
|
||||
[HttpGet]
|
||||
public IActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: /Account/Register
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Register(RegisterViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// Crear el usuario con los datos del formulario
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = model.Email, // Usamos Email como nombre de usuario
|
||||
Email = model.Email,
|
||||
Nombre = model.Nombre,
|
||||
Apellido = model.Apellido,
|
||||
Documento = model.Documento,
|
||||
Direccion = model.Direccion,
|
||||
RolPrincipal = "Usuario" // valor por defecto, luego puedes asignar roles
|
||||
};
|
||||
|
||||
var result = await _userManager.CreateAsync(user, model.Password);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// Asignar rol "Usuario" por defecto (si el rol existe)
|
||||
// Si no existe, lo creamos o lo ignoramos. Lo veremos después.
|
||||
// Por ahora, solo iniciamos sesión.
|
||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
// Si llegamos aquí, algo falló, devolver la vista con errores
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// GET: /Account/Login
|
||||
[HttpGet]
|
||||
public IActionResult Login(string returnUrl = null)
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: /Account/Login
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var result = await _signInManager.PasswordSignInAsync(
|
||||
model.Email,
|
||||
model.Password,
|
||||
model.RememberMe,
|
||||
lockoutOnFailure: false);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
// Manejar 2FA si lo implementas después
|
||||
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
return RedirectToAction(nameof(Lockout));
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Intento de inicio de sesión inválido.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Si llegamos aquí, algo falló
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: /Account/Logout
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
// Acciones auxiliares para redirección, 2FA, bloqueo, etc. (opcionales)
|
||||
private IActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
}
|
||||
|
||||
// GET: /Account/Lockout
|
||||
[HttpGet]
|
||||
public IActionResult Lockout()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: /Account/LoginWith2fa (opcional)
|
||||
[HttpGet]
|
||||
public IActionResult LoginWith2fa(bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
// Implementar si se usa 2FA
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
using vita_asistente.Models;
|
||||
|
||||
namespace vita_asistente.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public HomeController(ILogger<HomeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace vita_asistente.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class RolesController : Controller
|
||||
{
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
|
||||
public RolesController(RoleManager<IdentityRole> roleManager)
|
||||
{
|
||||
_roleManager = roleManager;
|
||||
}
|
||||
|
||||
// GET: Roles
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var roles = await _roleManager.Roles.ToListAsync();
|
||||
return View(roles);
|
||||
}
|
||||
|
||||
// GET: Roles/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Roles/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(string roleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(roleName))
|
||||
{
|
||||
ModelState.AddModelError("", "El nombre del rol no puede estar vacío.");
|
||||
return View();
|
||||
}
|
||||
|
||||
var roleExists = await _roleManager.RoleExistsAsync(roleName);
|
||||
if (!roleExists)
|
||||
{
|
||||
var result = await _roleManager.CreateAsync(new IdentityRole(roleName));
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError("", error.Description);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("", "El rol ya existe.");
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: Roles/Delete/5
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
var rol = await _roleManager.FindByIdAsync(id);
|
||||
if (rol == null) return NotFound();
|
||||
return View(rol);
|
||||
}
|
||||
|
||||
// POST: Roles/Delete/5
|
||||
[HttpPost, ActionName("Delete")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(string id)
|
||||
{
|
||||
var rol = await _roleManager.FindByIdAsync(id);
|
||||
if (rol != null)
|
||||
{
|
||||
await _roleManager.DeleteAsync(rol);
|
||||
}
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using vita_asistente.Models;
|
||||
|
||||
namespace vita_asistente.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class UsuariosController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
|
||||
public UsuariosController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_roleManager = roleManager;
|
||||
}
|
||||
|
||||
// GET: Usuarios
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var usuarios = await _userManager.Users.ToListAsync();
|
||||
return View(usuarios);
|
||||
}
|
||||
|
||||
// GET: Usuarios/Details/5
|
||||
public async Task<IActionResult> Details(string id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario == null) return NotFound();
|
||||
var roles = await _userManager.GetRolesAsync(usuario);
|
||||
ViewBag.Roles = roles;
|
||||
return View(usuario);
|
||||
}
|
||||
|
||||
// GET: Usuarios/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Usuarios/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(ApplicationUser usuario, string password)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = usuario.Email,
|
||||
Email = usuario.Email,
|
||||
Nombre = usuario.Nombre,
|
||||
Apellido = usuario.Apellido,
|
||||
Documento = usuario.Documento,
|
||||
Direccion = usuario.Direccion,
|
||||
// RolPrincipal se asigna después
|
||||
};
|
||||
var result = await _userManager.CreateAsync(user, password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// Asignar un rol por defecto (por ejemplo "Usuario" si existe)
|
||||
if (await _roleManager.RoleExistsAsync("Usuario"))
|
||||
{
|
||||
await _userManager.AddToRoleAsync(user, "Usuario");
|
||||
}
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
return View(usuario);
|
||||
}
|
||||
|
||||
// GET: Usuarios/Edit/5
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario == null) return NotFound();
|
||||
return View(usuario);
|
||||
}
|
||||
|
||||
// POST: Usuarios/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(string id, ApplicationUser usuarioActualizado)
|
||||
{
|
||||
if (id != usuarioActualizado.Id) return NotFound();
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario == null) return NotFound();
|
||||
|
||||
// Actualizar solo los campos permitidos
|
||||
usuario.Nombre = usuarioActualizado.Nombre;
|
||||
usuario.Apellido = usuarioActualizado.Apellido;
|
||||
usuario.Documento = usuarioActualizado.Documento;
|
||||
usuario.Direccion = usuarioActualizado.Direccion;
|
||||
usuario.Email = usuarioActualizado.Email;
|
||||
usuario.UserName = usuarioActualizado.Email;
|
||||
|
||||
var result = await _userManager.UpdateAsync(usuario);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
return View(usuarioActualizado);
|
||||
}
|
||||
|
||||
// GET: Usuarios/Delete/5
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario == null) return NotFound();
|
||||
return View(usuario);
|
||||
}
|
||||
|
||||
// POST: Usuarios/Delete/5
|
||||
[HttpPost, ActionName("Delete")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(string id)
|
||||
{
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario != null)
|
||||
{
|
||||
var result = await _userManager.DeleteAsync(usuario);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// GET: Usuarios/ManageRoles/5
|
||||
public async Task<IActionResult> ManageRoles(string id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
var usuario = await _userManager.FindByIdAsync(id);
|
||||
if (usuario == null) return NotFound();
|
||||
|
||||
var rolesDelUsuario = await _userManager.GetRolesAsync(usuario);
|
||||
var todosLosRoles = await _roleManager.Roles.ToListAsync();
|
||||
|
||||
var model = new ManageRolesViewModel
|
||||
{
|
||||
UsuarioId = usuario.Id,
|
||||
UsuarioNombre = $"{usuario.Nombre} {usuario.Apellido}",
|
||||
RolesDisponibles = todosLosRoles.Select(r => new RoleCheckbox
|
||||
{
|
||||
RoleId = r.Id,
|
||||
RoleName = r.Name,
|
||||
IsSelected = rolesDelUsuario.Contains(r.Name)
|
||||
}).ToList()
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: Usuarios/ManageRoles
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ManageRoles(ManageRolesViewModel model)
|
||||
{
|
||||
var usuario = await _userManager.FindByIdAsync(model.UsuarioId);
|
||||
if (usuario == null) return NotFound();
|
||||
|
||||
var rolesDelUsuario = await _userManager.GetRolesAsync(usuario);
|
||||
var rolesSeleccionados = model.RolesDisponibles.Where(r => r.IsSelected).Select(r => r.RoleName).ToList();
|
||||
|
||||
// Remover roles que ya no están seleccionados
|
||||
var rolesAEliminar = rolesDelUsuario.Except(rolesSeleccionados).ToList();
|
||||
foreach (var rol in rolesAEliminar)
|
||||
{
|
||||
await _userManager.RemoveFromRoleAsync(usuario, rol);
|
||||
}
|
||||
|
||||
// Agregar roles nuevos
|
||||
var rolesAAgregar = rolesSeleccionados.Except(rolesDelUsuario).ToList();
|
||||
foreach (var rol in rolesAAgregar)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(usuario, rol);
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// ViewModels para la gestión de roles (dentro del mismo archivo por simplicidad)
|
||||
public class ManageRolesViewModel
|
||||
{
|
||||
public string UsuarioId { get; set; }
|
||||
public string UsuarioNombre { get; set; }
|
||||
public List<RoleCheckbox> RolesDisponibles { get; set; }
|
||||
}
|
||||
|
||||
public class RoleCheckbox
|
||||
{
|
||||
public string RoleId { get; set; }
|
||||
public string RoleName { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vita_asistente.Models;
|
||||
|
||||
namespace vita_asistente.Data
|
||||
{
|
||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using vita_asistente.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace vita_asistente.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260810173940_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.29")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vita_asistente.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Apellido")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Direccion")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Documento")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Nombre")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("RolPrincipal")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace vita_asistente.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Name = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
NormalizedName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Nombre = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Apellido = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Documento = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Direccion = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
RolPrincipal = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
UserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
NormalizedUserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Email = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
NormalizedEmail = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
EmailConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
SecurityStamp = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
PhoneNumber = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "datetime(6)", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ClaimType = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ClaimType = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ProviderKey = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ProviderDisplayName = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
LoginProvider = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Name = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Value = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using vita_asistente.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace vita_asistente.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.29")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vita_asistente.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Apellido")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Direccion")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Documento")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Nombre")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("RolPrincipal")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("vita_asistente.Models.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace vita_asistente.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
// Tus propiedades específicas:
|
||||
public string Nombre { get; set; } = string.Empty;
|
||||
public string Apellido { get; set; } = string.Empty;
|
||||
public string Documento { get; set; } = string.Empty;
|
||||
public string Direccion { get; set; } = string.Empty;
|
||||
|
||||
// Si quieres un rol por defecto para consultas rápidas (opcional):
|
||||
public string RolPrincipal { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace vita_asistente.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using vita_asistente.Data;
|
||||
using vita_asistente.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 1. Configurar el DbContext con Pomelo MySQL
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||
|
||||
// 2. Configurar Identity con ApplicationUser y ApplicationDbContext
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||
{
|
||||
// Configuraciones opcionales de Identity (contraseñas, bloqueos, etc.)
|
||||
options.Password.RequireDigit = true;
|
||||
options.Password.RequiredLength = 6;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/Account/Login";
|
||||
options.LogoutPath = "/Account/Logout";
|
||||
options.AccessDeniedPath = "/Account/AccessDenied";
|
||||
});
|
||||
|
||||
// 3. Agregar autenticación (ya incluida con AddIdentity, pero aseguramos)
|
||||
builder.Services.AddAuthentication();
|
||||
|
||||
// El resto de tus servicios...
|
||||
builder.Services.AddControllersWithViews(); // o AddRazorPages según tu proyecto
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//seeds
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
|
||||
// Crear roles si no existen
|
||||
string[] roles = { "Admin", "Usuario" };
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!await roleManager.RoleExistsAsync(role))
|
||||
{
|
||||
await roleManager.CreateAsync(new IdentityRole(role));
|
||||
}
|
||||
}
|
||||
|
||||
// Crear un usuario admin si no existe
|
||||
var adminUser = await userManager.FindByEmailAsync("admin@example.com");
|
||||
if (adminUser == null)
|
||||
{
|
||||
adminUser = new ApplicationUser
|
||||
{
|
||||
UserName = "admin@example.com",
|
||||
Email = "admin@example.com",
|
||||
Nombre = "Admin",
|
||||
Apellido = "Sistema",
|
||||
Documento = "00000000",
|
||||
Direccion = "Calle Falsa 123",
|
||||
};
|
||||
var result = await userManager.CreateAsync(adminUser, "Admin123!");
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await userManager.AddToRoleAsync(adminUser, "Admin");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "Error al crear roles o usuario admin.");
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:15517",
|
||||
"sslPort": 44391
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5131",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7084;http://localhost:5131",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
🏗️ Visión General de la Arquitectura (Alto Nivel)
|
||||
Frontend: ASP.NET Core MVC (Razor Views) con Bootstrap (o cualquier otro framework CSS) para la interfaz de usuario.
|
||||
|
||||
Backend: ASP.NET Core 6+ con inyección de dependencias, Identity para autenticación, y Entity Framework Core (con Pomelo MySQL) para el acceso a datos.
|
||||
|
||||
Base de datos: MySQL (ya definida).
|
||||
|
||||
Servicios externos:
|
||||
|
||||
Google Calendar API para la gestión de citas (embebido o mediante integración).
|
||||
|
||||
Chatwoot para mensajería (solo redirección y notificaciones básicas).
|
||||
|
||||
Seguridad: Autenticación por Identity con roles (Administrador, Médico, Secretaria, Recepcionista, etc.) y autorización por políticas personalizadas.
|
||||
|
||||
📌 Módulos Funcionales
|
||||
Basado en tu descripción, podemos desglosar la aplicación en estos módulos principales:
|
||||
|
||||
Gestión de Usuarios y Roles (ya iniciada).
|
||||
|
||||
Gestión de Pacientes (alta, baja, modificación, historial clínico).
|
||||
|
||||
Gestión de Citas / Agenda (integración con Google Calendar).
|
||||
|
||||
Historia Clínica (diagnósticos, tratamientos, evolución, recetas, etc.).
|
||||
|
||||
Dashboard / Resumen (vista rápida para secretarias y médicos).
|
||||
|
||||
Integración con Chatwoot (botón y contador de mensajes no leídos).
|
||||
|
||||
🧩 Diseño de Datos (Entidades Principales)
|
||||
1. Usuario (ApplicationUser - ya extendido)
|
||||
Campos actuales: Nombre, Apellido, Documento, Dirección, Teléfono, Email, RolPrincipal.
|
||||
|
||||
Nota: El rol define qué funciones puede ver/ejecutar (Administrador, Médico, Secretaria, etc.).
|
||||
|
||||
2. Paciente
|
||||
Representa a la persona que recibe atención médica.
|
||||
|
||||
Atributos sugeridos:
|
||||
|
||||
Id (int o guid)
|
||||
|
||||
Nombres
|
||||
|
||||
Apellidos
|
||||
|
||||
Fecha de Nacimiento
|
||||
|
||||
Género
|
||||
|
||||
Documento de Identidad (único)
|
||||
|
||||
Dirección (opcional)
|
||||
|
||||
Teléfono(s)
|
||||
|
||||
Email (opcional)
|
||||
|
||||
Obra Social / Plan de Salud (opcional, pero útil)
|
||||
|
||||
Fecha de Registro
|
||||
|
||||
Usuario que lo dio de alta (relación con ApplicationUser)
|
||||
|
||||
3. Médico (opcional si ya es un rol, pero si necesitas atributos específicos)
|
||||
Si los médicos son también usuarios del sistema, puedes simplemente usar el rol "Médico" y extender su perfil con atributos como:
|
||||
|
||||
Especialidad
|
||||
|
||||
Matrícula Profesional
|
||||
|
||||
Horario de atención (aunque la agenda esté en Google Calendar)
|
||||
|
||||
Si no todos los médicos son usuarios (por ejemplo, si trabajan en el consultorio pero no usan el sistema), podrías tener una tabla independiente de "Médico" que no herede de Identity. Pero por simplicidad, recomiendo que todos los médicos sean usuarios con rol "Médico" y agregar esos campos extra en ApplicationUser.
|
||||
|
||||
4. Cita
|
||||
Como usas Google Calendar, quizás solo necesites almacenar un enlace o ID de evento de Google, más algunos metadatos.
|
||||
|
||||
Atributos sugeridos:
|
||||
|
||||
Id
|
||||
|
||||
PacienteId (relación con Paciente)
|
||||
|
||||
MédicoId (relación con ApplicationUser - Médico)
|
||||
|
||||
Fecha/Hora de inicio
|
||||
|
||||
Fecha/Hora de fin (o duración)
|
||||
|
||||
Motivo de la cita (texto breve)
|
||||
|
||||
Estado (Programada, Confirmada, Cancelada, Atendida, etc.)
|
||||
|
||||
GoogleCalendarEventId (para sincronizar)
|
||||
|
||||
Fecha de creación
|
||||
|
||||
Usuario que la creó (Secretaria o Médico)
|
||||
|
||||
5. Historia Clínica (Registro médico del paciente)
|
||||
Puede ser una tabla principal HistoriaClinica que agrupe todas las consultas.
|
||||
|
||||
Atributos sugeridos:
|
||||
|
||||
Id
|
||||
|
||||
PacienteId
|
||||
|
||||
MédicoId (quién atiende)
|
||||
|
||||
Fecha de consulta
|
||||
|
||||
Motivo de consulta
|
||||
|
||||
Diagnóstico (texto libre, o podrías usar códigos CIE-10 si es necesario)
|
||||
|
||||
Tratamiento indicado (texto)
|
||||
|
||||
Observaciones / Notas
|
||||
|
||||
Próximo control (fecha sugerida)
|
||||
|
||||
Estado del paciente (ej: activo, en tratamiento, derivado, etc.)
|
||||
|
||||
6. Detalle de Historia Clínica (opcional si necesitas más granularidad)
|
||||
Si quieres separar por tipos (ej: Exámenes, Recetas, Procedimientos), podrías tener subentidades. Pero para empezar, un solo campo de texto "Observaciones" o "Notas" es suficiente.
|
||||
|
||||
🔗 Relaciones Principales
|
||||
Usuario (ApplicationUser) → Paciente (1 a muchos: un usuario puede dar de alta a varios pacientes).
|
||||
|
||||
Usuario (Médico) → Cita (1 a muchos).
|
||||
|
||||
Paciente → Cita (1 a muchos).
|
||||
|
||||
Paciente → HistoriaClinica (1 a muchos).
|
||||
|
||||
Médico → HistoriaClinica (1 a muchos).
|
||||
|
||||
🗂️ Estructura de Carpetas (Proyecto)
|
||||
Te sugiero esta organización:
|
||||
|
||||
text
|
||||
TuProyecto/
|
||||
├── Controllers/
|
||||
│ ├── AccountController (Identity)
|
||||
│ ├── UsuariosController (gestión de usuarios)
|
||||
│ ├── PacientesController
|
||||
│ ├── CitasController
|
||||
│ ├── HistoriaClinicaController
|
||||
│ └── DashboardController
|
||||
├── Models/
|
||||
│ ├── ApplicationUser.cs
|
||||
│ ├── Paciente.cs
|
||||
│ ├── Cita.cs
|
||||
│ ├── HistoriaClinica.cs
|
||||
│ └── ViewModels/ (todos los ViewModels)
|
||||
├── Data/
|
||||
│ ├── ApplicationDbContext.cs
|
||||
│ └── Seeders/
|
||||
│ └── RoleSeeder.cs
|
||||
├── Services/
|
||||
│ ├── GoogleCalendarService.cs (para interactuar con Google Calendar)
|
||||
│ ├── ChatwootService.cs (para obtener contador de mensajes no leídos, si es posible)
|
||||
│ └── IUsuarioService.cs (si quieres separar lógica de negocio)
|
||||
├── Views/
|
||||
│ ├── Usuarios/
|
||||
│ ├── Pacientes/
|
||||
│ ├── Citas/
|
||||
│ ├── HistoriaClinica/
|
||||
│ └── Shared/
|
||||
├── Helpers/ (clases utilitarias)
|
||||
└── Migrations/
|
||||
🚀 Flujos de Trabajo (Casos de Uso)
|
||||
Para Secretaria:
|
||||
Gestión de Pacientes: Dar de alta, buscar, editar, ver historial resumido.
|
||||
|
||||
Agenda de Citas: Ver calendario (Google Calendar embebido), crear/editar/cancelar citas asignando paciente y médico.
|
||||
|
||||
Dashboard: Resumen de citas del día, pacientes nuevos, mensajes sin leer (Chatwoot).
|
||||
|
||||
Para Médico:
|
||||
Ver su agenda (Google Calendar).
|
||||
|
||||
Acceder a Historia Clínica de sus pacientes (puede ver todas, pero quizás solo las que él atendió).
|
||||
|
||||
Cargar / actualizar historia clínica de sus pacientes (diagnóstico, tratamiento, etc.).
|
||||
|
||||
Comunicación: Botón para abrir Chatwoot (conversaciones con pacientes/secretarias).
|
||||
|
||||
Para Administrador:
|
||||
Gestión de usuarios (altas, bajas, roles).
|
||||
|
||||
Parametrización (especialidades, configuraciones, etc.).
|
||||
|
||||
🔐 Seguridad y Autorización
|
||||
Roles:
|
||||
|
||||
Administrador: Puede todo.
|
||||
|
||||
Médico: Puede ver su agenda, gestionar sus pacientes y cargar historias clínicas.
|
||||
|
||||
Secretaria: Puede gestionar pacientes y citas de todos los médicos, pero no puede cargar historias clínicas (quizás solo leer).
|
||||
|
||||
Recepcionista (opcional): Solo agenda y pacientes, sin historial clínico.
|
||||
|
||||
Políticas (si quieres más granularidad): por ejemplo, un médico solo puede editar historias clínicas que él creó, o las de sus pacientes asignados.
|
||||
|
||||
🧰 Tecnologías y Librerías Adicionales
|
||||
Google Calendar API: Necesitas registrar la aplicación en Google Cloud Console, habilitar la API y usar la librería Google.Apis.Calendar.v3.
|
||||
|
||||
Chatwoot: Para el botón de acceso, podrías usar un widget de Chatwoot (código JavaScript) y para el contador de mensajes no leídos, Chatwoot tiene una API REST que puedes consultar desde el backend (usando su token de acceso).
|
||||
|
||||
Entity Framework Core (ya incluido).
|
||||
|
||||
Bootstrap / AdminLTE para un layout administrativo.
|
||||
|
||||
DataTables o similar para listados con búsqueda y paginación.
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace vita_asistente.ViewModels
|
||||
{
|
||||
public class LoginViewModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Correo electrónico")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Contraseña")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Display(Name = "Recordarme")]
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace vita_asistente.ViewModels
|
||||
{
|
||||
public class RegisterViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Nombre")]
|
||||
public string Nombre { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Apellido")]
|
||||
public string Apellido { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Documento")]
|
||||
public string Documento { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Dirección")]
|
||||
public string Direccion { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Teléfono")]
|
||||
public string Telefono { get; set; } // puedes usar PhoneNumber de Identity también
|
||||
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Correo electrónico")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Contraseña")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirmar contraseña")]
|
||||
[Compare("Password", ErrorMessage = "Las contraseñas no coinciden")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@model vita_asistente.ViewModels.LoginViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Iniciar Sesión";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
|
||||
<h4>Inicia sesión con tu cuenta.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<div class="checkbox">
|
||||
<input asp-for="RememberMe" />
|
||||
<label asp-for="RememberMe"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-primary">Iniciar sesión</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a asp-action="Register">Registrarse como nuevo usuario</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
@model vita_asistente.ViewModels.RegisterViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Registro de Usuario";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal">
|
||||
<h4>Crear una nueva cuenta.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Nombre" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Nombre" class="form-control" />
|
||||
<span asp-validation-for="Nombre" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Apellido" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Apellido" class="form-control" />
|
||||
<span asp-validation-for="Apellido" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Documento" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Documento" class="form-control" />
|
||||
<span asp-validation-for="Documento" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Direccion" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Direccion" class="form-control" />
|
||||
<span asp-validation-for="Direccion" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Telefono" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Telefono" class="form-control" />
|
||||
<span asp-validation-for="Telefono" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="ConfirmPassword" class="form-control" />
|
||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-primary">Registrar</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
||||
@@ -0,0 +1,19 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@model IEnumerable<IdentityRole>
|
||||
|
||||
<h1>Roles</h1>
|
||||
<p><a asp-action="Create" class="btn btn-primary">Crear nuevo rol</a></p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Nombre</th><th>Acciones</th></tr></thead>
|
||||
<tbody>
|
||||
@foreach (var role in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@role.Name</td>
|
||||
<td>
|
||||
<a asp-action="Delete" asp-route-id="@role.Id" class="btn btn-danger btn-sm">Eliminar</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,25 @@
|
||||
@model ErrorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - vita_asistente</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/vita_asistente.styles.css" asp-append-version="true" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">vita_asistente</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="adminDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Administración
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="adminDropdown">
|
||||
<li><a class="dropdown-item" asp-controller="Usuarios" asp-action="Index">Usuarios</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Roles" asp-action="Index">Roles</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
<ul class="navbar-nav ms-auto">
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<span class="navbar-text">Hola, @User.Identity.Name!</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<form asp-controller="Account" asp-action="Logout" method="post" class="form-inline">
|
||||
<button type="submit" class="nav-link btn btn-link text-dark">Cerrar sesión</button>
|
||||
</form>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-controller="Account" asp-action="Register">Registrarse</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Iniciar sesión</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2026 - vita_asistente - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
for details on configuring this project to bundle and minify static web assets. */
|
||||
|
||||
a.navbar-brand {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0077cc;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.box-shadow {
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
button.accept-policy {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
@@ -0,0 +1,47 @@
|
||||
@model ApplicationUser
|
||||
|
||||
<h1>Crear Usuario</h1>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form asp-action="Create">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Nombre" class="control-label"></label>
|
||||
<input asp-for="Nombre" class="form-control" />
|
||||
<span asp-validation-for="Nombre" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Apellido" class="control-label"></label>
|
||||
<input asp-for="Apellido" class="form-control" />
|
||||
<span asp-validation-for="Apellido" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="control-label"></label>
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Documento" class="control-label"></label>
|
||||
<input asp-for="Documento" class="form-control" />
|
||||
<span asp-validation-for="Documento" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Direccion" class="control-label"></label>
|
||||
<input asp-for="Direccion" class="form-control" />
|
||||
<span asp-validation-for="Direccion" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">Contraseña</label>
|
||||
<input name="password" class="form-control" type="password" />
|
||||
<span asp-validation-for="PasswordHash" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Crear" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-action="Index">Volver a la lista</a>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
@model ApplicationUser
|
||||
|
||||
<h1>Eliminar Usuario</h1>
|
||||
<h3>¿Está seguro de eliminar este usuario?</h3>
|
||||
<div>
|
||||
<hr />
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Nombre</dt>
|
||||
<dd class="col-sm-10">@Model.Nombre</dd>
|
||||
<dt class="col-sm-2">Apellido</dt>
|
||||
<dd class="col-sm-10">@Model.Apellido</dd>
|
||||
<dt class="col-sm-2">Email</dt>
|
||||
<dd class="col-sm-10">@Model.Email</dd>
|
||||
<dt class="col-sm-2">Documento</dt>
|
||||
<dd class="col-sm-10">@Model.Documento</dd>
|
||||
</dl>
|
||||
<form asp-action="Delete">
|
||||
<input type="hidden" asp-for="Id" />
|
||||
<input type="submit" value="Eliminar" class="btn btn-danger" />
|
||||
<a asp-action="Index" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
@model ApplicationUser
|
||||
|
||||
<h1>Crear Usuario</h1>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form asp-action="Edit">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Nombre" class="control-label"></label>
|
||||
<input asp-for="Nombre" class="form-control" />
|
||||
<span asp-validation-for="Nombre" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Apellido" class="control-label"></label>
|
||||
<input asp-for="Apellido" class="form-control" />
|
||||
<span asp-validation-for="Apellido" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="control-label"></label>
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Documento" class="control-label"></label>
|
||||
<input asp-for="Documento" class="form-control" />
|
||||
<span asp-validation-for="Documento" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Direccion" class="control-label"></label>
|
||||
<input asp-for="Direccion" class="form-control" />
|
||||
<span asp-validation-for="Direccion" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">Contraseña</label>
|
||||
<input name="password" class="form-control" type="password" />
|
||||
<span asp-validation-for="PasswordHash" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Crear" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-action="Index">Volver a la lista</a>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@model IEnumerable<ApplicationUser>
|
||||
|
||||
<h1>Usuarios</h1>
|
||||
<p>
|
||||
<a asp-action="Create" class="btn btn-primary">Crear nuevo usuario</a>
|
||||
</p>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
<th>Apellido</th>
|
||||
<th>Email</th>
|
||||
<th>Documento</th>
|
||||
<th>Roles</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var usuario in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@usuario.Nombre</td>
|
||||
<td>@usuario.Apellido</td>
|
||||
<td>@usuario.Email</td>
|
||||
<td>@usuario.Documento</td>
|
||||
<td>
|
||||
@{
|
||||
var roles = await UserManager.GetRolesAsync(usuario);
|
||||
@string.Join(", ", roles)
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<a asp-action="Details" asp-route-id="@usuario.Id" class="btn btn-info btn-sm">Detalles</a>
|
||||
<a asp-action="Edit" asp-route-id="@usuario.Id" class="btn btn-warning btn-sm">Editar</a>
|
||||
<a asp-action="Delete" asp-route-id="@usuario.Id" class="btn btn-danger btn-sm">Eliminar</a>
|
||||
<a asp-action="ManageRoles" asp-route-id="@usuario.Id" class="btn btn-secondary btn-sm">Roles</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,21 @@
|
||||
@using vita_asistente.Controllers
|
||||
@model ManageRolesViewModel
|
||||
|
||||
<h1>Gestionar roles de @Model.UsuarioNombre</h1>
|
||||
<hr />
|
||||
<form asp-action="ManageRoles" method="post">
|
||||
<input type="hidden" asp-for="UsuarioId" />
|
||||
<div class="form-group">
|
||||
@foreach (var role in Model.RolesDisponibles)
|
||||
{
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="RolesDisponibles[@Model.RolesDisponibles.IndexOf(role)].IsSelected" value="true" @(role.IsSelected ? "checked" : "") />
|
||||
<input type="hidden" name="RolesDisponibles[@Model.RolesDisponibles.IndexOf(role)].RoleId" value="@role.RoleId" />
|
||||
<input type="hidden" name="RolesDisponibles[@Model.RolesDisponibles.IndexOf(role)].RoleName" value="@role.RoleName" />
|
||||
<label class="form-check-label">@role.RoleName</label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||
<a asp-action="Index" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
@@ -0,0 +1,3 @@
|
||||
@using vita_asistente
|
||||
@using vita_asistente.Models
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=192.168.20.48;Database=vita_db;User Id=vetta;Password=Vetta2026;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=192.168.20.48;Database=vita_db;User Id=vetta;Password=Vetta2026;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user