IO-3166-Global-Notifications-Part-2 - Checkpoint
This commit is contained in:
@@ -2,106 +2,115 @@ import { Virtuoso } from "react-virtuoso";
|
|||||||
import { Alert, Badge, Button, Space, Spin, Tooltip, Typography } from "antd";
|
import { Alert, Badge, Button, Space, Spin, Tooltip, Typography } from "antd";
|
||||||
import { CheckCircleFilled, CheckCircleOutlined, EyeFilled, EyeOutlined } 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 { useNavigate } from "react-router-dom";
|
||||||
import "./notification-center.styles.scss";
|
import "./notification-center.styles.scss";
|
||||||
import day from "../../utils/day.js";
|
import day from "../../utils/day.js";
|
||||||
|
import { forwardRef } from "react";
|
||||||
|
|
||||||
const { Text, Title } = Typography;
|
const { Text, Title } = Typography;
|
||||||
|
|
||||||
const NotificationCenterComponent = ({
|
/**
|
||||||
visible,
|
* Notification Center Component
|
||||||
onClose,
|
* @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>>}
|
||||||
notifications,
|
*/
|
||||||
loading,
|
const NotificationCenterComponent = forwardRef(
|
||||||
error,
|
(
|
||||||
showUnreadOnly,
|
{
|
||||||
toggleUnreadOnly,
|
visible,
|
||||||
markAllRead,
|
onClose,
|
||||||
loadMore,
|
notifications,
|
||||||
onNotificationClick,
|
loading,
|
||||||
unreadCount
|
error,
|
||||||
}) => {
|
showUnreadOnly,
|
||||||
const { t } = useTranslation();
|
toggleUnreadOnly,
|
||||||
|
markAllRead,
|
||||||
|
loadMore,
|
||||||
|
onNotificationClick,
|
||||||
|
unreadCount
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const renderNotification = (index, notification) => {
|
const renderNotification = (index, notification) => {
|
||||||
return (
|
const handleClick = () => {
|
||||||
<div
|
if (!notification.read) {
|
||||||
key={`${notification.id}-${index}`}
|
onNotificationClick(notification.id);
|
||||||
className={`notification-item ${notification.read ? "notification-read" : "notification-unread"}`}
|
}
|
||||||
onClick={() => !notification.read && onNotificationClick(notification.id)}
|
navigate(`/manage/jobs/${notification.jobid}`);
|
||||||
>
|
};
|
||||||
<Badge dot={!notification.read}>
|
|
||||||
<div className="notification-content">
|
return (
|
||||||
<Title level={5} className="notification-title">
|
<div
|
||||||
<Link
|
key={`${notification.id}-${index}`}
|
||||||
to={`/manage/jobs/${notification.jobid}`}
|
className={`notification-item ${notification.read ? "notification-read" : "notification-unread"}`}
|
||||||
onClick={(e) => {
|
onClick={handleClick}
|
||||||
e.stopPropagation();
|
>
|
||||||
if (!notification.read) {
|
<Badge dot={!notification.read}>
|
||||||
onNotificationClick(notification.id);
|
<div className="notification-content">
|
||||||
}
|
<Title level={5} className="notification-title">
|
||||||
}}
|
<span className="ro-number">
|
||||||
className="ro-number"
|
{t("notifications.labels.ro-number", { ro_number: notification.roNumber })}
|
||||||
>
|
</span>
|
||||||
{t("notifications.labels.ro-number", { ro_number: notification.roNumber })}
|
<Text
|
||||||
</Link>
|
type="secondary"
|
||||||
<Text
|
className="relative-time"
|
||||||
type="secondary"
|
title={day(notification.created_at).format("YYYY-MM-DD hh:mm A")}
|
||||||
className="relative-time"
|
>
|
||||||
title={day(notification.created_at).format("YYYY-MM-DD hh:mm A")}
|
{day(notification.created_at).fromNow()}
|
||||||
>
|
</Text>
|
||||||
{day(notification.created_at).fromNow()}
|
</Title>
|
||||||
|
<Text strong={!notification.read} className="notification-body">
|
||||||
|
<ul>
|
||||||
|
{notification.scenarioText.map((text, idx) => (
|
||||||
|
<li key={`${notification.id}-${idx}`}>{text}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</Text>
|
</Text>
|
||||||
</Title>
|
</div>
|
||||||
<Text strong={!notification.read} className="notification-body">
|
</Badge>
|
||||||
<ul>
|
</div>
|
||||||
{notification.scenarioText.map((text, idx) => (
|
);
|
||||||
<li key={`${notification.id}-${idx}`}>{text}</li>
|
};
|
||||||
))}
|
|
||||||
</ul>
|
return (
|
||||||
</Text>
|
<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>
|
</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>
|
</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;
|
export default NotificationCenterComponent;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; // Add useRef
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import NotificationCenterComponent from "./notification-center.component";
|
import NotificationCenterComponent from "./notification-center.component";
|
||||||
@@ -11,11 +11,21 @@ import day from "../../utils/day.js";
|
|||||||
// This will be used to poll for notifications when the socket is disconnected
|
// This will be used to poll for notifications when the socket is disconnected
|
||||||
const NOTIFICATION_POLL_INTERVAL_SECONDS = 60;
|
const NOTIFICATION_POLL_INTERVAL_SECONDS = 60;
|
||||||
|
|
||||||
export function NotificationCenterContainer({ visible, onClose, bodyshop, unreadCount }) {
|
/**
|
||||||
|
* Notification Center Container
|
||||||
|
* @param visible
|
||||||
|
* @param onClose
|
||||||
|
* @param bodyshop
|
||||||
|
* @param unreadCount
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount }) => {
|
||||||
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
||||||
const [notifications, setNotifications] = useState([]);
|
const [notifications, setNotifications] = useState([]);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const { isConnected, markNotificationRead, markAllNotificationsRead } = useSocket();
|
const { isConnected, markNotificationRead, markAllNotificationsRead } = useSocket();
|
||||||
|
const notificationRef = useRef(null); // Add ref for the notification center
|
||||||
|
|
||||||
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||||
|
|
||||||
@@ -50,6 +60,19 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop, unread
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle click outside to close
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (visible && notificationRef.current && !notificationRef.current.contains(event.target)) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [visible, onClose]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data?.notifications) {
|
if (data?.notifications) {
|
||||||
const processedNotifications = data.notifications
|
const processedNotifications = data.notifications
|
||||||
@@ -159,6 +182,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop, unread
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NotificationCenterComponent
|
<NotificationCenterComponent
|
||||||
|
ref={notificationRef}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
notifications={notifications}
|
notifications={notifications}
|
||||||
@@ -172,7 +196,7 @@ export function NotificationCenterContainer({ visible, onClose, bodyshop, unread
|
|||||||
unreadCount={unreadCount}
|
unreadCount={unreadCount}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08), 0 3px 6px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08), 0 3px 6px rgba(0, 0, 0, 0.06);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: none;
|
display: none;
|
||||||
overflow-x: hidden; /* Prevent horizontal overflow */
|
overflow-x: hidden;
|
||||||
|
|
||||||
&.visible {
|
&.visible {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -67,12 +67,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notification-item {
|
.notification-item {
|
||||||
padding: 8px 16px;
|
padding: 12px 16px; // Increased padding from 8px to 12px for more space
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
display: block;
|
display: block;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
cursor: pointer; // Add pointer cursor to indicate clickability
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #fafafa; // Optional: Add hover effect for better UX
|
||||||
|
}
|
||||||
|
|
||||||
.notification-content {
|
.notification-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -122,7 +127,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-badge {
|
.ant-badge {
|
||||||
width: 100%; /* Ensure Badge takes full width to allow .notification-title to stretch properly */
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-alert {
|
.ant-alert {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { notificationScenarios } from "../../utils/jobNotificationScenarios.js";
|
||||||
|
import { Checkbox, Form } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColumnHeaderCheckbox
|
||||||
|
* @param channel
|
||||||
|
* @param form
|
||||||
|
* @param disabled
|
||||||
|
* @param onHeaderChange
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const ColumnHeaderCheckbox = ({ channel, form, disabled = false, onHeaderChange }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Subscribe to all form values so that this component re-renders on changes.
|
||||||
|
const formValues = Form.useWatch([], form) || {};
|
||||||
|
|
||||||
|
// Determine if all scenarios for this channel are checked.
|
||||||
|
const allChecked =
|
||||||
|
notificationScenarios.length > 0 && notificationScenarios.every((scenario) => formValues[scenario]?.[channel]);
|
||||||
|
|
||||||
|
const onChange = (e) => {
|
||||||
|
const checked = e.target.checked;
|
||||||
|
// Get current form values.
|
||||||
|
const currentValues = form.getFieldsValue();
|
||||||
|
// Update each scenario for this channel.
|
||||||
|
const newValues = { ...currentValues };
|
||||||
|
notificationScenarios.forEach((scenario) => {
|
||||||
|
newValues[scenario] = { ...newValues[scenario], [channel]: checked };
|
||||||
|
});
|
||||||
|
// Update form values.
|
||||||
|
form.setFieldsValue(newValues);
|
||||||
|
// Manually mark the form as dirty.
|
||||||
|
if (onHeaderChange) {
|
||||||
|
onHeaderChange();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox onChange={onChange} checked={allChecked} disabled={disabled}>
|
||||||
|
{t(`notifications.channels.${channel}`)}
|
||||||
|
</Checkbox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ColumnHeaderCheckbox.propTypes = {
|
||||||
|
channel: PropTypes.oneOf(["app", "email", "fcm"]).isRequired,
|
||||||
|
form: PropTypes.object.isRequired,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onHeaderChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColumnHeaderCheckbox;
|
||||||
@@ -2,7 +2,6 @@ import { useMutation, useQuery } from "@apollo/client";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button, Card, Checkbox, Form, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Form, Space, Table } from "antd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
@@ -11,52 +10,21 @@ import { QUERY_NOTIFICATION_SETTINGS, UPDATE_NOTIFICATION_SETTINGS } from "../..
|
|||||||
import { notificationScenarios } from "../../utils/jobNotificationScenarios.js";
|
import { notificationScenarios } from "../../utils/jobNotificationScenarios.js";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component.jsx";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component.jsx";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
|
import ColumnHeaderCheckbox from "../notification-settings/column-header-checkbox.component.jsx";
|
||||||
|
|
||||||
const ColumnHeaderCheckbox = ({ channel, form, disabled = false, onHeaderChange }) => {
|
/**
|
||||||
const { t } = useTranslation();
|
* Notifications Settings Form
|
||||||
// Subscribe to all form values so that this component re-renders on changes.
|
* @param currentUser
|
||||||
const formValues = Form.useWatch([], form) || {};
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
// Determine if all scenarios for this channel are checked.
|
*/
|
||||||
const allChecked =
|
const NotificationSettingsForm = ({ currentUser }) => {
|
||||||
notificationScenarios.length > 0 && notificationScenarios.every((scenario) => formValues[scenario]?.[channel]);
|
|
||||||
|
|
||||||
const onChange = (e) => {
|
|
||||||
const checked = e.target.checked;
|
|
||||||
// Get current form values.
|
|
||||||
const currentValues = form.getFieldsValue();
|
|
||||||
// Update each scenario for this channel.
|
|
||||||
const newValues = { ...currentValues };
|
|
||||||
notificationScenarios.forEach((scenario) => {
|
|
||||||
newValues[scenario] = { ...newValues[scenario], [channel]: checked };
|
|
||||||
});
|
|
||||||
// Update form values.
|
|
||||||
form.setFieldsValue(newValues);
|
|
||||||
// Manually mark the form as dirty.
|
|
||||||
if (onHeaderChange) {
|
|
||||||
onHeaderChange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Checkbox onChange={onChange} checked={allChecked} disabled={disabled}>
|
|
||||||
{t(`notifications.channels.${channel}`)}
|
|
||||||
</Checkbox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ColumnHeaderCheckbox.propTypes = {
|
|
||||||
channel: PropTypes.oneOf(["app", "email", "fcm"]).isRequired,
|
|
||||||
form: PropTypes.object.isRequired,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onHeaderChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
function NotificationSettingsForm({ currentUser }) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [initialValues, setInitialValues] = useState({});
|
const [initialValues, setInitialValues] = useState({});
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
|
const notification = useNotification();
|
||||||
|
|
||||||
// Fetch notification settings.
|
// Fetch notification settings.
|
||||||
const { loading, error, data } = useQuery(QUERY_NOTIFICATION_SETTINGS, {
|
const { loading, error, data } = useQuery(QUERY_NOTIFICATION_SETTINGS, {
|
||||||
@@ -88,9 +56,14 @@ function NotificationSettingsForm({ currentUser }) {
|
|||||||
if (data?.associations?.length > 0) {
|
if (data?.associations?.length > 0) {
|
||||||
const userId = data.associations[0].id;
|
const userId = data.associations[0].id;
|
||||||
// Save the updated notification settings.
|
// Save the updated notification settings.
|
||||||
await updateNotificationSettings({ variables: { id: userId, ns: values } });
|
const result = await updateNotificationSettings({ variables: { id: userId, ns: values } });
|
||||||
setInitialValues(values);
|
if (!result?.errors) {
|
||||||
setIsDirty(false);
|
notification.success({ message: t("notifications.labels.notification-settings-success") });
|
||||||
|
setInitialValues(values);
|
||||||
|
setIsDirty(false);
|
||||||
|
} else {
|
||||||
|
notification.error({ message: t("notifications.labels.notification-settings-failure") });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,7 +153,7 @@ function NotificationSettingsForm({ currentUser }) {
|
|||||||
</Card>
|
</Card>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
NotificationSettingsForm.propTypes = {
|
NotificationSettingsForm.propTypes = {
|
||||||
currentUser: PropTypes.shape({
|
currentUser: PropTypes.shape({
|
||||||
@@ -8,8 +8,8 @@ import { selectCurrentUser } from "../../redux/user/user.selectors";
|
|||||||
import { logImEXEvent, updateCurrentPassword } from "../../firebase/firebase.utils";
|
import { logImEXEvent, updateCurrentPassword } from "../../firebase/firebase.utils";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import NotificationSettingsForm from "./notification-settings.component.jsx";
|
|
||||||
import { useSocket } from "../../contexts/SocketIO/useSocket.jsx";
|
import { useSocket } from "../../contexts/SocketIO/useSocket.jsx";
|
||||||
|
import NotificationSettingsForm from "../notification-settings/notification-settings-form.component.jsx";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser
|
currentUser: selectCurrentUser
|
||||||
|
|||||||
@@ -3783,7 +3783,9 @@
|
|||||||
"mark-all-read": "Mark Read",
|
"mark-all-read": "Mark Read",
|
||||||
"notification-popup-title": "Changes for Job #{{ro_number}}",
|
"notification-popup-title": "Changes for Job #{{ro_number}}",
|
||||||
"ro-number": "RO #{{ro_number}}",
|
"ro-number": "RO #{{ro_number}}",
|
||||||
"no-watchers": "No Watchers"
|
"no-watchers": "No Watchers",
|
||||||
|
"notification-settings-success": "Notification Settings saved successfully.",
|
||||||
|
"notification-settings-failure": "Error saving Notification Settings. {{error}}"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": "remove"
|
"remove": "remove"
|
||||||
|
|||||||
@@ -3783,7 +3783,9 @@
|
|||||||
"mark-all-read": "",
|
"mark-all-read": "",
|
||||||
"notification-popup-title": "",
|
"notification-popup-title": "",
|
||||||
"ro-number": "",
|
"ro-number": "",
|
||||||
"no-watchers": ""
|
"no-watchers": "",
|
||||||
|
"notification-settings-success": "",
|
||||||
|
"notification-settings-failure": ""
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": ""
|
"remove": ""
|
||||||
|
|||||||
@@ -3783,7 +3783,9 @@
|
|||||||
"mark-all-read": "",
|
"mark-all-read": "",
|
||||||
"notification-popup-title": "",
|
"notification-popup-title": "",
|
||||||
"ro-number": "",
|
"ro-number": "",
|
||||||
"no-watchers": ""
|
"no-watchers": "",
|
||||||
|
"notification-settings-success": "",
|
||||||
|
"notification-settings-failure": ""
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"remove": ""
|
"remove": ""
|
||||||
|
|||||||
Reference in New Issue
Block a user