Documento de referencia para entender y modificar el proyecto rápido (también
para abrir un chat nuevo con contexto y gastar menos tokens). Léelo junto con
el README.md de la carpeta raíz (estado, despliegue, variables de entorno).
⚠️ Next.js 16.2.6 — no es como el del entrenamiento (App Router, Turbopack,proxy.tsen vez demiddleware.ts,params/cookiesasync). VerAGENTS.md.
App web que mide la madurez digital de una empresa. El usuario responde un cuestionario con un semáforo (🔴 No · 🟡 Más o menos · 🟢 Sí) y obtiene un diagnóstico cualitativo por colores, un plan de acción y un PDF/correo.
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 (@theme, sin
config) · Motion (animaciones) · Recharts (gráficas admin) · jsPDF (PDF) ·
Supabase (Postgres + RLS) · Resend (correos) · IA multi-proveedor
(Claude/Gemini/Groq/OpenRouter). Despliegue en Vercel (push a main = deploy).
src/lib/questionnaire.ts define TODO el test. Cambiar aquí y el resto
(radar, scoring, diagnóstico, resultados) se adapta solo.
dimensions[]— las 4 secciones (estrategia, cultura, cliente, procesos), cada una conid,name,title,icon(Lucide),color,description,questions[](2 por dimensión) yrecommendationspor banda (low/medium/high).responseOptions[]— el semáforo:no=0 (rojo),parcial=3 (amarillo),si=10 (verde). El valor alimenta la escala interna 0–10. "Más o menos" pesa poco (3) a propósito: "tal vez/no sé" no es media madurez.maturityLevels[]— 3 estados por color: Inicial (0–2,9, rojo), En desarrollo (3–7,9, amarillo), Consolidado (8–10, verde).⚠️ Estos cortes están duplicados en 3 archivos (ver «Riesgos»).
responseOptions→ respuesta por pregunta (0/3/10): rojo=0, amarillo=3, verde=10.- Bandas centralizadas en
src/lib/scoring.ts:BAND_EDGES = { redMax: 3, amberMax: 8 }— fuente única de los cortes.levelIndexForScore(score)→ 0 rojo · 1 amarillo · 2 verde (la luz encendida).levelForScore(score)→ estado =maturityLevels[index].- Cortes: rojo < 3 · amarillo 3–7,9 · verde ≥ 8. Vara exigente pero justa: con
"más o menos"=3, "todo más o menos" (promedio 3,0) queda en el piso del amarillo;
el rojo aparece cuando hay algún "no" arrastrando por debajo de 3. Verde exigente
(6 verdes + 2 amarillas = 8,25 → verde).
maturityLevels[].rangelos refleja.
src/lib/utils.ts → scoreColor(score)→ color SÓLIDO (ya no gradual): rojo#ef4444, ámbar#f59e0b, verde#22c55e. Lo usan el semáforo, las áreas y el PDF.bandForScore()(recomendaciones del PDF/correo): <3 low, <8 medium, ≥8 high.
- Onboarding (
components/assessment/onboarding-form.tsx): datos de la empresa + persona, validados con Zod (lib/validation.ts). Anti-spam: honeypot, validaciones. - Cuestionario (
components/assessment/questionnaire.tsx+traffic-light.tsx): semáforo por pregunta, auto-avance, progreso por dimensión. Estado enlib/storage.ts(localStorage, reanudable). - Scoring (
lib/scoring.ts): promedia respuestas → puntaje 0–10 por dimensión y global → estado por color. (El servidor recalcula, nunca confía en el cliente.) - Resultados (
components/results/result-view.tsx): 100% cualitativo, sin números. Semáforo grande (izq.) + las 4 áreas por estado (der.), 4 indicadores cualitativos, diagnóstico y botón «Descargar mi semáforo digital» (PDF). Ya no hay radar, barras con puntaje, plan de acción ni asesor IA en pantalla (el plan y el asesor siguen en el código; ver «Riesgos»). - Guardado (
lib/submissions.ts→ Supabase) + aviso por correo (lib/notify.ts→ Resend) + webhook opcional (N8N_WEBHOOK_URL).
src/
app/
layout.tsx Root layout, fuentes, tema (script anti-flash)
page.tsx Home (landing)
diagnostico/page.tsx Flujo del test
admin/ Panel protegido (KPIs, gráficas, tabla)
api/ respuestas · contacto · asesor · benchmark · admin
opengraph-image.tsx Imagen para compartir el sitio
components/
landing/hero.tsx
assessment/ onboarding-form · questionnaire · traffic-light · assessment-flow
results/ result-view · ai-advisor (SIN USO)
admin/ submissions-table · admin-charts · ...
ui/ button · logo · theme-toggle · reveal · dimension-icon
lib/
questionnaire.ts ← fuente única del test (4 dims × 2 preguntas)
types.ts Tipos del dominio
scoring.ts Puntajes, estados, bandas
diagnosis.ts Diagnóstico + plan por fases (reglas; reemplazable por IA)
utils.ts cn, formatScore, scoreColor (gradual), mix (mezcla color)
pdf.ts PDF «Semáforo Digital» (jsPDF) — copia A MANO del semáforo
share-card.ts Tarjeta PNG (canvas) — SIN USO (se quitó «Compartir imagen»)
notify.ts Correo de lead (Resend) — OJO: aún con números
submissions.ts Guardado + agregados del admin (+ datos demo)
storage.ts Persistencia local del test
validation.ts Esquemas Zod del onboarding
ai/advisor.ts Asesor IA multi-proveedor — SIN USO en resultados
supabase/server.ts Cliente service-role
admin-auth.ts Sesión admin (cookie HMAC)
proxy.ts Middleware (cabeceras de seguridad, guard admin)
Sobre la versión de los compañeros (rebrand a Semáforo Digital, semaforo.tsx,
nivel educativo, Postgres/Sheets). Qué cambió en este pase y dónde:
- Fix de bandas (definidas por el usuario): rojo <3 · amarillo 3–7,9 · verde ≥8
centralizados en
scoring.ts(BAND_EDGES,levelIndexForScore). "Más o menos"=3 (no 5) para subir la vara, pero "todo más o menos" queda en amarillo; el rojo necesita algún "no". Ver «Colores / semáforo». - Onboarding (
onboarding-form.tsx): semáforo de estado en la esquina sup. der. (ámbar por defecto · rojo si hay errores · verde si todo válido, víaformState) + wash tenue alrededor del formulario que sigue ese color. Campos inválidos con aura roja. - Cuestionario (
questionnaire.tsx): scroll a la siguiente pregunta conanimatedScrollTo(easeInOutCubic, ~900 ms) para que sea más suave. scoreColorahora es SÓLIDO (rojo/ámbar/verde fuertes), ya no gradual. Resultados, áreas y PDF muestran color fuerte.components/ui/semaforo.tsxreescrito: aspecto 3D (gradientes, glow, brillo), tamañoxl, hookuseSemaforoCycle()+SemaforoCycle(cicla rojo→amarillo→verde) yAmbientWash(blobs que tiñen el fondo del color actual).- Home (
hero.tsx,page.tsx): semáforo animado + wash en hero y en "Qué obtienes"; copy a autodiagnóstico; features recortadas (sin chatbot/recos/plan por fases); CTA y subtítulos ajustados; widgets de "Cómo funciona" y "4 dimensiones" con micro- interacciones (hover lift, glow, watermark). Logo con luces más pequeñas. - Resultados (
result-view.tsx): tarjetas de dimensión con más contraste + descripción de 2 renglones; widgets con etiquetas a 2 líneas (móvil); print header «Semáforo Digital». - Onboarding (
assessment-flow.tsx):AmbientWashdetrás del formulario. - Cuestionario (
questionnaire.tsx): aura sutil (barra + fondo) del color de la respuesta al contestar; liviano (solo box-shadow/bg, sin blur). - Modo oscuro neón desactivado en las cabeceras públicas (se quitó
ThemeToggledesite-headerydiagnostico); el CSS.darksigue ahí. Admin conserva su toggle. - Rebrand restante: footer ©, headers de admin/login → «Semáforo Digital».
Resumen para no auditar a ciegas. Qué cambió en esa iteración y dónde:
- Umbrales de estado (
scoring.ts,questionnaire.ts,utils.ts): rojo 0–6, amarillo 6,1–9, verde 9,1–10 (antes 0–5 / 5–9 / 9–10). - Pantalla de resultados (
components/results/result-view.tsx) reescrita: sin números; semáforo grande + 4 áreas por estado + 4 indicadores cualitativos. Se quitaron: radar, barras de puntaje, tarjetas fortalezas/debilidades, plan de acción, asesor IA, el toggle «¿quieres que te contactemos?» y el botón «Compartir imagen». - Contacto: ahora siempre se manda
wantsContact: true(todo el que termina es lead). El endpointapi/contactono cambió. - Botón de descarga renombrado a «Descargar mi semáforo digital».
- PDF (
lib/pdf.ts) rediseñado como semáforo: semáforo grande que replica el de pantalla, 4 áreas por color, leyenda de 3 estados; sin radar, sin escalera de 5 niveles y sin números. Archivo:Semaforo-Digital-<empresa>.pdf. - Diagnóstico (
lib/diagnosis.ts): el resumen ya no incluye cifras.
- Umbrales ya casi centralizados. La verdad vive en
scoring.ts → BAND_EDGES(levelIndexForScore/levelForScore). Aún hay que mantener a mano los espejos:questionnaire.ts → maturityLevels[].range(mismos cortes 3 / 8) y los hex deutils.ts → scoreColor. Los cortes asumenround1(1 decimal): con respuestas 0/3/10 conviene revisar que no caigan promedios justo en 4/8 (empates de frontera). - El semáforo del PDF es una copia a mano del de pantalla (
BigTrafficLightenresult-view.tsxvs. el dibujo jsPDF enpdf.ts). No se sincronizan solos: si cambias colores/forma/tamaño en uno, hay que replicarlo en el otro. - Código sin uso (no borrado) — limpiar o reconectar, según se decida:
components/results/ai-advisor.tsx+ rutaapp/api/asesor+lib/ai/advisor.ts(+ deps@anthropic-ai/sdk,@google/genai,openai): el asesor ya no se muestra. Si no se reconecta, se pueden borrar para aligerar el bundle.components/results/semaforo.tsx: semáforo animado reutilizable (sm/md/lg).lib/share-card.ts: la tarjeta PNG se quitó («Compartir imagen»).
- El plan de acción sigue vivo en el motor (
diagnosis.ts → plan) aunque no se vea en pantalla: lo usan el PDF y el correo de lead (notify.ts). No borresgenerateDiagnosisni los camposActionItem.score/target. - El correo de lead aún muestra números (
notify.ts: puntaje global, por dimensión, metas del plan). La UI y el PDF ya son sin cifras; el correo no. Si se busca coherencia total, falta actualizarnotify.ts. - Privacidad / leads. Al no haber opción «No, gracias», cada persona que termina queda marcada para contacto. Verificar que sea aceptable (Habeas Data / Ley 1581).
Despliegue auto-hospedado en VPS, ya NO Vercel — lo montó @Manuel (commits
8cad3fa, 7accffd). Dominio: semaforodigital.com.
- Modelo: Docker (Next.js
output: "standalone",Dockerfile) → contenedor en127.0.0.1:3000→ nginx (80/443,nginx/semaforodigital.com.conf) → Certbot (SSL). Orquestadocker-compose.yml. VPS158.101.105.13, app en/opt/radar-digital. - CI/CD:
git push origin maindispara.github/workflows/deploy.yml, que entra por SSH al VPS y corregit pull+docker compose up -d --build. Requiere secrets de GitHub:VPS_HOST,VPS_USER,VPS_SSH_KEY. - Deploy manual (alternativa):
bash scripts/deploy.sh(rsync + rebuild por SSH); preparación inicial del servidor conscripts/setup-vps.sh(Docker, nginx, Certbot). - Variables de entorno: en el VPS, archivo
.env.production(lo leedocker-compose.ymlconenv_file), no en Vercel. Vars:SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,ADMIN_PASSWORD,ADMIN_SESSION_SECRET,IP_HASH_SALT,RESEND_API_KEY,LEAD_NOTIFY_EMAIL, y opcional una key de IA. - Local:
npm install·npm run dev(localhost:3000) ·npm run build.⚠️ Se añadió la dependenciaqrcode: correnpm installpara sincronizarnode_modules. - Atajo local: doble clic en
Iniciar Semáforo Digital.bat.
⚠️ Coexisten dos mecanismos de deploy: GitHub Actions hacegit pullen el VPS (despliega lo que está en GitHub), mientrasscripts/deploy.shhacersyncdesde tu máquina (sube tus archivos locales, incluso sin commitear). Usa uno u otro con cuidado para no desincronizar el VPS.
Tags de git marcan estados estables. P. ej. v1-8dimensiones = versión previa
al rediseño de 4 secciones + semáforo grande. Restaurar: git checkout <tag>.