diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index dc32c51..8f9efc6 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,3 +1,5 @@ +"use client" + import { Send, PiggyBank, FileText, Shield } from "lucide-react"; import CurrentMoneySplitWidget from '@/components/CurrentMoneySplitWidget' import GoalProgress from "@/components/Dashboard/GoalProgress"; @@ -9,69 +11,89 @@ import TransactionHistoryItem, { Transaction } from "@/components/Dashboard/Tran import MoneyDistributionWidget from "@/components/Dashboard/MoneyDistributionWidget"; import RecentTransactionsWidget from "@/components/Dashboard/RecentTransactionsWidget"; import QuickActions from "@/components/Dashboard/QuickActions"; +import WidgetErrorBoundary from '@/components/ui/WidgetErrorBoundary' export default function Dashboard() { return (
{/* Stats Overview */}
- } - trend="up" - /> - } - trend="up" - /> - } - trend="none" - /> - } - trend="none" - /> + + } + trend="up" + /> + + + } + trend="up" + /> + + + } + trend="none" + /> + + + } + trend="none" + /> +
{/* Quick Actions Panel */}
- + + +
{/* Money Split Visualization */}
- + + +
- + + +
{/* Recent Transactions */}
- + + +
{/* Savings Goals Progress */} - + + + {/* Bills by Type */} -
+ +
@@ -106,7 +128,8 @@ export default function Dashboard() { gradient={{ from: "#5F1515", to: "#4F1111" }} />
-
+
+
); diff --git a/components/ui/WidgetErrorBoundary.tsx b/components/ui/WidgetErrorBoundary.tsx new file mode 100644 index 0000000..c517c74 --- /dev/null +++ b/components/ui/WidgetErrorBoundary.tsx @@ -0,0 +1,62 @@ +"use client" + +import React from 'react' +import WidgetErrorState from '@/components/ui/WidgetErrorState' +import { logError } from '@/lib/logger' +import { generateRequestId } from '@/lib/requestId' + +interface WidgetErrorBoundaryProps { + widgetName: string + children: React.ReactNode +} + +interface WidgetErrorBoundaryState { + hasError: boolean + error: Error | null +} + +/** + * Reusable React error boundary for dashboard widgets. + * Catches render-time errors in wrapped widgets, logs via logger, + * and renders WidgetErrorState with retry. + */ +class WidgetErrorBoundary extends React.Component< + WidgetErrorBoundaryProps, + WidgetErrorBoundaryState +> { + state: WidgetErrorBoundaryState = { hasError: false, error: null } + + static getDerivedStateFromError(error: Error): WidgetErrorBoundaryState { + return { hasError: true, error } + } + + componentDidCatch(error: Error, _info: React.ErrorInfo): void { + const requestId = generateRequestId() + logError(requestId, 'WIDGET_ERROR', `dashboard/${this.props.widgetName}`, error) + console.error(`Widget error in ${this.props.widgetName}:`, error) + } + + reset = (): void => { + this.setState({ hasError: false, error: null }) + } + + render(): React.ReactNode { + if (this.state.hasError) { + return ( +
+ +
+ ) + } + + return this.props.children + } +} + +export default WidgetErrorBoundary