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:
86
frontend/src/components/NotificationContainer.tsx
Normal file
86
frontend/src/components/NotificationContainer.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Notification display container.
|
||||
*
|
||||
* Renders all active notifications from the NotificationService as a vertical stack
|
||||
* at the top of the application. Each notification displays with appropriate styling
|
||||
* based on its intent level and auto-dismisses after a configured duration.
|
||||
*/
|
||||
|
||||
import { MessageBar, MessageBarBody, makeStyles, tokens } from "@fluentui/react-components";
|
||||
import { useNotificationQueue } from "../services/notificationService";
|
||||
import type { Notification } from "../types/notification";
|
||||
|
||||
/** Styles for the notification container and messages. */
|
||||
const useStyles = makeStyles({
|
||||
container: {
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10000, // Above all other content
|
||||
pointerEvents: "none", // Allow clicks to pass through to content below
|
||||
padding: tokens.spacingVerticalL,
|
||||
maxHeight: "60vh",
|
||||
overflowY: "auto",
|
||||
},
|
||||
stack: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: tokens.spacingVerticalM,
|
||||
maxWidth: "600px",
|
||||
margin: "0 auto",
|
||||
},
|
||||
messageBar: {
|
||||
pointerEvents: "auto", // But restore pointer events on the messages
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* NotificationContainer renders the notification queue.
|
||||
*
|
||||
* Place this component once in your app root (e.g., in App.tsx) to display
|
||||
* all notifications managed by the NotificationService.
|
||||
*
|
||||
* @returns JSX element rendering the notification stack or empty fragment if no notifications.
|
||||
*/
|
||||
export function NotificationContainer(): React.JSX.Element {
|
||||
const styles = useStyles();
|
||||
const notifications = useNotificationQueue();
|
||||
|
||||
if (notifications.length === 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container} role="alert" aria-live="polite">
|
||||
<div className={styles.stack}>
|
||||
{notifications.map((notification) => (
|
||||
<NotificationMessage key={notification.id} notification={notification} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single notification message component.
|
||||
* Renders a Fluent UI MessageBar with appropriate styling based on notification intent.
|
||||
*/
|
||||
interface NotificationMessageProps {
|
||||
notification: Notification;
|
||||
}
|
||||
|
||||
function NotificationMessage({ notification }: NotificationMessageProps): React.JSX.Element {
|
||||
const styles = useStyles();
|
||||
|
||||
// Map notification intent to MessageBar intent
|
||||
const messageBarIntent: "success" | "error" | "warning" | "info" =
|
||||
notification.intent === "info" ? "info" : notification.intent;
|
||||
|
||||
return (
|
||||
<MessageBar intent={messageBarIntent} className={styles.messageBar}>
|
||||
<MessageBarBody>{notification.message}</MessageBarBody>
|
||||
</MessageBar>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user