import { useMemo } from "react"; import { getFolderStats } from "../api"; import { formatCost, formatDurationMs, formatInteger, formatPercent } from "../data/formatters"; import { useResource } from "../data/useResource"; import { buildFolderRows, type FolderRowView } from "../data/view-models"; import type { TimeRange } from "../types"; import { AsyncBoundary, DataTable, Panel, StatusPill } from "../ui"; export interface ProjectsRouteProps { active: boolean; range: TimeRange; refreshTrigger: number; } export function ProjectsRoute({ active, range, refreshTrigger }: ProjectsRouteProps) { const { data: foldersData, error, loading, } = useResource(["projects", range, refreshTrigger], signal => getFolderStats(range, signal), { pollMs: 30000, enabled: active, }); const folderRows = useMemo(() => { if (!foldersData) return []; return buildFolderRows(foldersData); }, [foldersData]); const columns = useMemo( () => [ { key: "folder", header: "Project/Folder", render: (item: FolderRowView) => (
{item.folder || "(root)"}
), }, { key: "totalRequests", header: "Requests", numeric: true, render: (item: FolderRowView) => (
{formatInteger(item.totalRequests)}
), }, { key: "totalCost", header: "Cost", numeric: true, render: (item: FolderRowView) => (
{formatCost(item.totalCost)}
), }, { key: "totalTokens", header: "Tokens", numeric: true, render: (item: FolderRowView) => (
{formatInteger(item.totalInputTokens + item.totalOutputTokens)}
), }, { key: "cacheRate", header: "Cache Rate", numeric: true, render: (item: FolderRowView) => ( {formatPercent(item.cacheRate)} ), }, { key: "errorRate", header: "Error Rate", numeric: true, render: (item: FolderRowView) => ( 0.1 ? "danger" : item.errorRate > 0 ? "warning" : "success"}> {formatPercent(item.errorRate)} ), }, { key: "avgDuration", header: "Avg Duration", numeric: true, render: (item: FolderRowView) => formatDurationMs(item.avgDuration), }, ], [], ); const renderMobileCard = (item: FolderRowView) => (
{item.folder || "(root)"}
0.1 ? "danger" : item.errorRate > 0 ? "warning" : "success"}> {formatPercent(item.errorRate)} Err
Requests
{formatInteger(item.totalRequests)}
Cost
{formatCost(item.totalCost)}
Cache
{formatPercent(item.cacheRate)}
Duration
{formatDurationMs(item.avgDuration)}
); return (
item.folder} renderMobileCard={renderMobileCard} emptyText="No project folders recorded for this range." />
); }