Add country-specific companion table filtering for map page

This commit is contained in:
2026-04-05 22:12:06 +02:00
parent c03a5c1cbc
commit c51858ec71
13 changed files with 332 additions and 85 deletions

View File

@@ -0,0 +1,34 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { Mock } from "vitest";
import { ENDPOINTS } from "./endpoints";
import { fetchBansByCountry } from "./map";
import { get } from "./client";
vi.mock("./client", () => ({
get: vi.fn(),
}));
const mockedGet = get as Mock;
describe("fetchBansByCountry", () => {
beforeEach(() => {
mockedGet.mockReset();
mockedGet.mockResolvedValue({ countries: {}, country_names: {}, bans: [], total: 0 });
});
it("appends country_code when provided", async () => {
await fetchBansByCountry("24h", "all", "fail2ban", "US");
expect(get).toHaveBeenCalledWith(
`${ENDPOINTS.dashboardBansByCountry}?range=24h&country_code=US`
);
});
it("does not append country_code when undefined", async () => {
await fetchBansByCountry("24h", "all", "fail2ban");
expect(get).toHaveBeenCalledWith(
`${ENDPOINTS.dashboardBansByCountry}?range=24h`
);
});
});

View File

@@ -18,6 +18,7 @@ export async function fetchBansByCountry(
range: TimeRange = "24h",
origin: BanOriginFilter = "all",
source: "fail2ban" | "archive" = "fail2ban",
countryCode?: string,
): Promise<BansByCountryResponse> {
const params = new URLSearchParams({ range });
if (origin !== "all") {
@@ -26,5 +27,8 @@ export async function fetchBansByCountry(
if (source !== "fail2ban") {
params.set("source", source);
}
if (countryCode) {
params.set("country_code", countryCode);
}
return get<BansByCountryResponse>(`${ENDPOINTS.dashboardBansByCountry}?${params.toString()}`);
}

View File

@@ -44,6 +44,7 @@ export function useMapData(
range: TimeRange = "24h",
origin: BanOriginFilter = "all",
source: "fail2ban" | "archive" = "fail2ban",
countryCode?: string,
): UseMapDataResult {
const [data, setData] = useState<BansByCountryResponse | null>(null);
const [loading, setLoading] = useState(true);
@@ -65,7 +66,7 @@ export function useMapData(
abortRef.current?.abort();
abortRef.current = new AbortController();
fetchBansByCountry(range, origin, source)
fetchBansByCountry(range, origin, source, countryCode)
.then((resp) => {
setData(resp);
})
@@ -76,7 +77,7 @@ export function useMapData(
setLoading(false);
});
}, DEBOUNCE_MS);
}, [range, origin, source]);
}, [range, origin, source, countryCode]);
useEffect((): (() => void) => {
load();

View File

@@ -101,7 +101,7 @@ export function MapPage(): React.JSX.Element {
const source = range === "24h" ? "fail2ban" : "archive";
const { countries, countryNames, bans, total, loading, error, refresh } =
useMapData(range, originFilter, source);
useMapData(range, originFilter, source, selectedCountry ?? undefined);
const {
thresholds: mapThresholds,