Add origin field and filter for ban sources (Tasks 1 & 2)

- Task 1: Mark imported blocklist IP addresses
  - Add BanOrigin type and _derive_origin() to ban.py model
  - Populate origin field in ban_service list_bans() and bans_by_country()
  - BanTable and MapPage companion table show origin badge column
  - Tests: origin derivation in test_ban_service.py and test_dashboard.py

- Task 2: Add origin filter to dashboard and world map
  - ban_service: _origin_sql_filter() helper; origin param on list_bans()
    and bans_by_country()
  - dashboard router: optional origin query param forwarded to service
  - Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts
  - fetchBans / fetchBansByCountry forward origin to API
  - useBans / useMapData accept and pass origin; page resets on change
  - BanTable accepts origin prop; DashboardPage adds segmented filter
  - MapPage adds origin Select next to time-range picker
  - Tests: origin filter assertions in test_ban_service and test_dashboard
This commit is contained in:
2026-03-07 20:03:43 +01:00
parent 706d2e1df8
commit 53d664de4f
28 changed files with 1637 additions and 103 deletions

View File

@@ -16,8 +16,8 @@ import {
} from "@fluentui/react-components";
import { BanTable } from "../components/BanTable";
import { ServerStatusBar } from "../components/ServerStatusBar";
import type { TimeRange } from "../types/ban";
import { TIME_RANGE_LABELS } from "../types/ban";
import type { BanOriginFilter, TimeRange } from "../types/ban";
import { BAN_ORIGIN_FILTER_LABELS, TIME_RANGE_LABELS } from "../types/ban";
// ---------------------------------------------------------------------------
@@ -73,6 +73,9 @@ const useStyles = makeStyles({
/** Ordered time-range presets for the toolbar. */
const TIME_RANGES: TimeRange[] = ["24h", "7d", "30d", "365d"];
/** Ordered origin filter options for the toolbar. */
const ORIGIN_FILTERS: BanOriginFilter[] = ["all", "blocklist", "selfblock"];
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
@@ -86,6 +89,7 @@ const TIME_RANGES: TimeRange[] = ["24h", "7d", "30d", "365d"];
export function DashboardPage(): React.JSX.Element {
const styles = useStyles();
const [timeRange, setTimeRange] = useState<TimeRange>("24h");
const [originFilter, setOriginFilter] = useState<BanOriginFilter>("all");
return (
<div className={styles.root}>
@@ -119,11 +123,28 @@ export function DashboardPage(): React.JSX.Element {
</ToggleButton>
))}
</Toolbar>
{/* Origin filter */}
<Toolbar aria-label="Origin filter" size="small">
{ORIGIN_FILTERS.map((f) => (
<ToggleButton
key={f}
size="small"
checked={originFilter === f}
onClick={() => {
setOriginFilter(f);
}}
aria-pressed={originFilter === f}
>
{BAN_ORIGIN_FILTER_LABELS[f]}
</ToggleButton>
))}
</Toolbar>
</div>
{/* Ban table */}
<div className={styles.tabContent}>
<BanTable timeRange={timeRange} />
<BanTable timeRange={timeRange} origin={originFilter} />
</div>
</div>
</div>