"use client";
import type {
DatasetDisplayInfo,
EpisodeLengthStats,
CameraInfo,
} from "@/app/[org]/[dataset]/[episode]/fetch-data";
interface StatsPanelProps {
datasetInfo: DatasetDisplayInfo;
episodeLengthStats: EpisodeLengthStats | null;
loading: boolean;
}
function formatTotalTime(totalFrames: number, fps: number): string {
const totalSec = totalFrames / fps;
const hours = Math.floor(totalSec / 3600);
const minutes = Math.floor((totalSec % 3600) / 60);
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
}
/** SVG bar chart for the episode-length histogram */
function EpisodeLengthHistogram({
data,
}: {
data: { binLabel: string; count: number }[];
}) {
if (data.length === 0) return null;
const maxCount = Math.max(...data.map((d) => d.count));
if (maxCount === 0) return null;
const totalWidth = 560;
const gap = Math.max(1, Math.min(3, Math.floor(60 / data.length)));
const barWidth = Math.max(
4,
Math.floor((totalWidth - gap * data.length) / data.length),
);
const chartHeight = 150;
const labelHeight = 30;
const topPad = 16;
const svgWidth = data.length * (barWidth + gap);
const labelStep = Math.max(1, Math.ceil(data.length / 10));
return (
);
}
function Card({ label, value }: { label: string; value: string | number }) {
return (
);
}
function StatsPanel({
datasetInfo,
episodeLengthStats,
loading,
}: StatsPanelProps) {
const els = episodeLengthStats;
return (
Dataset Statistics:{" "}
{datasetInfo.repoId}
{/* Overview cards */}
{/* Camera resolutions */}
{datasetInfo.cameras.length > 0 && (
Camera Resolutions
{datasetInfo.cameras.map((cam: CameraInfo) => (
{cam.name}
{cam.width}×{cam.height}
))}
)}
{/* Loading spinner for async stats */}
{loading && (
Computing episode statistics…
)}
{/* Episode length section */}
{els && (
<>
{els.episodeLengthHistogram.length > 0 && (
Episode Length Distribution
{els.episodeLengthHistogram.length} bin
{els.episodeLengthHistogram.length !== 1 ? "s" : ""}
)}
>
)}
);
}
export default StatsPanel;