diff --git a/client/src/Components/NotificationIntegrationModal/Components/NotificationIntegrationModal.jsx b/client/src/Components/NotificationIntegrationModal/Components/NotificationIntegrationModal.jsx index 67502d9a0..dc94ee731 100644 --- a/client/src/Components/NotificationIntegrationModal/Components/NotificationIntegrationModal.jsx +++ b/client/src/Components/NotificationIntegrationModal/Components/NotificationIntegrationModal.jsx @@ -1,17 +1,17 @@ -import { useState, useMemo, useEffect, useCallback} from "react"; +import { useState, useMemo, useEffect, useCallback } from "react"; import { useTranslation } from "react-i18next"; import PropTypes from "prop-types"; -import { - Dialog, - DialogContent, - DialogActions, - Button, - Typography, - Box, - Tabs, - Tab, - Stack, +import { + Dialog, + DialogContent, + DialogActions, + Button, + Typography, + Box, + Tabs, + Tab, + Stack, } from "@mui/material"; import { useTheme } from "@emotion/react"; import TabPanel from "./TabPanel"; @@ -20,115 +20,118 @@ import useNotifications from "../Hooks/useNotification"; // Define constants for notification types to avoid magic values const NOTIFICATION_TYPES = { - SLACK: 'slack', - DISCORD: 'discord', - TELEGRAM: 'telegram', - WEBHOOK: 'webhook' + SLACK: "slack", + DISCORD: "discord", + TELEGRAM: "telegram", + WEBHOOK: "webhook", }; // Define constants for field IDs const FIELD_IDS = { - WEBHOOK: 'webhook', - TOKEN: 'token', - CHAT_ID: 'chatId', - URL: 'url' + WEBHOOK: "webhook", + TOKEN: "token", + CHAT_ID: "chatId", + URL: "url", }; -const NotificationIntegrationModal = ({ - open, - onClose, - monitor, - setMonitor, - // Optional prop to configure available notification types - notificationTypes = null +const NotificationIntegrationModal = ({ + open, + onClose, + monitor, + setMonitor, + // Optional prop to configure available notification types + notificationTypes = null, }) => { - const { t } = useTranslation(); - const theme = useTheme(); - const [tabValue, setTabValue] = useState(0); - - const [loading, _, sendTestNotification] = useNotifications(); - - // Helper to get the field state key with error handling - const getFieldKey = useCallback((typeId, fieldId) => { - if (typeof typeId !== 'string' || typeId === '') { - throw new Error(t('errorInvalidTypeId')); - } - - if (typeof fieldId !== 'string' || fieldId === '') { - throw new Error(t('errorInvalidFieldId')); - } - - return `${typeId}${fieldId.charAt(0).toUpperCase() + fieldId.slice(1)}`; - }); - - // Define notification types - const DEFAULT_NOTIFICATION_TYPES = [ - { - id: NOTIFICATION_TYPES.SLACK, - label: t('notifications.slack.label'), - description: t('notifications.slack.description'), - fields: [ - { - id: FIELD_IDS.WEBHOOK, - label: t('notifications.slack.webhookLabel'), - placeholder: t('notifications.slack.webhookPlaceholder'), - type: 'text' - } - ] - }, - { - id: NOTIFICATION_TYPES.DISCORD, - label: t('notifications.discord.label'), - description: t('notifications.discord.description'), - fields: [ - { - id: FIELD_IDS.WEBHOOK, - label: t('notifications.discord.webhookLabel'), - placeholder: t('notifications.discord.webhookPlaceholder'), - type: 'text' - } - ] - }, - { - id: NOTIFICATION_TYPES.TELEGRAM, - label: t('notifications.telegram.label'), - description: t('notifications.telegram.description'), - fields: [ - { - id: FIELD_IDS.TOKEN, - label: t('notifications.telegram.tokenLabel'), - placeholder: t('notifications.telegram.tokenPlaceholder'), - type: 'text' - }, - { - id: FIELD_IDS.CHAT_ID, - label: t('notifications.telegram.chatIdLabel'), - placeholder: t('notifications.telegram.chatIdPlaceholder'), - type: 'text' - } - ] - }, - { - id: NOTIFICATION_TYPES.WEBHOOK, - label: t('notifications.webhook.label'), - description: t('notifications.webhook.description'), - fields: [ - { - id: FIELD_IDS.URL, - label: t('notifications.webhook.urlLabel'), - placeholder: t('notifications.webhook.urlPlaceholder'), - type: 'text' - } - ] - } - ]; + const { t } = useTranslation(); + const theme = useTheme(); + const [tabValue, setTabValue] = useState(0); - // Use provided notification types or default to our translated ones - const activeNotificationTypes = notificationTypes || DEFAULT_NOTIFICATION_TYPES; - - // Memoized function to initialize integrations state - const initialIntegrationsState = useMemo(() => { - const state = {}; + const [loading, _, sendTestNotification] = useNotifications(); + + // Helper to get the field state key with error handling + const getFieldKey = useCallback( + (typeId, fieldId) => { + if (typeof typeId !== "string" || typeId === "") { + throw new Error(t("errorInvalidTypeId")); + } + + if (typeof fieldId !== "string" || fieldId === "") { + throw new Error(t("errorInvalidFieldId")); + } + + return `${typeId}${fieldId.charAt(0).toUpperCase() + fieldId.slice(1)}`; + }, + [t] + ); + + // Define notification types + const DEFAULT_NOTIFICATION_TYPES = [ + { + id: NOTIFICATION_TYPES.SLACK, + label: t("notifications.slack.label"), + description: t("notifications.slack.description"), + fields: [ + { + id: FIELD_IDS.WEBHOOK, + label: t("notifications.slack.webhookLabel"), + placeholder: t("notifications.slack.webhookPlaceholder"), + type: "text", + }, + ], + }, + { + id: NOTIFICATION_TYPES.DISCORD, + label: t("notifications.discord.label"), + description: t("notifications.discord.description"), + fields: [ + { + id: FIELD_IDS.WEBHOOK, + label: t("notifications.discord.webhookLabel"), + placeholder: t("notifications.discord.webhookPlaceholder"), + type: "text", + }, + ], + }, + { + id: NOTIFICATION_TYPES.TELEGRAM, + label: t("notifications.telegram.label"), + description: t("notifications.telegram.description"), + fields: [ + { + id: FIELD_IDS.TOKEN, + label: t("notifications.telegram.tokenLabel"), + placeholder: t("notifications.telegram.tokenPlaceholder"), + type: "text", + }, + { + id: FIELD_IDS.CHAT_ID, + label: t("notifications.telegram.chatIdLabel"), + placeholder: t("notifications.telegram.chatIdPlaceholder"), + type: "text", + }, + ], + }, + { + id: NOTIFICATION_TYPES.WEBHOOK, + label: t("notifications.webhook.label"), + description: t("notifications.webhook.description"), + fields: [ + { + id: FIELD_IDS.URL, + label: t("notifications.webhook.urlLabel"), + placeholder: t("notifications.webhook.urlPlaceholder"), + type: "text", + }, + ], + }, + ]; + + // Use provided notification types or default to our translated ones + const activeNotificationTypes = notificationTypes || DEFAULT_NOTIFICATION_TYPES; + + // Memoized function to initialize integrations state + const initialIntegrationsState = useMemo(() => { + const state = {}; activeNotificationTypes.forEach((type) => { // Add enabled flag for each notification type @@ -140,13 +143,13 @@ const NotificationIntegrationModal = ({ state[fieldKey] = ""; }); }); - - return state; - }, [activeNotificationTypes, getFieldKey]); // Only recompute when these dependencies change - - const [integrations, setIntegrations] = useState(initialIntegrationsState); - useEffect(() => { + return state; + }, [activeNotificationTypes, getFieldKey]); // Only recompute when these dependencies change + + const [integrations, setIntegrations] = useState(initialIntegrationsState); + + useEffect(() => { if (open) { const extractNotificationValues = () => { const values = {}; @@ -202,204 +205,215 @@ const NotificationIntegrationModal = ({ } }, [open, monitor, getFieldKey]); - const handleChangeTab = (event, newValue) => { - setTabValue(newValue); - }; + const handleChangeTab = (event, newValue) => { + setTabValue(newValue); + }; - const handleIntegrationChange = (type, checked) => { - setIntegrations(prev => ({ - ...prev, - [type]: checked - })); - }; + const handleIntegrationChange = (type, checked) => { + setIntegrations((prev) => ({ + ...prev, + [type]: checked, + })); + }; - const handleInputChange = (type, value) => { - setIntegrations(prev => ({ - ...prev, - [type]: value - })); - }; + const handleInputChange = (type, value) => { + setIntegrations((prev) => ({ + ...prev, + [type]: value, + })); + }; - const handleTestNotification = async (type) => { - // Get the notification type details - const notificationType = activeNotificationTypes.find(t => t.id === type); - - if (typeof notificationType === "undefined") { - return; - } - - // Prepare config object based on notification type - const config = {}; - - // Add each field value to the config object - notificationType.fields.forEach(field => { - const fieldKey = getFieldKey(type, field.id); - config[field.id] = integrations[fieldKey]; - }); - - await sendTestNotification(type, config); - }; - - // In NotificationIntegrationModal.jsx, update the handleSave function: + const handleTestNotification = async (type) => { + // Get the notification type details + const notificationType = activeNotificationTypes.find((t) => t.id === type); -const handleSave = () => { - // Get existing notifications - const notifications = [...(monitor?.notifications || [])]; - - // Get all notification types IDs - const existingTypes = activeNotificationTypes.map(type => type.id); - - // Filter out notifications that are configurable in this modal - const filteredNotifications = notifications.filter( - notification => { - - if (notification.platform) { - return !existingTypes.includes(notification.platform); - } - - return !existingTypes.includes(notification.type); - } - ); + if (typeof notificationType === "undefined") { + return; + } - // Add each enabled notification with its configured fields - activeNotificationTypes.forEach(type => { - if (integrations[type.id]) { + // Prepare config object based on notification type + const config = {}; - let notificationObject = { - type: "webhook", - platform: type.id, // Set platform to identify the specific service - config: {} - }; - - // Configure based on notification type - switch(type.id) { - case "slack": - case "discord": - notificationObject.config.webhookUrl = integrations[getFieldKey(type.id, 'webhook')]; - break; - case "telegram": - notificationObject.config.botToken = integrations[getFieldKey(type.id, 'token')]; - notificationObject.config.chatId = integrations[getFieldKey(type.id, 'chatId')]; - break; - case "webhook": - notificationObject.config.webhookUrl = integrations[getFieldKey(type.id, 'url')]; - break; - } - - filteredNotifications.push(notificationObject); - } - }); + // Add each field value to the config object + notificationType.fields.forEach((field) => { + const fieldKey = getFieldKey(type, field.id); + config[field.id] = integrations[fieldKey]; + }); - // Update monitor with new notifications - setMonitor(prev => ({ - ...prev, - notifications: filteredNotifications - })); - - onClose(); -}; + await sendTestNotification(type, config); + }; - return ( - - - - {/* Left sidebar with tabs */} - - - {t('notifications.addOrEditNotifications')} - - - - {activeNotificationTypes.map((type) => ( - - ))} - - + // In NotificationIntegrationModal.jsx, update the handleSave function: - {/* Right side content */} - - {activeNotificationTypes.map((type, index) => ( - - - - ))} - - - - - - - - ); + const handleSave = () => { + // Get existing notifications + const notifications = [...(monitor?.notifications || [])]; + + // Get all notification types IDs + const existingTypes = activeNotificationTypes.map((type) => type.id); + + // Filter out notifications that are configurable in this modal + const filteredNotifications = notifications.filter((notification) => { + if (notification.platform) { + return !existingTypes.includes(notification.platform); + } + + return !existingTypes.includes(notification.type); + }); + + // Add each enabled notification with its configured fields + activeNotificationTypes.forEach((type) => { + if (integrations[type.id]) { + let notificationObject = { + type: "webhook", + platform: type.id, // Set platform to identify the specific service + config: {}, + }; + + // Configure based on notification type + switch (type.id) { + case "slack": + case "discord": + notificationObject.config.webhookUrl = + integrations[getFieldKey(type.id, "webhook")]; + break; + case "telegram": + notificationObject.config.botToken = + integrations[getFieldKey(type.id, "token")]; + notificationObject.config.chatId = + integrations[getFieldKey(type.id, "chatId")]; + break; + case "webhook": + notificationObject.config.webhookUrl = + integrations[getFieldKey(type.id, "url")]; + break; + } + + filteredNotifications.push(notificationObject); + } + }); + + // Update monitor with new notifications + setMonitor((prev) => ({ + ...prev, + notifications: filteredNotifications, + })); + + onClose(); + }; + + return ( + + + + {/* Left sidebar with tabs */} + + + {t("notifications.addOrEditNotifications")} + + + + {activeNotificationTypes.map((type) => ( + + ))} + + + + {/* Right side content */} + + {activeNotificationTypes.map((type, index) => ( + + + + ))} + + + + + + + + ); }; NotificationIntegrationModal.propTypes = { - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - monitor: PropTypes.object.isRequired, - setMonitor: PropTypes.func.isRequired, - notificationTypes: PropTypes.array + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + monitor: PropTypes.object.isRequired, + setMonitor: PropTypes.func.isRequired, + notificationTypes: PropTypes.array, }; -export default NotificationIntegrationModal; \ No newline at end of file +export default NotificationIntegrationModal;