Mensual

Revisión mensual. 30 minutos. Ajustar rumbo.


Cambio de fase (si aplica)

  • Fase anterior: INPUT[text:fase_anterior]
  • Nueva fase: INPUT[text:fase_nueva]
  • ¿Por qué cambia? INPUT[text:motivo_cambio_fase]

KPIs del funnel (solo si estás en post-lanzamiento)

const c = dv.current();
const signup = Number(c.signup_capsula_semana || 0);
const completado = Number(c.completado_capsula_pct || 0);
const conversion = Number(c.conversion_capsula_ritual_pct || 0);
const retencion = Number(c.retencion_trimestral_pct || 0);
 
const kpis = [
  { nombre: "Signup Cápsula", valor: signup, moderadoMin: 40, altoMin: 80, unidad: "/sem" },
  { nombre: "Completado Cápsula", valor: completado, moderadoMin: 55, altoMin: 70, unidad: "%" },
  { nombre: "Conversión Cápsula→Ritual", valor: conversion, moderadoMin: 5, altoMin: 7, unidad: "%" },
  { nombre: "Retención trimestral", valor: retencion, moderadoMin: 68, altoMin: 75, unidad: "%" },
];
 
function estadoKpi(kpi) {
  if (!kpi.valor || kpi.valor <= 0) return "sin_dato";
  if (kpi.valor >= kpi.altoMin) return "alto";
  if (kpi.valor >= kpi.moderadoMin) return "moderado";
  return "conservador";
}
 
const estadoGlobal = kpis.some(k => estadoKpi(k) === "conservador")
  ? "conservador"
  : kpis.every(k => estadoKpi(k) === "alto")
    ? "alto"
    : "moderado";
 
const badge = {
  alto: "🟢 ALTO",
  moderado: "🟡 MODERADO",
  conservador: "🔴 CONSERVADOR",
};
 
dv.paragraph(`### Estado mensual: ${badge[estadoGlobal]}`);
 
const tabla = kpis.map(k => {
  const estado = estadoKpi(k);
  const sem = estado === "alto" ? "🟢" : estado === "moderado" ? "🟡" : estado === "conservador" ? "🔴" : "⚪";
  const valorFmt = k.valor > 0 ? `${k.valor}${k.unidad}` : "—";
  return [k.nombre, valorFmt, sem];
});
dv.table(["KPI", "Valor", "Estado"], tabla);

Acciones sugeridas automáticas

const c = dv.current();
const signup = Number(c.signup_capsula_semana || 0);
const completado = Number(c.completado_capsula_pct || 0);
const conversion = Number(c.conversion_capsula_ritual_pct || 0);
const retencion = Number(c.retencion_trimestral_pct || 0);
const engagement = Number(c.engagement_rate_semana || 0);
 
const acciones = [];
if (signup > 0 && signup < 40) acciones.push(["Signup < 40", "Revisar asunto email + segundo blast a no-abiertos en 48h"]);
if (completado > 0 && completado < 55) acciones.push(["Completado < 55%", "Revisar UX sesión 1 + CTA de final de sesión + email día 1"]);
if (conversion > 0 && conversion < 5) acciones.push(["Conversión < 5%", "Reforzar prueba social en email día 3 + urgencia 72h día 4"]);
if (retencion > 0 && retencion < 65) acciones.push(["Retención < 65%", "Activar secuencia retención semana 8-10 con hitos de progreso"]);
if (engagement > 0 && engagement < 0.15) acciones.push(["Engagement < 0.15%", "Subir frecuencia y priorizar formato rutina/guardados"]);
 
if (acciones.length === 0) {
  dv.paragraph("✅ Sin alertas críticas este mes. Sigue con foco.");
} else {
  dv.table(["Trigger", "Acción prioritaria"], acciones);
}

Contenido y métricas de Instagram

Top 5 posts del mes por alcance

TABLE
  fecha         as "Fecha",
  tipo          as "Tipo",
  alcance_total as "Alcance",
  guardados     as "Guardados",
  compartidos   as "Compart.",
  titulo        as "Tema"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE date(fecha) >= date(today) - dur(31 days)
SORT alcance_total DESC
LIMIT 5

Pain flags acumulados

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"');
const contador = {};
for (const nota of notas) {
  const flags = nota.pain_flags;
  if (!flags) continue;
  const lista = Array.isArray(flags) ? flags : [flags];
  for (const f of lista) {
    const key = f.trim().toLowerCase();
    if (!key || key === "") continue;
    contador[key] = (contador[key] || 0) + 1;
  }
}
const ranking = Object.entries(contador).sort((a, b) => b[1] - a[1]).slice(0, 5);
if (ranking.length === 0) {
  dv.paragraph("_(Aún no hay pain flags registrados)_");
} else {
  dv.table(["Dolencia", "Menciones"], ranking);
}

Posts sin clasificar

TABLE fecha as "Fecha", tipo as "Tipo", alcance_total as "Alcance"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE (categoria = "" OR categoria = null) AND alcance_total > 2000
SORT alcance_total DESC
LIMIT 8

Estado del lanzamiento

const hoyMs = Date.now();
const lanzamiento = new Date("2026-09-01").getTime();
const diasRestantes = Math.ceil((lanzamiento - hoyMs) / (1000 * 60 * 60 * 24));
const semanas = Math.floor(diasRestantes / 7);
 
const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0).array();
const totalAlcance = notas.reduce((a, p) => a + (p.alcance_total || 0), 0);
const totalCompartidos = notas.reduce((a, p) => a + (p.compartidos || 0), 0);
const totalGuardados = notas.reduce((a, p) => a + (p.guardados || 0), 0);
 
const urgente = diasRestantes > 0
  ? `🚀 **${diasRestantes} días** para el lanzamiento (${semanas} semanas)`
  : `⚡ **LANZAMIENTO ACTIVO**`;
 
dv.paragraph(urgente);
dv.paragraph(`
| Métrica acumulada | Valor |
|---|---|
| Posts con métricas | ${notas.length} |
| Alcance histórico | ${totalAlcance.toLocaleString()} |
| Guardados totales | ${totalGuardados.toLocaleString()} |
| Compartidos totales | ${totalCompartidos.toLocaleString()} |
`);

Proyección a 30 días

  • ¿Qué espero haber conseguido en 30 días?
  • ¿Qué necesito para lograrlo?