Add dashboard filter context to remove prop drilling

This commit is contained in:
2026-04-21 20:08:54 +02:00
parent b6d9c649ca
commit b7fbad0328
8 changed files with 164 additions and 39 deletions

View File

@@ -9,6 +9,7 @@
*/
import { memo, useMemo } from "react";
import { useDashboardFilters } from "../providers/DashboardFilterProvider";
import {
Badge,
Button,
@@ -41,7 +42,7 @@ interface BanTableProps {
* Active time-range preset — controlled by the parent `DashboardPage`.
* Changing this value triggers a re-fetch.
*/
timeRange: TimeRange;
timeRange?: TimeRange;
/**
* Active origin filter — controlled by the parent `DashboardPage`.
* Changing this value triggers a re-fetch and resets to page 1.
@@ -191,9 +192,17 @@ function buildBanColumns(styles: ReturnType<typeof useStyles>): TableColumnDefin
* @param props.timeRange - Active time-range preset from the parent page.
* @param props.origin - Active origin filter from the parent page.
*/
export const BanTable = memo(function BanTable({ timeRange, origin = "all", source = "fail2ban" }: BanTableProps): React.JSX.Element {
export const BanTable = memo(function BanTable({ timeRange, origin, source }: BanTableProps): React.JSX.Element {
const styles = useStyles();
const { banItems, total, page, setPage, loading, error, refresh } = useBans(timeRange, origin, source);
const context = useDashboardFilters();
const effectiveTimeRange = timeRange ?? context.timeRange;
const effectiveOrigin = origin ?? context.originFilter;
const effectiveSource = source ?? context.source;
const { banItems, total, page, setPage, loading, error, refresh } = useBans(
effectiveTimeRange,
effectiveOrigin,
effectiveSource,
);
const banColumns = useMemo(() => buildBanColumns(styles), [styles]);

View File

@@ -5,6 +5,7 @@
*/
import { memo, useMemo } from "react";
import { useDashboardFilters } from "../providers/DashboardFilterProvider";
import {
Area,
AreaChart,
@@ -52,9 +53,9 @@ const TICK_INTERVAL: Record<TimeRange, number> = {
/** Props for {@link BanTrendChart}. */
interface BanTrendChartProps {
/** Time-range preset controlling the query window. */
timeRange: TimeRange;
timeRange?: TimeRange;
/** Origin filter controlling which bans are included. */
origin: BanOriginFilter;
origin?: BanOriginFilter;
/** Data source used for the chart. */
source?: "fail2ban" | "archive";
}
@@ -198,14 +199,18 @@ function TrendTooltip(props: TrendTooltipProps): React.JSX.Element | null {
export const BanTrendChart = memo(function BanTrendChart({
timeRange,
origin,
source = "fail2ban",
source,
}: BanTrendChartProps): React.JSX.Element {
const styles = useStyles();
const context = useDashboardFilters();
const effectiveTimeRange = timeRange ?? context.timeRange;
const effectiveOrigin = origin ?? context.originFilter;
const effectiveSource = source ?? context.source;
const { colorMode } = useThemeMode();
const { buckets, loading, error, reload } = useBanTrend(timeRange, origin, source);
const { buckets, loading, error, reload } = useBanTrend(effectiveTimeRange, effectiveOrigin, effectiveSource);
const isEmpty = buckets.every((b) => b.count === 0);
const entries = buildEntries(buckets, timeRange);
const entries = buildEntries(buckets, effectiveTimeRange);
const { primaryColour, axisColour, gridColour } = useMemo(
() => {
void colorMode;
@@ -217,7 +222,7 @@ export const BanTrendChart = memo(function BanTrendChart({
},
[colorMode],
);
const tickInterval = TICK_INTERVAL[timeRange];
const tickInterval = TICK_INTERVAL[effectiveTimeRange];
const tooltipContent = useMemo(
() => {

View File

@@ -16,6 +16,7 @@ import {
tokens,
} from "@fluentui/react-components";
import { useCardStyles } from "../components/commonStyles";
import { useDashboardFilters } from "../providers/DashboardFilterProvider";
import type { BanOriginFilter, TimeRange } from "../types/ban";
import {
BAN_ORIGIN_FILTER_LABELS,
@@ -29,13 +30,13 @@ import {
/** Props for {@link DashboardFilterBar}. */
export interface DashboardFilterBarProps {
/** Currently selected time-range preset. */
timeRange: TimeRange;
timeRange?: TimeRange;
/** Called when the user selects a different time-range preset. */
onTimeRangeChange: (value: TimeRange) => void;
onTimeRangeChange?: (value: TimeRange) => void;
/** Currently selected origin filter. */
originFilter: BanOriginFilter;
originFilter?: BanOriginFilter;
/** Called when the user selects a different origin filter. */
onOriginFilterChange: (value: BanOriginFilter) => void;
onOriginFilterChange?: (value: BanOriginFilter) => void;
/** Jail filter value (optional). */
jail?: string;
/** Called when the jail filter text changes (optional). */
@@ -106,9 +107,15 @@ export function DashboardFilterBar({
ip,
onIpChange,
}: DashboardFilterBarProps): React.JSX.Element {
const context = useDashboardFilters();
const styles = useStyles();
const cardStyles = useCardStyles();
const currentTimeRange = timeRange ?? context.timeRange;
const handleTimeRangeChange = onTimeRangeChange ?? context.setTimeRange;
const currentOriginFilter = originFilter ?? context.originFilter;
const handleOriginFilterChange = onOriginFilterChange ?? context.setOriginFilter;
return (
<div className={`${styles.container} ${cardStyles.card}`}>
{/* Time-range group */}
@@ -121,10 +128,10 @@ export function DashboardFilterBar({
<ToggleButton
key={r}
size="small"
checked={timeRange === r}
aria-pressed={timeRange === r}
checked={currentTimeRange === r}
aria-pressed={currentTimeRange === r}
onClick={() => {
onTimeRangeChange(r);
handleTimeRangeChange(r);
}}
>
{TIME_RANGE_LABELS[r]}
@@ -148,10 +155,10 @@ export function DashboardFilterBar({
<ToggleButton
key={f}
size="small"
checked={originFilter === f}
aria-pressed={originFilter === f}
checked={currentOriginFilter === f}
aria-pressed={currentOriginFilter === f}
onClick={() => {
onOriginFilterChange(f);
handleOriginFilterChange(f);
}}
>
{BAN_ORIGIN_FILTER_LABELS[f]}

View File

@@ -24,6 +24,7 @@ import {
CHART_PALETTE,
resolveFluentToken,
} from "../utils/chartTheme";
import { useDashboardFilters } from "../providers/DashboardFilterProvider";
import { useThemeMode } from "../providers/ThemeProvider";
import { ChartStateWrapper } from "./ChartStateWrapper";
import { ChartTooltip } from "./ChartTooltip";
@@ -50,9 +51,9 @@ const MIN_CHART_HEIGHT = 180;
/** Props for {@link JailDistributionChart}. */
interface JailDistributionChartProps {
/** Time-range preset controlling the query window. */
timeRange: TimeRange;
timeRange?: TimeRange;
/** Origin filter controlling which bans are included. */
origin: BanOriginFilter;
origin?: BanOriginFilter;
}
/** Internal chart data point shape. */
@@ -135,8 +136,11 @@ export const JailDistributionChart = memo(function JailDistributionChart({
origin,
}: JailDistributionChartProps): React.JSX.Element {
const styles = useStyles();
const context = useDashboardFilters();
const effectiveTimeRange = timeRange ?? context.timeRange;
const effectiveOrigin = origin ?? context.originFilter;
const { colorMode } = useThemeMode();
const { jails, loading, error, reload } = useJailDistribution(timeRange, origin);
const { jails, loading, error, reload } = useJailDistribution(effectiveTimeRange, effectiveOrigin);
const entries = buildEntries(jails);
const chartHeight = Math.max(entries.length * BAR_HEIGHT_PX, MIN_CHART_HEIGHT);