Improve error boundary granularity with page and section level boundaries
Implement three-level error boundary strategy: - Top-level (app shell): catches critical failures - Page-level: preserves navigation when page crashes - Section-level: graceful degradation for charts/tables Create new components: - PageErrorBoundary: wraps page routes - SectionErrorBoundary: wraps data-heavy sections Enhance ErrorBoundary with customizable titles, messages, and reload behavior. Apply page boundaries to all route handlers in App.tsx. Apply section boundaries to: - DashboardPage: server status, ban trend, country charts, ban list - JailsPage: jail overview, ban/unban form, IP lookup - MapPage: world map, ban table - ConfigPage: configuration editor - HistoryPage: history table, IP detail view - BlocklistsPage: sources, schedule, import log Update Web-Development.md with error boundary strategy documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
* Shows a clickable SVG world map coloured by ban density, a time-range
|
||||
* selector, and a companion table filtered by the selected country (or all
|
||||
* bans when no country is selected).
|
||||
*
|
||||
* Critical sections wrapped with SectionErrorBoundary for resilience.
|
||||
*/
|
||||
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
@@ -23,6 +25,7 @@ import {
|
||||
DismissRegular,
|
||||
} from "@fluentui/react-icons";
|
||||
import { DashboardFilterBar } from "../components/DashboardFilterBar";
|
||||
import { SectionErrorBoundary } from "../components/SectionErrorBoundary";
|
||||
import { WorldMap } from "../components/WorldMap";
|
||||
import { useMapData } from "../hooks/useMapData";
|
||||
import { useMapColorThresholds } from "../hooks/useMapColorThresholds";
|
||||
@@ -250,15 +253,17 @@ export function MapPage(): React.JSX.Element {
|
||||
{/* immediate visual feedback before the filtered data arrives. */}
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
{!error && hasLoadedOnce && (
|
||||
<WorldMap
|
||||
countries={countries}
|
||||
countryNames={countryNames}
|
||||
selectedCountry={selectedCountry}
|
||||
onSelectCountry={setSelectedCountry}
|
||||
thresholdLow={thresholdLow}
|
||||
thresholdMedium={thresholdMedium}
|
||||
thresholdHigh={thresholdHigh}
|
||||
/>
|
||||
<SectionErrorBoundary sectionName="World Map">
|
||||
<WorldMap
|
||||
countries={countries}
|
||||
countryNames={countryNames}
|
||||
selectedCountry={selectedCountry}
|
||||
onSelectCountry={setSelectedCountry}
|
||||
thresholdLow={thresholdLow}
|
||||
thresholdMedium={thresholdMedium}
|
||||
thresholdHigh={thresholdHigh}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
)}
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
@@ -302,19 +307,21 @@ export function MapPage(): React.JSX.Element {
|
||||
{/* Companion bans table */}
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
{!error && hasLoadedOnce && (
|
||||
<div className={mergeClasses(styles.tableWrapper, loading && styles.tableWrapperLoading)}>
|
||||
<MapBansTable
|
||||
pageBans={pageBans}
|
||||
visibleCount={visibleBans.length}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
totalPages={totalPages}
|
||||
hasPrev={hasPrev}
|
||||
hasNext={hasNext}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
/>
|
||||
</div>
|
||||
<SectionErrorBoundary sectionName="Map Ban Table">
|
||||
<div className={mergeClasses(styles.tableWrapper, loading && styles.tableWrapperLoading)}>
|
||||
<MapBansTable
|
||||
pageBans={pageBans}
|
||||
visibleCount={visibleBans.length}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
totalPages={totalPages}
|
||||
hasPrev={hasPrev}
|
||||
hasNext={hasNext}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
/>
|
||||
</div>
|
||||
</SectionErrorBoundary>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user