import { useT } from "@agent-native/core/client/i18n"; import type { ResponseInsightsWidgetResult } from "@shared/types"; import { IconArrowLeft, IconChartBar, IconExternalLink, IconRefresh, } from "@tabler/icons-react"; import { Link, useSearchParams } from "react-router"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { useResponseInsights } from "@/hooks/use-responses"; import { cn } from "@/lib/utils"; function formatNumber(value: number): string { return new Intl.NumberFormat().format(value); } function formatDay(value: string): string { const date = new Date(`${value}T00:00:00Z`); if (Number.isNaN(date.getTime())) return value; return new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", timeZone: "UTC", }).format(date); } function formatCell(value: string | number | boolean | null | undefined) { if (value === null || value === undefined || value === "") return "-"; if (typeof value === "boolean") return value ? "Yes" : "No"; return String(value); } function Metric({ label, value, detail, }: { label: string; value: string; detail?: string; }) { return (
{label}
{value}
{detail ? (
{detail}
) : null}
); } function SubmissionBars({ data, }: { data: Array<{ date: string; submissions: number }>; }) { const max = Math.max(1, ...data.map((point) => point.submissions)); const visibleTicks = Math.max(1, Math.ceil(data.length / 6)); return (
{data.map((point, index) => { const height = Math.max( point.submissions > 0 ? 10 : 3, Math.round((point.submissions / max) * 148), ); const showLabel = index === 0 || index === data.length - 1 || index % visibleTicks === 0; return (
{showLabel ? formatDay(point.date) : ""}
); })}
); } function LoadingState() { return (
{Array.from({ length: 4 }).map((_, index) => ( ))}
); } export function ResponseInsightsPage() { const t = useT(); const [params] = useSearchParams(); const formId = params.get("formId") ?? undefined; const { data, isLoading, error, refetch } = useResponseInsights(formId); const insights = data as ResponseInsightsWidgetResult | undefined; if (isLoading) return ; if ( error || !insights || insights.widget !== "data-insights" || insights.widgetId !== "forms.responseInsights.v1" ) { return (

{t("responseInsights.unavailable")}

{t("responseInsights.unavailableDescription")}

); } const { summary, chartSeries, table, display } = insights; return (

{display.title}

{formatDay(summary.rangeStart)} - {formatDay(summary.rangeEnd)} {summary.truncated || summary.scopeCapped ? ( Sampled ) : null}

{chartSeries.title}

{chartSeries.sampled ? "Calculated from the recent response sample" : "Calculated from visible response data"}

{table.title}

{formatNumber(table.rows.length)} shown {table.truncated ? ` of ${formatNumber(table.totalRows)} total` : ""}

{table.rows.length === 0 ? (
{t("responseInsights.noResponsesYet")}
) : (
{table.columns.map((column) => ( {column.label} ))} {table.rows.map((row) => ( {table.columns.map((column) => ( {formatCell(row[column.key])} ))} ))}
)}
); }