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
87 changes: 87 additions & 0 deletions diagnostic/build-1702a6da.json

Large diffs are not rendered by default.

Binary file added diagnostic/build-1702a6da.logd
Binary file not shown.
63 changes: 63 additions & 0 deletions frontend/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';

interface ErrorBoundaryProps {
children: React.ReactNode;
title: string;
message?: string;
}

interface ErrorBoundaryState {
hasError: boolean;
}

const fallbackStyle: React.CSSProperties = {
backgroundColor: '#1e293b',
border: '1px solid #334155',
borderRadius: 12,
color: '#f8fafc',
padding: '1rem',
};

const fallbackTitleStyle: React.CSSProperties = {
color: '#f8fafc',
fontSize: '1rem',
marginBottom: '0.35rem',
};

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

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('UI section failed to render', error, errorInfo);
}

render() {
const { children, title, message } = this.props;

if (this.state.hasError) {
return (
<div role="alert" style={fallbackStyle}>
<h3 style={fallbackTitleStyle}>{title}</h3>
<p style={fallbackMessageStyle}>
{message ?? 'This section is temporarily unavailable.'}
</p>
</div>
);
}

return children;
}
}

export default ErrorBoundary;
26 changes: 21 additions & 5 deletions frontend/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import Header from './Header';
import Sidebar from './Sidebar';

Expand All @@ -11,13 +12,28 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {

return (
<div className="app-layout">
<Header
onMenuToggle={() => setSidebarOpen((prev) => !prev)}
/>
<ErrorBoundary
title="Header unavailable"
message="Navigation controls are temporarily unavailable."
>
<Header
onMenuToggle={() => setSidebarOpen((prev) => !prev)}
/>
</ErrorBoundary>
<div className="app-body">
<Sidebar isOpen={sidebarOpen} />
<ErrorBoundary
title="Sidebar unavailable"
message="Secondary navigation is temporarily unavailable."
>
<Sidebar isOpen={sidebarOpen} />
</ErrorBoundary>
<main className="app-content">
{children}
<ErrorBoundary
title="Content unavailable"
message="The selected workspace could not be rendered."
>
{children}
</ErrorBoundary>
</main>
</div>
</div>
Expand Down
122 changes: 83 additions & 39 deletions frontend/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import React from 'react';
import ErrorBoundary from '../components/ErrorBoundary';
import { useDashboardStats } from '../hooks';
import type { DashboardStats } from '../types';

const statCards = [
interface StatCardConfig {
key: keyof Pick<
DashboardStats,
| 'totalUsers'
| 'activeSessions'
| 'trialsCompleted'
| 'avgResponseTime'
| 'errorRate'
| 'uptime'
>;
label: string;
color: string;
suffix?: string;
}

const statCards: StatCardConfig[] = [
{ key: 'totalUsers', label: 'Total Users', color: '#4f46e5' },
{ key: 'activeSessions', label: 'Active Sessions', color: '#059669' },
{ key: 'trialsCompleted', label: 'Trials Completed', color: '#d97706' },
Expand All @@ -10,6 +27,42 @@ const statCards = [
{ key: 'uptime', label: 'Uptime', color: '#0891b2', suffix: '%' },
];

interface StatCardProps {
card: StatCardConfig;
stats: DashboardStats | null | undefined;
}

const StatCard: React.FC<StatCardProps> = ({ card, stats }) => (
<div 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>
);

interface StatCardsGridProps {
stats: DashboardStats | null | undefined;
}

const StatCardsGrid: React.FC<StatCardsGridProps> = ({ stats }) => (
<div className="stats-grid">
{statCards.map((card) => (
<StatCard key={card.key} card={card} stats={stats} />
))}
</div>
);

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

Expand All @@ -31,50 +84,41 @@ const Dashboard: React.FC = () => {
}

return (
<div className="dashboard">
<div className="dashboard-header">
<h2>Dashboard</h2>
<p className="dashboard-subtitle">
Tent of Trials System Overview
</p>
</div>
<ErrorBoundary
title="Dashboard unavailable"
message="The dashboard panel could not be rendered."
>
<div className="dashboard">
<div className="dashboard-header">
<h2>Dashboard</h2>
<p className="dashboard-subtitle">
Tent of Trials System Overview
</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
title="Dashboard stats unavailable"
message="Live stat cards could not be rendered."
>
<StatCardsGrid stats={stats} />
</ErrorBoundary>

<div className="dashboard-panels">
<div className="panel">
<h3>Recent Activity</h3>
<div className="panel-placeholder">
Activity feed will appear here
<div className="dashboard-panels">
<div className="panel">
<h3>Recent Activity</h3>
<div className="panel-placeholder">
Activity feed will appear here
</div>
</div>
</div>
<div className="panel">
<h3>System Health</h3>
<div className="panel-placeholder">
Health metrics will appear here
<div className="panel">
<h3>System Health</h3>
<div className="panel-placeholder">
Health metrics will appear here
</div>
</div>
</div>
</div>
</div>
</ErrorBoundary>
);
};

Expand Down