La primera corrida completa dio 57 fallas. Solo una parte eran del producto; el resto era instrumentación mal escrita. Queda en 261 en verde. Defectos del producto: - La atribución de campaña se perdía. La captura vivía en un efecto de React, así que si la persona llegaba con parámetros de campaña y navegaba antes de hidratar, el primer toque nunca se guardaba y utmCampaign llegaba vacío al CRM. Ahora se captura en un script en línea que corre al analizar el documento. - Siete páginas sin og:image: una página que declara su propio openGraph no hereda las imágenes del layout, las sobreescribe. Solo se nota al compartir el enlace. Se resuelve con una función compartida en seo.ts. - El panel no se podía usar desde Safari por HTTP: la cookie Secure no se guarda y el acceso falla en silencio. Queda explícito y documentado. - El panel se corría de lado en móvil: la cabecera no cabía a 375px. - Las tarjetas de perfil del acceso se cortaban en móvil. Un elemento de rejilla nace con min-width auto y no puede encogerse bajo su contenido. Era además el origen de un desplazamiento que se arrastraba al tablero. - Las tablas accesibles de las gráficas volvían desplazable la página: sr-only fija width 1px y el algoritmo de tablas lo ignora. - El campo de búsqueda del catálogo no tenía nombre accesible. - Límite de tasa de 5 envíos por IP: bloqueaba una demostración en vivo con varias personas en la misma red. Configurable, ahora 20. - El salto de contenido pasa de left:-9999px a recorte de 1px. Instrumentación corregida, con la razón escrita en cada caso: - El desborde horizontal se mide intentando desplazar la página, no con documentElement.scrollWidth, que sobreinforma con contenedores propios. - La comprobación de accesibilidad respeta el árbol de accesibilidad. - El aislamiento entre roles se prueba pidiendo por URL una oportunidad ajena, no comparando folios entre listados paginados. - Fuera las esperas por networkidle, que agotaban el tiempo sin fallar. - Que el panel no esté enlazado se comprueba leyendo el HTML servido. - Safari no enfoca enlaces con Tab: declarado como del navegador. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
473 lines
21 KiB
TypeScript
473 lines
21 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { FormularioContacto } from "@/components/formulario/FormularioContacto";
|
|
import { Migas } from "@/components/site/contenido/Migas";
|
|
import { FOTOS, HeroeLanding } from "@/components/site/HeroeLanding";
|
|
import { AVISO_DEMO, EMPRESA, SITIO_URL } from "@/data/empresa";
|
|
import { imagenesOg } from "@/lib/seo";
|
|
|
|
/**
|
|
* /aviso-de-privacidad
|
|
*
|
|
* Reproduce, reordenado para poder leerse, el aviso publicado por FSPM en su
|
|
* sitio público, más una sección que declara qué recaba realmente el formulario
|
|
* de esta demo —cuatro campos visibles y el contexto de la página— y un resumen
|
|
* en lenguaje llano arriba, para que alguien pueda enterarse sin leer nueve
|
|
* apartados. Lo que no estaba en la fuente no se agregó, y la versión
|
|
* definitiva es siempre la que publique la empresa.
|
|
*
|
|
* Va con `robots: { index: false }` a propósito: es la reproducción de un
|
|
* documento legal de un tercero dentro de una demo, y no queremos que compita
|
|
* en buscadores con el aviso que la empresa publica en su propio dominio.
|
|
*/
|
|
|
|
const TITULO = "Aviso de privacidad";
|
|
const DESCRIPCION = `Aviso de privacidad de ${EMPRESA.razonSocial}: quién es el responsable, qué datos recaba este sitio, para qué se usan, a quién se transfieren y cómo ejercer tus derechos de acceso, rectificación, cancelación y oposición.`;
|
|
|
|
export const metadata: Metadata = {
|
|
title: TITULO,
|
|
description: DESCRIPCION,
|
|
alternates: { canonical: `${SITIO_URL}/aviso-de-privacidad` },
|
|
robots: { index: false, follow: true },
|
|
openGraph: {
|
|
title: `${TITULO} | ${EMPRESA.nombreCorto}`,
|
|
description: DESCRIPCION,
|
|
url: `${SITIO_URL}/aviso-de-privacidad`,
|
|
...imagenesOg(),
|
|
},
|
|
};
|
|
|
|
const CORREO_ARCO = "[email protected]";
|
|
|
|
const MENSAJE_INICIAL = "Mi duda sobre este sitio es ";
|
|
|
|
/** El aviso en cinco frases, para quien no va a leer los nueve apartados. */
|
|
const EN_CORTO = [
|
|
"Tus datos los trata Enforcer Units Fire Service Plus México, S.A. de C.V.",
|
|
"Se usan para identificarte, contactarte y darle seguimiento a tu solicitud.",
|
|
"El aviso original contempla también envío de invitaciones, noticias y campañas.",
|
|
"Contempla transferencias a asociados y a terceros para esas mismas finalidades.",
|
|
`Puedes pedir acceso, corrección, cancelación u oposición escribiendo a ${CORREO_ARCO}.`,
|
|
];
|
|
|
|
/**
|
|
* Qué recaba el formulario de este sitio. Cuatro campos visibles y el contexto
|
|
* que se deduce de la página, sin preguntarlo.
|
|
*/
|
|
const CAMPOS_VISIBLES = [
|
|
{ campo: "Nombre completo", uso: "Identificarte y dirigirnos a ti por tu nombre en el seguimiento." },
|
|
{ campo: "Correo electrónico", uso: "Enviarte la respuesta técnica y la propuesta." },
|
|
{ campo: "Teléfono", uso: "Contactarte cuando la solicitud requiere una llamada." },
|
|
{
|
|
campo: "Mensaje",
|
|
uso: "Conocer el requerimiento en tus palabras. Lo que escribas aquí es lo único que decides compartir.",
|
|
},
|
|
{
|
|
campo: "Aceptación de este aviso",
|
|
uso: "Registrar que aceptaste el tratamiento antes de enviar. Sin esa casilla no se envía nada.",
|
|
},
|
|
];
|
|
|
|
const CAMPOS_CONTEXTO = [
|
|
{
|
|
campo: "Página desde la que escribiste",
|
|
uso: "Saber si llegaste desde una ficha de producto, un segmento o la página de cumplimiento, para responder sobre eso.",
|
|
},
|
|
{
|
|
campo: "Producto, categoría, industria o escenario de esa página",
|
|
uso: "Interpretar el escenario sin obligarte a llenar un cuestionario técnico.",
|
|
},
|
|
{
|
|
campo: "Clases de fuego asociadas a esa página",
|
|
uso: "Preparar la selección de equipo antes de la primera llamada.",
|
|
},
|
|
{
|
|
campo: "Origen de la visita (campaña, buscador o enlace)",
|
|
uso: "Medir qué contenido resuelve dudas reales. No identifica a nadie por sí solo.",
|
|
},
|
|
];
|
|
|
|
export default function PaginaAviso() {
|
|
return (
|
|
<>
|
|
<HeroeLanding
|
|
antes={<Migas actual="Aviso de privacidad" />}
|
|
eyebrow="Documento legal"
|
|
titulo="Aviso de privacidad"
|
|
entrada={`Reproducción del aviso publicado por ${EMPRESA.razonSocial}, reordenado para poder leerse. La versión vigente y definitiva es la que la empresa publique en su propio sitio.`}
|
|
foto={FOTOS.mapa}
|
|
enfoque="45% 45%"
|
|
prioridad
|
|
>
|
|
<div
|
|
role="note"
|
|
aria-label="Naturaleza de este sitio"
|
|
className="max-w-2xl rounded-[6px] border border-rule border-l-2 border-l-ember bg-ink-2/90 p-5"
|
|
>
|
|
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-ember">
|
|
Sobre este sitio
|
|
</p>
|
|
<p className="mt-3 text-sm leading-relaxed text-muted">{AVISO_DEMO}</p>
|
|
</div>
|
|
</HeroeLanding>
|
|
|
|
{/* ---------- el aviso en corto ---------- */}
|
|
<section className="border-t border-rule bg-ink-2 py-12 sm:py-14" aria-labelledby="en-corto">
|
|
<div className="shell">
|
|
<div className="max-w-3xl">
|
|
<p className="eyebrow eyebrow-muted mb-4">En corto</p>
|
|
<h2 id="en-corto" className="font-display text-xl font-extrabold sm:text-2xl">
|
|
Lo esencial en cinco frases
|
|
</h2>
|
|
<p className="mt-3 text-sm leading-relaxed text-muted">
|
|
Este resumen no sustituye al aviso: es una entrada para que puedas leerlo sabiendo qué
|
|
buscar. El texto que manda es el de los nueve apartados de abajo.
|
|
</p>
|
|
<ul className="mt-6 space-y-3">
|
|
{EN_CORTO.map((punto) => (
|
|
<li key={punto} className="flex gap-3 text-sm leading-relaxed text-bright">
|
|
<span
|
|
aria-hidden="true"
|
|
className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-signal"
|
|
/>
|
|
{punto}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ---------- el aviso ---------- */}
|
|
<article className="border-t border-rule bg-paper py-14 text-deep sm:py-20">
|
|
<div className="shell">
|
|
<div className="max-w-3xl space-y-12">
|
|
<section aria-labelledby="responsable">
|
|
<h2
|
|
id="responsable"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
1. Responsable del tratamiento
|
|
</h2>
|
|
<div className="mt-4 space-y-4 text-sm leading-relaxed text-deep-muted">
|
|
<p>
|
|
{EMPRESA.razonSocial} ({EMPRESA.nombreCorto}), empresa legalmente constituida
|
|
conforme a las leyes de la República Mexicana, con domicilio en Avenida Instituto
|
|
Técnico Industrial número 11, Colonia {EMPRESA.colonia}, Alcaldía Cuauhtémoc, C.P.
|
|
06400, {EMPRESA.ciudad}.
|
|
</p>
|
|
<p>
|
|
{EMPRESA.nombreCorto} será el responsable del tratamiento que, con motivo de la
|
|
relación jurídica que haya sido celebrada y para el cumplimiento de la misma, se
|
|
le dé a tus datos personales.
|
|
</p>
|
|
<p>
|
|
En cumplimiento de la Ley Federal de Protección de Datos Personales en Posesión de
|
|
los Particulares, pone a tu disposición el presente aviso para hacer de tu
|
|
conocimiento que los datos personales que nos proporciones, incluso aquellos
|
|
sensibles y patrimoniales o financieros, serán tratados dentro de un marco que
|
|
garantice su privacidad y el derecho a la autodeterminación informativa de las
|
|
personas.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="datos">
|
|
<h2 id="datos" className="font-display text-xl font-extrabold text-deep sm:text-2xl">
|
|
2. Qué datos recaba este sitio
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
Esta sección no viene del aviso original: describe exactamente lo que recoge el
|
|
formulario de este sitio, para que sepas qué estás enviando antes de enviarlo. Son
|
|
cuatro campos visibles, más el contexto de la página en la que estabas.
|
|
</p>
|
|
|
|
<h3 className="mt-8 font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
|
|
Lo que escribes tú
|
|
</h3>
|
|
<div
|
|
className="mt-4 overflow-x-auto rounded-[6px] border border-rule-lite bg-white [-webkit-overflow-scrolling:touch]"
|
|
tabIndex={0}
|
|
role="region"
|
|
aria-label="Campos que llena la persona, tabla desplazable en horizontal"
|
|
>
|
|
<table className="w-full min-w-[34rem] border-collapse text-left">
|
|
<caption className="sr-only">
|
|
Campos visibles del formulario de contacto y para qué se usa cada uno.
|
|
</caption>
|
|
<thead>
|
|
<tr className="border-b border-rule-lite bg-paper-2">
|
|
{["Dato", "Para qué se usa"].map((c) => (
|
|
<th
|
|
key={c}
|
|
scope="col"
|
|
className="whitespace-nowrap px-4 py-3 font-mono text-[0.625rem] font-medium uppercase tracking-[0.14em] text-deep-muted"
|
|
>
|
|
{c}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{CAMPOS_VISIBLES.map((fila) => (
|
|
<tr key={fila.campo} className="border-b border-rule-lite last:border-b-0">
|
|
<th scope="row" className="px-4 py-3.5 text-left align-top">
|
|
<span className="font-sans text-sm font-semibold text-deep">
|
|
{fila.campo}
|
|
</span>
|
|
</th>
|
|
<td className="px-4 py-3.5 align-top text-sm leading-relaxed text-deep-muted">
|
|
{fila.uso}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h3 className="mt-8 font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
|
|
Lo que se deduce de la página, sin preguntártelo
|
|
</h3>
|
|
<div
|
|
className="mt-4 overflow-x-auto rounded-[6px] border border-rule-lite bg-white [-webkit-overflow-scrolling:touch]"
|
|
tabIndex={0}
|
|
role="region"
|
|
aria-label="Contexto que registra el formulario, tabla desplazable en horizontal"
|
|
>
|
|
<table className="w-full min-w-[34rem] border-collapse text-left">
|
|
<caption className="sr-only">
|
|
Contexto que el formulario registra a partir de la página de origen.
|
|
</caption>
|
|
<thead>
|
|
<tr className="border-b border-rule-lite bg-paper-2">
|
|
{["Dato", "Para qué se usa"].map((c) => (
|
|
<th
|
|
key={c}
|
|
scope="col"
|
|
className="whitespace-nowrap px-4 py-3 font-mono text-[0.625rem] font-medium uppercase tracking-[0.14em] text-deep-muted"
|
|
>
|
|
{c}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{CAMPOS_CONTEXTO.map((fila) => (
|
|
<tr key={fila.campo} className="border-b border-rule-lite last:border-b-0">
|
|
<th scope="row" className="px-4 py-3.5 text-left align-top">
|
|
<span className="font-sans text-sm font-semibold text-deep">
|
|
{fila.campo}
|
|
</span>
|
|
</th>
|
|
<td className="px-4 py-3.5 align-top text-sm leading-relaxed text-deep-muted">
|
|
{fila.uso}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<p className="mt-5 text-sm leading-relaxed text-deep-muted">
|
|
El formulario no pide datos de tarjeta, contraseñas ni información financiera. Si
|
|
necesitas compartir documentación sensible de tu operación, acuérdalo por correo
|
|
antes de enviarla.
|
|
</p>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="finalidades">
|
|
<h2
|
|
id="finalidades"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
3. Finalidades del tratamiento
|
|
</h2>
|
|
<div className="mt-4 space-y-4 text-sm leading-relaxed text-deep-muted">
|
|
<p>
|
|
Tus datos personales, incluso aquellos sensibles y patrimoniales o financieros,
|
|
serán utilizados para las siguientes finalidades: identificarte, localizarte,
|
|
contactarte, así como para cumplir las obligaciones derivadas de la relación
|
|
celebrada contigo, y cualquier otro fin compatible o análogo de los aquí
|
|
establecidos.
|
|
</p>
|
|
<p>
|
|
Esto incluye enviar, por cualquier medio físico, electrónico o magnético, de
|
|
manera enunciativa mas no limitativa: invitación a reuniones de trabajo, minutas,
|
|
noticias y anuncios en general relacionados con la institución, campañas
|
|
publicitarias afines e invitación a eventos.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="transferencias">
|
|
<h2
|
|
id="transferencias"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
4. Transferencias
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
Queda convenido que aceptas la transferencia que {EMPRESA.nombreCorto} pudiera
|
|
realizar a sus asociados y a terceros, en cumplimiento de las finalidades
|
|
anteriormente descritas.
|
|
</p>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="seguridad">
|
|
<h2
|
|
id="seguridad"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
5. Medidas de seguridad
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
Con el objeto de limitar el uso y divulgación de tus datos personales, se mantienen
|
|
medidas de seguridad administrativas, técnicas y físicas para evitar su daño,
|
|
pérdida, alteración, destrucción, uso, acceso o divulgación indebida.
|
|
</p>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="consentimiento">
|
|
<h2
|
|
id="consentimiento"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
6. Excepciones al consentimiento
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
{EMPRESA.nombreCorto} no requerirá el consentimiento del titular para tratar datos
|
|
personales cuando lo haga con fundamento en lo dispuesto por la Ley Federal de
|
|
Protección de Datos Personales en Posesión de los Particulares.
|
|
</p>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="arco">
|
|
<h2 id="arco" className="font-display text-xl font-extrabold text-deep sm:text-2xl">
|
|
7. Derechos de acceso, rectificación, cancelación y oposición
|
|
</h2>
|
|
<div className="mt-4 space-y-4 text-sm leading-relaxed text-deep-muted">
|
|
<p>
|
|
El ejercicio de los derechos de Acceso, Rectificación, Cancelación y Oposición
|
|
respecto de tus datos personales, así como la revocación del consentimiento para
|
|
su tratamiento, podrá realizarse a través de los procedimientos implementados o
|
|
solicitándolo al siguiente correo electrónico:
|
|
</p>
|
|
<p>
|
|
<a href={`mailto:${CORREO_ARCO}`} className="link break-all font-mono text-sm">
|
|
{CORREO_ARCO}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="cambios">
|
|
<h2 id="cambios" className="font-display text-xl font-extrabold text-deep sm:text-2xl">
|
|
8. Cambios al aviso
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
{EMPRESA.nombreCorto} podrá enmendar o modificar el presente aviso de privacidad
|
|
como lo estime conveniente, e informará y pondrá a tu disposición el aviso
|
|
actualizado en su sitio web.
|
|
</p>
|
|
</section>
|
|
|
|
<hr className="hair-lite" />
|
|
|
|
<section aria-labelledby="contacto-aviso">
|
|
<h2
|
|
id="contacto-aviso"
|
|
className="font-display text-xl font-extrabold text-deep sm:text-2xl"
|
|
>
|
|
9. Contacto
|
|
</h2>
|
|
<dl className="spec mt-5 sm:grid-cols-2">
|
|
<div className="spec-cell !bg-white">
|
|
<dt className="spec-key !text-deep-muted">Teléfono</dt>
|
|
<dd className="spec-val !text-deep">
|
|
<a href={`tel:${EMPRESA.telefonoTel}`} className="link">
|
|
{EMPRESA.telefono}
|
|
</a>
|
|
</dd>
|
|
</div>
|
|
<div className="spec-cell !bg-white">
|
|
<dt className="spec-key !text-deep-muted">Correo general</dt>
|
|
<dd className="spec-val !text-deep">
|
|
<a href={`mailto:${EMPRESA.correo}`} className="link break-all">
|
|
{EMPRESA.correo}
|
|
</a>
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<p className="mt-6 text-xs leading-relaxed text-deep-muted">
|
|
Si detectas una diferencia entre este texto y el aviso publicado por la empresa,
|
|
prevalece el de la empresa.{" "}
|
|
<Link href="/contacto" className="link">
|
|
Avísanos
|
|
</Link>{" "}
|
|
y lo corregimos.
|
|
</p>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
{/* ---------- salida ---------- */}
|
|
<section
|
|
className="border-t border-rule bg-ink py-14 sm:py-20"
|
|
aria-labelledby="dudas-titulo"
|
|
>
|
|
<div className="shell">
|
|
<div className="grid gap-10 lg:grid-cols-[1fr_1.05fr] lg:items-start lg:gap-16">
|
|
<div>
|
|
<p className="eyebrow mb-4">Si te quedó una duda</p>
|
|
<h2 id="dudas-titulo" className="font-display text-2xl font-extrabold sm:text-3xl">
|
|
Pregunta antes de enviar tus datos, no después
|
|
</h2>
|
|
<p className="mt-4 text-sm leading-relaxed text-muted sm:text-base">
|
|
Para ejercer tus derechos de acceso, rectificación, cancelación u oposición, el canal
|
|
es el correo del apartado 7:{" "}
|
|
<a href={`mailto:${CORREO_ARCO}`} className="link break-all font-mono text-xs">
|
|
{CORREO_ARCO}
|
|
</a>
|
|
. Este formulario es para lo demás: dudas sobre el sitio, sobre qué se registra o
|
|
sobre lo que necesitas proteger.
|
|
</p>
|
|
<p className="mt-4 text-sm leading-relaxed text-muted">
|
|
Y si lo que buscabas era información técnica,{" "}
|
|
<Link href="/cumplimiento-nom-002" className="link">
|
|
la página de cumplimiento
|
|
</Link>{" "}
|
|
es un mejor punto de partida.
|
|
</p>
|
|
</div>
|
|
|
|
<FormularioContacto
|
|
id="formulario-aviso"
|
|
canal="catalogo"
|
|
titulo="Escribirnos"
|
|
descripcion="Completa la frase del mensaje con tu duda. Aplica el mismo aviso de privacidad que acabas de leer."
|
|
mensajeInicial={MENSAJE_INICIAL}
|
|
textoBoton="Enviar mi duda"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</>
|
|
);
|
|
}
|