service worker
This commit is contained in:
@@ -261,193 +261,284 @@
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
let timeoutBusqueda = null;
|
||||
|
||||
// Búsqueda de miembros
|
||||
document.getElementById('buscarMiembro').addEventListener('input', function(e) {
|
||||
const termino = e.target.value;
|
||||
const resultadosDiv = document.getElementById('resultadosBusqueda');
|
||||
|
||||
clearTimeout(timeoutBusqueda);
|
||||
|
||||
if (termino.length < 2) {
|
||||
resultadosDiv.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
timeoutBusqueda = setTimeout(async () => {
|
||||
try {
|
||||
const response = await fetch('@Url.Action("BuscarMiembros", "Colaboracion")?termino=' + encodeURIComponent(termino));
|
||||
const miembros = await response.json();
|
||||
|
||||
if (miembros.length === 0) {
|
||||
resultadosDiv.innerHTML = '<div class="list-group-item text-muted">No se encontraron resultados</div>';
|
||||
resultadosDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
miembros.forEach(miembro => {
|
||||
html += `
|
||||
<button type="button" class="list-group-item list-group-item-action" onclick="seleccionarMiembro(${miembro.id}, '${miembro.text}')">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<i class="bi bi-person me-2"></i>
|
||||
<strong>${miembro.text}</strong>
|
||||
</div>
|
||||
${miembro.telefono ? '<small class="text-muted">' + miembro.telefono + '</small>' : ''}
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
});
|
||||
|
||||
resultadosDiv.innerHTML = html;
|
||||
resultadosDiv.style.display = 'block';
|
||||
} catch (error) {
|
||||
console.error('Error al buscar miembros:', error);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Cerrar resultados cuando se hace clic fuera
|
||||
document.addEventListener('click', function(e) {
|
||||
const buscarInput = document.getElementById('buscarMiembro');
|
||||
const resultadosDiv = document.getElementById('resultadosBusqueda');
|
||||
|
||||
if (!buscarInput.contains(e.target) && !resultadosDiv.contains(e.target)) {
|
||||
resultadosDiv.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
function seleccionarMiembro(id, nombre) {
|
||||
document.getElementById('miembroIdHidden').value = id;
|
||||
document.getElementById('nombreMiembroSeleccionado').textContent = nombre;
|
||||
document.getElementById('miembroSeleccionado').style.display = 'block';
|
||||
document.getElementById('buscarMiembro').value = '';
|
||||
document.getElementById('buscarMiembro').style.display = 'none';
|
||||
document.getElementById('resultadosBusqueda').style.display = 'none';
|
||||
|
||||
// Cargar historial de pagos
|
||||
cargarHistorialPagos(id);
|
||||
}
|
||||
|
||||
function limpiarMiembro() {
|
||||
document.getElementById('miembroIdHidden').value = '';
|
||||
document.getElementById('miembroSeleccionado').style.display = 'none';
|
||||
document.getElementById('buscarMiembro').style.display = 'block';
|
||||
document.getElementById('buscarMiembro').focus();
|
||||
|
||||
// Ocultar historial
|
||||
document.getElementById('infoUltimosPagos').style.display = 'none';
|
||||
document.getElementById('listaUltimosPagos').innerHTML = '';
|
||||
}
|
||||
|
||||
async function cargarHistorialPagos(miembroId) {
|
||||
const contenedor = document.getElementById('infoUltimosPagos');
|
||||
const lista = document.getElementById('listaUltimosPagos');
|
||||
|
||||
lista.innerHTML = '<div class="spinner-border spinner-border-sm text-info" role="status"></div> Cargando historial...';
|
||||
contenedor.style.display = 'block';
|
||||
|
||||
try {
|
||||
const response = await fetch('@Url.Action("ObtenerUltimosPagos", "Colaboracion")?miembroId=' + miembroId);
|
||||
const pagos = await response.json();
|
||||
|
||||
if (pagos && pagos.length > 0) {
|
||||
let html = '';
|
||||
pagos.forEach(p => {
|
||||
const colorClass = p.ultimoMes > 0 ? 'bg-white text-info border border-info' : 'bg-secondary text-white';
|
||||
html += `
|
||||
<span class="badge ${colorClass} fw-normal p-2">
|
||||
<strong>${p.nombreTipo}:</strong> ${p.ultimoPeriodoTexto}
|
||||
</span>
|
||||
`;
|
||||
});
|
||||
lista.innerHTML = html;
|
||||
} else {
|
||||
lista.innerHTML = '<span class="text-muted small">No hay historial de pagos registrado.</span>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al cargar historial:', error);
|
||||
lista.innerHTML = '<span class="text-danger small"><i class="bi bi-exclamation-circle"></i> Error al cargar historial</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function calcularSugerido() {
|
||||
try {
|
||||
// Obtener valores
|
||||
const mesInicial = parseInt(document.getElementById('mesInicial').value);
|
||||
const anioInicial = parseInt(document.getElementById('anioInicial').value);
|
||||
const mesFinal = parseInt(document.getElementById('mesFinal').value);
|
||||
const anioFinal = parseInt(document.getElementById('anioFinal').value);
|
||||
|
||||
// Calcular total de meses
|
||||
const fechaInicial = new Date(anioInicial, mesInicial - 1, 1);
|
||||
const fechaFinal = new Date(anioFinal, mesFinal - 1, 1);
|
||||
|
||||
let totalMeses = 0;
|
||||
if (fechaFinal >= fechaInicial) {
|
||||
totalMeses = ((anioFinal - anioInicial) * 12) + (mesFinal - mesInicial) + 1;
|
||||
}
|
||||
|
||||
// Obtener tipos seleccionados y sus montos
|
||||
const tiposCheckboxes = document.querySelectorAll('.tipo-checkbox:checked');
|
||||
const totalTipos = tiposCheckboxes.length;
|
||||
|
||||
// Calcular monto sugerido total
|
||||
let montoSugeridoTotal = 0;
|
||||
tiposCheckboxes.forEach(checkbox => {
|
||||
const montoPorMes = parseFloat(checkbox.getAttribute('data-monto')) || 0;
|
||||
montoSugeridoTotal += montoPorMes * totalMeses;
|
||||
});
|
||||
|
||||
// Actualizar UI
|
||||
document.getElementById('totalMeses').textContent = totalMeses;
|
||||
document.getElementById('totalTipos').textContent = totalTipos;
|
||||
document.getElementById('montoSugerido').textContent = montoSugeridoTotal.toFixed(2);
|
||||
|
||||
// Comparar con monto ingresado
|
||||
const montoIngresado = parseFloat(document.getElementById('montoTotal').value) || 0;
|
||||
const alertaDiv = document.getElementById('alertaDiferencia');
|
||||
const mensajeSpan = document.getElementById('mensajeDiferencia');
|
||||
|
||||
if (montoIngresado > 0) {
|
||||
if (Math.abs(montoIngresado - montoSugeridoTotal) > 0.01) {
|
||||
const diferencia = montoIngresado - montoSugeridoTotal;
|
||||
if (diferencia > 0) {
|
||||
mensajeSpan.textContent = `Sobra: $${diferencia.toFixed(2)}`;
|
||||
alertaDiv.className = 'alert alert-info py-1 px-2 mb-0 mt-2';
|
||||
} else {
|
||||
mensajeSpan.textContent = `Falta: $${Math.abs(diferencia).toFixed(2)}`;
|
||||
alertaDiv.className = 'alert alert-warning py-1 px-2 mb-0 mt-2';
|
||||
}
|
||||
alertaDiv.style.display = 'block';
|
||||
} else {
|
||||
alertaDiv.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
alertaDiv.style.display = 'none';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al calcular sugerido:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Calcular cuando cambia el monto total
|
||||
document.getElementById('montoTotal')?.addEventListener('input', calcularSugerido);
|
||||
|
||||
// Calcular al cargar la página
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
calcularSugerido();
|
||||
});
|
||||
|
||||
// Show error messages
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<text>
|
||||
toastr.error('@TempData["Error"]');
|
||||
</text>
|
||||
<!-- Offline Support Scripts -->
|
||||
<script src="~/js/colaboraciones-offline-db.js"></script>
|
||||
<script src="~/js/colaboraciones-sync.js"></script>
|
||||
|
||||
<script>
|
||||
let timeoutBusqueda = null;
|
||||
|
||||
// Búsqueda de miembros
|
||||
document.getElementById('buscarMiembro').addEventListener('input', function(e) {
|
||||
const termino = e.target.value;
|
||||
const resultadosDiv = document.getElementById('resultadosBusqueda');
|
||||
|
||||
clearTimeout(timeoutBusqueda);
|
||||
|
||||
if (termino.length < 2) {
|
||||
resultadosDiv.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
timeoutBusqueda = setTimeout(async () => {
|
||||
try {
|
||||
const response = await fetch('@Url.Action("BuscarMiembros", "Colaboracion")?termino=' + encodeURIComponent(termino));
|
||||
const miembros = await response.json();
|
||||
|
||||
if (miembros.length === 0) {
|
||||
resultadosDiv.innerHTML = '<div class="list-group-item text-muted">No se encontraron resultados</div>';
|
||||
resultadosDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
miembros.forEach(miembro => {
|
||||
html += `
|
||||
<button type="button" class="list-group-item list-group-item-action" onclick="seleccionarMiembro(${miembro.id}, '${miembro.text}')">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<i class="bi bi-person me-2"></i>
|
||||
<strong>${miembro.text}</strong>
|
||||
</div>
|
||||
${miembro.telefono ? '<small class="text-muted">' + miembro.telefono + '</small>' : ''}
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
});
|
||||
|
||||
resultadosDiv.innerHTML = html;
|
||||
resultadosDiv.style.display = 'block';
|
||||
} catch (error) {
|
||||
console.error('Error al buscar miembros:', error);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Cerrar resultados cuando se hace clic fuera
|
||||
document.addEventListener('click', function(e) {
|
||||
const buscarInput = document.getElementById('buscarMiembro');
|
||||
const resultadosDiv = document.getElementById('resultadosBusqueda');
|
||||
|
||||
if (!buscarInput.contains(e.target) && !resultadosDiv.contains(e.target)) {
|
||||
resultadosDiv.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
function seleccionarMiembro(id, nombre) {
|
||||
document.getElementById('miembroIdHidden').value = id;
|
||||
document.getElementById('nombreMiembroSeleccionado').textContent = nombre;
|
||||
document.getElementById('miembroSeleccionado').style.display = 'block';
|
||||
document.getElementById('buscarMiembro').value = '';
|
||||
document.getElementById('buscarMiembro').style.display = 'none';
|
||||
document.getElementById('resultadosBusqueda').style.display = 'none';
|
||||
|
||||
// Cargar historial de pagos
|
||||
cargarHistorialPagos(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
function limpiarMiembro() {
|
||||
document.getElementById('miembroIdHidden').value = '';
|
||||
document.getElementById('miembroSeleccionado').style.display = 'none';
|
||||
document.getElementById('buscarMiembro').style.display = 'block';
|
||||
document.getElementById('buscarMiembro').focus();
|
||||
|
||||
// Ocultar historial
|
||||
document.getElementById('infoUltimosPagos').style.display = 'none';
|
||||
document.getElementById('listaUltimosPagos').innerHTML = '';
|
||||
}
|
||||
|
||||
async function cargarHistorialPagos(miembroId) {
|
||||
const contenedor = document.getElementById('infoUltimosPagos');
|
||||
const lista = document.getElementById('listaUltimosPagos');
|
||||
|
||||
lista.innerHTML = '<div class="spinner-border spinner-border-sm text-info" role="status"></div> Cargando historial...';
|
||||
contenedor.style.display = 'block';
|
||||
|
||||
try {
|
||||
const response = await fetch('@Url.Action("ObtenerUltimosPagos", "Colaboracion")?miembroId=' + miembroId);
|
||||
const pagos = await response.json();
|
||||
|
||||
if (pagos && pagos.length > 0) {
|
||||
let html = '';
|
||||
pagos.forEach(p => {
|
||||
const colorClass = p.ultimoMes > 0 ? 'bg-white text-info border border-info' : 'bg-secondary text-white';
|
||||
html += `
|
||||
<span class="badge ${colorClass} fw-normal p-2">
|
||||
<strong>${p.nombreTipo}:</strong> ${p.ultimoPeriodoTexto}
|
||||
</span>
|
||||
`;
|
||||
});
|
||||
lista.innerHTML = html;
|
||||
} else {
|
||||
lista.innerHTML = '<span class="text-muted small">No hay historial de pagos registrado.</span>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al cargar historial:', error);
|
||||
lista.innerHTML = '<span class="text-danger small"><i class="bi bi-exclamation-circle"></i> Error al cargar historial</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function calcularSugerido() {
|
||||
try {
|
||||
// Obtener valores
|
||||
const mesInicial = parseInt(document.getElementById('mesInicial').value);
|
||||
const anioInicial = parseInt(document.getElementById('anioInicial').value);
|
||||
const mesFinal = parseInt(document.getElementById('mesFinal').value);
|
||||
const anioFinal = parseInt(document.getElementById('anioFinal').value);
|
||||
|
||||
// Calcular total de meses
|
||||
const fechaInicial = new Date(anioInicial, mesInicial - 1, 1);
|
||||
const fechaFinal = new Date(anioFinal, mesFinal - 1, 1);
|
||||
|
||||
let totalMeses = 0;
|
||||
if (fechaFinal >= fechaInicial) {
|
||||
totalMeses = ((anioFinal - anioInicial) * 12) + (mesFinal - mesInicial) + 1;
|
||||
}
|
||||
|
||||
// Obtener tipos seleccionados y sus montos
|
||||
const tiposCheckboxes = document.querySelectorAll('.tipo-checkbox:checked');
|
||||
const totalTipos = tiposCheckboxes.length;
|
||||
|
||||
// Calcular monto sugerido total
|
||||
let montoSugeridoTotal = 0;
|
||||
tiposCheckboxes.forEach(checkbox => {
|
||||
const montoPorMes = parseFloat(checkbox.getAttribute('data-monto')) || 0;
|
||||
montoSugeridoTotal += montoPorMes * totalMeses;
|
||||
});
|
||||
|
||||
// Actualizar UI
|
||||
document.getElementById('totalMeses').textContent = totalMeses;
|
||||
document.getElementById('totalTipos').textContent = totalTipos;
|
||||
document.getElementById('montoSugerido').textContent = montoSugeridoTotal.toFixed(2);
|
||||
|
||||
// Comparar con monto ingresado
|
||||
const montoIngresado = parseFloat(document.getElementById('montoTotal').value) || 0;
|
||||
const alertaDiv = document.getElementById('alertaDiferencia');
|
||||
const mensajeSpan = document.getElementById('mensajeDiferencia');
|
||||
|
||||
if (montoIngresado > 0) {
|
||||
if (Math.abs(montoIngresado - montoSugeridoTotal) > 0.01) {
|
||||
const diferencia = montoSugeridoTotal - montoIngresado; // Corrected calculation for difference
|
||||
if (diferencia > 0) {
|
||||
mensajeSpan.textContent = `Falta: $${diferencia.toFixed(2)}`;
|
||||
alertaDiv.className = 'alert alert-warning py-1 px-2 mb-0 mt-2';
|
||||
} else {
|
||||
mensajeSpan.textContent = `Sobra: $${Math.abs(diferencia).toFixed(2)}`;
|
||||
alertaDiv.className = 'alert alert-info py-1 px-2 mb-0 mt-2';
|
||||
}
|
||||
alertaDiv.style.display = 'block';
|
||||
} else {
|
||||
alertaDiv.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
alertaDiv.style.display = 'none';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al calcular sugerido:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Calcular cuando cambia el monto total
|
||||
document.getElementById('montoTotal')?.addEventListener('input', calcularSugerido);
|
||||
|
||||
// ===== OFFLINE-FIRST FORM SUBMISSION =====
|
||||
document.getElementById('colaboracionForm')?.addEventListener('submit', async function(e) {
|
||||
e.preventDefault(); // Prevent default form submission
|
||||
|
||||
// Gather form data
|
||||
const miembroId = document.getElementById('miembroIdHidden').value;
|
||||
const mesInicial = document.getElementById('mesInicial').value;
|
||||
const anioInicial = document.getElementById('anioInicial').value;
|
||||
const mesFinal = document.getElementById('mesFinal').value;
|
||||
const anioFinal = document.getElementById('anioFinal').value;
|
||||
const montoTotal = document.getElementById('montoTotal').value;
|
||||
const observaciones = document.querySelector('[name="Observaciones"]').value;
|
||||
const tipoPrioritario = document.getElementById('tipoPrioritario').value;
|
||||
|
||||
// Get selected tipos
|
||||
const tiposSeleccionados = Array.from(document.querySelectorAll('.tipo-checkbox:checked'))
|
||||
.map(cb => cb.value);
|
||||
|
||||
// Validate
|
||||
if (!miembroId) {
|
||||
toastr.error('Por favor seleccione un miembro');
|
||||
return;
|
||||
}
|
||||
|
||||
if (tiposSeleccionados.length === 0) {
|
||||
toastr.error('Por favor seleccione al menos un tipo de colaboración');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!montoTotal || parseFloat(montoTotal) <= 0) {
|
||||
toastr.error('Por favor ingrese un monto válido');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare data object
|
||||
const colaboracionData = {
|
||||
miembroId: parseInt(miembroId),
|
||||
mesInicial: parseInt(mesInicial),
|
||||
anioInicial: parseInt(anioInicial),
|
||||
mesFinal: parseInt(mesFinal),
|
||||
anioFinal: parseInt(anioFinal),
|
||||
montoTotal: parseFloat(montoTotal),
|
||||
observaciones: observaciones,
|
||||
tiposSeleccionados: tiposSeleccionados,
|
||||
tipoPrioritario: tipoPrioritario || null,
|
||||
registradoPor: '@User.Identity?.Name'
|
||||
};
|
||||
|
||||
// Disable submit button
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalBtnText = submitBtn.innerHTML;
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Guardando...';
|
||||
|
||||
try {
|
||||
// Use sync manager to save (handles online/offline automatically)
|
||||
const result = await ColaboracionesSyncManager.saveColaboracion(colaboracionData);
|
||||
|
||||
if (result.success) {
|
||||
if (result.offline) {
|
||||
toastr.warning(result.message);
|
||||
|
||||
// Clear form
|
||||
setTimeout(() => {
|
||||
window.location.href = '@Url.Action("Index", "Colaboracion")';
|
||||
}, 2000);
|
||||
} else {
|
||||
toastr.success(result.message);
|
||||
|
||||
// Redirect to index
|
||||
setTimeout(() => {
|
||||
window.location.href = '@Url.Action("Index", "Colaboracion")';
|
||||
}, 1500);
|
||||
}
|
||||
} else {
|
||||
toastr.error(result.message || 'Error al guardar');
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
toastr.error('Error inesperado: ' + error.message);
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
}
|
||||
});
|
||||
|
||||
// Calcular al cargar la página
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
calcularSugerido();
|
||||
});
|
||||
|
||||
// Show error messages
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<text>
|
||||
toastr.error('@TempData["Error"]');
|
||||
</text>
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model IEnumerable<Rs_system.Models.ViewModels.MiembroViewModel>
|
||||
@model Rs_system.Models.ViewModels.PaginatedViewModel<Rs_system.Models.ViewModels.MiembroViewModel>
|
||||
@{
|
||||
ViewData["Title"] = "Miembros de la Iglesia";
|
||||
}
|
||||
@@ -8,9 +8,48 @@
|
||||
<h4 class="mb-1">Miembros en Propiedad</h4>
|
||||
<p class="text-muted mb-0">Gestión de miembros de la congregación</p>
|
||||
</div>
|
||||
<a asp-action="Create" class="btn btn-primary-custom">
|
||||
<i class="bi bi-plus-lg me-1"></i> Nuevo Miembro
|
||||
</a>
|
||||
<div>
|
||||
<a asp-action="Importar" class="btn btn-outline-success me-2">
|
||||
<i class="bi bi-file-earmark-spreadsheet me-1"></i> Importar CSV
|
||||
</a>
|
||||
<a asp-action="Create" class="btn btn-primary-custom">
|
||||
<i class="bi bi-plus-lg me-1"></i> Nuevo Miembro
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search and Page Size Controls -->
|
||||
<div class="card-custom mb-3">
|
||||
<form method="get" asp-action="Index" id="searchForm" class="row g-3 align-items-end">
|
||||
<div class="col-md-6">
|
||||
<label for="search" class="form-label">Buscar Miembro</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="search"
|
||||
name="search"
|
||||
value="@Model.SearchQuery"
|
||||
placeholder="Buscar por nombre o apellido...">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="pageSize" class="form-label">Registros por página</label>
|
||||
<select class="form-select"
|
||||
id="pageSize"
|
||||
asp-for="PageSize"
|
||||
onchange="document.getElementById('searchForm').submit();">
|
||||
<option value="5">5</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="bi bi-search me-1"></i> Buscar
|
||||
</button>
|
||||
</div>
|
||||
<input type="hidden" name="page" value="1" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Summary Cards -->
|
||||
@@ -18,19 +57,19 @@
|
||||
<div class="col-md-4">
|
||||
<div class="card-custom text-center">
|
||||
<h6 class="text-muted mb-2">Total Miembros</h6>
|
||||
<h3 class="text-primary mb-0">@Model.Count()</h3>
|
||||
<h3 class="text-primary mb-0">@Model.TotalItems</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card-custom text-center">
|
||||
<h6 class="text-muted mb-2">Bautizados en el Espíritu Santo</h6>
|
||||
<h3 class="text-success mb-0">@Model.Count(m => m.BautizadoEspirituSanto)</h3>
|
||||
<h3 class="text-success mb-0">@Model.Items.Count(m => m.BautizadoEspirituSanto)</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card-custom text-center">
|
||||
<h6 class="text-muted mb-2">Grupos de Trabajo</h6>
|
||||
<h3 class="text-info mb-0">@Model.Where(m => m.GrupoTrabajoId.HasValue).GroupBy(m => m.GrupoTrabajoId).Count()</h3>
|
||||
<h3 class="text-info mb-0">@Model.Items.Where(m => m.GrupoTrabajoId.HasValue).GroupBy(m => m.GrupoTrabajoId).Count()</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,16 +91,23 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (!Model.Any())
|
||||
@if (!Model.Items.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
<i class="bi bi-people fs-1 d-block mb-2"></i>
|
||||
No hay miembros registrados
|
||||
@if (!string.IsNullOrWhiteSpace(Model.SearchQuery))
|
||||
{
|
||||
<text>No se encontraron miembros con el criterio de búsqueda "@Model.SearchQuery"</text>
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>No hay miembros registrados</text>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@foreach (var miembro in Model)
|
||||
@foreach (var miembro in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
@@ -152,6 +198,71 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination Controls -->
|
||||
@if (Model.TotalPages > 1)
|
||||
{
|
||||
<div class="d-flex justify-content-between align-items-center mt-3 px-3 pb-3">
|
||||
<div class="text-muted">
|
||||
Mostrando @((Model.CurrentPage - 1) * Model.PageSize + 1) a @(Math.Min(Model.CurrentPage * Model.PageSize, Model.TotalItems)) de @Model.TotalItems registros
|
||||
</div>
|
||||
<nav aria-label="Paginación de miembros">
|
||||
<ul class="pagination mb-0">
|
||||
<!-- Previous Button -->
|
||||
<li class="page-item @(!Model.HasPreviousPage ? "disabled" : "")">
|
||||
<a class="page-link"
|
||||
href="@Url.Action("Index", new { page = Model.CurrentPage - 1, pageSize = Model.PageSize, search = Model.SearchQuery })"
|
||||
aria-label="Anterior">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@{
|
||||
var startPage = Math.Max(1, Model.CurrentPage - 2);
|
||||
var endPage = Math.Min(Model.TotalPages, Model.CurrentPage + 2);
|
||||
|
||||
if (startPage > 1)
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="@Url.Action("Index", new { page = 1, pageSize = Model.PageSize, search = Model.SearchQuery })">1</a>
|
||||
</li>
|
||||
if (startPage > 2)
|
||||
{
|
||||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = startPage; i <= endPage; i++)
|
||||
{
|
||||
<li class="page-item @(i == Model.CurrentPage ? "active" : "")">
|
||||
<a class="page-link" href="@Url.Action("Index", new { page = i, pageSize = Model.PageSize, search = Model.SearchQuery })">@i</a>
|
||||
</li>
|
||||
}
|
||||
|
||||
if (endPage < Model.TotalPages)
|
||||
{
|
||||
if (endPage < Model.TotalPages - 1)
|
||||
{
|
||||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||||
}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="@Url.Action("Index", new { page = Model.TotalPages, pageSize = Model.PageSize, search = Model.SearchQuery })">@Model.TotalPages</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Next Button -->
|
||||
<li class="page-item @(!Model.HasNextPage ? "disabled" : "")">
|
||||
<a class="page-link"
|
||||
href="@Url.Action("Index", new { page = Model.CurrentPage + 1, pageSize = Model.PageSize, search = Model.SearchQuery })"
|
||||
aria-label="Siguiente">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Delete Form -->
|
||||
|
||||
@@ -22,6 +22,22 @@
|
||||
<!--<link rel="stylesheet" href="~/Rs_system.styles.css" asp-append-version="true"/>-->
|
||||
<link rel="manifest" href="~/manifest.json">
|
||||
<meta name="theme-color" content="#1e293b">
|
||||
|
||||
<!-- Service Worker Registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/service-worker.js')
|
||||
.then(registration => {
|
||||
console.log('Service Worker registered successfully:', registration.scope);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@RenderSection("Styles", required: false)
|
||||
</head>
|
||||
<body>
|
||||
@@ -53,6 +69,10 @@
|
||||
<h5 class="mb-0 fw-semibold">@ViewData["Title"]</h5>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span id="offlineStatus" class="badge bg-success" style="display: none;">
|
||||
<i class="bi bi-wifi"></i> En línea
|
||||
</span>
|
||||
<span id="pendingBadge" class="badge bg-warning ms-2" style="display: none;">0</span>
|
||||
<partial name="_LoginPartial"/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user