Formulario de feedback para un sitio web o una app

Formulario de feedback para un sitio web o una app6 min de lectura

Añade un formulario de reporte de bugs que capture la página, el navegador y los pasos

Un formulario de reporte de bugs que pide a la gente los pasos y la gravedad, y deja que la página añada por su cuenta la URL, el navegador, el viewport y los errores recientes.

Preet SutharFundador, Ikiform

Pide a quien reporta solo lo que sabe: qué falló, qué hizo antes y cuánto le bloquea. Deja que la página rellene el resto al enviar: la URL de la página, el user agent del navegador, el viewport y los últimos errores de JavaScript. Envíalo a un backend de formularios y reenvía cada reporte a tu gestor de incidencias por webhook.

Qué tiene de distinto un reporte de bugs

Un reporte de bugs solo sirve si puedes reproducirlo, y quien lo reporta rara vez conoce los detalles que necesitas. Pregunta a alguien qué navegador usa y te dirá "Chrome" o "el normal". Pide la URL y te dará el nombre de la página. Así que divide el formulario en dos: preguntas que solo quien reporta puede responder, y contexto que la página ya conoce.

Qué partes de un reporte de bugs escribe la persona y cuáles añade la páginalo escribe quien reportala página lo añade al enviarresumenuna línea, de 8a 140caracterespasoslo que hizojusto antesgravedadbloqueante,grave o leveenlace acapturaopcional, unaURL, no unarchivopage_urlorigen y ruta,sin queryuser_agentnavegador y SOviewporttamaño y pixelratiorecent_errorslos últimoscinco, sinhostsQué partes de un reporte de bugs escribe la persona y cuáles añade la páginalo escribe quien reportala página lo añade al enviarresumenuna línea, de 8 a 140 caracterespasoslo que hizo justo antesgravedadbloqueante, grave o leveenlace a capturaopcional, una URL, no un archivopage_urlorigen y ruta, sin queryuser_agentnavegador y SOviewporttamaño y pixel ratiorecent_errorslos últimos cinco, sin hosts
  1. Quien reporta escribe un resumen de una línea.
  2. Describe lo que hizo justo antes de que fallara.
  3. Elige cuánto le bloquea.
  4. Puede pegar un enlace a una captura o una grabación.
  5. Al enviar, la página añade su propia dirección sin la query string.
  6. Añade el user agent del navegador.
  7. Añade el tamaño de la ventana y el pixel ratio.
  8. Añade los últimos errores de JavaScript que vio, sin los nombres de host.
La fila de arriba es todo lo que ve quien reporta. La de abajo son inputs ocultos, que se rellenan en cuanto pulsa Enviar.

Tres restricciones dan forma al código:

  • El referrer no es la página. Por defecto, los navegadores solo envían tu origen en un envío entre sitios, así que meta.referrer no te dirá la ruta. Envía page_url tú mismo.
  • Un envío desde el servidor oculta el navegador. Ikiform registra el user agent de quien hace la petición. Desde una server action, ese es tu servidor. Reenvía la cabecera user-agent del visitante como un campo.
  • Las trazas de pila están llenas de enlaces. Cada frame tiene la URL de un script, y tres o más enlaces suman 2 a la puntuación de spam. Quita los hosts antes de enviar los errores, o un reporte con un enlace a una captura puede acabar en spam.

Los campos

Vista previa

Ejecuta el validador de Ikiform en tu navegador. No se envía nada.

¿Cuánto te bloquea?
Los cinco últimos campos son inputs ocultos en el formulario real. Al definirlos, Ikiform comprueba su longitud y tipo como con cualquier otro campo.

La gravedad se formula como lo que le provoca a quien reporta, no como una etiqueta de prioridad. La gente elige "No puedo continuar" con precisión; en cambio, elige "Crítico" para todo. Los pasos tienen un mínimo de 20 caracteres, lo que convierte "está roto" en una frase con la que puedes trabajar.

Captura el contexto

El navegador rellena los campos ocultos en el handler de submit, cuando la persona ya ha terminado de escribir, así que la lista de errores incluye cualquier cosa que fallara mientras escribía el reporte.

El formulario se envía de la forma normal; el script solo rellena los inputs ocultos durante el submit, antes de que el navegador construya la petición. Pon data-app-version en el elemento <html> en tiempo de build. El navegador envía directamente, así que el meta.user_agent propio de Ikiform coincide con el campo user_agent.

report-bug.html
<form id="bug" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="summary">What went wrong?</label>
  <input id="summary" name="summary" required minlength="8" maxlength="140" />
  <label for="steps">What did you do right before?</label>
  <textarea id="steps" name="steps" rows="5" required minlength="20"></textarea>
  <label for="expected">What did you expect to happen?</label>
  <textarea id="expected" name="expected" rows="3"></textarea>
  <fieldset>
    <legend>How much does it block you?</legend>
    <label><input type="radio" name="severity" value="blocker" required /> I can't continue</label>
    <label><input type="radio" name="severity" value="major" /> I found a workaround</label>
    <label><input type="radio" name="severity" value="minor" /> It's cosmetic</label>
  </fieldset>
  <label for="screenshot_url">Link to a screenshot or recording</label>
  <input id="screenshot_url" name="screenshot_url" type="url" />
  <label for="email">Email for follow-up questions</label>
  <input id="email" name="email" type="email" />
  <input type="hidden" name="page_url" />
  <input type="hidden" name="user_agent" />
  <input type="hidden" name="viewport" />
  <input type="hidden" name="app_version" />
  <input type="hidden" name="recent_errors" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send report</button>
</form>

<script>
  const recent = [];
  function remember(text) {
    recent.push(String(text).replace(/https?:\/\/[^/\s]+/g, "").slice(0, 400));
    if (recent.length > 5) recent.shift();
  }
  window.addEventListener("error", (event) => remember(`${event.message} at ${event.filename}:${event.lineno}`));
  window.addEventListener("unhandledrejection", (event) => remember(`Unhandled: ${event.reason?.stack ?? event.reason}`));

  document.getElementById("bug").addEventListener("submit", (event) => {
    const fields = event.currentTarget.elements;
    fields.page_url.value = location.origin + location.pathname;
    fields.user_agent.value = navigator.userAgent;
    fields.viewport.value = `${innerWidth}x${innerHeight} @${devicePixelRatio}x`;
    fields.app_version.value = document.documentElement.dataset.appVersion ?? "";
    fields.recent_errors.value = recent.join("\n---\n");
  });
</script>
Las versiones de servidor envían con una clave secreta, así que Ikiform se salta Allowed domains (dominios permitidos) y su verificación humana. Todos los envíos vienen entonces de la IP de tu servidor, y el límite Standard de 5 por minuto por visitante cuenta a todos tus visitantes juntos. Elige el límite de frecuencia Relaxed o Custom para un formulario que envías a través de un servidor, y guarda IKIFORM_SECRET_KEY en variables de entorno solo de servidor.

El mensaje de éxito muestra el id del envío. Cuando quien reportó escriba más tarde a soporte, ese id encuentra el reporte en una sola búsqueda. La plantilla de reporte de bugs tiene una versión más corta sin el contexto capturado.

A tu gestor de incidencias

Los reportes deben estar donde se hará el seguimiento del arreglo. Añade un webhook que apunte a un relay que cree una incidencia: summary como título, los pasos y el contexto como cuerpo, severity como etiqueta. La guía de Linear y GitHub Issues tiene ese relay, con comprobación de firmas y reintentos. La documentación de webhooks lista cada valor de meta que lleva el payload. Pon los campos capturados en un bloque plegado al final del cuerpo de la incidencia para que los pasos se lean bien.

CampoEn la incidencia
summaryTítulo
steps, expectedCuerpo, al principio
severityUna etiqueta, para poder filtrar los bloqueantes
page_url, user_agent, viewport, app_versionUn bloque de detalles bajo los pasos
recent_errorsUn bloque de código, al final

Comprueba que funciona

  1. Abre la página en un navegador de escritorio, ejecuta undefinedFunction() en la consola y luego envía un reporte.
  2. Abre el envío en el panel. recent_errors contiene el ReferenceError sin ningún host https://.
  3. user_agent muestra tu navegador, no node, también en las versiones de servidor.
  4. page_url tiene la ruta pero no la query string.
  5. Envía un reporte con un steps de 10 caracteres. Ikiform responde 422 nombrando steps, y el formulario muestra el mensaje.

Errores comunes

  • La subida de archivos aún no se admite. Un envío con un archivo recibe 422 files_not_supported. Pide un enlace a una captura, o sube antes la imagen a tu propio almacenamiento y envía su URL.
  • Cada valor puede ocupar hasta 16 KB. Cinco errores recortados a 400 caracteres quedan muy por debajo; un volcado completo de la consola no.
  • Los navegadores Chromium congelan partes del user agent, como la versión de macOS, que siempre aparece como 10_15_7. Al hacer el triaje, usa el nombre del navegador y su versión principal, no la versión del sistema operativo.
  • Los errores de scripts de otro origen solo muestran Script error. salvo que ese script se sirva con cabeceras CORS y se cargue con crossorigin.
  • No captures en el reporte el contenido de formularios, el local storage ni las cookies. Pueden contener contraseñas y tokens, y los guardarías en texto plano.

Solución de problemas

ProblemaQué hacer
422 validation_failedEl envío no coincide con la definición de campos del formulario. error.fields indica cada campo y el motivo. Haz que el name del input coincida con el id del campo.
422 files_not_supportedEl formulario tiene un <input type="file"> con un archivo elegido. La subida de archivos aún no está disponible; quita el input o pide un enlace.
Los reportes con enlaces a capturas acaban en spamCuenta los enlaces de todos los campos. Tres o más suman a la puntuación; con una repetición del mismo reporte se alcanza el umbral. Quita los hosts de los errores y marca el reporte como no spam.
403 origin_not_allowed, o todos los envíos acaban en spamEl dominio de la página no está en Allowed domains del formulario. Añádelo, y localhost para las pruebas locales. Los subdominios de un dominio de la lista están permitidos.

Preguntas

¿Te atascaste en un paso o quieres una guía para tu caso? Escribe a hello@ikiform.com.