Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
459182a
Add error state tracking to TelemetryDashboard
Mosas2000 Apr 28, 2026
cc4e6ab
Add comprehensive error state tests for TelemetryDashboard
Mosas2000 Apr 28, 2026
546bf7c
Fix error state test to use retry button instead of rerender
Mosas2000 Apr 28, 2026
df049c4
Enhance error message display with monospace font and better styling
Mosas2000 Apr 28, 2026
2b37430
Add tests for error message styling and alert icon
Mosas2000 Apr 28, 2026
ed29ad6
Document error handling behavior in component header
Mosas2000 Apr 28, 2026
06352e6
Add inline comments explaining error extraction logic
Mosas2000 Apr 28, 2026
3b31cac
Add test to verify loading state clears on error
Mosas2000 Apr 28, 2026
b41254f
Add ARIA attributes for accessibility in error state
Mosas2000 Apr 28, 2026
4381dfd
Add accessibility tests for error state ARIA attributes
Mosas2000 Apr 28, 2026
6108636
Add test to verify export buttons remain enabled during error
Mosas2000 Apr 28, 2026
8634397
Extract error message handling into separate function
Mosas2000 Apr 28, 2026
1ee26d2
Add JSDoc documentation for main component
Mosas2000 Apr 28, 2026
13991ee
Improve error message extraction to handle string errors
Mosas2000 Apr 28, 2026
3fd62ac
Add test for string error handling
Mosas2000 Apr 28, 2026
9c47d2e
Add comment explaining error state rendering
Mosas2000 Apr 28, 2026
d4fab56
Add test to verify no error state on successful load
Mosas2000 Apr 28, 2026
04d7034
Add test for null error handling
Mosas2000 Apr 28, 2026
cb7feb0
Add test to verify retry button triggers reload
Mosas2000 Apr 28, 2026
ede8510
Add test to verify error message container styling
Mosas2000 Apr 28, 2026
78ddd2f
Extract default error message to constant
Mosas2000 Apr 28, 2026
0af85b4
Organize tests with section comments
Mosas2000 Apr 28, 2026
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
91 changes: 91 additions & 0 deletions frontend/src/components/TelemetryDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,56 @@ import {

/**
* TelemetryDashboard -- displays application performance metrics and error logs.
*
* Error Handling:
* - Displays visible error state when analytics data fails to load
* - Keeps refresh and export actions available during errors
* - Allows retry after failed loads
*
* Note: Shared sub-components (MetricCard, AlertPanel, etc.) must be defined
* at the top level to avoid "symbol has already been declared" errors during
* Vite/Rollup transformation when build optimizations are enabled.
*/

const DEFAULT_ERROR_MESSAGE = 'Failed to load telemetry data';

/**
* Extract a user-friendly error message from an exception.
* @param {Error} err - The error object
* @returns {string} User-friendly error message
*/
function extractErrorMessage(err) {
if (!err) return DEFAULT_ERROR_MESSAGE;

if (err.message) {
return err.message;
}

if (typeof err === 'string') {
return err;
}

return DEFAULT_ERROR_MESSAGE;
}

/**
* TelemetryDashboard component displays application performance metrics.
*
* @param {Object} props - Component props
* @param {Function} props.addToast - Function to display toast notifications
* @returns {JSX.Element} The telemetry dashboard UI
*/
export default function TelemetryDashboard({ addToast }) {
const { demoEnabled } = useDemoMode();
const [summary, setSummary] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [exporting, setExporting] = useState(false);
const [syncing, setSyncing] = useState(false);

const loadData = useCallback(() => {
setLoading(true);
setError(null);

if (demoEnabled) {
setTimeout(() => {
Expand Down Expand Up @@ -81,7 +118,12 @@ export default function TelemetryDashboard({ addToast }) {
try {
const data = analytics.getSummary();
setSummary(data);
setError(null);
} catch (err) {
// Extract error message for display, fallback to generic message
const errorMessage = extractErrorMessage(err);
setError(errorMessage);
// Keep console logging for debugging purposes
console.error('Failed to load telemetry:', err);
} finally {
setLoading(false);
Expand Down Expand Up @@ -170,6 +212,55 @@ export default function TelemetryDashboard({ addToast }) {
);
}

// Render error state with retry and export options
if (error) {
return (
<div className="space-y-4">
<div className="bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-800 p-8" role="alert" aria-live="assertive">
<div className="flex items-start gap-4">
<AlertTriangle className="w-6 h-6 text-red-500 shrink-0 mt-1" aria-hidden="true" />
<div className="flex-1">
<h2 className="text-lg font-bold text-gray-900 dark:text-white mb-2">
Failed to Load Telemetry Data
</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4 font-mono bg-gray-50 dark:bg-gray-800 p-3 rounded border border-gray-200 dark:border-gray-700">
{error}
</p>
<div className="flex flex-wrap gap-3">
<button
onClick={handleRefresh}
className="flex items-center gap-2 px-4 py-2 bg-gray-900 dark:bg-white dark:text-gray-900 text-white rounded-lg font-semibold hover:opacity-90 transition-opacity"
aria-label="Retry loading telemetry data"
>
<RefreshCw className="w-4 h-4" aria-hidden="true" />
Retry
</button>
<button
onClick={handleExportJson}
disabled={exporting}
className="flex items-center gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg font-semibold hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors disabled:opacity-50"
aria-label="Export telemetry data as JSON"
>
<FileJson className="w-4 h-4" aria-hidden="true" />
Export JSON
</button>
<button
onClick={handleExportCsv}
disabled={exporting}
className="flex items-center gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg font-semibold hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors disabled:opacity-50"
aria-label="Export telemetry data as CSV"
>
<FileSpreadsheet className="w-4 h-4" aria-hidden="true" />
Export CSV
</button>
</div>
</div>
</div>
</div>
</div>
);
}

const vitalsSummary = computeVitalsSummary(summary?.webVitals || {});
const tipFunnel = computeTipFunnel(summary || {});
const batchFunnel = computeBatchFunnel(summary || {});
Expand Down
Loading
Loading