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-0dde0a52-part001.logd
Binary file not shown.
Binary file added diagnostic/build-0dde0a52-part002.logd
Binary file not shown.
Binary file added diagnostic/build-0dde0a52-part003.logd
Binary file not shown.
Binary file added diagnostic/build-0dde0a52-part004.logd
Binary file not shown.
Binary file added diagnostic/build-0dde0a52-part005.logd
Binary file not shown.
Binary file added diagnostic/build-0dde0a52-part006.logd
Binary file not shown.
31 changes: 31 additions & 0 deletions diagnostic/build-0dde0a52.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"generated_at": "2026-06-27T01:04:00.344287+00:00",
"commit": "0dde0a52",
"diagnostic_logd": [
"diagnostic\\build-0dde0a52-part001.logd",
"diagnostic\\build-0dde0a52-part002.logd",
"diagnostic\\build-0dde0a52-part003.logd",
"diagnostic\\build-0dde0a52-part004.logd",
"diagnostic\\build-0dde0a52-part005.logd",
"diagnostic\\build-0dde0a52-part006.logd"
],
"diagnostic_logd_error": null,
"message_blocker": null,
"chunked": true,
"chunk_size_bytes": 41943040,
"password": "994378c16a1a357d6bdf",
"decrypt_command": "encryptly unpack diagnostic\\build-0dde0a52.logd <outdir> --password 994378c16a1a357d6bdf",
"total_modules": 1,
"passed": 1,
"failed": 0,
"modules": [
{
"name": "frontend",
"status": "PASS",
"elapsed_seconds": 8.478,
"artifact": "C:\\Users\\FLOR\\Documents\\Codex\\2026-06-26\\g\\work\\therapy-tentoftrials-submit\\frontend\\dist",
"output": "> tent-frontend@0.0.0 build\n> tsc -b && vite build\n\n\u001b[36mvite v6.4.3 \u001b[32mbuilding for production...\u001b[36m\u001b[39m\ntransforming...\n\u001b[32m\u2713\u001b[39m 102 modules transformed.\nrendering chunks...\ncomputing gzip size...\n\u001b[2mdist/\u001b[22m\u001b[32mindex.html \u001b[39m\u001b[1m\u001b[2m 0.71 kB\u001b[22m\u001b[1m\u001b[22m\u001b[2m \u2502 gzip: 0.38 kB\u001b[22m\n\u001b[2mdist/\u001b[22m\u001b[35massets/index-C4shL4Bc.css \u001b[39m\u001b[1m\u001b[2m 0.50 kB\u001b[22m\u001b[1m\u001b[22m\u001b[2m \u2502 gzip: 0.23 kB\u001b[22m\n\u001b[2mdist/\u001b[22m\u001b[36massets/state-BkjSKDbY.js \u001b[39m\u001b[1m\u001b[2m 8.91 kB\u001b[22m\u001b[1m\u001b[22m\u001b[2m \u2502 gzip: 3.54 kB\u001b[22m\u001b[2m \u2502 map: 57.15 kB\u001b[22m\n\u001b[2mdist/\u001b[22m\u001b[36massets/vendor-CREcWLHI.js \u001b[39m\u001b[1m\u001b[2m 48.93 kB\u001b[22m\u001b[1m\u001b[22m\u001b[2m \u2502 gzip: 17.25 kB\u001b[22m\u001b[2m \u2502 map: 481.27 kB\u001b[22m\n\u001b[2mdist/\u001b[22m\u001b[36massets/index-BHsknpR9.js \u001b[39m\u001b[1m\u001b[2m232.92 kB\u001b[22m\u001b[1m\u001b[22m\u001b[2m \u2502 gzip: 72.65 kB\u001b[22m\u001b[2m \u2502 map: 1,049.91 kB\u001b[22m\n\u001b[32m\u2713 built in 2.02s\u001b[39m"
}
],
"pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic\\build-0dde0a52-part001.logd, diagnostic\\build-0dde0a52-part002.logd, diagnostic\\build-0dde0a52-part003.logd, diagnostic\\build-0dde0a52-part004.logd, diagnostic\\build-0dde0a52-part005.logd, diagnostic\\build-0dde0a52-part006.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."
}
13 changes: 12 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import Layout from './components/Layout';
import ErrorBoundary from './components/ErrorBoundary';
import Dashboard from './pages/Dashboard';
import Analytics from './pages/Analytics';
import Settings from './pages/Settings';
Expand All @@ -9,7 +10,17 @@ const App: React.FC = () => {
return (
<Layout>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route
path="/"
element={
<ErrorBoundary
fallbackTitle="Dashboard unavailable"
fallbackMessage="The dashboard panel could not be rendered. Other application areas remain available."
>
<Dashboard />
</ErrorBoundary>
}
/>
<Route path="/analytics" element={<Analytics />} />
<Route path="/settings" element={<Settings />} />
<Route path="*" element={<Navigate to="/" replace />} />
Expand Down
37 changes: 37 additions & 0 deletions frontend/src/components/ErrorBoundary.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.error-boundary-fallback {
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
color: #f8fafc;
padding: 1rem;
}

.error-boundary-fallback.compact {
padding: 0.75rem;
}

.error-boundary-fallback h2,
.error-boundary-fallback h3 {
color: #f8fafc;
margin: 0 0 0.5rem;
}

.error-boundary-fallback h2 {
font-size: 1.25rem;
}

.error-boundary-fallback h3 {
font-size: 1rem;
}

.error-boundary-fallback p {
color: #94a3b8;
margin: 0;
}

.error-boundary-fallback .error-boundary-detail {
color: #eab308;
display: block;
font-size: 0.875rem;
margin-top: 0.75rem;
}
55 changes: 55 additions & 0 deletions frontend/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import './ErrorBoundary.css';

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

interface ErrorBoundaryState {
error: Error | null;
}

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

static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { error };
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('[ErrorBoundary] Render failure isolated:', error, errorInfo);
}

render() {
if (this.state.error) {
const {
fallbackTitle = 'Section unavailable',
fallbackMessage = 'This part of the interface could not be rendered. The rest of the application is still available.',
compact = false,
} = this.props;

return (
<section
className={`error-boundary-fallback ${compact ? 'compact' : ''}`}
role="alert"
aria-live="polite"
>
{compact ? <h3>{fallbackTitle}</h3> : <h2>{fallbackTitle}</h2>}
<p>{fallbackMessage}</p>
{import.meta.env.DEV ? (
<code className="error-boundary-detail">{this.state.error.message}</code>
) : null}
</section>
);
}

return this.props.children;
}
}

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

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

return (
<div className="app-layout">
<Header
onMenuToggle={() => setSidebarOpen((prev) => !prev)}
/>
<ErrorBoundary
fallbackTitle="Header unavailable"
fallbackMessage="Navigation controls could not be rendered."
compact
>
<Header
onMenuToggle={() => setSidebarOpen((prev) => !prev)}
/>
</ErrorBoundary>
<div className="app-body">
<Sidebar isOpen={sidebarOpen} />
<ErrorBoundary
fallbackTitle="Sidebar unavailable"
fallbackMessage="Secondary navigation could not be rendered."
compact
>
<Sidebar isOpen={sidebarOpen} />
</ErrorBoundary>
<main className="app-content">
{children}
<ErrorBoundary
fallbackTitle="Content unavailable"
fallbackMessage="The selected view could not be rendered. Other navigation areas remain available."
>
{children}
</ErrorBoundary>
</main>
</div>
</div>
Expand Down
58 changes: 39 additions & 19 deletions frontend/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { useDashboardStats } from '../hooks';
import ErrorBoundary from '../components/ErrorBoundary';

const statCards = [
{ key: 'totalUsers', label: 'Total Users', color: '#4f46e5' },
Expand All @@ -10,6 +11,30 @@ const statCards = [
{ key: 'uptime', label: 'Uptime', color: '#0891b2', suffix: '%' },
];

interface StatCardProps {
card: typeof statCards[number];
value: unknown;
}

const StatCard: React.FC<StatCardProps> = ({ card, value }) => (
<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(value ?? ' - ')}
{card.suffix || ''}
</span>
</div>
</div>
);

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

Expand Down Expand Up @@ -39,26 +64,21 @@ 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 }}
<ErrorBoundary
fallbackTitle="Stats unavailable"
fallbackMessage="Dashboard stat cards could not be rendered."
compact
>
<div className="stats-grid">
{statCards.map((card) => (
<StatCard
key={card.key}
card={card}
value={stats?.[card.key as keyof typeof stats]}
/>
<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>
))}
</div>
</ErrorBoundary>

<div className="dashboard-panels">
<div className="panel">
Expand Down