dev experience

This commit is contained in:
2026-01-01 17:43:38 +01:00
parent df7e5db5ba
commit 9958053462
8 changed files with 208 additions and 173 deletions

View File

@@ -3,11 +3,12 @@
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
import { useNotifications } from '@/components/notifications/NotificationProvider';
import type { NotificationVariant } from '@/components/notifications/notificationTypes';
import { Wrench, ChevronDown, ChevronUp, X, MessageSquare, Activity, LogIn, Play } from 'lucide-react';
import { Wrench, ChevronDown, ChevronUp, X, MessageSquare, Activity, LogIn, AlertTriangle } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
// Import our new components
import { Accordion } from './Accordion';
@@ -16,7 +17,6 @@ import { UrgencySection } from './sections/UrgencySection';
import { NotificationSendSection } from './sections/NotificationSendSection';
import { APIStatusSection } from './sections/APIStatusSection';
import { LoginSection } from './sections/LoginSection';
import { ReplaySection } from './sections/ReplaySection';
// Import types
import type { DemoNotificationType, DemoUrgency, LoginMode } from './types';
@@ -39,6 +39,9 @@ export default function DevToolbar() {
const [circuitBreakers, setCircuitBreakers] = useState(() => CircuitBreakerRegistry.getInstance().getStatus());
const [checkingHealth, setCheckingHealth] = useState(false);
// Error Stats State
const [errorStats, setErrorStats] = useState({ total: 0, byType: {} as Record<string, number> });
// Accordion state - only one open at a time
const [openAccordion, setOpenAccordion] = useState<string | null>('notifications');
@@ -136,6 +139,28 @@ export default function DevToolbar() {
};
}, []);
// Error Stats Effect
useEffect(() => {
const updateErrorStats = () => {
try {
const handler = getGlobalErrorHandler();
const stats = handler.getStats();
setErrorStats(stats);
} catch {
// Handler might not be initialized yet
setErrorStats({ total: 0, byType: {} });
}
};
// Initial update
updateErrorStats();
// Poll for updates every 3 seconds
const interval = setInterval(updateErrorStats, 3000);
return () => clearInterval(interval);
}, []);
// API Health Check Handler
const handleApiHealthCheck = async () => {
setCheckingHealth(true);
@@ -412,15 +437,43 @@ export default function DevToolbar() {
/>
</Accordion>
{/* Replay Section - Accordion */}
{/* Error Stats Section - Accordion */}
<Accordion
title="Error Replay"
icon={<Play className="w-4 h-4 text-gray-400" />}
isOpen={openAccordion === 'replay'}
onToggle={() => setOpenAccordion(openAccordion === 'replay' ? null : 'replay')}
title="Error Stats"
icon={<AlertTriangle className="w-4 h-4 text-gray-400" />}
isOpen={openAccordion === 'errors'}
onToggle={() => setOpenAccordion(openAccordion === 'errors' ? null : 'errors')}
>
<ReplaySection />
<div className="space-y-2 text-xs">
<div className="flex justify-between items-center p-2 bg-iron-gray/30 rounded">
<span className="text-gray-400">Total Errors</span>
<span className="font-mono font-bold text-red-400">{errorStats.total}</span>
</div>
{Object.keys(errorStats.byType).length > 0 ? (
<div className="space-y-1">
{Object.entries(errorStats.byType).map(([type, count]) => (
<div key={type} className="flex justify-between items-center p-1.5 bg-deep-graphite rounded">
<span className="text-gray-300">{type}</span>
<span className="font-mono text-yellow-400">{count}</span>
</div>
))}
</div>
) : (
<div className="text-center text-gray-500 py-2">No errors yet</div>
)}
<button
onClick={() => {
const handler = getGlobalErrorHandler();
handler.clearHistory();
setErrorStats({ total: 0, byType: {} });
}}
className="w-full p-2 bg-iron-gray hover:bg-charcoal-outline text-gray-300 rounded border border-charcoal-outline text-xs"
>
Clear Error History
</button>
</div>
</Accordion>
</div>
)}