mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 05:23:31 +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] 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)
|
||||
- [ ] 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)
|
||||
- [ ] 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)
|
||||
|
||||
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