diff --git a/ui_src/src/App.js b/ui_src/src/App.js
index a4548a7d0..0da13ab08 100644
--- a/ui_src/src/App.js
+++ b/ui_src/src/App.js
@@ -14,9 +14,11 @@ import './App.scss';
import { Switch, Route, withRouter } from 'react-router-dom';
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
+import { JSONCodec, StringCodec, connect } from 'nats.ws';
import { useMediaQuery } from 'react-responsive';
-import { connect } from 'nats.ws';
-import { message } from 'antd';
+import { useHistory } from 'react-router-dom';
+import { message, notification } from 'antd';
+import { Redirect } from 'react-router-dom';
import {
LOCAL_STORAGE_ACCOUNT_ID,
@@ -28,23 +30,27 @@ import {
} from './const/localStorageConsts';
import { CLOUD_URL, ENVIRONMENT, HANDLE_REFRESH_INTERVAL, WS_PREFIX, WS_SERVER_URL_PRODUCTION } from './config';
import { handleRefreshTokenRequest, httpRequest } from './services/http';
+import infoNotificationIcon from './assets/images/infoNotificationIcon.svg';
+import successIcon from './assets/images/successIcon.svg';
+import close from './assets/images/closeNotification.svg';
import StationOverview from './domain/stationOverview';
+import errorIcon from './assets/images/errorIcon.svg';
import MessageJourney from './domain/messageJourney';
-import { isCloud } from './services/valueConvertor';
import Administration from './domain/administration';
+import { ApiEndpoints } from './const/apiEndpoints';
+import { isCloud } from './services/valueConvertor';
+import warnIcon from './assets/images/warnIcon.svg';
import AppWrapper from './components/appWrapper';
import StationsList from './domain/stationsList';
import SchemaManagment from './domain/schema';
-import { useHistory } from 'react-router-dom';
-import { Redirect } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';
+import AuthService from './services/auth';
import Overview from './domain/overview';
+import Loader from './components/loader';
import { Context } from './hooks/store';
import Profile from './domain/profile';
import pathDomains from './router';
import Users from './domain/users';
-import { ApiEndpoints } from './const/apiEndpoints';
-import AuthService from './services/auth';
let SysLogs = undefined;
let Login = undefined;
@@ -65,9 +71,14 @@ const App = withRouter((props) => {
const firebase_id_token = urlParams.get('firebase_id_token');
const firebase_organization_id = urlParams.get('firebase_organization_id');
const [cloudLogedIn, setCloudLogedIn] = useState(isCloud() ? false : true);
+ const [persistedNotifications, setPersistedNotifications] = useState(() => {
+ const storedNotifications = JSON.parse(localStorage.getItem('persistedNotifications'));
+ return storedNotifications || [];
+ });
+ const [displayedNotifications, setDisplayedNotifications] = useState([]);
- const ref = useRef();
- ref.current = cloudLogedIn;
+ const stateRef = useRef([]);
+ stateRef.current = [cloudLogedIn, persistedNotifications];
const handleLoginWithToken = async () => {
try {
@@ -133,7 +144,7 @@ const App = withRouter((props) => {
}, [isMobile]);
const handleRefresh = useCallback(async (firstTime) => {
- if (window.location.pathname === pathDomains.login || (firebase_id_token !== null && !ref.current)) {
+ if (window.location.pathname === pathDomains.login || (firebase_id_token !== null && !stateRef.current[0])) {
return;
} else if (localStorage.getItem(LOCAL_STORAGE_TOKEN)) {
const ws_port = localStorage.getItem(LOCAL_STORAGE_WS_PORT);
@@ -189,8 +200,115 @@ const App = withRouter((props) => {
};
}, [handleRefresh, setAuthCheck]);
+ useEffect(() => {
+ const sc = StringCodec();
+ const jc = JSONCodec();
+ let sub;
+ const subscribeToNotifications = async () => {
+ try {
+ const rawBrokerName = await state.socket?.request(`$memphis_ws_subs.get_system_messages`, sc.encode('SUB'));
+ if (rawBrokerName) {
+ const brokerName = JSON.parse(sc.decode(rawBrokerName?._rdata))['name'];
+ sub = state.socket?.subscribe(`$memphis_ws_pubs.get_system_messages.${brokerName}`);
+ listenForUpdates();
+ }
+ } catch (err) {
+ console.error('Error subscribing to overview data:', err);
+ }
+ };
+
+ const listenForUpdates = async () => {
+ try {
+ if (sub) {
+ for await (const msg of sub) {
+ let data = jc.decode(msg.data);
+ const uniqueNewNotifications = data.filter((newNotification) => {
+ return !stateRef.current[1].some((existingNotification) => existingNotification.id === newNotification.id);
+ });
+ setPersistedNotifications((prevPersistedNotifications) => [...prevPersistedNotifications, ...uniqueNewNotifications]);
+ localStorage.setItem('persistedNotifications', JSON.stringify([...stateRef.current[1], ...uniqueNewNotifications]));
+ }
+ }
+ } catch (err) {
+ console.error('Error receiving overview data updates:', err);
+ }
+ };
+
+ isCloud() && subscribeToNotifications();
+
+ return () => {
+ if (sub) {
+ try {
+ sub.unsubscribe();
+ } catch (err) {
+ console.error('Error unsubscribing from overview data:', err);
+ }
+ }
+ };
+ }, [state.socket]);
+
+ const notificationHandler = (id, type, message, duration) => {
+ const defaultAntdField = {
+ className: 'notification-wrapper',
+ closeIcon: ,
+ message: 'System Message',
+ onClose: () => {
+ const updatedNotifications = stateRef.current[1].map((n) => (n.id === id ? { ...n, read: true } : n));
+ setPersistedNotifications(updatedNotifications);
+ localStorage.setItem('persistedNotifications', JSON.stringify(updatedNotifications));
+ }
+ };
+ switch (type) {
+ case 'info':
+ notification.info({
+ ...defaultAntdField,
+ icon: ,
+ description: message,
+ duration: duration
+ });
+ break;
+ case 'warning':
+ notification.warning({
+ ...defaultAntdField,
+
+ icon: ,
+ description: message,
+ duration: duration
+ });
+ break;
+ case 'error':
+ notification.error({
+ ...defaultAntdField,
+ icon: ,
+ description: message,
+ duration: duration
+ });
+ break;
+ case 'success':
+ notification.success({
+ ...defaultAntdField,
+ icon: ,
+ description: message,
+ duration: duration
+ });
+ break;
+ default:
+ break;
+ }
+ };
+
+ useEffect(() => {
+ stateRef.current[1].forEach((notification) => {
+ if (!displayedNotifications.includes(notification.id) && !notification.read) {
+ notificationHandler(notification.id, notification.message_type, notification.message_payload, 0);
+ setDisplayedNotifications((prevDisplayedNotifications) => [...prevDisplayedNotifications, notification.id]);
+ }
+ });
+ }, [stateRef.current[1]]);
+
return (
+ {!cloudLogedIn &&
}
{' '}
{!authCheck &&
diff --git a/ui_src/src/App.scss b/ui_src/src/App.scss
index aa5eae054..99ea45dbc 100644
--- a/ui_src/src/App.scss
+++ b/ui_src/src/App.scss
@@ -159,4 +159,19 @@ pre {
}
.open {
transform: rotate(0deg);
+}
+
+.notification-wrapper{
+ border-radius: 12px;
+ border: 1px solid var(--gray-200, #EAECF0);
+ box-shadow: 0px 4px 6px -2px rgba(16, 24, 40, 0.03), 0px 12px 16px -4px rgba(16, 24, 40, 0.08);
+ .ant-notification-notice-message{
+ font-family: 'InterSemiBold';
+ font-size: 16px;
+ }
+ .ant-notification-notice-description{
+ font-family: 'Inter';
+ font-size: 14px;
+ color: #475467;
+ }
}
\ No newline at end of file
diff --git a/ui_src/src/assets/images/closeNotification.svg b/ui_src/src/assets/images/closeNotification.svg
new file mode 100644
index 000000000..3385614d8
--- /dev/null
+++ b/ui_src/src/assets/images/closeNotification.svg
@@ -0,0 +1,3 @@
+
diff --git a/ui_src/src/assets/images/errorIcon.svg b/ui_src/src/assets/images/errorIcon.svg
new file mode 100644
index 000000000..bc560c40f
--- /dev/null
+++ b/ui_src/src/assets/images/errorIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui_src/src/assets/images/infoNotificationIcon.svg b/ui_src/src/assets/images/infoNotificationIcon.svg
new file mode 100644
index 000000000..23b7de4bf
--- /dev/null
+++ b/ui_src/src/assets/images/infoNotificationIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui_src/src/assets/images/successIcon.svg b/ui_src/src/assets/images/successIcon.svg
new file mode 100644
index 000000000..02171bf7a
--- /dev/null
+++ b/ui_src/src/assets/images/successIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui_src/src/assets/images/warnIcon.svg b/ui_src/src/assets/images/warnIcon.svg
new file mode 100644
index 000000000..93c3bf731
--- /dev/null
+++ b/ui_src/src/assets/images/warnIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui_src/src/components/createStationForm/index.js b/ui_src/src/components/createStationForm/index.js
index 8c6fd3bf6..87fbd8ba2 100644
--- a/ui_src/src/components/createStationForm/index.js
+++ b/ui_src/src/components/createStationForm/index.js
@@ -176,26 +176,26 @@ const CreateStationForm = ({ createStationFormRef, getStartedStateRef, finishUpd
break;
}
setActualPods(replicas);
- } catch (error) { }
+ } catch (error) {}
};
const getAllSchemas = async () => {
try {
const data = await httpRequest('GET', ApiEndpoints.GET_ALL_SCHEMAS);
setSchemas(data);
- } catch (error) { }
+ } catch (error) {}
};
const getIntegration = async () => {
try {
const data = await httpRequest('GET', `${ApiEndpoints.GET_INTEGRATION_DETAILS}?name=s3`);
setIntegrateValue(data);
- } catch (error) { }
+ } catch (error) {}
};
const createStation = async (bodyRequest) => {
try {
- getStarted && setLoading(true);
+ setLoading(true);
const data = await httpRequest('POST', ApiEndpoints.CREATE_STATION, bodyRequest);
if (data) {
if (!getStarted) history.push(`${pathDomains.stations}/${data.name}`);
@@ -203,7 +203,7 @@ const CreateStationForm = ({ createStationFormRef, getStartedStateRef, finishUpd
}
} catch (error) {
} finally {
- getStarted && setLoading(false);
+ setLoading(false);
}
};
@@ -287,7 +287,7 @@ const CreateStationForm = ({ createStationFormRef, getStartedStateRef, finishUpd
)}
- {!isCloud() &&
+ {!isCloud() && (
- }
+ )}
diff --git a/ui_src/src/domain/overview/index.js b/ui_src/src/domain/overview/index.js
index b2b8ace55..888fe71ef 100644
--- a/ui_src/src/domain/overview/index.js
+++ b/ui_src/src/domain/overview/index.js
@@ -214,12 +214,12 @@ function OverView() {
Account ID :
{localStorage.getItem(LOCAL_STORAGE_ACCOUNT_ID)}
-
+
Broker Hostname :
{host}
-
+
)}
@@ -291,7 +291,7 @@ function OverView() {
open={open}
isLoading={creatingProsessd}
>
- setCreatingProsessd(e)} />
+ setCreatingProsessd(e)} />
);
diff --git a/ui_src/src/domain/overview/schemaverse/index.js b/ui_src/src/domain/overview/schemaverse/index.js
index beb49ede6..660695b63 100644
--- a/ui_src/src/domain/overview/schemaverse/index.js
+++ b/ui_src/src/domain/overview/schemaverse/index.js
@@ -35,12 +35,12 @@ const Schemaverse = () => {
- Total Schemas
+ Total schemas
{state?.monitor_data?.schemas_details?.total_schemas}
- Enforced Schemas
+ Enforced schemas
{state?.monitor_data?.schemas_details?.enforced_schemas}
diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/messages/index.js b/ui_src/src/domain/stationOverview/stationObservabilty/messages/index.js
index ae7760483..206b86cfc 100644
--- a/ui_src/src/domain/stationOverview/stationObservabilty/messages/index.js
+++ b/ui_src/src/domain/stationOverview/stationObservabilty/messages/index.js
@@ -150,22 +150,22 @@ const Messages = () => {
};
const handleResend = async () => {
- setResendProcced(true);
+ // setResendProcced(true);
try {
await httpRequest('POST', `${ApiEndpoints.RESEND_POISON_MESSAGE_JOURNEY}`, { poison_message_ids: isCheck, station_name: stationName });
- setTimeout(() => {
- setResendProcced(false);
- message.success({
- key: 'memphisSuccessMessage',
- content: isCheck.length === 1 ? 'The message was sent successfully' : 'The messages were sent successfully',
- duration: 5,
- style: { cursor: 'pointer' },
- onClick: () => message.destroy('memphisSuccessMessage')
- });
- setIsCheck([]);
- }, 1500);
+ // setTimeout(() => {
+ // setResendProcced(false);
+ // message.success({
+ // key: 'memphisSuccessMessage',
+ // content: isCheck.length === 1 ? 'The message was sent successfully' : 'The messages were sent successfully',
+ // duration: 5,
+ // style: { cursor: 'pointer' },
+ // onClick: () => message.destroy('memphisSuccessMessage')
+ // });
+ // setIsCheck([]);
+ // }, 1500);
} catch (error) {
- setResendProcced(false);
+ // setResendProcced(false);
}
};
@@ -256,28 +256,27 @@ const Messages = () => {