diff --git a/ui/web/src/components/shared/error-boundary.tsx b/ui/web/src/components/shared/error-boundary.tsx new file mode 100644 index 00000000..7b6b7946 --- /dev/null +++ b/ui/web/src/components/shared/error-boundary.tsx @@ -0,0 +1,45 @@ +import { Component, type ErrorInfo, type ReactNode } from "react"; +import { AlertTriangle, RefreshCw } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; +} + +export class ErrorBoundary extends Component { + state: State = { hasError: false }; + + static getDerivedStateFromError(): State { + return { hasError: true }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + console.error("[ErrorBoundary]", error, info.componentStack); + } + + private handleRetry = () => { + this.setState({ hasError: false }); + }; + + render() { + if (!this.state.hasError) return this.props.children; + + if (this.props.fallback) return this.props.fallback; + + return ( +
+ +

Something went wrong rendering this section.

+ +
+ ); + } +} diff --git a/ui/web/src/pages/usage/components/token-area-chart.tsx b/ui/web/src/pages/usage/components/token-area-chart.tsx index 446fdc8d..3483df2d 100644 --- a/ui/web/src/pages/usage/components/token-area-chart.tsx +++ b/ui/web/src/pages/usage/components/token-area-chart.tsx @@ -38,7 +38,7 @@ export function TokenAreaChart({ data, loading, granularity }: TokenAreaChartPro emptyText={t("analytics.noData")} > - + diff --git a/ui/web/src/pages/usage/usage-page.tsx b/ui/web/src/pages/usage/usage-page.tsx index 6105da5a..079ca890 100644 --- a/ui/web/src/pages/usage/usage-page.tsx +++ b/ui/web/src/pages/usage/usage-page.tsx @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge"; import { PageHeader } from "@/components/shared/page-header"; import { EmptyState } from "@/components/shared/empty-state"; import { Pagination } from "@/components/shared/pagination"; +import { ErrorBoundary } from "@/components/shared/error-boundary"; import { formatTokens, formatCost } from "@/lib/format"; import { useAgents } from "@/pages/agents/hooks/use-agents"; import { useUsage } from "./hooks/use-usage"; @@ -173,7 +174,9 @@ function AnalyticsDashboard() { export function UsagePage() { return ( - + + + ); }