diff --git a/app/localization/translated/be.json b/app/localization/translated/be.json
index bb5a0cf464..e49c296492 100644
--- a/app/localization/translated/be.json
+++ b/app/localization/translated/be.json
@@ -938,9 +938,9 @@
"IgnoreInAAModal.textMultiple": "Вы ўпэўненыя, што хочаце ігнараваць элементы ў Аўта-Аналізу?",
"IgnoreInAAModal.title": "Iгнараваць элемент ў Аўта-Аналізу",
"IgnoreInAAModal.titleMultiple": "Iгнараваць элементы ў Аўта-Аналізу",
+ "ImportLaunchModal.note": "Увага:",
"ImportModal.importConfirmation": "Пацвердзіць адмену",
"ImportModal.incorrectFileFormat": "Няправільны фармат файла",
- "ImportModal.note": "Увага:",
"IncludeInAAModal.includeButton": "Уключыць",
"IncludeInAAModal.successMessage": "Элемент паспяхова дададзены ў Аўта-Аналіз",
"IncludeInAAModal.successMessageMultiple": "Элементы паспяхова дададзены ў Аўта-Аналіз",
@@ -1572,6 +1572,7 @@
"PhotoControls.uploadError": "Не ўдалося абнавіць фота",
"PhotoControls.uploadPhoto": "Запампаваць Фотаздымак",
"PhotoControls.wasDeleted": "Фота паспяхова выдалена",
+ "PluginDropDown.ReportType": "Тып дакладу:",
"PluginItem.disablePluginMessage": "Вы ўпэўнены, што хочаце адключыць плагін {pluginName}? Калі вы адключыце плагін, інфармацыя пра яго будзе хавацца ў {pluginLocation}, і карыстальнікі не змогуць з ім ўзаемадзейнічаць",
"PluginItem.disablePluginTitle": "Адключыць плагін",
"PluginItem.disabledPluginMessage": "Плагін быў адключаны",
@@ -1580,6 +1581,7 @@
"PluginItem.enabledPluginMessage": "Плагін быў уключаны",
"PluginItem.titleVersion": "версія {version}",
"Plugins.disabled.bts": "не будзе паказаны ў наладах праекта. Карыстальнікі не змогуць стварыць дэфект або прымацаваць праблему да існуючага дэфекту ў СОД",
+ "Plugins.disabled.import": "Зараз у асобнік не ўключаны/запампаваны плагін {name}. Дакументацыя",
"Plugins.disabled.notification": "не будзе паказаны ў наладах праекта. Карыстальнікі не змогуць атрымліваць абвесткі аб завершаных запусках, а таксама не змогуць адпраўляць запрашэння",
"Plugins.disabled.other": "не будзе паказаны ў наладах праекта",
"PluginsFilter.all": "Усе",
diff --git a/app/localization/translated/ru.json b/app/localization/translated/ru.json
index e1d405dd76..7d9c6784c9 100644
--- a/app/localization/translated/ru.json
+++ b/app/localization/translated/ru.json
@@ -938,9 +938,9 @@
"IgnoreInAAModal.textMultiple": "Вы уверены, что хотите игнорировать элементы в Авто-Анализ?",
"IgnoreInAAModal.title": "Игнорировать элемент при Авто-Анализе",
"IgnoreInAAModal.titleMultiple": "Игнорировать элементы при Авто-Анализе",
+ "ImportLaunchModal.note": "Внимание:",
"ImportModal.importConfirmation": "Подтвердить отмену",
"ImportModal.incorrectFileFormat": "Неправильный формат файла",
- "ImportModal.note": "Внимание:",
"IncludeInAAModal.includeButton": "Включить",
"IncludeInAAModal.successMessage": "Элемент успешно добавлен в Авто-Анализ",
"IncludeInAAModal.successMessageMultiple": "Элементы успешно добавлены в Авто-Анализ",
@@ -1572,6 +1572,7 @@
"PhotoControls.uploadError": "Не удалось обновить фото",
"PhotoControls.uploadPhoto": "Загрузить Фото",
"PhotoControls.wasDeleted": "Фото успешно удалено",
+ "PluginDropDown.ReportType": "Тип отчёта:",
"PluginItem.disablePluginMessage": "Вы действительно хотите отключить плагин {pluginName}? Если вы отключите плагин, информация о нем будет скрыта в {pluginLocation}, и пользователи не смогут с ним взаимодействовать",
"PluginItem.disablePluginTitle": "Отключить плагин",
"PluginItem.disabledPluginMessage": "Плагин был отключен",
@@ -1580,6 +1581,7 @@
"PluginItem.enabledPluginMessage": "Плагин был включен",
"PluginItem.titleVersion": "версия {version}",
"Plugins.disabled.bts": "не будет показан в настройках проекта. Пользователи не смогут создать дефект или прикрепить проблему к существующему дефекту в СОД",
+ "Plugins.disabled.import": "В данный момент нет включенного/загруженного {name} плагина на инстансе. Документация",
"Plugins.disabled.notification": "не будет показан в настройках проекта. Пользователи не смогут получать оповещения о завершенных запусках, а также не смогут отправлять приглашения",
"Plugins.disabled.other": "не будет показан в настройках проекта",
"PluginsFilter.all": "Все",
diff --git a/app/localization/translated/uk.json b/app/localization/translated/uk.json
index 890c36822d..c86c3ff930 100644
--- a/app/localization/translated/uk.json
+++ b/app/localization/translated/uk.json
@@ -938,9 +938,9 @@
"IgnoreInAAModal.textMultiple": "Ви впевнені, що хочете ігнорувати елементи в Авто-Аналіз?",
"IgnoreInAAModal.title": "Ігнорувати елемент Авто-при Аналізі",
"IgnoreInAAModal.titleMultiple": "Ігнорувати елементи Авто-при Аналізі",
+ "ImportLaunchModal.note": "Увага:",
"ImportModal.importConfirmation": "Підтвердити скасування",
"ImportModal.incorrectFileFormat": "Неправильний формат файла",
- "ImportModal.note": "Увага:",
"IncludeInAAModal.includeButton": "Включити",
"IncludeInAAModal.successMessage": "Елемент успішно додано в Авто-Аналіз",
"IncludeInAAModal.successMessageMultiple": "Елементи успішно додано в Авто-Аналіз",
@@ -1572,6 +1572,7 @@
"PhotoControls.uploadError": "Фото Не вдалося оновити",
"PhotoControls.uploadPhoto": "Фото Завантажити",
"PhotoControls.wasDeleted": "Фото успішно видалено",
+ "PluginDropDown.ReportType": "Тип звіту:",
"PluginItem.disablePluginMessage": "Ви впевнені, що хочете вимкнути плагін {pluginName}? Якщо ви вимкнете плагін, інформація про нього буде прихована в {pluginLocation}, і користувачі не можуть взаємодіяти з ним",
"PluginItem.disablePluginTitle": "Вимкнути плагін",
"PluginItem.disabledPluginMessage": "Плагін був відключений",
@@ -1580,6 +1581,7 @@
"PluginItem.enabledPluginMessage": "Плагін був включений",
"PluginItem.titleVersion": "версія {version}",
"Plugins.disabled.bts": "не буде показаний в настроюваннях проекту. Користувачі не зможуть створити дефект або проблему прикріпити до існуючого дефекту в СОД",
+ "Plugins.disabled.import": "Жоден плагін {name} наразі не ввімкнено/завантажено в екземпляр. Документація",
"Plugins.disabled.notification": "не буде показаний в настроюваннях проекту. Користувачі не зможуть отримувати оповіщення про завершених запусках, а також не зможуть відсилати запрошення",
"Plugins.disabled.other": "не буде показаний в настроюваннях проекту",
"PluginsFilter.all": "Всі",
diff --git a/app/localization/translated/zh.json b/app/localization/translated/zh.json
index 794c779314..ad4f0d124d 100644
--- a/app/localization/translated/zh.json
+++ b/app/localization/translated/zh.json
@@ -938,9 +938,9 @@
"IgnoreInAAModal.textMultiple": "您确定要在自动分析中忽略这些测试项吗?",
"IgnoreInAAModal.title": "在自动分析中忽略测试项",
"IgnoreInAAModal.titleMultiple": "在自动分析中忽略测试项",
+ "ImportLaunchModal.note": "注:",
"ImportModal.importConfirmation": "确认取消",
"ImportModal.incorrectFileFormat": "文件格式不正确",
- "ImportModal.note": "注:",
"IncludeInAAModal.includeButton": "包含",
"IncludeInAAModal.successMessage": "测试项已成功包含在自动分析中",
"IncludeInAAModal.successMessageMultiple": "测试项已成功包含在自动分析中",
@@ -1572,6 +1572,7 @@
"PhotoControls.uploadError": "照片未成功上传",
"PhotoControls.uploadPhoto": "上传照片",
"PhotoControls.wasDeleted": "照片已删除",
+ "PluginDropDown.ReportType": "Report type:",
"PluginItem.disablePluginMessage": "您确定要禁用插件{pluginName}吗?如果您禁用该插件,有关它的信息将在{pluginLocation}上被隐藏,用户将无法与之交互",
"PluginItem.disablePluginTitle": "禁用插件",
"PluginItem.disabledPluginMessage": "插件已禁用",
@@ -1580,6 +1581,7 @@
"PluginItem.enabledPluginMessage": "插件已启用",
"PluginItem.titleVersion": "{version}",
"Plugins.disabled.bts": "{name}将在项目设置中隐藏。用户将无法在BTS中发布或关联问题",
+ "Plugins.disabled.import": "No {name} plugin is currently enabled/uploaded on the instance. Documentation",
"Plugins.disabled.notification": "{name}将在项目设置中隐藏。用户将无法收到通知并为新用户发送邀请",
"Plugins.disabled.other": "{name}将在项目设置中隐藏",
"PluginsFilter.all": "全部",
diff --git a/app/src/common/constants/pluginsGroupTypes.js b/app/src/common/constants/pluginsGroupTypes.js
index 918a758290..8648d3e777 100644
--- a/app/src/common/constants/pluginsGroupTypes.js
+++ b/app/src/common/constants/pluginsGroupTypes.js
@@ -19,4 +19,5 @@ export const NOTIFICATION_GROUP_TYPE = 'NOTIFICATION';
export const AUTHORIZATION_GROUP_TYPE = 'AUTH';
export const BTS_GROUP_TYPE = 'BTS';
export const ANALYZER_GROUP_TYPE = 'ANALYZER';
+export const IMPORT_GROUP_TYPE = 'IMPORT';
export const OTHER_GROUP_TYPE = 'OTHER';
diff --git a/app/src/components/integrations/messages.jsx b/app/src/components/integrations/messages.jsx
index 33f963b61c..305281fdf1 100644
--- a/app/src/components/integrations/messages.jsx
+++ b/app/src/components/integrations/messages.jsx
@@ -20,6 +20,7 @@ import {
ANALYZER_GROUP_TYPE,
AUTHORIZATION_GROUP_TYPE,
BTS_GROUP_TYPE,
+ IMPORT_GROUP_TYPE,
NOTIFICATION_GROUP_TYPE,
OTHER_GROUP_TYPE,
} from 'common/constants/pluginsGroupTypes';
@@ -80,6 +81,11 @@ const messages = defineMessages({
defaultMessage:
'{name} will be hidden on project settings. RP users can not get notifications and send invitations for new users',
},
+ pluginDisabledImport: {
+ id: 'Plugins.disabled.import',
+ defaultMessage:
+ 'No {name} plugin is currently enabled/uploaded on the instance. Documentation',
+ },
pluginDisabledOther: {
id: 'Plugins.disabled.other',
defaultMessage: '{name} will be hidden on project settings',
@@ -89,7 +95,8 @@ const messages = defineMessages({
export const PLUGIN_DISABLED_MESSAGES_BY_GROUP_TYPE = {
[BTS_GROUP_TYPE]: messages.pluginDisabledBts,
[NOTIFICATION_GROUP_TYPE]: messages.pluginDisabledNotification,
- [OTHER_GROUP_TYPE]: messages.pluginDisabledOther,
+ [IMPORT_GROUP_TYPE]: messages.pluginDisabledImport,
[AUTHORIZATION_GROUP_TYPE]: messages.pluginDisabledOther,
[ANALYZER_GROUP_TYPE]: messages.pluginDisabledOther,
+ [OTHER_GROUP_TYPE]: messages.pluginDisabledOther,
};
diff --git a/app/src/controllers/plugins/index.js b/app/src/controllers/plugins/index.js
index 8584216bec..c025ca343c 100644
--- a/app/src/controllers/plugins/index.js
+++ b/app/src/controllers/plugins/index.js
@@ -53,6 +53,7 @@ export {
isEmailIntegrationAvailableSelector,
isBtsPluginsExistSelector,
enabledBtsPluginsSelector,
+ enabledImportPluginsSelector,
globalIntegrationsSelector,
pluginsLoadingSelector,
} from './selectors';
diff --git a/app/src/controllers/plugins/selectors.js b/app/src/controllers/plugins/selectors.js
index 9e34ccaca4..34db3c3178 100644
--- a/app/src/controllers/plugins/selectors.js
+++ b/app/src/controllers/plugins/selectors.js
@@ -15,7 +15,11 @@
*/
import { createSelector } from 'reselect';
-import { BTS_GROUP_TYPE, NOTIFICATION_GROUP_TYPE } from 'common/constants/pluginsGroupTypes';
+import {
+ BTS_GROUP_TYPE,
+ IMPORT_GROUP_TYPE,
+ NOTIFICATION_GROUP_TYPE,
+} from 'common/constants/pluginsGroupTypes';
import { EMAIL } from 'common/constants/pluginNames';
import {
filterAvailablePlugins,
@@ -72,6 +76,10 @@ export const enabledBtsPluginsSelector = createSelector(pluginsSelector, (plugin
plugins.filter((item) => item.groupType === BTS_GROUP_TYPE && item.enabled),
);
+export const enabledImportPluginsSelector = createSelector(pluginsSelector, (plugins) =>
+ plugins.filter((plugin) => plugin.groupType === IMPORT_GROUP_TYPE && plugin.enabled),
+);
+
export const createNamedIntegrationsSelector = (integrationName, integrationsSelector) =>
createSelector(integrationsSelector, (integrations) =>
filterIntegrationsByName(integrations, integrationName),
diff --git a/app/src/pages/admin/pluginsPage/pluginsToolbar/actionPanel/actionPanel.jsx b/app/src/pages/admin/pluginsPage/pluginsToolbar/actionPanel/actionPanel.jsx
index 932c28cb7c..6ea41346ee 100644
--- a/app/src/pages/admin/pluginsPage/pluginsToolbar/actionPanel/actionPanel.jsx
+++ b/app/src/pages/admin/pluginsPage/pluginsToolbar/actionPanel/actionPanel.jsx
@@ -26,7 +26,7 @@ import { GhostButton } from 'components/buttons/ghostButton';
import { PLUGINS_PAGE_EVENTS } from 'components/main/analytics/events';
import ImportIcon from 'common/img/import-inline.svg';
import { URLS } from 'common/urls';
-import { MODAL_TYPE_UPLOAD_PLUGIN } from 'pages/common/modals/importModal/constants';
+import DOMPurify from 'dompurify';
import styles from './actionPanel.scss';
export const UPLOAD = 'upload';
@@ -90,13 +90,15 @@ export class ActionPanel extends Component {
tracking.trackEvent(PLUGINS_PAGE_EVENTS.CLICK_UPLOAD_BTN);
this.props.showModalAction({
- id: 'importModal',
+ id: 'importPluginModal',
data: {
- type: MODAL_TYPE_UPLOAD_PLUGIN,
onImport: this.props.fetchPluginsAction,
title: formatMessage(messages.modalTitle),
importButton: formatMessage(messages.uploadButton),
- tip: formatMessage(messages.uploadTip),
+ tip: formatMessage(messages.uploadTip, {
+ b: (data) => DOMPurify.sanitize(`${data}`),
+ span: (data) => DOMPurify.sanitize(`${data}`),
+ }),
incorrectFileSize: formatMessage(messages.incorrectFileSize),
url: URLS.plugin(),
singleImport: true,
diff --git a/app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.jsx b/app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.jsx
new file mode 100644
index 0000000000..b6aae53acc
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.jsx
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { useIntl } from 'react-intl';
+import Parser from 'html-react-parser';
+import classNames from 'classnames/bind';
+import Dropzone from 'react-dropzone';
+import PropTypes from 'prop-types';
+import { ImportFileIcon } from 'pages/common/modals/importModal/importFileIcon';
+import { uniqueId } from 'common/utils';
+import DropZoneIcon from 'common/img/shape-inline.svg';
+import styles from './dropzoneField.scss';
+
+const cx = classNames.bind(styles);
+
+export const DropzoneField = ({
+ disabled,
+ incorrectFileSize,
+ tip,
+ singleImport,
+ incorrectFileFormatMessage,
+ files,
+ setFiles,
+ maxFileSize,
+ acceptFileMimeTypes,
+}) => {
+ const { formatMessage } = useIntl();
+
+ const acceptFile = acceptFileMimeTypes.join(',');
+
+ const onDropAcceptedFileHandler = (file) => ({
+ file,
+ valid: true,
+ id: uniqueId(),
+ isLoading: false,
+ uploaded: false,
+ uploadingProgress: 0,
+ });
+
+ const formValidationMessage = (validationProperties) => {
+ const validationMessages = {
+ incorrectFileFormat: formatMessage(incorrectFileFormatMessage),
+ incorrectFileSize,
+ };
+ const validationMessage = [];
+
+ Object.keys(validationProperties).forEach((message) => {
+ if (validationProperties[message]) {
+ validationMessage.push(validationMessages[message]);
+ }
+ });
+
+ return validationMessage.join('. ').trim();
+ };
+
+ const validateFile = (file) => ({
+ incorrectFileFormat: !acceptFileMimeTypes.includes(file.type),
+ incorrectFileSize: file.size > maxFileSize,
+ });
+
+ const addFileRejectMessage = (file) => {
+ const validationProperties = validateFile(file);
+
+ return formValidationMessage(validationProperties);
+ };
+
+ const onDropRejectedFileHandler = (file) => ({
+ file,
+ valid: false,
+ id: uniqueId(),
+ rejectMessage: addFileRejectMessage(file),
+ });
+
+ const onDrop = (acceptedFiles, rejectedFiles) => {
+ const accepted = acceptedFiles.map(onDropAcceptedFileHandler);
+ const rejected = rejectedFiles.map(onDropRejectedFileHandler);
+
+ setFiles([...files, ...accepted, ...rejected]);
+ };
+
+ const onDelete = (id) => {
+ setFiles(files.filter((item) => item.id !== id));
+ };
+
+ return (
+
+ {files.length === 0 && (
+
+
{Parser(DropZoneIcon)}
+
{Parser(tip)}
+
+ )}
+ {files.length > 0 && (
+
+ {files.map((item) => (
+
+ ))}
+
+ )}
+
+ );
+};
+DropzoneField.propTypes = {
+ disabled: PropTypes.bool,
+ incorrectFileSize: PropTypes.string,
+ tip: PropTypes.string,
+ singleImport: PropTypes.bool,
+ incorrectFileFormatMessage: PropTypes.object,
+ files: PropTypes.array,
+ setFiles: PropTypes.func,
+ maxFileSize: PropTypes.number.isRequired,
+ acceptFileMimeTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
+};
+DropzoneField.defaultProps = {
+ disabled: false,
+ incorrectFileSize: '',
+ tip: '',
+ singleImport: true,
+ incorrectFileFormatMessage: {},
+ files: [],
+ setFiles: () => {},
+};
diff --git a/app/src/pages/common/modals/importModal/importModal.scss b/app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.scss
similarity index 82%
rename from app/src/pages/common/modals/importModal/importModal.scss
rename to app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.scss
index 3a5c87f01d..277ddb3e7c 100644
--- a/app/src/pages/common/modals/importModal/importModal.scss
+++ b/app/src/pages/common/modals/importModal/dropzoneField/dropzoneField.scss
@@ -1,5 +1,5 @@
-/*!
- * Copyright 2019 EPAM Systems
+/*
+ * Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,16 +60,3 @@
flex-wrap: wrap;
padding: 20px;
}
-
-.note-label {
- margin-top: 20px;
- color: $COLOR--tealish;
- font-size: 13px;
- font-family: $FONT-SEMIBOLD, sans-serif;
-}
-
-.note-message {
- font-family: $FONT-REGULAR, sans-serif;
- font-size: 12px;
- line-height: 1.5;
-}
diff --git a/app/src/pages/common/modals/importModal/index.js b/app/src/pages/common/modals/importModal/dropzoneField/index.js
similarity index 88%
rename from app/src/pages/common/modals/importModal/index.js
rename to app/src/pages/common/modals/importModal/dropzoneField/index.js
index ca97cb67ed..83d9ded81f 100644
--- a/app/src/pages/common/modals/importModal/index.js
+++ b/app/src/pages/common/modals/importModal/dropzoneField/index.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 EPAM Systems
+ * Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,4 +14,4 @@
* limitations under the License.
*/
-export { ImportModal } from './importModal';
+export DropzoneField from './dropzoneField';
diff --git a/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.jsx b/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.jsx
new file mode 100644
index 0000000000..cdc519a122
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.jsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { useState } from 'react';
+import classNames from 'classnames/bind';
+import Parser from 'html-react-parser';
+import { defineMessages, useIntl } from 'react-intl';
+import { withModal } from 'controllers/modal';
+import PropTypes from 'prop-types';
+import { PluginDropDown } from 'pages/inside/launchesPage/pluginDropDown';
+import { ImportModalLayout } from '../importModalLayout/importModalLayout';
+import styles from './importLaunchModal.scss';
+
+const cx = classNames.bind(styles);
+
+const messages = defineMessages({
+ note: {
+ id: 'ImportLaunchModal.note',
+ defaultMessage: 'Note:',
+ },
+});
+
+// todo get it from selectedPluginData.details
+const MAX_FILE_SIZES = 33554432;
+const ACCEPT_FILE_MIME_TYPES = [
+ 'application/zip',
+ 'application/x-zip-compressed',
+ 'application/zip-compressed',
+ 'application/xml',
+ 'text/xml',
+];
+
+export const ImportLaunchModal = ({ data }) => {
+ const { formatMessage } = useIntl();
+ const [selectedPluginData, setSelectedPluginData] = useState();
+
+ // todo use selectedPluginData for get supported format and max size
+ console.log(selectedPluginData);
+
+ return (
+
+
+ {formatMessage(messages.note)}
+ {Parser(data.noteMessage)}
+
+ );
+};
+ImportLaunchModal.propTypes = {
+ data: PropTypes.object.isRequired,
+};
+export default withModal('importLaunchModal')(ImportLaunchModal);
diff --git a/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.scss b/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.scss
new file mode 100644
index 0000000000..838c826893
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/importLaunchModal/importLaunchModal.scss
@@ -0,0 +1,28 @@
+/*!
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.note-label {
+ margin-top: 20px;
+ color: $COLOR--tealish;
+ font-size: 13px;
+ font-family: $FONT-SEMIBOLD, sans-serif;
+}
+
+.note-message {
+ font-family: $FONT-REGULAR, sans-serif;
+ font-size: 12px;
+ line-height: 1.5;
+}
diff --git a/app/src/pages/common/modals/importModal/constants.js b/app/src/pages/common/modals/importModal/importLaunchModal/index.js
similarity index 52%
rename from app/src/pages/common/modals/importModal/constants.js
rename to app/src/pages/common/modals/importModal/importLaunchModal/index.js
index 27d1332ea7..ca59e5decf 100644
--- a/app/src/pages/common/modals/importModal/constants.js
+++ b/app/src/pages/common/modals/importModal/importLaunchModal/index.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 EPAM Systems
+ * Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,21 +14,4 @@
* limitations under the License.
*/
-export const MODAL_TYPE_IMPORT_LAUNCH = 'import';
-export const MODAL_TYPE_UPLOAD_PLUGIN = 'upload';
-
-export const ACCEPT_FILE_MIME_TYPES = {
- [MODAL_TYPE_IMPORT_LAUNCH]: [
- 'application/zip',
- 'application/x-zip-compressed',
- 'application/zip-compressed',
- 'application/xml',
- 'text/xml',
- ],
- [MODAL_TYPE_UPLOAD_PLUGIN]: ['.jar'],
-};
-
-export const MAX_FILE_SIZES = {
- [MODAL_TYPE_IMPORT_LAUNCH]: 33554432,
- [MODAL_TYPE_UPLOAD_PLUGIN]: 134217728,
-};
+export { ImportLaunchModal } from './importLaunchModal';
diff --git a/app/src/pages/common/modals/importModal/importModal.jsx b/app/src/pages/common/modals/importModal/importModalLayout/importModalLayout.jsx
similarity index 67%
rename from app/src/pages/common/modals/importModal/importModal.jsx
rename to app/src/pages/common/modals/importModal/importModalLayout/importModalLayout.jsx
index a775a34f78..993c6ac035 100644
--- a/app/src/pages/common/modals/importModal/importModal.jsx
+++ b/app/src/pages/common/modals/importModal/importModalLayout/importModalLayout.jsx
@@ -16,28 +16,16 @@
import React, { Component, Fragment } from 'react';
import track from 'react-tracking';
-import classNames from 'classnames/bind';
-import Dropzone from 'react-dropzone';
-import Parser from 'html-react-parser';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, defineMessages } from 'react-intl';
import { ModalLayout, withModal } from 'components/main/modal';
import { showNotification, NOTIFICATION_TYPES } from 'controllers/notification';
import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
-import { uniqueId, fetch } from 'common/utils';
-import DropZoneIcon from 'common/img/shape-inline.svg';
-import { ImportFileIcon } from './importFileIcon';
-import styles from './importModal.scss';
-import { ACCEPT_FILE_MIME_TYPES, MAX_FILE_SIZES } from './constants';
-
-const cx = classNames.bind(styles);
+import { fetch } from 'common/utils';
+import { DropzoneField } from 'pages/common/modals/importModal/dropzoneField/dropzoneField';
const messages = defineMessages({
- note: {
- id: 'ImportModal.note',
- defaultMessage: 'Note:',
- },
importConfirmation: {
id: 'ImportModal.importConfirmation',
defaultMessage: 'Confirm cancel',
@@ -54,7 +42,7 @@ const messages = defineMessages({
showNotification,
})
@track()
-export class ImportModal extends Component {
+export class ImportModalLayout extends Component {
static propTypes = {
intl: PropTypes.object.isRequired,
showNotification: PropTypes.func.isRequired,
@@ -63,50 +51,22 @@ export class ImportModal extends Component {
trackEvent: PropTypes.func,
getTrackingData: PropTypes.func,
}).isRequired,
+ dropzoneCountNumber: PropTypes.number,
+ children: PropTypes.node,
+ maxFileSize: PropTypes.number.isRequired,
+ acceptFileMimeTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
};
static defaultProps = {
data: { eventsInfo: { uploadButton: () => {}, cancelBtn: {}, closeIcon: {} } },
+ dropzoneCountNumber: 0,
+ children: [null],
};
state = {
files: [],
};
- onDrop = (acceptedFiles, rejectedFiles) => {
- const { files: stateFiles } = this.state;
- const accepted = acceptedFiles.map(this.onDropAcceptedFileHandler);
- const rejected = rejectedFiles.map(this.onDropRejectedFileHandler);
-
- this.setState({
- files: [...stateFiles, ...accepted, ...rejected],
- });
- };
-
- onDropAcceptedFileHandler = (file) => ({
- file,
- valid: true,
- id: uniqueId(),
- isLoading: false,
- uploaded: false,
- uploadingProgress: 0,
- });
-
- onDropRejectedFileHandler = (file) => ({
- file,
- valid: false,
- id: uniqueId(),
- rejectMessage: this.addFileRejectMessage(file),
- });
-
- onDelete = (id) => {
- const { files } = this.state;
-
- this.setState({
- files: files.filter((item) => item.id !== id),
- });
- };
-
getOkButtonConfig = (isLoading, uploadFinished) => {
const {
intl,
@@ -159,43 +119,8 @@ export class ImportModal extends Component {
this.isUploadInProgress() ||
(this.props.data.singleImport && this.state.files.length > 0);
- validateFile = (file) => {
- const { type } = this.props.data;
-
- return {
- incorrectFileFormat: !ACCEPT_FILE_MIME_TYPES[type].includes(file.type),
- incorrectFileSize: file.size > MAX_FILE_SIZES[type],
- };
- };
-
getFilesNames = (files) => files.map(({ file: { name } }) => name).join('#');
- formValidationMessage = (validationProperties) => {
- const {
- intl,
- data: { incorrectFileSize },
- } = this.props;
- const validationMessages = {
- incorrectFileFormat: intl.formatMessage(messages.incorrectFileFormat),
- incorrectFileSize,
- };
- const validationMessage = [];
-
- Object.keys(validationProperties).forEach((message) => {
- if (validationProperties[message]) {
- validationMessage.push(validationMessages[message]);
- }
- });
-
- return validationMessage.join('. ').trim();
- };
-
- addFileRejectMessage = (file) => {
- const validationProperties = this.validateFile(file);
-
- return this.formValidationMessage(validationProperties);
- };
-
uploadFilesOnOkClick = () => {
const data = this.prepareDataForServerUploading();
@@ -329,14 +254,16 @@ export class ImportModal extends Component {
render() {
const {
intl,
- data: { type, title, tip, noteMessage, eventsInfo, singleImport },
+ data: { title, eventsInfo, tip, incorrectFileSize, singleImport },
+ children,
+ dropzoneCountNumber,
+ maxFileSize,
+ acceptFileMimeTypes,
} = this.props;
- const { files } = this.state;
const validFiles = this.getValidFiles();
const loading = this.isUploadInProgress();
const uploadFinished = this.isUploadFinished();
- const acceptFile = ACCEPT_FILE_MIME_TYPES[type].join(',');
return (
-
- {files.length === 0 && (
-
-
{Parser(DropZoneIcon)}
-
{Parser(tip)}
-
- )}
- {files.length > 0 && (
-
- {files.map((item) => (
-
- ))}
-
- )}
-
- {noteMessage && (
-
- {intl.formatMessage(messages.note)}
- {Parser(noteMessage)}
-
- )}
+ {children &&
+ children.map((child, index) => {
+ return index === dropzoneCountNumber ? (
+ // eslint-disable-next-line react/no-array-index-key
+
+ this.setState({ files: f })}
+ maxFileSize={maxFileSize}
+ acceptFileMimeTypes={acceptFileMimeTypes}
+ />
+ {child && child}
+
+ ) : (
+ child && child
+ );
+ })}
);
}
diff --git a/app/src/pages/common/modals/importModal/importModalLayout/index.js b/app/src/pages/common/modals/importModal/importModalLayout/index.js
new file mode 100644
index 0000000000..37c5dffc4d
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/importModalLayout/index.js
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { ImportModalLayout } from './importModalLayout';
diff --git a/app/src/pages/common/modals/importModal/importPluginModal/importPluginModal.jsx b/app/src/pages/common/modals/importModal/importPluginModal/importPluginModal.jsx
new file mode 100644
index 0000000000..1551d7f7fc
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/importPluginModal/importPluginModal.jsx
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { withModal } from 'controllers/modal';
+import PropTypes from 'prop-types';
+import { ImportModalLayout } from '../importModalLayout/importModalLayout';
+
+const MAX_FILE_SIZES = 134217728;
+const ACCEPT_FILE_MIME_TYPES = ['.jar'];
+
+export const ImportPluginModal = ({ data }) => (
+
+);
+ImportPluginModal.propTypes = {
+ data: PropTypes.object.isRequired,
+};
+export default withModal('importPluginModal')(ImportPluginModal);
diff --git a/app/src/pages/common/modals/importModal/importPluginModal/index.js b/app/src/pages/common/modals/importModal/importPluginModal/index.js
new file mode 100644
index 0000000000..16c7f0b4b7
--- /dev/null
+++ b/app/src/pages/common/modals/importModal/importPluginModal/index.js
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { ImportPluginModal } from './importPluginModal';
diff --git a/app/src/pages/common/modals/index.js b/app/src/pages/common/modals/index.js
index c0136eff35..d372fd8eca 100644
--- a/app/src/pages/common/modals/index.js
+++ b/app/src/pages/common/modals/index.js
@@ -15,4 +15,5 @@
*/
export { ConfirmationModal } from './confirmationModal';
-export { ImportModal } from './importModal';
+export { ImportLaunchModal } from './importModal/importLaunchModal';
+export { ImportPluginModal } from './importModal/importPluginModal';
diff --git a/app/src/pages/inside/launchesPage/LaunchToolbar/actionPanel/actionPanel.jsx b/app/src/pages/inside/launchesPage/LaunchToolbar/actionPanel/actionPanel.jsx
index db43309455..4fa9b79966 100644
--- a/app/src/pages/inside/launchesPage/LaunchToolbar/actionPanel/actionPanel.jsx
+++ b/app/src/pages/inside/launchesPage/LaunchToolbar/actionPanel/actionPanel.jsx
@@ -28,9 +28,13 @@ import { GhostMenuButton } from 'components/buttons/ghostMenuButton';
import { Breadcrumbs, breadcrumbDescriptorShape } from 'components/main/breadcrumbs';
import { breadcrumbsSelector, restorePathAction } from 'controllers/testItem';
import { LAUNCHES_PAGE_EVENTS } from 'components/main/analytics/events';
+import { PLUGIN_DISABLED_MESSAGES_BY_GROUP_TYPE } from 'components/integrations/messages';
+import { withTooltip } from 'componentLibrary/tooltip';
import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
+import { IMPORT_GROUP_TYPE } from 'common/constants/pluginsGroupTypes';
import AddWidgetIcon from 'common/img/add-widget-inline.svg';
import ImportIcon from 'common/img/import-inline.svg';
+import { createExternalLink, docsReferences } from 'common/utils';
import RefreshIcon from './img/refresh-inline.svg';
import styles from './actionPanel.scss';
@@ -46,6 +50,25 @@ const messages = defineMessages({
},
});
+const DisabledImportButton = () => (
+
+
+
+);
+
+const DisabledImportButtonTooltip = ({ tooltip }) => {tooltip};
+DisabledImportButtonTooltip.propTypes = {
+ tooltip: PropTypes.string.isRequired,
+};
+
+// fixme may be use popup instead tooltip? and convert string to html
+const DisabledImportButtonWithTooltip = withTooltip({
+ ContentComponent: DisabledImportButtonTooltip,
+ side: 'bottom',
+ noArrow: false,
+ tooltipWrapperClassName: cx('tooltip-wrapper'),
+})(DisabledImportButton);
+
@connect(
(state) => ({
breadcrumbs: breadcrumbsSelector(state),
@@ -87,6 +110,7 @@ export class ActionPanel extends Component {
activeFilterId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onAddNewWidget: PropTypes.func,
finishedLaunchesCount: PropTypes.number,
+ importPlugins: PropTypes.array.isRequired,
};
static defaultProps = {
@@ -233,8 +257,11 @@ export class ActionPanel extends Component {
restorePath,
onAddNewWidget,
finishedLaunchesCount,
+ importPlugins,
} = this.props;
const actionDescriptors = this.createActionDescriptors();
+ const isImportPluginEnabled = importPlugins.length > 0;
+ const importPluginDisabledMessage = PLUGIN_DISABLED_MESSAGES_BY_GROUP_TYPE[IMPORT_GROUP_TYPE];
return (
@@ -253,9 +280,18 @@ export class ActionPanel extends Component {
{this.isShowImportButton() && (
-
-
-
+ {isImportPluginEnabled ? (
+
+
+
+ ) : (
+ createExternalLink(data, docsReferences.pluginsDocs),
+ })}
+ />
+ )}
)}
{this.isShowWidgetButton() && (
diff --git a/app/src/pages/inside/launchesPage/LaunchToolbar/launchToolbar.jsx b/app/src/pages/inside/launchesPage/LaunchToolbar/launchToolbar.jsx
index 450b7398f5..e8190ad332 100644
--- a/app/src/pages/inside/launchesPage/LaunchToolbar/launchToolbar.jsx
+++ b/app/src/pages/inside/launchesPage/LaunchToolbar/launchToolbar.jsx
@@ -41,6 +41,7 @@ export const LaunchToolbar = ({
onAddNewWidget,
activeFilterId,
finishedLaunchesCount,
+ importPlugins,
}) => (
{!!selectedLaunches.length && (
@@ -70,6 +71,7 @@ export const LaunchToolbar = ({
activeFilterId={activeFilterId}
onAddNewWidget={onAddNewWidget}
finishedLaunchesCount={finishedLaunchesCount}
+ importPlugins={importPlugins}
/>
);
@@ -92,6 +94,7 @@ LaunchToolbar.propTypes = {
activeFilterId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onAddNewWidget: PropTypes.func,
finishedLaunchesCount: PropTypes.number,
+ importPlugins: PropTypes.array.isRequired,
};
LaunchToolbar.defaultProps = {
selectedLaunches: [],
diff --git a/app/src/pages/inside/launchesPage/index.js b/app/src/pages/inside/launchesPage/index.js
index a5a8dddeec..278653a8db 100644
--- a/app/src/pages/inside/launchesPage/index.js
+++ b/app/src/pages/inside/launchesPage/index.js
@@ -19,4 +19,5 @@ export { MoveToDebugModal, LaunchCompareModal, LaunchAnalysisModal } from './mod
export { DeleteItemsModal } from 'pages/inside/common/modals/deleteItemsModal';
export { EditItemModal } from 'pages/inside/common/modals/editItemModal';
export { EditItemsModal } from 'pages/inside/common/modals/editItemsModal';
-export { ImportModal } from 'pages/common/modals/importModal';
+export { ImportLaunchModal } from 'pages/common/modals/importModal/importLaunchModal';
+export { ImportPluginModal } from 'pages/common/modals/importModal/importPluginModal';
diff --git a/app/src/pages/inside/launchesPage/launchesPage.jsx b/app/src/pages/inside/launchesPage/launchesPage.jsx
index 65839dd88f..e1109eaada 100644
--- a/app/src/pages/inside/launchesPage/launchesPage.jsx
+++ b/app/src/pages/inside/launchesPage/launchesPage.jsx
@@ -33,7 +33,6 @@ import { LAUNCH_ITEM_TYPES } from 'common/constants/launchItemTypes';
import { ANALYZER_TYPES } from 'common/constants/analyzerTypes';
import { IN_PROGRESS } from 'common/constants/testStatuses';
import { PaginationToolbar } from 'components/main/paginationToolbar';
-import { MODAL_TYPE_IMPORT_LAUNCH } from 'pages/common/modals/importModal/constants';
import { activeProjectSelector, userIdSelector } from 'controllers/user';
import { isDemoInstanceSelector } from 'controllers/appInfo';
import { projectConfigSelector } from 'controllers/project';
@@ -72,6 +71,7 @@ import { LaunchSuiteGrid } from 'pages/inside/common/launchSuiteGrid';
import { LaunchFiltersContainer } from 'pages/inside/common/launchFiltersContainer';
import { LaunchFiltersToolbar } from 'pages/inside/common/launchFiltersToolbar';
import { RefineFiltersPanel } from 'pages/inside/common/refineFiltersPanel';
+import { enabledImportPluginsSelector } from 'controllers/plugins';
import { DebugFiltersContainer } from './debugFiltersContainer';
import { LaunchToolbar } from './LaunchToolbar';
import { NoItemsDemo } from './noItemsDemo';
@@ -174,6 +174,7 @@ const messages = defineMessages({
projectSetting: projectConfigSelector(state),
highlightItemId: prevTestItemSelector(state),
isDemoInstance: isDemoInstanceSelector(state),
+ importPlugins: enabledImportPluginsSelector(state),
}),
{
showModalAction,
@@ -243,6 +244,7 @@ export class LaunchesPage extends Component {
updateLaunchesLocallyAction: PropTypes.func.isRequired,
highlightItemId: PropTypes.number,
isDemoInstance: PropTypes.bool,
+ importPlugins: PropTypes.arrayOf(PropTypes.object).isRequired,
};
static defaultProps = {
@@ -642,17 +644,20 @@ export class LaunchesPage extends Component {
const {
intl: { formatMessage },
activeProject,
+ importPlugins,
} = this.props;
this.props.tracking.trackEvent(LAUNCHES_PAGE_EVENTS.CLICK_IMPORT_BTN);
this.props.showModalAction({
- id: 'importModal',
+ id: 'importLaunchModal',
data: {
- type: MODAL_TYPE_IMPORT_LAUNCH,
onImport: this.props.fetchLaunchesAction,
title: formatMessage(messages.modalTitle),
importButton: formatMessage(messages.importButton),
- tip: formatMessage(messages.importTip),
+ tip: formatMessage(messages.importTip, {
+ b: (data) => DOMPurify.sanitize(`
${data}`),
+ span: (data) => DOMPurify.sanitize(`
${data}`),
+ }),
incorrectFileSize: formatMessage(messages.incorrectFileSize),
noteMessage: formatMessage(messages.noteMessage),
importConfirmationWarning: formatMessage(messages.importConfirmationWarning),
@@ -662,6 +667,7 @@ export class LaunchesPage extends Component {
cancelBtn: LAUNCHES_MODAL_EVENTS.CANCEL_BTN_IMPORT_MODAL,
closeIcon: LAUNCHES_MODAL_EVENTS.CLOSE_ICON_IMPORT_MODAL,
},
+ importPlugins,
},
});
};
@@ -762,6 +768,7 @@ export class LaunchesPage extends Component {
loading,
debugMode,
isDemoInstance,
+ importPlugins,
} = this.props;
const rowHighlightingConfig = {
@@ -814,6 +821,7 @@ export class LaunchesPage extends Component {
activeFilterId={debugMode ? ALL : activeFilterId}
onAddNewWidget={this.showWidgetWizard}
finishedLaunchesCount={finishedLaunchesCount}
+ importPlugins={importPlugins}
/>
{debugMode && (
{
+ const { formatMessage } = useIntl();
+
+ const [selectedPlugin, setSelectedPlugin] = useState({
+ label: importPlugins?.[0]?.name,
+ value: importPlugins?.[0]?.name,
+ });
+
+ const pluginNamesOptions = importPlugins.map((plugin) => ({
+ label: plugin.name,
+ value: plugin.name,
+ }));
+
+ const onChangePluginName = (pluginName) => {
+ if (pluginName !== selectedPlugin.name) {
+ setSelectedPlugin({
+ label: pluginName,
+ value: pluginName,
+ });
+ setSelectedPluginData(importPlugins.filter((p) => p.name !== pluginName)?.[0]);
+ }
+ };
+
+ return (
+
+ {formatMessage(messages.reportType)}
+
+
+ );
+};
+PluginDropDown.propTypes = {
+ setSelectedPluginData: PropTypes.func.isRequired,
+ importPlugins: PropTypes.array.isRequired,
+};
diff --git a/app/src/pages/inside/launchesPage/pluginDropDown/pluginDropDown.scss b/app/src/pages/inside/launchesPage/pluginDropDown/pluginDropDown.scss
new file mode 100644
index 0000000000..85fcf466a2
--- /dev/null
+++ b/app/src/pages/inside/launchesPage/pluginDropDown/pluginDropDown.scss
@@ -0,0 +1,26 @@
+/*!
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.plugin-dropdown {
+ margin-bottom: 24px;
+
+ .field-name {
+ margin-right: 12px;
+ font-size: 13px;
+ font-family: $FONT-REGULAR;
+ color: $COLOR--charcoal-grey;
+ }
+}
diff --git a/app/src/pages/inside/projectSettingsPageContainer/content/notifications/LinkComponent/LinkComponent.scss b/app/src/pages/inside/projectSettingsPageContainer/content/notifications/LinkComponent/LinkComponent.scss
index 04c60abd92..22383f9cab 100644
--- a/app/src/pages/inside/projectSettingsPageContainer/content/notifications/LinkComponent/LinkComponent.scss
+++ b/app/src/pages/inside/projectSettingsPageContainer/content/notifications/LinkComponent/LinkComponent.scss
@@ -26,11 +26,11 @@
height: 22px;
}
-.icon {
- width: 16px;
- height: 16px;
-
- svg * {
- fill: $COLOR--e-300;
- }
-}
\ No newline at end of file
+.icon {
+ width: 16px;
+ height: 16px;
+
+ svg * {
+ fill: $COLOR--e-300;
+ }
+}
diff --git a/app/src/pages/inside/projectSettingsPageContainer/content/notifications/ruleGroup/ruleGroup.scss b/app/src/pages/inside/projectSettingsPageContainer/content/notifications/ruleGroup/ruleGroup.scss
index 4fb9a882ba..98bb2dfd8a 100644
--- a/app/src/pages/inside/projectSettingsPageContainer/content/notifications/ruleGroup/ruleGroup.scss
+++ b/app/src/pages/inside/projectSettingsPageContainer/content/notifications/ruleGroup/ruleGroup.scss
@@ -25,7 +25,7 @@
}
}
- .integrate-configurations{
+ .integrate-configurations {
display: flex;
gap: 24px;
align-items: center;
@@ -43,7 +43,7 @@
margin-top: 6px;
}
-.rule-section-layout{
+.rule-section-layout {
margin-top: 26px;
}
@@ -64,7 +64,7 @@
max-width: initial;
}
-.rule-group-list{
+.rule-group-list {
max-width: initial;
}