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