feat(threat-actors): hover tooltip on motivation badges

New MotivationBadge component with CSS tooltip showing:
- espionage: goal (intelligence theft), typical behavior, examples
- financial: goal (monetary), typical behavior, examples
- destruction: goal (disrupt/destroy infra), wiper/ICS attacks, examples
- hacktivism: goal (political/ideological), defacement/leaks, examples

Used in ThreatActorsPage (card list) and ThreatActorDetailPage (header).
This commit is contained in:
kitos
2026-06-02 10:50:37 +02:00
parent 62f5542ef2
commit 5c5398683a
3 changed files with 107 additions and 21 deletions
+103
View File
@@ -0,0 +1,103 @@
/**
* MotivationBadge — renders a threat actor motivation pill with a
* CSS-only hover tooltip explaining what the motivation means.
*/
const COLORS: Record<string, string> = {
espionage: "border-purple-500/30 bg-purple-900/50 text-purple-400",
financial: "border-yellow-500/30 bg-yellow-900/50 text-yellow-400",
destruction: "border-red-500/30 bg-red-900/50 text-red-400",
hacktivism: "border-cyan-500/30 bg-cyan-900/50 text-cyan-400",
};
const DEFAULT_COLOR = "border-gray-600/30 bg-gray-800/50 text-gray-400";
interface TooltipLine { label: string; text: string }
const TOOLTIPS: Record<string, { heading: string; lines: TooltipLine[] }> = {
espionage: {
heading: "🕵️ Espionage",
lines: [
{ label: "Goal", text: "Steal sensitive data, intellectual property, or government secrets for intelligence purposes." },
{ label: "Typical", text: "Nation-state APTs, long-duration intrusions, low detection priority." },
{ label: "Example", text: "APT28, APT29, Turla, Lazarus Group (intelligence ops)." },
],
},
financial: {
heading: "💰 Financial",
lines: [
{ label: "Goal", text: "Monetary gain through fraud, ransomware, cryptocurrency theft, or banking trojans." },
{ label: "Typical", text: "Opportunistic attacks, ransomware deployment, data exfiltration for sale." },
{ label: "Example", text: "FIN7, Carbanak, Cobalt Group, Wizard Spider." },
],
},
destruction: {
heading: "💥 Destruction",
lines: [
{ label: "Goal", text: "Disrupt or destroy critical infrastructure, systems, or data — often geopolitically motivated." },
{ label: "Typical", text: "Wiper malware, ICS/SCADA attacks, denial of service campaigns." },
{ label: "Example", text: "Sandworm, APT37 (destructive ops), Cleaver." },
],
},
hacktivism: {
heading: "✊ Hacktivism",
lines: [
{ label: "Goal", text: "Promote political or ideological causes through defacement, leaks, or disruptive attacks." },
{ label: "Typical", text: "Website defacement, data leaks, DDoS campaigns, public statements." },
{ label: "Example", text: "Anonymous Sudan, various politically motivated collectives." },
],
},
};
interface MotivationBadgeProps {
motivation: string;
size?: "sm" | "md";
/** Show icon before label */
showIcon?: boolean;
className?: string;
}
export default function MotivationBadge({
motivation,
size = "md",
className = "",
}: MotivationBadgeProps) {
const key = motivation.toLowerCase();
const color = COLORS[key] ?? DEFAULT_COLOR;
const tip = TOOLTIPS[key];
const pill = size === "sm" ? "px-2 py-0.5 text-[10px]" : "px-2 py-0.5 text-[11px]";
return (
<span className={`relative group inline-flex ${className}`}>
{/* Badge */}
<span className={`inline-flex cursor-help items-center rounded-full border font-medium capitalize ${pill} ${color}`}>
{motivation}
</span>
{/* Tooltip — below badge */}
{tip && (
<span
className="
pointer-events-none absolute top-full left-0 z-50 mt-2
w-72
rounded-xl border border-gray-700 bg-gray-900 p-3 shadow-xl
opacity-0 transition-opacity duration-150
group-hover:opacity-100
"
>
{/* Arrow */}
<span className="absolute bottom-full left-4 border-4 border-transparent border-b-gray-700" />
<p className="mb-2 text-xs font-semibold text-white">{tip.heading}</p>
{tip.lines.map(({ label, text }) => (
<div key={label} className="mb-1.5 last:mb-0">
<span className="text-[10px] font-medium uppercase tracking-wide text-gray-500">
{label}
</span>
<p className="text-[11px] leading-snug text-gray-300">{text}</p>
</div>
))}
</span>
)}
</span>
);
}