Venta Silenciosa PrivΓ© β€” Dashboard
Detalle del Suscriptor
Resumen General
⚠️ EstΓ‘s viendo datos de demostraciΓ³n β€” la conexiΓ³n con Google Sheets no estΓ‘ activa o fallΓ³.
⬑ Total Suscriptores
β€”
Cargando...
β—‰ Activos
β€”
Cargando...
βœ• Vencidos
β€”
Cargando...
⚠ Pendiente Skool
β€”
Cargando...
$ MRR i
β€”
Ingreso mensual
ΒΏCΓ³mo se calcula?
Suma de la columna FacturaciΓ³n de todos los suscriptores Activos.

ARR = MRR Γ— 12 meses
πŸ“‰ Churn Rate i
β€”
% mensual
ΒΏCΓ³mo se calcula?
Vencidos Γ· (Activos + Vencidos) Γ— 100

Representa el % de suscriptores que cancelaron respecto al total con suscripciΓ³n real.
πŸ’š Tasa RetenciΓ³n i
β€”
% activos reales
ΒΏCΓ³mo se calcula?
100% βˆ’ Churn Rate

El % de suscriptores que siguen activos vs el total con suscripciΓ³n.
β˜… LTV Promedio i
β€”
por suscriptor
ΒΏCΓ³mo se calcula?
FacturaciΓ³n total acumulada Γ· total de suscriptores.

CuΓ‘nto ha generado en promedio cada suscriptor desde su primera compra.
βŒ€ Compras Prom. i
β€”
renovaciones/suscriptor
ΒΏCΓ³mo se calcula?
Suma de "NΓΊmero de Compras" Γ· total suscriptores.

Indica cuΓ‘ntas veces renueva un suscriptor en promedio.
Churn vs RetenciΓ³n por Mes
EvoluciΓ³n mensual de cancelaciones vs nuevos
LTV por Plan
Valor total acumulado por tipo de suscripciΓ³n
Suscripciones por Mes
Nuevas vs Cancelaciones + FacturaciΓ³n
Estado Actual
DistribuciΓ³n de status
Por Plan
Por PaΓ­s
Por Plataforma
FacturaciΓ³n por Plan
Ingresos totales acumulados por tipo de plan
Top PaΓ­ses
FacturaciΓ³n por paΓ­s
Todos los Suscriptores
Cliente ↕ Plan ↕ PaΓ­s Plataforma Status ↕ Última Compra ↕ RenovaciΓ³n ↕ FacturaciΓ³n ↕ Compras
πŸ”
Acceso restringido
Solo usuarios autorizados pueden gestionar accesos Skool

PrΓ³ximas Renovaciones

Suscripciones con vencimiento en los prΓ³ximos 5 dΓ­as

Cliente Plan PaΓ­s Fecha RenovaciΓ³n DΓ­as Restantes Status Compras Totales

ConfiguraciΓ³n

Conecta tu Google Sheets al dashboard

πŸ”— Paso a Paso β€” ConexiΓ³n Google Sheets
Sigue estos pasos para conectar tu Google Sheet como fuente de datos en tiempo real.
1
Abre tu Google Sheet
Ve a Extensiones β†’ Apps Script para abrir el editor de scripts.
2
Crea el script API
Borra el contenido y pega el siguiente cΓ³digo. Este script expone tus datos como una API REST segura.
3
Despliega como Web App
Click en Implementar β†’ Nueva implementaciΓ³n β†’ AplicaciΓ³n web. Acceso: "Cualquier usuario". Copia la URL generada.
4
Pega la URL abajo
Ingresa la URL de tu Apps Script desplegado y guarda. El dashboard se conectarΓ‘ automΓ‘ticamente.
πŸ“‹ CΓ³digo Apps Script
Copia y pega este cΓ³digo completo en tu Google Apps Script.
// ═══════════════════════════════════════════════════════════
// SubsTrack β€” Google Apps Script API v4
// ═══════════════════════════════════════════════════════════

const SHEET_NAME = 'EstatusClientes';
const LOG_SHEET  = 'SkoolRemovals';

// πŸ” DOMINIOS PERMITIDOS β€” solo estas webs pueden usar este script
const ALLOWED_ORIGINS = [
  'https://www.aprendamosacademia.com',
  'https://aprendamosacademia.com'
];

// ── Validar origen ──────────────────────────────────────────
function isAllowedOrigin(e) {
  // JSONP trae el referer como parΓ‘metro 'origin' que aΓ±adimos desde el dashboard
  const origin = (e.parameter && e.parameter.origin) || '';
  return ALLOWED_ORIGINS.some(allowed => origin.startsWith(allowed));
}

// ── Rate limiting: mΓ‘x 30 req/min por origen ───────────────
function checkRateLimit(origin) {
  try {
    const props = PropertiesService.getScriptProperties();
    const key   = 'rl_' + origin.replace(/[^a-z0-9]/gi, '_').substring(0, 50);
    const now   = Date.now();
    const raw   = props.getProperty(key);
    const hits  = raw ? JSON.parse(raw).filter(t => now - t < 60000) : [];
    if (hits.length >= 30) return false;
    hits.push(now);
    props.setProperty(key, JSON.stringify(hits));
    return true;
  } catch(e) { return true; }
}

// ── Respuesta vacΓ­a sin pistas ──────────────────────────────
function empty(cb) {
  const p = JSON.stringify({ success: false });
  if (cb) return ContentService.createTextOutput(cb+'('+p+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
  return ContentService.createTextOutput(p).setMimeType(ContentService.MimeType.JSON);
}

// ── doGet ───────────────────────────────────────────────────
function doGet(e) {
  const cb     = (e.parameter && e.parameter.callback) || '';
  const action = (e.parameter && e.parameter.action)   || '';

  // πŸ” Bloquear si no viene del dominio autorizado
  if (!isAllowedOrigin(e)) return empty(cb);

  // πŸ” Rate limiting
  const origin = (e.parameter && e.parameter.origin) || '';
  if (!checkRateLimit(origin)) return empty(cb);

  // ── AcciΓ³n: registrar eliminaciΓ³n Skool ──────────────────
  if (action === 'skoolRemove') {
    const email = (e.parameter.email || '').trim().toLowerCase();
    if (!email || !email.includes('@') || email.length > 254) return empty(cb);

    const ss        = SpreadsheetApp.getActiveSpreadsheet();
    const mainSheet = ss.getSheetByName(SHEET_NAME);
    const mainData  = mainSheet.getDataRange().getValues();
    const emailCol  = mainData[0].indexOf('Email');

    // πŸ›‘οΈ Solo emails que existan en EstatusClientes
    const validEmails = mainData.slice(1).map(r => String(r[emailCol]).trim().toLowerCase());
    if (!validEmails.includes(email)) return empty(cb);

    let logSheet = ss.getSheetByName(LOG_SHEET);
    if (!logSheet) { logSheet = ss.insertSheet(LOG_SHEET); logSheet.appendRow(['Email','Fecha']); }

    const existing = logSheet.getDataRange().getValues().flat().map(v => String(v).trim().toLowerCase());
    if (!existing.includes(email)) {
      const fecha = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'dd/MM/yyyy HH:mm:ss');
      logSheet.appendRow([email, fecha]);
    }

    const r = JSON.stringify({ success: true });
    if (cb) return ContentService.createTextOutput(cb+'('+r+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
    return ContentService.createTextOutput(r).setMimeType(ContentService.MimeType.JSON);
  }

  // ── AcciΓ³n default: devolver datos ───────────────────────
  const ss      = SpreadsheetApp.getActiveSpreadsheet();
  const sheet   = ss.getSheetByName(SHEET_NAME);
  const data    = sheet.getDataRange().getValues();
  const headers = data[0];
  const emailIdx = headers.indexOf('Email');

  const SAFE = ['Email','Nombre','Apellido','Pais','PaΓ­s','Plan','Plataforma',
    'Ultima Compra','Última Compra','Renovacion','Renovación','Primera Compra',
    'Numero de Compras','NΓΊmero de Compras','Status','Estado Skool','ID Subscriptor',
    'Facturacion','FacturaciΓ³n'];

  const rows = data.slice(1)
    .filter(row => { const em = String(row[emailIdx]||'').trim(); return em.includes('@'); })
    .map(row => { let o={}; headers.forEach((h,i)=>{ if(SAFE.includes(h)) o[h]=row[i]; }); return o; });

  const payload = JSON.stringify({ success: true, data: rows, total: rows.length });
  if (cb) return ContentService.createTextOutput(cb+'('+payload+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
  return ContentService.createTextOutput(payload).setMimeType(ContentService.MimeType.JSON);
}
βš™ Conectar Dashboard
Una vez desplegado el Apps Script, ingresa la URL aquΓ­.
Debe coincidir exactamente con el TOKEN en tu Apps Script
🌐 Embeber en tu Web
Copia este cΓ³digo HTML para embeber el dashboard en cualquier pΓ‘gina web.
<iframe 
  src="URL_DE_TU_DASHBOARD" 
  width="100%" 
  height="800px" 
  frameborder="0"
  style="border-radius:16px;box-shadow:0 8px 40px rgba(0,0,0,0.3);"
></iframe>
βœ“ AcciΓ³n realizada