Modulos Pacientes Listo

This commit is contained in:
2026-08-11 23:21:29 -03:00
parent b4b25e49e0
commit 7c04a64269
164 changed files with 1717 additions and 5643 deletions
+429
View File
@@ -0,0 +1,429 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
.artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
**/.paket/paket.exe
paket-files/
# FAKE - F# Make
**/.fake/
# CodeRush personal settings
**/.cr/personal
# Python Tools for Visual Studio (PTVS)
**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
#tools/**
#!tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
**/.mfractor/
# Local History for Visual Studio
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
@@ -1,83 +0,0 @@
{
"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": ""
}
]
}
]
}
]
}
@@ -1,83 +0,0 @@
{
"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": ""
}
]
}
]
}
]
}
+2 -5
View File
@@ -48,11 +48,8 @@ namespace vita_asistente.Controllers
if (result.Succeeded) 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); await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home"); return LocalRedirect("~/");
} }
foreach (var error in result.Errors) foreach (var error in result.Errors)
@@ -118,7 +115,7 @@ namespace vita_asistente.Controllers
public async Task<IActionResult> Logout() public async Task<IActionResult> Logout()
{ {
await _signInManager.SignOutAsync(); await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Home"); return LocalRedirect("~/");
} }
// Acciones auxiliares para redirección, 2FA, bloqueo, etc. (opcionales) // Acciones auxiliares para redirección, 2FA, bloqueo, etc. (opcionales)
+205 -4
View File
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using vita_asistente.Data; using vita_asistente.Data;
using vita_asistente.Data.Models; using vita_asistente.Models;
namespace vita_asistente.Controllers namespace vita_asistente.Controllers
{ {
@@ -26,12 +26,12 @@ namespace vita_asistente.Controllers
if (!string.IsNullOrWhiteSpace(nombreBusqueda)) if (!string.IsNullOrWhiteSpace(nombreBusqueda))
{ {
pacientes = pacientes.Where(p => pacientes = pacientes.Where(p =>
(p.Nombre + " " + p.Apellido).Contains(nombreBusqueda.Trim()) || (p.FirstName + " " + p.LastName).Contains(nombreBusqueda.Trim()) ||
p.Documento.Contains(nombreBusqueda.Trim())); p.DocumentNumber.Contains(nombreBusqueda.Trim()));
} }
ViewBag.NombreBusqueda = nombreBusqueda; ViewBag.NombreBusqueda = nombreBusqueda;
return View(await pacientes.OrderByDescending(p => p.FechaRegistro).ToListAsync()); return View(await pacientes.OrderByDescending(p => p.CreatedAt).ToListAsync());
} }
// GET: Pacientes/Details/5 // GET: Pacientes/Details/5
@@ -43,6 +43,186 @@ namespace vita_asistente.Controllers
return View(paciente); return View(paciente);
} }
// GET: Pacientes/Create
public IActionResult Create()
{
return View();
}
// POST: Pacientes/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Pacientes paciente)
{
if (string.IsNullOrEmpty(paciente.Id))
{
paciente.Id = Guid.NewGuid().ToString("N");
if (ModelState.ContainsKey("Id"))
ModelState.Remove("Id");
}
paciente.AllergiesDescription ??= string.Empty;
if (ModelState.ContainsKey("AllergiesDescription") &&
ModelState["AllergiesDescription"].Errors.Any(e => e.ErrorMessage.Contains("required")))
{
ModelState.Remove("AllergiesDescription");
}
if (ModelState.IsValid)
{
_context.Add(paciente);
await _context.SaveChangesAsync();
if (Request.Headers["Accept"].Contains("application/json"))
{
return Json(new
{
success = true,
data = new
{
id = paciente.Id,
documentType = paciente.DocumentType,
documentNumber = paciente.DocumentNumber,
firstName = paciente.FirstName,
lastName = paciente.LastName,
email = paciente.Email,
birthDate = paciente.BirthDate?.ToString("yyyy-MM-dd"),
address = paciente.Address,
phone = paciente.Phone,
emergencyContactName = paciente.EmergencyContactName,
emergencyContactPhone = paciente.EmergencyContactPhone,
bloodType = paciente.BloodType,
allergiesDescription = paciente.AllergiesDescription,
hasMedicationAllergies = paciente.HasMedicationAllergies,
isActive = paciente.IsActive,
createdAt = paciente.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
}
});
}
return RedirectToAction(nameof(Index));
}
if (Request.Headers["Accept"].Contains("application/json"))
{
var errorDict = new Dictionary<string, string[]>();
foreach (var key in ModelState.Keys)
{
var errors = ModelState[key]?.Errors;
if (errors != null && errors.Any())
{
errorDict.Add(key, errors.Select(e => e.ErrorMessage).ToArray());
}
}
return BadRequest(new { success = false, errors = errorDict });
}
return View(paciente);
}
// GET: Pacientes/Edit/5
public async Task<IActionResult> Edit(string id)
{
if (id == null) return NotFound();
var paciente = await _context.Pacientes.FindAsync(id);
if (paciente == null) return NotFound();
return View(paciente);
}
// POST: Pacientes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(string id, Pacientes paciente)
{
if (id != paciente.Id) return NotFound();
paciente.Id = id;
paciente.AllergiesDescription ??= string.Empty;
if (ModelState.ContainsKey("AllergiesDescription") &&
ModelState["AllergiesDescription"].Errors.Any(e => e.ErrorMessage.Contains("required")))
{
ModelState.Remove("AllergiesDescription");
}
if (ModelState.IsValid)
{
try
{
_context.Update(paciente);
await _context.SaveChangesAsync();
if (Request.Headers["Accept"].Contains("application/json"))
{
return Json(new
{
success = true,
data = new
{
id = paciente.Id,
documentType = paciente.DocumentType,
documentNumber = paciente.DocumentNumber,
firstName = paciente.FirstName,
lastName = paciente.LastName,
email = paciente.Email,
birthDate = paciente.BirthDate?.ToString("yyyy-MM-dd"),
address = paciente.Address,
phone = paciente.Phone,
emergencyContactName = paciente.EmergencyContactName,
emergencyContactPhone = paciente.EmergencyContactPhone,
bloodType = paciente.BloodType,
allergiesDescription = paciente.AllergiesDescription,
hasMedicationAllergies = paciente.HasMedicationAllergies,
isActive = paciente.IsActive,
createdAt = paciente.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
}
});
}
return RedirectToAction(nameof(Index));
}
catch (DbUpdateConcurrencyException)
{
if (!PacienteExists(paciente.Id)) return NotFound();
throw;
}
}
if (Request.Headers["Accept"].Contains("application/json"))
{
var errorDict = new Dictionary<string, string[]>();
foreach (var key in ModelState.Keys)
{
var errors = ModelState[key]?.Errors;
if (errors != null && errors.Any())
{
errorDict.Add(key, errors.Select(e => e.ErrorMessage).ToArray());
}
}
return BadRequest(new { success = false, errors = errorDict });
}
return View(paciente);
}
// GET: Pacientes/Delete/5
public async Task<IActionResult> Delete(string id)
{
if (id == null) return NotFound();
var paciente = await _context.Pacientes.FindAsync(id);
if (paciente == null) return NotFound();
return View(paciente);
}
// POST: Pacientes/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(string id)
{
var paciente = await _context.Pacientes.FindAsync(id);
if (paciente != null)
{
_context.Pacientes.Remove(paciente);
await _context.SaveChangesAsync();
}
if (Request.Headers["Accept"].Contains("application/json"))
{
return Json(new { success = true });
}
return RedirectToAction(nameof(Index));
}
// POST: Pacientes/Activate/5 // POST: Pacientes/Activate/5
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin")] [Authorize(Roles = "Admin")]
@@ -58,5 +238,26 @@ namespace vita_asistente.Controllers
return RedirectToAction(nameof(Index)); return RedirectToAction(nameof(Index));
} }
// POST: Pacientes/Disable/5
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Disable(string id)
{
var paciente = await _context.Pacientes.FindAsync(id);
if (paciente == null) return NotFound();
paciente.IsActive = false;
_context.Pacientes.Update(paciente);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool PacienteExists(string id)
{
return _context.Pacientes.Any(e => e.Id == id);
}
} }
} }
+12 -7
View File
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using vita_asistente.Data.Models; using System.Reflection.Emit;
using vita_asistente.Models; using vita_asistente.Models;
namespace vita_asistente.Data namespace vita_asistente.Data
@@ -12,17 +12,22 @@ namespace vita_asistente.Data
{ {
} }
// DbSet for Paciente model public DbSet<Pacientes> Pacientes { get; set; }
public DbSet<Paciente> Pacientes { get; set; }
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.Entity<ApplicationUser>().ToTable("AspNetUsers"); builder.Entity<ApplicationUser>().ToTable("AspNetUsers");
builder.Entity<Paciente>()
.ToTable("Pacientes") //------Paciente----------------
.HasBaseType<ApplicationUser>(); builder.Entity<Pacientes>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.DocumentType).HasMaxLength(50).IsRequired();
entity.Property(e => e.DocumentNumber).HasMaxLength(20).IsRequired();
entity.Property(e => e.FirstName).HasMaxLength(100).IsRequired();
entity.Property(e => e.LastName).HasMaxLength(100).IsRequired();
} );
} }
} }
} }
-30
View File
@@ -1,30 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using vita_asistente.Models;
namespace vita_asistente.Data.Models
{
public class Paciente : ApplicationUser
{
[StringLength(100)]
public string NombreContactoEmergencia { get; set; } = string.Empty;
[StringLength(50)]
public string TelefonoContactoEmergencia { get; set; } = string.Empty;
[StringLength(50)]
public string TipoSangre { get; set; } = string.Empty;
public bool TieneAlergiasMedicamentosas { get; set; }
[StringLength(500)]
public string AlergiasDescripcion { get; set; } = string.Empty;
public DateTime FechaRegistro { get; set; }
/// <summary>
/// Indicates whether the patient account is active and can log in
/// </summary>
public bool IsActive { get; set; } = true; // Default to active
}
}
-341
View File
@@ -1,341 +0,0 @@
// <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("20260811005032_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);
b.UseTptMappingStrategy();
});
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b =>
{
b.HasBaseType("vita_asistente.Models.ApplicationUser");
b.Property<string>("AlergiasDescripcion")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("varchar(500)");
b.Property<DateTime>("FechaRegistro")
.HasColumnType("datetime(6)");
b.Property<string>("NombreContactoEmergencia")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<string>("TelefonoContactoEmergencia")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<bool>("TieneAlergiasMedicamentosas")
.HasColumnType("tinyint(1)");
b.Property<string>("TipoSangre")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.ToTable("Pacientes", (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();
});
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b =>
{
b.HasOne("vita_asistente.Models.ApplicationUser", null)
.WithOne()
.HasForeignKey("vita_asistente.Data.Models.Paciente", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -1,29 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vita_asistente.Migrations
{
/// <inheritdoc />
public partial class AddIsActiveColumnToPacientes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsActive",
table: "Pacientes",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsActive",
table: "Pacientes");
}
}
}
@@ -12,8 +12,8 @@ using vita_asistente.Data;
namespace vita_asistente.Migrations namespace vita_asistente.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20260811012627_AddIsActiveColumnToPacientes")] [Migration("20260812014543_InitialCreate")]
partial class AddIsActiveColumnToPacientes partial class InitialCreate
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -239,44 +239,82 @@ namespace vita_asistente.Migrations
.HasDatabaseName("UserNameIndex"); .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null); b.ToTable("AspNetUsers", (string)null);
b.UseTptMappingStrategy();
}); });
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b => modelBuilder.Entity("vita_asistente.Models.Pacientes", b =>
{ {
b.HasBaseType("vita_asistente.Models.ApplicationUser"); b.Property<string>("Id")
.HasMaxLength(36)
.HasColumnType("varchar(36)");
b.Property<string>("AlergiasDescripcion") b.Property<string>("Address")
.HasColumnType("longtext");
b.Property<string>("AllergiesDescription")
.IsRequired() .IsRequired()
.HasMaxLength(500) .HasMaxLength(500)
.HasColumnType("varchar(500)"); .HasColumnType("varchar(500)");
b.Property<DateTime>("FechaRegistro") b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<bool>("IsActive") b.Property<string>("BloodType")
.HasColumnType("tinyint(1)"); .IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("NombreContactoEmergencia") b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("DocumentNumber")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<string>("DocumentType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("EmergencyContactName")
.IsRequired() .IsRequired()
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("varchar(100)"); .HasColumnType("varchar(100)");
b.Property<string>("TelefonoContactoEmergencia") b.Property<string>("EmergencyContactPhone")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(20)
.HasColumnType("varchar(50)"); .HasColumnType("varchar(20)");
b.Property<bool>("TieneAlergiasMedicamentosas") b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<bool>("HasMedicationAllergies")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<string>("TipoSangre") b.Property<bool>("IsActive")
.IsRequired() .HasColumnType("tinyint(1)");
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.ToTable("Pacientes", (string)null); b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.HasKey("Id");
b.ToTable("Pacientes");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@@ -329,15 +367,6 @@ namespace vita_asistente.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b =>
{
b.HasOne("vita_asistente.Models.ApplicationUser", null)
.WithOne()
.HasForeignKey("vita_asistente.Data.Models.Paciente", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }
@@ -79,6 +79,45 @@ namespace vita_asistente.Migrations
}) })
.Annotation("MySql:CharSet", "utf8mb4"); .Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Pacientes",
columns: table => new
{
Id = table.Column<string>(type: "varchar(36)", maxLength: 36, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DocumentType = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DocumentNumber = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
FirstName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
LastName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
BirthDate = table.Column<DateTime>(type: "datetime(6)", nullable: true),
Address = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Phone = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
EmergencyContactName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
EmergencyContactPhone = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
BloodType = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
AllergiesDescription = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
HasMedicationAllergies = table.Column<bool>(type: "tinyint(1)", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Pacientes", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetRoleClaims", name: "AspNetRoleClaims",
columns: table => new columns: table => new
@@ -206,35 +245,6 @@ namespace vita_asistente.Migrations
}) })
.Annotation("MySql:CharSet", "utf8mb4"); .Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Pacientes",
columns: table => new
{
Id = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
NombreContactoEmergencia = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TelefonoContactoEmergencia = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TipoSangre = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
TieneAlergiasMedicamentosas = table.Column<bool>(type: "tinyint(1)", nullable: false),
AlergiasDescripcion = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
FechaRegistro = table.Column<DateTime>(type: "datetime(6)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Pacientes", x => x.Id);
table.ForeignKey(
name: "FK_Pacientes_AspNetUsers_Id",
column: x => x.Id,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId", name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims", table: "AspNetRoleClaims",
+56 -27
View File
@@ -236,44 +236,82 @@ namespace vita_asistente.Migrations
.HasDatabaseName("UserNameIndex"); .HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null); b.ToTable("AspNetUsers", (string)null);
b.UseTptMappingStrategy();
}); });
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b => modelBuilder.Entity("vita_asistente.Models.Pacientes", b =>
{ {
b.HasBaseType("vita_asistente.Models.ApplicationUser"); b.Property<string>("Id")
.HasMaxLength(36)
.HasColumnType("varchar(36)");
b.Property<string>("AlergiasDescripcion") b.Property<string>("Address")
.HasColumnType("longtext");
b.Property<string>("AllergiesDescription")
.IsRequired() .IsRequired()
.HasMaxLength(500) .HasMaxLength(500)
.HasColumnType("varchar(500)"); .HasColumnType("varchar(500)");
b.Property<DateTime>("FechaRegistro") b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<bool>("IsActive") b.Property<string>("BloodType")
.HasColumnType("tinyint(1)"); .IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("NombreContactoEmergencia") b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("DocumentNumber")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.Property<string>("DocumentType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("EmergencyContactName")
.IsRequired() .IsRequired()
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("varchar(100)"); .HasColumnType("varchar(100)");
b.Property<string>("TelefonoContactoEmergencia") b.Property<string>("EmergencyContactPhone")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(20)
.HasColumnType("varchar(50)"); .HasColumnType("varchar(20)");
b.Property<bool>("TieneAlergiasMedicamentosas") b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<bool>("HasMedicationAllergies")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<string>("TipoSangre") b.Property<bool>("IsActive")
.IsRequired() .HasColumnType("tinyint(1)");
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.ToTable("Pacientes", (string)null); b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("varchar(20)");
b.HasKey("Id");
b.ToTable("Pacientes");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@@ -326,15 +364,6 @@ namespace vita_asistente.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("vita_asistente.Data.Models.Paciente", b =>
{
b.HasOne("vita_asistente.Models.ApplicationUser", null)
.WithOne()
.HasForeignKey("vita_asistente.Data.Models.Paciente", "Id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }
+2 -2
View File
@@ -4,13 +4,13 @@ namespace vita_asistente.Models
{ {
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
{ {
// Tus propiedades específicas: // Custom properties:
public string Nombre { get; set; } = string.Empty; public string Nombre { get; set; } = string.Empty;
public string Apellido { get; set; } = string.Empty; public string Apellido { get; set; } = string.Empty;
public string Documento { get; set; } = string.Empty; public string Documento { get; set; } = string.Empty;
public string Direccion { get; set; } = string.Empty; public string Direccion { get; set; } = string.Empty;
// Si quieres un rol por defecto para consultas rápidas (opcional): // If you want a default role for quick queries (optional):
public string RolPrincipal { get; set; } = string.Empty; public string RolPrincipal { get; set; } = string.Empty;
} }
} }
+55
View File
@@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
namespace vita_asistente.Models
{
public class Pacientes
{
[Key]
[StringLength(36)]
public string Id { get; set; }
[Required]
[StringLength(50)]
public string DocumentType { get; set; } = string.Empty;
[Required]
[StringLength(20)]
public string DocumentNumber { get; set; } = string.Empty;
[Required]
[StringLength(100)]
public string FirstName { get; set; } = string.Empty;
[Required]
[StringLength(100)]
public string LastName { get; set; } = string.Empty;
[StringLength(256)]
public string Email { get; set; } = string.Empty;
public DateTime? BirthDate { get; set; }
public string? Address { get; set; } = string.Empty;
[StringLength(20)]
public string Phone { get; set; } = string.Empty;
[StringLength(100)]
public string EmergencyContactName{ get; set; } = string.Empty;
[StringLength(20)]
public string EmergencyContactPhone { get; set; } = string.Empty;
[StringLength(50) ]
public string BloodType { get; set; } = string.Empty;
[StringLength(500)]
public string AllergiesDescription{ get; set; }= string.Empty;
public bool HasMedicationAllergies { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public bool IsActive { get; set; } = true;
}
}
+86
View File
@@ -0,0 +1,86 @@
@model vita_asistente.Models.Pacientes
@{
ViewData["Title"] = "Crear Paciente";
Layout = "_Layout";
}
<div class="container mt-4">
<h2>Crear Nuevo Paciente</h2>
<form asp-action="Create" method="post" class="mt-4">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row mb-3">
<label asp-for="DocumentNumber" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="DocumentNumber" class="form-control" />
<span asp-validation-for="DocumentNumber" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="FirstName" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="LastName" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Email" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="BirthDate" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="BirthDate" class="form-control" />
<span asp-validation-for="BirthDate" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Phone" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Address" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<textarea asp-for="Address" class="form-control"></textarea>
<span asp-validation-for="Address" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="IsActive" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10 form-check form-switch">
<input asp-for="IsActive" class="form-check-input" />
</div>
</div>
<div class="d-flex gap-2 mt-4">
<button type="submit" class="btn btn-primary">Guardar</button>
<a asp-action="Index" class="btn btn-outline-secondary">Cancelar</a>
</div>
</form>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
+31
View File
@@ -0,0 +1,31 @@
@model vita_asistente.Models.Pacientes
@{
ViewData["Title"] = "Eliminar Paciente";
Layout = "_Layout";
}
<div class="container mt-4">
<h2>¿Desactivar paciente?</h2>
<p>Está a punto de desactivar el siguiente paciente:</p>
<dl class="row">
<dt class="col-sm-2">Documento:</dt>
<dd class="col-sm-10">@Html.DisplayFor(model => model.DocumentNumber)</dd>
<dl class="row">
<dl class="row">
<dt class="col-sm-2">Nombre:</dt>
<dd class="col-sm-10">@Html.DisplayFor(model => model.FirstName) @Html.DisplayFor(model => model.LastName)</dd>
<dl class="row">
<form asp-action="DeleteConfirmed" asp-route-id="Model.Id" method="post">
@Html.AntiForgeryToken()
<div class="d-flex gap-2 mt-4">
<button type="submit" value="Eliminar" class="btn btn-danger":><button type="submit" value="Eliminar" class="btn btn-danger ms-1">Confirmar desactivación</button>
<a asp-action="Index" class="btn btn-outline-secondary">Cancelar</a>
</div>
</form>
</div>
+88
View File
@@ -0,0 +1,88 @@
@model vita_asistente.Models.Pacientes
@{
ViewData["Title"] = "Editar Paciente";
Layout = "_Layout";
}
<div class="container mt-4">
<h2>Editar Paciente</h2>
<form asp-action="Edit" method="post" asp-route-id="@Model.Id" class="mt-4">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="row mb-3">
<label asp-for="DocumentNumber" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="DocumentNumber" class="form-control" readonly>
<span asp-validation-for="DocumentNumber" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="FirstName" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="LastName" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Email" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="BirthDate" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="BirthDate" class="form-control" />
<span asp-validation-for="BirthDate" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Phone" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="Address" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<textarea asp-for="Address" class="form-control"></textarea>
<span asp-validation-for="Address" class="text-danger"></span>
</div>
</div>
<div class="row mb-3">
<label asp-for="IsActive" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10 form-check form-switch">
<input asp-for="IsActive" class="form-check-input" />
</div>
</div>
<div class="d-flex gap-2 mt-4">
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
<a asp-action="Index" class="btn btn-outline-secondary">Cancelar</a>
</div>
</form>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
+612 -42
View File
@@ -1,36 +1,45 @@
@model List<vita_asistente.Data.Models.Paciente> @model List<vita_asistente.Models.Pacientes>
@{ @{
ViewData["Title"] = "Pacientes"; ViewData["Title"] = "Pacientes";
Layout = "_Layout"; Layout = "_Layout";
} }
<div class="container mt-4"> <div class="container mt-4 pb-5">
<h2>Lista de Pacientes</h2> <!-- Encabezado + Botón Agregar -->
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-4">
<h2 class="mb-0">Lista de Pacientes</h2>
@if (User.IsInRole("Admin")) @if (User.IsInRole("Admin"))
{ {
<a asp-action="Create" class="btn btn-success mb-3">Nuevo Paciente</a> <button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#createModal">
<i class="bi bi-person-plus-fill me-1"></i> Agregar Paciente
</button>
} }
</div>
<div class="card"> <!-- Buscador -->
<div class="card shadow-sm mb-4">
<div class="card-body"> <div class="card-body">
<form asp-action="Index" method="get" class="row g-3 mb-4"> <form asp-action="Index" method="get" class="row g-3 align-items-end">
<div class="col-md-5"> <div class="col-md-8 col-lg-6">
<label for="nombreBusqueda" class="form-label">Nombre o Documento</label> <label for="nombreBusqueda" class="form-label">Nombre o Documento</label>
<div class="input-group">
<input type="text" class="form-control" id="nombreBusqueda" name="nombreBusqueda" <input type="text" class="form-control" id="nombreBusqueda" name="nombreBusqueda"
placeholder="Buscar por nombre o documento..." value="@ViewBag.NombreBusqueda"> placeholder="Buscar por nombre o documento..." value="@ViewBag.NombreBusqueda">
</div> <button type="submit" class="btn btn-primary">Buscar</button>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-3">Buscar</button>
@if (!string.IsNullOrEmpty(ViewBag.NombreBusqueda)) @if (!string.IsNullOrEmpty(ViewBag.NombreBusqueda))
{ {
<a asp-action="Index" class="btn btn-outline-secondary">Limpiar</a> <a asp-action="Index" class="btn btn-outline-secondary">Limpiar</a>
} }
</div> </div>
</div>
</form> </form>
</div>
</div>
<table class="table table-striped"> <!-- Tabla -->
<div class="table-responsive">
<table class="table table-hover align-middle" id="pacientesTable">
<thead class="table-dark"> <thead class="table-dark">
<tr> <tr>
<th>Documento</th> <th>Documento</th>
@@ -38,45 +47,53 @@
<th>Email</th> <th>Email</th>
<th>Fecha Registro</th> <th>Fecha Registro</th>
<th>Estado</th> <th>Estado</th>
<th>Acciones</th> <th class="text-end">Acciones</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var paciente in Model) @foreach (var paciente in Model)
{ {
<tr> <tr data-id="@paciente.Id">
<td>@Html.DisplayFor(modelItem => paciente.Documento)</td> <td>@paciente.DocumentNumber</td>
<td>@Html.DisplayFor(modelItem => paciente.Nombre) @Html.DisplayFor(modelItem => paciente.Apellido)</td> <td>@paciente.FirstName @paciente.LastName</td>
<td>@Html.DisplayFor(modelItem => paciente.Email)</td> <td>@paciente.Email</td>
<td>@Html.DisplayFor(modelItem => paciente.FechaRegistro.ToShortDateString())</td> <td>@paciente.CreatedAt.ToShortDateString()</td>
<td> <td>
@if (paciente.IsActive) <span class="badge @(paciente.IsActive ? "bg-success" : "bg-danger")">
{ @(paciente.IsActive ? "Activo" : "Inactivo")
<span class="badge bg-success">Activo</span> </span>
}
else
{
<span class="badge bg-danger">Inactivo</span>
}
</td> </td>
<td> <td class="text-end">
@if (User.IsInRole("Admin") || (User.Identity != null && User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value == paciente.Id)) <a asp-action="Details" asp-route-id="@paciente.Id" class="btn btn-sm btn-outline-info">
{ <i class="bi bi-eye"></i> Ver
<a asp-action="Details" asp-route-id="@paciente.Id" class="btn btn-sm btn-info">Ver</a> </a>
@if (User.IsInRole("Admin")) @if (User.IsInRole("Admin"))
{ {
<a asp-action="Edit" asp-route-id="@paciente.Id" class="btn btn-sm btn-warning ms-1">Editar</a> <button type="button" class="btn btn-sm btn-outline-warning edit-btn"
@if (!paciente.IsActive) data-id="@paciente.Id"
{ data-documenttype="@paciente.DocumentType"
<form asp-action="Activate" asp-route-id="@paciente.Id" class="d-inline"> data-documentnumber="@paciente.DocumentNumber"
<button type="submit" class="btn btn-sm btn-success ms-1" onclick="return confirm('¿Activar este paciente?');">Activar</button> data-firstname="@paciente.FirstName"
</form> data-lastname="@paciente.LastName"
} data-email="@paciente.Email"
else data-birthdate="@(paciente.BirthDate?.ToString("yyyy-MM-dd"))"
{ data-address="@paciente.Address"
<a asp-action="Delete" asp-route-id="@paciente.Id" class="btn btn-sm btn-danger ms-1">Desactivar</a> data-phone="@paciente.Phone"
} data-emergencycontactname="@paciente.EmergencyContactName"
} data-emergencycontactphone="@paciente.EmergencyContactPhone"
data-bloodtype="@paciente.BloodType"
data-allergiesdescription="@paciente.AllergiesDescription"
data-hasmedicationallergies="@paciente.HasMedicationAllergies.ToString().ToLower()"
data-isactive="@paciente.IsActive.ToString().ToLower()"
data-bs-toggle="modal" data-bs-target="#editModal">
<i class="bi bi-pencil"></i> Editar
</button>
<button type="button" class="btn btn-sm btn-outline-danger delete-btn"
data-id="@paciente.Id"
data-name="@($"{paciente.FirstName} {paciente.LastName}")"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="bi bi-trash"></i> Eliminar
</button>
} }
</td> </td>
</tr> </tr>
@@ -84,5 +101,558 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<!-- =============== MODAL CREAR =============== -->
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title" id="createModalLabel"><i class="bi bi-person-plus-fill me-2"></i>Nuevo Paciente</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form id="createForm" asp-action="Create" method="post">
<div class="modal-body">
@Html.AntiForgeryToken()
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Nombre</label>
<input type="text" class="form-control" id="createFirstName" name="FirstName" />
<span class="text-danger" id="createFirstNameError"></span>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Apellido</label>
<input type="text" class="form-control" id="createLastName" name="LastName" />
<span class="text-danger" id="createLastNameError"></span>
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Tipo Documento</label>
<input type="text" class="form-control" id="createDocumentType" name="DocumentType" />
<span class="text-danger" id="createDocumentTypeError"></span>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Número Documento</label>
<input type="text" class="form-control" id="createDocumentNumber" name="DocumentNumber" />
<span class="text-danger" id="createDocumentNumberError"></span>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" id="createEmail" name="Email" />
<span class="text-danger" id="createEmailError"></span>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Fecha Nacimiento</label>
<input type="date" class="form-control" id="createBirthDate" name="BirthDate" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control" id="createPhone" name="Phone" />
</div>
</div>
<div class="mb-3">
<label class="form-label">Dirección</label>
<input type="text" class="form-control" id="createAddress" name="Address" />
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Contacto Emergencia</label>
<input type="text" class="form-control" id="createEmergencyContactName" name="EmergencyContactName" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Teléfono Emergencia</label>
<input type="text" class="form-control" id="createEmergencyContactPhone" name="EmergencyContactPhone" />
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Tipo Sangre</label>
<input type="text" class="form-control" id="createBloodType" name="BloodType" />
</div>
<div class="col-md-8 mb-3">
<label class="form-label">Alergias</label>
<input type="text" class="form-control" id="createAllergiesDescription" name="AllergiesDescription" />
</div>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="createHasMedicationAllergies" name="HasMedicationAllergies" value="true" />
<label class="form-check-label" for="createHasMedicationAllergies">¿Tiene alergias a medicamentos?</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-success">Guardar</button>
</div>
</form>
</div>
</div> </div>
</div> </div>
<!-- =============== MODAL EDITAR =============== -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="editModalLabel"><i class="bi bi-pencil me-2"></i>Editar Paciente</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="editForm" method="post">
<div class="modal-body">
@Html.AntiForgeryToken()
<input type="hidden" id="editId" name="Id" />
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Nombre</label>
<input type="text" class="form-control" id="editFirstName" name="FirstName" />
<span class="text-danger" id="editFirstNameError"></span>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Apellido</label>
<input type="text" class="form-control" id="editLastName" name="LastName" />
<span class="text-danger" id="editLastNameError"></span>
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Tipo Documento</label>
<input type="text" class="form-control" id="editDocumentType" name="DocumentType" />
<span class="text-danger" id="editDocumentTypeError"></span>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Número Documento</label>
<input type="text" class="form-control" id="editDocumentNumber" name="DocumentNumber" />
<span class="text-danger" id="editDocumentNumberError"></span>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" id="editEmail" name="Email" />
<span class="text-danger" id="editEmailError"></span>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Fecha Nacimiento</label>
<input type="date" class="form-control" id="editBirthDate" name="BirthDate" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control" id="editPhone" name="Phone" />
</div>
</div>
<div class="mb-3">
<label class="form-label">Dirección</label>
<input type="text" class="form-control" id="editAddress" name="Address" />
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Contacto Emergencia</label>
<input type="text" class="form-control" id="editEmergencyContactName" name="EmergencyContactName" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Teléfono Emergencia</label>
<input type="text" class="form-control" id="editEmergencyContactPhone" name="EmergencyContactPhone" />
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Tipo Sangre</label>
<input type="text" class="form-control" id="editBloodType" name="BloodType" />
</div>
<div class="col-md-8 mb-3">
<label class="form-label">Alergias</label>
<input type="text" class="form-control" id="editAllergiesDescription" name="AllergiesDescription" />
</div>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="editHasMedicationAllergies" name="HasMedicationAllergies" value="true" />
<label class="form-check-label" for="editHasMedicationAllergies">¿Tiene alergias a medicamentos?</label>
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="editIsActive" name="IsActive" value="true" />
<label class="form-check-label" for="editIsActive">Activo</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-warning">Actualizar</button>
</div>
</form>
</div>
</div>
</div>
<!-- =============== MODAL ELIMINAR =============== -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteModalLabel"><i class="bi bi-exclamation-triangle me-2"></i>Eliminar Paciente</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>¿Estás seguro de que deseas eliminar al paciente <strong id="deletePatientName"></strong>?</p>
<p class="text-danger">Esta acción no se puede deshacer.</p>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="confirmDeleteCheck">
<label class="form-check-label" for="confirmDeleteCheck">
Confirmo que deseo eliminar este paciente.
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<form id="deleteForm" asp-action="Delete" method="post" class="d-inline">
@Html.AntiForgeryToken()
<input type="hidden" name="id" id="deleteId" />
<button type="submit" class="btn btn-danger" id="deleteConfirmBtn" disabled>Eliminar</button>
</form>
</div>
</div>
</div>
</div>
<!-- Toast container -->
<div id="toastContainer" class="position-fixed bottom-0 end-0 p-3" style="z-index: 1200;"></div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
document.addEventListener('DOMContentLoaded', function () {
// ---------- UTILIDADES ----------
function showToast(message, isSuccess = true) {
const container = document.getElementById('toastContainer');
const toast = document.createElement('div');
toast.className = `toast align-items-center text-white ${isSuccess ? 'bg-success' : 'bg-danger'} border-0`;
toast.role = 'alert';
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
const body = document.createElement('div');
body.className = 'toast-body';
body.textContent = message;
toast.appendChild(body);
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: 4000 });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
function getAntiForgeryToken(form) {
return form.querySelector('input[name="__RequestVerificationToken"]')?.value || '';
}
function clearValidationErrors(form) {
form.querySelectorAll('.text-danger').forEach(el => el.textContent = '');
}
function displayValidationErrors(form, errors) {
clearValidationErrors(form);
for (const [key, messages] of Object.entries(errors)) {
// Buscar el span de error correspondiente: id="[formId][PropertyName]Error"
const prefix = form.id === 'createForm' ? 'create' : 'edit';
const span = document.getElementById(`${prefix}${key}Error`);
if (span) {
span.textContent = Array.isArray(messages) ? messages.join(' ') : messages;
}
}
}
// ---------- CREAR PACIENTE ----------
const createForm = document.getElementById('createForm');
const createModalEl = document.getElementById('createModal');
const createModal = new bootstrap.Modal(createModalEl);
createForm.addEventListener('submit', async function (e) {
e.preventDefault();
clearValidationErrors(createForm);
const formData = new FormData(createForm);
const token = getAntiForgeryToken(createForm);
try {
const response = await fetch(createForm.action, {
method: 'POST',
headers: {
'Accept': 'application/json',
'RequestVerificationToken': token
},
body: new URLSearchParams(formData)
});
if (!response.ok) {
const errorData = await response.json();
if (errorData.errors) {
displayValidationErrors(createForm, errorData.errors);
showToast('Por favor corrige los errores.', false);
} else {
showToast('Error al guardar.', false);
}
return;
}
const result = await response.json();
if (result.success) {
const patient = result.data;
addRowToTable(patient);
createModal.hide();
showToast('Paciente creado exitosamente.');
createForm.reset();
} else {
showToast('Error al crear el paciente.', false);
}
} catch (error) {
console.error(error);
showToast('Ocurrió un error inesperado.', false);
}
});
// ---------- EDITAR PACIENTE ----------
const editModalEl = document.getElementById('editModal');
const editModal = new bootstrap.Modal(editModalEl);
const editForm = document.getElementById('editForm');
function loadEditData(btn) {
document.getElementById('editId').value = btn.dataset.id;
document.getElementById('editFirstName').value = btn.dataset.firstname || '';
document.getElementById('editLastName').value = btn.dataset.lastname || '';
document.getElementById('editDocumentType').value = btn.dataset.documenttype || '';
document.getElementById('editDocumentNumber').value = btn.dataset.documentnumber || '';
document.getElementById('editEmail').value = btn.dataset.email || '';
document.getElementById('editBirthDate').value = btn.dataset.birthdate || '';
document.getElementById('editAddress').value = btn.dataset.address || '';
document.getElementById('editPhone').value = btn.dataset.phone || '';
document.getElementById('editEmergencyContactName').value = btn.dataset.emergencycontactname || '';
document.getElementById('editEmergencyContactPhone').value = btn.dataset.emergencycontactphone || '';
document.getElementById('editBloodType').value = btn.dataset.bloodtype || '';
document.getElementById('editAllergiesDescription').value = btn.dataset.allergiesdescription || '';
document.getElementById('editHasMedicationAllergies').checked = btn.dataset.hasmedicationallergies === 'true';
document.getElementById('editIsActive').checked = btn.dataset.isactive === 'true';
editForm.action = `/Pacientes/Edit/${btn.dataset.id}`;
clearValidationErrors(editForm);
}
// Asignar evento a botones de edición (incluyendo los dinámicos)
document.addEventListener('click', function (e) {
const btn = e.target.closest('.edit-btn');
if (btn) {
loadEditData(btn);
}
});
editForm.addEventListener('submit', async function (e) {
e.preventDefault();
clearValidationErrors(editForm);
const formData = new FormData(editForm);
const token = getAntiForgeryToken(editForm);
try {
const response = await fetch(editForm.action, {
method: 'POST',
headers: {
'Accept': 'application/json',
'RequestVerificationToken': token
},
body: new URLSearchParams(formData)
});
if (!response.ok) {
const errorData = await response.json();
if (errorData.errors) {
displayValidationErrors(editForm, errorData.errors);
showToast('Por favor corrige los errores.', false);
} else {
showToast('Error al actualizar.', false);
}
return;
}
const result = await response.json();
if (result.success) {
const patient = result.data;
updateRowInTable(patient);
editModal.hide();
showToast('Paciente actualizado exitosamente.');
} else {
showToast('Error al actualizar.', false);
}
} catch (error) {
console.error(error);
showToast('Ocurrió un error inesperado.', false);
}
});
// ---------- ELIMINAR PACIENTE ----------
const deleteModalEl = document.getElementById('deleteModal');
const deleteModal = new bootstrap.Modal(deleteModalEl);
const deleteForm = document.getElementById('deleteForm');
const confirmCheck = document.getElementById('confirmDeleteCheck');
const deleteBtn = document.getElementById('deleteConfirmBtn');
const deletePatientName = document.getElementById('deletePatientName');
document.addEventListener('click', function (e) {
const btn = e.target.closest('.delete-btn');
if (btn) {
const id = btn.dataset.id;
const name = btn.dataset.name;
deletePatientName.textContent = name;
document.getElementById('deleteId').value = id;
confirmCheck.checked = false;
deleteBtn.disabled = true;
deleteForm.action = `/Pacientes/Delete/${id}`;
}
});
confirmCheck.addEventListener('change', function () {
deleteBtn.disabled = !this.checked;
});
deleteForm.addEventListener('submit', async function (e) {
e.preventDefault();
const token = getAntiForgeryToken(deleteForm);
const id = document.getElementById('deleteId').value;
try {
const response = await fetch(deleteForm.action, {
method: 'POST',
headers: {
'Accept': 'application/json',
'RequestVerificationToken': token
},
body: new URLSearchParams(new FormData(deleteForm))
});
if (!response.ok) {
showToast('Error al eliminar.', false);
return;
}
const result = await response.json();
if (result.success) {
removeRowFromTable(id);
deleteModal.hide();
showToast('Paciente eliminado correctamente.');
} else {
showToast('Error al eliminar.', false);
}
} catch (error) {
console.error(error);
showToast('Ocurrió un error inesperado.', false);
}
});
// ---------- FUNCIONES DE ACTUALIZACIÓN DE TABLA ----------
function addRowToTable(patient) {
const tbody = document.querySelector('#pacientesTable tbody');
const row = document.createElement('tr');
row.dataset.id = patient.id;
const statusBadge = patient.isActive
? '<span class="badge bg-success">Activo</span>'
: '<span class="badge bg-danger">Inactivo</span>';
const adminActions = `
<button type="button" class="btn btn-sm btn-outline-warning edit-btn"
data-id="${patient.id}"
data-documenttype="${patient.documentType || ''}"
data-documentnumber="${patient.documentNumber || ''}"
data-firstname="${patient.firstName || ''}"
data-lastname="${patient.lastName || ''}"
data-email="${patient.email || ''}"
data-birthdate="${patient.birthDate || ''}"
data-address="${patient.address || ''}"
data-phone="${patient.phone || ''}"
data-emergencycontactname="${patient.emergencyContactName || ''}"
data-emergencycontactphone="${patient.emergencyContactPhone || ''}"
data-bloodtype="${patient.bloodType || ''}"
data-allergiesdescription="${patient.allergiesDescription || ''}"
data-hasmedicationallergies="${patient.hasMedicationAllergies || false}"
data-isactive="${patient.isActive || false}"
data-bs-toggle="modal" data-bs-target="#editModal">
<i class="bi bi-pencil"></i> Editar
</button>
<button type="button" class="btn btn-sm btn-outline-danger delete-btn"
data-id="${patient.id}"
data-name="${patient.firstName || ''} ${patient.lastName || ''}"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="bi bi-trash"></i> Eliminar
</button>
`;
row.innerHTML = `
<td>${patient.documentNumber || ''}</td>
<td>${patient.firstName || ''} ${patient.lastName || ''}</td>
<td>${patient.email || ''}</td>
<td>${patient.createdAt ? new Date(patient.createdAt).toLocaleDateString() : 'N/A'}</td>
<td>${statusBadge}</td>
<td class="text-end">
<a href="/Pacientes/Details/${encodeURIComponent(patient.id)}" class="btn btn-sm btn-outline-info">
<i class="bi bi-eye"></i> Ver
</a>
${patient.id ? adminActions : ''}
</td>
`;
tbody.prepend(row);
}
function updateRowInTable(patient) {
const row = document.querySelector(`#pacientesTable tbody tr[data-id="${patient.id}"]`);
if (row) {
const newRow = document.createElement('tr');
newRow.dataset.id = patient.id;
const statusBadge = patient.isActive
? '<span class="badge bg-success">Activo</span>'
: '<span class="badge bg-danger">Inactivo</span>';
const adminActions = `
<button type="button" class="btn btn-sm btn-outline-warning edit-btn"
data-id="${patient.id}"
data-documenttype="${patient.documentType || ''}"
data-documentnumber="${patient.documentNumber || ''}"
data-firstname="${patient.firstName || ''}"
data-lastname="${patient.lastName || ''}"
data-email="${patient.email || ''}"
data-birthdate="${patient.birthDate || ''}"
data-address="${patient.address || ''}"
data-phone="${patient.phone || ''}"
data-emergencycontactname="${patient.emergencyContactName || ''}"
data-emergencycontactphone="${patient.emergencyContactPhone || ''}"
data-bloodtype="${patient.bloodType || ''}"
data-allergiesdescription="${patient.allergiesDescription || ''}"
data-hasmedicationallergies="${patient.hasMedicationAllergies || false}"
data-isactive="${patient.isActive || false}"
data-bs-toggle="modal" data-bs-target="#editModal">
<i class="bi bi-pencil"></i> Editar
</button>
<button type="button" class="btn btn-sm btn-outline-danger delete-btn"
data-id="${patient.id}"
data-name="${patient.firstName || ''} ${patient.lastName || ''}"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="bi bi-trash"></i> Eliminar
</button>
`;
newRow.innerHTML = `
<td>${patient.documentNumber || ''}</td>
<td>${patient.firstName || ''} ${patient.lastName || ''}</td>
<td>${patient.email || ''}</td>
<td>${patient.createdAt ? new Date(patient.createdAt).toLocaleDateString() : 'N/A'}</td>
<td>${statusBadge}</td>
<td class="text-end">
<a href="/Pacientes/Details/${encodeURIComponent(patient.id)}" class="btn btn-sm btn-outline-info">
<i class="bi bi-eye"></i> Ver
</a>
${patient.id ? adminActions : ''}
</td>
`;
row.replaceWith(newRow);
}
}
function removeRowFromTable(id) {
const row = document.querySelector(`#pacientesTable tbody tr[data-id="${id}"]`);
if (row) row.remove();
}
});
</script>
}
+4 -1
View File
@@ -10,7 +10,10 @@
<title>@ViewData["Title"] - vita_asistente</title> <title>@ViewData["Title"] - vita_asistente</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> <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="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/vita_asistente.styles.css" asp-append-version="true" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<links rel="stylesheet" href="~/vita_asistente.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css"
>
</head> </head>
<body> <body>
<div class="app-wrapper"> <div class="app-wrapper">
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.
@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
-12
View File
@@ -1,12 +0,0 @@
{
"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