Skip to content

Screens

Data Answer

An analytics question answered with KPI cards, a chart and a table, plus follow-up questions to dig further.

2 variantsMIT

Preview

Full screen

Onboarding activation

files · / commands

Installation

Copies the screen into components/ and installs every component it uses, with their dependencies and theme tokens.

pnpm dlx shadcn@latest add https://tuffui.com/r/data-answer.json

Usage

Pass your own data and wire the callbacks to your agent runtime.

import { DataAnswer } from "@/components/data-answer";
import { ChartCard } from "@/components/ui/chart-card";
import { DataTable } from "@/components/ui/data-table";
import { StatGrid } from "@/components/ui/stat-grid";
import { ToolCallBlock } from "@/components/ui/tool-call-block";

export function AnalyticsScreen({ workspace, chat }: { workspace: Workspace; chat: Chat }) {
  return (
    <DataAnswer
      data={{
        title: chat.title,
        model: chat.model,
        user: { name: workspace.user.name },
        sources: workspace.sources, // { id, name, detail, status: "synced" | "syncing" | "stale" }
        saved: workspace.savedQuestions,
        followUps: chat.done ? chat.followUps : undefined,
        // An answer is any React node — compose it from the data components.
        messages: chat.turns.map((turn) => ({
          id: turn.id,
          role: turn.role,
          at: turn.createdAt,
          content:
            turn.role === "user" ? (
              turn.text
            ) : (
              <div className="flex flex-col gap-3">
                <ToolCallBlock data={turn.query} />
                <p>{turn.text}</p>
                {turn.stats && <StatGrid data={turn.stats} sparklines />}
                {turn.chart && <ChartCard {...turn.chart} />}
                {turn.table && <DataTable {...turn.table} />}
              </div>
            ),
        })),
      }}
      status={chat.streaming ? "streaming" : "idle"}
      onSend={(text) => chat.ask(text)}
      onStop={() => chat.stop()}
    />
  );
}

Props

PropTypeDefaultDescription
data*DataAnswerData—Title, model, data sources and saved questions for the sidebar, the thread's messages, follow-up chips and the user.
status"idle" | "streaming""idle"While streaming the composer queues messages and follow-ups are hidden.
onSend(text: string) => void—A message from the composer, a follow-up chip or a saved question.
onStop() => void—Stop the running answer.
sidebarOpenboolean—Controls the sidebar on wide screens (from 56rem). Below that the toggle opens it as a drawer.
onSidebarOpenChange(open: boolean) => void—Called when the sidebar toggle is used on a wide screen.
classNamestring—Extra classes for the root.
type DataAnswerData = {
  title: string;
  model: string;
  sources: { id: string; name: string; detail?: string; status: "synced" | "syncing" | "stale" }[];
  saved: { id: string; title: string; prompt?: string }[];
  messages: ThreadMessage[]; // content: any ReactNode
  followUps?: PromptSuggestion[];
  user: { name: string; avatar?: string };
};