website refactor

This commit is contained in:
2026-01-18 16:18:18 +01:00
parent 0b301feb61
commit 13567d51af
329 changed files with 4701 additions and 4750 deletions

View File

@@ -2,11 +2,10 @@
import React from 'react';
import { AlertTriangle } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Icon } from '@/ui/Icon';
import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
interface AppErrorBoundaryViewProps {
title: string;
@@ -24,7 +23,7 @@ export function AppErrorBoundaryView({ title, description, children }: AppErrorB
return (
<Stack gap={6} align="center" fullWidth>
{/* Header Icon */}
<Box
<Stack
p={4}
rounded="full"
bg="bg-warning-amber"
@@ -33,7 +32,7 @@ export function AppErrorBoundaryView({ title, description, children }: AppErrorB
borderColor="border-warning-amber"
>
<Icon icon={AlertTriangle} size={8} color="var(--warning-amber)" />
</Box>
</Stack>
{/* Typography */}
<Stack gap={2} align="center">

View File

@@ -5,14 +5,14 @@ import { X, RefreshCw, Copy, Terminal, Activity, AlertTriangle } from 'lucide-re
import { ApiError } from '@/lib/api/base/ApiError';
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Icon } from '@/ui/Icon';
import { Badge } from '@/ui/Badge';
import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading';
import { Surface } from '@/ui/Surface';
import { Card } from '@/ui/Card';
import { Grid } from '@/ui/Grid';
interface DevErrorPanelProps {
error: ApiError;
@@ -100,7 +100,7 @@ export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
const reliability = connectionMonitor.getReliability();
return (
<Box
<Stack
position="fixed"
inset="0"
zIndex={50}
@@ -108,18 +108,18 @@ export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
bg="bg-deep-graphite"
p={4}
>
<Box maxWidth="6xl" mx="auto">
<Stack maxWidth="6xl" mx="auto" fullWidth>
<Stack gap={4}>
{/* Header */}
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="lg" p={4} display="flex" alignItems="center" justifyContent="between">
<Box display="flex" alignItems="center" gap={3}>
<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>
</Box>
<Box display="flex" gap={2}>
</Stack>
<Stack direction="row" gap={2}>
<Button
variant="secondary"
onClick={copyToClipboard}
@@ -134,141 +134,141 @@ export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
>
Close
</Button>
</Box>
</Box>
</Stack>
</Stack>
{/* Error Details */}
<Box display="grid" gridCols={{ base: 1, lg: 2 }} gap={4}>
<Grid cols={1} lgCols={2} gap={4}>
<Stack gap={4}>
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2} display="flex" alignItems="center" gap={2}>
<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>
</Box>
<Box p={4}>
<Stack gap={2} fontSize="0.75rem">
<Box display="grid" gridCols={3} gap={2}>
</Stack>
<Stack p={4}>
<Stack gap={2} style={{ fontSize: '0.75rem' }}>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Type:</Text>
<Text colSpan={2} color="text-red-400" weight="bold">{error.type}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color="text-gray-300">{error.message}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color="text-primary-blue">{error.context.endpoint || 'N/A'}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color="text-warning-amber">{error.context.method || 'N/A'}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2}>{error.context.statusCode || 'N/A'}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2}>{error.context.retryCount || 0}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<Text className="col-span-2">{error.context.retryCount || 0}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Timestamp:</Text>
<Text colSpan={2} color="text-gray-500">{error.context.timestamp}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color={error.isRetryable() ? 'text-performance-green' : 'text-red-400'}>
<Text className="col-span-2" color={error.isRetryable() ? 'text-performance-green' : 'text-red-400'}>
{error.isRetryable() ? 'Yes' : 'No'}
</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Connectivity:</Text>
<Text colSpan={2} color={error.isConnectivityIssue() ? 'text-red-400' : 'text-performance-green'}>
<Text className="col-span-2" color={error.isConnectivityIssue() ? 'text-red-400' : 'text-performance-green'}>
{error.isConnectivityIssue() ? 'Yes' : 'No'}
</Text>
</Box>
</Grid>
{error.context.troubleshooting && (
<Box display="grid" gridCols={3} gap={2}>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Troubleshoot:</Text>
<Text colSpan={2} color="text-warning-amber">{error.context.troubleshooting}</Text>
</Box>
<Text className="col-span-2" color="text-warning-amber">{error.context.troubleshooting}</Text>
</Grid>
)}
</Stack>
</Box>
</Surface>
</Stack>
</Card>
{/* Connection Status */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2} display="flex" alignItems="center" gap={2}>
<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>
</Box>
<Box p={4}>
<Stack gap={2} fontSize="0.75rem">
<Box display="grid" gridCols={3} gap={2}>
</Stack>
<Stack p={4}>
<Stack gap={2} style={{ fontSize: '0.75rem' }}>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Status:</Text>
<Text colSpan={2} weight="bold" color={
<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>
</Box>
<Box display="grid" gridCols={3} gap={2}>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Reliability:</Text>
<Text colSpan={2}>{reliability.toFixed(2)}%</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<Text className="col-span-2">{reliability.toFixed(2)}%</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Total Requests:</Text>
<Text colSpan={2}>{connectionStatus.totalRequests}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<Text className="col-span-2">{connectionStatus.totalRequests}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Successful:</Text>
<Text colSpan={2} color="text-performance-green">{connectionStatus.successfulRequests}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color="text-red-400">{connectionStatus.failedRequests}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2}>{connectionStatus.consecutiveFailures}</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<Text className="col-span-2">{connectionStatus.consecutiveFailures}</Text>
</Grid>
<Grid cols={3} gap={2}>
<Text color="text-gray-500">Avg Response:</Text>
<Text colSpan={2}>{connectionStatus.averageResponseTime.toFixed(2)}ms</Text>
</Box>
<Box display="grid" gridCols={3} gap={2}>
<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 colSpan={2} color="text-gray-500">
<Text className="col-span-2" color="text-gray-500">
{connectionStatus.lastCheck?.toLocaleTimeString() || 'Never'}
</Text>
</Box>
</Grid>
</Stack>
</Box>
</Surface>
</Stack>
</Card>
</Stack>
{/* Right Column */}
<Stack gap={4}>
{/* Circuit Breakers */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2} display="flex" alignItems="center" gap={2}>
<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>
</Box>
<Box p={4}>
</Stack>
<Stack p={4}>
{Object.keys(circuitBreakers).length === 0 ? (
<Box textAlign="center" py={4}>
<Stack align="center" py={4}>
<Text color="text-gray-500">No circuit breakers active</Text>
</Box>
</Stack>
) : (
<Stack gap={2} maxHeight="12rem" overflow="auto" fontSize="0.75rem">
<Stack gap={2} maxHeight="12rem" overflow="auto" style={{ fontSize: '0.75rem' }}>
{Object.entries(circuitBreakers).map(([endpoint, status]) => (
<Box key={endpoint} display="flex" alignItems="center" justifyContent="between" p={2} bg="bg-deep-graphite" rounded="md" border borderColor="border-charcoal-outline">
<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>
<Box px={2} py={1} rounded="sm" bg={
<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'
@@ -280,21 +280,21 @@ export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
}>
{status.state}
</Text>
</Box>
<Text color="text-gray-500" ml={2}>{status.failures} failures</Text>
</Box>
</Stack>
<Text color="text-gray-500" className="ml-2">{status.failures} failures</Text>
</Stack>
))}
</Stack>
)}
</Box>
</Surface>
</Stack>
</Card>
{/* Actions */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2}>
<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>
</Box>
<Box p={4}>
</Stack>
<Stack p={4}>
<Stack gap={2}>
<Button
variant="primary"
@@ -324,59 +324,59 @@ export function DevErrorPanel({ error, onReset }: DevErrorPanelProps) {
Reset Connection Stats
</Button>
</Stack>
</Box>
</Surface>
</Stack>
</Card>
{/* Quick Fixes */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2}>
<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>
</Box>
<Box p={4}>
<Stack gap={2} fontSize="0.75rem">
</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}>
<Box as="li"><Text color="text-gray-300">Check API server is running</Text></Box>
<Box as="li"><Text color="text-gray-300">Verify CORS configuration</Text></Box>
<Box as="li"><Text color="text-gray-300">Check environment variables</Text></Box>
<Box as="li"><Text color="text-gray-300">Review network connectivity</Text></Box>
<Box as="li"><Text color="text-gray-300">Check API rate limits</Text></Box>
<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>
</Box>
</Surface>
</Stack>
</Card>
{/* Raw Error */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2}>
<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>
</Box>
<Box p={4}>
<Box as="pre" p={2} bg="bg-deep-graphite" rounded="md" overflow="auto" maxHeight="8rem" fontSize="0.75rem" color="text-gray-400">
</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)}
</Box>
</Box>
</Surface>
</Stack>
</Stack>
</Card>
</Stack>
</Box>
</Grid>
{/* Console Output */}
<Surface variant="muted" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
<Box bg="bg-charcoal-outline" px={4} py={2} display="flex" alignItems="center" gap={2}>
<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>
</Box>
<Box p={4} bg="bg-deep-graphite" fontSize="0.75rem">
</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>
</Box>
</Surface>
</Stack>
</Card>
</Stack>
</Box>
</Box>
</Stack>
</Stack>
);
}

View File

@@ -15,9 +15,8 @@ import {
} from 'lucide-react';
import { parseApiError, getErrorSeverity, isRetryable, isConnectivityError } from '@/lib/utils/errorUtils';
import { ApiError } from '@/lib/api/base/ApiError';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton';
import { Button } from '@/ui/Button';
@@ -66,13 +65,13 @@ export function EnhancedFormError({
const color = getColor();
return (
<Box
<Stack
as={motion.div}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
>
<Box
<Stack
bg={`bg-${color}-500/10`}
border
borderColor={`border-${color}-500/30`}
@@ -80,18 +79,18 @@ export function EnhancedFormError({
overflow="hidden"
>
{/* Main Error Message */}
<Box p={4} display="flex" alignItems="start" gap={3}>
<Box color={`text-${color}-400`} flexShrink={0} mt={0.5}>
<Stack p={4} display="flex" alignItems="start" gap={3}>
<Stack color={`text-${color}-400`} flexShrink={0} mt={0.5}>
<Icon icon={getIcon()} size={5} />
</Box>
</Stack>
<Box flexGrow={1} minWidth="0">
<Box display="flex" alignItems="center" justifyContent="between" gap={2}>
<Stack flexGrow={1} minWidth="0">
<Stack display="flex" alignItems="center" justifyContent="between" gap={2}>
<Text size="sm" weight="medium" color={`text-${color}-200`}>
{parsed.userMessage}
</Text>
<Box display="flex" alignItems="center" gap={2}>
<Stack display="flex" alignItems="center" gap={2}>
{retryable && onRetry && (
<IconButton
icon={RefreshCw}
@@ -121,8 +120,8 @@ export function EnhancedFormError({
title="Toggle technical details"
/>
)}
</Box>
</Box>
</Stack>
</Stack>
{/* Validation Errors List */}
{parsed.isValidationError && parsed.validationErrors.length > 0 && (
@@ -136,31 +135,31 @@ export function EnhancedFormError({
)}
{/* Action Hint */}
<Box mt={2}>
<Stack mt={2}>
<Text size="xs" color="text-gray-400">
{connectivity && "Check your internet connection and try again"}
{parsed.isValidationError && "Please review your input and try again"}
{retryable && !connectivity && !parsed.isValidationError && "Please try again in a moment"}
</Text>
</Box>
</Box>
</Box>
</Stack>
</Stack>
</Stack>
{/* Developer Details */}
<AnimatePresence>
{showDetails && (
<Box
<Stack
as={motion.div}
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
>
<Box borderTop borderColor={`border-${color}-500/20`} bg="bg-black/20" p={4}>
<Stack borderTop borderColor={`border-${color}-500/20`} bg="bg-black/20" p={4}>
<Stack gap={3} fontSize="0.75rem">
<Box display="flex" alignItems="center" gap={2} color="text-gray-400">
<Stack display="flex" alignItems="center" gap={2} color="text-gray-400">
<Icon icon={Bug} size={3} />
<Text weight="semibold">Developer Details</Text>
</Box>
</Stack>
<Stack gap={1}>
<Text color="text-gray-500">Error Type:</Text>
@@ -186,9 +185,9 @@ export function EnhancedFormError({
</Stack>
)}
<Box pt={2} borderTop borderColor="border-charcoal-outline/50">
<Stack pt={2} borderTop borderColor="border-charcoal-outline/50">
<Text color="text-gray-500" block mb={1}>Quick Actions:</Text>
<Box display="flex" gap={2}>
<Stack display="flex" gap={2}>
{retryable && onRetry && (
<Button
variant="secondary"
@@ -216,15 +215,15 @@ export function EnhancedFormError({
>
Log to Console
</Button>
</Box>
</Box>
</Stack>
</Stack>
</Stack>
</Box>
</Box>
</Stack>
</Stack>
)}
</AnimatePresence>
</Box>
</Box>
</Stack>
</Stack>
);
}
@@ -248,21 +247,21 @@ export function FormErrorSummary({
};
return (
<Box
<Stack
as={motion.div}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
>
<Box bg="bg-red-500/10" border borderColor="border-red-500/30" rounded="lg" p={3} display="flex" alignItems="start" gap={2}>
<Stack bg="bg-red-500/10" border borderColor="border-red-500/30" rounded="lg" p={3} display="flex" alignItems="start" gap={2}>
<Icon icon={AlertCircle} size={4} color="rgb(239, 68, 68)" mt={0.5} />
<Box flexGrow={1} minWidth="0">
<Box display="flex" alignItems="center" justifyContent="between" gap={2}>
<Box>
<Stack flexGrow={1} minWidth="0">
<Stack display="flex" alignItems="center" justifyContent="between" gap={2}>
<Stack>
<Text size="sm" weight="medium" color="text-red-200" block>{summary.title}</Text>
<Text size="xs" color="text-red-300/80" block mt={0.5}>{summary.description}</Text>
<Text size="xs" color="text-gray-400" block mt={1}>{summary.action}</Text>
</Box>
</Stack>
{onDismiss && (
<IconButton
icon={X}
@@ -272,9 +271,9 @@ export function FormErrorSummary({
color="rgb(239, 68, 68)"
/>
)}
</Box>
</Box>
</Box>
</Box>
</Stack>
</Stack>
</Stack>
</Stack>
);
}

View File

@@ -21,9 +21,8 @@ import {
Zap,
Terminal
} from 'lucide-react';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton';
import { Badge } from '@/ui/Badge';
@@ -162,7 +161,7 @@ export function ErrorAnalyticsDashboard({
if (!isExpanded) {
return (
<Box position="fixed" bottom="4" left="4" zIndex={50}>
<Stack position="fixed" bottom="4" left="4" zIndex={50}>
<IconButton
icon={Activity}
onClick={() => setIsExpanded(true)}
@@ -171,12 +170,12 @@ export function ErrorAnalyticsDashboard({
size="lg"
color="rgb(239, 68, 68)"
/>
</Box>
</Stack>
);
}
return (
<Box
<Stack
position="fixed"
bottom="4"
left="4"
@@ -193,8 +192,8 @@ export function ErrorAnalyticsDashboard({
maxHeight="80vh"
>
{/* Header */}
<Box display="flex" alignItems="center" justifyContent="between" px={4} py={3} bg="bg-iron-gray/50" borderBottom borderColor="border-charcoal-outline">
<Box display="flex" alignItems="center" gap={2}>
<Stack display="flex" alignItems="center" justifyContent="between" px={4} py={3} bg="bg-iron-gray/50" borderBottom borderColor="border-charcoal-outline">
<Stack display="flex" alignItems="center" gap={2}>
<Icon icon={Activity} size={4} color="rgb(239, 68, 68)" />
<Text size="sm" weight="semibold" color="text-white">Error Analytics</Text>
{isDev && (
@@ -202,8 +201,8 @@ export function ErrorAnalyticsDashboard({
DEV
</Badge>
)}
</Box>
<Box display="flex" alignItems="center" gap={1}>
</Stack>
<Stack display="flex" alignItems="center" gap={1}>
<IconButton
icon={RefreshCw}
onClick={updateStatsManual}
@@ -218,18 +217,18 @@ export function ErrorAnalyticsDashboard({
size="sm"
title="Minimize"
/>
</Box>
</Box>
</Stack>
</Stack>
{/* Tabs */}
<Box display="flex" borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/30">
<Stack display="flex" borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/30">
{[
{ id: 'errors', label: 'Errors', icon: AlertTriangle },
{ id: 'api', label: 'API', icon: Globe },
{ id: 'environment', label: 'Env', icon: Cpu },
{ id: 'raw', label: 'Raw', icon: FileText },
].map(tab => (
<Box
<Stack
key={tab.id}
as="button"
type="button"
@@ -256,12 +255,12 @@ export function ErrorAnalyticsDashboard({
>
{tab.label}
</Text>
</Box>
</Stack>
))}
</Box>
</Stack>
{/* Content */}
<Box flexGrow={1} overflow="auto" p={4}>
<Stack flexGrow={1} overflow="auto" p={4}>
<Stack gap={4}>
{/* Search Bar */}
{selectedTab === 'errors' && (
@@ -278,53 +277,53 @@ export function ErrorAnalyticsDashboard({
{selectedTab === 'errors' && stats && (
<Stack gap={4}>
{/* Error Summary */}
<Box display="grid" gridCols={2} gap={2}>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="grid" gridCols={2} gap={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Text size="xs" color="text-gray-500" block>Total Errors</Text>
<Text size="xl" weight="bold" color="text-red-400">{stats.totalErrors}</Text>
</Box>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
</Stack>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Text size="xs" color="text-gray-500" block>Error Types</Text>
<Text size="xl" weight="bold" color="text-warning-amber">
{Object.keys(stats.errorsByType).length}
</Text>
</Box>
</Box>
</Stack>
</Stack>
{/* Error Types Breakdown */}
{Object.keys(stats.errorsByType).length > 0 && (
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Bug} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Error Types</Text>
</Box>
</Stack>
<Stack gap={1} maxHeight="8rem" overflow="auto">
{Object.entries(stats.errorsByType).map(([type, count]) => (
<Box key={type} display="flex" justifyContent="between">
<Stack key={type} display="flex" justifyContent="between">
<Text size="xs" color="text-gray-300">{type}</Text>
<Text size="xs" color="text-red-400" font="mono">{count}</Text>
</Box>
</Stack>
))}
</Stack>
</Box>
</Stack>
)}
{/* Recent Errors */}
{filteredRecentErrors.length > 0 && (
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={AlertTriangle} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Recent Errors</Text>
</Box>
</Stack>
<Stack gap={2} maxHeight="16rem" overflow="auto">
{filteredRecentErrors.map((error, idx) => (
<Box key={idx} bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="md" p={2}>
<Box display="flex" justifyContent="between" alignItems="start" gap={2} mb={1}>
<Stack key={idx} bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="md" p={2}>
<Stack display="flex" justifyContent="between" alignItems="start" gap={2} mb={1}>
<Text size="xs" font="mono" weight="bold" color="text-red-400" truncate>{error.type}</Text>
<Text size="xs" color="text-gray-500" fontSize="10px">
{new Date(error.timestamp).toLocaleTimeString()}
</Text>
</Box>
</Stack>
<Text size="xs" color="text-gray-300" block mb={1}>{error.message}</Text>
<Button
variant="ghost"
@@ -336,28 +335,28 @@ export function ErrorAnalyticsDashboard({
>
<Text size="xs" color="text-gray-500" fontSize="10px">Copy Details</Text>
</Button>
</Box>
</Stack>
))}
</Stack>
</Box>
</Stack>
)}
{/* Error Timeline */}
{stats.errorsByTime.length > 0 && (
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Clock} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Last 10 Minutes</Text>
</Box>
</Stack>
<Stack gap={1} maxHeight="8rem" overflow="auto">
{stats.errorsByTime.map((point, idx) => (
<Box key={idx} display="flex" justifyContent="between">
<Stack key={idx} display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">{point.time}</Text>
<Text size="xs" color="text-red-400" font="mono">{point.count} errors</Text>
</Box>
</Stack>
))}
</Stack>
</Box>
</Stack>
)}
</Stack>
)}
@@ -366,57 +365,57 @@ export function ErrorAnalyticsDashboard({
{selectedTab === 'api' && stats && (
<Stack gap={4}>
{/* API Summary */}
<Box display="grid" gridCols={2} gap={2}>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="grid" gridCols={2} gap={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Text size="xs" color="text-gray-500" block>Total Requests</Text>
<Text size="xl" weight="bold" color="text-primary-blue">{stats.apiStats.totalRequests}</Text>
</Box>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
</Stack>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Text size="xs" color="text-gray-500" block>Success Rate</Text>
<Text size="xl" weight="bold" color="text-performance-green">
{formatPercentage(stats.apiStats.successful, stats.apiStats.totalRequests)}
</Text>
</Box>
</Box>
</Stack>
</Stack>
{/* API Stats */}
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Globe} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">API Metrics</Text>
</Box>
</Stack>
<Stack gap={1}>
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Successful</Text>
<Text size="xs" color="text-performance-green" font="mono">{stats.apiStats.successful}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Failed</Text>
<Text size="xs" color="text-red-400" font="mono">{stats.apiStats.failed}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Avg Duration</Text>
<Text size="xs" color="text-warning-amber" font="mono">{formatDuration(stats.apiStats.averageDuration)}</Text>
</Box>
</Stack>
</Stack>
</Box>
</Stack>
{/* Slowest Requests */}
{stats.apiStats.slowestRequests.length > 0 && (
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Zap} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Slowest Requests</Text>
</Box>
</Stack>
<Stack gap={1} maxHeight="10rem" overflow="auto">
{stats.apiStats.slowestRequests.map((req, idx) => (
<Box key={idx} display="flex" justifyContent="between" bg="bg-deep-graphite" p={1.5} rounded="sm" border borderColor="border-charcoal-outline">
<Stack key={idx} display="flex" justifyContent="between" bg="bg-deep-graphite" p={1.5} rounded="sm" border borderColor="border-charcoal-outline">
<Text size="xs" color="text-gray-300" truncate flexGrow={1}>{req.url}</Text>
<Text size="xs" color="text-red-400" font="mono" ml={2}>{formatDuration(req.duration)}</Text>
</Box>
</Stack>
))}
</Stack>
</Box>
</Stack>
)}
</Stack>
)}
@@ -425,103 +424,103 @@ export function ErrorAnalyticsDashboard({
{selectedTab === 'environment' && stats && (
<Stack gap={4}>
{/* Environment Info */}
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Cpu} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Environment</Text>
</Box>
</Stack>
<Stack gap={1}>
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Node Environment</Text>
<Text size="xs" font="mono" weight="bold" color={stats.environment.mode === 'development' ? 'text-performance-green' : 'text-warning-amber'}>
{stats.environment.mode}
</Text>
</Box>
</Stack>
{stats.environment.version && (
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Version</Text>
<Text size="xs" color="text-gray-300" font="mono">{stats.environment.version}</Text>
</Box>
</Stack>
)}
{stats.environment.buildTime && (
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Build Time</Text>
<Text size="xs" color="text-gray-500" font="mono" fontSize="10px">{stats.environment.buildTime}</Text>
</Box>
</Stack>
)}
</Stack>
</Box>
</Stack>
{/* Browser Info */}
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Globe} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Browser</Text>
</Box>
</Stack>
<Stack gap={1}>
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">User Agent</Text>
<Text size="xs" color="text-gray-300" truncate maxWidth="150px">{navigator.userAgent}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Language</Text>
<Text size="xs" color="text-gray-300" font="mono">{navigator.language}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Platform</Text>
<Text size="xs" color="text-gray-300" font="mono">{navigator.platform}</Text>
</Box>
</Stack>
</Stack>
</Box>
</Stack>
{/* Performance */}
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Activity} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Performance</Text>
</Box>
</Stack>
<Stack gap={1}>
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Viewport</Text>
<Text size="xs" color="text-gray-300" font="mono">{window.innerWidth}x{window.innerHeight}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Screen</Text>
<Text size="xs" color="text-gray-300" font="mono">{window.screen.width}x{window.screen.height}</Text>
</Box>
</Stack>
{perf?.memory && (
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">JS Heap</Text>
<Text size="xs" color="text-gray-300" font="mono">
{formatMemory(perf.memory.usedJSHeapSize)}
</Text>
</Box>
</Stack>
)}
</Stack>
</Box>
</Stack>
{/* Connection */}
{nav?.connection && (
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Zap} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Network</Text>
</Box>
</Stack>
<Stack gap={1}>
<Box display="flex" justifyContent="between">
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Type</Text>
<Text size="xs" color="text-gray-300" font="mono">{nav.connection.effectiveType}</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">Downlink</Text>
<Text size="xs" color="text-gray-300" font="mono">{nav.connection.downlink}Mbps</Text>
</Box>
<Box display="flex" justifyContent="between">
</Stack>
<Stack display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">RTT</Text>
<Text size="xs" color="text-gray-300" font="mono">{nav.connection.rtt}ms</Text>
</Box>
</Stack>
</Stack>
</Box>
</Stack>
)}
</Stack>
)}
@@ -529,12 +528,12 @@ export function ErrorAnalyticsDashboard({
{/* Raw Data Tab */}
{selectedTab === 'raw' && stats && (
<Stack gap={3}>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={FileText} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Export Options</Text>
</Box>
<Box display="flex" gap={2}>
</Stack>
<Stack display="flex" gap={2}>
<Button
variant="primary"
onClick={exportAllData}
@@ -553,14 +552,14 @@ export function ErrorAnalyticsDashboard({
>
Copy Stats
</Button>
</Box>
</Box>
</Stack>
</Stack>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Trash2} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Maintenance</Text>
</Box>
</Stack>
<Button
variant="danger"
onClick={clearAllData}
@@ -570,30 +569,30 @@ export function ErrorAnalyticsDashboard({
>
Clear All Logs
</Button>
</Box>
</Stack>
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Stack bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="md" p={3}>
<Stack display="flex" alignItems="center" gap={2} mb={2}>
<Icon icon={Terminal} size={3} color="rgb(156, 163, 175)" />
<Text size="xs" weight="semibold" color="text-gray-400">Console Commands</Text>
</Box>
</Stack>
<Stack gap={1} fontSize="10px">
<Text color="text-gray-400" font="mono"> window.__GRIDPILOT_GLOBAL_HANDLER__</Text>
<Text color="text-gray-400" font="mono"> window.__GRIDPILOT_API_LOGGER__</Text>
<Text color="text-gray-400" font="mono"> window.__GRIDPILOT_REACT_ERRORS__</Text>
</Stack>
</Box>
</Stack>
</Stack>
)}
</Stack>
</Box>
</Stack>
{/* Footer */}
<Box px={4} py={2} bg="bg-iron-gray/30" borderTop borderColor="border-charcoal-outline" display="flex" justifyContent="between" alignItems="center">
<Stack px={4} py={2} bg="bg-iron-gray/30" borderTop borderColor="border-charcoal-outline" display="flex" justifyContent="between" alignItems="center">
<Text size="xs" color="text-gray-500" fontSize="10px">Auto-refresh: {refreshInterval}ms</Text>
{copied && <Text size="xs" color="text-performance-green" fontSize="10px">Copied!</Text>}
</Box>
</Box>
</Stack>
</Stack>
);
}

View File

@@ -2,12 +2,11 @@
import React, { useState } from 'react';
import { ChevronDown, ChevronUp, Copy, Terminal } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Surface } from '@/ui/Surface';
import { Card } from '@/ui/Card';
interface ErrorDetailsProps {
error: Error & { digest?: string };
@@ -43,16 +42,15 @@ export function ErrorDetails({ error }: ErrorDetailsProps) {
return (
<Stack gap={4} fullWidth pt={4} borderTop borderColor="border-white">
<Box
<Stack
as="button"
onClick={() => setShowDetails(!showDetails)}
display="flex"
alignItems="center"
justifyContent="center"
direction="row"
align="center"
justify="center"
gap={2}
color="text-gray-500"
hoverTextColor="text-gray-300"
transition
className="transition-all hover:text-gray-300"
>
<Icon icon={Terminal} size={3} />
<Text
@@ -65,29 +63,27 @@ export function ErrorDetails({ error }: ErrorDetailsProps) {
{showDetails ? 'Hide Technical Logs' : 'Show Technical Logs'}
</Text>
{showDetails ? <Icon icon={ChevronUp} size={3} /> : <Icon icon={ChevronDown} size={3} />}
</Box>
</Stack>
{showDetails && (
<Stack gap={3}>
<Surface
variant="dark"
<Card
variant="outline"
rounded="md"
padding={4}
p={4}
fullWidth
maxHeight="48"
overflow="auto"
border
borderColor="border-white"
bgOpacity={0.4}
hideScrollbar={false}
className="bg-graphite-black/40"
>
<Text font="mono" size="xs" color="text-gray-500" block leading="relaxed">
{error.stack || 'No stack trace available'}
{error.digest && `\n\nDigest: ${error.digest}`}
</Text>
</Surface>
</Card>
<Box display="flex" justifyContent="end">
<Stack direction="row" justify="end">
<Button
variant="secondary"
size="sm"
@@ -98,7 +94,7 @@ export function ErrorDetails({ error }: ErrorDetailsProps) {
>
{copied ? 'Copied to Clipboard' : 'Copy Error Details'}
</Button>
</Box>
</Stack>
</Stack>
)}
</Stack>

View File

@@ -2,12 +2,11 @@
import React, { useState } from 'react';
import { Copy, ChevronDown, ChevronUp } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Surface } from '@/ui/Surface';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
interface ErrorDetailsBlockProps {
error: Error & { digest?: string };
@@ -42,53 +41,48 @@ export function ErrorDetailsBlock({ error }: ErrorDetailsBlockProps) {
};
return (
<Stack gap={4} fullWidth pt={4} borderTop borderColor="border-white" bgOpacity={0.1}>
<Box
<Stack gap={4} fullWidth pt={4} borderTop borderColor="border-white">
<Stack
as="button"
onClick={() => setShowDetails(!showDetails)}
display="flex"
alignItems="center"
justifyContent="center"
direction="row"
align="center"
justify="center"
gap={2}
transition
className="transition-all"
>
<Text
size="xs"
color="text-gray-500"
hoverTextColor="text-gray-300"
className="hover:text-gray-300 flex items-center gap-2"
uppercase
letterSpacing="widest"
weight="medium"
display="flex"
alignItems="center"
gap={2}
>
{showDetails ? <Icon icon={ChevronUp} size={3} /> : <Icon icon={ChevronDown} size={3} />}
{showDetails ? 'Hide Technical Logs' : 'Show Technical Logs'}
</Text>
</Box>
</Stack>
{showDetails && (
<Stack gap={3}>
<Surface
variant="dark"
<Card
variant="outline"
rounded="md"
padding={4}
p={4}
fullWidth
maxHeight="48"
overflow="auto"
border
borderColor="border-white"
bgOpacity={0.4}
hideScrollbar={false}
className="bg-graphite-black/40"
>
<Text font="mono" size="xs" color="text-gray-500" block leading="relaxed">
{error.stack || 'No stack trace available'}
{error.digest && `\n\nDigest: ${error.digest}`}
</Text>
</Surface>
</Card>
<Box display="flex" justifyContent="end">
<Stack direction="row" justify="end">
<Button
variant="secondary"
size="sm"
@@ -99,7 +93,7 @@ export function ErrorDetailsBlock({ error }: ErrorDetailsBlockProps) {
>
{copied ? 'Copied to Clipboard' : 'Copy Error Details'}
</Button>
</Box>
</Stack>
</Stack>
)}
</Stack>

View File

@@ -2,9 +2,9 @@
import React from 'react';
import { RefreshCw, Home } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
interface ErrorRecoveryActionsProps {
onRetry: () => void;
@@ -19,11 +19,11 @@ interface ErrorRecoveryActionsProps {
*/
export function ErrorRecoveryActions({ onRetry, onHome }: ErrorRecoveryActionsProps) {
return (
<Box
display="flex"
flexWrap="wrap"
alignItems="center"
justifyContent="center"
<Stack
direction="row"
wrap
align="center"
justify="center"
gap={3}
fullWidth
>
@@ -43,6 +43,6 @@ export function ErrorRecoveryActions({ onRetry, onHome }: ErrorRecoveryActionsPr
>
Return to Pits
</Button>
</Box>
</Stack>
);
}

View File

@@ -1,13 +1,13 @@
'use client';
import React from 'react';
import { Box } from '@/ui/Box';
import { Surface } from '@/ui/Surface';
import { Glow } from '@/ui/Glow';
import { Text } from '@/ui/Text';
import { AppErrorBoundaryView } from './AppErrorBoundaryView';
import { ErrorRecoveryActions } from './ErrorRecoveryActions';
import { ErrorDetailsBlock } from './ErrorDetailsBlock';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
interface ErrorScreenProps {
error: Error & { digest?: string };
@@ -23,13 +23,12 @@ interface ErrorScreenProps {
*/
export function ErrorScreen({ error, reset, onHome }: ErrorScreenProps) {
return (
<Box
<Stack
as="main"
minHeight="screen"
fullWidth
display="flex"
alignItems="center"
justifyContent="center"
align="center"
justify="center"
bg="bg-deep-graphite"
position="relative"
overflow="hidden"
@@ -38,43 +37,40 @@ export function ErrorScreen({ error, reset, onHome }: ErrorScreenProps) {
{/* Background Accents */}
<Glow color="primary" size="xl" position="center" opacity={0.05} />
<Surface
variant="glass"
border
<Card
variant="outline"
rounded="lg"
padding={8}
p={8}
maxWidth="2xl"
fullWidth
position="relative"
zIndex={10}
shadow="xl"
borderColor="border-white"
bgOpacity={0.05}
className="bg-white/5 backdrop-blur-md"
>
<AppErrorBoundaryView
title="System Malfunction"
description="The application encountered an unexpected state. Our telemetry has logged the incident."
>
{/* Error Message Summary */}
<Surface
variant="dark"
<Card
variant="outline"
rounded="md"
padding={4}
p={4}
fullWidth
border
borderColor="border-white"
bgOpacity={0.2}
className="bg-graphite-black/20"
>
<Text font="mono" size="sm" color="text-warning-amber" block>
{error.message || 'Unknown execution error'}
</Text>
</Surface>
</Card>
<ErrorRecoveryActions onRetry={reset} onHome={onHome} />
<ErrorDetailsBlock error={error} />
</AppErrorBoundaryView>
</Surface>
</Box>
</Card>
</Stack>
);
}

View File

@@ -1,15 +1,14 @@
'use client';
import React from 'react';
import { Box } from '@/ui/Box';
import { Surface } from '@/ui/Surface';
import { Glow } from '@/ui/Glow';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { AlertTriangle, RefreshCw, Home, Terminal } from 'lucide-react';
import { Button } from '@/ui/Button';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
interface GlobalErrorScreenProps {
error: Error & { digest?: string };
@@ -25,13 +24,12 @@ interface GlobalErrorScreenProps {
*/
export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenProps) {
return (
<Box
<Stack
as="main"
minHeight="screen"
fullWidth
display="flex"
alignItems="center"
justifyContent="center"
align="center"
justify="center"
bg="bg-base-black"
position="relative"
overflow="hidden"
@@ -40,28 +38,27 @@ export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenPro
{/* Background Accents - Subtle telemetry vibe */}
<Glow color="primary" size="xl" position="center" opacity={0.03} />
<Surface
variant="dark"
border
<Card
variant="outline"
rounded="none"
padding={0}
p={0}
maxWidth="2xl"
fullWidth
position="relative"
zIndex={10}
borderColor="border-white"
bgOpacity={0.1}
className="bg-graphite-black/10"
>
{/* System Status Header */}
<Box
<Stack
borderBottom
borderColor="border-white"
bgOpacity={0.05}
px={6}
py={4}
display="flex"
alignItems="center"
justifyContent="space-between"
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)" />
@@ -74,9 +71,9 @@ export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenPro
<Text font="mono" size="xs" color="text-gray-500" uppercase>
Status: Critical
</Text>
</Box>
</Stack>
<Box p={8}>
<Stack p={8}>
<Stack gap={8}>
{/* Fault Description */}
<Stack gap={4}>
@@ -91,24 +88,24 @@ export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenPro
{/* Recovery Actions */}
<RecoveryActions onRetry={reset} onHome={onHome} />
</Stack>
</Box>
</Stack>
{/* Footer / Metadata */}
<Box
<Stack
borderTop
borderColor="border-white"
bgOpacity={0.05}
px={6}
py={3}
display="flex"
justifyContent="end"
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>
</Box>
</Surface>
</Box>
</Stack>
</Card>
</Stack>
);
}
@@ -119,22 +116,21 @@ export function GlobalErrorScreen({ error, reset, onHome }: GlobalErrorScreenPro
*/
function SystemStatusPanel({ error }: { error: Error & { digest?: string } }) {
return (
<Surface
variant="dark"
<Card
variant="outline"
rounded="none"
padding={4}
p={4}
fullWidth
border
borderColor="border-white"
bgOpacity={0.2}
className="bg-graphite-black/20"
>
<Stack gap={3}>
<Box display="flex" alignItems="center" gap={2}>
<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">
Fault Log
</Text>
</Box>
</Stack>
<Text font="mono" size="sm" color="text-warning-amber" block>
{error.message || 'Unknown execution fault'}
</Text>
@@ -144,7 +140,7 @@ function SystemStatusPanel({ error }: { error: Error & { digest?: string } }) {
</Text>
)}
</Stack>
</Surface>
</Card>
);
}
@@ -155,10 +151,10 @@ function SystemStatusPanel({ error }: { error: Error & { digest?: string } }) {
*/
function RecoveryActions({ onRetry, onHome }: { onRetry: () => void; onHome: () => void }) {
return (
<Box
display="flex"
flexWrap="wrap"
alignItems="center"
<Stack
direction="row"
wrap
align="center"
gap={4}
fullWidth
>
@@ -180,6 +176,6 @@ function RecoveryActions({ onRetry, onHome }: { onRetry: () => void; onHome: ()
>
Return to Pits
</Button>
</Box>
</Stack>
);
}

View File

@@ -3,7 +3,6 @@
import React from 'react';
import { Stack } from '@/ui/Stack';
import { Button } from '@/ui/Button';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
interface NotFoundActionsProps {
@@ -35,7 +34,7 @@ export function NotFoundActions({ primaryLabel, onPrimaryClick }: NotFoundAction
onClick={() => window.history.back()}
>
<Stack direction="row" gap={2} align="center">
<Box
<Stack
width={2}
height={2}
rounded="full"

View File

@@ -3,7 +3,6 @@
import React from 'react';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Box } from '@/ui/Box';
interface NotFoundDiagnosticsProps {
errorCode: string;
@@ -18,7 +17,7 @@ interface NotFoundDiagnosticsProps {
export function NotFoundDiagnostics({ errorCode }: NotFoundDiagnosticsProps) {
return (
<Stack gap={3} align="center">
<Box
<Stack
px={3}
py={1}
border
@@ -36,7 +35,7 @@ export function NotFoundDiagnostics({ errorCode }: NotFoundDiagnosticsProps) {
>
{errorCode}
</Text>
</Box>
</Stack>
<Text
size="xs"
color="text-gray-500"

View File

@@ -2,7 +2,6 @@
import React from 'react';
import { Stack } from '@/ui/Stack';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
interface NotFoundHelpLinksProps {
@@ -20,7 +19,7 @@ export function NotFoundHelpLinks({ links }: NotFoundHelpLinksProps) {
<Stack direction="row" gap={6} align="center" wrap center>
{links.map((link, index) => (
<React.Fragment key={link.href}>
<Box
<Stack
as="a"
href={link.href}
transition
@@ -36,9 +35,9 @@ export function NotFoundHelpLinks({ links }: NotFoundHelpLinksProps) {
>
{link.label}
</Text>
</Box>
</Stack>
{index < links.length - 1 && (
<Box width="1px" height="12px" bg="border-gray" opacity={0.5} />
<Stack width="1px" height="12px" bg="border-gray" opacity={0.5} />
)}
</React.Fragment>
))}

View File

@@ -1,14 +1,13 @@
'use client';
import React from 'react';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Surface } from '@/ui/Surface';
import { Glow } from '@/ui/Glow';
import { NotFoundActions } from './NotFoundActions';
import { NotFoundHelpLinks } from './NotFoundHelpLinks';
import { NotFoundDiagnostics } from './NotFoundDiagnostics';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
interface NotFoundScreenProps {
errorCode: string;
@@ -39,31 +38,31 @@ export function NotFoundScreen({
];
return (
<Box
<Stack
as="main"
minHeight="100vh"
display="flex"
alignItems="center"
justifyContent="center"
minHeight="screen"
align="center"
justify="center"
bg="graphite-black"
position="relative"
overflow="hidden"
fullWidth
>
{/* Background Glow Accent */}
<Glow color="primary" size="xl" opacity={0.1} position="center" />
<Surface
variant="glass"
border
padding={12}
<Card
variant="outline"
p={12}
rounded="none"
maxWidth="2xl"
fullWidth
mx={6}
position="relative"
zIndex={10}
className="bg-white/5 backdrop-blur-md"
>
<Stack gap={12} align="center" textAlign="center">
<Stack gap={12} align="center">
{/* Header Section */}
<Stack gap={4} align="center">
<NotFoundDiagnostics errorCode={errorCode} />
@@ -77,19 +76,20 @@ export function NotFoundScreen({
uppercase
block
leading="none"
textAlign="center"
>
{title}
</Text>
</Stack>
{/* Visual Separator */}
<Box width="full" height="1px" bg="primary-accent" opacity={0.3} position="relative" display="flex" alignItems="center" justifyContent="center">
<Box
width={3}
height={3}
<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"
/>
</Box>
>{null}</Stack>
</Stack>
{/* Message Section */}
<Text
@@ -99,6 +99,7 @@ export function NotFoundScreen({
leading="relaxed"
block
weight="medium"
textAlign="center"
>
{message}
</Text>
@@ -110,32 +111,32 @@ export function NotFoundScreen({
/>
{/* Footer Section */}
<Box pt={8} width="full">
<Box height="1px" width="full" bg="border-gray" opacity={0.1} mb={8} />
<Stack pt={8} width="full">
<Stack height="1px" width="full" bg="border-gray" opacity={0.1} mb={8}>{null}</Stack>
<NotFoundHelpLinks links={helpLinks} />
</Box>
</Stack>
</Stack>
</Surface>
</Card>
{/* Subtle Edge Details */}
<Box
<Stack
position="absolute"
top={0}
left={0}
right={0}
height="2px"
h="2px"
bg="primary-accent"
opacity={0.1}
/>
<Box
>{null}</Stack>
<Stack
position="absolute"
bottom={0}
left={0}
right={0}
height="2px"
h="2px"
bg="primary-accent"
opacity={0.1}
/>
</Box>
>{null}</Stack>
</Stack>
);
}

View File

@@ -2,9 +2,9 @@
import React from 'react';
import { RefreshCw, Home, LifeBuoy } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
interface RecoveryActionsProps {
onRetry: () => void;
@@ -19,11 +19,11 @@ interface RecoveryActionsProps {
*/
export function RecoveryActions({ onRetry, onHome }: RecoveryActionsProps) {
return (
<Box
display="flex"
flexWrap="wrap"
alignItems="center"
justifyContent="center"
<Stack
direction="row"
wrap
align="center"
justify="center"
gap={3}
fullWidth
>
@@ -54,6 +54,6 @@ export function RecoveryActions({ onRetry, onHome }: RecoveryActionsProps) {
>
Contact Support
</Button>
</Box>
</Stack>
);
}

View File

@@ -2,12 +2,11 @@
import React from 'react';
import { AlertTriangle } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text';
import { Icon } from '@/ui/Icon';
import { Surface } from '@/ui/Surface';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
interface ServerErrorPanelProps {
message?: string;
@@ -24,37 +23,38 @@ export function ServerErrorPanel({ message, incidentId }: ServerErrorPanelProps)
return (
<Stack gap={6} align="center" fullWidth>
{/* Status Indicator */}
<Box
<Stack
p={4}
rounded="full"
bg="bg-warning-amber"
bgOpacity={0.1}
{...({ bgOpacity: 0.1 } as any)}
border
borderColor="border-warning-amber"
align="center"
justify="center"
>
<Icon icon={AlertTriangle} size={8} color="var(--warning-amber)" />
</Box>
</Stack>
{/* Primary Message */}
<Stack gap={2} align="center">
<Heading level={1} weight="bold">
CRITICAL_SYSTEM_FAILURE
</Heading>
<Text color="text-gray-400" align="center" maxWidth="md">
<Text color="text-gray-400" textAlign="center" maxWidth="md">
The application engine encountered an unrecoverable state.
Telemetry has been dispatched to engineering.
</Text>
</Stack>
{/* Technical Summary */}
<Surface
variant="dark"
<Card
variant="outline"
rounded="md"
padding={4}
p={4}
fullWidth
border
borderColor="border-white"
bgOpacity={0.2}
className="bg-graphite-black/20"
>
<Stack gap={2}>
<Text font="mono" size="sm" color="text-warning-amber" block>
@@ -71,7 +71,7 @@ export function ServerErrorPanel({ message, incidentId }: ServerErrorPanelProps)
</Text>
)}
</Stack>
</Surface>
</Card>
</Stack>
);
}