website refactor

This commit is contained in:
2026-01-18 22:55:55 +01:00
parent b43a23a48c
commit aeaa43f4d3
179 changed files with 4736 additions and 6832 deletions

View File

@@ -3,8 +3,8 @@
import React, { Component, ReactNode, useState } from 'react';
import { ApiError } from '@/lib/api/base/ApiError';
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
import { ErrorDisplay } from '@/components/shared/state/ErrorDisplay';
import { DevErrorPanel } from './DevErrorPanel';
import { ErrorDisplay } from '@/ui/ErrorDisplay';
import { DevErrorPanel } from '@/ui/DevErrorPanel';
interface Props {
children: ReactNode;

View File

@@ -2,7 +2,6 @@
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { AlertTriangle } from 'lucide-react';
import React from 'react';
@@ -21,32 +20,30 @@ interface AppErrorBoundaryViewProps {
*/
export function AppErrorBoundaryView({ title, description, children }: AppErrorBoundaryViewProps) {
return (
<Stack gap={6} align="center" fullWidth>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1.5rem', width: '100%' }}>
{/* Header Icon */}
<Stack
p={4}
rounded="full"
bg="bg-warning-amber"
bgOpacity={0.1}
border
borderColor="border-warning-amber"
<div
style={{
padding: '1rem',
borderRadius: '9999px',
backgroundColor: 'rgba(255, 190, 77, 0.1)',
border: '1px solid rgba(255, 190, 77, 0.3)'
}}
>
<Icon icon={AlertTriangle} size={8} color="var(--warning-amber)" />
</Stack>
<Icon icon={AlertTriangle} size={8} intent="warning" />
</div>
{/* Typography */}
<Stack gap={2} align="center">
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.5rem' }}>
<Heading level={1} weight="bold">
<Text uppercase letterSpacing="tighter">
{title}
</Text>
{title}
</Heading>
<Text color="text-gray-400" align="center" maxWidth="md" leading="relaxed">
<Text variant="low" align="center" style={{ maxWidth: '32rem' }} leading="relaxed">
{description}
</Text>
</Stack>
</div>
{children}
</Stack>
</div>
);
}

View File

@@ -1,382 +0,0 @@
'use client';
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
import { ApiError } from '@/lib/api/base/ApiError';
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
import { Badge } from '@/ui/Badge';
import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Grid } from '@/ui/primitives/Grid';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { Activity, AlertTriangle, Copy, RefreshCw, Terminal, X } from 'lucide-react';
import { useEffect, useState } from 'react';
interface DevErrorPanelProps {
error: ApiError;
onReset: () => void;
}
/**
* Developer-focused error panel with detailed debugging information
*/
export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
const [connectionStatus, setConnectionStatus] = useState(connectionMonitor.getHealth());
const [circuitBreakers, setCircuitBreakers] = useState(CircuitBreakerRegistry.getInstance().getStatus());
const [copied, setCopied] = useState(false);
useEffect(() => {
// Update status on mount
const health = connectionMonitor.getHealth();
setConnectionStatus(health);
setCircuitBreakers(CircuitBreakerRegistry.getInstance().getStatus());
// Listen for status changes
const handleStatusChange = () => {
setConnectionStatus(connectionMonitor.getHealth());
setCircuitBreakers(CircuitBreakerRegistry.getInstance().getStatus());
};
connectionMonitor.on('success', handleStatusChange);
connectionMonitor.on('failure', handleStatusChange);
connectionMonitor.on('connected', handleStatusChange);
connectionMonitor.on('disconnected', handleStatusChange);
connectionMonitor.on('degraded', handleStatusChange);
return () => {
connectionMonitor.off('success', handleStatusChange);
connectionMonitor.off('failure', handleStatusChange);
connectionMonitor.off('connected', handleStatusChange);
connectionMonitor.off('disconnected', handleStatusChange);
connectionMonitor.off('degraded', handleStatusChange);
};
}, []);
const copyToClipboard = async () => {
const debugInfo = {
error: {
type: error.type,
message: error.message,
context: error.context,
stack: error.stack,
},
connection: connectionStatus,
circuitBreakers,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href,
};
try {
await navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2));
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
// Silent failure for clipboard operations
}
};
const triggerHealthCheck = async () => {
await connectionMonitor.performHealthCheck();
setConnectionStatus(connectionMonitor.getHealth());
};
const resetCircuitBreakers = () => {
CircuitBreakerRegistry.getInstance().resetAll();
setCircuitBreakers(CircuitBreakerRegistry.getInstance().getStatus());
};
const getSeverityVariant = (): 'danger' | 'warning' | 'info' | 'default' => {
switch (error.getSeverity()) {
case 'error': return 'danger';
case 'warn': return 'warning';
case 'info': return 'info';
default: return 'default';
}
};
const reliability = connectionMonitor.getReliability();
return (
<Stack
position="fixed"
inset="0"
zIndex={50}
overflow="auto"
bg="bg-deep-graphite"
p={4}
>
<Stack maxWidth="6xl" mx="auto" fullWidth>
<Stack gap={4}>
{/* Header */}
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="lg" p={4} direction="row" align="center" justify="between">
<Stack direction="row" align="center" gap={3}>
<Icon icon={Terminal} size={5} color="rgb(59, 130, 246)" />
<Heading level={2}>API Error Debug Panel</Heading>
<Badge variant={getSeverityVariant()}>
{error.type}
</Badge>
</Stack>
<Stack direction="row" gap={2}>
<Button
variant="secondary"
onClick={copyToClipboard}
icon={<Icon icon={Copy} size={4} />}
>
{copied ? 'Copied!' : 'Copy'}
</Button>
<Button
variant="primary"
onClick={onReset}
icon={<Icon icon={X} size={4} />}
>
Close
</Button>
</Stack>
</Stack>
{/* Error Details */}
<Grid cols={1} lgCols={2} gap={4}>
<Stack gap={4}>
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2} direction="row" align="center" gap={2}>
<Icon icon={AlertTriangle} size={4} color="text-white" />
<Text weight="semibold" color="text-white">Error Details</Text>
</Stack>
<Stack p={4}>
<Stack gap={2} style={{ fontSize: '0.75rem' }}>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Type:</Text>
<Text className="col-span-2" color="text-red-400" weight="bold">{error.type}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Message:</Text>
<Text className="col-span-2" color="text-gray-300">{error.message}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Endpoint:</Text>
<Text className="col-span-2" color="text-primary-blue">{error.context.endpoint || 'N/A'}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Method:</Text>
<Text className="col-span-2" color="text-warning-amber">{error.context.method || 'N/A'}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Status:</Text>
<Text className="col-span-2">{error.context.statusCode || 'N/A'}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Retry Count:</Text>
<Text className="col-span-2">{error.context.retryCount || 0}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Timestamp:</Text>
<Text className="col-span-2" color="text-gray-500">{error.context.timestamp}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Retryable:</Text>
<Text className="col-span-2" color={error.isRetryable() ? 'text-performance-green' : 'text-red-400'}>
{error.isRetryable() ? 'Yes' : 'No'}
</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Connectivity:</Text>
<Text className="col-span-2" color={error.isConnectivityIssue() ? 'text-red-400' : 'text-performance-green'}>
{error.isConnectivityIssue() ? 'Yes' : 'No'}
</Text>
</Grid>
{error.context.troubleshooting && (
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Troubleshoot:</Text>
<Text className="col-span-2" color="text-warning-amber">{error.context.troubleshooting}</Text>
</Grid>
)}
</Stack>
</Stack>
</Card>
{/* Connection Status */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2} direction="row" align="center" gap={2}>
<Icon icon={Activity} size={4} color="text-white" />
<Text weight="semibold" color="text-white">Connection Health</Text>
</Stack>
<Stack p={4}>
<Stack gap={2} style={{ fontSize: '0.75rem' }}>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Status:</Text>
<Text className="col-span-2" weight="bold" color={
connectionStatus.status === 'connected' ? 'text-performance-green' :
connectionStatus.status === 'degraded' ? 'text-warning-amber' :
'text-red-400'
}>
{connectionStatus.status.toUpperCase()}
</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Reliability:</Text>
<Text className="col-span-2">{reliability.toFixed(2)}%</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Total Requests:</Text>
<Text className="col-span-2">{connectionStatus.totalRequests}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Successful:</Text>
<Text className="col-span-2" color="text-performance-green">{connectionStatus.successfulRequests}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Failed:</Text>
<Text className="col-span-2" color="text-red-400">{connectionStatus.failedRequests}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Consecutive Failures:</Text>
<Text className="col-span-2">{connectionStatus.consecutiveFailures}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Avg Response:</Text>
<Text className="col-span-2">{connectionStatus.averageResponseTime.toFixed(2)}ms</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Last Check:</Text>
<Text className="col-span-2" color="text-gray-500">
{connectionStatus.lastCheck?.toLocaleTimeString() || 'Never'}
</Text>
</Grid>
</Stack>
</Stack>
</Card>
</Stack>
{/* Right Column */}
<Stack gap={4}>
{/* Circuit Breakers */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2} direction="row" align="center" gap={2}>
<Text size="lg"></Text>
<Text weight="semibold" color="text-white">Circuit Breakers</Text>
</Stack>
<Stack p={4}>
{Object.keys(circuitBreakers).length === 0 ? (
<Stack align="center" py={4}>
<Text color="text-gray-500">No circuit breakers active</Text>
</Stack>
) : (
<Stack gap={2} maxHeight="12rem" overflow="auto" style={{ fontSize: '0.75rem' }}>
{Object.entries(circuitBreakers).map(([endpoint, status]) => (
<Stack key={endpoint} direction="row" align="center" justify="between" p={2} bg="bg-deep-graphite" rounded="md" border borderColor="border-charcoal-outline">
<Text color="text-primary-blue" truncate flexGrow={1}>{endpoint}</Text>
<Stack px={2} py={1} rounded="sm" bg={
status.state === 'CLOSED' ? 'bg-green-500/20' :
status.state === 'OPEN' ? 'bg-red-500/20' :
'bg-yellow-500/20'
}>
<Text color={
status.state === 'CLOSED' ? 'text-performance-green' :
status.state === 'OPEN' ? 'text-red-400' :
'text-warning-amber'
}>
{status.state}
</Text>
</Stack>
<Text color="text-gray-500" className="ml-2">{status.failures} failures</Text>
</Stack>
))}
</Stack>
)}
</Stack>
</Card>
{/* Actions */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2}>
<Text weight="semibold" color="text-white">Actions</Text>
</Stack>
<Stack p={4}>
<Stack gap={2}>
<Button
variant="primary"
onClick={triggerHealthCheck}
fullWidth
icon={<Icon icon={RefreshCw} size={4} />}
>
Run Health Check
</Button>
<Button
variant="secondary"
onClick={resetCircuitBreakers}
fullWidth
icon={<Text size="lg">🔄</Text>}
>
Reset Circuit Breakers
</Button>
<Button
variant="danger"
onClick={() => {
connectionMonitor.reset();
setConnectionStatus(connectionMonitor.getHealth());
}}
fullWidth
icon={<Text size="lg">🗑</Text>}
>
Reset Connection Stats
</Button>
</Stack>
</Stack>
</Card>
{/* Quick Fixes */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2}>
<Text weight="semibold" color="text-white">Quick Fixes</Text>
</Stack>
<Stack p={4}>
<Stack gap={2} style={{ fontSize: '0.75rem' }}>
<Text color="text-gray-400">Common solutions:</Text>
<Stack as="ul" gap={1} pl={4}>
<Stack as="li"><Text color="text-gray-300">Check API server is running</Text></Stack>
<Stack as="li"><Text color="text-gray-300">Verify CORS configuration</Text></Stack>
<Stack as="li"><Text color="text-gray-300">Check environment variables</Text></Stack>
<Stack as="li"><Text color="text-gray-300">Review network connectivity</Text></Stack>
<Stack as="li"><Text color="text-gray-300">Check API rate limits</Text></Stack>
</Stack>
</Stack>
</Stack>
</Card>
{/* Raw Error */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2}>
<Text weight="semibold" color="text-white">Raw Error</Text>
</Stack>
<Stack p={4}>
<Stack as="pre" p={2} bg="bg-deep-graphite" rounded="md" overflow="auto" maxHeight="8rem" style={{ fontSize: '0.75rem' }} color="text-gray-400">
{JSON.stringify({
type: error.type,
message: error.message,
context: error.context,
}, null, 2)}
</Stack>
</Stack>
</Card>
</Stack>
</Grid>
{/* Console Output */}
<Card p={0} rounded="lg" overflow="hidden" variant="outline" borderColor="border-charcoal-outline" className="bg-panel-gray/40">
<Stack bg="bg-charcoal-outline" px={4} py={2} direction="row" align="center" gap={2}>
<Icon icon={Terminal} size={4} color="text-white" />
<Text weight="semibold" color="text-white">Console Output</Text>
</Stack>
<Stack p={4} bg="bg-deep-graphite" style={{ fontSize: '0.75rem' }}>
<Text color="text-gray-500" block mb={2}>{'>'} {error.getDeveloperMessage()}</Text>
<Text color="text-gray-600" block>Check browser console for full stack trace and additional debug info.</Text>
</Stack>
</Card>
</Stack>
</Stack>
</Stack>
);
}

View File

@@ -3,10 +3,10 @@
import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { ChevronDown, ChevronUp, Copy, Terminal } from 'lucide-react';
import { useState } from 'react';
import { Accordion } from '@/ui/Accordion';
import { Copy } from 'lucide-react';
import React, { useState } from 'react';
interface ErrorDetailsProps {
error: Error & { digest?: string };
@@ -19,7 +19,6 @@ interface ErrorDetailsProps {
* Part of the 500 route redesign.
*/
export function ErrorDetails({ error }: ErrorDetailsProps) {
const [showDetails, setShowDetails] = useState(false);
const [copied, setCopied] = useState(false);
const copyError = async () => {
@@ -41,62 +40,28 @@ export function ErrorDetails({ error }: ErrorDetailsProps) {
};
return (
<Stack gap={4} fullWidth pt={4} borderTop borderColor="border-white">
<Stack
as="button"
onClick={() => setShowDetails(!showDetails)}
direction="row"
align="center"
justify="center"
gap={2}
color="text-gray-500"
className="transition-all hover:text-gray-300"
>
<Icon icon={Terminal} size={3} />
<Text
size="xs"
weight="medium"
uppercase
letterSpacing="widest"
color="inherit"
>
{showDetails ? 'Hide Technical Logs' : 'Show Technical Logs'}
</Text>
{showDetails ? <Icon icon={ChevronUp} size={3} /> : <Icon icon={ChevronDown} size={3} />}
</Stack>
{showDetails && (
<Stack gap={3}>
<Card
variant="outline"
rounded="md"
p={4}
fullWidth
maxHeight="48"
overflow="auto"
borderColor="border-white"
className="bg-graphite-black/40"
>
<Text font="mono" size="xs" color="text-gray-500" block leading="relaxed">
<div style={{ width: '100%', marginTop: '1.5rem', paddingTop: '1.5rem', borderTop: '1px solid var(--ui-color-border-muted)' }}>
<Accordion title="Technical Logs">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Card variant="outline">
<Text font="mono" size="xs" variant="low" block leading="relaxed" style={{ maxHeight: '12rem', overflow: 'auto' }}>
{error.stack || 'No stack trace available'}
{error.digest && `\n\nDigest: ${error.digest}`}
</Text>
</Card>
<Stack direction="row" justify="end">
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="secondary"
size="sm"
onClick={copyError}
icon={<Icon icon={Copy} size={3} />}
height="8"
fontSize="10px"
icon={<Icon icon={Copy} size={3} intent="low" />}
>
{copied ? 'Copied to Clipboard' : 'Copy Error Details'}
{copied ? 'Copied!' : 'Copy Details'}
</Button>
</Stack>
</Stack>
)}
</Stack>
</div>
</div>
</Accordion>
</div>
);
}

View File

@@ -3,10 +3,10 @@
import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { ChevronDown, ChevronUp, Copy } from 'lucide-react';
import { useState } from 'react';
import { Accordion } from '@/ui/Accordion';
import { Copy } from 'lucide-react';
import React, { useState } from 'react';
interface ErrorDetailsBlockProps {
error: Error & { digest?: string };
@@ -19,7 +19,6 @@ interface ErrorDetailsBlockProps {
* Follows "Precision Racing Minimal" theme.
*/
export function ErrorDetailsBlock({ error }: ErrorDetailsBlockProps) {
const [showDetails, setShowDetails] = useState(false);
const [copied, setCopied] = useState(false);
const copyError = async () => {
@@ -41,61 +40,28 @@ export function ErrorDetailsBlock({ error }: ErrorDetailsBlockProps) {
};
return (
<Stack gap={4} fullWidth pt={4} borderTop borderColor="border-white">
<Stack
as="button"
onClick={() => setShowDetails(!showDetails)}
direction="row"
align="center"
justify="center"
gap={2}
className="transition-all"
>
<Text
size="xs"
color="text-gray-500"
className="hover:text-gray-300 flex items-center gap-2"
uppercase
letterSpacing="widest"
weight="medium"
>
{showDetails ? <Icon icon={ChevronUp} size={3} /> : <Icon icon={ChevronDown} size={3} />}
{showDetails ? 'Hide Technical Logs' : 'Show Technical Logs'}
</Text>
</Stack>
{showDetails && (
<Stack gap={3}>
<Card
variant="outline"
rounded="md"
p={4}
fullWidth
maxHeight="48"
overflow="auto"
borderColor="border-white"
className="bg-graphite-black/40"
>
<Text font="mono" size="xs" color="text-gray-500" block leading="relaxed">
<div style={{ width: '100%', marginTop: '1.5rem', paddingTop: '1.5rem', borderTop: '1px solid var(--ui-color-border-muted)' }}>
<Accordion title="Technical Logs">
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Card variant="outline">
<Text font="mono" size="xs" variant="low" block leading="relaxed" style={{ maxHeight: '12rem', overflow: 'auto' }}>
{error.stack || 'No stack trace available'}
{error.digest && `\n\nDigest: ${error.digest}`}
</Text>
</Card>
<Stack direction="row" justify="end">
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="secondary"
size="sm"
onClick={copyError}
icon={<Icon icon={Copy} size={3} />}
height="8"
fontSize="10px"
icon={<Icon icon={Copy} size={3} intent="low" />}
>
{copied ? 'Copied to Clipboard' : 'Copy Error Details'}
{copied ? 'Copied!' : 'Copy Details'}
</Button>
</Stack>
</Stack>
)}
</Stack>
</div>
</div>
</Accordion>
</div>
);
}

View File

@@ -1,9 +1,7 @@
'use client';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Home, RefreshCw } from 'lucide-react';
import { ErrorActionButtons } from '@/ui/ErrorActionButtons';
import React from 'react';
interface ErrorRecoveryActionsProps {
onRetry: () => void;
@@ -18,30 +16,9 @@ interface ErrorRecoveryActionsProps {
*/
export function ErrorRecoveryActions({ onRetry, onHome }: ErrorRecoveryActionsProps) {
return (
<Stack
direction="row"
wrap
align="center"
justify="center"
gap={3}
fullWidth
>
<Button
variant="primary"
onClick={onRetry}
icon={<Icon icon={RefreshCw} size={4} />}
width="160px"
>
Retry Session
</Button>
<Button
variant="secondary"
onClick={onHome}
icon={<Icon icon={Home} size={4} />}
width="160px"
>
Return to Pits
</Button>
</Stack>
<ErrorActionButtons
onRetry={onRetry}
onGoHome={onHome}
/>
);
}

View File

@@ -2,11 +2,12 @@
import { Card } from '@/ui/Card';
import { Glow } from '@/ui/Glow';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { ErrorPageContainer } from '@/ui/ErrorPageContainer';
import { AppErrorBoundaryView } from './AppErrorBoundaryView';
import { ErrorDetailsBlock } from './ErrorDetailsBlock';
import { ErrorRecoveryActions } from './ErrorRecoveryActions';
import React from 'react';
interface ErrorScreenProps {
error: Error & { digest?: string };
@@ -22,54 +23,27 @@ interface ErrorScreenProps {
*/
export function ErrorScreen({ error, reset, onHome }: ErrorScreenProps) {
return (
<Stack
as="main"
minHeight="screen"
fullWidth
align="center"
justify="center"
bg="bg-deep-graphite"
position="relative"
overflow="hidden"
px={6}
>
<ErrorPageContainer size="lg" variant="glass">
{/* Background Accents */}
<Glow color="primary" size="xl" position="center" opacity={0.05} />
<Card
variant="outline"
rounded="lg"
p={8}
maxWidth="2xl"
fullWidth
position="relative"
zIndex={10}
borderColor="border-white"
className="bg-white/5 backdrop-blur-md"
<AppErrorBoundaryView
title="System Malfunction"
description="The application encountered an unexpected state. Our telemetry has logged the incident."
>
<AppErrorBoundaryView
title="System Malfunction"
description="The application encountered an unexpected state. Our telemetry has logged the incident."
>
{/* Error Message Summary */}
<Card
variant="outline"
rounded="md"
p={4}
fullWidth
borderColor="border-white"
className="bg-graphite-black/20"
>
<Text font="mono" size="sm" color="text-warning-amber" block>
{/* Error Message Summary */}
<div style={{ width: '100%', marginBottom: '1.5rem' }}>
<Card variant="outline">
<Text font="mono" size="sm" variant="warning" block>
{error.message || 'Unknown execution error'}
</Text>
</Card>
</div>
<ErrorRecoveryActions onRetry={reset} onHome={onHome} />
<ErrorDetailsBlock error={error} />
</AppErrorBoundaryView>
</Card>
</Stack>
<ErrorRecoveryActions onRetry={reset} onHome={onHome} />
<ErrorDetailsBlock error={error} />
</AppErrorBoundaryView>
</ErrorPageContainer>
);
}

View File

@@ -5,9 +5,10 @@ import { Card } from '@/ui/Card';
import { Glow } from '@/ui/Glow';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { ErrorPageContainer } from '@/ui/ErrorPageContainer';
import { AlertTriangle, Home, RefreshCw, Terminal } from 'lucide-react';
import React from 'react';
interface GlobalErrorScreenProps {
error: Error & { digest?: string };
@@ -23,88 +24,44 @@ interface GlobalErrorScreenProps {
*/
export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenProps) {
return (
<Stack
as="main"
minHeight="screen"
fullWidth
align="center"
justify="center"
bg="bg-base-black"
position="relative"
overflow="hidden"
px={6}
>
<ErrorPageContainer size="lg" variant="glass">
{/* Background Accents - Subtle telemetry vibe */}
<Glow color="primary" size="xl" position="center" opacity={0.03} />
<Card
variant="outline"
rounded="none"
p={0}
maxWidth="2xl"
fullWidth
position="relative"
zIndex={10}
borderColor="border-white"
className="bg-graphite-black/10"
>
{/* System Status Header */}
<Stack
borderBottom
borderColor="border-white"
px={6}
py={4}
direction="row"
align="center"
justify="between"
className="bg-white/5"
>
<Stack direction="row" gap={3} align="center">
<Icon icon={AlertTriangle} size={5} color="var(--warning-amber)" />
<Heading level={2} weight="bold">
<Text uppercase letterSpacing="widest" size="sm">
System Fault Detected
</Text>
</Heading>
</Stack>
<Text font="mono" size="xs" color="text-gray-500" uppercase>
Status: Critical
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1.5rem', paddingBottom: '1rem', borderBottom: '1px solid var(--ui-color-border-default)' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<Icon icon={AlertTriangle} size={5} intent="warning" />
<Heading level={2} weight="bold">
System Fault Detected
</Heading>
</div>
<Text font="mono" size="xs" variant="low" uppercase>
Status: Critical
</Text>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
{/* Fault Description */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Text variant="med" size="base" leading="relaxed">
The application kernel encountered an unrecoverable execution error.
Telemetry has been captured for diagnostic review.
</Text>
</Stack>
<Stack p={8}>
<Stack gap={8}>
{/* Fault Description */}
<Stack gap={4}>
<Text color="text-gray-400" size="base" leading="relaxed">
The application kernel encountered an unrecoverable execution error.
Telemetry has been captured for diagnostic review.
</Text>
<SystemStatusPanel error={error} />
</div>
<SystemStatusPanel error={error} />
</Stack>
{/* Recovery Actions */}
<RecoveryActions onRetry={reset} onHome={onHome} />
</div>
{/* Recovery Actions */}
<RecoveryActions onRetry={reset} onHome={onHome} />
</Stack>
</Stack>
{/* Footer / Metadata */}
<Stack
borderTop
borderColor="border-white"
px={6}
py={3}
direction="row"
justify="end"
className="bg-white/5"
>
<Text font="mono" size="xs" color="text-gray-600">
GP-CORE-ERR-{error.digest?.substring(0, 8).toUpperCase() || 'UNKNOWN'}
</Text>
</Stack>
</Card>
</Stack>
{/* Footer / Metadata */}
<div style={{ marginTop: '2rem', paddingTop: '1rem', borderTop: '1px solid var(--ui-color-border-default)', textAlign: 'right' }}>
<Text font="mono" size="xs" variant="low">
GP-CORE-ERR-{error.digest?.substring(0, 8).toUpperCase() || 'UNKNOWN'}
</Text>
</div>
</ErrorPageContainer>
);
}
@@ -115,30 +72,23 @@ export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenPro
*/
function SystemStatusPanel({ error }: { error: Error & { digest?: string } }) {
return (
<Card
variant="outline"
rounded="none"
p={4}
fullWidth
borderColor="border-white"
className="bg-graphite-black/20"
>
<Stack gap={3}>
<Stack direction="row" align="center" gap={2}>
<Icon icon={Terminal} size={3} color="var(--gray-500)" />
<Text font="mono" size="xs" color="text-gray-500" uppercase letterSpacing="wider">
<Card variant="outline">
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<Icon icon={Terminal} size={3} intent="low" />
<Text font="mono" size="xs" variant="low" uppercase>
Fault Log
</Text>
</Stack>
<Text font="mono" size="sm" color="text-warning-amber" block>
</div>
<Text font="mono" size="sm" variant="warning" block>
{error.message || 'Unknown execution fault'}
</Text>
{error.digest && (
<Text font="mono" size="xs" color="text-gray-600" block>
<Text font="mono" size="xs" variant="low" block>
Digest: {error.digest}
</Text>
)}
</Stack>
</div>
</Card>
);
}
@@ -150,19 +100,11 @@ function SystemStatusPanel({ error }: { error: Error & { digest?: string } }) {
*/
function RecoveryActions({ onRetry, onHome }: { onRetry: () => void; onHome: () => void }) {
return (
<Stack
direction="row"
wrap
align="center"
gap={4}
fullWidth
>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flexWrap: 'wrap' }}>
<Button
variant="primary"
onClick={onRetry}
icon={<Icon icon={RefreshCw} size={4} />}
rounded="none"
px={8}
>
Reboot Session
</Button>
@@ -170,11 +112,9 @@ function RecoveryActions({ onRetry, onHome }: { onRetry: () => void; onHome: ()
variant="secondary"
onClick={onHome}
icon={<Icon icon={Home} size={4} />}
rounded="none"
px={8}
>
Return to Pits
</Button>
</Stack>
</div>
);
}

View File

@@ -1,8 +1,9 @@
'use client';
import { Button } from '@/ui/Button';
import { Stack } from '@/ui/primitives/Stack';
import { Group } from '@/ui/Group';
import { Text } from '@/ui/Text';
import { StatusDot } from '@/ui/StatusDot';
interface NotFoundActionsProps {
primaryLabel: string;
@@ -17,12 +18,11 @@ interface NotFoundActionsProps {
*/
export function NotFoundActions({ primaryLabel, onPrimaryClick }: NotFoundActionsProps) {
return (
<Stack direction="row" gap={4} align="center" justify="center">
<Group direction="row" gap={4} align="center" justify="center">
<Button
variant="primary"
size="lg"
onClick={onPrimaryClick}
minWidth="200px"
>
{primaryLabel}
</Button>
@@ -32,18 +32,13 @@ export function NotFoundActions({ primaryLabel, onPrimaryClick }: NotFoundAction
size="lg"
onClick={() => window.history.back()}
>
<Stack direction="row" gap={2} align="center">
<Stack
width={2}
height={2}
rounded="full"
bg="soft-steel"
/>
<Text size="xs" weight="bold" uppercase letterSpacing="widest" color="text-gray-400">
<Group direction="row" gap={2} align="center">
<StatusDot intent="telemetry" size="sm" />
<Text size="xs" weight="bold" uppercase variant="low">
Previous Sector
</Text>
</Stack>
</Group>
</Button>
</Stack>
</Group>
);
}

View File

@@ -1,8 +1,8 @@
'use client';
import { Button } from '@/ui/Button';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import React from 'react';
interface NotFoundCallToActionProps {
label: string;
@@ -17,7 +17,7 @@ interface NotFoundCallToActionProps {
*/
export function NotFoundCallToAction({ label, onClick }: NotFoundCallToActionProps) {
return (
<Stack gap={4} align="center">
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }}>
<Button
variant="primary"
size="lg"
@@ -25,9 +25,9 @@ export function NotFoundCallToAction({ label, onClick }: NotFoundCallToActionPro
>
{label}
</Button>
<Text size="xs" color="text-gray-500" uppercase letterSpacing="widest">
<Text size="xs" variant="low" uppercase>
Telemetry connection lost
</Text>
</Stack>
</div>
);
}

View File

@@ -1,7 +1,8 @@
'use client';
import { Stack } from '@/ui/primitives/Stack';
import { Group } from '@/ui/Group';
import { Text } from '@/ui/Text';
import { Badge } from '@/ui/Badge';
interface NotFoundDiagnosticsProps {
errorCode: string;
@@ -15,35 +16,19 @@ interface NotFoundDiagnosticsProps {
*/
export function NotFoundDiagnostics({ errorCode }: NotFoundDiagnosticsProps) {
return (
<Stack gap={3} align="center">
<Stack
px={3}
py={1}
border
borderColor="primary-accent"
bg="primary-accent"
bgOpacity={0.1}
rounded="sm"
>
<Text
size="xs"
weight="bold"
color="text-primary-accent"
uppercase
letterSpacing="widest"
>
{errorCode}
</Text>
</Stack>
<Group direction="column" gap={3} align="center">
<Badge variant="primary" size="md">
{errorCode}
</Badge>
<Text
size="xs"
color="text-gray-500"
variant="low"
uppercase
letterSpacing="widest"
weight="medium"
align="center"
>
Telemetry connection lost // Sector data unavailable
</Text>
</Stack>
</Group>
);
}

View File

@@ -1,7 +1,8 @@
'use client';
import { Stack } from '@/ui/primitives/Stack';
import { NavGroup } from '@/ui/NavGroup';
import { Text } from '@/ui/Text';
import { Link } from '@/ui/Link';
import React from 'react';
interface NotFoundHelpLinksProps {
@@ -16,31 +17,20 @@ interface NotFoundHelpLinksProps {
*/
export function NotFoundHelpLinks({ links }: NotFoundHelpLinksProps) {
return (
<Stack direction="row" gap={6} align="center" wrap center>
{links.map((link, index) => (
<React.Fragment key={link.href}>
<Stack
as="a"
href={link.href}
transition
display="inline-block"
<NavGroup direction="horizontal" gap={6} align="center">
{links.map((link) => (
<Link key={link.href} href={link.href} variant="ghost" underline="none">
<Text
variant="low"
hoverVariant="primary"
weight="medium"
size="xs"
uppercase
>
<Text
color="text-gray-400"
hoverTextColor="primary-accent"
weight="medium"
size="xs"
letterSpacing="widest"
uppercase
>
{link.label}
</Text>
</Stack>
{index < links.length - 1 && (
<Stack width="1px" height="12px" bg="border-gray" opacity={0.5} />
)}
</React.Fragment>
{link.label}
</Text>
</Link>
))}
</Stack>
</NavGroup>
);
}

View File

@@ -1,12 +1,13 @@
'use client';
import { Card } from '@/ui/Card';
import { ErrorPageContainer } from '@/ui/ErrorPageContainer';
import { Glow } from '@/ui/Glow';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { FooterSection } from '@/ui/FooterSection';
import { NotFoundActions } from './NotFoundActions';
import { NotFoundDiagnostics } from './NotFoundDiagnostics';
import { NotFoundHelpLinks } from './NotFoundHelpLinks';
import React from 'react';
interface NotFoundScreenProps {
errorCode: string;
@@ -37,105 +38,44 @@ export function NotFoundScreen({
];
return (
<Stack
as="main"
minHeight="screen"
align="center"
justify="center"
bg="graphite-black"
position="relative"
overflow="hidden"
fullWidth
>
<ErrorPageContainer size="lg" variant="glass">
{/* Background Glow Accent */}
<Glow color="primary" size="xl" opacity={0.1} position="center" />
<Card
variant="outline"
p={12}
rounded="none"
maxWidth="2xl"
fullWidth
mx={6}
position="relative"
zIndex={10}
className="bg-white/5 backdrop-blur-md"
<NotFoundDiagnostics errorCode={errorCode} />
<Text
as="h1"
size="4xl"
weight="bold"
variant="high"
uppercase
block
align="center"
style={{ marginTop: '1rem', marginBottom: '2rem' }}
>
<Stack gap={12} align="center">
{/* Header Section */}
<Stack gap={4} align="center">
<NotFoundDiagnostics errorCode={errorCode} />
<Text
as="h1"
size="4xl"
weight="bold"
color="text-white"
letterSpacing="tighter"
uppercase
block
leading="none"
textAlign="center"
>
{title}
</Text>
</Stack>
{title}
</Text>
{/* Visual Separator */}
<Stack width="full" height="1px" bg="primary-accent" opacity={0.3} position="relative" align="center" justify="center">
<Stack
w="3"
h="3"
bg="primary-accent"
>{null}</Stack>
</Stack>
<Text
size="xl"
variant="med"
block
weight="medium"
align="center"
style={{ marginBottom: '3rem' }}
>
{message}
</Text>
{/* Message Section */}
<Text
size="xl"
color="text-gray-400"
maxWidth="lg"
leading="relaxed"
block
weight="medium"
textAlign="center"
>
{message}
</Text>
<NotFoundActions
primaryLabel={actionLabel}
onPrimaryClick={onActionClick}
/>
{/* Actions Section */}
<NotFoundActions
primaryLabel={actionLabel}
onPrimaryClick={onActionClick}
/>
{/* Footer Section */}
<Stack pt={8} width="full">
<Stack height="1px" width="full" bg="border-gray" opacity={0.1} mb={8}>{null}</Stack>
<NotFoundHelpLinks links={helpLinks} />
</Stack>
</Stack>
</Card>
{/* Subtle Edge Details */}
<Stack
position="absolute"
top={0}
left={0}
right={0}
h="2px"
bg="primary-accent"
opacity={0.1}
>{null}</Stack>
<Stack
position="absolute"
bottom={0}
left={0}
right={0}
h="2px"
bg="primary-accent"
opacity={0.1}
>{null}</Stack>
</Stack>
<FooterSection>
<NotFoundHelpLinks links={helpLinks} />
</FooterSection>
</ErrorPageContainer>
);
}

View File

@@ -2,8 +2,8 @@
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Home, LifeBuoy, RefreshCw } from 'lucide-react';
import React from 'react';
interface RecoveryActionsProps {
onRetry: () => void;
@@ -18,19 +18,11 @@ interface RecoveryActionsProps {
*/
export function RecoveryActions({ onRetry, onHome }: RecoveryActionsProps) {
return (
<Stack
direction="row"
wrap
align="center"
justify="center"
gap={3}
fullWidth
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.75rem', flexWrap: 'wrap', width: '100%' }}>
<Button
variant="primary"
onClick={onRetry}
icon={<Icon icon={RefreshCw} size={4} />}
width="160px"
>
Retry Session
</Button>
@@ -38,7 +30,6 @@ export function RecoveryActions({ onRetry, onHome }: RecoveryActionsProps) {
variant="secondary"
onClick={onHome}
icon={<Icon icon={Home} size={4} />}
width="160px"
>
Return to Pits
</Button>
@@ -49,10 +40,9 @@ export function RecoveryActions({ onRetry, onHome }: RecoveryActionsProps) {
target="_blank"
rel="noopener noreferrer"
icon={<Icon icon={LifeBuoy} size={4} />}
width="160px"
>
Contact Support
</Button>
</Stack>
</div>
);
}

View File

@@ -3,9 +3,9 @@
import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import { AlertTriangle } from 'lucide-react';
import React from 'react';
interface ServerErrorPanelProps {
message?: string;
@@ -20,57 +20,53 @@ interface ServerErrorPanelProps {
*/
export function ServerErrorPanel({ message, incidentId }: ServerErrorPanelProps) {
return (
<Stack gap={6} align="center" fullWidth>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1.5rem', width: '100%' }}>
{/* Status Indicator */}
<Stack
p={4}
rounded="full"
bg="bg-warning-amber"
{...({ bgOpacity: 0.1 } as any)}
border
borderColor="border-warning-amber"
align="center"
justify="center"
<div
style={{
padding: '1rem',
borderRadius: '9999px',
backgroundColor: 'rgba(255, 190, 77, 0.1)',
border: '1px solid rgba(255, 190, 77, 0.3)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Icon icon={AlertTriangle} size={8} color="var(--warning-amber)" />
</Stack>
<Icon icon={AlertTriangle} size={8} intent="warning" />
</div>
{/* Primary Message */}
<Stack gap={2} align="center">
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.5rem' }}>
<Heading level={1} weight="bold">
CRITICAL_SYSTEM_FAILURE
</Heading>
<Text color="text-gray-400" textAlign="center" maxWidth="md">
<Text variant="low" align="center" style={{ maxWidth: '32rem' }}>
The application engine encountered an unrecoverable state.
Telemetry has been dispatched to engineering.
</Text>
</Stack>
</div>
{/* Technical Summary */}
<Card
variant="outline"
rounded="md"
p={4}
fullWidth
borderColor="border-white"
className="bg-graphite-black/20"
>
<Stack gap={2}>
<Text font="mono" size="sm" color="text-warning-amber" block>
STATUS: 500_INTERNAL_SERVER_ERROR
</Text>
{message && (
<Text font="mono" size="xs" color="text-gray-400" block>
EXCEPTION: {message}
<div style={{ width: '100%' }}>
<Card variant="outline">
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
<Text font="mono" size="sm" variant="warning" block>
STATUS: 500_INTERNAL_SERVER_ERROR
</Text>
)}
{incidentId && (
<Text font="mono" size="xs" color="text-gray-500" block>
INCIDENT_ID: {incidentId}
</Text>
)}
</Stack>
</Card>
</Stack>
{message && (
<Text font="mono" size="xs" variant="low" block>
EXCEPTION: {message}
</Text>
)}
{incidentId && (
<Text font="mono" size="xs" variant="low" block>
INCIDENT_ID: {incidentId}
</Text>
)}
</div>
</Card>
</div>
</div>
);
}