Agregar archivos de proyecto.
This commit is contained in:
745
MieSystem/Views/Expedientes/Details.cshtml
Normal file
745
MieSystem/Views/Expedientes/Details.cshtml
Normal file
@@ -0,0 +1,745 @@
|
||||
@model ExpedienteViewModel
|
||||
@{
|
||||
Layout = null;
|
||||
ViewData["Title"] = "Expediente - " + Model.NombreCompleto;
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@ViewData["Title"]</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<style>
|
||||
/* Estilos generales - Tono ejecutivo */
|
||||
:root {
|
||||
--primary-color: #2c3e50;
|
||||
--secondary-color: #3498db;
|
||||
--accent-color: #e74c3c;
|
||||
--success-color: #27ae60;
|
||||
--light-bg: #f8f9fa;
|
||||
--border-color: #dee2e6;
|
||||
}
|
||||
|
||||
/* Estilos optimizados para impresión */
|
||||
@@page {
|
||||
size: A4;
|
||||
margin: 15mm 20mm;
|
||||
}
|
||||
|
||||
@@media print {
|
||||
body {
|
||||
font-family: 'Segoe UI', 'Roboto', Arial, sans-serif;
|
||||
color: #000;
|
||||
background: white;
|
||||
font-size: 11pt;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.no-print, .print-controls {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-shadow: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.document-header {
|
||||
margin-bottom: 15px !important;
|
||||
padding: 15px 0 !important;
|
||||
}
|
||||
|
||||
.document-header h1 {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 15px !important;
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px !important;
|
||||
padding: 8px 15px !important;
|
||||
margin: -15px -15px 10px -15px !important;
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 100px !important;
|
||||
height: 100px !important;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 11px !important;
|
||||
padding-left: 18px !important;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: block !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
padding: 0 !important;
|
||||
min-width: 100% !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.photo-container {
|
||||
margin: 10px 0 !important;
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.document-footer {
|
||||
margin-top: 20px !important;
|
||||
font-size: 9px !important;
|
||||
}
|
||||
/* Eliminar todos los saltos de página automáticos */
|
||||
h1, h2, h3, h4, h5, .section, .section-title, .info-grid, .info-item {
|
||||
page-break-inside: avoid !important;
|
||||
page-break-after: avoid !important;
|
||||
page-break-before: avoid !important;
|
||||
}
|
||||
/* Permitir saltos de página solo donde sea necesario */
|
||||
.page-break {
|
||||
page-break-before: always;
|
||||
}
|
||||
}
|
||||
|
||||
/* Estilos para pantalla */
|
||||
body {
|
||||
font-family: 'Segoe UI', 'Roboto', Arial, sans-serif;
|
||||
max-width: 210mm;
|
||||
margin: 0 auto;
|
||||
background-color: #f5f7fa;
|
||||
color: #333;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
margin: 20px auto;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Botones de control - NO se imprimen */
|
||||
.print-controls {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #4a6491 100%);
|
||||
padding: 15px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #2980b9 0%, #1f639e 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: linear-gradient(135deg, #7f8c8d 0%, #6c7b7d 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(149, 165, 166, 0.3);
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: linear-gradient(135deg, #27ae60 0%, #219653 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-info:hover {
|
||||
background: linear-gradient(135deg, #219653 0%, #1e8449 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.3);
|
||||
}
|
||||
|
||||
/* Cabecera del documento - REVISADO para impresión */
|
||||
.document-header {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid var(--primary-color);
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.document-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--secondary-color), var(--success-color));
|
||||
}
|
||||
|
||||
.document-header h1 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
color: var(--primary-color);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.document-subtitle {
|
||||
color: var(--secondary-color);
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.document-meta {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
color: #666;
|
||||
font-size: 11px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Secciones del documento - REVISADO para impresión */
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
background: linear-gradient(to bottom, var(--secondary-color), var(--success-color));
|
||||
}
|
||||
|
||||
.section-title {
|
||||
background: linear-gradient(135deg, var(--light-bg), #e9ecef);
|
||||
padding: 10px 15px;
|
||||
margin: -15px -15px 15px -15px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
/* Layout de información */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed #eee;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 4px;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.info-label i {
|
||||
color: var(--secondary-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #444;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/* Foto del estudiante - REVISADO para impresión */
|
||||
.photo-container {
|
||||
text-align: center;
|
||||
margin: 15px 0;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.photo-frame {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
|
||||
border: 2px solid var(--secondary-color);
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
/* Layout de columnas - REVISADO para impresión */
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -10px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
min-width: 280px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 15px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.badge-male {
|
||||
background: linear-gradient(135deg, #3498db, #2980b9);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-female {
|
||||
background: linear-gradient(135deg, #e74c3c, #c0392b);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-active {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 12px;
|
||||
background: linear-gradient(135deg, #27ae60, #219653);
|
||||
color: white;
|
||||
border-radius: 15px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-active::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Pie de documento */
|
||||
.document-footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
line-height: 1.5;
|
||||
page-break-before: avoid;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background: var(--light-bg);
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid var(--secondary-color);
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* Watermark solo para impresión */
|
||||
@@media print {
|
||||
.watermark {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
font-size: 60px;
|
||||
color: rgba(0, 0, 0, 0.03);
|
||||
z-index: -1;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.watermark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Responsive para pantalla */
|
||||
@@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.print-controls {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.col {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.document-header h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clase especial para evitar problemas de impresión */
|
||||
.keep-together {
|
||||
page-break-inside: avoid !important;
|
||||
break-inside: avoid !important;
|
||||
}
|
||||
|
||||
/* Ajuste específico para la primera sección */
|
||||
.first-section {
|
||||
page-break-after: avoid !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Watermark solo visible al imprimir -->
|
||||
<div class="watermark">CONFIDENCIAL - MIE SYSTEM</div>
|
||||
|
||||
<!-- Controles de impresión - NO se imprimen -->
|
||||
<div class="print-controls no-print">
|
||||
<div>
|
||||
<button onclick="window.history.back()" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Regresar
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<button onclick="window.print()" class="btn btn-primary">
|
||||
<i class="bi bi-printer"></i> Imprimir Documento
|
||||
</button>
|
||||
<button onclick="downloadPDF()" class="btn btn-info">
|
||||
<i class="bi bi-download"></i> Guardar como PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenedor principal del documento -->
|
||||
<div class="container keep-together">
|
||||
<!-- Cabecera del documento -->
|
||||
<div class="document-header keep-together">
|
||||
<h1>EXPEDIENTE MIE SYSTEM</h1>
|
||||
<p class="document-subtitle">Sistema Integral de Gestión Educativa</p>
|
||||
<div class="document-meta">
|
||||
<div class="meta-item">
|
||||
<i class="bi bi-calendar"></i>
|
||||
<span>Generado: @DateTime.Now.ToString("dd/MM/yyyy HH:mm")</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class="bi bi-hash"></i>
|
||||
<span>ID Expediente: @Model.Id</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class="bi bi-shield-check"></i>
|
||||
<span>Documento Oficial</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información Personal -->
|
||||
<div class="section first-section keep-together">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-person-badge"></i> Información Personal
|
||||
</div>
|
||||
<div class="row keep-together">
|
||||
<div class="col">
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-person"></i> Nombre Completo
|
||||
</div>
|
||||
<div class="info-value">@Model.NombreCompleto</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-calendar-event"></i> Fecha de Nacimiento
|
||||
</div>
|
||||
<div class="info-value">@Model.FechaNacimiento.ToString("dd/MM/yyyy")</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-calculator"></i> Edad
|
||||
</div>
|
||||
<div class="info-value">@Model.Edad años</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-gender-ambiguous"></i> Sexo
|
||||
</div>
|
||||
<div class="info-value">
|
||||
@if (Model.Sexo == "M")
|
||||
{
|
||||
<span class="badge badge-male">
|
||||
<i class="bi bi-gender-male"></i> Masculino
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge badge-female">
|
||||
<i class="bi bi-gender-female"></i> Femenino
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="photo-container">
|
||||
<div class="photo-frame">
|
||||
<img src="@(string.IsNullOrEmpty(Model.FotoUrl) ? "/images/default-avatar.png" : Model.FotoUrl)"
|
||||
alt="Fotografía del estudiante"
|
||||
class="photo">
|
||||
</div>
|
||||
<p style="margin-top: 8px; font-size: 11px; color: #666;">
|
||||
<i class="bi bi-camera"></i> Fotografía actual
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-grid keep-together">
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-geo-alt"></i> Dirección
|
||||
</div>
|
||||
<div class="info-value">@Model.Direccion</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-telephone"></i> Teléfono de Contacto
|
||||
</div>
|
||||
<div class="info-value">@(string.IsNullOrEmpty(Model.Telefono) ? "No especificado" : Model.Telefono)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información de Padres y Responsable -->
|
||||
<div class="row keep-together">
|
||||
<div class="col">
|
||||
<div class="section keep-together">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-people"></i> Información de los Padres
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-person-standing"></i> Padre
|
||||
</div>
|
||||
<div class="info-value">@(string.IsNullOrEmpty(Model.NombrePadre) ? "No especificado" : Model.NombrePadre)</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-person-standing-dress"></i> Madre
|
||||
</div>
|
||||
<div class="info-value">@(string.IsNullOrEmpty(Model.NombreMadre) ? "No especificado" : Model.NombreMadre)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="section keep-together">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-person-heart"></i> Responsable a Cargo
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-person-check"></i> Nombre del Responsable
|
||||
</div>
|
||||
<div class="info-value">@Model.NombreResponsable</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-diagram-3"></i> Parentesco
|
||||
</div>
|
||||
<div class="info-value">@(string.IsNullOrEmpty(Model.ParentescoResponsable) ? "Responsable" : Model.ParentescoResponsable)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Observaciones -->
|
||||
@if (!string.IsNullOrEmpty(Model.Observaciones))
|
||||
{
|
||||
<div class="section keep-together">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-chat-left-text"></i> Observaciones y Notas
|
||||
</div>
|
||||
<div class="info-value" style="white-space: pre-line; background: #f8f9fa; padding: 12px; border-radius: 5px; border-left: 3px solid var(--secondary-color); font-size: 12px;">
|
||||
@Model.Observaciones
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Información del Expediente -->
|
||||
<div class="section keep-together">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-folder-check"></i> Información del Expediente
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-hash"></i> ID del Expediente
|
||||
</div>
|
||||
<div class="info-value">@Model.Id</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-check-circle"></i> Estado
|
||||
</div>
|
||||
<div class="info-value">
|
||||
<span class="status-active">ACTIVO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">
|
||||
<i class="bi bi-clock-history"></i> Última Actualización
|
||||
</div>
|
||||
<div class="info-value">@DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pie de documento -->
|
||||
<div class="document-footer keep-together">
|
||||
<p><strong>MIE SYSTEM</strong> - Sistema Integral de Gestión Educativa</p>
|
||||
<p>Este documento ha sido generado automáticamente y es de carácter oficial.</p>
|
||||
<div class="footer-note">
|
||||
<i class="bi bi-shield-exclamation"></i> Documento confidencial - Uso exclusivo institucional
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Función para simular descarga de PDF
|
||||
function downloadPDF() {
|
||||
alert('Funcionalidad de descarga de PDF en desarrollo.\nPor ahora, use la opción "Imprimir" y seleccione "Guardar como PDF" en el diálogo de impresión.');
|
||||
}
|
||||
|
||||
// Prevenir que la página se cierre automáticamente
|
||||
window.onbeforeunload = null;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
647
MieSystem/Views/Expedientes/Index.cshtml
Normal file
647
MieSystem/Views/Expedientes/Index.cshtml
Normal file
@@ -0,0 +1,647 @@
|
||||
@{
|
||||
ViewData["Title"] = "Expedientes";
|
||||
ViewData["ActionButtons"] = @"<button class='btn btn-primary' data-bs-toggle='modal' data-bs-target='#createModal'>
|
||||
<i class='bi bi-plus-circle me-1'></i> Nuevo Expediente
|
||||
</button>";
|
||||
}
|
||||
|
||||
<div class="row mb-4">
|
||||
<!-- Tarjeta de total de niños registrados -->
|
||||
<div class="col-md-4">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="card-title text-primary">Total de Niños</h5>
|
||||
<h2 class="text-primary" id="totalNinos">0</h2>
|
||||
<p class="card-text text-muted">Registrados en el sistema</p>
|
||||
</div>
|
||||
<div class="bg-primary text-white rounded-circle p-3">
|
||||
<i class="bi bi-people-fill" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tarjeta de cumpleaños del mes -->
|
||||
<div class="col-md-4">
|
||||
<div class="card border-success">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="card-title text-success">Cumpleaños del Mes</h5>
|
||||
<h2 class="text-success" id="cumpleanerosMes">0</h2>
|
||||
<p class="card-text text-muted">Cumplen años este mes</p>
|
||||
</div>
|
||||
<div class="bg-success text-white rounded-circle p-3">
|
||||
<i class="bi bi-balloon-fill" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tarjeta de mayores de 14 años -->
|
||||
<div class="col-md-4">
|
||||
<div class="card border-warning">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="card-title text-warning">Mayores de 14 años</h5>
|
||||
<h2 class="text-warning" id="mayores14">0</h2>
|
||||
<p class="card-text text-muted">Niños con 14+ años</p>
|
||||
</div>
|
||||
<div class="bg-warning text-white rounded-circle p-3">
|
||||
<i class="bi bi-person-badge" style="font-size: 2rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de expedientes -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Lista de Expedientes</h5>
|
||||
<div class="input-group" style="max-width: 300px;">
|
||||
<input type="text" class="form-control" placeholder="Buscar niño..." id="searchInput">
|
||||
<button class="btn btn-outline-secondary" type="button" id="searchButton">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="expedientesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Foto</th>
|
||||
<th>Nombre Completo</th>
|
||||
<th>Edad</th>
|
||||
<th>Sexo</th>
|
||||
<th>Fecha Nacimiento</th>
|
||||
<th>Responsable</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="expedientesTableBody">
|
||||
<!-- Los datos se cargarán dinámicamente -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Paginación -->
|
||||
<nav aria-label="Page navigation" class="mt-3">
|
||||
<ul class="pagination justify-content-center" id="pagination">
|
||||
<!-- La paginación se generará dinámicamente -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para crear nuevo expediente -->
|
||||
<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">
|
||||
<h5 class="modal-title" id="createModalLabel">Nuevo Expediente</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@await Html.PartialAsync("_CreateOrEdit", new ExpedienteViewModel())
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="button" class="btn btn-primary" id="saveExpediente">Guardar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para editar expediente -->
|
||||
<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">
|
||||
<h5 class="modal-title" id="editModalLabel">Editar Expediente</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="editModalBody">
|
||||
<!-- El contenido se cargará dinámicamente -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="button" class="btn btn-primary" id="updateExpediente">Actualizar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
// Variables globales
|
||||
let currentPage = 1;
|
||||
const itemsPerPage = 10;
|
||||
let allExpedientes = [];
|
||||
|
||||
$(document).ready(function() {
|
||||
// Cargar datos iniciales
|
||||
loadDashboardStats();
|
||||
loadExpedientes();
|
||||
|
||||
// Configurar búsqueda
|
||||
$('#searchButton').click(searchExpedientes);
|
||||
$('#searchInput').on('keyup', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
searchExpedientes();
|
||||
}
|
||||
});
|
||||
|
||||
// Configurar guardar expediente
|
||||
$('#saveExpediente').click(saveExpediente);
|
||||
$('#updateExpediente').click(updateExpediente);
|
||||
|
||||
// Configurar subida de imagen independiente
|
||||
$(document).on('change', '#Foto, #FotoEdit', function() {
|
||||
const input = this;
|
||||
const isEdit = $(input).attr('id') === 'FotoEdit';
|
||||
const previewId = isEdit ? 'fotoPreviewEdit' : 'fotoPreview';
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
// Mostrar vista previa inmediata
|
||||
previewImage(input, previewId);
|
||||
|
||||
// Subir imagen al servidor
|
||||
uploadImage(input.files[0], isEdit);
|
||||
}
|
||||
});
|
||||
|
||||
// Configurar botones de eliminar imagen
|
||||
$(document).on('click', '#deleteFoto, #deleteFotoEdit', function() {
|
||||
const isEdit = $(this).attr('id') === 'deleteFotoEdit';
|
||||
deleteImage(isEdit);
|
||||
});
|
||||
});
|
||||
|
||||
// Cargar estadísticas del dashboard
|
||||
function loadDashboardStats() {
|
||||
$.ajax({
|
||||
url: '/Expedientes/GetDashboardStats',
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
$('#totalNinos').text(data.totalNinos);
|
||||
$('#cumpleanerosMes').text(data.cumpleanerosMes);
|
||||
$('#mayores14').text(data.mayores14);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cargar expedientes
|
||||
function loadExpedientes(page = 1) {
|
||||
currentPage = page;
|
||||
$.ajax({
|
||||
url: `/Expedientes/GetExpedientes?page=${page}&pageSize=${itemsPerPage}`,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
allExpedientes = data.items;
|
||||
renderExpedientesTable(data.items);
|
||||
renderPagination(data.totalItems, data.totalPages, page);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Renderizar tabla de expedientes
|
||||
function renderExpedientesTable(expedientes) {
|
||||
const tbody = $('#expedientesTableBody');
|
||||
tbody.empty();
|
||||
|
||||
if (expedientes.length === 0) {
|
||||
tbody.append(`
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4">
|
||||
<i class="bi bi-folder-x" style="font-size: 3rem; color: #6c757d;"></i>
|
||||
<p class="mt-2">No se encontraron expedientes</p>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
return;
|
||||
}
|
||||
|
||||
expedientes.forEach(expediente => {
|
||||
const edad = calculateAge(expediente.fechaNacimiento);
|
||||
const row = `
|
||||
<tr>
|
||||
<td>
|
||||
<div class="avatar avatar-sm">
|
||||
<img src="${expediente.fotoUrl || '/images/default-avatar.png'}"
|
||||
alt="${expediente.nombre}"
|
||||
class="rounded-circle"
|
||||
style="width: 40px; height: 40px; object-fit: cover;">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<strong>${expediente.nombre} ${expediente.apellidos}</strong>
|
||||
</td>
|
||||
<td>${edad} años</td>
|
||||
<td>
|
||||
<span class="badge ${expediente.sexo === 'M' ? 'bg-info' : 'bg-pink'}">
|
||||
${expediente.sexo === 'M' ? 'Masculino' : 'Femenino'}
|
||||
</span>
|
||||
</td>
|
||||
<td>${formatDate(expediente.fechaNacimiento)}</td>
|
||||
<td>${expediente.nombreResponsable}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="viewExpediente(${expediente.id})" title="Ver">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-warning" onclick="editExpediente(${expediente.id})" title="Editar">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="deleteExpediente(${expediente.id})" title="Eliminar">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
tbody.append(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Renderizar paginación
|
||||
function renderPagination(totalItems, totalPages, currentPage) {
|
||||
const pagination = $('#pagination');
|
||||
pagination.empty();
|
||||
|
||||
// Botón anterior
|
||||
pagination.append(`
|
||||
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadExpedientes(${currentPage - 1})" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
`);
|
||||
|
||||
// Números de página
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pagination.append(`
|
||||
<li class="page-item ${i === currentPage ? 'active' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadExpedientes(${i})">${i}</a>
|
||||
</li>
|
||||
`);
|
||||
}
|
||||
|
||||
// Botón siguiente
|
||||
pagination.append(`
|
||||
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadExpedientes(${currentPage + 1})" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
`);
|
||||
}
|
||||
|
||||
// Buscar expedientes
|
||||
function searchExpedientes() {
|
||||
const searchTerm = $('#searchInput').val().toLowerCase();
|
||||
|
||||
if (searchTerm.trim() === '') {
|
||||
loadExpedientes(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = allExpedientes.filter(exp =>
|
||||
exp.nombre.toLowerCase().includes(searchTerm) ||
|
||||
exp.apellidos.toLowerCase().includes(searchTerm) ||
|
||||
exp.nombreResponsable.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
|
||||
renderExpedientesTable(filtered);
|
||||
$('#pagination').empty(); // Limpiar paginación en búsqueda
|
||||
}
|
||||
|
||||
// Guardar nuevo expediente
|
||||
function saveExpediente() {
|
||||
const formData = new FormData($('#createForm')[0]);
|
||||
|
||||
$.ajax({
|
||||
url: '/Expedientes/Create',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
// Mostrar siempre el mensaje que viene en response.message
|
||||
const message = response.message ||
|
||||
(response.success ? 'Operación completada' : 'Error en la operación');
|
||||
|
||||
if (response.success) {
|
||||
$('#createModal').modal('hide');
|
||||
loadDashboardStats();
|
||||
loadExpedientes(currentPage);
|
||||
showAlert('success', message);
|
||||
} else {
|
||||
showAlert('error', message);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
// Extraer el message del JSON si existe
|
||||
let message = 'Error en la solicitud';
|
||||
try {
|
||||
const jsonResponse = JSON.parse(xhr.responseText);
|
||||
if (jsonResponse && jsonResponse.message) {
|
||||
message = jsonResponse.message;
|
||||
}
|
||||
} catch (e) {
|
||||
// Si no es JSON válido, usar el texto de respuesta
|
||||
if (xhr.responseText) {
|
||||
message = xhr.responseText;
|
||||
}
|
||||
}
|
||||
showAlert('error', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Editar expediente
|
||||
function editExpediente(id) {
|
||||
$.ajax({
|
||||
url: `/Expedientes/Edit/${id}`,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
$('#editModalBody').html(data);
|
||||
$('#editModal').modal('show');
|
||||
|
||||
// Mostrar/ocultar botón de eliminar imagen según si hay imagen personalizada
|
||||
const fotoUrl = $('#editForm input[name="FotoUrl"]').val();
|
||||
const deleteBtn = $('#deleteFotoEdit');
|
||||
if (fotoUrl && fotoUrl !== '/images/default-avatar.png') {
|
||||
deleteBtn.show();
|
||||
} else {
|
||||
deleteBtn.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Actualizar expediente
|
||||
function updateExpediente() {
|
||||
const formData = new FormData($('#editForm')[0]);
|
||||
const id = $('#editForm').data('id');
|
||||
|
||||
$.ajax({
|
||||
url: `/Expedientes/Edit/${id}`,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$('#editModal').modal('hide');
|
||||
loadDashboardStats();
|
||||
loadExpedientes(currentPage);
|
||||
showAlert('success', 'Expediente actualizado exitosamente');
|
||||
} else {
|
||||
showAlert('error', response.message || 'Error al actualizar expediente');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showAlert('error', 'Error al procesar la solicitud');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ver expediente (detalle)
|
||||
function viewExpediente(id) {
|
||||
window.location.href = `/Expedientes/Details/${id}`;
|
||||
}
|
||||
|
||||
// Eliminar expediente
|
||||
function deleteExpediente(id) {
|
||||
if (confirm('¿Está seguro de eliminar este expediente?')) {
|
||||
$.ajax({
|
||||
url: `/Expedientes/Delete/${id}`,
|
||||
type: 'DELETE',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
loadDashboardStats();
|
||||
loadExpedientes(currentPage);
|
||||
showAlert('success', 'Expediente eliminado exitosamente');
|
||||
} else {
|
||||
showAlert('error', response.message || 'Error al eliminar expediente');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ========== FUNCIONES PARA MANEJO DE IMÁGENES ==========
|
||||
|
||||
// Mostrar vista previa de imagen
|
||||
function previewImage(input, previewId) {
|
||||
const preview = document.getElementById(previewId);
|
||||
const file = input.files[0];
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Subir imagen al servidor
|
||||
function uploadImage(file, isEdit = false) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
// Mostrar indicador de carga
|
||||
const previewId = isEdit ? 'fotoPreviewEdit' : 'fotoPreview';
|
||||
const preview = document.getElementById(previewId);
|
||||
const originalOpacity = preview.style.opacity;
|
||||
preview.style.opacity = '0.5';
|
||||
|
||||
$.ajax({
|
||||
url: '/Expedientes/UploadImage',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
preview.style.opacity = originalOpacity;
|
||||
|
||||
if (response.success) {
|
||||
// Actualizar el campo oculto con la URL de la imagen
|
||||
const formId = isEdit ? 'editForm' : 'createForm';
|
||||
const hiddenInput = $(`#${formId} input[name="FotoUrl"]`);
|
||||
|
||||
if (hiddenInput.length === 0) {
|
||||
// Crear campo oculto si no existe
|
||||
$(`#${formId}`).append(`<input type="hidden" name="FotoUrl" value="${response.imageUrl}" />`);
|
||||
} else {
|
||||
// Actualizar valor existente
|
||||
hiddenInput.val(response.imageUrl);
|
||||
}
|
||||
|
||||
// Mostrar botón de eliminar
|
||||
const deleteBtnId = isEdit ? 'deleteFotoEdit' : 'deleteFoto';
|
||||
$(`#${deleteBtnId}`).show();
|
||||
|
||||
showAlert('success', 'Imagen subida exitosamente');
|
||||
} else {
|
||||
showAlert('error', response.message || 'Error al subir imagen');
|
||||
// Restaurar imagen predeterminada
|
||||
preview.src = '/images/default-avatar.png';
|
||||
// Ocultar botón de eliminar
|
||||
const deleteBtnId = isEdit ? 'deleteFotoEdit' : 'deleteFoto';
|
||||
$(`#${deleteBtnId}`).hide();
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
preview.style.opacity = originalOpacity;
|
||||
let message = 'Error en la solicitud';
|
||||
try {
|
||||
const jsonResponse = JSON.parse(xhr.responseText);
|
||||
if (jsonResponse && jsonResponse.message) {
|
||||
message = jsonResponse.message;
|
||||
}
|
||||
} catch (e) {
|
||||
if (xhr.responseText) {
|
||||
message = xhr.responseText;
|
||||
}
|
||||
}
|
||||
showAlert('error', message);
|
||||
// Restaurar imagen predeterminada
|
||||
preview.src = '/images/default-avatar.png';
|
||||
// Ocultar botón de eliminar
|
||||
const deleteBtnId = isEdit ? 'deleteFotoEdit' : 'deleteFoto';
|
||||
$(`#${deleteBtnId}`).hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Eliminar imagen
|
||||
function deleteImage(isEdit = false) {
|
||||
const formId = isEdit ? 'editForm' : 'createForm';
|
||||
const hiddenInput = $(`#${formId} input[name="FotoUrl"]`);
|
||||
const imageUrl = hiddenInput.val();
|
||||
const previewId = isEdit ? 'fotoPreviewEdit' : 'fotoPreview';
|
||||
const deleteBtnId = isEdit ? 'deleteFotoEdit' : 'deleteFoto';
|
||||
|
||||
if (imageUrl && imageUrl !== '/images/default-avatar.png') {
|
||||
if (confirm('¿Está seguro de eliminar esta imagen?')) {
|
||||
$.ajax({
|
||||
url: '/Expedientes/DeleteImage',
|
||||
type: 'DELETE',
|
||||
data: { imageUrl: imageUrl },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Restaurar imagen predeterminada
|
||||
$(`#${previewId}`).attr('src', '/images/default-avatar.png');
|
||||
hiddenInput.val('/images/default-avatar.png');
|
||||
$(`#${deleteBtnId}`).hide();
|
||||
showAlert('success', 'Imagen eliminada exitosamente');
|
||||
} else {
|
||||
showAlert('error', response.message || 'Error al eliminar imagen');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showAlert('error', 'Error al procesar la solicitud');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== FUNCIONES UTILITARIAS ==========
|
||||
|
||||
// Calcular edad
|
||||
function calculateAge(birthDate) {
|
||||
const today = new Date();
|
||||
const birth = new Date(birthDate);
|
||||
let age = today.getFullYear() - birth.getFullYear();
|
||||
const monthDiff = today.getMonth() - birth.getMonth();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
||||
age--;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
||||
|
||||
// Formatear fecha
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('es-ES', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar alerta
|
||||
function showAlert(type, message) {
|
||||
// Crear elemento de alerta
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
|
||||
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 1050; min-width: 300px;';
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
`;
|
||||
|
||||
// Agregar al body
|
||||
document.body.appendChild(alertDiv);
|
||||
|
||||
// Auto-eliminar después de 5 segundos
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.bg-pink {
|
||||
background-color: #e83e8c !important;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.table th {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
}
|
||||
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.35em 0.65em;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.modal-lg {
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
176
MieSystem/Views/Expedientes/_CreateOrEdit.cshtml
Normal file
176
MieSystem/Views/Expedientes/_CreateOrEdit.cshtml
Normal file
@@ -0,0 +1,176 @@
|
||||
@using MieSystem.Models;
|
||||
|
||||
@model ExpedienteViewModel
|
||||
|
||||
<form id="@(ViewData["IsEdit"] != null && (bool)ViewData["IsEdit"] ? "editForm" : "createForm")"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
data-id="@(Model?.Id ?? 0)">
|
||||
|
||||
@if (ViewData["IsEdit"] != null && (bool)ViewData["IsEdit"])
|
||||
{
|
||||
<input type="hidden" asp-for="Id" />
|
||||
}
|
||||
|
||||
<!-- Campo oculto para almacenar la URL de la imagen subida -->
|
||||
<input type="hidden" asp-for="FotoUrl" />
|
||||
|
||||
<div class="row">
|
||||
<!-- Columna izquierda - Datos personales -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0">Datos Personales</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Foto -->
|
||||
<div class="mb-3 text-center">
|
||||
<div class="mb-2">
|
||||
<img id="@(ViewData["IsEdit"] != null && (bool)ViewData["IsEdit"] ? "fotoPreviewEdit" : "fotoPreview")"
|
||||
src="@(Model?.FotoUrl ?? "/images/default-avatar.png")"
|
||||
alt="Foto del niño"
|
||||
class="rounded-circle border"
|
||||
style="width: 120px; height: 120px; object-fit: cover;">
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||
<label class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-camera me-1"></i> Seleccionar Foto
|
||||
<input type="file"
|
||||
asp-for="Foto"
|
||||
id="@(ViewData["IsEdit"] != null && (bool)ViewData["IsEdit"] ? "FotoEdit" : "Foto")"
|
||||
class="d-none"
|
||||
accept="image/*">
|
||||
</label>
|
||||
|
||||
<!-- Botón para eliminar imagen -->
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
id="@(ViewData["IsEdit"] != null && (bool)ViewData["IsEdit"] ? "deleteFotoEdit" : "deleteFoto")"
|
||||
style="display: @((Model?.FotoUrl != null && Model.FotoUrl != "/images/default-avatar.png") ? "block" : "none");">
|
||||
<i class="bi bi-trash me-1"></i> Eliminar
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<small class="text-muted">Formatos permitidos: JPG, PNG, GIF, BMP. Tamaño máximo: 5MB</small>
|
||||
</div>
|
||||
<span asp-validation-for="Foto" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Nombre -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Nombre" class="form-label">Nombre *</label>
|
||||
<input asp-for="Nombre" class="form-control" placeholder="Ingrese el nombre">
|
||||
<span asp-validation-for="Nombre" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Apellidos -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Apellidos" class="form-label">Apellidos *</label>
|
||||
<input asp-for="Apellidos" class="form-control" placeholder="Ingrese los apellidos">
|
||||
<span asp-validation-for="Apellidos" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Nacimiento -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="FechaNacimiento" class="form-label">Fecha de Nacimiento *</label>
|
||||
<input asp-for="FechaNacimiento" type="date" class="form-control">
|
||||
<span asp-validation-for="FechaNacimiento" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Sexo -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Sexo" class="form-label">Sexo *</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" asp-for="Sexo" value="M" id="sexoM">
|
||||
<label class="form-check-label" for="sexoM">Masculino</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" asp-for="Sexo" value="F" id="sexoF">
|
||||
<label class="form-check-label" for="sexoF">Femenino</label>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="Sexo" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Columna derecha - Datos familiares y dirección -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0">Datos Familiares</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Nombre del Padre -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="NombrePadre" class="form-label">Nombre del Padre</label>
|
||||
<input asp-for="NombrePadre" class="form-control" placeholder="Ingrese nombre del padre">
|
||||
<span asp-validation-for="NombrePadre" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Nombre de la Madre -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="NombreMadre" class="form-label">Nombre de la Madre</label>
|
||||
<input asp-for="NombreMadre" class="form-control" placeholder="Ingrese nombre de la madre">
|
||||
<span asp-validation-for="NombreMadre" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Nombre del Responsable -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="NombreResponsable" class="form-label">Nombre del Responsable *</label>
|
||||
<input asp-for="NombreResponsable" class="form-control" placeholder="Ingrese nombre del responsable">
|
||||
<span asp-validation-for="NombreResponsable" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Parentesco del Responsable -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="ParentescoResponsable" class="form-label">Parentesco del Responsable</label>
|
||||
<select asp-for="ParentescoResponsable" class="form-select">
|
||||
<option value="">Seleccione parentesco</option>
|
||||
<option value="Padre">Padre</option>
|
||||
<option value="Madre">Madre</option>
|
||||
<option value="Abuelo">Abuelo</option>
|
||||
<option value="Abuela">Abuela</option>
|
||||
<option value="Tío">Tío</option>
|
||||
<option value="Tía">Tía</option>
|
||||
<option value="Hermano">Hermano</option>
|
||||
<option value="Hermana">Hermana</option>
|
||||
<option value="Otro">Otro</option>
|
||||
</select>
|
||||
<span asp-validation-for="ParentescoResponsable" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0">Dirección y Contacto</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Dirección -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Direccion" class="form-label">Dirección *</label>
|
||||
<textarea asp-for="Direccion" class="form-control" rows="3" placeholder="Ingrese dirección completa"></textarea>
|
||||
<span asp-validation-for="Direccion" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Teléfono -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Telefono" class="form-label">Teléfono de Contacto</label>
|
||||
<input asp-for="Telefono" class="form-control" placeholder="Ingrese número telefónico">
|
||||
<span asp-validation-for="Telefono" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<!-- Observaciones -->
|
||||
<div class="mb-3">
|
||||
<label asp-for="Observaciones" class="form-label">Observaciones</label>
|
||||
<textarea asp-for="Observaciones" class="form-control" rows="2" placeholder="Observaciones adicionales"></textarea>
|
||||
<span asp-validation-for="Observaciones" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
Reference in New Issue
Block a user