- Rework Login As Admin: use sessionStorage flag + relative fetch login + polling loop
- Add data-testid to JailDetailPage error render path
- Add Collections library import for Get From List keyword
- Fix /jails API response extraction (returns {items, total} not plain list)
- Change Close Context to Close Browser for proper browser cleanup
- Add domcontentloaded + Sleep + polling to Config test to avoid premature timeout
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- LoginPage now shows a loading spinner while validating the session
- Redirect to dashboard automatically once validation completes and session is valid
- Expose isValidating state through AuthProvider for components to track validation status
- Update useAuth hook to return isValidating along with isAuthenticated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include NotificationContainer in test setup so notification messages
appear in the DOM and can be found by tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create NotificationService with context provider for centralized error/success messaging
- Add NotificationContainer component to render notification stack
- Integrate NotificationProvider into App root
- Refactor BanUnbanForm to use notification service instead of local error state
- Update fetchError utility to optionally use notification callbacks
- Add comprehensive error handling guidelines to Web-Development.md
- Prevent duplicate notifications with deduplication logic
- Support auto-dismiss with configurable TTL per notification type
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split the over-centralized ConfigPage into focused, composable layers:
1. useTabRouter hook: Encapsulates tab state management and URL synchronization
- Maintains selected tab and active item (e.g., jail name)
- Syncs state to location.state for deep linking and browser history
- Supports bookmarkable URLs and back/forward navigation
2. ConfigPageContainer: Orchestrates tab navigation
- Manages TabList and routes tab selection events
- Conditionally renders tab content panels
- Delegates domain-specific logic to tab components
3. ConfigPage: Focused page layout component
- Renders page structure (header, title, description)
- Delegates tab orchestration to ConfigPageContainer
- No routing or tab state logic
Benefits:
- Page is now 30 lines vs 125 lines (76% reduction)
- Tab state management is reusable for other multi-tab pages
- Each tab component remains focused on domain-specific UI
- Deep linking and browser history work out of the box
- Easier to test and maintain
Added comprehensive tests:
- useTabRouter: 6 tests covering state initialization, tab selection, and deep linking
- ConfigPageContainer: 8 tests covering tab rendering and navigation
- ConfigPage: 3 tests for page structure
Updated Web-Development.md with tab orchestration pattern documentation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace multi-hop prop forwarding with a dedicated JailContext that manages
jail state and actions. This reduces coupling, simplifies the component hierarchy,
and makes the data flow more explicit.
Changes:
- Create JailContext.tsx with JailProvider and useJailContext hook
- Wrap JailsPage content with JailProvider to expose jail state
- Refactor JailOverviewSection to use useJailContext instead of props
- Remove 10 props from JailOverviewSection component signature
- Add comprehensive documentation on state ownership and prop drilling
Benefits:
- Eliminates unnecessary prop chains through intermediate components
- Makes component contracts clearer (no longer need to pass unrelated props)
- Simplifies future refactoring of jail-related functionality
- Sets a pattern for other page-scoped state management
Testing:
- TypeScript type check passes (tsc --noEmit)
- Frontend builds successfully
- Existing JailsPage tests pass with new context structure
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement request deduplication to prevent multiple duplicate calls to GET
/api/setup when multiple components mount simultaneously. The fix introduces:
1. New 'useSharedSetupStatus' hook with module-level caching
- Shares a single in-flight request across all consumers
- Implements 30-second cache TTL with cache invalidation
- Notifies all subscribers when cache is invalidated
2. Refactored 'useSetup' hook to use shared cache
- Internally uses useSharedSetupStatus for status checks
- Calls invalidateSetupStatus() after successful setup submission
- Maintains backward-compatible API
3. Updated components using setup status
- SetupGuard and SetupPage automatically benefit from deduplication
- No changes needed to consumer code
4. Updated tests
- Mocked useSharedSetupStatus in component tests
- Added comprehensive tests for cache behavior
- All existing tests pass
5. Documentation updates
- Added 'Request Deduplication & Shared Caching' section to Web-Development.md
- Explains when and how to use shared hooks
- Provides complete implementation example
This eliminates wasted resources from duplicate API calls and potential
race conditions where different requests return slightly different states.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Moved all static layout properties (display, gap, margin, padding, colour)
from inline style props to makeStyles classes in:
- MapBansTable.tsx: Pagination row flexbox layout
- JailDetailPage.tsx: Link styling for textDecoration
- HistoryPage.tsx: Summary text styling
- IpDetailView.tsx: Loading container and text formatting
Kept inline styles only for genuinely dynamic values:
- WorldMap.tsx: Tooltip position (follows mouse)
- TopCountriesPieChart.tsx: Legend color (from recharts data)
- TopCountriesBarChart.tsx: Chart height (derives from data length)
This change improves performance by leveraging Griffel's atomic CSS cache
and ensures consistency with the established Fluent UI pattern.
Updated Docs/Web-Development.md with explicit rule: inline styles only
for runtime-dynamic values, all static properties go in makeStyles.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move DashboardFilterProvider component and tests from providers/ to pages/
- Update DashboardPage imports to reflect new structure
- Update documentation with latest task progress
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add BAN_PAGE_SIZE (100) and HISTORY_PAGE_SIZE (50) to frontend/src/utils/constants.ts
- Replace local PAGE_SIZE definitions in useBans.ts and HistoryPage.tsx with imports
- Eliminates risk of pagination constants silently diverging from backend defaults
- Single source of truth for all pagination sizes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
- Split monolithic useJailDetail hook into separate concerns
- Created useJailData for fetching and managing jail data
- Created useJailCommands for jail operations (power, console, etc.)
- Updated JailDetailPage to use new hooks
- Updated tests to reflect new hook structure
- Removed old useJailDetail hook
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace console.warn with visible MessageBar warning when map color thresholds fail to load
- Add DismissRegular icon button to allow users to dismiss the warning
- Add dismissedThresholdWarning state to manage warning visibility
- Add mock and test for useMapColorThresholds hook
- Add test case verifying warning displays and can be dismissed
- Remove TASK-QUALITY-04 from Tasks.md (completed)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Split useHistory interface to accept explicit parameters (page, pageSize, range, origin, jail, ip, source) instead of HistoryQuery object
- Add comprehensive JSDoc for useHistory function
- Update HistoryPage and tests to use new parameter structure
- Move TaskList documentation from Tasks.md to Web-Development.md
- Improve type safety with explicit TimeRange and BanOriginFilter types
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extract schedule logic into custom useSchedule hook
- Update BlocklistScheduleSection to use the new hook
- Add tests for useSchedule hook
- Update documentation with task progress
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DashboardFilterBar now requires all filter props (timeRange, onTimeRangeChange, originFilter, onOriginFilterChange) instead of falling back to context
- Removed useDashboardFilters() hook dependency from DashboardFilterBar, BanTrendChart, and JailDistributionChart
- Updated DashboardPage to explicitly pass all filter values and callbacks from context to components
- Made props required on BanTrendChart and JailDistributionChart
- Updated all tests to reflect new prop requirements
- This eliminates the silent dual-source behavior that could lead to subtle bugs when components are used with different data sources
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TASK-BUG-07: Remove duplicate useJails() hook call on JailsPage
Previously, useJails() was called twice on page load:
1. In JailsPage to extract jailNames for BanUnbanForm
2. In JailOverviewSection to manage the jail table
This caused two parallel GET /api/jails requests on every page load.
Changes:
- Lift useJails() to JailsPage as the single source of truth
- Accept jail state as props in JailOverviewSection
- Thread all required state (jails, total, loading, error, and action
handlers) down from JailsPage to JailOverviewSection
- Remove useJails hook import from JailOverviewSection
This consolidation reduces unnecessary HTTP requests and improves
page load performance, especially with many jails.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove 'bans' from the useEffect dependency array that resets pagination.
Since 'bans' changes with every background data refresh (new array reference),
the page was being reset to 1 every 30 seconds, making the table unusable for
pagination beyond the first page.
Add a separate effect that clamps the current page to totalPages when the
data shrinks below the current page offset (edge case when filtered results
are fewer than displayed page).
Fixes TASK-BUG-03.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously, the tab content wrapper used 'key={tab}' which caused React to
unmount and remount the entire subtree when switching tabs. This destroyed
all component state, including unsaved form data and pending auto-saves.
Changes:
- Removed 'key={tab}' from the wrapper div
- All tab panels now render at page initialization
- Inactive tabs use CSS 'display: none' to hide without unmounting
- Tabs remain mounted throughout the page lifetime
- Users can now switch tabs without losing form input
Updated ConfigPage.test.tsx to reflect that inactive tabs remain in the DOM
(just hidden with CSS) rather than being removed entirely.
Documentation: Added 'Tab Panels' section to Web-Development.md
explaining the rule and rationale.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Validate the ?next= query parameter to prevent open redirects to
external URLs. The parameter is validated to ensure it is a relative
path (starts with / but not //) before using it for navigation.
Invalid paths fall back to '/'.
This prevents attackers from crafting login links like /login?next=https://evil.com
that would transparently redirect authenticated users to malicious sites.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>