feat: centralized error notification service (issue #15)
- 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>
This commit is contained in:
@@ -3,14 +3,13 @@ import {
|
||||
Button,
|
||||
Field,
|
||||
Input,
|
||||
MessageBar,
|
||||
MessageBarBody,
|
||||
Select,
|
||||
Text,
|
||||
} from "@fluentui/react-components";
|
||||
import { LockClosedRegular, LockOpenRegular } from "@fluentui/react-icons";
|
||||
import { useCommonSectionStyles } from "../../components/commonStyles";
|
||||
import { useJailsPageStyles } from "./jailsPageStyles";
|
||||
import { useNotification } from "../../services/notificationService";
|
||||
import { ApiError } from "../../api/client";
|
||||
|
||||
interface BanUnbanFormProps {
|
||||
@@ -22,12 +21,11 @@ interface BanUnbanFormProps {
|
||||
export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps): React.JSX.Element {
|
||||
const styles = useJailsPageStyles();
|
||||
const sectionStyles = useCommonSectionStyles();
|
||||
const notification = useNotification();
|
||||
const [banIpVal, setBanIpVal] = useState("");
|
||||
const [banJail, setBanJail] = useState("");
|
||||
const [unbanIpVal, setUnbanIpVal] = useState("");
|
||||
const [unbanJail, setUnbanJail] = useState("");
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
const [formSuccess, setFormSuccess] = useState<string | null>(null);
|
||||
const [isBanning, setIsBanning] = useState(false);
|
||||
const [isUnbanning, setIsUnbanning] = useState(false);
|
||||
|
||||
@@ -39,10 +37,8 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
|
||||
: String(err);
|
||||
|
||||
const handleBan = async (): Promise<void> => {
|
||||
setFormError(null);
|
||||
setFormSuccess(null);
|
||||
if (!banIpVal.trim() || !banJail) {
|
||||
setFormError("Both IP address and jail are required.");
|
||||
notification.error("Both IP address and jail are required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,20 +46,18 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
|
||||
try {
|
||||
const ip = banIpVal.trim();
|
||||
await onBan(banJail, ip);
|
||||
setFormSuccess(`${ip} banned in ${banJail}.`);
|
||||
notification.success(`${ip} banned in ${banJail}.`);
|
||||
setBanIpVal("");
|
||||
} catch (err: unknown) {
|
||||
setFormError(formatErrorMessage(err));
|
||||
notification.error(formatErrorMessage(err));
|
||||
} finally {
|
||||
setIsBanning(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnban = async (fromAllJails: boolean): Promise<void> => {
|
||||
setFormError(null);
|
||||
setFormSuccess(null);
|
||||
if (!unbanIpVal.trim()) {
|
||||
setFormError("IP address is required.");
|
||||
notification.error("IP address is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,11 +67,11 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
|
||||
const jail = fromAllJails ? undefined : unbanJail || undefined;
|
||||
await onUnban(ip, jail);
|
||||
const scope = jail ?? "all jails";
|
||||
setFormSuccess(`${ip} unbanned from ${scope}.`);
|
||||
notification.success(`${ip} unbanned from ${scope}.`);
|
||||
setUnbanIpVal("");
|
||||
setUnbanJail("");
|
||||
} catch (err: unknown) {
|
||||
setFormError(formatErrorMessage(err));
|
||||
notification.error(formatErrorMessage(err));
|
||||
} finally {
|
||||
setIsUnbanning(false);
|
||||
}
|
||||
@@ -91,17 +85,6 @@ export function BanUnbanForm({ jailNames, onBan, onUnban }: BanUnbanFormProps):
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{formError && (
|
||||
<MessageBar intent="error">
|
||||
<MessageBarBody>{formError}</MessageBarBody>
|
||||
</MessageBar>
|
||||
)}
|
||||
{formSuccess && (
|
||||
<MessageBar intent="success">
|
||||
<MessageBarBody>{formSuccess}</MessageBarBody>
|
||||
</MessageBar>
|
||||
)}
|
||||
|
||||
<Text size={300} weight="semibold">
|
||||
Ban an IP
|
||||
</Text>
|
||||
|
||||
Reference in New Issue
Block a user