Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ export function AppRoutes() {
<Route path={routes.reports} element={<Reports />} />
<Route path={routes.workflows} element={<Workflows />} />
<Route path={routes.settings} element={<Settings />} />
<Route path={routes.task} element={<TaskDetails />} />

<Route path={routes.task} element={<TaskDetails />} /> -0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 9
<Route path="*" element={<Navigate to={routes.dashboard} replace />} />
</Routes>
)
Expand Down
35 changes: 17 additions & 18 deletions frontend/src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ export default function AppShell({ children }: AppShellProps) {
{ to: routes.reports, icon: 'summarize', label: 'Reports' },
{ to: routes.workflows, icon: 'account_tree', label: 'Workflows' },
{ to: routes.toolkit, icon: 'add_circle', label: 'Toolkit' },
]
const mobileDrawerNav = [
]; const mobileDrawerNav = [
{ to: routes.dashboard, label: 'Dashboard' },
{ to: routes.scans, label: 'Scans' },
{ to: routes.findings, label: 'Findings' },
Expand Down Expand Up @@ -111,22 +110,22 @@ export default function AppShell({ children }: AppShellProps) {
{children}
</main>

<nav className="lg:hidden fixed bottom-0 inset-x-0 z-40 h-16 bg-secondary border-t border-accent-silver/10 grid grid-cols-5">
{mobilePrimaryNav.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
`flex flex-col items-center justify-center gap-1 text-[9px] font-bold uppercase tracking-[0.08em] ${
isActive ? 'text-rag-red bg-rag-red/10' : 'text-silver/70'
}`
}
>
<span className="material-symbols-outlined text-[18px]">{item.icon}</span>
<span>{item.label}</span>
</NavLink>
))}
</nav>
<nav className="lg:hidden fixed bottom-0 inset-x-0 z-40 h-16 bg-secondary border-t border-accent-silver/10 grid grid-cols-6">
{mobilePrimaryNav.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
`flex flex-col items-center justify-center gap-1 text-[9px] font-bold uppercase tracking-[0.08em] ${
isActive ? 'text-rag-red bg-rag-red/10' : 'text-silver/70'
}`
}
>
<span className="material-symbols-outlined text-[18px]">{item.icon}</span>
<span>{item.label}</span>
</NavLink>
))}
</nav>
</div>
</>
)
Expand Down
187 changes: 187 additions & 0 deletions frontend/src/pages/Analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import {
Bar,
BarChart,
CartesianGrid,
Cell,
Line,
LineChart,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts'
import { useEffect, useMemo, useState } from 'react'

const vulnerabilityTrend = [
{ day: 'Mon', critical: 4, high: 9, medium: 18 },
{ day: 'Tue', critical: 6, high: 12, medium: 21 },
{ day: 'Wed', critical: 3, high: 10, medium: 16 },
{ day: 'Thu', critical: 8, high: 14, medium: 24 },
{ day: 'Fri', critical: 5, high: 11, medium: 19 },
{ day: 'Sat', critical: 7, high: 13, medium: 22 },
{ day: 'Sun', critical: 4, high: 8, medium: 15 },
]

const severityData = [
{ name: 'Critical', value: 18 },
{ name: 'High', value: 42 },
{ name: 'Medium', value: 76 },
{ name: 'Low', value: 55 },
]

const targetRisk = [
{ target: 'api.secuscan.local', risk: 94 },
{ target: 'admin.panel', risk: 87 },
{ target: 'auth.gateway', risk: 72 },
{ target: 'cdn.edge', risk: 51 },
]

const scanStats = [
{ name: 'Success', value: 72 },
{ name: 'Failed', value: 18 },
{ name: 'Aborted', value: 10 },
]

const COLORS = ['#ef4444', '#f97316', '#eab308', '#22c55e']

function MetricCard({ label, value, detail }: { label: string; value: string; detail: string }) {
return (
<div className="bg-charcoal border-4 border-black p-6 shadow-[6px_6px_0px_0px_rgba(0,0,0,1)]">
<p className="text-[10px] font-black uppercase tracking-[0.25em] text-silver/40">{label}</p>
<p className="mt-4 text-4xl font-black text-silver-bright">{value}</p>
<p className="mt-2 text-[10px] font-mono uppercase tracking-widest text-silver/30">{detail}</p>
</div>
)
}

function ChartCard({ title, children }: { title: string; children: React.ReactNode }) {
return (
<section className="bg-charcoal border-4 border-black p-6 shadow-[8px_8px_0px_0px_rgba(0,0,0,1)]">
<h2 className="mb-6 text-xs font-black uppercase tracking-[0.25em] text-silver-bright">
{title}
</h2>
<div className="h-72">{children}</div>
</section>
)
}
export default function Analytics() {

const [timeRange, setTimeRange] = useState<'7d' | '30d' | '90d'>('7d')
const [lastUpdated, setLastUpdated] = useState(new Date())

useEffect(() => {
const interval = window.setInterval(() => {
setLastUpdated(new Date())
}, 30000)

return () => window.clearInterval(interval)
}, [])

const filteredTrend = useMemo(() => {
if (timeRange === '7d') return vulnerabilityTrend.slice(-7)
if (timeRange === '30d') return vulnerabilityTrend
return vulnerabilityTrend
}, [timeRange])

return (
<div className="min-h-screen bg-charcoal-dark text-silver p-6 md:p-12 pb-32 space-y-10">
<header className="border-b-4 border-silver-bright/10 pb-10">
<div className="bg-rag-blue text-black px-4 py-1 text-xs uppercase tracking-widest inline-block font-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]">
Analytics_Core v1.0
</div>

<h1 className="mt-5 text-5xl md:text-7xl font-black uppercase italic tracking-tighter text-silver-bright">
Security Analytics
</h1>

<p className="mt-4 text-xs font-mono uppercase tracking-widest text-silver/40">
Vulnerability trends // scan statistics // target risk analysis
</p>

<div className="mt-6 flex flex-wrap gap-3">
{(['7d', '30d', '90d'] as const).map((range) => (
<button
key={range}
onClick={() => setTimeRange(range)}
className={`border-4 border-black px-4 py-2 text-xs font-black uppercase ${
timeRange === range
? 'bg-rag-blue text-black'
: 'bg-charcoal text-silver'
}`}
>
{range}
</button>
))}
</div>

<p className="mt-3 text-[10px] font-mono uppercase tracking-widest text-silver/40">
Auto refresh enabled // Last updated: {lastUpdated.toLocaleTimeString()}
</p>

</header>

<section className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-6">
<MetricCard label="Total Vulnerabilities" value="191" detail="+12 this week" />
<MetricCard label="Critical Findings" value="18" detail="Immediate action" />
<MetricCard label="Scan Success Rate" value="72%" detail="Last 30 days" />
<MetricCard label="High Risk Targets" value="04" detail="Ranked by exposure" />
</section>

<section className="grid grid-cols-1 xl:grid-cols-2 gap-8">
<ChartCard title="Vulnerabilities Over Time">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={filteredTrend}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="day" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="critical" stroke="#ef4444" strokeWidth={3} />
<Line type="monotone" dataKey="high" stroke="#f97316" strokeWidth={3} />
<Line type="monotone" dataKey="medium" stroke="#eab308" strokeWidth={3} />
</LineChart>
</ResponsiveContainer>
</ChartCard>

<ChartCard title="Severity Distribution">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie data={severityData} dataKey="value" nameKey="name" outerRadius={95} label>
{severityData.map((_, index) => (
<Cell key={index} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
</ChartCard>

<ChartCard title="Most Vulnerable Targets">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={targetRisk}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="target" />
<YAxis />
<Tooltip />
<Bar dataKey="risk" fill="#38bdf8" />
</BarChart>
</ResponsiveContainer>
</ChartCard>

<ChartCard title="Scan Success vs Failure">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie data={scanStats} dataKey="value" nameKey="name" outerRadius={95} label>
{scanStats.map((_, index) => (
<Cell key={index} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
</ChartCard>
</section>
</div>
)
}
1 change: 1 addition & 0 deletions frontend/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const routes = {
scanTool: '/toolkit/:toolId',
findings: '/findings',
scans: '/scans',
analytics: '/analytics',
reports: '/reports',
workflows: '/workflows',
settings: '/settings',
Expand Down
Loading