Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added diagnostic/build-2b707695-part001.logd
Binary file not shown.
Binary file added diagnostic/build-2b707695-part002.logd
Binary file not shown.
Binary file added diagnostic/build-2b707695-part003.logd
Binary file not shown.
Binary file added diagnostic/build-2b707695-part004.logd
Binary file not shown.
29 changes: 29 additions & 0 deletions diagnostic/build-2b707695.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"generated_at": "2026-06-23T20:40:04.002065+00:00",
"commit": "2b707695",
"diagnostic_logd": [
"diagnostic\\build-2b707695-part001.logd",
"diagnostic\\build-2b707695-part002.logd",
"diagnostic\\build-2b707695-part003.logd",
"diagnostic\\build-2b707695-part004.logd"
],
"diagnostic_logd_error": null,
"message_blocker": null,
"chunked": true,
"chunk_size_bytes": 41943040,
"password": "abd282865073fcbdd0f7",
"decrypt_command": "encryptly unpack diagnostic\\build-2b707695.logd <outdir> --password abd282865073fcbdd0f7",
"total_modules": 1,
"passed": 1,
"failed": 0,
"modules": [
{
"name": "frontend",
"status": "PASS",
"elapsed_seconds": 4.772,
"artifact": "D:\\\u6211\u7684\u575a\u679c\u4e91\\\u4e2a\u4eba\\SODA\u8ba1\u5212\\github-bounty-work\\tentoftrials-error-boundaries\\frontend\\dist",
"output": ""
}
],
"pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic\\build-2b707695-part001.logd, diagnostic\\build-2b707695-part002.logd, diagnostic\\build-2b707695-part003.logd, diagnostic\\build-2b707695-part004.logd. The encrypted .logd is the required diagnostic content for PR review; this JSON file is metadata. Maintainers may ask you to remove these diagnostic artifacts before merging."
}
64 changes: 64 additions & 0 deletions frontend/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';

interface ErrorBoundaryProps {
children: React.ReactNode;
fallbackTitle?: string;
fallbackMessage?: string;
}

interface ErrorBoundaryState {
hasError: boolean;
}

const fallbackStyle: React.CSSProperties = {
border: '1px solid rgba(220, 38, 38, 0.28)',
borderRadius: 12,
background: 'rgba(254, 242, 242, 0.96)',
color: '#7f1d1d',
padding: '1rem',
};

const fallbackTitleStyle: React.CSSProperties = {
margin: '0 0 0.35rem',
fontSize: '1rem',
fontWeight: 700,
};

const fallbackMessageStyle: React.CSSProperties = {
margin: 0,
color: '#991b1b',
};

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
state: ErrorBoundaryState = {
hasError: false,
};

static getDerivedStateFromError(): ErrorBoundaryState {
return { hasError: true };
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Dashboard render error boundary caught an error', error, errorInfo);
}

render() {
if (this.state.hasError) {
return (
<section role="alert" style={fallbackStyle}>
<h3 style={fallbackTitleStyle}>
{this.props.fallbackTitle ?? 'This section could not be displayed'}
</h3>
<p style={fallbackMessageStyle}>
{this.props.fallbackMessage ??
'The rest of the dashboard is still available. Refresh the page or try again shortly.'}
</p>
</section>
);
}

return this.props.children;
}
}

export default ErrorBoundary;
65 changes: 44 additions & 21 deletions frontend/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import ErrorBoundary from '../components/ErrorBoundary';
import { useDashboardStats } from '../hooks';

const statCards = [
Expand All @@ -10,7 +11,34 @@ const statCards = [
{ key: 'uptime', label: 'Uptime', color: '#0891b2', suffix: '%' },
];

const Dashboard: React.FC = () => {
interface StatsGridProps {
stats: Record<string, unknown> | undefined;
}

const StatsGrid: React.FC<StatsGridProps> = ({ stats }) => (
<div className="stats-grid">
{statCards.map((card) => (
<div key={card.key} className="stat-card">
<div
className="stat-card-indicator"
style={{ backgroundColor: card.color }}
/>
<div className="stat-card-content">
<span className="stat-card-label">{card.label}</span>
<span
className="stat-card-value"
style={{ color: card.color }}
>
{String(stats?.[card.key] ?? ' - ')}
{card.suffix || ''}
</span>
</div>
</div>
))}
</div>
);

const DashboardContent: React.FC = () => {
const { data: stats, isLoading, error } = useDashboardStats();

if (isLoading) {
Expand Down Expand Up @@ -39,26 +67,12 @@ const Dashboard: React.FC = () => {
</p>
</div>

<div className="stats-grid">
{statCards.map((card) => (
<div key={card.key} className="stat-card">
<div
className="stat-card-indicator"
style={{ backgroundColor: card.color }}
/>
<div className="stat-card-content">
<span className="stat-card-label">{card.label}</span>
<span
className="stat-card-value"
style={{ color: card.color }}
>
{String((stats as any)?.[card.key] ?? ' - ')}
{card.suffix || ''}
</span>
</div>
</div>
))}
</div>
<ErrorBoundary
fallbackTitle="Dashboard metrics unavailable"
fallbackMessage="The metric cards could not be rendered, but the rest of the dashboard remains available."
>
<StatsGrid stats={stats as Record<string, unknown> | undefined} />
</ErrorBoundary>

<div className="dashboard-panels">
<div className="panel">
Expand All @@ -78,4 +92,13 @@ const Dashboard: React.FC = () => {
);
};

const Dashboard: React.FC = () => (
<ErrorBoundary
fallbackTitle="Dashboard unavailable"
fallbackMessage="A dashboard panel failed to render. Navigation and the rest of the app remain available."
>
<DashboardContent />
</ErrorBoundary>
);

export default Dashboard;