From e8da716e8ffbc79c5bec4873c197d0a395266539 Mon Sep 17 00:00:00 2001 From: zain94rif Date: Tue, 13 Jan 2026 16:31:21 +0700 Subject: [PATCH 1/3] feat(svg): add new icon svg open mail --- .../component/ListNotification.jsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/pages/notification/component/ListNotification.jsx b/src/pages/notification/component/ListNotification.jsx index c7078f3..fc1b647 100644 --- a/src/pages/notification/component/ListNotification.jsx +++ b/src/pages/notification/component/ListNotification.jsx @@ -36,7 +36,6 @@ import { PlusOutlined, ExclamationCircleOutlined, SearchOutlined, - MailFilled, } from '@ant-design/icons'; import { useNavigate, Link as RouterLink } from 'react-router-dom'; import { @@ -49,7 +48,11 @@ import { } from '../../../api/notification'; const { Text, Paragraph, Link: AntdLink } = Typography; - +const openMail = ( + + + +); // Transform API response to component format const transformNotificationData = (apiData) => { return apiData.map((item, index) => ({ @@ -184,13 +187,13 @@ const ListNotification = memo(function ListNotification(props) { const getIconAndColor = (type) => { switch (type) { case 'critical': - return { IconComponent: MailFilled, color: '#faad14', bgColor: '#fff1f0' }; + return { IconComponent: MailOutlined, color: '#faad14', bgColor: '#fff1f0' }; case 'warning': - return { IconComponent: MailFilled, color: '#1890ff', bgColor: '#fffbe6' }; + return { IconComponent: MailOutlined, color: '#1890ff', bgColor: '#fffbe6' }; case 'resolved': - return { IconComponent: MailFilled, color: '#52c41a', bgColor: '#f6ffed' }; + return { IconComponent: MailOutlined, color: '#52c41a', bgColor: '#f6ffed' }; default: - return { IconComponent: MailFilled, color: '#1890ff', bgColor: '#e6f7ff' }; + return { IconComponent: MailOutlined, color: '#1890ff', bgColor: '#e6f7ff' }; } }; @@ -407,7 +410,11 @@ const ListNotification = memo(function ListNotification(props) { flexShrink: 0, }} > - + {notification.type === 'resolved' ? ( + openMail + ) : ( + + )}
From c0fe3aaca136b9ce1855c09fc7d597fe030834cc Mon Sep 17 00:00:00 2001 From: zain94rif Date: Tue, 13 Jan 2026 17:04:50 +0700 Subject: [PATCH 2/3] fix(icon): change size & color icon svg --- .../notification/component/ListNotification.jsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/notification/component/ListNotification.jsx b/src/pages/notification/component/ListNotification.jsx index fc1b647..4758135 100644 --- a/src/pages/notification/component/ListNotification.jsx +++ b/src/pages/notification/component/ListNotification.jsx @@ -48,8 +48,14 @@ import { } from '../../../api/notification'; const { Text, Paragraph, Link: AntdLink } = Typography; -const openMail = ( - +const OpenMail = ({ size = 22, color = 'black' }) => ( + ); @@ -411,7 +417,7 @@ const ListNotification = memo(function ListNotification(props) { }} > {notification.type === 'resolved' ? ( - openMail + ) : ( )} From 2b3d8ea3d25dc3b3068a0f3787dbd14cdeef1483 Mon Sep 17 00:00:00 2001 From: zain94rif Date: Wed, 14 Jan 2026 11:26:39 +0700 Subject: [PATCH 3/3] feat(mqtt): update notification use websocket --- src/components/Global/MqttConnection.jsx | 23 +++++++++++++++++-- .../component/ListNotification.jsx | 10 +++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/components/Global/MqttConnection.jsx b/src/components/Global/MqttConnection.jsx index bde3404..e2be3aa 100644 --- a/src/components/Global/MqttConnection.jsx +++ b/src/components/Global/MqttConnection.jsx @@ -10,7 +10,8 @@ const topics = [ 'PIU_COD/COMPRESSOR/OVERVIEW', 'PIU_COD/COMPRESSOR/COMPRESSOR_A', 'PIU_COD/COMPRESSOR/COMPRESSOR_B', - 'PIU_COD/COMPRESSOR/COMPRESSOR_C' + 'PIU_COD/COMPRESSOR/COMPRESSOR_C', + 'PIU_COD/ERROR_CODE/SIM', ]; const options = { keepalive: 30, @@ -98,4 +99,22 @@ const setValSvg = (listenTopic, svg) => { }); }; -export { publishMessage, listenMessage, setValSvg }; +// === NOTIFICATION LISTENER === +const notifListeners = []; + +const onNotifUpdate = (callback) => { + notifListeners.push(callback); +}; + +client.on('message', (topic, message) => { + if (topic === import.meta.env.VITE_MQTT_TOPIC_COD) { + try { + const payload = JSON.parse(message.toString()); + notifListeners.forEach((cb) => cb(payload)); + } catch (err) { + console.error('Invalid notif payload', err); + } + } +}); + +export { publishMessage, listenMessage, setValSvg, onNotifUpdate }; diff --git a/src/pages/notification/component/ListNotification.jsx b/src/pages/notification/component/ListNotification.jsx index 4758135..64fe055 100644 --- a/src/pages/notification/component/ListNotification.jsx +++ b/src/pages/notification/component/ListNotification.jsx @@ -46,6 +46,7 @@ import { resendChatAllUser, searchData, } from '../../../api/notification'; +import { onNotifUpdate } from '../../../components/Global/MqttConnection'; const { Text, Paragraph, Link: AntdLink } = Typography; const OpenMail = ({ size = 22, color = 'black' }) => ( @@ -101,6 +102,7 @@ const ListNotification = memo(function ListNotification(props) { const [activeTab, setActiveTab] = useState('all'); const [searchTerm, setSearchTerm] = useState(''); const [searchValue, setSearchValue] = useState(''); + const [notifTrigger, setNotifTrigger] = useState(0); const [loading, setLoading] = useState(false); const [modalContent, setModalContent] = useState(null); // 'user', 'log', 'details', or null const [isAddingLog, setIsAddingLog] = useState(false); @@ -178,6 +180,12 @@ const ListNotification = memo(function ListNotification(props) { fetchNotifications(page, pageSize, isReadFilter); }; + useEffect(() => { + onNotifUpdate(() => { + setNotifTrigger((prev) => prev + 1); + }); + }, []); + useEffect(() => { const token = localStorage.getItem('token'); if (!token) { @@ -188,7 +196,7 @@ const ListNotification = memo(function ListNotification(props) { // Fetch notifications on component mount and when tab changes const isReadFilter = activeTab === 'read' ? 1 : activeTab === 'unread' ? 0 : null; fetchNotifications(pagination.current_page, pagination.current_limit, isReadFilter); - }, [activeTab]); + }, [activeTab, notifTrigger]); const getIconAndColor = (type) => { switch (type) {