Dashboard Estratégico — TheFittClub

Meta mes 6: 109 clientas activas · 17.300€/mes Actualizado: $= dv.date("today").toFormat("dd/MM/yyyy")


Resumen ejecutivo

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').array();
const conDatos = notas.filter(p => p.alcance_total > 0);
const total = notas.length;
 
const totalAlcance     = conDatos.reduce((a, p) => a + (p.alcance_total || 0), 0);
const totalGuardados   = notas.reduce((a, p) => a + (p.guardados || 0), 0);
const totalCompartidos = notas.reduce((a, p) => a + (p.compartidos || 0), 0);
const topAlcance     = conDatos.slice().sort((a, b) => (b.alcance_total||0) - (a.alcance_total||0))[0];
const topGuardados   = notas.slice().sort((a, b) => (b.guardados||0) - (a.guardados||0))[0];
const topCompartidos = notas.filter(p => p.compartidos > 0).sort((a, b) => (b.compartidos||0) - (a.compartidos||0))[0];
 
dv.paragraph(`
| Señal | Valor | Qué significa |
|---|---|---|
| Posts con métricas | ${conDatos.length} / ${total} | — |
| Alcance histórico total | ${totalAlcance.toLocaleString()} | Impacto acumulado |
| Guardados totales | ${totalGuardados.toLocaleString()} | Autoridad generada |
| Compartidos totales | ${totalCompartidos.toLocaleString()} | Crecimiento orgánico |
| Mejor post (alcance) | ${topAlcance ? topAlcance.titulo?.slice(0,35) + "…" : "—"} | ${topAlcance ? topAlcance.alcance_total?.toLocaleString() + " alcance" : ""} |
| Mejor post (guardados) | ${topGuardados ? topGuardados.titulo?.slice(0,35) + "…" : "—"} | ${topGuardados ? topGuardados.guardados?.toLocaleString() + " guardados" : ""} |
`);

Panel de acción rápida (esta semana)

Qué repetir (ganadores)

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0).array();
const ganadores = notas
  .filter(p => (p.guardados || 0) >= 120 || (p.compartidos || 0) >= 25)
  .sort((a, b) => ((b.guardados || 0) + (b.compartidos || 0) * 3) - ((a.guardados || 0) + (a.compartidos || 0) * 3))
  .slice(0, 8)
  .map(p => [
    p.fecha || "—",
    p.categoria || "sin_categoria",
    p.hook_tipo || "sin_hook",
    p.guardados || 0,
    p.compartidos || 0,
    (p.titulo || "").slice(0, 60)
  ]);
 
if (ganadores.length === 0) {
  dv.paragraph("Aún no hay suficientes datos para recomendar repetición.");
} else {
  dv.table(["Fecha", "Categoría", "Hook", "Guard.", "Comp.", "Tema"], ganadores);
}

Qué parar o reformular (señal débil)

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0).array();
const flojos = notas
  .filter(p => (p.alcance_total || 0) < 2000 && (p.guardados || 0) < 40 && (p.compartidos || 0) < 8)
  .sort((a, b) => (a.alcance_total || 0) - (b.alcance_total || 0))
  .slice(0, 8)
  .map(p => [
    p.fecha || "—",
    p.categoria || "sin_categoria",
    p.hook_tipo || "sin_hook",
    p.alcance_total || 0,
    p.guardados || 0,
    p.compartidos || 0,
    "Reformular hook + CTA"
  ]);
 
if (flojos.length === 0) {
  dv.paragraph("No hay piezas claramente flojas con datos suficientes.");
} else {
  dv.table(["Fecha", "Categoría", "Hook", "Alcance", "Guard.", "Comp.", "Acción"], flojos);
}

KPI de negocio (prioridad semanal)

Este dashboard ayuda con contenido. La decisión semanal final se toma con 00_Home/Semanal.md y estos 4 KPI.

KPIConservadorModeradoAlto
Signup Cápsula / semana< 2040-60> 80
Completado Cápsula< 40%55-65%> 70%
Conversión Cápsula → Ritual< 3%5-6%> 7%
Retención trimestral< 60%68-72%> 75%

Trigger de acción si estás por debajo del moderado:

  • Signup bajo → revisar blast, asunto y segundo envío a no-abiertos.
  • Completado bajo → revisar UX sesión 1 + timing email día 1.
  • Conversión baja → reforzar testimonials en día 3 + oferta 72h en día 4.
  • Retención baja → activar email semana 8-10 con progreso real.

1 · MOTOR DE CRECIMIENTO — Qué contenido atrae seguidoras nuevas

Estos son los posts que más han llegado a personas que no te seguían. Son tu materia prima para crecer: recíclalos, adáptalos, hazlos Reels nuevos.

1a · Top por ALCANCE TOTAL (cuántas personas te vieron)

TABLE
  fecha         as "Fecha",
  tipo          as "Tipo",
  alcance_total as "Alcance",
  compartidos   as "Compart.",
  likes         as "Likes",
  titulo        as "Tema"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE alcance_total > 5000
SORT alcance_total DESC
LIMIT 15

1b · Top por COMPARTIDOS (cuántas personas te recomendaron)

Un compartido vale más que 10 likes: alguien mandó tu contenido a otra persona que no te conoce.

TABLE
  fecha         as "Fecha",
  tipo          as "Tipo",
  compartidos   as "Compartidos",
  alcance_total as "Alcance",
  likes         as "Likes",
  titulo        as "Tema"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE compartidos > 0
SORT compartidos DESC
LIMIT 15

1c · Patrón ganador — ¿Qué CATEGORÍA de contenido crece más?

Clasifica cada post con categoria: en la nota. A medida que lo rellenas, esta tabla se vuelve tu brújula de producción.

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0);
const porCat = {};
const etiquetas = {
  rutina:          "💪 Rutina",
  explicacion:     "🧠 Explicación",
  gancho_estetico: "✨ Gancho estético",
  detras_camaras:  "🎬 Detrás de cámaras",
  testimonial:     "💬 Testimonial",
};
for (const p of notas) {
  const cat = p.categoria || "sin_clasificar";
  if (!porCat[cat]) porCat[cat] = { posts: 0, alcance: 0, compartidos: 0, guardados: 0 };
  porCat[cat].posts++;
  porCat[cat].alcance     += p.alcance_total || 0;
  porCat[cat].compartidos += p.compartidos   || 0;
  porCat[cat].guardados   += p.guardados     || 0;
}
const filas = Object.entries(porCat)
  .map(([cat, d]) => {
    const label = etiquetas[cat] || cat;
    const alcMed = Math.round(d.alcance / d.posts);
    const guarMed = Math.round(d.guardados / d.posts);
    const compMed = Math.round(d.compartidos / d.posts * 10) / 10;
    return [label, d.posts, alcMed.toLocaleString(), guarMed, compMed];
  })
  .sort((a, b) => parseInt(b[2].replace(/\D/g,'')) - parseInt(a[2].replace(/\D/g,'')));
dv.table(["Categoría", "Posts", "Alcance medio", "Guardados medio", "Compart. medio"], filas);

1d · Patrón ganador — ¿Qué HOOK abre más?

Rellena hook_tipo: en las notas nuevas. Aquí verás qué gancho funciona mejor.

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0 && p.hook_tipo);
const porHook = {};
for (const p of notas) {
  const h = p.hook_tipo;
  if (!porHook[h]) porHook[h] = { posts: 0, alcance: 0, compartidos: 0 };
  porHook[h].posts++;
  porHook[h].alcance     += p.alcance_total || 0;
  porHook[h].compartidos += p.compartidos   || 0;
}
const filas = Object.entries(porHook)
  .map(([h, d]) => [h, d.posts, Math.round(d.alcance / d.posts).toLocaleString(), Math.round(d.compartidos / d.posts * 10) / 10])
  .sort((a, b) => parseInt(b[2].replace(/\D/g,'')) - parseInt(a[2].replace(/\D/g,'')));
if (filas.length === 0) {
  dv.paragraph("⚠️ Añade `hook_tipo:` en las notas para activar este análisis.");
} else {
  dv.table(["Hook", "Posts", "Alcance medio", "Compart. medio"], filas);
}

1e · Distribución por PILAR (sistema GEO)

Esta tabla mide qué pilar aporta más alcance medio para equilibrar la estrategia semanal. Distribución objetivo inicial: 35% método · 30% ciencia · 25% filosofía · 10% transformación.

const notas = dv.pages('"40_Contenido_Redes/Publicado_IG"').filter(p => p.alcance_total > 0 && p.pilar).array();
const porPilar = {};
const etiquetas = {
  ciencia: "Ciencia",
  metodo: "Método",
  filosofia: "Filosofía",
  transformacion: "Transformación",
};
 
for (const p of notas) {
  const k = (p.pilar || "").toString().trim().toLowerCase();
  if (!k) continue;
  if (!porPilar[k]) porPilar[k] = { posts: 0, alcance: 0, guardados: 0, compartidos: 0 };
  porPilar[k].posts++;
  porPilar[k].alcance += p.alcance_total || 0;
  porPilar[k].guardados += p.guardados || 0;
  porPilar[k].compartidos += p.compartidos || 0;
}
 
const filas = Object.entries(porPilar)
  .map(([k, d]) => {
    const label = etiquetas[k] || k;
    const alcanceMedio = Math.round(d.alcance / d.posts);
    const guardadosMedio = Math.round(d.guardados / d.posts);
    const compartidosMedio = Math.round((d.compartidos / d.posts) * 10) / 10;
    return [label, d.posts, alcanceMedio.toLocaleString(), guardadosMedio, compartidosMedio];
  })
  .sort((a, b) => parseInt(b[2].replace(/\D/g, "")) - parseInt(a[2].replace(/\D/g, "")));
 
if (filas.length === 0) {
  dv.paragraph("⚠️ Añade `pilar:` en las notas de Publicado_IG para activar este análisis.");
} else {
  dv.table(["Pilar", "Posts", "Alcance medio", "Guardados medio", "Compart. medio"], filas);
}

2 · DECISIÓN DE PRODUCTO — Qué Cápsula crear primero

Las dolencias que más aparecen en comentarios = demanda real con dinero sobre la mesa. Rellena pain_flags cada vez que publicas — es el dato más valioso del sistema.

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 flag of lista) {
    const f = flag.trim().toLowerCase();
    if (!f || f === "") continue;
    contador[f] = (contador[f] || 0) + 1;
  }
}
const mapaProducto = {
  diastasis:              "Módulo Core (97€)",
  cesarea:                "Cápsula Cicatriz (67€)",
  suelo_pelvico:          "Módulo Pélvico (97€)",
  incontinencia:          "Cápsula Piso Pélvico (47€)",
  dolor_pelvico:          "Cápsula Dolor Pélvico (47€)",
  dolor_lumbar:           "Cápsula Lumbar (47€)",
  recuperacion_postparto: "Ritual Trimestral (147€)",
  falta_tiempo:           "Ritual Trimestral (147€)",
  barriga_postparto:      "Cápsula Estética (47€)",
  hipopresivos:           "Cápsula Hipopresivos (67€)",
};
const ranking = Object.entries(contador).sort((a, b) => b[1] - a[1]);
if (ranking.length === 0) {
  dv.paragraph("⚠️ Aún no hay pain_flags registrados. Añádelos en cada nota tras leer los comentarios.");
} else {
  dv.table(
    ["Dolencia", "Menciones", "Prioridad", "Producto sugerido"],
    ranking.map(([f, n]) => [f, n, "⭐".repeat(Math.min(n, 5)), mapaProducto[f] || "—"])
  );
}

3 · AUTORIDAD — Qué contenido convierte al Ritual Trimestral (147€)

Posts con muchos guardados = tu audiencia los usa como referencia → mayor intención de compra. Úsalos en Stories de venta, emails y como “prueba social” en la landing.

TABLE
  fecha     as "Fecha",
  tipo      as "Tipo",
  guardados as "Guardados",
  alcance_total as "Alcance",
  titulo    as "Tema"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE guardados > 200
SORT guardados DESC
LIMIT 15

4 · Tracción Externa — Ganchos para Cápsula Cero (0€)

Posts que más alcanzan a no seguidoras → reciclar como gancho de captación de tráfico frío.

TABLE
  fecha                                                          as "Fecha",
  tipo                                                           as "Tipo",
  round((alcance_no_seguidores / alcance_total) * 100, 1) + "%" as "Tracción %",
  alcance_no_seguidores                                          as "No Seguidoras",
  alcance_total                                                  as "Alcance",
  choice(gancho_estetico, "✅", "—")                            as "Gancho"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE alcance_no_seguidores > 0
SORT (alcance_no_seguidores / alcance_total) DESC
LIMIT 15


6 · Biblioteca de Ganchos Estéticos (listos para reciclar en Reels o Ads)

TABLE
  fecha         as "Fecha",
  tipo          as "Tipo",
  alcance_total as "Alcance",
  compartidos   as "Compart.",
  angulo        as "Ángulo",
  url           as "URL"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE gancho_estetico = true
SORT alcance_total DESC

7 · Alertas — Posts sin clasificar

Añade angulo, producto_objetivo y pain_flags para mantener el sistema útil.

TABLE
  fecha     as "Fecha",
  tipo      as "Tipo",
  alcance_total as "Alcance",
  guardados as "Guardados"
FROM "40_Contenido_Redes/Publicado_IG"
WHERE (producto_objetivo = "" OR producto_objetivo = null) AND alcance_total > 1000
SORT fecha DESC
LIMIT 20