diff --git a/Docs/Tasks.md b/Docs/Tasks.md index d7f32a1..037ee84 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -8,49 +8,16 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. ## Open Issues -1. Ban history durability and fail2ban DB size management - - status: completed - - description: BanGUI currently reads fail2ban history directly, but fail2ban's `dbpurgeage` may erase old history and can cause DB growth issues with long retention. Implement a BanGUI-native persistent archive and keep fail2ban DB short-lived. - - acceptance criteria: - - BanGUI can configure and fetch fail2ban `dbpurgeage` and `dbfile` from server API. - - Introduce periodic sync job that reads new fail2ban ban/unban events and writes them to BanGUI app DB. - - Use dedupe logic to avoid duplicate entries (unique constraint by `ip,jail,action,timestamp`). - - Add persistence policy settings (default 365 days) in UI and server config. - - Add backfill workflow on startup for last 7 days if archive empty. - - Existing history API endpoints must support both a `source` filter (`fail2ban`, `archive`) and time range. - - implementation notes: - - Add repository methods `archive_ban_event`, `get_archived_history(...)`, `purge_archived_history(age_seconds)`. - - Add periodic task in `backend/app/tasks/history_sync.py` triggered by scheduler. - - Extend `Backend/app/routers/history.py` to include endpoint `/api/history/archive`. - -2. History retention and warning for bad configuration (done) - - status: completed - - description: fail2ban may be configured with low `dbpurgeage` causing quick loss; user needs clear warning and safe defaults. - - acceptance criteria: - - On server settings load, if `dbpurgeage` < 86400, expose warning state in API. - - UI displays warning banner: "Current fail2ban purge age is under 24h; detailed history may be lost.". - - Allow user to increase `dbpurgeage` through server settings panel; sync to fail2ban using `set dbpurgeage`. - - Add tests for server service response and UI warning logic. - -3. History access from existing BanGUI features - - status: completed - - description: Doors for dashboard and map data should use archived history to avoid data gaps. - - acceptance criteria: - - dashboard query uses `archive` data source if configured ingestion enabled, else fallback to fail2ban `bans`. - - world map grouping includes archived data and can aggregate `count` with timeframe filters. - - API and UI unit tests verify data source fallback. - -4. Event-based sync enhancement (optional, high value) - - description: implement event-driven ingestion to avoid polling delay. - - acceptance criteria: - - Add fail2ban hook or systemd journal watcher to capture ban/unban events in real time. - - Recorded events store to BanGUI archive in transaction-safe manner. - - Add validation for event integrity and order. - ---- - - - - +- **World Map highlight does not reset on mouse leave** (done) + - Location: `frontend/src/components/WorldMap.tsx`, `GeoLayer` component + - Root cause: interactive event handlers (`onMouseEnter`, `onMouseMove`, `onMouseLeave`, `onClick`, `onKeyDown`) were on wrapping `` element, creating mismatch with `Geography` internal hover state. + - Fix applied: + 1. Interactive props and aria attributes are on ``. + 2. Hover highlight uses `style.hover` with derived `fill` from `isSelected/count`. + 3. Tooltip state resets via `Geography.onMouseLeave` -> `setTooltip(null)`. + 4. Test asserts tooltip appears/disappears and button role label is correct. + - Verification commands: + - `cd frontend && npx vitest --run src/components/__tests__/WorldMap.test.tsx` + - `cd frontend && npx vitest --run` diff --git a/frontend/src/components/__tests__/WorldMap.test.tsx b/frontend/src/components/__tests__/WorldMap.test.tsx index a6c08f8..4d70cda 100644 --- a/frontend/src/components/__tests__/WorldMap.test.tsx +++ b/frontend/src/components/__tests__/WorldMap.test.tsx @@ -12,7 +12,7 @@ import { FluentProvider, webLightTheme } from "@fluentui/react-components"; vi.mock("react-simple-maps", () => ({ ComposableMap: ({ children }: { children: React.ReactNode }) =>
{children}
, ZoomableGroup: ({ children }: { children: React.ReactNode }) =>
{children}
, - Geography: ({ children }: { children?: React.ReactNode }) => {children}, + Geography: ({ children, ...props }: { children?: React.ReactNode } & Record) => {children}, useGeographies: () => ({ geographies: [{ rsmKey: "geo-1", id: 840 }], path: { centroid: () => [10, 10] }, @@ -37,7 +37,10 @@ describe("WorldMap", () => { // Tooltip should not be present initially expect(screen.queryByRole("tooltip")).toBeNull(); - const countryButton = screen.getByRole("button", { name: /US: 42 bans/i }); + // Country map area is exposed as an accessible button with an accurate label + const countryButton = screen.getByRole("button", { name: "US: 42 bans" }); + expect(countryButton).toBeInTheDocument(); + fireEvent.mouseEnter(countryButton, { clientX: 10, clientY: 10 }); const tooltip = screen.getByRole("tooltip");