diff --git a/resources/views/telegram.html b/resources/views/telegram.html index d7a055b..d53ab50 100644 --- a/resources/views/telegram.html +++ b/resources/views/telegram.html @@ -238,7 +238,18 @@ function telegramApp() { async loadConfigs() { const res = await fetch('/app/loadtelegram'); - this.configs = await res.json(); + const data = await res.json(); + this.configs = data; + // Pre-inicializar claves reactivas para cada config. + // Sin esto Alpine.js no detecta las asignaciones dinámicas cfg.ID -> value. + const tl = {}, tr = {}, to = {}; + const wl = {}, wr = {}, wo = {}; + for (const cfg of data) { + tl[cfg.ID] = false; tr[cfg.ID] = ''; to[cfg.ID] = false; + wl[cfg.ID] = false; wr[cfg.ID] = ''; wo[cfg.ID] = false; + } + this.testLoading = tl; this.testResult = tr; this.testOk = to; + this.webhookLoading = wl; this.webhookResult = wr; this.webhookOk = wo; }, openAdd() { diff --git a/resources/views/vcard_api.html b/resources/views/vcard_api.html index 697cb0e..7f92355 100644 --- a/resources/views/vcard_api.html +++ b/resources/views/vcard_api.html @@ -1811,7 +1811,15 @@ function vcardApiApp() { // ─── Ordenar por vencimiento ───────────────────────────────────────── + // Retorna true para planes de pago (emprendimiento / normal). + // Estos planes no tienen vencimiento relevante en el contexto de membresía premium. + _isPaidPlan(u) { + const nombre = ((u.plan && u.plan.nombre) || '').toLowerCase(); + return nombre.includes('emprendimiento') || nombre.includes('normal'); + }, + diasHastaVencer(u) { + if (this._isPaidPlan(u)) return null; // plan de pago: sin vencimiento const id = u.id || u._id; const m = this.membresiaMap[id]; if (!m || !m.fecha_fin) return null; @@ -1837,6 +1845,7 @@ function vcardApiApp() { }, vencimientoTexto(u) { + if (this._isPaidPlan(u)) return '—'; // plan de pago: no aplica vencimiento const id = u.id || u._id; const m = this.membresiaMap[id]; if (this.loadingVenc && !m) return '...'; @@ -1851,8 +1860,10 @@ function vcardApiApp() { 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); + // Solo buscar membresía para usuarios free (los de plan de pago no tienen vencimiento relevante) + const usersToFetch = this.items.filter(u => !this._isPaidPlan(u)); + for (let i = 0; i < usersToFetch.length; i += batchSize) { + const batch = usersToFetch.slice(i, i + batchSize); await Promise.all(batch.map(async u => { const id = u.id || u._id; if (this.membresiaMap[id]) return; @@ -1863,15 +1874,32 @@ function vcardApiApp() { })); } this.loadingVenc = false; + // Orden: 0 = free+premium (más urgente, por fecha), 1 = plan pagado, 2 = free sin premium 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; + const paidA = this._isPaidPlan(a); + const paidB = this._isPaidPlan(b); + const premA = a.premium === true || a.premium === 'true'; + const premB = b.premium === true || b.premium === 'true'; + const grp = (isPaid, isPrem) => { + if (!isPaid && isPrem) return 0; // free + premium → primero + if (isPaid) return 1; // plan pagado → en medio + return 2; // free sin premium → al final + }; + const ga = grp(paidA, premA); + const gb = grp(paidB, premB); + if (ga !== gb) return ga - gb; + // Dentro del grupo 0 (free+premium), ordenar por fecha_fin (más próxima primero) + if (ga === 0) { + 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; + } + return 0; }); },