refactor: move jail detail sub-sections from pages/jail to components/jail

Move reusable UI section components (JailInfoSection, PatternsSection,
BantimeEscalationSection, IgnoreListSection, CodeList) from pages/jail/
to components/jail/, aligning with the project convention that pages/
contains only route-level entry points while components/ contains reusable
UI building blocks.

Changes:
- Move 5 section components + jailDetailPageStyles.ts to components/jail/
- Update import paths in moved components (relative paths to commonStyles)
- Update JailDetailPage.tsx imports to reference components/jail/
- Delete empty pages/jail/ directory
- Document pages/ vs components/ distinction in Web-Development.md

All components use standard import structure and TypeScript passes type
checking. BannedIpsSection was already correctly placed in components/jail/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-25 19:17:03 +02:00
parent 8bd5713d38
commit 6a062a72a7
9 changed files with 25 additions and 33 deletions

View File

@@ -1,27 +1,3 @@
### T-13 · Split `useJailDetail` — SRP violation (read state + write commands in one hook)
**Where found:** `frontend/src/hooks/useJailDetail.ts`
**Why this is needed:** The hook manages fetch state AND exposes 8 write operations (`start`, `stop`, `reload`, `setIdle`, `addIp`, `removeIp`, `toggleIgnoreSelf`, `load`). Read concerns and command concerns are independent. The consumer (`JailDetailPage`) passes them separately through props anyway, so the UI doesn't depend on them being co-located.
**Goal:** `useJailData(name)` for reading, `useJailCommands(name, onSuccess)` for mutations.
**What to do:**
1. Create `useJailData(name): { jail, ignoreList, ignoreSelf, loading, error, refresh }` using `useListData` or the abort-controller pattern.
2. Create `useJailCommands(name, onSuccess: () => void): { start, stop, reload, setIdle, addIp, removeIp, toggleIgnoreSelf }` — each command calls the API and then calls `onSuccess()` to trigger a refresh.
3. In `JailDetailPage`, call both hooks and pass results to child components.
4. Delete `useJailDetail.ts`.
**Possible traps and issues:**
- `JailDetailPage` destructures all properties from `useJailDetail` in a single line — update the destructuring.
- Commands currently call `load()` directly. The `onSuccess` callback pattern keeps them decoupled from the data hook's internals.
**Docs changes needed:** None.
**Doc references:** `frontend/src/hooks/useJailDetail.ts`, `frontend/src/pages/JailDetailPage.tsx`
---
### T-14 · Move jail detail sub-sections from `pages/jail/` to `components/jail/`
**Where found:** `frontend/src/pages/jail/``JailInfoSection`, `PatternsSection`, `BantimeEscalationSection`, `IgnoreListSection`, `jailDetailPageStyles.ts`. `BannedIpsSection` is in `frontend/src/components/jail/` (correct location).

View File

@@ -153,6 +153,22 @@ frontend/
> container network `localhost` resolves to the frontend container itself and
> causes `ECONNREFUSED`.
### Pages vs Components
The distinction between **`pages/`** and **`components/`** is fundamental to the project structure:
- **`pages/`** contains route-level entry point components — exactly **one component per route**. Pages map directly to URL paths (e.g., `JailDetailPage.tsx``/jail/:name`). Pages orchestrate the layout and compose multiple components, but contain **no reusable UI logic**. Pages should rarely be reused.
- **`components/`** contains **reusable UI building blocks** — anything that could plausibly be used on multiple pages or in multiple contexts. This includes:
- Presentation components (Button wrappers, Cards, custom form fields, data tables)
- Feature sub-sections (e.g., `JailInfoSection`, `BannedIpsSection` — components that render a logical grouping of related UI within a page)
- Modals, dialogs, popovers
- Complex, stateful UI patterns
**Rule of thumb:** If a component is only ever used on a single page, it **still belongs in `components/`** if it represents a coherent, self-contained piece of UI that could logically be reused on another page in the future. Pages are entry points; components are building blocks.
**Example:** `BannedIpsSection` lives in `components/jail/` (not `pages/jail/`) because it is a reusable UI section that presents banned IPs. If a future report or dashboard also needed to show banned IPs, the same component could be imported and reused. By contrast, `JailDetailPage.tsx` lives in `pages/` because it is the top-level route component.
### Separation of Concerns
- **Pages** handle routing and compose layout + components — they contain no business logic.

View File

@@ -1,5 +1,5 @@
import { Badge, Text } from "@fluentui/react-components";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useCommonSectionStyles } from "../commonStyles";
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
import type { Jail } from "../../types/jail";
import { formatSeconds } from "../../utils/formatDate";

View File

@@ -11,7 +11,7 @@ import {
Tooltip,
} from "@fluentui/react-components";
import { DismissRegular } from "@fluentui/react-icons";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useCommonSectionStyles } from "../commonStyles";
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
interface IgnoreListSectionProps {

View File

@@ -14,7 +14,7 @@ import {
PlayRegular,
StopRegular,
} from "@fluentui/react-icons";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useCommonSectionStyles } from "../commonStyles";
import { useJailDetailPageStyles } from "./jailDetailPageStyles";
import type { Jail } from "../../types/jail";

View File

@@ -1,5 +1,5 @@
import { Text, makeStyles, tokens } from "@fluentui/react-components";
import { useCommonSectionStyles } from "../../components/commonStyles";
import { useCommonSectionStyles } from "../commonStyles";
import type { Jail } from "../../types/jail";
import { CodeList } from "./CodeList";

View File

@@ -12,11 +12,11 @@ import { useJailData } from "../hooks/useJailData";
import { useJailCommands } from "../hooks/useJailCommands";
import { useJailBannedIps } from "../hooks/useJailBannedIps";
import { BannedIpsSection } from "../components/jail/BannedIpsSection";
import { JailInfoSection } from "./jail/JailInfoSection";
import { PatternsSection } from "./jail/PatternsSection";
import { BantimeEscalationSection } from "./jail/BantimeEscalationSection";
import { IgnoreListSection } from "./jail/IgnoreListSection";
import { useJailDetailPageStyles } from "./jail/jailDetailPageStyles";
import { JailInfoSection } from "../components/jail/JailInfoSection";
import { PatternsSection } from "../components/jail/PatternsSection";
import { BantimeEscalationSection } from "../components/jail/BantimeEscalationSection";
import { IgnoreListSection } from "../components/jail/IgnoreListSection";
import { useJailDetailPageStyles } from "../components/jail/jailDetailPageStyles";
export function JailDetailPage(): React.JSX.Element {
const styles = useJailDetailPageStyles();