Relocate misplaced frontend files
This commit is contained in:
@@ -420,6 +420,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
|
|||||||
|
|
||||||
**Goal:** Move `areHistoryQueriesEqual` to `frontend/src/utils/` (either into a new `queryUtils.ts` or an existing utility file if a suitable one exists). Import it back into `HistoryPage.tsx` from that location. Verify the function has no implicit dependency on page-local types — if it relies on `HistoryQuery` from `types/history.ts`, it can still live in utils by importing that type.
|
**Goal:** Move `areHistoryQueriesEqual` to `frontend/src/utils/` (either into a new `queryUtils.ts` or an existing utility file if a suitable one exists). Import it back into `HistoryPage.tsx` from that location. Verify the function has no implicit dependency on page-local types — if it relies on `HistoryQuery` from `types/history.ts`, it can still live in utils by importing that type.
|
||||||
|
|
||||||
|
**Status:** Completed.
|
||||||
|
|
||||||
**Possible traps and issues:**
|
**Possible traps and issues:**
|
||||||
- This is a mechanical move with no behavioral change. The only risk is a stale import if the function is used in more than one place (confirm with a search before moving).
|
- This is a mechanical move with no behavioral change. The only risk is a stale import if the function is used in more than one place (confirm with a search before moving).
|
||||||
- If similar utility functions exist in other page files (a search for `function` declarations inside page files is worthwhile), extract them in the same pass.
|
- If similar utility functions exist in other page files (a search for `function` declarations inside page files is worthwhile), extract them in the same pass.
|
||||||
@@ -445,6 +447,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
|
|||||||
- Move `theme/commonStyles.ts`: if the styles in it are shared across multiple components with no better home, move them to the component directory that uses them most, or rename the file to `components/commonStyles.ts`. The `theme/` directory must contain only `customTheme.ts` and token-related files.
|
- Move `theme/commonStyles.ts`: if the styles in it are shared across multiple components with no better home, move them to the component directory that uses them most, or rename the file to `components/commonStyles.ts`. The `theme/` directory must contain only `customTheme.ts` and token-related files.
|
||||||
- For `configStyles.ts` and `blocklistStyles.ts`: inline each exported `makeStyles` hook into the component file that uses it, or keep the file in the same directory as the components but document that it is a style helper rather than a theme definition. The architecture rule is "co-locate styles in the same file as the component" — a shared styles file is acceptable only if documented as an explicit exception for styles used by multiple components in the same subdirectory.
|
- For `configStyles.ts` and `blocklistStyles.ts`: inline each exported `makeStyles` hook into the component file that uses it, or keep the file in the same directory as the components but document that it is a style helper rather than a theme definition. The architecture rule is "co-locate styles in the same file as the component" — a shared styles file is acceptable only if documented as an explicit exception for styles used by multiple components in the same subdirectory.
|
||||||
|
|
||||||
|
**Status:** Completed.
|
||||||
|
|
||||||
**Possible traps and issues:**
|
**Possible traps and issues:**
|
||||||
- Moving `isoNumericToAlpha2.ts` changes its import path in `WorldMap.tsx` and any other consumer — update all import sites.
|
- Moving `isoNumericToAlpha2.ts` changes its import path in `WorldMap.tsx` and any other consumer — update all import sites.
|
||||||
- Moving `api/map.test.ts` to a `__tests__` subdirectory requires the test runner config (`vitest.config.ts`) to include `api/__tests__/` if it only scans `__tests__/` top-level directories — verify the glob pattern first.
|
- Moving `api/map.test.ts` to a `__tests__` subdirectory requires the test runner config (`vitest.config.ts`) to include `api/__tests__/` if it only scans `__tests__/` top-level directories — verify the glob pattern first.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { Mock } from "vitest";
|
import type { Mock } from "vitest";
|
||||||
import { ENDPOINTS } from "./endpoints";
|
import { ENDPOINTS } from "../endpoints";
|
||||||
import { fetchBansByCountry } from "./map";
|
import { fetchBansByCountry } from "../map";
|
||||||
import { get } from "./client";
|
import { get } from "../client";
|
||||||
|
|
||||||
vi.mock("./client", () => ({
|
vi.mock("../client", () => ({
|
||||||
get: vi.fn(),
|
get: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
makeStyles,
|
makeStyles,
|
||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { useCardStyles } from "../theme/commonStyles";
|
import { useCardStyles } from "../components/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,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
tokens,
|
tokens,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { useCardStyles } from "../theme/commonStyles";
|
import { useCardStyles } from "../components/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";
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import type {
|
|||||||
Topology,
|
Topology,
|
||||||
} from "topojson-specification";
|
} from "topojson-specification";
|
||||||
import worldData from "world-atlas/countries-110m.json";
|
import worldData from "world-atlas/countries-110m.json";
|
||||||
import { useCardStyles } from "../theme/commonStyles";
|
import { useCardStyles } from "../components/commonStyles";
|
||||||
import { ISO_NUMERIC_TO_ALPHA2 } from "../data/isoNumericToAlpha2";
|
import { ISO_NUMERIC_TO_ALPHA2 } from "../utils/isoNumericToAlpha2";
|
||||||
import { getBanCountColor } from "../utils/mapColors";
|
import { getBanCountColor } from "../utils/mapColors";
|
||||||
|
|
||||||
const MAP_WIDTH = 800;
|
const MAP_WIDTH = 800;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button, Badge, Table, TableBody, TableCell, TableCellLayout, TableHeader, TableHeaderCell, TableRow, Text, MessageBar, MessageBarBody, Spinner } from "@fluentui/react-components";
|
import { Button, Badge, Table, TableBody, TableCell, TableCellLayout, TableHeader, TableHeaderCell, TableRow, Text, MessageBar, MessageBarBody, Spinner } from "@fluentui/react-components";
|
||||||
import { ArrowClockwiseRegular } from "@fluentui/react-icons";
|
import { ArrowClockwiseRegular } from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useImportLog } from "../../hooks/useBlocklist";
|
import { useImportLog } from "../../hooks/useBlocklist";
|
||||||
import { useBlocklistStyles } from "./blocklistStyles";
|
import { useBlocklistStyles } from "./blocklistStyles";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Button, Field, Input, MessageBar, MessageBarBody, Select, Spinner, Text } from "@fluentui/react-components";
|
import { Button, Field, Input, MessageBar, MessageBarBody, Select, Spinner, Text } from "@fluentui/react-components";
|
||||||
import { PlayRegular } from "@fluentui/react-icons";
|
import { PlayRegular } from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useSchedule } from "../../hooks/useBlocklist";
|
import { useSchedule } from "../../hooks/useBlocklist";
|
||||||
import { useBlocklistStyles } from "./blocklistStyles";
|
import { useBlocklistStyles } from "./blocklistStyles";
|
||||||
import type { ScheduleConfig, ScheduleFrequency } from "../../types/blocklist";
|
import type { ScheduleConfig, ScheduleFrequency } from "../../types/blocklist";
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
Text,
|
Text,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import {
|
import {
|
||||||
AddRegular,
|
AddRegular,
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
type TableColumnDefinition,
|
type TableColumnDefinition,
|
||||||
createTableColumn,
|
createTableColumn,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { formatTimestamp } from "../../utils/formatDate";
|
import { formatTimestamp } from "../../utils/formatDate";
|
||||||
import {
|
import {
|
||||||
ArrowClockwiseRegular,
|
ArrowClockwiseRegular,
|
||||||
|
|||||||
@@ -15,7 +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 { useCommonSectionStyles } from "../components/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";
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
import { DashboardFilterBar } from "../components/DashboardFilterBar";
|
import { DashboardFilterBar } from "../components/DashboardFilterBar";
|
||||||
import { useHistory } from "../hooks/useHistory";
|
import { useHistory } from "../hooks/useHistory";
|
||||||
import { IpDetailView } from "./history/IpDetailView";
|
import { IpDetailView } from "./history/IpDetailView";
|
||||||
|
import { areHistoryQueriesEqual } from "../utils/queryUtils";
|
||||||
import type { HistoryBanItem, HistoryQuery, TimeRange } from "../types/history";
|
import type { HistoryBanItem, HistoryQuery, TimeRange } from "../types/history";
|
||||||
import type { BanOriginFilter } from "../types/ban";
|
import type { BanOriginFilter } from "../types/ban";
|
||||||
|
|
||||||
@@ -119,25 +120,6 @@ const useStyles = makeStyles({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Utilities
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function areHistoryQueriesEqual(
|
|
||||||
a: HistoryQuery,
|
|
||||||
b: HistoryQuery,
|
|
||||||
): boolean {
|
|
||||||
return (
|
|
||||||
a.range === b.range &&
|
|
||||||
a.origin === b.origin &&
|
|
||||||
a.jail === b.jail &&
|
|
||||||
a.ip === b.ip &&
|
|
||||||
a.source === b.source &&
|
|
||||||
a.page === b.page &&
|
|
||||||
a.page_size === b.page_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Column definitions for the main history table
|
// Column definitions for the main history table
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { ArrowCounterclockwiseRegular, ArrowLeftRegular } from "@fluentui/react-icons";
|
import { ArrowCounterclockwiseRegular, ArrowLeftRegular } from "@fluentui/react-icons";
|
||||||
import { useCardStyles } from "../../theme/commonStyles";
|
import { useCardStyles } from "../../components/commonStyles";
|
||||||
import { useIpHistory } from "../../hooks/useHistory";
|
import { useIpHistory } from "../../hooks/useHistory";
|
||||||
|
|
||||||
interface IpDetailViewProps {
|
interface IpDetailViewProps {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Badge, Text } from "@fluentui/react-components";
|
import { Badge, Text } from "@fluentui/react-components";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
||||||
import type { Jail } from "../../types/jail";
|
import type { Jail } from "../../types/jail";
|
||||||
import { formatSeconds } from "../../utils/formatDate";
|
import { formatSeconds } from "../../utils/formatDate";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { DismissRegular } from "@fluentui/react-icons";
|
import { DismissRegular } from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
||||||
|
|
||||||
interface IgnoreListSectionProps {
|
interface IgnoreListSectionProps {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
PlayRegular,
|
PlayRegular,
|
||||||
StopRegular,
|
StopRegular,
|
||||||
} from "@fluentui/react-icons";
|
} from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
|
||||||
import type { Jail } from "../../types/jail";
|
import type { Jail } from "../../types/jail";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Text } from "@fluentui/react-components";
|
import { Text } from "@fluentui/react-components";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import type { Jail } from "../../types/jail";
|
import type { Jail } from "../../types/jail";
|
||||||
import { CodeList } from "./CodeList";
|
import { CodeList } from "./CodeList";
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
tokens,
|
tokens,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { LockClosedRegular, LockOpenRegular } from "@fluentui/react-icons";
|
import { LockClosedRegular, LockOpenRegular } from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailsPageStyles } from "./jailsPageStyles";
|
import { useJailsPageStyles } from "./jailsPageStyles";
|
||||||
import { ApiError } from "../../api/client";
|
import { ApiError } from "../../api/client";
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { SearchRegular } from "@fluentui/react-icons";
|
import { SearchRegular } from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailsPageStyles } from "./jailsPageStyles";
|
import { useJailsPageStyles } from "./jailsPageStyles";
|
||||||
import { useIpLookup } from "../../hooks/useJails";
|
import { useIpLookup } from "../../hooks/useJails";
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
PlayRegular,
|
PlayRegular,
|
||||||
StopRegular,
|
StopRegular,
|
||||||
} from "@fluentui/react-icons";
|
} from "@fluentui/react-icons";
|
||||||
import { useCommonSectionStyles } from "../../theme/commonStyles";
|
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||||
import { useJailsPageStyles } from "./jailsPageStyles";
|
import { useJailsPageStyles } from "./jailsPageStyles";
|
||||||
import { useJails } from "../../hooks/useJails";
|
import { useJails } from "../../hooks/useJails";
|
||||||
import type { JailSummary } from "../../types/jail";
|
import type { JailSummary } from "../../types/jail";
|
||||||
|
|||||||
39
frontend/src/utils/__tests__/queryUtils.test.ts
Normal file
39
frontend/src/utils/__tests__/queryUtils.test.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { areHistoryQueriesEqual } from "../queryUtils";
|
||||||
|
|
||||||
|
describe("areHistoryQueriesEqual", () => {
|
||||||
|
it("returns true for identical history queries", () => {
|
||||||
|
const a = {
|
||||||
|
range: "7d",
|
||||||
|
origin: "blocklist",
|
||||||
|
jail: "ssh",
|
||||||
|
ip: "192.0.2.1",
|
||||||
|
source: "archive",
|
||||||
|
page: 2,
|
||||||
|
page_size: 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
const b = { ...a };
|
||||||
|
|
||||||
|
expect(areHistoryQueriesEqual(a, b)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false when a single query field differs", () => {
|
||||||
|
const base = {
|
||||||
|
range: "7d",
|
||||||
|
origin: "all",
|
||||||
|
jail: "ssh",
|
||||||
|
ip: "192.0.2.1",
|
||||||
|
source: "archive",
|
||||||
|
page: 2,
|
||||||
|
page_size: 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
areHistoryQueriesEqual(base, { ...base, page: 3 }),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
areHistoryQueriesEqual(base, { ...base, ip: "198.51.100.1" }),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
27
frontend/src/utils/queryUtils.ts
Normal file
27
frontend/src/utils/queryUtils.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Shared query utilities used by pages and hooks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { HistoryQuery } from "../types/history";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two history query objects for semantic equality.
|
||||||
|
*
|
||||||
|
* @param a - First query object.
|
||||||
|
* @param b - Second query object.
|
||||||
|
* @returns True when every query field has the same value.
|
||||||
|
*/
|
||||||
|
export function areHistoryQueriesEqual(
|
||||||
|
a: HistoryQuery,
|
||||||
|
b: HistoryQuery,
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
a.range === b.range &&
|
||||||
|
a.origin === b.origin &&
|
||||||
|
a.jail === b.jail &&
|
||||||
|
a.ip === b.ip &&
|
||||||
|
a.source === b.source &&
|
||||||
|
a.page === b.page &&
|
||||||
|
a.page_size === b.page_size
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user