65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
import { useCallback } from 'react'
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import { KPICards } from '@/components/dashboard/KPICards'
|
|
import { TrendChart } from '@/components/dashboard/TrendChart'
|
|
import { SeverityDistribution } from '@/components/dashboard/SeverityDistribution'
|
|
import { RecentFindings } from '@/components/dashboard/RecentFindings'
|
|
import { ActiveSessions } from '@/components/dashboard/ActiveSessions'
|
|
import { QuickActions } from '@/components/dashboard/QuickActions'
|
|
import { useFindings, useFindingStats } from '@/hooks/useFindings'
|
|
import { useSessions } from '@/hooks/useSessions'
|
|
import { useSocket } from '@/hooks/useSocket'
|
|
|
|
export function Dashboard() {
|
|
const queryClient = useQueryClient()
|
|
|
|
const { data: findings = [], isLoading: findingsLoading } = useFindings()
|
|
const { data: stats, isLoading: statsLoading } = useFindingStats()
|
|
const { data: sessions = [] } = useSessions()
|
|
|
|
const refreshData = useCallback(() => {
|
|
void queryClient.invalidateQueries({ queryKey: ['findings'] })
|
|
void queryClient.invalidateQueries({ queryKey: ['sessions'] })
|
|
}, [queryClient])
|
|
|
|
useSocket(useCallback((event: string) => {
|
|
if (['session:started', 'session:completed', 'session:error', 'anomaly:detected'].includes(event)) {
|
|
refreshData()
|
|
}
|
|
}, [refreshData]))
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold">Dashboard</h1>
|
|
<p className="text-sm text-muted-foreground">Overview of your security findings</p>
|
|
</div>
|
|
<QuickActions />
|
|
</div>
|
|
|
|
<KPICards stats={stats} isLoading={statsLoading} />
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div className="lg:col-span-2">
|
|
<TrendChart findings={findings} />
|
|
</div>
|
|
<SeverityDistribution findings={findings} />
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div className="lg:col-span-2">
|
|
<RecentFindings findings={findings.slice().sort((a, b) => b.timestamp - a.timestamp)} />
|
|
</div>
|
|
<ActiveSessions sessions={sessions} />
|
|
</div>
|
|
|
|
{findingsLoading && (
|
|
<div className="text-center text-sm text-muted-foreground py-4">
|
|
Loading findings...
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|