feature/IO-3096-GlobalNotifications - Checkpoint - Additional String Cleanup, loading spinner

This commit is contained in:
Dave Richer
2025-03-03 12:07:19 -05:00
parent 35b92570e5
commit 86aa5bf5e7
8 changed files with 46 additions and 20 deletions

View File

@@ -180,3 +180,13 @@
.muted-button:hover { .muted-button:hover {
color: darkgrey; color: darkgrey;
} }
.notification-alert-unordered-list {
cursor: pointer;
padding: 0;
margin: 0;
.notification-alert-unordered-list-item {
margin-right: 0;
}
}

View File

@@ -1,6 +1,6 @@
import { Virtuoso } from "react-virtuoso"; import { Virtuoso } from "react-virtuoso";
import { Alert, Badge, Button, Tooltip, Typography } from "antd"; import { Alert, Badge, Button, Space, Spin, Tooltip, Typography } from "antd";
import { EyeFilled, EyeOutlined, CheckCircleFilled, CheckCircleOutlined } from "@ant-design/icons"; import { CheckCircleFilled, CheckCircleOutlined, EyeFilled, EyeOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import "./notification-center.styles.scss"; import "./notification-center.styles.scss";
@@ -66,7 +66,10 @@ const NotificationCenterComponent = ({
return ( return (
<div className={`notification-center ${visible ? "visible" : ""}`}> <div className={`notification-center ${visible ? "visible" : ""}`}>
<div className="notification-header"> <div className="notification-header">
<h3>{t("notifications.labels.notification-center")}</h3> <Space direction="horizontal">
<h3>{t("notifications.labels.notification-center")}</h3>
{loading && !error && <Spin spinning={loading} size="small"></Spin>}
</Space>
<div className="notification-controls"> <div className="notification-controls">
<Tooltip title={t("notifications.labels.show-unread-only")}> <Tooltip title={t("notifications.labels.show-unread-only")}>
<Button <Button
@@ -94,7 +97,6 @@ const NotificationCenterComponent = ({
endReached={loadMore} endReached={loadMore}
itemContent={renderNotification} itemContent={renderNotification}
/> />
{loading && !error && <div>{t("notifications.labels.loading")}</div>}
</div> </div>
); );
}; };

View File

@@ -12,13 +12,14 @@ import {
MARK_NOTIFICATION_READ MARK_NOTIFICATION_READ
} from "../../graphql/notifications.queries.js"; } from "../../graphql/notifications.queries.js";
import { gql, useMutation } from "@apollo/client"; import { gql, useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
const SocketContext = createContext(null); const SocketContext = createContext(null);
const INITIAL_NOTIFICATIONS = 10; const INITIAL_NOTIFICATIONS = 10;
/** /**
* Socket Provider - Scenario Notifications / Web Socket related items. * Socket Provider - Scenario Notifications / Web Socket related items
* @param children * @param children
* @param bodyshop * @param bodyshop
* @param navigate * @param navigate
@@ -33,6 +34,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const notification = useNotification(); const notification = useNotification();
const userAssociationId = bodyshop?.associations?.[0]?.id; const userAssociationId = bodyshop?.associations?.[0]?.id;
const { t } = useTranslation();
const [markNotificationRead] = useMutation(MARK_NOTIFICATION_READ, { const [markNotificationRead] = useMutation(MARK_NOTIFICATION_READ, {
update: (cache, { data: { update_notifications } }) => { update: (cache, { data: { update_notifications } }) => {
@@ -284,19 +286,18 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser, scenarioNot
}); });
notification.info({ notification.info({
message: `Changes for ${jobRoNumber}:`, message: t("notifications.labels.notification-popup-title", { ro_number: jobRoNumber }),
description: ( description: (
<ul <ul
className="notification-alert-unorderd-list" className="notification-alert-unordered-list"
onClick={() => { onClick={() => {
markNotificationRead({ variables: { id: notificationId } }) markNotificationRead({ variables: { id: notificationId } })
.then(() => navigate(`/manage/jobs/${jobId}`)) .then(() => navigate(`/manage/jobs/${jobId}`))
.catch((e) => console.error(`Error marking notification read: ${e?.message || ""}`)); .catch((e) => console.error(`Error marking notification read: ${e?.message || ""}`));
}} }}
style={{ cursor: "pointer" }}
> >
{notifications.map((notif, index) => ( {notifications.map((notif, index) => (
<li className="notification-alert-unorderd-list-item" key={index}> <li className="notification-alert-unordered-list-item" key={index}>
{notif.body} {notif.body}
</li> </li>
))} ))}

View File

@@ -23,8 +23,8 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
const jobid = job.id; const jobid = job.id;
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedWatcher, setSelectedWatcher] = useState(null); // New state for selected value const [selectedWatcher, setSelectedWatcher] = useState(null);
const [selectedTeam, setSelectedTeam] = useState(null); // New state to track selected team const [selectedTeam, setSelectedTeam] = useState(null);
// Fetch current watchers // Fetch current watchers
const { data: watcherData, loading: watcherLoading } = useQuery(GET_JOB_WATCHERS, { variables: { jobid } }); const { data: watcherData, loading: watcherLoading } = useQuery(GET_JOB_WATCHERS, { variables: { jobid } });
@@ -109,7 +109,7 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
<List.Item.Meta <List.Item.Meta
avatar={<Avatar icon={<UserOutlined />} />} avatar={<Avatar icon={<UserOutlined />} />}
title={<Text>{displayName}</Text>} title={<Text>{displayName}</Text>}
description={watcher.user_email} // Keep the email for reference description={watcher.user_email}
/> />
</List.Item> </List.Item>
); );
@@ -118,7 +118,6 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
// Popover content // Popover content
const popoverContent = ( const popoverContent = (
<div style={{ width: 600 }}> <div style={{ width: 600 }}>
{/* Self-toggle Button */}
<Button <Button
block block
type="text" type="text"
@@ -128,12 +127,10 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
> >
{isWatching ? t("notifications.tooltips.unwatch") : t("notifications.tooltips.watch")} {isWatching ? t("notifications.tooltips.unwatch") : t("notifications.tooltips.watch")}
</Button> </Button>
{/* List of Watchers */}
<Text type="secondary" style={{ marginBottom: 8, display: "block" }}> <Text type="secondary" style={{ marginBottom: 8, display: "block" }}>
{t("notifications.labels.watching-issue")} {t("notifications.labels.watching-issue")}
</Text> </Text>
{watcherLoading ? <LoadingSpinner /> : <List dataSource={jobWatchers} renderItem={handleRenderItem} />} {watcherLoading ? <LoadingSpinner /> : <List dataSource={jobWatchers} renderItem={handleRenderItem} />}
{/* Employee Search Select (for adding watchers) */}
<Divider /> <Divider />
<Text type="secondary">{t("notifications.labels.add-watchers")}</Text> <Text type="secondary">{t("notifications.labels.add-watchers")}</Text>

View File

@@ -3781,7 +3781,7 @@
"new-notification-title": "New Notification:", "new-notification-title": "New Notification:",
"show-unread-only": "Show Unread", "show-unread-only": "Show Unread",
"mark-all-read": "Mark Read", "mark-all-read": "Mark Read",
"loading": "Loading Notifications..." "notification-popup-title": "Changes for Job #{{ro_number}}"
}, },
"actions": { "actions": {
"remove": "remove" "remove": "remove"

View File

@@ -3769,6 +3769,7 @@
}, },
"notifications": { "notifications": {
"labels": { "labels": {
"notification-center": "",
"scenario": "", "scenario": "",
"notificationscenarios": "", "notificationscenarios": "",
"save": "", "save": "",
@@ -3776,7 +3777,11 @@
"add-watchers": "", "add-watchers": "",
"employee-search": "", "employee-search": "",
"teams-search": "", "teams-search": "",
"add-watchers-team": "" "add-watchers-team": "",
"new-notification-title": "",
"show-unread-only": "",
"mark-all-read": "",
"notification-popup-title": ""
}, },
"actions": { "actions": {
"remove": "" "remove": ""

View File

@@ -3769,6 +3769,7 @@
}, },
"notifications": { "notifications": {
"labels": { "labels": {
"notification-center": "",
"scenario": "", "scenario": "",
"notificationscenarios": "", "notificationscenarios": "",
"save": "", "save": "",
@@ -3776,7 +3777,11 @@
"add-watchers": "", "add-watchers": "",
"employee-search": "", "employee-search": "",
"teams-search": "", "teams-search": "",
"add-watchers-team": "" "add-watchers-team": "",
"new-notification-title": "",
"show-unread-only": "",
"mark-all-read": "",
"notification-popup-title": ""
}, },
"actions": { "actions": {
"remove": "" "remove": ""

View File

@@ -9,7 +9,13 @@ const { getJobAssignmentType } = require("./stringHelpers");
const populateWatchers = (data, result) => { const populateWatchers = (data, result) => {
data.scenarioWatchers.forEach((recipients) => { data.scenarioWatchers.forEach((recipients) => {
const { user, app, fcm, email, firstName, lastName, employeeId, associationId } = recipients; const { user, app, fcm, email, firstName, lastName, employeeId, associationId } = recipients;
if (app === true) result.app.recipients.push({ user, bodyShopId: data.bodyShopId, employeeId, associationId }); if (app === true)
result.app.recipients.push({
user,
bodyShopId: data.bodyShopId,
employeeId,
associationId
});
if (fcm === true) result.fcm.recipients.push(user); if (fcm === true) result.fcm.recipients.push(user);
if (email === true) result.email.recipients.push({ user, firstName, lastName }); if (email === true) result.email.recipients.push({ user, firstName, lastName });
}); });
@@ -147,7 +153,7 @@ const intakeDeliveryChecklistCompletedBuilder = (data) => {
* Builds notification data for job assignment events. * Builds notification data for job assignment events.
*/ */
const jobAssignedToMeBuilder = (data) => { const jobAssignedToMeBuilder = (data) => {
const body = `You have been assigned to [${getJobAssignmentType(data.scenarioFields?.[0])}]`; const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}`;
const result = { const result = {
app: { app: {
jobId: data.jobId, jobId: data.jobId,