F1 - Autentcacion - Completa

This commit is contained in:
2026-08-10 15:54:42 -03:00
commit dd7f5b3bf6
245 changed files with 81985 additions and 0 deletions
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": ""
}
]
}
]
}
]
}
+152
View File
@@ -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();
}
}
}
+32
View File
@@ -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 });
}
}
}
+86
View File
@@ -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));
}
}
}
+220
View File
@@ -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; }
}
}
+14
View File
@@ -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
View File
@@ -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
}
}
}
+272
View File
@@ -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
}
}
}
+16
View File
@@ -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;
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace vita_asistente.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
+108
View File
@@ -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();
+38
View File
@@ -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"
}
}
}
}
+219
View File
@@ -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.
View File
+20
View File
@@ -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; }
}
}
+42
View File
@@ -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; }
}
}
+51
View File
@@ -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")
}
+87
View File
@@ -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")
}
+8
View File
@@ -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>
+6
View File
@@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
+19
View File
@@ -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>
+25
View File
@@ -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>
+75
View File
@@ -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">
&copy; 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>
+48
View File
@@ -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>
+47
View File
@@ -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>
+22
View File
@@ -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>
+47
View File
@@ -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>
+43
View File
@@ -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>
+21
View File
@@ -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>
+3
View File
@@ -0,0 +1,3 @@
@using vita_asistente
@using vita_asistente.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+3
View File
@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}
+8
View File
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+12
View File
@@ -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.
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+12
View File
@@ -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": "*"
}

Some files were not shown because too many files have changed in this diff Show More