feat: sitio comercial FSPM con micro CRM interno
Sitio público orientado a venta consultiva e implementación de las recomendaciones de la auditoría de marca y mercado: - Inicio con tres rutas (industria, riesgo, cumplimiento) y clasificador de riesgo en el héroe, contra el hallazgo de que el visitante tenía que inferir qué solución le correspondía. - Cinco páginas por segmento prioritario y catálogo comparable por clase de fuego, capacidad y agente. - Los claims sin evidencia por modelo quedan aislados y se presentan como compromiso de documentación, nunca como característica del producto. - SEO técnico completo: metadatos por página, sitemap derivado de los datos, JSON-LD, iconos y tarjeta social generadas. Micro CRM: - Captura por formulario embebido y por widget de WhatsApp que registra antes de abrir la conversación. - Atribución de primer toque que sobrevive a la navegación. - Deduplicación de contacto; un mismo contacto acumula oportunidades. - Motor de asignación por reglas con reparto por turno de respaldo. - Panel con dos roles de alcance distinto, tablero analítico, bitácora, pedidos y reglas. No enlazado desde el sitio público y con noindex. Empaquetado en Docker multietapa con la base de demostración horneada en la imagen y el volumen de datos separado, para que los leads capturados sobrevivan a los redespliegues. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
122e06a4bb
commit
01b817d098
@@ -0,0 +1,162 @@
|
||||
# Contrato del proyecto — léelo completo antes de escribir código
|
||||
|
||||
Sitio + micro CRM para **FSPM (Fire Service Plus México)**, protección contra incendios.
|
||||
Es una **demo comercial** de Consultoría E3: tiene que verse y funcionar como producción.
|
||||
|
||||
Raíz: `H:\MegaSync\Proyectos\fspm\fspm-web`
|
||||
|
||||
## Stack ya instalado — no cambies versiones, no agregues dependencias
|
||||
|
||||
Next.js 15.5.4 (App Router) · React 19 · TypeScript estricto · Tailwind CSS **v4**
|
||||
(sin `tailwind.config`, los tokens viven en `@theme` dentro de `src/app/globals.css`) ·
|
||||
Prisma 6.16 + SQLite · zod 4 · jose · bcryptjs.
|
||||
|
||||
Si crees que necesitas una dependencia nueva, **no la instales**: resuélvelo con lo que hay
|
||||
o dilo en tu reporte final.
|
||||
|
||||
## Regla de propiedad de archivos — la más importante
|
||||
|
||||
Trabajas en paralelo con otros agentes sobre el mismo árbol. **Solo puedes crear y editar
|
||||
los archivos de tu lote.** Si necesitas cambiar un archivo ajeno, no lo toques: descríbelo
|
||||
en tu reporte final y el orquestador lo integra.
|
||||
|
||||
Archivos del orquestador, **de solo lectura para ti**:
|
||||
|
||||
```
|
||||
src/app/globals.css src/app/layout.tsx
|
||||
src/data/*.ts src/lib/db.ts
|
||||
src/lib/utm.ts src/lib/crm/constantes.ts
|
||||
src/lib/leads/esquema.ts src/components/marca/RielClases.tsx
|
||||
src/components/site/Encabezado.tsx src/components/site/Pie.tsx
|
||||
prisma/schema.prisma package.json next.config.ts .env
|
||||
```
|
||||
|
||||
## Sistema de diseño — deriva todo de aquí, no improvises color ni tipografía
|
||||
|
||||
Los tokens están en `src/app/globals.css` bajo `@theme`. Úsalos como clases de Tailwind
|
||||
(`bg-ink`, `text-muted`, `border-rule`, `text-signal`) o como `var(--color-…)`.
|
||||
|
||||
**Dos mundos, y el contraste entre ellos es la idea:**
|
||||
|
||||
- **Oscuro = el riesgo y la operación.** `bg-ink` (#0C1116, acero-humo azulado), paneles
|
||||
`bg-ink-2` / `bg-ink-3`, filetes `border-rule`.
|
||||
- **Claro = la evidencia y el documento.** `bg-paper` (#EEF1F3, blanco de plano de
|
||||
ingeniería — **no** es crema), texto `text-deep`.
|
||||
|
||||
Alterna secciones entre los dos mundos. Es la traducción visual del diagnóstico del
|
||||
estudio: convertir autoridad técnica en confianza comprable.
|
||||
|
||||
**Señal:** `text-signal` / `bg-signal` = #DB042B, el rojo real de FSPM extraído de su CSS.
|
||||
Es señal, no relleno: úsalo en el CTA, en el riel de clases y en acentos puntuales.
|
||||
`text-ember` (#F2B705) es el amarillo del traje de bombero de su propia fotografía:
|
||||
**solo para estados de riesgo, advertencia y atención**, nunca decorativo.
|
||||
|
||||
**Tipografía**, ya cargada en el layout raíz:
|
||||
|
||||
- `font-display` — Archivo 800/900. Encabezados. Versalitas y tracking cerrado. Es
|
||||
rotulación de equipo industrial.
|
||||
- `font-sans` — IBM Plex Sans. Cuerpo.
|
||||
- `font-mono` — IBM Plex Mono. **La capa de datos**: etiquetas, especificaciones, folios,
|
||||
cifras, códigos, UTMs. Todo dato numérico lleva `font-variant-numeric: tabular-nums`
|
||||
(ya aplicado a `.font-mono` y a `table`).
|
||||
|
||||
Radios casi rectos: `rounded-[3px]` / `rounded-[2px]`. Es una herramienta, no una app de consumo.
|
||||
|
||||
**Utilidades ya escritas — reúsalas en vez de reinventar:** `.shell` `.shell-wide`
|
||||
`.eyebrow` `.btn .btn-signal .btn-ghost .btn-deep .btn-sm` `.field .field-lite
|
||||
.field-label .field-error` `.panel .panel-lite` `.spec .spec-cell .spec-key .spec-val`
|
||||
`.hair .hair-lite` `.grid-bg .grid-bg-lite` `.hazard` `.badge .badge-signal .badge-ember
|
||||
.badge-ok` `.link` `.skip` `.anim-rise` `.anim-ignite` `.reveal`.
|
||||
|
||||
### Elemento firma: el riel de clases de fuego
|
||||
|
||||
`src/components/marca/RielClases.tsx` — cinco celdas fijas `A B C D K`, se encienden solo
|
||||
las que ese producto/solución/lead cubre. Aparece en el sitio **y** en el panel. Úsalo
|
||||
siempre que haya clases de fuego; no dibujes otra variante.
|
||||
|
||||
### Movimiento
|
||||
|
||||
Contenido. Una secuencia orquestada en el héroe y revelados discretos al hacer scroll con
|
||||
`.reveal` + IntersectionObserver. Nada de efectos dispersos: delatan trabajo automático.
|
||||
`prefers-reduced-motion` ya está respetado globalmente.
|
||||
|
||||
## Piso de calidad, no negociable
|
||||
|
||||
- **Responsive real** desde 320px. Prueba mentalmente 320 / 768 / 1280 / 1920.
|
||||
- **Compatibilidad WebKit (Safari e iOS).** Es requisito explícito del cliente:
|
||||
usa `-webkit-backdrop-filter` junto a `backdrop-filter`, `-webkit-appearance: none` en
|
||||
controles, `-webkit-overflow-scrolling: touch` en scroll horizontal, `-webkit-tap-highlight-color`
|
||||
en elementos táctiles, y evita `gap` en `inline-flex` sin fallback. Nada de `100vh` a secas
|
||||
en móvil: usa `100dvh` con `100vh` de respaldo.
|
||||
- **Accesibilidad:** un solo `<h1>` por página, jerarquía correcta, `aria-label` en
|
||||
controles sin texto, foco visible (ya en `:focus-visible`), contraste suficiente,
|
||||
navegación completa con teclado.
|
||||
- Nada de texto dentro de imágenes. `next/image` con `width`/`height` o `fill` + `sizes`.
|
||||
- Sin `any`. Sin `console.log` en el código final. `npx tsc --noEmit` debe pasar.
|
||||
- Español de México, acentuación correcta, tono técnico y sereno.
|
||||
|
||||
## Honestidad del contenido — regla dura del cliente
|
||||
|
||||
El estudio de marca concluyó que los claims del sitio original (*"no caduca"*,
|
||||
*"20 años de garantía"*, *"no requiere recarga"*) **no pueden presentarse como universales**.
|
||||
`src/data/catalogo.ts` ya los aisló en `producto.claimsPorVerificar`.
|
||||
|
||||
- **Nunca** presentes un claim de `claimsPorVerificar` como hecho.
|
||||
- Muéstralos en un bloque aparte, honesto, del tipo "Qué documentamos antes de cotizar".
|
||||
Eso **es** el argumento de venta: la competencia promete, FSPM documenta.
|
||||
- No inventes cifras, certificados, precios, casos de éxito ni testimonios. Si un dato no
|
||||
está en `src/data/`, no existe.
|
||||
- Los logotipos de clientes en `src/data/empresa.ts` se muestran **sin** atribuirles
|
||||
testimonio ni resultado: el estudio pide confirmar la relación antes de usarlos como caso.
|
||||
- Los casos de aplicación deben estar rotulados como ilustrativos.
|
||||
|
||||
## Datos disponibles (solo lectura)
|
||||
|
||||
- `src/data/catalogo.ts` — 8 categorías, 22 productos. `CATEGORIAS`, `PRODUCTOS`,
|
||||
`getCategoria`, `getProducto`, `productosDeCategoria`.
|
||||
- `src/data/industrias.ts` — los 5 segmentos prioritarios del estudio. `INDUSTRIAS`, `getIndustria`.
|
||||
- `src/data/clases-fuego.ts` — `CLASES`, `CLASES_INFO`, `parseClases`, `serializeClases`.
|
||||
- `src/data/empresa.ts` — `EMPRESA`, `NAV_PRINCIPAL`, `NAV_PIE`, `CLIENTES`, `PRENSA`,
|
||||
`NORMAS`, `SITIO_URL`, `WHATSAPP`, `AVISO_DEMO`.
|
||||
- `src/lib/crm/constantes.ts` — dominio del CRM. SQLite no guarda enums: **todo estado sale de aquí.**
|
||||
- `src/lib/leads/esquema.ts` — contrato de captura de leads y helpers de normalización.
|
||||
- `src/lib/utm.ts` — `capturarAtribucion()`, para clientes.
|
||||
|
||||
Assets en `public/media/`: `marca/`, `producto/`, `clientes/`, `prensa/`, `iconos/`, `foto/`.
|
||||
**Usa solo archivos que existan**: verifícalo con `ls public/media/<carpeta>`.
|
||||
|
||||
## Rutas del proyecto
|
||||
|
||||
Público, dentro del grupo `src/app/(sitio)/`:
|
||||
`/` · `/soluciones` · `/soluciones/[industria]` · `/catalogo` · `/catalogo/[categoria]` ·
|
||||
`/producto/[slug]` · `/cumplimiento-nom-002` · `/evidencia` · `/casos` ·
|
||||
`/preguntas-frecuentes` · `/nosotros` · `/contacto` · `/aviso-de-privacidad`
|
||||
|
||||
Panel, en `src/app/admin/`:
|
||||
`/admin/login` · `/admin` · `/admin/oportunidades` · `/admin/oportunidades/[id]` ·
|
||||
`/admin/contactos` · `/admin/contactos/[id]` · `/admin/pedidos` · `/admin/analitica` ·
|
||||
`/admin/equipo` · `/admin/reglas`
|
||||
|
||||
**El panel no se enlaza desde ninguna parte del sitio público.** Sin enlace en el
|
||||
encabezado, en el pie, ni en el sitemap. Se llega escribiendo la URL. Además va con
|
||||
`robots: { index: false, follow: false }` en su metadata.
|
||||
|
||||
## Comandos
|
||||
|
||||
```
|
||||
npm run dev # servidor de desarrollo
|
||||
npx tsc --noEmit # verificación de tipos: tiene que pasar limpio
|
||||
npx prisma db push # aplicar el esquema
|
||||
npm run db:seed # sembrar datos de demo
|
||||
```
|
||||
|
||||
No corras `npm install`. No corras `git commit`. No borres archivos ajenos.
|
||||
|
||||
## Tu reporte final
|
||||
|
||||
Termina con, en este orden:
|
||||
|
||||
1. Archivos creados o modificados, con una línea de qué hace cada uno.
|
||||
2. Resultado literal de `npx tsc --noEmit` sobre tus archivos.
|
||||
3. Lo que dejaste pendiente o lo que necesitas de otro lote.
|
||||
4. Cualquier dato del sitio original que te pareció inconsistente o sin sustento.
|
||||
@@ -0,0 +1,145 @@
|
||||
# Tarea: construir el catálogo tipado de FSPM
|
||||
|
||||
Eres el agente de datos del proyecto. Tu única entrega es **un archivo**:
|
||||
`src/data/catalogo.ts` en este repo (`H:\MegaSync\Proyectos\fspm\fspm-web`).
|
||||
|
||||
No toques ningún otro archivo. No instales dependencias. No corras `npm`.
|
||||
|
||||
## Fuente de verdad (solo lectura)
|
||||
|
||||
Base de conocimiento del sitio real, escrapeada:
|
||||
`H:\MegaSync\Proyectos\Web Clonner Scrapper toolkit\proyectos\fspm.com.mx\conocimiento\`
|
||||
|
||||
Lee **estos archivos en español** (ignora los `en-*` y los que llevan sufijo hash como `-8e5a3550`):
|
||||
|
||||
- `index.md` — las 8 categorías y cómo se presentan
|
||||
- `extintores-portatiles.md`
|
||||
- `extintores-fijos.md`
|
||||
- `productos-moviles.md`
|
||||
- `rociadores.md`
|
||||
- `agentes-extinguidores.md`
|
||||
- `firemiks.md`
|
||||
- `robots.md`
|
||||
- `proteccion-civil.md`
|
||||
- `normas-y-certificaciones.md`
|
||||
- `preguntas-frecuentes.md`
|
||||
|
||||
## Imágenes disponibles
|
||||
|
||||
Ya están copiadas en `public/media/producto/` y `public/media/iconos/`. Lista real de archivos —
|
||||
usa **exactamente** estos nombres, con ruta `/media/producto/<archivo>`:
|
||||
|
||||
```
|
||||
firemiks.png
|
||||
FSPM_espumas_contra_incendios_flameAde.png
|
||||
fspm_extintores_facil_uso_en_mexico.png
|
||||
fspm_extintores_fijos_enforcer_10.png
|
||||
fspm_extintores_fijos_esfera_sin_detector_de_humo.png
|
||||
fspm_extintores_garantia_en_mexico.png
|
||||
fspm_extintores_largo_alcance_en_mexico.png
|
||||
fspm_extintores_moviles_carretilla_100litros.png
|
||||
fspm_extintores_moviles_carretilla_30litros_acero_inoxidable.png
|
||||
fspm_extintores_moviles_carretilla_50litros_acero_inoxidable.png
|
||||
fspm_extintores_moviles_carretilla_70litros_acero_inoxidable.png
|
||||
fspm_extintores_moviles_enforcer_60.png
|
||||
fspm_extintores_no_caducan_en_mexico.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_12litros.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_1litro.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_2litros.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_4litros.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_6litros.png
|
||||
fspm_extintores_portatiles_en_mexico_enforcer_9litros.png
|
||||
fspm_extintores_rociadores_enforcer_esfera.png
|
||||
fspm_extintores_rociadores_enforcer_robot.png
|
||||
fspm_extintores_rociadores_enforcer_robot_componente_de_agua.png
|
||||
fspm_extintores_rociadores_enforcer_robot_panel_de_control.png
|
||||
fspm_extintores_sin_recarga_en_mexico.png
|
||||
FSPM-trakAde-supresion-de-polvos.png
|
||||
FSPM-vaporAde.png
|
||||
receptor-de-alertamiento.jpg
|
||||
```
|
||||
|
||||
Iconos de aplicación en `/media/iconos/`: `icono-agricultura.png`, `icono-auditorios.png`,
|
||||
`icono-bosques.png`, `icono-central-bomberos.png`, `icono-exterior.png`, `icono-gasolineras.png`,
|
||||
`icono-hogar.png`, `icono-industria.png`, `icono-museos.png`, `icono-no-toxico.png`,
|
||||
`icono-oficina.png`, `iconos-aeropuertos.png`, `iconos-minas.png`.
|
||||
|
||||
Si un producto no tiene imagen propia, reutiliza la más cercana de su categoría. Nunca inventes
|
||||
un nombre de archivo que no esté en la lista.
|
||||
|
||||
## Contrato de tipos — respétalo al carácter
|
||||
|
||||
```ts
|
||||
export type ClaseFuego = "A" | "B" | "C" | "D" | "K";
|
||||
|
||||
export type Categoria = {
|
||||
slug: string; // kebab-case, ej. "extintores-portatiles"
|
||||
nombre: string; // ej. "Extintores portátiles"
|
||||
tagline: string; // 1 línea: qué problema resuelve, orientado a comprador
|
||||
descripcion: string; // 2-3 frases
|
||||
imagen: string; // "/media/producto/..."
|
||||
aplicaciones: string[];
|
||||
orden: number;
|
||||
};
|
||||
|
||||
export type Producto = {
|
||||
slug: string;
|
||||
categoria: string; // debe existir en CATEGORIAS
|
||||
nombre: string;
|
||||
modelo: string | null; // ej. "Enforcer 12"
|
||||
capacidad: string | null; // ej. "12 L"
|
||||
agente: string | null; // ej. "FireAde 2000"
|
||||
clases: ClaseFuego[]; // SOLO las clases que la fuente sostiene
|
||||
alcance: string | null;
|
||||
descarga: string | null;
|
||||
imagen: string;
|
||||
resumen: string; // 1-2 frases, orientado a venta, SIN claims sin sustento
|
||||
caracteristicas: string[]; // bullets tomados de la fuente
|
||||
aplicaciones: string[];
|
||||
claimsPorVerificar: string[]; // ver regla crítica abajo
|
||||
orden: number;
|
||||
};
|
||||
|
||||
export const CATEGORIAS: Categoria[] = [ ... ];
|
||||
export const PRODUCTOS: Producto[] = [ ... ];
|
||||
|
||||
// helpers que debes exportar también:
|
||||
export function getCategoria(slug: string): Categoria | undefined
|
||||
export function getProducto(slug: string): Producto | undefined
|
||||
export function productosDeCategoria(slug: string): Producto[]
|
||||
```
|
||||
|
||||
## REGLA CRÍTICA — honestidad de los datos
|
||||
|
||||
El sitio original hace afirmaciones de alto riesgo comercial: *"no caduca"*, *"20 años de
|
||||
garantía"*, *"no requiere recarga"*, *"extingue clases A, B, C, D y K"*. Una auditoría concluyó
|
||||
que **no deben presentarse como universales sin condiciones y evidencia por modelo**.
|
||||
|
||||
Por lo tanto:
|
||||
|
||||
1. **Nunca** pongas uno de esos claims en `resumen` ni en `caracteristicas` como hecho.
|
||||
2. Cada claim así va en `claimsPorVerificar`, redactado como lo que hay que documentar.
|
||||
Ejemplo: `"Vida útil sin caducidad: requiere carta técnica del fabricante por número de modelo"`.
|
||||
3. `clases` solo lleva las clases que la fuente realmente atribuye a **ese** modelo. Si la
|
||||
fuente es ambigua, pon las que sí sostiene y anota la duda en `claimsPorVerificar`.
|
||||
4. No inventes capacidades, alcances, tiempos de descarga, precios ni certificados. Si el dato
|
||||
no está en la fuente, es `null` o se omite del arreglo. Es mejor `null` que un número inventado.
|
||||
|
||||
## Cobertura esperada
|
||||
|
||||
- Las **8 categorías** del sitio.
|
||||
- Todos los modelos concretos que la fuente identifique. Del scrape se ven al menos: portátiles
|
||||
Enforcer de 1, 2, 4, 6, 9 y 12 litros; móviles Enforcer 60 y carretillas de 30, 50, 70 y 100 L;
|
||||
fijos Enforcer 10 y esfera; rociadores esfera y robot; agentes FireAde 2000, flameAde, vaporAde,
|
||||
trakAde; FIREMIKS; robot cañón; y receptor de alertamiento en protección civil.
|
||||
Confirma cada uno contra la fuente antes de escribirlo.
|
||||
|
||||
## Salida
|
||||
|
||||
- TypeScript válido y formateado, sin `any`, sin imports.
|
||||
- Español de México en todo el contenido, con acentuación correcta.
|
||||
- Al terminar, escribe al final del archivo un comentario de bloque con:
|
||||
las categorías y el número de productos por categoría, y la lista de datos que no pudiste
|
||||
confirmar en la fuente.
|
||||
|
||||
Cuando acabes, di exactamente `CATALOGO LISTO` y un resumen de 3 líneas.
|
||||
@@ -0,0 +1,119 @@
|
||||
# Tarea: pruebas de calidad del sitio y del panel FSPM
|
||||
|
||||
Eres el agente de QA del proyecto. **No corriges código.** Tu trabajo es ejecutar
|
||||
las pruebas, reproducir lo que falle, y entregar un reporte que el orquestador
|
||||
pueda accionar sin volver a investigar desde cero.
|
||||
|
||||
Repo: `H:\MegaSync\Proyectos\fspm\fspm-web`
|
||||
|
||||
## Contexto
|
||||
|
||||
Sitio comercial de FSPM (protección contra incendios) con un micro CRM interno.
|
||||
Es una demostración para directivos: un error visible cuesta la venta del
|
||||
servicio, así que la barra es alta.
|
||||
|
||||
Lee `docs/CONTRATO.md` para conocer el sistema de diseño y las reglas de
|
||||
contenido, y `README.md` para entender el circuito de captura.
|
||||
|
||||
## Preparación — YA ESTÁ HECHA, no la repitas
|
||||
|
||||
El artefacto de producción **ya está construido y corriendo en un contenedor**:
|
||||
|
||||
- Imagen `fspm-web:demo`, contenedor `fspm-demo`, en `http://127.0.0.1:3100`.
|
||||
- Base sembrada: 5 usuarios, 60 contactos, 95 oportunidades, 18 pedidos, 6 reglas.
|
||||
- `curl http://127.0.0.1:3100/api/salud` responde `{"ok":true,...}`.
|
||||
|
||||
Playwright está configurado con `reuseExistingServer: true` apuntando al 3100,
|
||||
así que **usa ese contenedor y no arranques ningún servidor de desarrollo**.
|
||||
|
||||
**No corras `npm run build` ni `npm run dev` en esta máquina.** El repositorio
|
||||
vive dentro de una carpeta sincronizada a la nube y el cliente de
|
||||
sincronización mueve archivos de `.next` a media compilación: el build falla
|
||||
con `Cannot find module './NNNN.js'` aunque el código esté bien. Ya está
|
||||
comprobado. Si necesitas reiniciar la aplicación, usa
|
||||
`docker restart fspm-demo`.
|
||||
|
||||
Verifica que el contenedor responde antes de empezar:
|
||||
|
||||
```bash
|
||||
docker ps --filter name=fspm-demo
|
||||
curl -s http://127.0.0.1:3100/api/salud
|
||||
```
|
||||
|
||||
## Ejecución
|
||||
|
||||
```bash
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
Cuatro proyectos: `escritorio-chromium`, `escritorio-webkit`, `movil-webkit`,
|
||||
`movil-chico` (320px). **Los dos de WebKit son obligatorios**: es el motor de
|
||||
Safari e iOS, donde más se rompe la responsividad, y el cliente lo pidió por
|
||||
nombre. Un resultado que solo cubre Chromium no sirve.
|
||||
|
||||
El servidor de pruebas lo arranca Playwright solo, en el puerto 3100.
|
||||
|
||||
Las suites:
|
||||
|
||||
- `tests/01-humo.spec.ts` — rutas, títulos, meta description, Open Graph,
|
||||
canónicas, sitemap, robots, iconos, 404.
|
||||
- `tests/02-responsivo.spec.ts` — desborde horizontal de 320 a 1440, menú
|
||||
móvil, widget de WhatsApp, tabla comparativa.
|
||||
- `tests/03-captura.spec.ts` — el circuito comercial: campaña → navegación →
|
||||
formulario, y que la atribución de primer toque sobreviva.
|
||||
- `tests/04-panel.spec.ts` — panel no expuesto, sesión obligatoria, y el
|
||||
aislamiento entre administrador y vendedor.
|
||||
- `tests/05-accesibilidad.spec.ts` — alt, etiquetas, jerarquía de encabezados,
|
||||
foco, movimiento reducido.
|
||||
|
||||
## Además de las pruebas automáticas: revisión visual
|
||||
|
||||
Toma capturas y **míralas**. Las pruebas no detectan que algo se ve mal.
|
||||
|
||||
```bash
|
||||
npx playwright screenshot --viewport-size=1440,900 http://127.0.0.1:3100/ tests/reporte/inicio-1440.png
|
||||
npx playwright screenshot --viewport-size=375,900 --full-page http://127.0.0.1:3100/ tests/reporte/inicio-375.png
|
||||
```
|
||||
|
||||
Hazlo para `/`, `/catalogo`, una ficha de producto, `/soluciones/industria-energia`
|
||||
y `/admin` tras entrar con el acceso rápido. Revisa en cada una:
|
||||
|
||||
- Texto encima de imagen: ¿se lee de verdad, o el degradado no alcanza?
|
||||
- Espaciado entre secciones: ¿hay saltos incoherentes o secciones pegadas?
|
||||
Es la falla típica cuando varios agentes escriben CSS en paralelo.
|
||||
- ¿Alguna sección se ve como plantilla genérica frente al resto?
|
||||
- ¿El riel de clases de fuego aparece consistente en sitio y panel?
|
||||
- ¿Hay estados vacíos que se vean como errores?
|
||||
- Contraste: ¿algún texto gris sobre fondo oscuro que no se alcance a leer?
|
||||
|
||||
## Sondeos manuales que valen la pena
|
||||
|
||||
1. **Doble envío.** Manda el formulario dos veces con el mismo correo y
|
||||
confirma en `/admin/contactos` que hay **un contacto con dos oportunidades**,
|
||||
no dos contactos. Es un requisito explícito del cliente.
|
||||
2. **Fuga entre roles.** Entra como vendedor, copia el id de una oportunidad
|
||||
que **no** sea suya (obtenlo entrando como administrador en otra pestaña) y
|
||||
pide `/admin/oportunidades/<id>` directo. Debe negarlo. Si la muestra, es
|
||||
una falla de seguridad y va primero en tu reporte.
|
||||
3. **Widget sin JavaScript de terceros.** Confirma que el enlace de WhatsApp
|
||||
que devuelve la API lleva el prefijo `[DEMO]` y el folio.
|
||||
4. **Panel en móvil real.** 375px de ancho: ¿las tablas se pueden usar o hay
|
||||
que hacer zoom?
|
||||
|
||||
## Reporte final — lo que necesito de ti
|
||||
|
||||
Nada de "todo pasó" ni de listas de pruebas verdes. Quiero:
|
||||
|
||||
1. **Resumen**: cuántas pruebas por proyecto, cuántas fallaron. Cifras reales,
|
||||
pegadas de la salida.
|
||||
2. **Fallas, ordenadas por gravedad.** Para cada una:
|
||||
- qué se rompe, en qué ruta y en qué proyecto/ancho;
|
||||
- el error literal de Playwright;
|
||||
- el archivo y la línea donde está la causa, si lo pudiste localizar;
|
||||
- si es una falla real del producto o una prueba mal escrita — dilo con
|
||||
claridad, no todo lo que falla es un bug.
|
||||
3. **Hallazgos visuales** de las capturas, con la ruta y el ancho.
|
||||
4. **Lo que no pudiste probar** y por qué.
|
||||
|
||||
Sé directo. Si algo está mal hecho, dilo con el argumento técnico. Un reporte
|
||||
complaciente no sirve de nada aquí.
|
||||
Reference in New Issue
Block a user