refactoring-backend #3

Merged
lukas.pupkalipinski merged 403 commits from refactoring-backend into main 2026-05-20 20:23:46 +02:00
4 changed files with 34 additions and 26 deletions
Showing only changes of commit 0bfa975222 - Show all commits

View File

@@ -1,15 +1,3 @@
### ✅ TASK-BUG-01 — Infinite Re-Fetch Loop in `useJailConfigs` — DONE
**Fix Summary**
Wrapped the `onSuccess` callback in `useCallback` with empty dependencies in `frontend/src/hooks/useJailConfigs.ts` (lines 33-35). The inline callback was creating a new reference on every render, which caused `useListData`'s internal `refresh` function to be recreated (since `onSuccess` is in its deps), which triggered the `useEffect` again, causing an infinite fetch loop.
Added comprehensive test coverage in `frontend/src/hooks/__tests__/useJailConfigs.test.ts` to verify the hook no longer triggers infinite refetches. Updated `Docs/Refactoring.md` with documentation explaining the `onSuccess` stability requirement for all `useListData` callers.
Commit: `de8af09a3da36dbf24b56fa28656673b232b5e91`
---
### TASK-BUG-02 — `ConfigPage` Tab Switch Destroys All Form State
**Where found**
@@ -213,7 +201,6 @@ None required.
**Why this is needed**
Typing `"500"` in the Lines field currently fires three HTTP requests (`"5"`, `"50"`, `"500"`). Each request fetches potentially hundreds of log lines and serializes them, adding unnecessary backend load.
---
### TASK-ABORT-01 — Missing `signal` Parameter on Multiple API Functions
@@ -640,4 +627,4 @@ Remove the `console.log` call.
None required.
**Why this is needed**
Debug logs in test files pollute the test runner output and make it harder to spot real failures or warnings.
Debug logs in test files pollute the test runner output and make it harder to spot real failures or warnings.

View File

@@ -270,6 +270,12 @@ function BanCard({ isHighlighted }: BanCardProps): JSX.Element {
- Supply a `key` prop whenever rendering lists — never use array indices as keys if the list can reorder.
- Prefer Fluent UI components (`Button`, `Table`, `Input`, …) over raw HTML elements for any interactive or styled element.
### Tab Panels
- **Never** use `key` on a tab panel wrapper to switch between tabs. This causes the entire subtree to unmount and remount, destroying all state, pending saves, and form input.
- Instead, render all tab panels and use CSS `display: none` / `display: block` to hide inactive tabs, keeping components mounted across tab switches.
- All tab components remain mounted throughout the page lifetime. Hooks continue to run in hidden tabs — if a tab-specific effect must only run on activation, use an explicit activation flag rather than relying on mount/unmount.
```tsx
import { Table, TableBody, TableRow, TableCell, Button } from "@fluentui/react-components";
import type { Ban } from "../types/ban";

View File

@@ -36,7 +36,11 @@ const useStyles = makeStyles({
header: {
marginBottom: tokens.spacingVerticalL,
},
tabContent: {
tabPanel: {
display: "none",
},
tabPanelVisible: {
display: "block",
marginTop: tokens.spacingVerticalL,
animationName: "fadeInUp",
animationDuration: tokens.durationNormal,
@@ -94,16 +98,26 @@ export function ConfigPage(): React.JSX.Element {
<Tab value="regex">Regex Tester</Tab>
</TabList>
<div className={styles.tabContent} key={tab}>
{tab === "jails" && (
<JailsTab
initialJail={(location.state as { jail?: string } | null)?.jail}
/>
)}
{tab === "filters" && <FiltersTab />}
{tab === "actions" && <ActionsTab />}
{tab === "server" && <ServerTab />}
{tab === "regex" && <RegexTesterTab />}
<div className={tab === "jails" ? styles.tabPanelVisible : styles.tabPanel}>
<JailsTab
initialJail={(location.state as { jail?: string } | null)?.jail}
/>
</div>
<div className={tab === "filters" ? styles.tabPanelVisible : styles.tabPanel}>
<FiltersTab />
</div>
<div className={tab === "actions" ? styles.tabPanelVisible : styles.tabPanel}>
<ActionsTab />
</div>
<div className={tab === "server" ? styles.tabPanelVisible : styles.tabPanel}>
<ServerTab />
</div>
<div className={tab === "regex" ? styles.tabPanelVisible : styles.tabPanel}>
<RegexTesterTab />
</div>
</div>
);

View File

@@ -38,7 +38,8 @@ describe("ConfigPage", () => {
renderPage();
fireEvent.click(screen.getByRole("tab", { name: /filters/i }));
expect(screen.getByTestId("filters-tab")).toBeInTheDocument();
expect(screen.queryByTestId("jails-tab")).not.toBeInTheDocument();
// Jails tab remains mounted (not removed from DOM), just hidden with CSS
expect(screen.getByTestId("jails-tab")).toBeInTheDocument();
});
it("switches to Actions tab when Actions tab is clicked", () => {