feat(dashboards): hover tooltips on all metric cards
New MetricTooltip component — a small ⓘ icon showing an executive- friendly explanation panel on hover (CSS, no JS, instant). DashboardPage: tooltips on all 6 coverage summary cards (Total Techniques, Validated, Partial, In Progress, Not Covered, Not Evaluated), Coverage Evolution chart, Test Pipeline funnel, Team Activity and Validation Rate section headers. ExecutiveDashboardPage: tooltips on all 4 sub-scores (Coverage, Detection, Critical, Response), Score Trend, Top Threat Actors, 4 KPIs (MTTD, MTTR, Detection Efficacy, Validation Throughput), Coverage by Tactic, Critical Gaps table, and all 6 team metrics (Red/Blue Tests Done, Avg Time, Rejection). Each tooltip explains what the metric measures, what a good/bad value looks like, and what action to take — written for non- technical executives.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { ReactNode } from "react";
|
||||
import MetricTooltip from "./MetricTooltip";
|
||||
|
||||
interface CoverageSummaryCardProps {
|
||||
title: string;
|
||||
@@ -7,6 +8,8 @@ interface CoverageSummaryCardProps {
|
||||
icon: ReactNode;
|
||||
colorClass: string;
|
||||
bgClass: string;
|
||||
/** Optional tooltip explaining this metric to non-technical users */
|
||||
tooltip?: { description: string; context?: string };
|
||||
}
|
||||
|
||||
export default function CoverageSummaryCard({
|
||||
@@ -16,6 +19,7 @@ export default function CoverageSummaryCard({
|
||||
icon,
|
||||
colorClass,
|
||||
bgClass,
|
||||
tooltip,
|
||||
}: CoverageSummaryCardProps) {
|
||||
const percentage = total && total > 0 ? ((value / total) * 100).toFixed(1) : null;
|
||||
|
||||
@@ -23,7 +27,16 @@ export default function CoverageSummaryCard({
|
||||
<div className={`rounded-xl border border-gray-800 ${bgClass} p-5`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-400">{title}</p>
|
||||
<p className="text-sm font-medium text-gray-400 flex items-center">
|
||||
{title}
|
||||
{tooltip && (
|
||||
<MetricTooltip
|
||||
title={title}
|
||||
description={tooltip.description}
|
||||
context={tooltip.context}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<p className={`mt-1 text-3xl font-bold ${colorClass}`}>{value}</p>
|
||||
{percentage !== null && (
|
||||
<p className="mt-1 text-xs text-gray-500">{percentage}% of total</p>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* MetricTooltip — a small ⓘ icon that shows a plain-language explanation
|
||||
* of a metric on hover. Designed for executive dashboards where not everyone
|
||||
* knows what "MTTD" or "partial coverage" means.
|
||||
*/
|
||||
|
||||
interface MetricTooltipProps {
|
||||
title: string;
|
||||
description: string;
|
||||
/** Optional extra context line (e.g. "Why it matters") */
|
||||
context?: string;
|
||||
/** Positioning hint — defaults to 'below' */
|
||||
position?: "below" | "above" | "left";
|
||||
}
|
||||
|
||||
export default function MetricTooltip({
|
||||
title,
|
||||
description,
|
||||
context,
|
||||
position = "below",
|
||||
}: MetricTooltipProps) {
|
||||
const posClass =
|
||||
position === "above"
|
||||
? "bottom-full mb-2 left-0"
|
||||
: position === "left"
|
||||
? "right-full mr-2 top-0"
|
||||
: "top-full mt-2 left-0";
|
||||
|
||||
return (
|
||||
<span className="relative group inline-flex items-center ml-1.5 align-middle">
|
||||
{/* The ⓘ icon */}
|
||||
<span className="flex h-4 w-4 cursor-help items-center justify-center rounded-full border border-gray-600 bg-gray-800 text-[9px] font-bold text-gray-400 hover:border-cyan-500/50 hover:text-cyan-400 transition-colors select-none">
|
||||
i
|
||||
</span>
|
||||
|
||||
{/* Tooltip panel */}
|
||||
<span
|
||||
className={`
|
||||
pointer-events-none absolute ${posClass} z-50 w-64
|
||||
rounded-xl border border-gray-700 bg-gray-900 p-3 shadow-xl
|
||||
opacity-0 transition-opacity duration-150 group-hover:opacity-100
|
||||
`}
|
||||
>
|
||||
{/* Arrow */}
|
||||
{position !== "above" && position !== "left" && (
|
||||
<span className="absolute bottom-full left-3 border-4 border-transparent border-b-gray-700" />
|
||||
)}
|
||||
<p className="mb-1.5 text-xs font-semibold text-white">{title}</p>
|
||||
<p className="text-[11px] leading-snug text-gray-300">{description}</p>
|
||||
{context && (
|
||||
<p className="mt-1.5 text-[10px] leading-snug text-cyan-400/80 border-t border-gray-800 pt-1.5">
|
||||
{context}
|
||||
</p>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user