chore: verify and finalize task completion for existing refactor tasks
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
makeStyles,
|
makeStyles,
|
||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCardStyles } from "../theme/commonStyles";
|
||||||
import type { BanOriginFilter, TimeRange } from "../types/ban";
|
import type { BanOriginFilter, TimeRange } from "../types/ban";
|
||||||
import {
|
import {
|
||||||
BAN_ORIGIN_FILTER_LABELS,
|
BAN_ORIGIN_FILTER_LABELS,
|
||||||
@@ -57,20 +58,6 @@ const useStyles = makeStyles({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: tokens.spacingVerticalS,
|
gap: tokens.spacingVerticalS,
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
paddingTop: tokens.spacingVerticalS,
|
paddingTop: tokens.spacingVerticalS,
|
||||||
paddingBottom: tokens.spacingVerticalS,
|
paddingBottom: tokens.spacingVerticalS,
|
||||||
paddingLeft: tokens.spacingHorizontalM,
|
paddingLeft: tokens.spacingHorizontalM,
|
||||||
@@ -107,9 +94,10 @@ export function DashboardFilterBar({
|
|||||||
onOriginFilterChange,
|
onOriginFilterChange,
|
||||||
}: DashboardFilterBarProps): React.JSX.Element {
|
}: DashboardFilterBarProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const cardStyles = useCardStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={`${styles.container} ${cardStyles.card}`}>
|
||||||
{/* Time-range group */}
|
{/* Time-range group */}
|
||||||
<div className={styles.group}>
|
<div className={styles.group}>
|
||||||
<Text weight="semibold" size={300}>
|
<Text weight="semibold" size={300}>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
tokens,
|
tokens,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCardStyles } from "../theme/commonStyles";
|
||||||
import { ArrowClockwiseRegular, ShieldRegular } from "@fluentui/react-icons";
|
import { ArrowClockwiseRegular, ShieldRegular } from "@fluentui/react-icons";
|
||||||
import { useServerStatus } from "../hooks/useServerStatus";
|
import { useServerStatus } from "../hooks/useServerStatus";
|
||||||
|
|
||||||
@@ -31,20 +32,6 @@ const useStyles = makeStyles({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: tokens.spacingHorizontalL,
|
gap: tokens.spacingHorizontalL,
|
||||||
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalL}`,
|
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalL}`,
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
marginBottom: tokens.spacingVerticalL,
|
marginBottom: tokens.spacingVerticalL,
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
},
|
},
|
||||||
@@ -85,8 +72,10 @@ export function ServerStatusBar(): React.JSX.Element {
|
|||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const { status, loading, error, refresh } = useServerStatus();
|
const { status, loading, error, refresh } = useServerStatus();
|
||||||
|
|
||||||
|
const cardStyles = useCardStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.bar} role="status" aria-label="fail2ban server status">
|
<div className={`${cardStyles.card} ${styles.bar}`} role="status" aria-label="fail2ban server status">
|
||||||
{/* ---------------------------------------------------------------- */}
|
{/* ---------------------------------------------------------------- */}
|
||||||
{/* Online / Offline badge */}
|
{/* Online / Offline badge */}
|
||||||
{/* ---------------------------------------------------------------- */}
|
{/* ---------------------------------------------------------------- */}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { ComposableMap, ZoomableGroup, Geography, useGeographies } from "react-simple-maps";
|
import { ComposableMap, ZoomableGroup, Geography, useGeographies } from "react-simple-maps";
|
||||||
import { Button, makeStyles, tokens } from "@fluentui/react-components";
|
import { Button, makeStyles, tokens } from "@fluentui/react-components";
|
||||||
|
import { useCardStyles } from "../theme/commonStyles";
|
||||||
import type { GeoPermissibleObjects } from "d3-geo";
|
import type { GeoPermissibleObjects } from "d3-geo";
|
||||||
import { ISO_NUMERIC_TO_ALPHA2 } from "../data/isoNumericToAlpha2";
|
import { ISO_NUMERIC_TO_ALPHA2 } from "../data/isoNumericToAlpha2";
|
||||||
import { getBanCountColor } from "../utils/mapColors";
|
import { getBanCountColor } from "../utils/mapColors";
|
||||||
@@ -29,9 +30,6 @@ const useStyles = makeStyles({
|
|||||||
mapWrapper: {
|
mapWrapper: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
backgroundColor: tokens.colorNeutralBackground2,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
border: `1px solid ${tokens.colorNeutralStroke1}`,
|
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
countLabel: {
|
countLabel: {
|
||||||
@@ -211,6 +209,7 @@ export function WorldMap({
|
|||||||
thresholdHigh = 100,
|
thresholdHigh = 100,
|
||||||
}: WorldMapProps): React.JSX.Element {
|
}: WorldMapProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const cardStyles = useCardStyles();
|
||||||
const [zoom, setZoom] = useState<number>(1);
|
const [zoom, setZoom] = useState<number>(1);
|
||||||
const [center, setCenter] = useState<[number, number]>([0, 0]);
|
const [center, setCenter] = useState<[number, number]>([0, 0]);
|
||||||
|
|
||||||
@@ -229,7 +228,7 @@ export function WorldMap({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.mapWrapper}
|
className={`${cardStyles.card} ${styles.mapWrapper}`}
|
||||||
role="img"
|
role="img"
|
||||||
aria-label="World map showing banned IP counts by country. Click a country to filter the table below."
|
aria-label="World map showing banned IP counts by country. Click a country to filter the table below."
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
type TableColumnDefinition,
|
type TableColumnDefinition,
|
||||||
createTableColumn,
|
createTableColumn,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
||||||
import { formatTimestamp } from "../../utils/formatDate";
|
import { formatTimestamp } from "../../utils/formatDate";
|
||||||
import {
|
import {
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
@@ -54,26 +55,6 @@ const PAGE_SIZE_OPTIONS = [10, 25, 50, 100] as const;
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
root: {
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: tokens.spacingVerticalS,
|
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
padding: tokens.spacingVerticalM,
|
|
||||||
},
|
|
||||||
header: {
|
header: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -243,6 +224,7 @@ export function BannedIpsSection({
|
|||||||
onUnban,
|
onUnban,
|
||||||
}: BannedIpsSectionProps): React.JSX.Element {
|
}: BannedIpsSectionProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
|
|
||||||
const rows: BanRow[] = items.map((ban) => ({
|
const rows: BanRow[] = items.map((ban) => ({
|
||||||
ban,
|
ban,
|
||||||
@@ -252,7 +234,7 @@ export function BannedIpsSection({
|
|||||||
const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 1;
|
const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={sectionStyles.section}>
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.headerLeft}>
|
<div className={styles.headerLeft}>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
makeStyles,
|
makeStyles,
|
||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCommonSectionStyles } from "../theme/commonStyles";
|
||||||
import {
|
import {
|
||||||
AddRegular,
|
AddRegular,
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
@@ -69,35 +70,7 @@ const useStyles = makeStyles({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: tokens.spacingVerticalXL,
|
gap: tokens.spacingVerticalXL,
|
||||||
},
|
},
|
||||||
section: {
|
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
padding: tokens.spacingVerticalM,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: tokens.spacingVerticalS,
|
|
||||||
},
|
|
||||||
sectionHeader: {
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
paddingBottom: tokens.spacingVerticalS,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
},
|
|
||||||
tableWrapper: { overflowX: "auto" },
|
tableWrapper: { overflowX: "auto" },
|
||||||
actionsCell: { display: "flex", gap: tokens.spacingHorizontalS, flexWrap: "wrap" },
|
actionsCell: { display: "flex", gap: tokens.spacingHorizontalS, flexWrap: "wrap" },
|
||||||
mono: { fontFamily: "Consolas, 'Courier New', monospace", fontSize: "12px" },
|
mono: { fontFamily: "Consolas, 'Courier New', monospace", fontSize: "12px" },
|
||||||
@@ -400,6 +373,7 @@ interface SourcesSectionProps {
|
|||||||
|
|
||||||
function SourcesSection({ onRunImport, runImportRunning }: SourcesSectionProps): React.JSX.Element {
|
function SourcesSection({ onRunImport, runImportRunning }: SourcesSectionProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const { sources, loading, error, refresh, createSource, updateSource, removeSource, previewSource } =
|
const { sources, loading, error, refresh, createSource, updateSource, removeSource, previewSource } =
|
||||||
useBlocklists();
|
useBlocklists();
|
||||||
|
|
||||||
@@ -471,8 +445,8 @@ function SourcesSection({ onRunImport, runImportRunning }: SourcesSectionProps):
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text size={500} weight="semibold">
|
<Text size={500} weight="semibold">
|
||||||
Blocklist Sources
|
Blocklist Sources
|
||||||
</Text>
|
</Text>
|
||||||
@@ -631,6 +605,7 @@ interface ScheduleSectionProps {
|
|||||||
|
|
||||||
function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps): React.JSX.Element {
|
function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const { info, loading, error, saveSchedule } = useSchedule();
|
const { info, loading, error, saveSchedule } = useSchedule();
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [saveMsg, setSaveMsg] = useState<string | null>(null);
|
const [saveMsg, setSaveMsg] = useState<string | null>(null);
|
||||||
@@ -663,8 +638,8 @@ function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps
|
|||||||
}, [draft, saveSchedule]);
|
}, [draft, saveSchedule]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text size={500} weight="semibold">
|
<Text size={500} weight="semibold">
|
||||||
Import Schedule
|
Import Schedule
|
||||||
</Text>
|
</Text>
|
||||||
@@ -810,11 +785,12 @@ function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps
|
|||||||
|
|
||||||
function ImportLogSection(): React.JSX.Element {
|
function ImportLogSection(): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const { data, loading, error, page, setPage, refresh } = useImportLog(undefined, 20);
|
const { data, loading, error, page, setPage, refresh } = useImportLog(undefined, 20);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text size={500} weight="semibold">
|
<Text size={500} weight="semibold">
|
||||||
Import Log
|
Import Log
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { DashboardFilterBar } from "../components/DashboardFilterBar";
|
|||||||
import { ServerStatusBar } from "../components/ServerStatusBar";
|
import { ServerStatusBar } from "../components/ServerStatusBar";
|
||||||
import { TopCountriesBarChart } from "../components/TopCountriesBarChart";
|
import { TopCountriesBarChart } from "../components/TopCountriesBarChart";
|
||||||
import { TopCountriesPieChart } from "../components/TopCountriesPieChart";
|
import { TopCountriesPieChart } from "../components/TopCountriesPieChart";
|
||||||
|
import { useCommonSectionStyles } from "../theme/commonStyles";
|
||||||
import { useDashboardCountryData } from "../hooks/useDashboardCountryData";
|
import { useDashboardCountryData } from "../hooks/useDashboardCountryData";
|
||||||
import type { BanOriginFilter, TimeRange } from "../types/ban";
|
import type { BanOriginFilter, TimeRange } from "../types/ban";
|
||||||
|
|
||||||
@@ -29,26 +30,6 @@ const useStyles = makeStyles({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: tokens.spacingVerticalM,
|
gap: tokens.spacingVerticalM,
|
||||||
},
|
},
|
||||||
section: {
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: tokens.spacingVerticalS,
|
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
padding: tokens.spacingVerticalM,
|
|
||||||
},
|
|
||||||
sectionHeader: {
|
sectionHeader: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -93,6 +74,8 @@ export function DashboardPage(): React.JSX.Element {
|
|||||||
const { countries, countryNames, isLoading: countryLoading, error: countryError, reload: reloadCountry } =
|
const { countries, countryNames, isLoading: countryLoading, error: countryError, reload: reloadCountry } =
|
||||||
useDashboardCountryData(timeRange, originFilter);
|
useDashboardCountryData(timeRange, originFilter);
|
||||||
|
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={styles.root}>
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
@@ -113,7 +96,7 @@ export function DashboardPage(): React.JSX.Element {
|
|||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
{/* Ban Trend section */}
|
{/* Ban Trend section */}
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={styles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Ban Trend
|
Ban Trend
|
||||||
@@ -127,7 +110,7 @@ export function DashboardPage(): React.JSX.Element {
|
|||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
{/* Charts section */}
|
{/* Charts section */}
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={styles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Top Countries
|
Top Countries
|
||||||
@@ -162,7 +145,7 @@ export function DashboardPage(): React.JSX.Element {
|
|||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
{/* Ban list section */}
|
{/* Ban list section */}
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={styles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Ban List
|
Ban List
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
makeStyles,
|
makeStyles,
|
||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCardStyles } from "../theme/commonStyles";
|
||||||
import {
|
import {
|
||||||
ArrowCounterclockwiseRegular,
|
ArrowCounterclockwiseRegular,
|
||||||
ArrowLeftRegular,
|
ArrowLeftRegular,
|
||||||
@@ -118,9 +119,6 @@ const useStyles = makeStyles({
|
|||||||
gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
|
gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
|
||||||
gap: tokens.spacingVerticalM,
|
gap: tokens.spacingVerticalM,
|
||||||
padding: tokens.spacingVerticalM,
|
padding: tokens.spacingVerticalM,
|
||||||
background: tokens.colorNeutralBackground2,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
border: `1px solid ${tokens.colorNeutralStroke1}`,
|
|
||||||
marginBottom: tokens.spacingVerticalM,
|
marginBottom: tokens.spacingVerticalM,
|
||||||
},
|
},
|
||||||
detailField: {
|
detailField: {
|
||||||
@@ -222,6 +220,7 @@ interface IpDetailViewProps {
|
|||||||
|
|
||||||
function IpDetailView({ ip, onBack }: IpDetailViewProps): React.JSX.Element {
|
function IpDetailView({ ip, onBack }: IpDetailViewProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const cardStyles = useCardStyles();
|
||||||
const { detail, loading, error, refresh } = useIpHistory(ip);
|
const { detail, loading, error, refresh } = useIpHistory(ip);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -278,7 +277,7 @@ function IpDetailView({ ip, onBack }: IpDetailViewProps): React.JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary grid */}
|
{/* Summary grid */}
|
||||||
<div className={styles.detailGrid}>
|
<div className={`${cardStyles.card} ${styles.detailGrid}`}>
|
||||||
<div className={styles.detailField}>
|
<div className={styles.detailField}>
|
||||||
<span className={styles.detailLabel}>Total Bans</span>
|
<span className={styles.detailLabel}>Total Bans</span>
|
||||||
<span className={styles.detailValue}>{String(detail.total_bans)}</span>
|
<span className={styles.detailValue}>{String(detail.total_bans)}</span>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
makeStyles,
|
makeStyles,
|
||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCommonSectionStyles } from "../theme/commonStyles";
|
||||||
import {
|
import {
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
ArrowLeftRegular,
|
ArrowLeftRegular,
|
||||||
@@ -53,36 +54,7 @@ const useStyles = makeStyles({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: tokens.spacingHorizontalS,
|
gap: tokens.spacingHorizontalS,
|
||||||
},
|
},
|
||||||
section: {
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: tokens.spacingVerticalS,
|
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
padding: tokens.spacingVerticalM,
|
|
||||||
},
|
|
||||||
sectionHeader: {
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: tokens.spacingHorizontalM,
|
|
||||||
paddingBottom: tokens.spacingVerticalS,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
},
|
|
||||||
headerRow: {
|
headerRow: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -181,6 +153,7 @@ interface JailInfoProps {
|
|||||||
|
|
||||||
function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload }: JailInfoProps): React.JSX.Element {
|
function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload }: JailInfoProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [ctrlError, setCtrlError] = useState<string | null>(null);
|
const [ctrlError, setCtrlError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -206,8 +179,8 @@ function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<div className={styles.headerRow}>
|
<div className={styles.headerRow}>
|
||||||
<Text
|
<Text
|
||||||
size={600}
|
size={600}
|
||||||
@@ -334,10 +307,10 @@ function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
|
function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const sectionStyles = useCommonSectionStyles();
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Log Paths & Patterns
|
Log Paths & Patterns
|
||||||
</Text>
|
</Text>
|
||||||
@@ -374,12 +347,13 @@ function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
|
|||||||
|
|
||||||
function BantimeEscalationSection({ jail }: { jail: Jail }): React.JSX.Element | null {
|
function BantimeEscalationSection({ jail }: { jail: Jail }): React.JSX.Element | null {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const esc = jail.bantime_escalation;
|
const esc = jail.bantime_escalation;
|
||||||
if (!esc?.increment) return null;
|
if (!esc?.increment) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Ban-time Escalation
|
Ban-time Escalation
|
||||||
</Text>
|
</Text>
|
||||||
@@ -445,6 +419,7 @@ function IgnoreListSection({
|
|||||||
onToggleIgnoreSelf,
|
onToggleIgnoreSelf,
|
||||||
}: IgnoreListSectionProps): React.JSX.Element {
|
}: IgnoreListSectionProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const [inputVal, setInputVal] = useState("");
|
const [inputVal, setInputVal] = useState("");
|
||||||
const [opError, setOpError] = useState<string | null>(null);
|
const [opError, setOpError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -470,8 +445,8 @@ function IgnoreListSection({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalM }}>
|
<div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalM }}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Ignore List (IP Whitelist)
|
Ignore List (IP Whitelist)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
type TableColumnDefinition,
|
type TableColumnDefinition,
|
||||||
createTableColumn,
|
createTableColumn,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
|
import { useCardStyles, useCommonSectionStyles } from "../theme/commonStyles";
|
||||||
import {
|
import {
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
ArrowSyncRegular,
|
ArrowSyncRegular,
|
||||||
@@ -58,36 +59,7 @@ const useStyles = makeStyles({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: tokens.spacingVerticalL,
|
gap: tokens.spacingVerticalL,
|
||||||
},
|
},
|
||||||
section: {
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: tokens.spacingVerticalS,
|
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
padding: tokens.spacingVerticalM,
|
|
||||||
},
|
|
||||||
sectionHeader: {
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: tokens.spacingHorizontalM,
|
|
||||||
paddingBottom: tokens.spacingVerticalS,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
},
|
|
||||||
tableWrapper: { overflowX: "auto" },
|
tableWrapper: { overflowX: "auto" },
|
||||||
centred: {
|
centred: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -117,20 +89,6 @@ const useStyles = makeStyles({
|
|||||||
gap: tokens.spacingVerticalS,
|
gap: tokens.spacingVerticalS,
|
||||||
marginTop: tokens.spacingVerticalS,
|
marginTop: tokens.spacingVerticalS,
|
||||||
padding: tokens.spacingVerticalS,
|
padding: tokens.spacingVerticalS,
|
||||||
backgroundColor: tokens.colorNeutralBackground2,
|
|
||||||
borderRadius: tokens.borderRadiusMedium,
|
|
||||||
borderTopWidth: "1px",
|
|
||||||
borderTopStyle: "solid",
|
|
||||||
borderTopColor: tokens.colorNeutralStroke2,
|
|
||||||
borderRightWidth: "1px",
|
|
||||||
borderRightStyle: "solid",
|
|
||||||
borderRightColor: tokens.colorNeutralStroke2,
|
|
||||||
borderBottomWidth: "1px",
|
|
||||||
borderBottomStyle: "solid",
|
|
||||||
borderBottomColor: tokens.colorNeutralStroke2,
|
|
||||||
borderLeftWidth: "1px",
|
|
||||||
borderLeftStyle: "solid",
|
|
||||||
borderLeftColor: tokens.colorNeutralStroke2,
|
|
||||||
},
|
},
|
||||||
lookupRow: {
|
lookupRow: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -208,6 +166,7 @@ const jailColumns: TableColumnDefinition<JailSummary>[] = [
|
|||||||
|
|
||||||
function JailOverviewSection(): React.JSX.Element {
|
function JailOverviewSection(): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const { jails, total, loading, error, refresh, startJail, stopJail, setIdle, reloadJail, reloadAll } =
|
const { jails, total, loading, error, refresh, startJail, stopJail, setIdle, reloadJail, reloadAll } =
|
||||||
useJails();
|
useJails();
|
||||||
const [opError, setOpError] = useState<string | null>(null);
|
const [opError, setOpError] = useState<string | null>(null);
|
||||||
@@ -220,8 +179,8 @@ function JailOverviewSection(): React.JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Jail Overview
|
Jail Overview
|
||||||
{total > 0 && (
|
{total > 0 && (
|
||||||
@@ -358,6 +317,7 @@ interface BanUnbanFormProps {
|
|||||||
|
|
||||||
function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.JSX.Element {
|
function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
const [banIpVal, setBanIpVal] = useState("");
|
const [banIpVal, setBanIpVal] = useState("");
|
||||||
const [banJail, setBanJail] = useState("");
|
const [banJail, setBanJail] = useState("");
|
||||||
const [unbanIpVal, setUnbanIpVal] = useState("");
|
const [unbanIpVal, setUnbanIpVal] = useState("");
|
||||||
@@ -415,8 +375,8 @@ function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.J
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
Ban / Unban IP
|
Ban / Unban IP
|
||||||
</Text>
|
</Text>
|
||||||
@@ -544,6 +504,8 @@ function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.J
|
|||||||
|
|
||||||
function IpLookupSection(): React.JSX.Element {
|
function IpLookupSection(): React.JSX.Element {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const sectionStyles = useCommonSectionStyles();
|
||||||
|
const cardStyles = useCardStyles();
|
||||||
const { result, loading, error, lookup, clear } = useIpLookup();
|
const { result, loading, error, lookup, clear } = useIpLookup();
|
||||||
const [inputVal, setInputVal] = useState("");
|
const [inputVal, setInputVal] = useState("");
|
||||||
|
|
||||||
@@ -554,8 +516,8 @@ function IpLookupSection(): React.JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.section}>
|
<div className={sectionStyles.section}>
|
||||||
<div className={styles.sectionHeader}>
|
<div className={sectionStyles.sectionHeader}>
|
||||||
<Text as="h2" size={500} weight="semibold">
|
<Text as="h2" size={500} weight="semibold">
|
||||||
IP Lookup
|
IP Lookup
|
||||||
</Text>
|
</Text>
|
||||||
@@ -595,7 +557,7 @@ function IpLookupSection(): React.JSX.Element {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<div className={styles.lookupResult}>
|
<div className={`${cardStyles.card} ${styles.lookupResult}`}>
|
||||||
<div className={styles.lookupRow}>
|
<div className={styles.lookupRow}>
|
||||||
<Text className={styles.lookupLabel}>IP:</Text>
|
<Text className={styles.lookupLabel}>IP:</Text>
|
||||||
<Text className={styles.mono}>{result.ip}</Text>
|
<Text className={styles.mono}>{result.ip}</Text>
|
||||||
|
|||||||
30
frontend/src/theme/commonStyles.ts
Normal file
30
frontend/src/theme/commonStyles.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { makeStyles, tokens } from "@fluentui/react-components";
|
||||||
|
|
||||||
|
export const useCommonSectionStyles = makeStyles({
|
||||||
|
section: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: tokens.spacingVerticalS,
|
||||||
|
backgroundColor: tokens.colorNeutralBackground1,
|
||||||
|
borderRadius: tokens.borderRadiusMedium,
|
||||||
|
border: `1px solid ${tokens.colorNeutralStroke2}`,
|
||||||
|
padding: tokens.spacingVerticalM,
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
gap: tokens.spacingHorizontalM,
|
||||||
|
paddingBottom: tokens.spacingVerticalS,
|
||||||
|
borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useCardStyles = makeStyles({
|
||||||
|
card: {
|
||||||
|
backgroundColor: tokens.colorNeutralBackground1,
|
||||||
|
borderRadius: tokens.borderRadiusMedium,
|
||||||
|
border: `1px solid ${tokens.colorNeutralStroke2}`,
|
||||||
|
padding: tokens.spacingVerticalM,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user