Pular para o conteúdo principal

Feedback de usuários

Colete feedback de tema, avaliação e comentário com o widget pronto para usar ou com hooks que não exigem provider.

O feedback dos usuários vale mais quando não é anônimo.

O feedback do Fluxo pode usar o mesmo contexto de visitante do SupportProvider. Quando alguém deixa uma avaliação, um tema ou um comentário, o envio pode ser associado ao visitante e, se ele estiver identificado, ao contato. Isso permite que o seu time entenda quem ficou travado, veja o contexto do feedback e faça o acompanhamento em uma conversa, em vez de ler a resposta solta de uma pesquisa.

Avise os seus usuários quando um feedback ficar associado à conta ou ao histórico de suporte deles. Colete apenas os metadados de que o seu time precisa e aplique os seus próprios requisitos de consentimento, retenção e exclusão.

Use o widget <Feedback /> padrão quando quiser o caminho mais rápido. Use useFeedbackForm quando quiser o mesmo motor de feedback dentro da sua própria interface com shadcn. Para telas de feedback totalmente próprias, passe um client, um visitorId e, opcionalmente, um contactId explícitos em vez de envolver essa subárvore em SupportProvider.

Antes de começar

Para o widget <Feedback /> pronto para usar, conclua primeiro o início rápido do React. O seu app já deve ter:

  • @fluxolat/react instalado
  • SupportProvider configurado com a sua chave pública
  • um ponto de entrada CSS do Fluxo importado na raiz do seu app
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 dá ao widget padrão o seu contexto de cliente, site, visitante e contato. Os hooks de feedback também conseguem ler esse contexto, mas ele é um recurso de reserva: se você passar um client explícito, pode usá-los fora de SupportProvider.

O caminho mais rápido

Renderize o widget padrão quando quiser um painel de feedback completo sem precisar cuidar da interface.

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

O valor de trigger é a sua etiqueta para explicar por que esse feedback foi coletado. Use nomes como product_feedback, churn, nps_survey ou conversation_resolved.

Instale os exemplos do shadcn

Os exemplos abaixo usam o Fluxo para o estado do feedback e componentes do shadcn para a interface.

Se você ainda não instalou o Fluxo:

pnpm add @fluxolat/react

Os exemplos sem provider que criam o cliente diretamente também precisam do pacote core:

pnpm add @fluxolat/core

Instale os componentes do shadcn usados pelos exemplos:

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

Depois abra a aba Code em qualquer uma das prévias e copie o componente para o seu app. Os dois exemplos importam useFeedbackForm de @fluxolat/react/feedback, então conseguem enviar com o mesmo contexto de visitante e de contato do seu widget de suporte.

Feedback com emojis

Comece com um popover compacto: tema, comentário, avaliação e um botão pequeno de envio.

Feedback com estrelas

O mesmo hook pode alimentar uma interface de avaliação de cinco estrelas.

Construa a sua

Use useFeedbackForm quando quiser controle total da interface mas não quiser refazer o estado do formulário, a validação nem o encanamento do envio. Omita client para usar o SupportProvider mais próximo, ou passe client, visitorId e, opcionalmente, contactId para um uso sem provider.

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 gerencia as peças móveis de que qualquer interface de feedback precisa:

  • o estado de abertura com open, setOpen e handleOpenChange
  • o estado da avaliação com rating, hoveredRating, handleRatingSelect e handleRatingHoverChange
  • o estado do tema e do comentário com valores normalizados
  • o estado de validação da avaliação, dos temas e dos comentários obrigatórios
  • os estados pendente, de erro e enviado
  • as ações de sucesso com sendAnother e done

As opções mais comuns são:

  • client: um FluxoClient explícito para feedback sem provider
  • topics: as categorias de feedback que podem ser escolhidas
  • defaultTopic: um tema pré-selecionado de topics
  • trigger: o motivo pelo qual esse formulário de feedback apareceu
  • source: de onde veio esse feedback; por padrão, widget
  • conversationId: associa o feedback a uma conversa de suporte específica
  • visitorId: obrigatório quando nenhum contexto de provider fornece um visitante
  • contactId: associa o feedback a um contato identificado
  • commentRequired: exige um comentário escrito antes de enviar
  • defaultOpen: já abre o popover ou o diálogo de início
  • onSuccess: executa código depois que o Fluxo guardar o feedback
  • onError: trata um envio que falhou

Formulário de feedback sem provider

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 envio de mais baixo nível

Use useSubmitFeedback quando você já controla o formulário inteiro e só precisa da escrita. Ele segue a mesma regra de provider opcional: omita client para usar o SupportProvider, ou passe entradas explícitas de runtime para um formulário 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>
  );
}

Se você omitir client, useSubmitFeedback lê o cliente do Fluxo e o contexto de visitante, site e contato do SupportProvider. Na maioria dos apps com widget, passe rating, topic, comment, trigger e, opcionalmente, conversationId. O hook preenche visitorId e contactId a partir do contexto.

Para formulários sem provider, passe o cliente e o visitante explicitamente:

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>
  );
}

Passe visitorId ou contactId na mão só se você estiver construindo uma integração de mais baixo nível e souber que precisa sobrescrever o contexto.

Dados do feedback

Cada envio guarda:

  • rating: obrigatório, de 1 a 5
  • topic: categoria estruturada opcional
  • comment: comentário escrito opcional
  • trigger: etiqueta opcional do que provocou o formulário
  • source: por padrão, widget
  • conversationId: link opcional para uma conversa
  • visitorId: o visitante que deixou o feedback
  • contactId: o contato identificado, quando o visitante tem um

Essa associação é o que importa. Uma avaliação baixa de um cliente logado pode virar um acompanhamento de suporte real, não só um número em um painel.

Verificar e solucionar problemas

Depois de enviar, aguarde o estado de envio do formulário e confirme que o feedback aparece no painel com o contexto de visitante e contato esperado. Um 401 normalmente significa que a chave pública está faltando ou é inválida. Um 403 normalmente significa que o hostname atual não é permitido. Se um formulário sem provider não tiver visitante, passe um visitorId válido; caso contrário, mantenha o formulário dentro do SupportProvider.

Exportações dos hooks

useFeedbackForm e useSubmitFeedback são exportados de @fluxolat/react/hooks, @fluxolat/react/feedback e de importações profundas explícitas como @fluxolat/react/hooks/use-feedback-form. Apps Next.js podem usar as exportações equivalentes de @fluxolat/next/hooks e @fluxolat/next/feedback.

Os hooks de provider opcional tratam client com cuidado: omiti-lo significa "leia o contexto do provider", enquanto passar client: null desativa de propósito o recurso ao provider.

Referência de tipos

Props do Feedback

Prop

Tipo

Opções de useFeedbackForm

Prop

Tipo

Resultado de useFeedbackForm

Nome

Tipo

Variáveis de envio de feedback

Parâmetro

Tipo

Esta página foi útil?

Abra uma issue de documentação já preenchida para que a equipe possa agir.