Añade docs/estados-flujo.html: flujos de estado por canal

Referencia visual que separa las 4 dimensiones de estado (pipeline_stage,
lead_estado, estado_wa, estado_conversacion del bot) y dibuja el flujo de
formulario/WhatsApp/llamada sobre ellas, para decidir el modelado del estado
de conversación de Luisa.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-04 17:01:21 +02:00
parent f2b19ab719
commit 0b46de89f2

207
docs/estados-flujo.html Normal file
View File

@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Reformix — Flujos de estado por canal</title>
<style>
:root{
--pipe:#0066ff; --pipe-bg:#e8f0fe;
--crm:#0f7a52; --crm-bg:#e6f4ee;
--wa:#0891b2; --wa-bg:#e0f4f8;
--bot:#b45309; --bot-bg:#fbf0e0;
--bad:#dc2626; --bad-bg:#fdeaea;
--ink:#18181b; --muted:#71717a; --line:#e4e4e7; --card:#fff; --bg:#f4f4f5;
--font:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);line-height:1.5}
.wrap{max-width:1280px;margin:0 auto;padding:28px 20px 80px}
h1{font-size:26px;font-weight:800;letter-spacing:-.4px;margin:0 0 4px}
.sub{color:var(--muted);margin:0 0 22px;font-size:14px}
h2{font-size:15px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);margin:34px 0 12px;font-weight:700}
/* Leyenda de dimensiones */
.dims{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.dim{background:var(--card);border:1px solid var(--line);border-left-width:5px;border-radius:10px;padding:12px 14px}
.dim h3{margin:0 0 4px;font-size:13px;font-family:ui-monospace,monospace}
.dim p{margin:0 0 8px;font-size:12px;color:var(--muted)}
.dim .vals{display:flex;flex-wrap:wrap;gap:4px}
.pip{border-left-color:var(--pipe)} .pip h3{color:var(--pipe)}
.crm{border-left-color:var(--crm)} .crm h3{color:var(--crm)}
.waL{border-left-color:var(--wa)} .waL h3{color:var(--wa)}
.botL{border-left-color:var(--bot)} .botL h3{color:var(--bot)}
.chip{display:inline-block;font-size:10.5px;font-family:ui-monospace,monospace;padding:2px 7px;border-radius:99px;border:1px solid var(--line);background:#fafafa;color:#3f3f46;white-space:nowrap}
.chip.p{background:var(--pipe-bg);border-color:#cdddff;color:#1d4ed8}
.chip.c{background:var(--crm-bg);border-color:#bfe3d2;color:#0f7a52}
.chip.w{background:var(--wa-bg);border-color:#bfe6ef;color:#0e7490}
.chip.b{background:var(--bot-bg);border-color:#f0d9b5;color:#b45309}
.chip.x{background:var(--bad-bg);border-color:#f5c2c2;color:#dc2626}
/* Columnas de canal */
.cols{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;align-items:start}
.col{background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.col > .head{padding:12px 16px;font-weight:700;font-size:15px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:8px}
.col.form > .head{background:#f5f3ff} .col.wa > .head{background:var(--wa-bg)} .col.call > .head{background:#fff7ed}
.steps{padding:14px 16px;display:flex;flex-direction:column;gap:0}
.step{position:relative;padding:10px 0 14px}
.step .t{font-size:13.5px;font-weight:600}
.step .d{font-size:12px;color:var(--muted);margin:2px 0 6px}
.step .tags{display:flex;flex-wrap:wrap;gap:4px}
.arrow{height:16px;display:flex;justify-content:center;color:#a1a1aa;font-size:13px}
.branch{border:1px dashed var(--line);border-radius:8px;padding:8px 10px;margin-top:6px;background:#fafafa}
.branch .t{font-size:12.5px;font-weight:600}
.dot{width:8px;height:8px;border-radius:99px;display:inline-block}
.conv{background:var(--bot-bg);border:1px solid #f0d9b5;border-radius:8px;padding:8px 10px;margin-top:6px}
.conv .t{font-size:12px;font-weight:700;color:var(--bot);margin-bottom:4px}
.conv .flow{font-family:ui-monospace,monospace;font-size:11px;color:#92400e;line-height:1.7}
.converge{margin-top:18px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px}
.converge .row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:13px}
.converge .b{font-weight:700}
.note{background:#fffbeb;border:1px solid #fde68a;border-radius:10px;padding:14px 16px;margin-top:14px;font-size:13.5px}
.note h3{margin:0 0 8px;font-size:14px}
.note ul{margin:6px 0 0;padding-left:18px} .note li{margin:4px 0}
.rec{background:#ecfdf5;border:1px solid #a7f3d0}
table{width:100%;border-collapse:collapse;font-size:12.5px;margin-top:6px}
th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line);vertical-align:top}
th{color:var(--muted);font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.5px}
code{font-family:ui-monospace,monospace;background:#f4f4f5;padding:1px 5px;border-radius:5px;font-size:11.5px}
@media(max-width:980px){.dims{grid-template-columns:repeat(2,1fr)}.cols{grid-template-columns:1fr}}
</style>
</head>
<body>
<div class="wrap">
<h1>Reformix — Flujos de estado por canal</h1>
<p class="sub">El lío viene de mezclar varias "estados" que en realidad son <b>4 dimensiones independientes</b>. Aquí están separadas y el flujo de cada canal sobre ellas. DB única; el lead se crea siempre en el form web.</p>
<h2>1 · Las 4 dimensiones de estado (ortogonales)</h2>
<div class="dims">
<div class="dim pip">
<h3>pipeline_stage</h3>
<p>Avance TÉCNICO en el funnel. Lo comparten los 3 canales y es lo que ve el panel. <b>Lo gestiona la app/EP, no el bot.</b></p>
<div class="vals">
<span class="chip p">form_completado</span><span class="chip p">fotos_subidas</span><span class="chip p">prellamada_enviada</span><span class="chip p">llamada_completada</span><span class="chip p">render_generado</span><span class="chip p">presupuesto_generado</span><span class="chip p">whatsapp_entregado</span>
</div>
</div>
<div class="dim crm">
<h3>lead_estado</h3>
<p>Estado COMERCIAL / CRM. Lo lleva el reformista (y algún automatismo). Independiente del canal.</p>
<div class="vals">
<span class="chip c">nuevo</span><span class="chip c">contactado</span><span class="chip c">visita_agendada</span><span class="chip c">presupuesto_enviado</span><span class="chip c">ganado</span><span class="chip x">perdido</span>
</div>
</div>
<div class="dim waL">
<h3>estado_wa</h3>
<p>SOLO entrega del último mensaje de WhatsApp (técnico, por-mensaje). <b>No</b> es "en qué punto va la conversación".</p>
<div class="vals">
<span class="chip w">sin_enviar</span><span class="chip w">enviado</span><span class="chip w">entregado</span><span class="chip w">leido</span><span class="chip x">fallido</span>
</div>
</div>
<div class="dim botL">
<h3>estado_conversacion <span style="color:var(--bad)">(NO existe aún)</span></h3>
<p>En qué paso va Luisa en la cualificación. Hoy vive solo dentro del bot. <b>La decisión es si lo persistimos.</b></p>
<div class="vals">
<span class="chip b">apertura</span><span class="chip b">espacio</span><span class="chip b">tamaño</span><span class="chip b">estilo</span><span class="chip b">urgencia</span><span class="chip b">presupuesto</span><span class="chip b">pide_fotos</span><span class="chip b">completado</span>
</div>
</div>
</div>
<h2>2 · Flujo de cada canal sobre esas dimensiones</h2>
<div class="cols">
<!-- FORMULARIO -->
<div class="col form">
<div class="head">📝 Formulario web</div>
<div class="steps">
<div class="step"><div class="t">Cliente deja datos (crearLead)</div><div class="d">nombre · tel · email · opt-ins</div><div class="tags"><span class="chip p">form_completado</span><span class="chip c">nuevo</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Rellena por zonas + sube fotos</div><div class="d">guardarDetallesYFotos → lead_fotos (antes) + lead_notas</div><div class="tags"><span class="chip p">fotos_subidas</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Presupuesto orientativo al instante</div><div class="d">motor de presupuesto + señal perfilCompleto</div><div class="tags"><span class="chip p">presupuesto_generado</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Converge en calificación →</div><div class="tags"><span class="chip c">contactado</span></div></div>
<div class="conv"><div class="t">No usa</div><div class="flow">estado_wa · estado_conversacion (no hay chat)</div></div>
</div>
</div>
<!-- WHATSAPP -->
<div class="col wa">
<div class="head">💬 WhatsApp — Luisa</div>
<div class="steps">
<div class="step"><div class="t">Lead ya existe (del form) → elige WhatsApp</div><div class="d">app emite WHATSAPP_START con leadId</div><div class="tags"><span class="chip p">form_completado</span><span class="chip c">nuevo</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Bot escribe el 1er mensaje</div><div class="d">entrega del mensaje (no la conversación)</div><div class="tags"><span class="chip w">sin_enviar→enviado→entregado→leido</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Luisa cualifica (conversación)</div><div class="d">guarda cada turno en conversacion_whatsapp + extrae a leads</div>
<div class="conv"><div class="t">estado_conversacion (bot)</div><div class="flow">apertura → espacio → tamaño → estilo → urgencia → presupuesto</div></div>
</div>
<div class="arrow"></div>
<div class="step"><div class="t">¿Viable? (≥ 5000€)</div>
<div class="branch"><div class="t">No → <span class="chip x">perdido</span> (no_viable, descartado)</div></div>
<div class="branch"><div class="t">Sí → pide fotos por WA</div><div class="tags" style="margin-top:6px"><span class="chip b">pide_fotos</span></div></div>
</div>
<div class="arrow"></div>
<div class="step"><div class="t">Fotos recibidas → EP ingesta</div><div class="d">lead_fotos (antes) + worker analiza</div><div class="tags"><span class="chip p">fotos_subidas</span><span class="chip b">completado</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Converge en calificación →</div><div class="tags"><span class="chip c">contactado</span></div></div>
</div>
</div>
<!-- LLAMADA -->
<div class="col call">
<div class="head">📞 Llamada</div>
<div class="steps">
<div class="step"><div class="t">Lead ya existe (del form) → pide llamada</div><div class="d">ahora / programar</div><div class="tags"><span class="chip p">prellamada_enviada</span><span class="chip c">nuevo</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Bot de llamada (externo)</div><div class="d">registra intento en intentos_contacto</div><div class="tags"><span class="chip p">llamada_completada</span></div>
<div class="branch"><div class="t">resultado_contacto</div><div class="tags" style="margin-top:6px"><span class="chip">exitoso</span><span class="chip">no_contesta</span><span class="chip">ocupado</span><span class="chip x">rechaza</span></div></div>
</div>
<div class="arrow"></div>
<div class="step"><div class="t">Pide fotos por WA o email→formulario</div><div class="d">leads.fotos_solicitadas_at</div><div class="tags"><span class="chip w">enviado</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Fotos recibidas → EP ingesta</div><div class="d">lead_fotos (antes) + worker</div><div class="tags"><span class="chip p">fotos_subidas</span></div></div>
<div class="arrow"></div>
<div class="step"><div class="t">Converge en calificación →</div><div class="tags"><span class="chip c">contactado</span></div></div>
</div>
</div>
</div>
<!-- CONVERGENCIA -->
<h2>3 · Convergencia (los 3 canales acaban igual)</h2>
<div class="converge">
<div class="row">
<span class="chip c">Calificación</span><span>lead_calificacion (score + A/B/C/D)</span> <span style="color:#a1a1aa"></span>
<span class="chip c">visita_agendada</span> <span style="color:#a1a1aa"></span>
<span class="chip p">render_generado</span><span class="chip p">presupuesto_generado</span> <span style="color:#a1a1aa"></span>
<span class="chip p">whatsapp_entregado</span><span class="chip c">presupuesto_enviado</span> <span style="color:#a1a1aa"></span>
<span class="chip c">ganado</span> / <span class="chip x">perdido</span> <span style="color:#a1a1aa"></span> testimonio
</div>
</div>
<!-- DECISIÓN -->
<h2>4 · La decisión a tomar</h2>
<div class="note">
<h3>¿WhatsApp necesita "otros estados"? Sí, pero ojo a CUÁL:</h3>
<ul>
<li><b>estado_wa</b> (ya lo tenemos): es solo si el mensaje llegó/se leyó. Útil pero de bajo nivel.</li>
<li><b>estado_conversacion (Luisa)</b>: <u>esto es lo que de verdad falta</u> si queremos saber "por qué paso va el chat" y poder retomarlo si se corta. Hoy NO está en la DB.</li>
<li>El <b>diagrama del compañero</b> ponía <code>estado_wa = nuevo</code> → mezcla los dos conceptos. <code>nuevo</code> no es entrega de mensaje, es "conversación sin empezar".</li>
</ul>
</div>
<div class="note rec">
<h3>Mi recomendación</h3>
<ul>
<li><b>pipeline_stage</b> = única fuente del avance del lead (lo pinta el panel). Lo escribe la app/EP. <b>El bot NO lo toca.</b></li>
<li><b>lead_estado</b> = comercial, lo lleva el reformista.</li>
<li><b>estado_wa</b> = déjalo solo para entrega de mensaje (sin_enviar…leido). No metas "nuevo" ahí.</li>
<li><b>estado_conversacion del bot</b>: 2 opciones —
<br>(a) vive SOLO dentro del bot/n8n (no lo persistimos en nuestra DB) → más simple, suficiente para la demo;
<br>(b) lo persistimos como columna nueva <code>leads.bot_step</code> (o tabla) si queremos verlo en el panel / retomar conversaciones. <b>← esto es lo que hay que decidir.</b></li>
</ul>
</div>
</div>
</body>
</html>