refactoring-backend #3
@@ -8,7 +8,7 @@
|
||||
* Columns: Time, IP, Service, Country, Jail, Ban Count.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -191,7 +191,7 @@ 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 function BanTable({ timeRange, origin = "all", source = "fail2ban" }: BanTableProps): React.JSX.Element {
|
||||
export const BanTable = memo(function BanTable({ timeRange, origin = "all", source = "fail2ban" }: BanTableProps): React.JSX.Element {
|
||||
const styles = useStyles();
|
||||
const { banItems, total, page, setPage, loading, error, refresh } = useBans(timeRange, origin, source);
|
||||
|
||||
@@ -276,4 +276,4 @@ export function BanTable({ timeRange, origin = "all", source = "fail2ban" }: Ban
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Calls `useBanTrend` internally and handles loading, error, and empty states.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
@@ -194,7 +194,7 @@ function TrendTooltip(props: TrendTooltipProps): React.JSX.Element | null {
|
||||
*
|
||||
* @param props - `timeRange` and `origin` filter props.
|
||||
*/
|
||||
export function BanTrendChart({
|
||||
export const BanTrendChart = memo(function BanTrendChart({
|
||||
timeRange,
|
||||
origin,
|
||||
source = "fail2ban",
|
||||
@@ -271,5 +271,5 @@ export function BanTrendChart({
|
||||
</div>
|
||||
</ChartStateWrapper>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* empty states so the parent only needs to pass filter props.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
@@ -129,7 +129,7 @@ function JailTooltip(props: TooltipContentProps): React.JSX.Element | null {
|
||||
*
|
||||
* @param props - `timeRange` and `origin` filter props.
|
||||
*/
|
||||
export function JailDistributionChart({
|
||||
export const JailDistributionChart = memo(function JailDistributionChart({
|
||||
timeRange,
|
||||
origin,
|
||||
}: JailDistributionChartProps): React.JSX.Element {
|
||||
@@ -185,4 +185,4 @@ export function JailDistributionChart({
|
||||
</div>
|
||||
</ChartStateWrapper>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* via the {@link useServerStatus} hook.
|
||||
*/
|
||||
|
||||
import { memo } from "react";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -68,7 +69,7 @@ const useStyles = makeStyles({
|
||||
* Render this at the top of the dashboard page (and any page that should
|
||||
* show live server status).
|
||||
*/
|
||||
export function ServerStatusBar(): React.JSX.Element {
|
||||
export const ServerStatusBar = memo(function ServerStatusBar(): React.JSX.Element {
|
||||
const styles = useStyles();
|
||||
const { status, loading, error, refresh } = useServerStatus();
|
||||
|
||||
@@ -165,4 +166,4 @@ export function ServerStatusBar(): React.JSX.Element {
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* by ban count, sorted descending.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
@@ -137,7 +137,7 @@ function BarTooltip(props: TooltipContentProps): React.JSX.Element | null {
|
||||
* @param props - `countries` map and `countryNames` map from the
|
||||
* `/api/dashboard/bans/by-country` response.
|
||||
*/
|
||||
export function TopCountriesBarChart({
|
||||
export const TopCountriesBarChart = memo(function TopCountriesBarChart({
|
||||
countries,
|
||||
countryNames,
|
||||
}: TopCountriesBarChartProps): React.JSX.Element {
|
||||
@@ -193,4 +193,4 @@ export function TopCountriesBarChart({
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* an "Other" slice aggregating all remaining countries.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import {
|
||||
Cell,
|
||||
Legend,
|
||||
@@ -129,7 +129,7 @@ function PieTooltip(props: TooltipContentProps): React.JSX.Element | null {
|
||||
* @param props - `countries` map and `countryNames` map from the
|
||||
* `/api/dashboard/bans/by-country` response.
|
||||
*/
|
||||
export function TopCountriesPieChart({
|
||||
export const TopCountriesPieChart = memo(function TopCountriesPieChart({
|
||||
countries,
|
||||
countryNames,
|
||||
}: TopCountriesPieChartProps): React.JSX.Element {
|
||||
@@ -194,4 +194,4 @@ export function TopCountriesPieChart({
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -130,7 +131,43 @@ interface WorldMapProps {
|
||||
thresholdHigh?: number;
|
||||
}
|
||||
|
||||
export function WorldMap({
|
||||
function areStringRecordShallowEqual(
|
||||
left: Record<string, string>,
|
||||
right: Record<string, string>,
|
||||
): boolean {
|
||||
const leftKeys = Object.keys(left);
|
||||
const rightKeys = Object.keys(right);
|
||||
if (leftKeys.length !== rightKeys.length) {
|
||||
return false;
|
||||
}
|
||||
return leftKeys.every((key) => left[key] === right[key]);
|
||||
}
|
||||
|
||||
function areNumberRecordShallowEqual(
|
||||
left: Record<string, number>,
|
||||
right: Record<string, number>,
|
||||
): boolean {
|
||||
const leftKeys = Object.keys(left);
|
||||
const rightKeys = Object.keys(right);
|
||||
if (leftKeys.length !== rightKeys.length) {
|
||||
return false;
|
||||
}
|
||||
return leftKeys.every((key) => left[key] === right[key]);
|
||||
}
|
||||
|
||||
function areWorldMapPropsEqual(prev: WorldMapProps, next: WorldMapProps): boolean {
|
||||
return (
|
||||
prev.selectedCountry === next.selectedCountry &&
|
||||
prev.onSelectCountry === next.onSelectCountry &&
|
||||
prev.thresholdLow === next.thresholdLow &&
|
||||
prev.thresholdMedium === next.thresholdMedium &&
|
||||
prev.thresholdHigh === next.thresholdHigh &&
|
||||
areNumberRecordShallowEqual(prev.countries, next.countries) &&
|
||||
areStringRecordShallowEqual(prev.countryNames ?? {}, next.countryNames ?? {})
|
||||
);
|
||||
}
|
||||
|
||||
function WorldMapComponent({
|
||||
countries,
|
||||
countryNames,
|
||||
selectedCountry,
|
||||
@@ -430,3 +467,5 @@ export function WorldMap({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const WorldMap = memo(WorldMapComponent, areWorldMapPropsEqual);
|
||||
|
||||
Reference in New Issue
Block a user