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
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.jsonUsage
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
| Prop | Type | Default | Description |
|---|---|---|---|
| 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. |
| sidebarOpen | boolean | — | 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. |
| className | string | — | 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 };
};