import type { ApiRequestLogger } from '@/lib/infrastructure/ApiRequestLogger'; import { getGlobalApiLogger } from '@/lib/infrastructure/ApiRequestLogger'; import type { GlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler'; import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler'; import { Button } from '@/ui/Button'; import { Icon } from '@/ui/Icon'; import { Grid } from '@/ui/primitives/Grid'; import { Stack } from '@/ui/primitives/Stack'; import { Text } from '@/ui/Text'; import { Bug, Shield, X } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; // Extend Window interface for debug globals declare global { interface Window { __GRIDPILOT_FETCH_LOGGED__?: boolean; __GRIDPILOT_GLOBAL_HANDLER__?: GlobalErrorHandler; __GRIDPILOT_API_LOGGER__?: ApiRequestLogger; __GRIDPILOT_REACT_ERRORS__?: Array<{ error: unknown; componentStack?: string }>; } } interface DebugModeToggleProps { /** * Whether to show the toggle (auto-detected from environment) */ show?: boolean; } /** * Debug Mode Toggle Component * Provides a floating interface to control debug features and view real-time metrics */ export function DebugModeToggle({ show }: DebugModeToggleProps) { const [isOpen, setIsOpen] = useState(false); const [debugEnabled, setDebugEnabled] = useState(false); const [metrics, setMetrics] = useState({ errors: 0, apiRequests: 0, apiFailures: 0, }); const isDev = process.env.NODE_ENV === 'development'; const shouldShow = show ?? isDev; const updateMetrics = useCallback(() => { if (!debugEnabled) return; const globalHandler = getGlobalErrorHandler(); const apiLogger = getGlobalApiLogger(); const errorStats = globalHandler.getStats(); const apiStats = apiLogger.getStats(); setMetrics({ errors: errorStats.total, apiRequests: apiStats.total, apiFailures: apiStats.failed, }); }, [debugEnabled]); const initializeDebugFeatures = useCallback(() => { const globalHandler = getGlobalErrorHandler(); const apiLogger = getGlobalApiLogger(); // Initialize global error handler globalHandler.initialize(); // Override fetch with logging if (!window.__GRIDPILOT_FETCH_LOGGED__) { const loggedFetch = apiLogger.createLoggedFetch(); window.fetch = loggedFetch as typeof fetch; window.__GRIDPILOT_FETCH_LOGGED__ = true; } // Expose to window for easy access window.__GRIDPILOT_GLOBAL_HANDLER__ = globalHandler; window.__GRIDPILOT_API_LOGGER__ = apiLogger; console.log('%c[DEBUG MODE] Enabled', 'color: #00ff88; font-weight: bold; font-size: 14px;'); }, []); useEffect(() => { if (!shouldShow) return; // Load debug state from localStorage const saved = localStorage.getItem('gridpilot_debug_enabled'); if (saved === 'true') { setDebugEnabled(true); initializeDebugFeatures(); } // Update metrics every 2 seconds const interval = setInterval(updateMetrics, 2000); return () => clearInterval(interval); }, [shouldShow, initializeDebugFeatures, updateMetrics]); useEffect(() => { // Save debug state if (shouldShow) { localStorage.setItem('gridpilot_debug_enabled', debugEnabled.toString()); } }, [debugEnabled, shouldShow]); const toggleDebug = () => { const newEnabled = !debugEnabled; setDebugEnabled(newEnabled); if (newEnabled) { initializeDebugFeatures(); } else { // Disable debug features const globalHandler = getGlobalErrorHandler(); globalHandler.destroy(); console.log('%c[DEBUG MODE] Disabled', 'color: #ff4444; font-weight: bold; font-size: 14px;'); } }; const triggerTestError = () => { if (!debugEnabled) return; // Trigger a test API error const testError = new Error('This is a test error for debugging'); (testError as Error & { type?: string }).type = 'TEST_ERROR'; const globalHandler = getGlobalErrorHandler(); globalHandler.report(testError, { test: true, timestamp: Date.now() }); console.log('%c[TEST] Error triggered', 'color: #ffaa00; font-weight: bold;', testError); }; const triggerTestApiCall = async () => { if (!debugEnabled) return; try { // This will fail and be logged await fetch('https://httpstat.us/500'); } catch (_error) { // Already logged by interceptor console.log('%c[TEST] API call completed', 'color: #00aaff; font-weight: bold;'); } }; const clearAllLogs = () => { const globalHandler = getGlobalErrorHandler(); const apiLogger = getGlobalApiLogger(); globalHandler.clearHistory(); apiLogger.clearHistory(); setMetrics({ errors: 0, apiRequests: 0, apiFailures: 0 }); console.log('%c[DEBUG] All logs cleared', 'color: #00ff88; font-weight: bold;'); }; const copyDebugInfo = async () => { const globalHandler = getGlobalErrorHandler(); const apiLogger = getGlobalApiLogger(); const debugInfo = { timestamp: new Date().toISOString(), environment: { mode: process.env.NODE_ENV, version: process.env.NEXT_PUBLIC_APP_VERSION, }, browser: { userAgent: navigator.userAgent, language: navigator.language, platform: navigator.platform, }, errors: globalHandler.getStats(), api: apiLogger.getStats(), reactErrors: window.__GRIDPILOT_REACT_ERRORS__ || [], }; try { await navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)); console.log('%c[DEBUG] Debug info copied to clipboard', 'color: #00ff88; font-weight: bold;'); } catch (err) { console.error('Failed to copy:', err); } }; if (!shouldShow) { return null; } return ( {/* Main Toggle Button */} {!isOpen && ( )} {/* Debug Panel */} {isOpen && ( {/* Header */} Debug Control {/* Content */} {/* Debug Toggle */} Debug Mode {/* Metrics */} {debugEnabled && ( Errors {metrics.errors} API {metrics.apiRequests} Failures {metrics.apiFailures} )} {/* Actions */} {debugEnabled && ( Test Actions Utilities )} {/* Quick Links */} {debugEnabled && ( Quick Access • window.__GRIDPILOT_GLOBAL_HANDLER__ • window.__GRIDPILOT_API_LOGGER__ • window.__GRIDPILOT_REACT_ERRORS__ )} {/* Status */} {debugEnabled ? 'Debug features active' : 'Debug mode disabled'} {isDev && ' • Development Environment'} )} ); } /** * Hook for programmatic debug control */ export function useDebugMode() { const [debugEnabled, setDebugEnabled] = useState(false); useEffect(() => { const saved = localStorage.getItem('gridpilot_debug_enabled'); setDebugEnabled(saved === 'true'); }, []); const enable = useCallback(() => { setDebugEnabled(true); localStorage.setItem('gridpilot_debug_enabled', 'true'); // Initialize debug features const globalHandler = getGlobalErrorHandler(); globalHandler.initialize(); const apiLogger = getGlobalApiLogger(); if (!window.__GRIDPILOT_FETCH_LOGGED__) { const loggedFetch = apiLogger.createLoggedFetch(); window.fetch = loggedFetch as typeof fetch; window.__GRIDPILOT_FETCH_LOGGED__ = true; } window.__GRIDPILOT_GLOBAL_HANDLER__ = globalHandler; window.__GRIDPILOT_API_LOGGER__ = apiLogger; }, []); const disable = useCallback(() => { setDebugEnabled(false); localStorage.setItem('gridpilot_debug_enabled', 'false'); const globalHandler = getGlobalErrorHandler(); globalHandler.destroy(); }, []); const toggle = useCallback(() => { if (debugEnabled) { disable(); } else { enable(); } }, [debugEnabled, enable, disable]); return { enabled: debugEnabled, enable, disable, toggle, }; }