mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 13:33:30 +01:00
MAESTRO: add hop chain visualisation
This commit is contained in:
@@ -40,7 +40,7 @@ ReportContainer
|
||||
|
||||
- [x] T030 [US4] Write failing tests (TDD Red) in `frontend/src/__tests__/report/TestResultCard.test.tsx` (each severity level, expand/collapse, error indicator), `HopChainVisualisation.test.tsx` (render with sample hops), `ReportSearchBar.test.tsx` (filter simulation), `ReportExport.test.tsx` (export trigger), `SecurityAppliancesSummary.test.tsx` (render with sample appliances, empty state), `ReportContainer.test.tsx` (full report with mixed results)
|
||||
- [x] T031 [P] [US4] Create `frontend/src/components/report/TestResultCard.tsx` — collapsible card per test result. Severity-coloured indicator (red=spam, amber=suspicious, green=clean per FR-09), header name, monospace value, analysis text. Failed tests show error indicator (FR-25). Expand/collapse with animation, keyboard accessible (NFR-02). Verify `TestResultCard.test.tsx` passes (TDD Green)
|
||||
- [ ] T032 [P] [US4] Create `frontend/src/components/report/HopChainVisualisation.tsx` — vertical flow diagram of mail server hop chain (FR-08): hostname, IP, timestamp, server version, connecting arrows. FontAwesome server/network icons. Responsive. Verify `HopChainVisualisation.test.tsx` passes (TDD Green)
|
||||
- [x] T032 [P] [US4] Create `frontend/src/components/report/HopChainVisualisation.tsx` — vertical flow diagram of mail server hop chain (FR-08): hostname, IP, timestamp, server version, connecting arrows. FontAwesome server/network icons. Responsive. Verify `HopChainVisualisation.test.tsx` passes (TDD Green)
|
||||
- [ ] T033 [P] [US4] Create `frontend/src/components/report/ReportSearchBar.tsx` — search/filter bar above report (FR-20). Filters by text match against test name, header name, or analysis text. Highlights matches, shows count. FontAwesome search icon, Escape to clear. Verify `ReportSearchBar.test.tsx` passes (TDD Green)
|
||||
- [ ] T034 [P] [US4] Create `frontend/src/components/report/ReportExport.tsx` — export as HTML (styled standalone page) or JSON (raw data) per FR-21. FontAwesome download icons, triggers browser download. Verify `ReportExport.test.tsx` passes (TDD Green)
|
||||
- [ ] T035 [US4] Create `frontend/src/components/report/SecurityAppliancesSummary.tsx` — summary listing detected email security products as badges/tags with FontAwesome shield icons. Handle empty state (no appliances detected). Verify `SecurityAppliancesSummary.test.tsx` passes (TDD Green)
|
||||
|
||||
109
frontend/src/components/report/HopChainVisualisation.tsx
Normal file
109
frontend/src/components/report/HopChainVisualisation.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faArrowDown,
|
||||
faClock,
|
||||
faNetworkWired,
|
||||
faServer,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import type { HopChainNode } from "../../types/analysis";
|
||||
|
||||
type HopChainVisualisationProps = {
|
||||
hopChain: HopChainNode[];
|
||||
};
|
||||
|
||||
const formatDelay = (delay?: number | null): string | null => {
|
||||
if (delay === null || delay === undefined || Number.isNaN(delay)) {
|
||||
return null;
|
||||
}
|
||||
return `${delay.toFixed(2)}s`;
|
||||
};
|
||||
|
||||
export default function HopChainVisualisation({
|
||||
hopChain,
|
||||
}: HopChainVisualisationProps) {
|
||||
if (hopChain.length === 0) {
|
||||
return (
|
||||
<section
|
||||
data-testid="hop-chain-visualisation"
|
||||
className="rounded-2xl border border-info/10 bg-surface/50 p-4"
|
||||
>
|
||||
<p className="text-sm text-text/60">No hop chain data available.</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
data-testid="hop-chain-visualisation"
|
||||
className="rounded-2xl border border-info/10 bg-surface/50 p-4"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
{hopChain.map((node, index) => {
|
||||
const delayLabel = formatDelay(node.delay);
|
||||
|
||||
return (
|
||||
<div key={`${node.index}-${node.hostname}`} className="flex flex-col">
|
||||
<article
|
||||
data-testid={`hop-chain-node-${node.index}`}
|
||||
className="rounded-2xl border border-info/10 bg-background/40 p-4"
|
||||
>
|
||||
<div className="flex flex-wrap items-start gap-3">
|
||||
<span className="flex h-10 w-10 items-center justify-center rounded-xl border border-info/20 bg-info/10 text-info">
|
||||
<FontAwesomeIcon icon={faServer} />
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-wrap items-baseline gap-2">
|
||||
<span className="text-sm font-semibold text-text/90">
|
||||
{node.hostname}
|
||||
</span>
|
||||
{node.ip ? (
|
||||
<span className="font-mono text-xs text-text/60">
|
||||
{node.ip}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-2 flex flex-wrap gap-4 text-xs text-text/60">
|
||||
{node.timestamp ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<FontAwesomeIcon icon={faClock} className="text-[10px]" />
|
||||
{node.timestamp}
|
||||
</span>
|
||||
) : null}
|
||||
{node.serverInfo ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<FontAwesomeIcon
|
||||
icon={faNetworkWired}
|
||||
className="text-[10px]"
|
||||
/>
|
||||
{node.serverInfo}
|
||||
</span>
|
||||
) : null}
|
||||
{delayLabel ? (
|
||||
<span className="flex items-center gap-2 font-mono text-[11px] text-text/50">
|
||||
+{delayLabel}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{index < hopChain.length - 1 ? (
|
||||
<div
|
||||
data-testid={`hop-chain-connector-${node.index}`}
|
||||
className="ml-5 flex items-center gap-3 py-2 text-text/40"
|
||||
>
|
||||
<span className="h-8 w-px rounded-full bg-info/20" />
|
||||
<FontAwesomeIcon icon={faArrowDown} className="text-xs" />
|
||||
<span className="h-8 w-px rounded-full bg-info/20" />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user