diff --git a/app/localization/translated/be.json b/app/localization/translated/be.json index f449811b43..72ad550050 100644 --- a/app/localization/translated/be.json +++ b/app/localization/translated/be.json @@ -274,6 +274,7 @@ "Common.add": "Дадаць", "Common.analyzerDisabled": "Сэрвiс analyzer не запушчаны", "Common.april": "Красавік", + "Common.assign": "Прызначыць", "Common.august": "Жнівень", "Common.back": "Назад", "Common.cancel": "Адмяніць", @@ -1076,9 +1077,11 @@ "InvestigatedPercentageOfLaunchesControls.ItemsFieldLabel": "Элементы", "InvestigatedPercentageOfLaunchesControls.ItemsValidationError": "Колькасць элементаў павінна быць памерам ад '1' да '600'", "InviteUserModal.description": "Калі ласка, звярніце ўвагу, што новыя карыстальнікі далучацца да арганізацыі з роляй “Удзельнік”, а існуючыя карыстальнікі захаваюць сваю бягучую арганізацыйную ролю.", + "InviteUserModal.descriptionAssign": "Прызначыць карыстальніка на праект", "InviteUserModal.emailLabel": "Электронная пошта", "InviteUserModal.canEditProject": "Можна рэдагаваць праект", "InviteUserModal.canEditTooltip": "Абраны карыстальнік мае ролю мэнэджэра ў арганізацыі і па змаўчанні будзе мець права ‘Mожа рэдагаваць’ у праекце", + "InviteUserModal.headerAssignUserModal": "Прызначыць карыстальніка", "InviteUserModal.headerInviteUserModal": "Запрасіць карыстальніка ў", "InviteUserModal.hintMessage": "Па змаўчанні запрошаныя карыстальнікі атрымліваюць правы 'Толькі прагляд'. Карыстальнікі з правамі 'Mожа рэдагаваць' могуць змяняць праект і ўсе яго дадзеныя (ствараць справаздачы аб запусках, змяняць тыпы дэфектаў і г.д.).", "InviteUserModal.inputPlaceholder": "Увядзіце поўнае імя або адрас электроннай пошты (напр. example@mail.com)", @@ -1381,19 +1384,22 @@ "MembersGrid.nameCol": "Назва фільтра", "MembersGrid.optionsCol": "Опцыі", "MembersGrid.ownerCol": "Уласнік", + "MembersGrid.roleCol": "Праектная ролю", "MembersListTable.adminAccessInfo": "Адміністратар мае поўны доступ да ўсіх арганізацый і праектаў у межах інстанцыі, незалежна ад указанай ролі і дазволаў.", "MembersListTable.adminRole": "Адміністратар", "MembersListTable.managerRole": "Менеджэр", "MembersListTable.editorRole": "Можа рэдагаваць", "MembersListTable.email": "Электронная пошта", "MembersListTable.lastLogin": "Апошні ўваход у сістэму", - "MembersListTable.managerRole": "Менеджэр", "MembersListTable.name": "Імя", "MembersListTable.permissions": "Дазвол", "MembersListTable.projects": "Projects", "MembersListTable.role": "Role", "MembersListTable.viewerRole": "Толькі для прагляду", "MembersListTable.youRole": "Вы", + "MembersPage.notFound": "Не знойдзена удзельнікаў па фільтру ''{filter}''", + "MembersPageToolbar.assignUser": "Прызначыць карыстальніка", + "MembersPageToolbar.inviteUser": "Запрасіць Карыстальніка", "MembersPageToolbar.membersSearchHint": "Імя карыстальніка не павінна быць пустым", "MembersPageToolbar.permissionMap": "Карта Правоў", "MembersPageToolbar.searchByName": "Шукаць па імя", @@ -1945,9 +1951,11 @@ "SortingControl.sortByFailedItems": "Няўдалыя пункты", "SortingControl.sortByPassingRate": "Прахадны бал", "SortingControl.sortByTotal": "Агульная колькасць", - "SsoUsersForm.formHeader": "Instance Invitations", + "SsoUsersForm.errorNotification": "Не ўдалося абнавіць налады SSO", + "SsoUsersForm.formHeader": "Запрашэнне карыстальнікаў", "SsoUsersForm.manualInvitesDescription": "Карыстальнікі могуць адпраўляць запрашэнні іншым карыстальнікам. Калі ўключана, новыя карыстальнікі могуць быць ствараны толькі праз SSO.", "SsoUsersForm.ssoOnlyDescription": "Новыя карыстальнікі могуць быць створаны толькі праз SSO.", + "SsoUsersForm.successNotification": "Налады SSO паспяхова абноўлены", "SsoUsersForm.switcherLabel": "Толькі SSO карыстальнікі", "StackTrace.jumpTo": "Перайсці", "StackTrace.linkText": "Адкрыць логі", diff --git a/app/localization/translated/es.json b/app/localization/translated/es.json index 6f5adf1795..6fddeb4049 100644 --- a/app/localization/translated/es.json +++ b/app/localization/translated/es.json @@ -273,6 +273,7 @@ "Common.add": "Agregar", "Common.analyzerDisabled": "El servicio del analizador no está disponible", "Common.april": "Abril", + "Common.assign": "Assign", "Common.august": "Agosto", "Common.back": "Atrás", "Common.cancel": "Cancelar", @@ -1075,9 +1076,11 @@ "InvestigatedPercentageOfLaunchesControls.ItemsFieldLabel": "Elementos", "InvestigatedPercentageOfLaunchesControls.ItemsValidationError": "El número de elementos debe estar entre '1' y '600'", "InviteUserModal.description": "Please note, users new to the organization will join it with “Member” role, whereas existing users will maintain their current organizational role.", + "InviteUserModal.descriptionAssign": "Assign user to the project", "InviteUserModal.canEditProject": "Can edit the Project", "InviteUserModal.canEditTooltip": "The selected user has the Manager role in organization and will have ‘Can edit’ permissions in the project by default", "InviteUserModal.emailLabel": "Correo electrónico", + "InviteUserModal.headerAssignUserModal": "Assign user", "InviteUserModal.headerInviteUserModal": "Invite user to", "InviteUserModal.hintMessage": "By default, invited users receive 'View only' permissions. Users with 'Can edit' permissions can modify the project and all its data (report launches, change defect types, etc.).", "InviteUserModal.inputPlaceholder": "Enter full name or email (e.g. example@mail.com)", @@ -1380,6 +1383,7 @@ "MembersGrid.nameCol": "Nombre / Usuario", "MembersGrid.optionsCol": "Opciones", "MembersGrid.ownerCol": "Propietario", + "MembersGrid.roleCol": "Rol de proyecto", "MembersListTable.adminAccessInfo": "Admin has full access to all organizations and projects within the instance, regardless of specified role and permissions.", "MembersListTable.adminRole": "Admin", "MembersListTable.editorRole": "Can edit", @@ -1392,6 +1396,9 @@ "MembersListTable.role": "Role", "MembersListTable.viewerRole": "View only", "MembersListTable.youRole": "You", + "MembersPage.notFound": "No se encontraron miembros para ''{filter}''", + "MembersPageToolbar.assignUser": "Assign User", + "MembersPageToolbar.inviteUser": "Invitar Usuario", "MembersPageToolbar.membersSearchHint": "El nombre de usuario no debe estar vacío", "MembersPageToolbar.permissionMap": "Mapa de permisos", "MembersPageToolbar.searchByName": "Buscar por nombre", @@ -1923,9 +1930,11 @@ "SortingControl.sortByFailedItems": "Elementos fallidos", "SortingControl.sortByPassingRate": "Porcentaje de aprobados", "SortingControl.sortByTotal": "Cantidad total", + "SsoUsersForm.errorNotification": "Failed to update SSO settings", "SsoUsersForm.formHeader": "Instance Invitations", "SsoUsersForm.manualInvitesDescription": "Users can manually send invitations for other users. If enabled new users can be created via SSO only.", "SsoUsersForm.ssoOnlyDescription": "New users can be created via SSO only.", + "SsoUsersForm.successNotification": "SSO settings have been updated successfully", "SsoUsersForm.switcherLabel": "SSO users only", "StackTrace.jumpTo": "Ir a", "StackTrace.linkText": "Abrir registros", diff --git a/app/localization/translated/ru.json b/app/localization/translated/ru.json index d003a9d17b..767665618e 100644 --- a/app/localization/translated/ru.json +++ b/app/localization/translated/ru.json @@ -274,6 +274,7 @@ "Common.add": "Добавить", "Common.analyzerDisabled": "Сервис analyzer не запущен", "Common.april": "Апрель", + "Common.assign": "Назначить", "Common.august": "Август", "Common.back": "Назад", "Common.cancel": "Отменить", @@ -1076,9 +1077,11 @@ "InvestigatedPercentageOfLaunchesControls.ItemsFieldLabel": "Элементы", "InvestigatedPercentageOfLaunchesControls.ItemsValidationError": "Количество элементов принимает значения от '1' до '600'", "InviteUserModal.description": "Пожалуйста, обратите внимание, что новые пользователи присоединятся к организации с ролью “Участник”, в то время как существующие пользователи сохранят свою текущую организационную роль.", + "InviteUserModal.descriptionAssign": "Назначить пользователя на проект", "InviteUserModal.canEditProject": "Может редактировать проект", "InviteUserModal.canEditTooltip": "Выбранный пользователь имеет роль менеджера в организации и по умолчанию будет иметь права ‘Может редактировать’ в проекте", "InviteUserModal.emailLabel": "Электронная почта", + "InviteUserModal.headerAssignUserModal": "Назначить пользователя", "InviteUserModal.headerInviteUserModal": "Пригласить пользователя в", "InviteUserModal.hintMessage": "По умолчанию приглашенные пользователи получают права 'Только просмотр'. Пользователи с правами 'Может редактировать' могут изменять проект и все его данные (создавать отчеты о запусках, изменять типы дефектов и т.д.).", "InviteUserModal.inputPlaceholder": "Введите полное имя или адрес электронной почты (напр. example@mail.com)", @@ -1379,6 +1382,7 @@ "MembersGrid.nameCol": "Имя / Логин", "MembersGrid.optionsCol": "Опции", "MembersGrid.ownerCol": "Владелец", + "MembersGrid.roleCol": "Проектная роль", "MembersListTable.adminAccessInfo": "Администратор имеет полный доступ ко всем организациям и проектам в рамках инстанции, независимо от указанной роли и разрешений.", "MembersListTable.adminRole": "Администратор", "MembersListTable.editorRole": "Может редактировать", @@ -1391,6 +1395,9 @@ "MembersListTable.role": "Role", "MembersListTable.viewerRole": "Только для просмотра", "MembersListTable.youRole": "Вы", + "MembersPage.notFound": "No members found for ''{filter}''", + "MembersPageToolbar.assignUser": "Назначить пользователя", + "MembersPageToolbar.inviteUser": "Пригласить Пользователя", "MembersPageToolbar.membersSearchHint": "Имя пользователя не должно быть пустым", "MergeLaunchDialog.MergeLaunchHeader": "Объединить запуски", "MergeLaunchDialog.extendSuitesDescriptionText": "Дополнить описание дочерних наборов названием оригинального запуска (напр. @запуск 'Запуск №1')", @@ -1942,9 +1949,11 @@ "SortingControl.sortByFailedItems": "Неудачные пункты", "SortingControl.sortByPassingRate": "Проходной балл", "SortingControl.sortByTotal": "Общее количество", - "SsoUsersForm.formHeader": "Instance Invitations", + "SsoUsersForm.errorNotification": "Не удалось обновить настройки SSO", + "SsoUsersForm.formHeader": "Приглашение пользователей", "SsoUsersForm.manualInvitesDescription": "Пользователи могут вручную отправлять приглашения другим пользователям. Если включено, новых пользователей можно создавать только через SSO.", "SsoUsersForm.ssoOnlyDescription": "Новых пользователей можно создавать только через SSO.", + "SsoUsersForm.successNotification": "Настройки SSO успешно обновлены", "SsoUsersForm.switcherLabel": "Только SSO пользователи", "StackTrace.jumpTo": "Перейти", "StackTrace.linkText": "Открыть логи", diff --git a/app/localization/translated/uk.json b/app/localization/translated/uk.json index 6a55d43530..80d30adf13 100644 --- a/app/localization/translated/uk.json +++ b/app/localization/translated/uk.json @@ -274,6 +274,7 @@ "Common.add": "Додати", "Common.analyzerDisabled": "Сервіс analyzer не запущений", "Common.april": "Квітень", + "Common.assign": "Призначити", "Common.august": "Серпень", "Common.back": "Тому", "Common.cancel": "Скасувати", @@ -1076,9 +1077,11 @@ "InvestigatedPercentageOfLaunchesControls.ItemsFieldLabel": "Елементи", "InvestigatedPercentageOfLaunchesControls.ItemsValidationError": "Кількість елементів приймає значення від '1' до '600'", "InviteUserModal.description": "Будь ласка, зверніть увагу, що нові користувачі приєднаються до організації з роллю “Учасник”, в той час як існуючі користувачі збережуть свою поточну організаційну роль.", + "InviteUserModal.descriptionAssign": "Долучити користувача до проєкту", "InviteUserModal.canEditProject": "Можна редагувати проект", "InviteUserModal.canEditTooltip": "Обраний користувач має роль менеджера в організації і за замовчуванням матиме права ‘Може редагувати’ в проекті", "InviteUserModal.emailLabel": "Адреса електронної пошти", + "InviteUserModal.headerAssignUserModal": "Призначити користувача", "InviteUserModal.headerInviteUserModal": "Запросити користувача в", "InviteUserModal.hintMessage": "За замовчуванням запрошені користувачі отримують права 'Тільки перегляд'. Користувачі з правами 'Може редагувати' можуть змінювати проект і всі його дані (створювати звіти про запуски, змінювати типи дефектів і т.д.).", "InviteUserModal.inputPlaceholder": "Введіть повне ім'я або адресу електронної пошти (напр. example@mail.com)", @@ -1381,6 +1384,7 @@ "MembersGrid.nameCol": "Ім’я / Логін", "MembersGrid.optionsCol": "Опції", "MembersGrid.ownerCol": "Власник", + "MembersGrid.roleCol": "Проектна роль", "MembersListTable.adminAccessInfo": "Адміністратор має повний доступ до всіх організацій та проєктів у межах інстанції, незалежно від зазначеної ролі та дозволів.", "MembersListTable.adminRole": "Адміністратор", "MembersListTable.editorRole": "Може редагувати", @@ -1393,6 +1397,9 @@ "MembersListTable.role": "Role", "MembersListTable.viewerRole": "Тільки для перегляду", "MembersListTable.youRole": "Ви", + "MembersPage.notFound": "No members found for ''{filter}''", + "MembersPageToolbar.assignUser": "Призначити користувача", + "MembersPageToolbar.inviteUser": "Запросити Користувача", "MembersPageToolbar.membersSearchHint": "Ім’я користувача не повинно бути порожнім", "MergeLaunchDialog.MergeLaunchHeader": "Об'єднати запуски", "MergeLaunchDialog.extendSuitesDescriptionText": "Доповнити опис дочірніх наборів назвою оригінального запуску (напр. @запуск '№Запуск 1')", @@ -1944,10 +1951,12 @@ "SortingControl.sortByFailedItems": "Невдалі пункти", "SortingControl.sortByPassingRate": "Прохідний бал", "SortingControl.sortByTotal": "Загальна кількість", - "SsoUsersForm.formHeader": "Instance Invitations", + "SsoUsersForm.errorNotification": "Не вдалося оновити налаштування SSO", + "SsoUsersForm.formHeader": "Запрошення користувачів", "SsoUsersForm.manualInvitesDescription": "Користувачі можуть самостійно надсилати запрошення іншим користувачам. Якщо ввімкнено, нові користувачі створюються виключно через SSO.", "SsoUsersForm.ssoOnlyDescription": "Користувачі створюються виключно через SSO.", - "SsoUsersForm.switcherLabel": "SSO users only", + "SsoUsersForm.successNotification": "Налаштування SSO успішно оновлено", + "SsoUsersForm.switcherLabel": "Тільки SSO користувачі", "StackTrace.jumpTo": "Перейти", "StackTrace.linkText": "Логи Відкрити", "StackTrace.loadLabel": "Завантажити", diff --git a/app/localization/translated/zh.json b/app/localization/translated/zh.json index e70470bdf9..8277f49306 100644 --- a/app/localization/translated/zh.json +++ b/app/localization/translated/zh.json @@ -274,6 +274,7 @@ "Common.add": "添加", "Common.analyzerDisabled": "服务分析器未运行", "Common.april": "四月", + "Common.assign": "Assign", "Common.august": "八月", "Common.back": "返回", "Common.cancel": "取消", @@ -1076,9 +1077,11 @@ "InvestigatedPercentageOfLaunchesControls.ItemsFieldLabel": "测试项", "InvestigatedPercentageOfLaunchesControls.ItemsValidationError": "测试项的数量应为1到600个", "InviteUserModal.description": "Please note, users new to the organization will join it with “Member” role, whereas existing users will maintain their current organizational role.", + "InviteUserModal.descriptionAssign": "Assign user to the project", "InviteUserModal.canEditProject": "Can edit the Project", "InviteUserModal.canEditTooltip": "The selected user has the Manager role in organization and will have ‘Can edit’ permissions in the project by default", "InviteUserModal.emailLabel": "邮箱", + "InviteUserModal.headerAssignUserModal": "Assign user", "InviteUserModal.headerInviteUserModal": "Invite user to", "InviteUserModal.hintMessage": "By default, invited users receive 'View only' permissions. Users with 'Can edit' permissions can modify the project and all its data (report launches, change defect types, etc.).", "InviteUserModal.inputPlaceholder": "Enter full name or email (e.g. example@mail.com)", @@ -1381,6 +1384,7 @@ "MembersGrid.nameCol": "过滤器名称", "MembersGrid.optionsCol": "选项", "MembersGrid.ownerCol": "所有者", + "MembersGrid.roleCol": "项目角色", "MembersListTable.adminAccessInfo": "Admin has full access to all organizations and projects within the instance, regardless of specified role and permissions.", "MembersListTable.adminRole": "Admin", "MembersListTable.editorRole": "Can edit", @@ -1393,6 +1397,9 @@ "MembersListTable.role": "Role", "MembersListTable.viewerRole": "View only", "MembersListTable.youRole": "You", + "MembersPage.notFound": "未找到“{filter}”的成员", + "MembersPageToolbar.assignUser": "Assign User", + "MembersPageToolbar.inviteUser": "邀请用户", "MembersPageToolbar.membersSearchHint": "成员名不能为空", "MergeLaunchDialog.MergeLaunchHeader": "合并测试任务", "MergeLaunchDialog.extendSuitesDescriptionText": "使用原始测试任务名称扩展子套件描述(例如 @测试任务 “测试任务 #1”)", @@ -1944,9 +1951,11 @@ "SortingControl.sortByFailedItems": "失败的测试项", "SortingControl.sortByPassingRate": "通过率", "SortingControl.sortByTotal": "全部", + "SsoUsersForm.errorNotification": "Failed to update SSO settings", "SsoUsersForm.formHeader": "Instance Invitations", "SsoUsersForm.manualInvitesDescription": "Users can manually send invitations for other users. If enabled new users can be created via SSO only.", "SsoUsersForm.ssoOnlyDescription": "New users can be created via SSO only.", + "SsoUsersForm.successNotification": "SSO settings have been updated successfully", "SsoUsersForm.switcherLabel": "SSO users only", "StackTrace.jumpTo": "跳转至", "StackTrace.linkText": "在日志视图中打开", diff --git a/app/src/common/constants/localization.js b/app/src/common/constants/localization.js index a1b668373a..1f2c379024 100644 --- a/app/src/common/constants/localization.js +++ b/app/src/common/constants/localization.js @@ -299,6 +299,10 @@ export const COMMON_LOCALE_KEYS = defineMessages({ id: 'Common.value', defaultMessage: 'Value', }, + ASSIGN: { + id: 'Common.assign', + defaultMessage: 'Assign', + }, }); export const months = [ diff --git a/app/src/common/urls.js b/app/src/common/urls.js index 6eb54419e3..6e6ecaa61b 100644 --- a/app/src/common/urls.js +++ b/app/src/common/urls.js @@ -318,4 +318,5 @@ export const URLS = { clusterByLaunchId: (projectKey, launchId, query) => `${urlBase}${projectKey}/launch/cluster/${launchId}${getQueryParams(query)}`, onboarding: (page = 'GENERAL') => `${urlBase}onboarding?page=${page}`, + instanceSettings: () => `${urlBase}settings`, }; diff --git a/app/src/components/inputs/inputUserSearch/inputUserSearch.jsx b/app/src/components/inputs/inputUserSearch/inputUserSearch.jsx index 7d83addf9f..cfd95778fc 100644 --- a/app/src/components/inputs/inputUserSearch/inputUserSearch.jsx +++ b/app/src/components/inputs/inputUserSearch/inputUserSearch.jsx @@ -80,6 +80,7 @@ export const InputUserSearch = ({ error, touched, placeholder, + creatable, }) => ( ); @@ -107,7 +108,9 @@ InputUserSearch.propTypes = { value: PropTypes.object, error: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), touched: PropTypes.bool, + creatable: PropTypes.bool, }; + InputUserSearch.defaultProps = { isAdmin: false, onChange: () => {}, @@ -115,4 +118,5 @@ InputUserSearch.defaultProps = { value: null, error: false, touched: false, + creatable: true, }; diff --git a/app/src/components/main/analytics/events/adminServerSettingsPageEvents.js b/app/src/components/main/analytics/events/adminServerSettingsPageEvents.js index 5fdca71142..c290f7527b 100644 --- a/app/src/components/main/analytics/events/adminServerSettingsPageEvents.js +++ b/app/src/components/main/analytics/events/adminServerSettingsPageEvents.js @@ -29,15 +29,21 @@ export const submitAnalyticsBtn = (status) => ({ }); export const ADMIN_SERVER_SETTINGS_PAGE_EVENTS = { + toggleSsoUsers: (switcherValue) => ({ + ...basicClickEventParametersAdminServerSettingsPage, + element_name: 'sso', + switcher: switcherValue ? 'on' : 'off', + }), + ANALYTICS_TAB: { + ...basicClickEventParametersAdminServerSettingsPage, + element_name: 'analytics', + }, + // GA3 events AUTHORIZATION_CONFIGURATION_TAB: { category: ADMIN_SERVER_SETTINGS_PAGE, action: 'Click on tab Authorization Configuration', label: 'Open tab Authorization Configuration', }, - ANALYTICS_TAB: { - ...basicClickEventParametersAdminServerSettingsPage, - element_name: 'analytics', - }, ACTIVATE_GITHUB_SWITCHER: { category: ADMIN_SERVER_SETTINGS_PAGE, action: 'Click on switcher Activate Github authorization on tab Authorization Configuration', diff --git a/app/src/pages/common/membersPage/membersPageToolbar/membersPageToolbar.jsx b/app/src/pages/common/membersPage/membersPageToolbar/membersPageToolbar.jsx index 9d9cb89782..5d1abbcad4 100644 --- a/app/src/pages/common/membersPage/membersPageToolbar/membersPageToolbar.jsx +++ b/app/src/pages/common/membersPage/membersPageToolbar/membersPageToolbar.jsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import React from 'react'; import PropTypes from 'prop-types'; import track from 'react-tracking'; import classNames from 'classnames/bind'; @@ -23,6 +24,7 @@ import { injectIntl, defineMessages } from 'react-intl'; import { reduxForm } from 'redux-form'; import { userRolesType } from 'common/constants/projectRoles'; import { userRolesSelector } from 'controllers/pages'; +import { ssoUsersOnlySelector } from 'controllers/appInfo'; import { canInviteInternalUser } from 'common/utils/permissions'; import { GhostButton } from 'components/buttons/ghostButton'; import { FieldProvider } from 'components/fields/fieldProvider'; @@ -45,14 +47,20 @@ const messages = defineMessages({ id: 'MembersPageToolbar.inviteUser', defaultMessage: 'Invite User', }, + assignUser: { + id: 'MembersPageToolbar.assignUser', + defaultMessage: 'Assign User', + }, searchInputPlaceholder: { id: 'MembersPageToolbar.searchByName', defaultMessage: 'Search by name', }, }); + @connect( (state) => ({ userRoles: userRolesSelector(state), + ssoUsersOnly: ssoUsersOnlySelector(state), }), { showModalAction, @@ -73,6 +81,7 @@ export class MembersPageToolbar extends React.Component { showModalAction: PropTypes.func.isRequired, onInvite: PropTypes.func, userRoles: userRolesType, + ssoUsersOnly: PropTypes.bool, tracking: PropTypes.shape({ trackEvent: PropTypes.func, getTrackingData: PropTypes.func, @@ -84,6 +93,7 @@ export class MembersPageToolbar extends React.Component { intl: {}, onInvite: () => {}, userRoles: {}, + ssoUsersOnly: false, onFilterChange: () => {}, }; @@ -103,6 +113,12 @@ export class MembersPageToolbar extends React.Component { this.props.showModalAction({ id: 'permissionMapModal' }); }; + getButtonText = () => { + const { ssoUsersOnly } = this.props; + if (ssoUsersOnly === undefined) return messages.inviteUser; + return ssoUsersOnly ? messages.assignUser : messages.inviteUser; + }; + render() { return (
@@ -125,7 +141,7 @@ export class MembersPageToolbar extends React.Component { onClick={this.showInviteUserModal} disabled={!canInviteInternalUser(this.props.userRoles)} > - {this.props.intl.formatMessage(messages.inviteUser)} + {this.props.intl.formatMessage(this.getButtonText())}
diff --git a/app/src/pages/common/membersPage/modals/inviteUserModal/inviteUserModal.jsx b/app/src/pages/common/membersPage/modals/inviteUserModal/inviteUserModal.jsx index 6559983eb9..b0ff8afec1 100644 --- a/app/src/pages/common/membersPage/modals/inviteUserModal/inviteUserModal.jsx +++ b/app/src/pages/common/membersPage/modals/inviteUserModal/inviteUserModal.jsx @@ -36,12 +36,12 @@ import { withModal, ModalField } from 'components/main/modal'; import { FieldProvider } from 'components/fields/fieldProvider'; import { FieldErrorHint } from 'components/fields/fieldErrorHint'; import { hideModalAction, showModalAction } from 'controllers/modal'; -import { areUserSuggestionsAllowedSelector } from 'controllers/appInfo'; +import { Input } from 'components/inputs/input'; +import { areUserSuggestionsAllowedSelector, ssoUsersOnlySelector } from 'controllers/appInfo'; import { AsyncAutocomplete } from 'components/inputs/autocompletes/asyncAutocomplete'; import { MEMBERS_PAGE_EVENTS } from 'components/main/analytics/events'; import { projectKeySelector, projectNameSelector } from 'controllers/project'; import { InputUserSearch, makeOptions } from 'components/inputs/inputUserSearch'; -import { Input } from 'components/inputs/input'; import { ADMINISTRATOR } from 'common/constants/accountRoles'; import HintIcon from './img/hint-inline.svg'; import styles from './inviteUserModal.scss'; @@ -64,6 +64,10 @@ const messages = defineMessages({ defaultMessage: 'The selected user has the Manager role in organization and will have ‘Can edit’ permissions in the project by default', }, + headerAssignUserModal: { + id: 'InviteUserModal.headerAssignUserModal', + defaultMessage: 'Assign user', + }, description: { id: 'InviteUserModal.description', defaultMessage: @@ -73,6 +77,14 @@ const messages = defineMessages({ id: 'InviteUserModal.nameOrEmailLabel', defaultMessage: 'Name or email', }, + descriptionAssign: { + id: 'InviteUserModal.descriptionAssign', + defaultMessage: 'Assign user to the project', + }, + loginOrEmailLabel: { + id: 'InviteUserModal.loginOrEmailLabel', + defaultMessage: 'Login or email', + }, emailLabel: { id: 'InviteUserModal.emailLabel', defaultMessage: 'Email', @@ -115,6 +127,7 @@ const inviteFormSelector = formValueSelector(INVITE_USER_FORM); selectedUser: inviteFormSelector(state, 'user'), isAdmin: isAdminSelector(state), projectKey: projectKeySelector(state), + ssoUsersOnly: ssoUsersOnlySelector(state), initialValues: { canEdit: false, project: urlProjectSlugSelector(state), @@ -157,6 +170,7 @@ export class InviteUserModal extends Component { selectedProject: PropTypes.string, selectedUser: PropTypes.object, isAdmin: PropTypes.bool, + ssoUsersOnly: PropTypes.bool, dirty: PropTypes.bool, areUserSuggestionsAllowed: PropTypes.bool.isRequired, projectName: PropTypes.string.isRequired, @@ -171,6 +185,7 @@ export class InviteUserModal extends Component { selectedProject: '', selectedUser: {}, isAdmin: false, + ssoUsersOnly: false, dirty: false, }; @@ -315,6 +330,7 @@ export class InviteUserModal extends Component { selectedUser, anyTouched, invalid, + ssoUsersOnly, } = this.props; const okButton = { @@ -323,6 +339,9 @@ export class InviteUserModal extends Component { ? COMMON_LOCALE_KEYS.INVITE_AND_ASSIGN : COMMON_LOCALE_KEYS.INVITE, ), + text: intl.formatMessage( + ssoUsersOnly ? COMMON_LOCALE_KEYS.ASSIGN : COMMON_LOCALE_KEYS.INVITE, + ), onClick: () => { handleSubmit(this.inviteUserAndCloseModal)(); }, @@ -339,7 +358,11 @@ export class InviteUserModal extends Component { return ( {intl.formatMessage(messages.inviteAdmin)} )} -

{intl.formatMessage(messages.description)}

+

+ {intl.formatMessage(ssoUsersOnly ? messages.descriptionAssign : messages.description)} +

{isProjectSelector || areUserSuggestionsAllowed ? ( diff --git a/app/src/pages/inside/stepPage/modals/makeDecisionModal/makeDecisionModal.jsx b/app/src/pages/inside/stepPage/modals/makeDecisionModal/makeDecisionModal.jsx index f3c7a39c17..de4366e76f 100644 --- a/app/src/pages/inside/stepPage/modals/makeDecisionModal/makeDecisionModal.jsx +++ b/app/src/pages/inside/stepPage/modals/makeDecisionModal/makeDecisionModal.jsx @@ -83,7 +83,9 @@ const MakeDecision = ({ data }) => { suggestChoice: {}, historyChoice: historyItems.find( (item) => - item.id !== itemData.id && !item.issue?.issueType.startsWith(TO_INVESTIGATE_LOCATOR_PREFIX), + item.id !== itemData.id && + item.issue && + !item.issue.issueType.startsWith(TO_INVESTIGATE_LOCATOR_PREFIX), ), commentOption: isBulkOperation ? NOT_CHANGED_FOR_ALL : REPLACE_FOR_ALL, extraAnalyticsParams: { @@ -390,7 +392,9 @@ const MakeDecision = ({ data }) => { const getMakeDecisionTabs = () => { const preparedHistoryLineItems = historyItems.filter( (item) => - item.id !== itemData.id && !item.issue?.issueType.startsWith(TO_INVESTIGATE_LOCATOR_PREFIX), + item.id !== itemData.id && + item.issue && + !item.issue.issueType.startsWith(TO_INVESTIGATE_LOCATOR_PREFIX), ); const tabsData = [ diff --git a/app/src/pages/instance/allUsersPage/usersToolbar/actionPanel/actionPanel.jsx b/app/src/pages/instance/allUsersPage/usersToolbar/actionPanel/actionPanel.jsx index 7aa6eb3f12..5b8e1cb026 100644 --- a/app/src/pages/instance/allUsersPage/usersToolbar/actionPanel/actionPanel.jsx +++ b/app/src/pages/instance/allUsersPage/usersToolbar/actionPanel/actionPanel.jsx @@ -27,6 +27,7 @@ import InviteUserIcon from 'common/img/invite-inline.svg'; import AddUserIcon from 'common/img/add-user-inline.svg'; import { URLS } from 'common/urls'; import { showModalAction } from 'controllers/modal'; +import { ssoUsersOnlySelector } from 'controllers/appInfo'; import { showNotification, showDefaultErrorNotification, @@ -65,6 +66,7 @@ const messages = defineMessages({ @connect( (state) => ({ filterEntities: collectFilterEntities(querySelector(state)), + ssoUsersOnly: ssoUsersOnlySelector(state), }), { showModalAction, @@ -86,10 +88,12 @@ export class ActionPanel extends Component { trackEvent: PropTypes.func, getTrackingData: PropTypes.func, }).isRequired, + ssoUsersOnly: PropTypes.bool, }; static defaultProps = { filterEntities: {}, + ssoUsersOnly: false, }; onExportUsers = () => { @@ -139,23 +143,28 @@ export class ActionPanel extends Component { }); }; - actionButtons = [ - { - key: EXPORT, - icon: ExportIcon, - onClick: this.onExportUsers, - }, - { - key: INVITE_USER, - icon: InviteUserIcon, - onClick: this.showInviteUserModal, - }, - { - key: ADD_USER, - icon: AddUserIcon, - onClick: this.showAddUserModal, - }, - ]; + get actionButtons() { + const { ssoUsersOnly } = this.props; + const allButtons = [ + { + key: EXPORT, + icon: ExportIcon, + onClick: this.onExportUsers, + }, + { + key: INVITE_USER, + icon: InviteUserIcon, + onClick: this.showInviteUserModal, + }, + { + key: ADD_USER, + icon: AddUserIcon, + onClick: this.showAddUserModal, + }, + ]; + + return ssoUsersOnly ? [allButtons[0]] : allButtons; + } renderHeaderButtons = () => { const { diff --git a/app/src/pages/instance/serverSettingsPage/serverSettingsTabs/authConfigurationTab/forms/ssoUsersForm/ssoUsersForm.jsx b/app/src/pages/instance/serverSettingsPage/serverSettingsTabs/authConfigurationTab/forms/ssoUsersForm/ssoUsersForm.jsx index c3d88defb1..8af2e6e283 100644 --- a/app/src/pages/instance/serverSettingsPage/serverSettingsTabs/authConfigurationTab/forms/ssoUsersForm/ssoUsersForm.jsx +++ b/app/src/pages/instance/serverSettingsPage/serverSettingsTabs/authConfigurationTab/forms/ssoUsersForm/ssoUsersForm.jsx @@ -15,15 +15,18 @@ */ import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; +import { useSelector, useDispatch } from 'react-redux'; import { defineMessages, useIntl } from 'react-intl'; import classNames from 'classnames/bind'; -import { connect } from 'react-redux'; +import { useTracking } from 'react-tracking'; import { InputBigSwitcher } from 'components/inputs/inputBigSwitcher'; import { SectionHeader } from 'components/main/sectionHeader'; import { ADMIN_SERVER_SETTINGS_PAGE_EVENTS } from 'components/main/analytics/events'; import { ssoUsersOnlySelector, fetchAppInfoAction } from 'controllers/appInfo'; import formStyles from 'pages/instance/serverSettingsPage/common/formController/formController.scss'; +import { showSuccessNotification, showErrorNotification } from 'controllers/notification'; +import { fetch } from 'common/utils/fetch'; +import { URLS } from 'common/urls'; import styles from './ssoUsersForm.scss'; const formCx = classNames.bind(formStyles); @@ -47,16 +50,27 @@ const messages = defineMessages({ defaultMessage: 'Users can manually send invitations for other users. If enabled new users can be created via SSO only.', }, + successNotification: { + id: 'SsoUsersForm.successNotification', + defaultMessage: 'SSO settings have been updated successfully', + }, + errorNotification: { + id: 'SsoUsersForm.errorNotification', + defaultMessage: 'Failed to update SSO settings', + }, }); -const SsoUsersFormComponent = ({ enabled: enabledFromStore, fetchAppInfo }) => { +export const SsoUsersForm = () => { const { formatMessage } = useIntl(); + const dispatch = useDispatch(); + const enabledFromStore = useSelector(ssoUsersOnlySelector); const [enabled, setEnabled] = useState(enabledFromStore); const inputId = 'ssoUsersToggle'; + const { trackEvent } = useTracking(); useEffect(() => { - fetchAppInfo(); - }, [fetchAppInfo]); + dispatch(fetchAppInfoAction()); + }, [dispatch]); useEffect(() => { setEnabled(enabledFromStore); @@ -65,8 +79,25 @@ const SsoUsersFormComponent = ({ enabled: enabledFromStore, fetchAppInfo }) => { const getDescription = () => formatMessage(enabled ? messages.ssoOnlyDescription : messages.manualInvitesDescription); - const handleToggle = (value) => { + const handleToggle = async (value) => { setEnabled(value); + trackEvent(ADMIN_SERVER_SETTINGS_PAGE_EVENTS.toggleSsoUsers(value)); + + try { + await fetch(URLS.instanceSettings(), { + method: 'PUT', + data: { + key: 'server.users.sso', + value: value.toString(), + }, + }); + + await dispatch(fetchAppInfoAction()); + dispatch(showSuccessNotification({ message: formatMessage(messages.successNotification) })); + } catch (error) { + setEnabled(!value); + dispatch(showErrorNotification({ message: formatMessage(messages.errorNotification) })); + } }; return ( @@ -86,7 +117,6 @@ const SsoUsersFormComponent = ({ enabled: enabledFromStore, fetchAppInfo }) => { value={enabled} onChange={handleToggle} mobileDisabled - onChangeEventInfo={ADMIN_SERVER_SETTINGS_PAGE_EVENTS.SSO_USERS_SWITCHER} />
{getDescription()} @@ -98,22 +128,3 @@ const SsoUsersFormComponent = ({ enabled: enabledFromStore, fetchAppInfo }) => {
); }; - -SsoUsersFormComponent.propTypes = { - enabled: PropTypes.bool, - fetchAppInfo: PropTypes.func.isRequired, -}; - -SsoUsersFormComponent.defaultProps = { - enabled: false, -}; - -const mapStateToProps = (state) => ({ - enabled: ssoUsersOnlySelector(state), -}); - -const mapDispatchToProps = { - fetchAppInfo: fetchAppInfoAction, -}; - -export const SsoUsersForm = connect(mapStateToProps, mapDispatchToProps)(SsoUsersFormComponent);