diff --git a/frontend/src/pages/BlocklistsPage.tsx b/frontend/src/pages/BlocklistsPage.tsx
index dd9f728..fa39db2 100644
--- a/frontend/src/pages/BlocklistsPage.tsx
+++ b/frontend/src/pages/BlocklistsPage.tsx
@@ -37,6 +37,7 @@ import {
makeStyles,
tokens,
} from "@fluentui/react-components";
+import { useCommonSectionStyles } from "../theme/commonStyles";
import {
AddRegular,
ArrowClockwiseRegular,
@@ -69,35 +70,7 @@ const useStyles = makeStyles({
flexDirection: "column",
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" },
actionsCell: { display: "flex", gap: tokens.spacingHorizontalS, flexWrap: "wrap" },
mono: { fontFamily: "Consolas, 'Courier New', monospace", fontSize: "12px" },
@@ -400,6 +373,7 @@ interface SourcesSectionProps {
function SourcesSection({ onRunImport, runImportRunning }: SourcesSectionProps): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const { sources, loading, error, refresh, createSource, updateSource, removeSource, previewSource } =
useBlocklists();
@@ -471,8 +445,8 @@ function SourcesSection({ onRunImport, runImportRunning }: SourcesSectionProps):
}, []);
return (
-
-
+
+
Blocklist Sources
@@ -631,6 +605,7 @@ interface ScheduleSectionProps {
function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const { info, loading, error, saveSchedule } = useSchedule();
const [saving, setSaving] = useState(false);
const [saveMsg, setSaveMsg] = useState
(null);
@@ -663,8 +638,8 @@ function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps
}, [draft, saveSchedule]);
return (
-
-
+
+
Import Schedule
@@ -810,11 +785,12 @@ function ScheduleSection({ onRunImport, runImportRunning }: ScheduleSectionProps
function ImportLogSection(): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const { data, loading, error, page, setPage, refresh } = useImportLog(undefined, 20);
return (
-
-
+
+
Import Log
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx
index 8497420..c22abcc 100644
--- a/frontend/src/pages/DashboardPage.tsx
+++ b/frontend/src/pages/DashboardPage.tsx
@@ -15,6 +15,7 @@ import { DashboardFilterBar } from "../components/DashboardFilterBar";
import { ServerStatusBar } from "../components/ServerStatusBar";
import { TopCountriesBarChart } from "../components/TopCountriesBarChart";
import { TopCountriesPieChart } from "../components/TopCountriesPieChart";
+import { useCommonSectionStyles } from "../theme/commonStyles";
import { useDashboardCountryData } from "../hooks/useDashboardCountryData";
import type { BanOriginFilter, TimeRange } from "../types/ban";
@@ -29,26 +30,6 @@ const useStyles = makeStyles({
flexDirection: "column",
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: {
display: "flex",
alignItems: "center",
@@ -93,6 +74,8 @@ export function DashboardPage(): React.JSX.Element {
const { countries, countryNames, isLoading: countryLoading, error: countryError, reload: reloadCountry } =
useDashboardCountryData(timeRange, originFilter);
+ const sectionStyles = useCommonSectionStyles();
+
return (
{/* ------------------------------------------------------------------ */}
@@ -113,7 +96,7 @@ export function DashboardPage(): React.JSX.Element {
{/* ------------------------------------------------------------------ */}
{/* Ban Trend section */}
{/* ------------------------------------------------------------------ */}
-
+
Ban Trend
@@ -127,7 +110,7 @@ export function DashboardPage(): React.JSX.Element {
{/* ------------------------------------------------------------------ */}
{/* Charts section */}
{/* ------------------------------------------------------------------ */}
-
+
Top Countries
@@ -162,7 +145,7 @@ export function DashboardPage(): React.JSX.Element {
{/* ------------------------------------------------------------------ */}
{/* Ban list section */}
{/* ------------------------------------------------------------------ */}
-
+
Ban List
diff --git a/frontend/src/pages/HistoryPage.tsx b/frontend/src/pages/HistoryPage.tsx
index 3d323eb..84f1eac 100644
--- a/frontend/src/pages/HistoryPage.tsx
+++ b/frontend/src/pages/HistoryPage.tsx
@@ -35,6 +35,7 @@ import {
makeStyles,
tokens,
} from "@fluentui/react-components";
+import { useCardStyles } from "../theme/commonStyles";
import {
ArrowCounterclockwiseRegular,
ArrowLeftRegular,
@@ -112,9 +113,6 @@ const useStyles = makeStyles({
gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
gap: tokens.spacingVerticalM,
padding: tokens.spacingVerticalM,
- background: tokens.colorNeutralBackground2,
- borderRadius: tokens.borderRadiusMedium,
- border: `1px solid ${tokens.colorNeutralStroke1}`,
marginBottom: tokens.spacingVerticalM,
},
detailField: {
@@ -216,6 +214,7 @@ interface IpDetailViewProps {
function IpDetailView({ ip, onBack }: IpDetailViewProps): React.JSX.Element {
const styles = useStyles();
+ const cardStyles = useCardStyles();
const { detail, loading, error, refresh } = useIpHistory(ip);
if (loading) {
@@ -272,7 +271,7 @@ function IpDetailView({ ip, onBack }: IpDetailViewProps): React.JSX.Element {
{/* Summary grid */}
-
+
Total Bans
{String(detail.total_bans)}
diff --git a/frontend/src/pages/JailDetailPage.tsx b/frontend/src/pages/JailDetailPage.tsx
index 5005f0c..6659a87 100644
--- a/frontend/src/pages/JailDetailPage.tsx
+++ b/frontend/src/pages/JailDetailPage.tsx
@@ -23,6 +23,7 @@ import {
makeStyles,
tokens,
} from "@fluentui/react-components";
+import { useCommonSectionStyles } from "../theme/commonStyles";
import {
ArrowClockwiseRegular,
ArrowLeftRegular,
@@ -53,36 +54,7 @@ const useStyles = makeStyles({
alignItems: "center",
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: {
display: "flex",
alignItems: "center",
@@ -181,6 +153,7 @@ interface JailInfoProps {
function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload }: JailInfoProps): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const navigate = useNavigate();
const [ctrlError, setCtrlError] = useState
(null);
@@ -206,8 +179,8 @@ function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload
};
return (
-
-
+
+
-
+
+
Log Paths & Patterns
@@ -374,12 +347,13 @@ function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
function BantimeEscalationSection({ jail }: { jail: Jail }): React.JSX.Element | null {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const esc = jail.bantime_escalation;
if (!esc?.increment) return null;
return (
-
-
+
+
Ban-time Escalation
@@ -445,6 +419,7 @@ function IgnoreListSection({
onToggleIgnoreSelf,
}: IgnoreListSectionProps): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const [inputVal, setInputVal] = useState("");
const [opError, setOpError] = useState
(null);
@@ -470,8 +445,8 @@ function IgnoreListSection({
};
return (
-
-
+
+
Ignore List (IP Whitelist)
diff --git a/frontend/src/pages/JailsPage.tsx b/frontend/src/pages/JailsPage.tsx
index 5a7de95..1ee46b3 100644
--- a/frontend/src/pages/JailsPage.tsx
+++ b/frontend/src/pages/JailsPage.tsx
@@ -33,6 +33,7 @@ import {
type TableColumnDefinition,
createTableColumn,
} from "@fluentui/react-components";
+import { useCardStyles, useCommonSectionStyles } from "../theme/commonStyles";
import {
ArrowClockwiseRegular,
ArrowSyncRegular,
@@ -58,36 +59,7 @@ const useStyles = makeStyles({
flexDirection: "column",
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" },
centred: {
display: "flex",
@@ -117,20 +89,6 @@ const useStyles = makeStyles({
gap: tokens.spacingVerticalS,
marginTop: 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: {
display: "flex",
@@ -208,6 +166,7 @@ const jailColumns: TableColumnDefinition[] = [
function JailOverviewSection(): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const { jails, total, loading, error, refresh, startJail, stopJail, setIdle, reloadJail, reloadAll } =
useJails();
const [opError, setOpError] = useState(null);
@@ -220,8 +179,8 @@ function JailOverviewSection(): React.JSX.Element {
};
return (
-
-
+
+
Jail Overview
{total > 0 && (
@@ -358,6 +317,7 @@ interface BanUnbanFormProps {
function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
const [banIpVal, setBanIpVal] = useState("");
const [banJail, setBanJail] = useState("");
const [unbanIpVal, setUnbanIpVal] = useState("");
@@ -415,8 +375,8 @@ function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.J
};
return (
-
-
+
+
Ban / Unban IP
@@ -544,6 +504,8 @@ function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.J
function IpLookupSection(): React.JSX.Element {
const styles = useStyles();
+ const sectionStyles = useCommonSectionStyles();
+ const cardStyles = useCardStyles();
const { result, loading, error, lookup, clear } = useIpLookup();
const [inputVal, setInputVal] = useState("");
@@ -554,8 +516,8 @@ function IpLookupSection(): React.JSX.Element {
};
return (
-
-
+
+
IP Lookup
@@ -595,7 +557,7 @@ function IpLookupSection(): React.JSX.Element {
)}
{result && (
-
+
IP:
{result.ip}
diff --git a/frontend/src/theme/commonStyles.ts b/frontend/src/theme/commonStyles.ts
new file mode 100644
index 0000000..bfd2ee1
--- /dev/null
+++ b/frontend/src/theme/commonStyles.ts
@@ -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,
+ },
+});