Saltar al contenido principal

Feedback de usuarios

Recoge feedback de tema, valoración y comentario con el widget listo para usar o con hooks que no exigen proveedor.

Las opiniones de los usuarios valen más cuando no son anónimas.

El feedback de Fluxo puede usar el mismo contexto de visitante que SupportProvider. Cuando alguien deja una valoración, un tema o un comentario, el envío se puede asociar al visitante y, si está identificado, al contacto. Eso permite a tu equipo entender a quién se le atascó algo, ver el contexto del feedback y hacer seguimiento en una conversación, en lugar de leer la respuesta suelta de una encuesta.

Avisa a tus usuarios cuando una opinión quede asociada a su cuenta o a su historial de soporte. Recoge solo los metadatos que tu equipo necesite y aplica tus propios requisitos de consentimiento, retención y borrado.

Usa el widget <Feedback /> por defecto cuando quieras el camino más rápido. Usa useFeedbackForm cuando quieras el mismo motor de feedback dentro de tu propia interfaz con shadcn. Para pantallas de feedback totalmente propias, pasa un client, un visitorId y, opcionalmente, un contactId explícitos en lugar de envolver ese subárbol en SupportProvider.

Antes de empezar

Para el widget <Feedback /> listo para usar, completa primero el inicio rápido de React. Tu aplicación ya debería tener:

  • @fluxolat/react instalado
  • SupportProvider configurado con tu clave pública
  • un punto de entrada CSS de Fluxo importado en la raíz de tu aplicación
tssrc/main.tsx
import { SupportProvider } from "@fluxolat/react";
import type { ReactNode } from "react";
import "@fluxolat/react/styles.css";
 
export function AppRoot({ children }: { children: ReactNode }) {
  return <SupportProvider publicKey="pk_test_xxxx">{children}</SupportProvider>;
}

SupportProvider le da al widget por defecto su contexto de cliente, sitio web, visitante y contacto. Los hooks de feedback también pueden leer ese contexto, pero es un recurso de reserva: si pasas un client explícito, puedes usarlos fuera de SupportProvider.

El camino más rápido

Renderiza el widget por defecto cuando quieras un panel de feedback completo sin encargarte de la interfaz.

tssrc/App.tsx
import { Feedback } from "@fluxolat/react";
 
export function App() {
  return (
    <Feedback
      topics={["Bug", "Feature request", "UX", "Other"]}
      trigger="product_feedback"
    />
  );
}

El valor de trigger es tu etiqueta para explicar por qué se recogió este feedback. Usa nombres como product_feedback, churn, nps_survey o conversation_resolved.

Instala los ejemplos de shadcn

Los ejemplos de abajo usan Fluxo para el estado del feedback y componentes de shadcn para la interfaz.

Si todavía no tienes Fluxo instalado:

pnpm add @fluxolat/react

Los ejemplos sin proveedor que crean el cliente directamente también necesitan el paquete core:

pnpm add @fluxolat/core

Instala los componentes de shadcn que usan los ejemplos:

pnpm dlx shadcn@latest add button popover select textarea toggle-group

Después abre la pestaña Code en cualquiera de las vistas previas y copia el componente a tu aplicación. Los dos ejemplos importan useFeedbackForm desde @fluxolat/react/feedback, así que pueden enviar con el mismo contexto de visitante y de contacto que tu widget de soporte.

Feedback con emojis

Empieza con un popover compacto: tema, comentario, valoración y un botón pequeño de envío.

Feedback con estrellas

El mismo hook puede alimentar una interfaz de valoración de cinco estrellas.

Construye la tuya

Usa useFeedbackForm cuando quieras el control total de la interfaz pero no quieras rehacer el estado del formulario, la validación ni la fontanería del envío. Omite client para usar el SupportProvider más cercano, o pasa client, visitorId y, opcionalmente, contactId para un uso sin proveedor.

tscomponents/product-feedback.tsx
"use client";
 
import { useFeedbackForm } from "@fluxolat/react/hooks/use-feedback-form";
 
export function ProductFeedback() {
  const feedback = useFeedbackForm({
    topics: ["Bug", "Feature request", "UX", "Other"],
    trigger: "product_feedback",
    commentRequired: true,
  });
 
  return (
    <form aria-describedby="feedback-status" onSubmit={feedback.handleSubmit}>
      <label htmlFor="feedback-topic">Topic</label>
      <select
        aria-describedby={
          feedback.fields.topic.isMissing ? "feedback-topic-error" : undefined
        }
        aria-invalid={feedback.fields.topic.isMissing}
        id="feedback-topic"
        onBlur={feedback.fields.topic.handleBlur}
        onChange={(event) => feedback.handleTopicChange(event.target.value)}
        value={feedback.topic}
      >
        <option value="">Select topic</option>
        {feedback.availableTopics.map((topic) => (
          <option key={topic} value={topic}>
            {topic}
          </option>
        ))}
      </select>
      {feedback.fields.topic.isMissing ? (
        <p id="feedback-topic-error" role="alert">
          Choose a topic.
        </p>
      ) : null}
 
      <label htmlFor="feedback-comment">Comment</label>
      <textarea
        aria-describedby={
          feedback.fields.comment.isMissing
            ? "feedback-comment-error"
            : undefined
        }
        aria-invalid={feedback.fields.comment.isMissing}
        id="feedback-comment"
        onBlur={feedback.fields.comment.handleBlur}
        onChange={(event) => feedback.handleCommentChange(event.target.value)}
        value={feedback.comment}
      />
      {feedback.fields.comment.isMissing ? (
        <p id="feedback-comment-error" role="alert">
          Add a comment.
        </p>
      ) : null}
 
      <fieldset>
        <legend>Rating</legend>
        {[1, 2, 3, 4, 5].map((rating) => (
          <button
            aria-pressed={feedback.rating === rating}
            key={rating}
            onClick={() => feedback.handleRatingSelect(rating)}
            type="button"
          >
            {rating}
          </button>
        ))}
      </fieldset>
 
      <button disabled={feedback.submit.disabled} type="submit">
        {feedback.submit.label}
      </button>
 
      <p aria-live="polite" id="feedback-status">
        {feedback.submitted
          ? "Feedback sent."
          : feedback.error?.message ?? ""}
      </p>
    </form>
  );
}

useFeedbackForm gestiona las piezas móviles que necesita cualquier interfaz de feedback:

  • el estado de apertura con open, setOpen y handleOpenChange
  • el estado de la valoración con rating, hoveredRating, handleRatingSelect y handleRatingHoverChange
  • el estado del tema y del comentario con valores normalizados
  • el estado de validación de la valoración, los temas y los comentarios obligatorios
  • los estados pendiente, de error y enviado
  • las acciones de éxito con sendAnother y done

Las opciones más habituales son:

  • client: un FluxoClient explícito para feedback sin proveedor
  • topics: las categorías de feedback que se pueden elegir
  • defaultTopic: un tema preseleccionado de topics
  • trigger: el motivo por el que apareció este formulario de feedback
  • source: de dónde vino este feedback; por defecto, widget
  • conversationId: asocia el feedback a una conversación de soporte concreta
  • visitorId: obligatorio cuando ningún contexto de proveedor aporta un visitante
  • contactId: asocia el feedback a un contacto identificado
  • commentRequired: exige un comentario escrito antes de enviar
  • defaultOpen: abre el popover o el diálogo desde el principio
  • onSuccess: ejecuta código después de que Fluxo guarde el feedback
  • onError: gestiona un envío fallido

Formulario de feedback sin proveedor

tscomponents/provider-free-feedback.tsx
"use client";
 
import { FluxoClient } from "@fluxolat/core";
import { useFeedbackForm } from "@fluxolat/react/hooks/use-feedback-form";
 
const client = new FluxoClient({ publicKey: "pk_test_xxxx" });
 
export function ProviderFreeFeedback({ visitorId }: { visitorId: string }) {
  const feedback = useFeedbackForm({
    client,
    visitorId,
    source: "headless",
    topics: ["Bug", "Feature request", "UX", "Other"],
  });
 
  return (
    <form onSubmit={feedback.handleSubmit}>
      <button onClick={() => feedback.handleRatingSelect(5)} type="button">
        Great
      </button>
      <button disabled={feedback.submit.disabled} type="submit">
        {feedback.submit.label}
      </button>
    </form>
  );
}

API de envío de más bajo nivel

Usa useSubmitFeedback cuando ya controlas todo el formulario y solo necesitas la escritura. Sigue la misma regla de proveedor opcional: omite client para usar SupportProvider, o pasa entradas explícitas de runtime para un formulario headless.

tscomponents/custom-feedback-submit.tsx
"use client";
 
import { useSubmitFeedback } from "@fluxolat/react/hooks/use-submit-feedback";
 
export function CustomFeedbackSubmit() {
  const feedback = useSubmitFeedback({});
 
  async function submit() {
    await feedback.mutateAsync({
      rating: 5,
      topic: "UX",
      comment: "The new onboarding screen is much clearer.",
      trigger: "onboarding_feedback",
    });
  }
 
  return (
    <button disabled={feedback.isPending} onClick={submit} type="button">
      Send feedback
    </button>
  );
}

Si omites client, useSubmitFeedback lee el cliente de Fluxo y el contexto de visitante, sitio web y contacto desde SupportProvider. En la mayoría de aplicaciones con widget, pasa rating, topic, comment, trigger y, opcionalmente, conversationId. El hook rellena visitorId y contactId a partir del contexto.

Para formularios sin proveedor, pasa el cliente y el visitante explícitamente:

tscomponents/headless-feedback-submit.tsx
"use client";
 
import { FluxoClient } from "@fluxolat/core";
import { useSubmitFeedback } from "@fluxolat/react/hooks/use-submit-feedback";
 
const client = new FluxoClient({ publicKey: "pk_test_xxxx" });
 
export function HeadlessFeedbackSubmit({ visitorId }: { visitorId: string }) {
  const feedback = useSubmitFeedback({ client });
 
  return (
    <button
      disabled={feedback.isPending}
      onClick={() =>
        feedback.mutate({
          rating: 5,
          source: "headless",
          visitorId,
        })
      }
      type="button"
    >
      Send feedback
    </button>
  );
}

Pasa visitorId o contactId a mano solo si estás construyendo una integración de más bajo nivel y sabes que necesitas sobrescribir el contexto.

Datos del feedback

Cada envío guarda:

  • rating: obligatorio, del 1 al 5
  • topic: categoría estructurada opcional
  • comment: comentario escrito opcional
  • trigger: etiqueta opcional de lo que provocó el formulario
  • source: por defecto, widget
  • conversationId: enlace opcional a una conversación
  • visitorId: el visitante que dejó el feedback
  • contactId: el contacto identificado, cuando el visitante tiene uno

Esa asociación es lo importante. Una valoración baja de un cliente con sesión iniciada puede convertirse en un seguimiento de soporte real, no solo en un número en un panel.

Comprobar y resolver problemas

Después de enviar, espera al estado de envío del formulario y confirma que el feedback aparece en el panel con el contexto de visitante y contacto esperado. Un 401 suele significar que falta la clave pública o que no es válida. Un 403 suele significar que el nombre de host actual no está permitido. Si un formulario sin proveedor no tiene visitante, pasa un visitorId válido; si no, deja el formulario dentro de SupportProvider.

Exportaciones de los hooks

useFeedbackForm y useSubmitFeedback se exportan desde @fluxolat/react/hooks, @fluxolat/react/feedback e importaciones profundas explícitas como @fluxolat/react/hooks/use-feedback-form. Las aplicaciones de Next.js pueden usar las exportaciones equivalentes de @fluxolat/next/hooks y @fluxolat/next/feedback.

Los hooks de proveedor opcional tratan client con cuidado: omitirlo significa «lee el contexto del proveedor», mientras que pasar client: null desactiva a propósito el recurso al proveedor.

Referencia de tipos

Props de Feedback

Prop

Tipo

Opciones de useFeedbackForm

Prop

Tipo

Resultado de useFeedbackForm

Nombre

Tipo

Variables de envío de feedback

Parámetro

Tipo

¿Te resultó útil esta página?

Abre una incidencia de documentación ya rellenada para que el equipo pueda actuar.