HuggingClaw-MissionControl / src /components /dashboard /widgets /security-audit-widget.tsx
nyk
feat(refactor): ready for manual QA after main sync (#274)
b6ecafa unverified
Raw
History Blame Contribute Delete
2.48 kB
'use client'
import { useState, useEffect, useCallback } from 'react'
import { StatRow, type DashboardData } from '../widget-primitives'
import { useNavigateToPanel } from '@/lib/navigation'
interface PostureInfo {
score: number
level: string
}
const postureBadge: Record<string, { label: string; className: string }> = {
hardened: { label: 'Hardened', className: 'bg-green-500/15 text-green-400' },
secure: { label: 'Secure', className: 'bg-green-500/10 text-green-300' },
'needs-attention': { label: 'Needs Attention', className: 'bg-yellow-500/15 text-yellow-400' },
'at-risk': { label: 'At Risk', className: 'bg-red-500/15 text-red-400' },
}
export function SecurityAuditWidget({ data }: { data: DashboardData }) {
const { dbStats } = data
const navigateToPanel = useNavigateToPanel()
const [posture, setPosture] = useState<PostureInfo | null>(null)
const fetchPosture = useCallback(async () => {
try {
const res = await fetch('/api/security-audit?timeframe=day')
if (res.ok) {
const json = await res.json()
if (json.posture) setPosture(json.posture)
}
} catch {
// Silent
}
}, [])
useEffect(() => { fetchPosture() }, [fetchPosture])
const badge = posture ? postureBadge[posture.level] || postureBadge['secure'] : null
return (
<div className="panel">
<div className="panel-header">
<h3 className="text-sm font-semibold">Security + Audit</h3>
{posture && badge && (
<span className={`text-2xs px-1.5 py-0.5 rounded font-medium ${badge.className}`}>
{posture.score} - {badge.label}
</span>
)}
</div>
<div className="panel-body space-y-3">
<StatRow label="Audit events (24h)" value={dbStats?.audit.day ?? 0} />
<StatRow label="Audit events (7d)" value={dbStats?.audit.week ?? 0} />
<StatRow label="Login failures (24h)" value={dbStats?.audit.loginFailures ?? 0} alert={dbStats ? dbStats.audit.loginFailures > 0 : false} />
<StatRow label="Unread notifications" value={dbStats?.notifications.unread ?? 0} alert={(dbStats?.notifications.unread ?? 0) > 0} />
<button
onClick={() => navigateToPanel('security')}
className="w-full text-center text-xs text-primary hover:text-primary/80 py-1.5 mt-1 border border-border/50 rounded hover:bg-secondary transition-colors"
>
View Security Panel
</button>
</div>
</div>
)
}