- 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>
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
/**
|
|
* 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>
|
|
);
|
|
}
|
|
|