mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 13:33:30 +01:00
MAESTRO: add report search bar
This commit is contained in:
@@ -41,7 +41,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] 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)
|
- [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)
|
||||||
- [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)
|
- [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)
|
- [x] 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)
|
- [ ] 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)
|
- [ ] 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)
|
||||||
- [ ] T036 [US4] Create `frontend/src/components/report/ReportContainer.tsx` — top-level wrapper receiving `AnalysisReport`. Renders: summary stats (total tests, passed, failed, severity breakdown), `TestResultCard` list, `HopChainVisualisation`, `SecurityAppliancesSummary`, `ReportSearchBar`, `ReportExport`. FontAwesome summary icons. Verify `ReportContainer.test.tsx` passes (TDD Green)
|
- [ ] T036 [US4] Create `frontend/src/components/report/ReportContainer.tsx` — top-level wrapper receiving `AnalysisReport`. Renders: summary stats (total tests, passed, failed, severity breakdown), `TestResultCard` list, `HopChainVisualisation`, `SecurityAppliancesSummary`, `ReportSearchBar`, `ReportExport`. FontAwesome summary icons. Verify `ReportContainer.test.tsx` passes (TDD Green)
|
||||||
|
|||||||
87
frontend/src/components/report/ReportSearchBar.tsx
Normal file
87
frontend/src/components/report/ReportSearchBar.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { FormEvent, KeyboardEvent } from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faMagnifyingGlass, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
type ReportSearchBarProps = {
|
||||||
|
query: string;
|
||||||
|
matchCount: number;
|
||||||
|
totalCount: number;
|
||||||
|
onQueryChange: (next: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ReportSearchBar({
|
||||||
|
query,
|
||||||
|
matchCount,
|
||||||
|
totalCount,
|
||||||
|
onQueryChange,
|
||||||
|
}: ReportSearchBarProps) {
|
||||||
|
const handleInput = (event: FormEvent<HTMLInputElement>) => {
|
||||||
|
onQueryChange(event.currentTarget.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
onQueryChange("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
onQueryChange("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasQuery = query.trim().length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
data-testid="report-search-bar"
|
||||||
|
className="rounded-2xl border border-info/10 bg-surface/50 p-4 shadow-[0_0_30px_rgba(15,23,42,0.18)]"
|
||||||
|
>
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<label className="flex flex-1 items-center gap-2 rounded-full border border-info/20 bg-background/40 px-3 py-2 text-sm text-text/70 focus-within:border-info/40">
|
||||||
|
<FontAwesomeIcon icon={faMagnifyingGlass} className="text-xs text-text/40" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={query}
|
||||||
|
onInput={handleInput}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
className="flex-1 bg-transparent text-xs text-text/80 outline-none"
|
||||||
|
placeholder="Search test names, headers, or analysis"
|
||||||
|
data-testid="report-search-input"
|
||||||
|
aria-label="Search report results"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{hasQuery ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center gap-2 rounded-full border border-info/20 bg-background/40 px-3 py-2 text-[11px] font-semibold uppercase tracking-[0.2em] text-text/70 transition hover:border-info/40 hover:text-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-info"
|
||||||
|
onClick={handleClear}
|
||||||
|
aria-label="Clear search"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} className="text-[10px]" />
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<span
|
||||||
|
data-testid="report-search-count"
|
||||||
|
aria-live="polite"
|
||||||
|
className="rounded-full border border-info/20 bg-background/40 px-3 py-2 text-[11px] font-mono text-text/60"
|
||||||
|
>
|
||||||
|
{matchCount} / {totalCount}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex flex-wrap items-center gap-2 text-[11px] text-text/50">
|
||||||
|
<span>{hasQuery ? "Matches for" : "Showing all results"}</span>
|
||||||
|
{hasQuery ? (
|
||||||
|
<span className="rounded-full border border-accent/30 bg-accent/10 px-2 py-0.5 font-mono text-[10px] text-accent">
|
||||||
|
{query}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user