|
|
@@ -93,6 +115,9 @@
class="text-xs px-2 py-0.5 rounded-full" x-text="(u.rol && u.rol.nombre) ? u.rol.nombre : 'β'">
|
|
+
+
+ |
Activar
+
@@ -1182,6 +1211,65 @@
+
+
+
+
+
+ Desactivar usuario + VCards
+
+
+
+
+
+
+
+
+ Cargando VCards del usuario...
+
+
+
+
+ Se inactivarΓ‘n VCard(s):
+
+
+
+
+
+ Este usuario no tiene VCards. Solo se desactivarΓ‘ la cuenta.
+
+ ΒΏConfirmas desactivar la cuenta del usuario y todas sus VCards?
+
+
+
+
+
+
+
+
+
@@ -1483,6 +1571,19 @@ function vcardApiApp() {
membresiaError: '',
planes: [],
+ // Ordenar usuarios por vencimiento
+ sortByVenc: false,
+ loadingVenc: false,
+ membresiaMap: {}, // { userId: { fecha_fin, activo, plan } }
+
+ // Desactivar usuario + VCards
+ desactivarVcardsModal: false,
+ desactivarVcardsUser: null,
+ desactivarVcardsItems: [],
+ desactivarVcardsLoading: false,
+ desactivarVcardsError: '',
+ desactivarVcardsResult: '',
+
// Filtros por tab
filters: {
pagos: { estado: '', moneda: '', desde: '', hasta: '' },
@@ -1571,6 +1672,7 @@ function vcardApiApp() {
this.verVcardModal = false;
this.verPlanModal = false;
this.editPlanModal = false;
+ this.desactivarVcardsModal = false;
},
async loadConfig() {
@@ -1690,6 +1792,10 @@ function vcardApiApp() {
}
} catch(e) { this.errorMsg = e.message; }
this.loading = false;
+ // Si el sort por vencimiento estaba activo, re-aplicarlo con los nuevos items
+ if (this.sortByVenc && this.tab === 'usuarios' && this.items.length > 0) {
+ await this._fetchAndSortVenc();
+ }
},
_collectRoles(users) {
@@ -1703,6 +1809,82 @@ function vcardApiApp() {
});
},
+ // βββ Ordenar por vencimiento βββββββββββββββββββββββββββββββββββββββββ
+
+ diasHastaVencer(u) {
+ const id = u.id || u._id;
+ const m = this.membresiaMap[id];
+ if (!m || !m.fecha_fin) return null;
+ const diff = new Date(m.fecha_fin) - new Date();
+ return Math.ceil(diff / (1000 * 60 * 60 * 24));
+ },
+
+ vencimientoRowClass(u) {
+ const dias = this.diasHastaVencer(u);
+ if (dias === null) return 'hover:bg-gray-50';
+ if (dias <= 7) return 'bg-red-50 hover:bg-red-100';
+ if (dias <= 30) return 'bg-yellow-50 hover:bg-yellow-100';
+ return 'bg-green-50 hover:bg-green-100';
+ },
+
+ vencimientoBadgeClass(u) {
+ const dias = this.diasHastaVencer(u);
+ if (dias === null) return 'bg-gray-100 text-gray-400';
+ if (dias <= 0) return 'bg-red-200 text-red-800 font-semibold';
+ if (dias <= 7) return 'bg-red-100 text-red-700 font-semibold';
+ if (dias <= 30) return 'bg-yellow-100 text-yellow-700';
+ return 'bg-green-100 text-green-700';
+ },
+
+ vencimientoTexto(u) {
+ const id = u.id || u._id;
+ const m = this.membresiaMap[id];
+ if (this.loadingVenc && !m) return '...';
+ if (!m || !m.fecha_fin) return 'β';
+ const dias = this.diasHastaVencer(u);
+ const fecha = m.fecha_fin.substring(0, 10);
+ if (dias <= 0) return `Vencido Β· ${fecha}`;
+ if (dias === 1) return `MaΓ±ana Β· ${fecha}`;
+ return `${dias}d Β· ${fecha}`;
+ },
+
+ async _fetchAndSortVenc() {
+ this.loadingVenc = true;
+ const batchSize = 5;
+ for (let i = 0; i < this.items.length; i += batchSize) {
+ const batch = this.items.slice(i, i + batchSize);
+ await Promise.all(batch.map(async u => {
+ const id = u.id || u._id;
+ if (this.membresiaMap[id]) return;
+ try {
+ const r = await fetch(`/app/vcard-api/usuarios/${id}/membresia`);
+ if (r.ok) { this.membresiaMap[id] = await r.json(); }
+ } catch(e) { /* ignorar errores individuales */ }
+ }));
+ }
+ this.loadingVenc = false;
+ this.items = [...this.items].sort((a, b) => {
+ const ma = this.membresiaMap[a.id || a._id];
+ const mb = this.membresiaMap[b.id || b._id];
+ const fa = ma?.fecha_fin ? new Date(ma.fecha_fin) : null;
+ const fb = mb?.fecha_fin ? new Date(mb.fecha_fin) : null;
+ if (!fa && !fb) return 0;
+ if (!fa) return 1;
+ if (!fb) return -1;
+ return fa - fb;
+ });
+ },
+
+ async toggleSortVenc() {
+ if (this.sortByVenc) {
+ this.sortByVenc = false;
+ this.loadTab();
+ return;
+ }
+ this.sortByVenc = true;
+ await this._fetchAndSortVenc();
+ },
+
clearFilters(tab) {
if (tab === 'pagos') this.filters.pagos = { estado: '', moneda: '', desde: '', hasta: '' };
if (tab === 'transacciones') this.filters.transacciones = { type: '', status: '' };
@@ -1801,6 +1983,57 @@ function vcardApiApp() {
this.loading = false;
},
+ async openDesactivarConVcardsModal(u) {
+ this.desactivarVcardsUser = u;
+ this.desactivarVcardsItems = [];
+ this.desactivarVcardsError = '';
+ this.desactivarVcardsResult = '';
+ this.desactivarVcardsLoading = true;
+ this.desactivarVcardsModal = true;
+ try {
+ const id = u.id || u._id;
+ const r = await fetch(`/app/vcard-api/usuarios/${id}/vcards`);
+ const d = await r.json();
+ if (d.data && Array.isArray(d.data)) this.desactivarVcardsItems = d.data;
+ else if (Array.isArray(d)) this.desactivarVcardsItems = d;
+ } catch(e) { this.desactivarVcardsError = 'No se pudieron cargar las VCards: ' + e.message; }
+ this.desactivarVcardsLoading = false;
+ },
+
+ async confirmarDesactivarConVcards() {
+ this.desactivarVcardsError = '';
+ this.desactivarVcardsResult = '';
+ this.saving = true;
+ const id = this.desactivarVcardsUser.id || this.desactivarVcardsUser._id;
+ try {
+ // 1. Inactivar cada VCard activa
+ let inactivadas = 0;
+ const vcards = this.desactivarVcardsItems.filter(v => v.estado === true || v.estado === 1);
+ for (const v of vcards) {
+ const vid = v.id || v._id;
+ const r = await fetch(`/app/vcard-api/vcards/${vid}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ estado: false })
+ });
+ if (r.ok) inactivadas++;
+ }
+ // 2. Desactivar usuario
+ const rU = await fetch(`/app/vcard-api/usuarios/${id}/desactivar`, { method: 'POST' });
+ const dU = await rU.json();
+ if (!rU.ok) {
+ this.desactivarVcardsError = dU.error || 'Error al desactivar el usuario.';
+ } else {
+ this.desactivarVcardsResult = `Usuario desactivado. VCards inactivadas: ${inactivadas} de ${vcards.length}.`;
+ setTimeout(async () => {
+ this.desactivarVcardsModal = false;
+ await this.loadTab();
+ }, 1800);
+ }
+ } catch(e) { this.desactivarVcardsError = e.message; }
+ this.saving = false;
+ },
+
// βββ Edit Usuario ββββββββββββββββββββββββββββββββββββββββββββββββββββ
openEditUsuarioModal(u) {
@@ -1987,6 +2220,8 @@ function vcardApiApp() {
const r = await fetch(`/app/vcard-api/usuarios/${id}/membresia`);
const d = await r.json();
this.membresiaInfo = d;
+ // Cachear para el sort por vencimiento
+ this.membresiaMap[id] = d;
} catch(e) {}
},
|