# FSPM — sitio comercial y micro CRM Demostración construida por **Consultoría E3** para Fire Service Plus México. Sitio público orientado a venta consultiva, con un CRM interno que registra cada envío de formulario y de widget de WhatsApp como oportunidad, con su atribución de campaña. No es un maquetado: los formularios escriben en base de datos, el panel lee de ella, y las reglas de asignación reparten los leads entre vendedores. ## De dónde salió el contenido El sitio implementa las recomendaciones de una auditoría de marca y mercado previa. Los hallazgos que dirigieron el diseño: | Hallazgo de la auditoría | Cómo se resuelve aquí | |---|---| | Propuesta demasiado amplia, ocho categorías sin rutas | Inicio con tres rutas —industria, riesgo y cumplimiento— y cinco páginas por segmento prioritario | | El visitante debe inferir qué solución le toca | Clasificador de riesgo en el héroe: elige operación y clases de fuego, y el sitio lo enruta | | Claims sin condiciones ni evidencia por modelo | Aislados en `claimsPorVerificar` y presentados como compromiso de documentación, nunca como característica | | Sin meta description ni Open Graph | Metadatos por página, sitemap derivado de los datos, JSON-LD y tarjeta social generada | | Conversión poco consultiva | El formulario pide lo que un ingeniero necesita para cotizar, no "solicita información" | | Sin forma de medir efectividad comercial | Atribución de primer toque que sobrevive a la navegación, y analítica por campaña en el panel | Los datos de producto provienen de la extracción del sitio público de FSPM. Lo que la fuente no sostiene está marcado como pendiente de verificar; nada se inventó para rellenar. ## Stack Next.js 15 App Router · React 19 · TypeScript estricto · Tailwind CSS v4 · Prisma 6 + SQLite · sesiones propias con `jose` · Playwright para pruebas. Sin librería de gráficas: el tablero dibuja SVG a mano. ## Arrancar en local ```bash npm install cp .env.example .env # ajusta AUTH_SECRET npx prisma db push npm run db:seed # datos de demostración npm run dev ``` Sitio en `http://localhost:3000`. El panel en `/admin/login` — **no está enlazado desde el sitio público**, es intencional. Las credenciales del seed se imprimen al final de `npm run db:seed`. En la pantalla de acceso hay además entrada rápida por perfil para la demostración, que se apaga con `DEMO_ACCESO_RAPIDO=false`. ## Estructura ``` src/app/(sitio)/ páginas públicas src/app/admin/ panel comercial, con noindex y fuera del sitemap src/app/api/leads/ captura: deduplica contacto, crea oportunidad, asigna src/data/ catálogo, industrias, clases de fuego, datos de empresa src/lib/crm/ dominio, sesiones, consultas, acciones, asignación src/components/marca/RielClases.tsx el elemento firma del diseño tests/ Playwright, incluidos dos proyectos WebKit docs/CONTRATO.md sistema de diseño y reglas de contenido docs/DESPLIEGUE.md variables, Docker y despliegue ``` ## Cómo funciona la captura 1. El visitante llega con parámetros de campaña. `src/lib/utm.ts` guarda el **primer toque** y no lo sobreescribe. 2. Navega. Envía el formulario en una ficha de producto, ya sin parámetros en la URL. 3. `POST /api/leads` deduplica el contacto por correo o teléfono normalizado, crea una **oportunidad nueva** —un mismo contacto acumula varias— y guarda la campaña del primer toque junto con el producto y la URL exactos. 4. Las reglas de asignación eligen vendedor; si ninguna coincide, se reparte por turno entre los activos. 5. El widget de WhatsApp hace lo mismo **antes** de abrir la conversación, así el lead existe aunque la persona nunca mande el mensaje. ## Pruebas ```bash npm run test:e2e # todos los proyectos npm run test:webkit # solo Safari e iOS ``` Cubren rutas y metadatos, responsividad de 320 a 1440 sin desborde horizontal, el circuito de captura con atribución, el aislamiento entre roles del panel y accesibilidad básica. ## Alcance Los datos del panel son ficticios. Los logotipos de clientes se muestran sin atribuirles testimonio ni resultado: la auditoría pide confirmar la relación antes de usarlos comercialmente. La información normativa es de referencia y no sustituye dictamen técnico ni jurídico.