Files
BanGUI/Docs/Tasks.md
Lukas 44f3fb8718 chore: add GitHub Copilot agent, fix ESLint config, update task list
- .github/agents/ProcessTasks.agent.md: Copilot agent definition
- eslint.config.ts: minor lint rule adjustment
- Docs/Tasks.md: update completed and in-progress task status
2026-03-13 13:48:20 +01:00

12 KiB

BanGUI — Task List

This document breaks the entire BanGUI project into development stages, ordered so that each stage builds on the previous one. Every task is described in prose with enough detail for a developer to begin work. References point to the relevant documentation.


Config View Redesign — List/Detail Layout with Active Status and Raw Export

Overview

Redesign the Jails, Filters, and Actions tabs on the Configuration page (frontend/src/pages/ConfigPage.tsx) to use a master/detail list layout instead of the current accordion pattern. The left pane shows a scrollable list of config items (jail names, filter names, action names). Each item displays an active/inactive badge. Active items sort to the top. Clicking an item shows its structured form editor in the right pane, with a collapsible raw-text export section appended at the bottom.

Use only Fluent UI React v9 (@fluentui/react-components, @fluentui/react-icons) components as specified in Web-Design.md and Web-Development.md. No additional UI libraries.

References


Task A — Shared List/Detail Layout Component

File: frontend/src/components/config/ConfigListDetail.tsx

Create a reusable layout component that renders a two-pane master/detail view.

Left pane (list):

  • Fixed width ~280 px, full height of the tab content area, with its own vertical scroll.
  • Use a vertical stack of clickable items. Each item is a Fluent UI Card (or a simple styled div with tokens.colorNeutralBackground2 on hover) displaying:
    • The config name (e.g. sshd, iptables-multiport), truncated with ellipsis + tooltip for long names.
    • A Fluent UI Badge to the right of the name: "Active" (appearance="filled", color="success") or "Inactive" (appearance="outline", color="informative").
  • The selected item gets a left border accent (tokens.colorBrandBackground) and a highlighted background (tokens.colorNeutralBackground1Selected).
  • Items are sorted: active items first, then inactive, alphabetical within each group.
  • Keyboard navigable (arrow keys, Enter to select). Follow the accessibility rules from Web-Design.md §15.

Right pane (detail):

  • Takes remaining width. Renders whatever children or render-prop content the parent tab passes for the currently selected item.
  • Displays an empty state ("Select an item from the list") when nothing is selected.
  • Uses Skeleton / Spinner while the detail is loading.

Props interface (suggestion):

interface ConfigListDetailProps<T extends { name: string }> {
  items: T[];
  isActive: (item: T) => boolean;
  selectedName: string | null;
  onSelect: (name: string) => void;
  loading: boolean;
  error: string | null;
  children: React.ReactNode; // detail content for selected item
}

Styles: Add new style slots to frontend/src/components/config/configStyles.ts:

  • listDetailRoot — flex row, gap tokens.spacingHorizontalM.
  • listPane — fixed width 280 px, overflow-y auto, border-right tokens.colorNeutralStroke2.
  • listItem — padding, hover background, cursor pointer.
  • listItemSelected — left brand-colour border, selected background.
  • detailPane — flex 1, overflow-y auto, padding.

Responsive: On screens < 900 px, collapse the list pane into a Dropdown / Select above the detail pane so it doesn't take too much horizontal space.


Task B — Active Status for Jails, Filters, and Actions

Determine "active" status for each config type so it can be shown in the list.

Jails:

  • The existing JailConfig type does not carry an enabled flag directly. The jails API (GET /api/jails) returns JailSummary objects which contain enabled: boolean. To get the active status, fetch the jails list from fetchJails() (in api/jails.ts) alongside the jail configs from fetchJailConfigs(). Merge the two by name: a jail is "active" if enabled === true in the jails list.
  • Alternatively, check the JailConfigFile entries from the jail files API which have enabled: boolean — determine which data source is more reliable for showing runtime active state.

Filters:

  • A filter is "active" if it is referenced by at least one active jail. After fetching jail configs (JailConfig[]), collect all unique filter names used by enabled jails. Cross-reference with the filter files list (ConfFileEntry[]). Mark used ones as active.
  • This requires correlating data: jail config has a name field that often matches the filter name (convention in fail2ban: jail name = filter name unless overridden). For accuracy, the filter name a jail uses can be inferred from the jail name or, if the backend provides a filter field on JailConfig, from that.

Actions:

  • An action is "active" if it is referenced by at least one active jail's actions array. After fetching jail configs, collect all unique action names from JailConfig.actions[] arrays of enabled jails. Cross-reference with action files.

Implementation:

  • Create a new hook frontend/src/hooks/useConfigActiveStatus.ts that:
    1. Fetches jails list (fetchJails), jail configs (fetchJailConfigs), filter files (fetchFilterFiles), and action files (fetchActionFiles) in parallel.
    2. Computes and returns { activeJails: Set<string>, activeFilters: Set<string>, activeActions: Set<string>, loading: boolean, error: string | null }.
    3. Cache results and re-fetch on demand (expose a refresh function).
  • The hook is consumed by the three tabs so each tab knows which items are active.

Task C — Redesign JailsTab to List/Detail Layout

File: frontend/src/components/config/JailsTab.tsx

Replace the current Accordion-based layout with the ConfigListDetail component from Task A.

  1. Fetch jail configs via useJailConfigs (already exists in hooks/useConfig.ts).
  2. Use the active-status hook from Task B to get activeJails.
  3. Render ConfigListDetail with:
    • items = jail config list.
    • isActive = (jail) => activeJails.has(jail.name).
    • onSelect updates selectedName state.
  4. The right-pane detail content is the existing JailAccordionPanel logic (the form fields for ban_time, find_time, max_retry, regex patterns, log paths, escalation, etc.) — extract it into a standalone JailConfigDetail component if not already. Remove the accordion wrapper; it is just the form body now.
  5. Export section (new, at the bottom of the detail pane):
    • A collapsible section (use Accordion with a single item, or a Button toggle) titled "Raw Configuration".
    • Contains a Fluent UI Textarea (monospace font, full width, ~20 rows) pre-filled with the raw plain-text representation of the jail config. The raw text is fetched via the existing fetchJailConfig or fetchJailConfigFile endpoint that returns the file content as a string.
    • The textarea is editable: the user can modify the raw text and click a "Save Raw" button to push the changes back via the existing updateJailConfigFile PUT endpoint.
    • Show an AutoSaveIndicator or manual save button + success/error MessageBar.

Task D — Redesign FiltersTab to List/Detail Layout

File: frontend/src/components/config/FiltersTab.tsx

Replace the current Accordion-based layout with ConfigListDetail.

  1. Fetch filter file list via fetchFilterFiles (already used).
  2. Use activeFilters from the active-status hook (Task B).
  3. Render ConfigListDetail with:
    • items = filter file entries.
    • isActive = (f) => activeFilters.has(f.name).
  4. On item select, lazily load the parsed filter via useFilterConfig (already exists in hooks/useFilterConfig.ts).
  5. Right pane renders the existing FilterForm component with the loaded config.
  6. Export section at the bottom of the detail pane:
    • Collapsible "Raw Configuration" section.
    • Textarea (monospace) pre-filled with the raw file content fetched via fetchFilterFile(name) (returns ConfFileContent with a content: string field).
    • Editable with a "Save Raw" Button that calls updateFilterFile(name, { content }).
    • Success/error feedback via MessageBar.

Task E — Redesign ActionsTab to List/Detail Layout

File: frontend/src/components/config/ActionsTab.tsx

Same pattern as Task D but for actions.

  1. Fetch action file list via fetchActionFiles.
  2. Use activeActions from the active-status hook (Task B).
  3. Render ConfigListDetail with:
    • items = action file entries.
    • isActive = (a) => activeActions.has(a.name).
  4. On item select, lazily load the parsed action via useActionConfig (already exists).
  5. Right pane renders the existing ActionForm component.
  6. Export section at the bottom:
    • Collapsible "Raw Configuration" section.
    • Textarea (monospace) with raw file content from fetchActionFile(name).
    • "Save Raw" button calling updateActionFile(name, { content }).
    • Feedback messages.

Task F — Raw Export Section Component

File: frontend/src/components/config/RawConfigSection.tsx

Extract the raw-export pattern into a reusable component so Jails, Filters, and Actions tabs don't duplicate logic.

Props:

interface RawConfigSectionProps {
  /** Async function that returns the raw file content string. */
  fetchContent: () => Promise<string>;
  /** Async function that saves updated raw content. */
  saveContent: (content: string) => Promise<void>;
  /** Label shown in the collapsible header, e.g. "Raw Jail Configuration". */
  label?: string;
}

Behaviour:

  • Renders a collapsible section (single AccordionItem or a disclosure Button).
  • When expanded for the first time, calls fetchContent() and fills the Textarea.
  • Uses monospace font (fontFamily: "monospace") and a left brand-colour accent border (reuse styles.codeInput from configStyles.ts).
  • "Save Raw" Button with appearance="primary" calls saveContent(text).
  • Shows AutoSaveIndicator-style feedback (idle → saving → saved / error).
  • The Textarea is resizable vertically, minimum 15 rows.

Task G — Update Barrel Exports and ConfigPage

  1. frontend/src/components/config/index.ts — Add exports for ConfigListDetail and RawConfigSection.
  2. frontend/src/pages/ConfigPage.tsx — No structural changes needed if the tabs internally switch to the new layout. Verify the page still renders all tabs correctly.
  3. frontend/src/components/config/configStyles.ts — Add the new style slots described in Task A. Do not remove existing styles that may still be used by other tabs (Global, Server, Map, Regex Tester, Export).

Task H — Testing and Validation

  1. Type-check: Run npx tsc --noEmit — zero errors.
  2. Lint: Run npm run lint — zero warnings.
  3. Existing tests: Run npx vitest run — all existing tests pass.
  4. Manual verification:
    • Navigate to Config → Jails tab. List pane shows jail names with active badges. Active jails appear at the top. Click a jail → right pane shows configuration form + collapsible raw editor.
    • Navigate to Config → Filters tab. Same list/detail pattern. Active filters (used by running jails) show "Active" badge.
    • Navigate to Config → Actions tab. Same pattern.
    • Resize window below 900 px — list collapses to a dropdown selector above the detail.
    • Keyboard: Tab into the list, arrow-key navigate, Enter to select.
  5. New tests (optional but recommended):
    • Unit test ConfigListDetail rendering with mock items, verifying sort order (active first) and selection callback.
    • Unit test RawConfigSection with mocked fetch/save functions.

Implementation Order

  1. Task F (RawConfigSection) — standalone, no dependencies.
  2. Task A (ConfigListDetail layout) — standalone component.
  3. Task B (useConfigActiveStatus hook) — needs only the existing API layer.
  4. Task C (JailsTab redesign) — depends on A, B, F.
  5. Task D (FiltersTab redesign) — depends on A, B, F.
  6. Task E (ActionsTab redesign) — depends on A, B, F.
  7. Task G (exports and wiring) — after C/D/E.
  8. Task H (testing) — last.