IO-3166-Global-Notifications-Part-2 - Checkpoint

This commit is contained in:
Dave Richer
2025-03-05 11:43:05 -05:00
parent f8ae6dc5af
commit 059067bc61
9 changed files with 220 additions and 147 deletions

View File

@@ -2,106 +2,115 @@ import { Virtuoso } from "react-virtuoso";
import { Alert, Badge, Button, Space, Spin, Tooltip, Typography } from "antd";
import { CheckCircleFilled, CheckCircleOutlined, EyeFilled, EyeOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import "./notification-center.styles.scss";
import day from "../../utils/day.js";
import { forwardRef } from "react";
const { Text, Title } = Typography;
const NotificationCenterComponent = ({
visible,
onClose,
notifications,
loading,
error,
showUnreadOnly,
toggleUnreadOnly,
markAllRead,
loadMore,
onNotificationClick,
unreadCount
}) => {
const { t } = useTranslation();
/**
* Notification Center Component
* @type {React.ForwardRefExoticComponent<React.PropsWithoutRef<{readonly visible?: *, readonly onClose?: *, readonly notifications?: *, readonly loading?: *, readonly error?: *, readonly showUnreadOnly?: *, readonly toggleUnreadOnly?: *, readonly markAllRead?: *, readonly loadMore?: *, readonly onNotificationClick?: *, readonly unreadCount?: *}> & React.RefAttributes<unknown>>}
*/
const NotificationCenterComponent = forwardRef(
(
{
visible,
onClose,
notifications,
loading,
error,
showUnreadOnly,
toggleUnreadOnly,
markAllRead,
loadMore,
onNotificationClick,
unreadCount
},
ref
) => {
const { t } = useTranslation();
const navigate = useNavigate();
const renderNotification = (index, notification) => {
return (
<div
key={`${notification.id}-${index}`}
className={`notification-item ${notification.read ? "notification-read" : "notification-unread"}`}
onClick={() => !notification.read && onNotificationClick(notification.id)}
>
<Badge dot={!notification.read}>
<div className="notification-content">
<Title level={5} className="notification-title">
<Link
to={`/manage/jobs/${notification.jobid}`}
onClick={(e) => {
e.stopPropagation();
if (!notification.read) {
onNotificationClick(notification.id);
}
}}
className="ro-number"
>
{t("notifications.labels.ro-number", { ro_number: notification.roNumber })}
</Link>
<Text
type="secondary"
className="relative-time"
title={day(notification.created_at).format("YYYY-MM-DD hh:mm A")}
>
{day(notification.created_at).fromNow()}
const renderNotification = (index, notification) => {
const handleClick = () => {
if (!notification.read) {
onNotificationClick(notification.id);
}
navigate(`/manage/jobs/${notification.jobid}`);
};
return (
<div
key={`${notification.id}-${index}`}
className={`notification-item ${notification.read ? "notification-read" : "notification-unread"}`}
onClick={handleClick}
>
<Badge dot={!notification.read}>
<div className="notification-content">
<Title level={5} className="notification-title">
<span className="ro-number">
{t("notifications.labels.ro-number", { ro_number: notification.roNumber })}
</span>
<Text
type="secondary"
className="relative-time"
title={day(notification.created_at).format("YYYY-MM-DD hh:mm A")}
>
{day(notification.created_at).fromNow()}
</Text>
</Title>
<Text strong={!notification.read} className="notification-body">
<ul>
{notification.scenarioText.map((text, idx) => (
<li key={`${notification.id}-${idx}`}>{text}</li>
))}
</ul>
</Text>
</Title>
<Text strong={!notification.read} className="notification-body">
<ul>
{notification.scenarioText.map((text, idx) => (
<li key={`${notification.id}-${idx}`}>{text}</li>
))}
</ul>
</Text>
</div>
</Badge>
</div>
);
};
return (
<div className={`notification-center ${visible ? "visible" : ""}`} ref={ref}>
<div className="notification-header">
<Space direction="horizontal">
<h3>{t("notifications.labels.notification-center")}</h3>
{loading && !error && <Spin spinning={loading} size="small"></Spin>}
</Space>
<div className="notification-controls">
<Tooltip title={t("notifications.labels.show-unread-only")}>
<Button
type="link"
icon={showUnreadOnly ? <EyeFilled /> : <EyeOutlined />}
onClick={() => toggleUnreadOnly(!showUnreadOnly)}
className={showUnreadOnly ? "active" : ""}
/>
</Tooltip>
<Tooltip title={t("notifications.labels.mark-all-read")}>
<Button
type="link"
icon={!unreadCount ? <CheckCircleFilled /> : <CheckCircleOutlined />}
onClick={markAllRead}
disabled={!unreadCount}
/>
</Tooltip>
</div>
</Badge>
</div>
{error && <Alert message={error} type="error" closable onClose={() => onClose()} />}
<Virtuoso
style={{ height: "400px", width: "100%" }}
data={notifications}
totalCount={notifications.length}
endReached={loadMore}
itemContent={renderNotification}
/>
</div>
);
};
return (
<div className={`notification-center ${visible ? "visible" : ""}`}>
<div className="notification-header">
<Space direction="horizontal">
<h3>{t("notifications.labels.notification-center")}</h3>
{loading && !error && <Spin spinning={loading} size="small"></Spin>}
</Space>
<div className="notification-controls">
<Tooltip title={t("notifications.labels.show-unread-only")}>
<Button
type="link"
icon={showUnreadOnly ? <EyeFilled /> : <EyeOutlined />}
onClick={() => toggleUnreadOnly(!showUnreadOnly)}
className={showUnreadOnly ? "active" : ""}
/>
</Tooltip>
<Tooltip title={t("notifications.labels.mark-all-read")}>
<Button
type="link"
icon={!unreadCount ? <CheckCircleFilled /> : <CheckCircleOutlined />}
onClick={markAllRead}
disabled={!unreadCount}
/>
</Tooltip>
</div>
</div>
{error && <Alert message={error} type="error" closable onClose={() => onClose()} />}
<Virtuoso
style={{ height: "400px", width: "100%" }}
data={notifications}
totalCount={notifications.length}
endReached={loadMore}
itemContent={renderNotification}
/>
</div>
);
};
}
);
export default NotificationCenterComponent;