diff --git a/frontend/src/components/App.tsx b/frontend/src/components/App.tsx index 9d7cde8..8ff3917 100644 --- a/frontend/src/components/App.tsx +++ b/frontend/src/components/App.tsx @@ -9,9 +9,9 @@ import { ResultsSkeleton } from './ResultsSkeleton'; import { ToolPanels } from './tools/ToolPanels'; import { ScanComparison } from './views/ScanComparison'; import { WatchlistView } from './views/WatchlistView'; -import { MODULE_MAP } from './Sidebar'; import { startScan, getWsUrl, getScan, getUsage } from '@/lib/api'; import { detectScanType, normalizeScanTarget } from '@/lib/scan-target'; +import { MODULE_MAP } from './Sidebar'; import type { ScanType, ScanStatus, ToolMode, ScanResults as ScanResultsType, ScanMeta, LiveModuleStatus, UsageData } from '@/lib/types'; type View = 'idle' | 'tool' | 'scanning' | 'results' | 'compare' | 'watchlist'; @@ -47,7 +47,9 @@ export function App() { const [totalModules, setTotalModules] = useState(0); const [compareIds, setCompareIds] = useState<[string, string] | null>(null); const [usage, setUsage] = useState(null); + const [isStarting, setIsStarting] = useState(false); const wsRef = useRef(null); + const urlScanStarted = useRef(false); const usageLimitedRef = useRef(true); const handleHome = useCallback(() => { @@ -228,29 +230,23 @@ export function App() { setProgressLog([]); setModuleStatuses({}); setTotalModules(modules.length); - setScanStatus('running'); - setView('scanning'); setScanMeta(null); + setIsStarting(true); try { const { scan_id } = await startScan(target, type, modules); + setIsStarting(false); + setScanStatus('running'); + setView('scanning'); setScanId(scan_id); connectWs(scan_id); handleUsageRefresh(); } catch (e: unknown) { + setIsStarting(false); setScanStatus('failed'); setProgressLog([`Failed to start scan: ${e instanceof Error ? e.message : 'Unknown error'}`]); } }, [connectWs, handleUsageRefresh]); - useEffect(() => { - handleUsageRefresh(); - }, [handleUsageRefresh]); - - useEffect(() => { - return () => { wsRef.current?.close(); }; - }, []); - - const urlScanStarted = useRef(false); useEffect(() => { if (urlScanStarted.current) return; try { @@ -265,14 +261,22 @@ export function App() { } catch {} }, [handleScan]); + useEffect(() => { + handleUsageRefresh(); + }, [handleUsageRefresh]); + + useEffect(() => { + return () => { wsRef.current?.close(); }; + }, []); + return (
setSidebarOpen(v => !v)} />
{sidebarOpen &&
setSidebarOpen(false)} className="fixed inset-0 bg-black/50 z-40 md:hidden" />} - setSidebarOpen(false)} /> + setSidebarOpen(false)} />
- {view === 'idle' && } + {view === 'idle' && } {view === 'tool' && toolMode && ( setView('idle')} /> )} @@ -293,4 +297,4 @@ export function App() {
); -} +} \ No newline at end of file diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index b131b2b..8396193 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -30,6 +30,7 @@ interface Props { onLoadScan?: (scanId: string) => void; onCompare?: (a: string, b: string) => void; isRunning: boolean; + isStarting?: boolean; isOpen: boolean; onClose: () => void; } @@ -55,7 +56,7 @@ function useRecentScans() { return { recents, add, clear }; } -export function Sidebar({ onScan, onLoadScan, onCompare, isRunning, isOpen, onClose }: Props) { +export function Sidebar({ onScan, onLoadScan, onCompare, isRunning, isStarting = false, isOpen, onClose }: Props) { const { t } = useTranslations(); const [target, setTarget] = useState(''); const [scanType, setScanType] = useState('domain'); @@ -211,11 +212,11 @@ export function Sidebar({ onScan, onLoadScan, onCompare, isRunning, isOpen, onCl @@ -367,4 +368,4 @@ export function Sidebar({ onScan, onLoadScan, onCompare, isRunning, isOpen, onCl
); -} +} \ No newline at end of file diff --git a/frontend/src/components/views/IdleView.tsx b/frontend/src/components/views/IdleView.tsx index 0ba5399..452ff8d 100644 --- a/frontend/src/components/views/IdleView.tsx +++ b/frontend/src/components/views/IdleView.tsx @@ -1,6 +1,6 @@ 'use client'; import { useEffect, useState } from 'react'; -import { FileText, Mail, Bitcoin, QrCode, ChevronRight, Globe, User, Phone, Shield, Database, Zap, Eye, Activity, Network, Hash, Binary, KeyRound, Search } from 'lucide-react'; +import { FileText, Mail, Bitcoin, QrCode, ChevronRight, Globe, User, Phone, Shield, Database, Zap, Eye, Activity, Network, Hash, Binary, KeyRound, Search, Loader2 } from 'lucide-react'; import { useTranslations } from '@/lib/i18n'; import { getPrismDemoMode } from '@/lib/prism-config'; import { detectScanType, normalizeScanTarget } from '@/lib/scan-target'; @@ -29,9 +29,10 @@ type CapsKey = typeof CAPS_KEYS[number]; interface Props { onTool: (mode: ToolMode) => void; onScan: (target: string, type: ScanType, modules: string[]) => void; + isStarting?: boolean; } -export function IdleView({ onTool, onScan }: Props) { +export function IdleView({ onTool, onScan, isStarting = false }: Props) { const { t } = useTranslations(); const demoMode = getPrismDemoMode(); const [targetIdx, setTargetIdx] = useState(0); @@ -110,11 +111,11 @@ export function IdleView({ onTool, onScan }: Props) { @@ -194,4 +195,4 @@ export function IdleView({ onTool, onScan }: Props) { )} ); -} +} \ No newline at end of file