diff --git a/app/localization/translated/be.json b/app/localization/translated/be.json index 2c02b42be6..7522258c86 100644 --- a/app/localization/translated/be.json +++ b/app/localization/translated/be.json @@ -1553,6 +1553,17 @@ "OrganizationsControl.allOrganizations": "Усе арганізацыі", "OrganizationsControl.organization": "Арганізацыя", "OrganizationsItem.open": "адкрыць", + "OrganizationsPage.title": "Усе арганізацыі", + "OrganizationsPage.description": "Спіс даступных вам арганізацый у дадзены момант пусты. Калі ласка, звяжыцеся са сваім адміністратарам, каб атрымаць прызначэнне ва ўжо існуючую арганізацыю.", + "OrganizationsPage.createOrganization": "Стварыць арганізацыю", + "OrganizationsPage.createNewOrganization": "Стварыце новую арганізацыю, каб пачаць свой шлях на партале справаздач", + "OrganizationsPage.noOrganizationsYet": "Пакуль няма арганізацый", + "OrganizationsPage.noOrganizationsAvailableYet": "Пакуль няма даступных арганізацый", + "OrganizationsPage.synchedOrganization": "Сінхранізаваная арганізацыя", + "OrganizationsPage.lastLaunch": "Апошні запуск быў ажыццёўлены больш 3 месяцаў таму", + "OrganizationsPage.organizationUsers": "Карыстальнікі арганізацыі", + "OrganizationsPage.organizationProjects": "Арганізацыйныя праекты", + "OrganizationsPage.latestLaunch": "Апошняе выкананне запуску", "OrganizationsPopover.allOrganizations": "Усе арганізацыі", "OrganizationsControl.assignmentsList": "Прызначаны спіс", "OverallStatisticsControls.ContentFieldsValidationError": "Абярыце па меншай меры адзін элемент", diff --git a/app/localization/translated/es.json b/app/localization/translated/es.json index 5bae1f2199..7b4cfb5216 100644 --- a/app/localization/translated/es.json +++ b/app/localization/translated/es.json @@ -1544,6 +1544,17 @@ "OrganizationsControl.allOrganizations": "All organizations", "OrganizationsControl.organization": "Organization", "OrganizationsItem.open": "open", + "OrganizationsPage.title": "All Organizations", + "OrganizationsPage.description": "The list of organizations available to you is currently empty. Please contact your Administrator to be assigned to an existing one.", + "OrganizationsPage.createOrganization": "Create Organization", + "OrganizationsPage.createNewOrganization": "Create a new organization to begin your ReportPortal journey", + "OrganizationsPage.noOrganizationsYet": "No organizations yet", + "OrganizationsPage.noOrganizationsAvailableYet": "No organizations available yet", + "OrganizationsPage.synchedOrganization": "Synched organization", + "OrganizationsPage.lastLaunch": "The last launch was executed more than 3 months ago", + "OrganizationsPage.organizationUsers": "Organization users", + "OrganizationsPage.organizationProjects": "Organization projects", + "OrganizationsPage.latestLaunch": "The latest launch execution", "OrganizationsPopover.allOrganizations": "All organizatoins", "OrganizationsControl.assignmentsList": "Assignments list", "OverallStatisticsControls.ContentFieldsValidationError": "Seleccione al menos un elemento", diff --git a/app/localization/translated/ru.json b/app/localization/translated/ru.json index 14d88011bb..e7d3459876 100644 --- a/app/localization/translated/ru.json +++ b/app/localization/translated/ru.json @@ -1553,6 +1553,17 @@ "OrganizationsControl.allOrganizations": "Все организации", "OrganizationsControl.organization": "Организация", "OrganizationsItem.open": "открыть", + "OrganizationsPage.title": "Все организации", + "OrganizationsPage.description": "Список доступных вам организаций в данный момент пуст. Пожалуйста, свяжитесь со своим администратором, чтобы получить назначение в уже существующую организацию.", + "OrganizationsPage.createOrganization": "Создать организацию", + "OrganizationsPage.createNewOrganization": "Создайте новую организацию, чтобы начать свой путь на портале отчетов", + "OrganizationsPage.noOrganizationsYet": "Пока нет организаций", + "OrganizationsPage.noOrganizationsAvailableYet": "Пока нет доступных организаций", + "OrganizationsPage.synchedOrganization": "Синхронизированная организация", + "OrganizationsPage.lastLaunch": "Последний запуск был осуществлен более 3 месяцев назад", + "OrganizationsPage.organizationUsers": "Пользователи организации", + "OrganizationsPage.organizationProjects": "Организационные проекты", + "OrganizationsPage.latestLaunch": "Последнее выполнение запуска", "OrganizationsPopover.allOrganizations": "Все организации", "OrganizationsControl.assignmentsList": "Назначенный список", "OverallStatisticsControls.ContentFieldsValidationError": "Выберите не меньше одного элемента", diff --git a/app/localization/translated/uk.json b/app/localization/translated/uk.json index 85f9be9e75..91b20b2c30 100644 --- a/app/localization/translated/uk.json +++ b/app/localization/translated/uk.json @@ -1553,6 +1553,17 @@ "OrganizationsControl.allOrganizations": "Всі організації", "OrganizationsControl.organization": "Організація", "OrganizationsItem.open": "відкрити", + "OrganizationsPage.title": "Всі організації", + "OrganizationsPage.description": "Список доступних вам організацій в даний момент порожній. Будь ласка, зв'яжіться зі своїм адміністратором, щоб отримати призначення в уже існуючу організацію.", + "OrganizationsPage.createOrganization": "Створити організацію", + "OrganizationsPage.createNewOrganization": "Створіть нову організацію, щоб розпочати свій шлях на порталі звітів", + "OrganizationsPage.noOrganizationsYet": "Поки немає організацій", + "OrganizationsPage.noOrganizationsAvailableYet": "Поки що немає доступних організацій", + "OrganizationsPage.synchedOrganization": "Синхронізована організація", + "OrganizationsPage.lastLaunch": "Останній запуск був здійснений більше 3 місяців тому", + "OrganizationsPage.organizationUsers": "Користувачі організації", + "OrganizationsPage.organizationProjects": "Організаційні проекти", + "OrganizationsPage.latestLaunch": "Останнє виконання запуску", "OrganizationsPopover.allOrganizations": "Всі організації", "OrganizationsControl.assignmentsList": "Призначений список", "OverallStatisticsControls.ContentFieldsValidationError": "Виберіть не менше одного елемента", diff --git a/app/localization/translated/zh.json b/app/localization/translated/zh.json index 03ac388c6b..d9d06d2171 100644 --- a/app/localization/translated/zh.json +++ b/app/localization/translated/zh.json @@ -1553,6 +1553,17 @@ "OrganizationsControl.allOrganizations": "All organizations", "OrganizationsControl.organization": "组织", "OrganizationsItem.open": "open", + "OrganizationsPage.title": "All Organizations", + "OrganizationsPage.description": "The list of organizations available to you is currently empty. Please contact your Administrator to be assigned to an existing one.", + "OrganizationsPage.createOrganization": "Create Organization", + "OrganizationsPage.createNewOrganization": "Create a new organization to begin your ReportPortal journey", + "OrganizationsPage.noOrganizationsYet": "No organizations yet", + "OrganizationsPage.noOrganizationsAvailableYet": "No organizations available yet", + "OrganizationsPage.synchedOrganization": "Synched organization", + "OrganizationsPage.lastLaunch": "The last launch was executed more than 3 months ago", + "OrganizationsPage.organizationUsers": "Organization users", + "OrganizationsPage.organizationProjects": "Organization projects", + "OrganizationsPage.latestLaunch": "The latest launch execution", "OrganizationsPopover.allOrganizations": "All organizations", "OrganizationsControl.assignmentsList": "Assignments list", "OverallStatisticsControls.ContentFieldsValidationError": "您必须选中至少一个测试项", diff --git a/app/package-lock.json b/app/package-lock.json index 1f6708ae6c..92c6e53539 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,7 +12,7 @@ "@formatjs/intl-pluralrules": "1.3.9", "@formatjs/intl-relativetimeformat": "4.5.1", "@formatjs/intl-utils": "1.6.0", - "@reportportal/ui-kit": "^0.0.1-alpha.28", + "@reportportal/ui-kit": "^0.0.1-alpha.29", "axios": "1.6.4", "c3": "0.7.20", "chart.js": "2.9.4", @@ -24,7 +24,7 @@ "d3": "7.8.5", "d3-selection": "1.4.0", "date-fns": "2.29.3", - "dompurify": "^2.5.4", + "dompurify": "2.5.4", "dotenv": "5.0.1", "downshift": "6.1.7", "fast-deep-equal": "3.1.3", @@ -78,7 +78,7 @@ "reset-css": "2.2.1", "semver-diff": "3.1.1", "simplemde": "1.11.2", - "swagger-ui-react": "^5.17.12", + "swagger-ui-react": "5.17.12", "video.js": "7.17.0" }, "devDependencies": { @@ -3465,9 +3465,9 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "node_modules/@reportportal/ui-kit": { - "version": "0.0.1-alpha.28", - "resolved": "https://registry.npmjs.org/@reportportal/ui-kit/-/ui-kit-0.0.1-alpha.28.tgz", - "integrity": "sha512-NWt0kJt87waqZwfKKKaenxbUlOipFFbal+aj2mAe2u3tHytvy3dlcU/vcnvcWL4OH3LwybnjF9vxuMYhpOWF5A==", + "version": "0.0.1-alpha.29", + "resolved": "https://registry.npmjs.org/@reportportal/ui-kit/-/ui-kit-0.0.1-alpha.29.tgz", + "integrity": "sha512-fAy8zZd7Hsrdh5AS+ptAdjxXV/Qj4OyHUKWL1Veo0UwYsz3LkSP1Vb+qXmDksPW/FEPrHrvYwc1UjnxSRU+uAg==", "dependencies": { "@floating-ui/react": "^0.26.16", "@floating-ui/react-dom": "^2.0.1", @@ -4418,7 +4418,7 @@ "version": "22.0.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz", "integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==", - "devOptional": true, + "dev": true, "dependencies": { "undici-types": "~6.11.1" } @@ -26385,7 +26385,7 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", - "devOptional": true + "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", diff --git a/app/package.json b/app/package.json index 471c9c3ad2..0634581202 100644 --- a/app/package.json +++ b/app/package.json @@ -25,7 +25,7 @@ "@formatjs/intl-pluralrules": "1.3.9", "@formatjs/intl-relativetimeformat": "4.5.1", "@formatjs/intl-utils": "1.6.0", - "@reportportal/ui-kit": "^0.0.1-alpha.28", + "@reportportal/ui-kit": "^0.0.1-alpha.29", "axios": "1.6.4", "c3": "0.7.20", "chart.js": "2.9.4", diff --git a/app/src/pages/instance/projectsPage/projectsGrid/index.js b/app/src/common/constants/organizationTypes.js similarity index 86% rename from app/src/pages/instance/projectsPage/projectsGrid/index.js rename to app/src/common/constants/organizationTypes.js index 8d0e58776f..09a2cc0e27 100644 --- a/app/src/pages/instance/projectsPage/projectsGrid/index.js +++ b/app/src/common/constants/organizationTypes.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 { ProjectsGrid } from './projectsGrid'; +export const ORGANIZATION_EXTERNAL_TYPE = 'EXTERNAL'; diff --git a/app/src/common/utils/permissions/index.js b/app/src/common/utils/permissions/index.js index 6feda1c009..fde6863d48 100644 --- a/app/src/common/utils/permissions/index.js +++ b/app/src/common/utils/permissions/index.js @@ -41,5 +41,6 @@ export { canWorkWithTests, canSeeEmailMembers, canSeeSidebarOptions, + canCreateOrganization, } from './permissions'; export { getRoleTitle } from './getRoleTitle'; diff --git a/app/src/controllers/appInfo/selectors.js b/app/src/controllers/appInfo/selectors.js index 71496cd3ff..919745c2b5 100644 --- a/app/src/controllers/appInfo/selectors.js +++ b/app/src/controllers/appInfo/selectors.js @@ -22,7 +22,7 @@ import { patternAnalysisEnabledSelector, projectInfoIdSelector, } from 'controllers/project/selectors'; -import { activeOrganizationIdSelector } from 'controllers/organizations/organization/selectors'; +import { activeOrganizationIdSelector } from 'controllers/organization/selectors'; import { ANALYTICS_INSTANCE_KEY, ANALYTICS_ALL_KEY, diff --git a/app/src/controllers/instance/events/reducer.js b/app/src/controllers/instance/events/reducer.js index f8ca68958b..28bb556fa9 100644 --- a/app/src/controllers/instance/events/reducer.js +++ b/app/src/controllers/instance/events/reducer.js @@ -15,17 +15,13 @@ */ import { combineReducers } from 'redux'; -import { createPageScopedReducer } from 'common/utils/createPageScopedReducer'; import { fetchReducer } from 'controllers/fetch'; import { loadingReducer } from 'controllers/loading'; -import { PROJECT_DETAILS_PAGE } from 'controllers/pages'; import { alternativePaginationReducer } from 'controllers/pagination'; import { initialPaginationState, NAMESPACE } from './constants'; -const reducer = combineReducers({ +export const eventsReducer = combineReducers({ events: fetchReducer(NAMESPACE, { contentPath: 'items' }), pagination: alternativePaginationReducer(NAMESPACE, initialPaginationState), loading: loadingReducer(NAMESPACE), }); - -export const eventsReducer = createPageScopedReducer(reducer, PROJECT_DETAILS_PAGE); diff --git a/app/src/controllers/organizations/actionCreators.js b/app/src/controllers/instance/organizations/actionCreators.js similarity index 100% rename from app/src/controllers/organizations/actionCreators.js rename to app/src/controllers/instance/organizations/actionCreators.js diff --git a/app/src/pages/instance/projectsPage/index.js b/app/src/controllers/instance/organizations/constants.js similarity index 65% rename from app/src/pages/instance/projectsPage/index.js rename to app/src/controllers/instance/organizations/constants.js index 1017bea8ee..ee0ccc1ba2 100644 --- a/app/src/pages/instance/projectsPage/index.js +++ b/app/src/controllers/instance/organizations/constants.js @@ -14,5 +14,13 @@ * limitations under the License. */ -export { AddProjectModal } from 'pages/organization/organizationProjectsPage/modals/addProjectModal'; -export { ProjectsPage } from './projectsPage'; +export const NAMESPACE = 'organizations'; + +export const FETCH_ORGANIZATIONS = 'fetchOrganizations'; +export const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100]; +export const DEFAULT_LIMITATION = 20; +export const initialPaginationState = { + size: DEFAULT_LIMITATION, + totalElements: 0, + totalPages: 0, +}; diff --git a/app/src/controllers/organizations/index.js b/app/src/controllers/instance/organizations/index.js similarity index 85% rename from app/src/controllers/organizations/index.js rename to app/src/controllers/instance/organizations/index.js index 30db3f8dda..c28ec3d2ef 100644 --- a/app/src/controllers/organizations/index.js +++ b/app/src/controllers/instance/organizations/index.js @@ -17,5 +17,9 @@ export { FETCH_ORGANIZATIONS } from './constants'; export { fetchOrganizationsAction } from './actionCreators'; export { organizationsReducer } from './reducer'; -export { organizationsListSelector, organizationsListLoadingSelector } from './selectors'; +export { + organizationsListSelector, + organizationsListLoadingSelector, + organizationsListPaginationSelector, +} from './selectors'; export { organizationsSagas } from './sagas'; diff --git a/app/src/controllers/organizations/reducer.js b/app/src/controllers/instance/organizations/reducer.js similarity index 76% rename from app/src/controllers/organizations/reducer.js rename to app/src/controllers/instance/organizations/reducer.js index 71c8f9be46..d04e3e9eaf 100644 --- a/app/src/controllers/organizations/reducer.js +++ b/app/src/controllers/instance/organizations/reducer.js @@ -17,11 +17,13 @@ import { combineReducers } from 'redux'; import { fetchReducer } from 'controllers/fetch'; import { loadingReducer } from 'controllers/loading'; -import { organizationReducer } from 'controllers/organizations/organization/reducer'; -import { NAMESPACE } from './constants'; +import { alternativePaginationReducer } from 'controllers/pagination'; +import { organizationReducer } from 'controllers/organization/reducer'; +import { initialPaginationState, NAMESPACE } from './constants'; export const organizationsReducer = combineReducers({ list: fetchReducer(NAMESPACE, { contentPath: 'items' }), listLoading: loadingReducer(NAMESPACE), + pagination: alternativePaginationReducer(NAMESPACE, initialPaginationState), organization: organizationReducer, }); diff --git a/app/src/controllers/organizations/sagas.js b/app/src/controllers/instance/organizations/sagas.js similarity index 90% rename from app/src/controllers/organizations/sagas.js rename to app/src/controllers/instance/organizations/sagas.js index 12cc042350..c179fb1676 100644 --- a/app/src/controllers/organizations/sagas.js +++ b/app/src/controllers/instance/organizations/sagas.js @@ -18,9 +18,9 @@ import { takeEvery, all, put } from 'redux-saga/effects'; import { URLS } from 'common/urls'; import { showDefaultErrorNotification } from 'controllers/notification'; import { fetchDataAction } from 'controllers/fetch'; -import { organizationSagas } from 'controllers/organizations/organization/sagas'; +import { organizationSagas } from 'controllers/organization'; +import { projectsSagas } from 'controllers/organization/projects'; import { FETCH_ORGANIZATIONS, NAMESPACE } from './constants'; -import { projectsSagas } from './projects'; function* fetchOrganizations() { try { diff --git a/app/src/controllers/organizations/selectors.js b/app/src/controllers/instance/organizations/selectors.js similarity index 89% rename from app/src/controllers/organizations/selectors.js rename to app/src/controllers/instance/organizations/selectors.js index 5ea88f415d..b3a76a75a5 100644 --- a/app/src/controllers/organizations/selectors.js +++ b/app/src/controllers/instance/organizations/selectors.js @@ -19,3 +19,6 @@ export const organizationsSelector = (state) => state.organizations || {}; export const organizationsListSelector = (state) => organizationsSelector(state).list || []; export const organizationsListLoadingSelector = (state) => organizationsSelector(state).listLoading; + +export const organizationsListPaginationSelector = (state) => + organizationsSelector(state).pagination; diff --git a/app/src/controllers/instance/projects/actionCreators.js b/app/src/controllers/instance/projects/actionCreators.js deleted file mode 100644 index 26ad63b675..0000000000 --- a/app/src/controllers/instance/projects/actionCreators.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 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 { - toggleItemSelectionAction, - toggleAllItemsAction, - defineGroupOperation, - unselectAllItemsAction, -} from 'controllers/groupOperations'; -import { showModalAction } from 'controllers/modal'; -import { PROJECT_DETAILS_PAGE, PROJECT_SETTINGS_PAGE } from 'controllers/pages'; -import { - FETCH_PROJECTS, - START_SET_VIEW_MODE, - NAMESPACE, - DELETE_PROJECT, - NAVIGATE_TO_PROJECT, - CONFIRM_ASSIGN_TO_PROJECT, - ADD_PROJECT, -} from './constants'; - -export const fetchProjectsAction = (params) => ({ - type: FETCH_PROJECTS, - payload: params, -}); - -export const startSetViewMode = (viewMode) => ({ - type: START_SET_VIEW_MODE, - payload: { viewMode }, -}); - -export const toggleProjectSelectionAction = toggleItemSelectionAction(NAMESPACE); -export const toggleAllProjectsAction = toggleAllItemsAction(NAMESPACE); -export const unselectAllProjectsAction = unselectAllItemsAction(NAMESPACE); - -export const deleteItemsAction = defineGroupOperation( - NAMESPACE, - 'deleteProjects', - (items, { onConfirm, header, mainContent, userId, warning, eventsInfo }) => - showModalAction({ - id: 'deleteItemsModal', - data: { items, onConfirm, header, mainContent, userId, warning, eventsInfo }, - }), - () => {}, -); - -export const addProjectAction = (projectKey) => ({ - type: ADD_PROJECT, - payload: projectKey, -}); - -export const deleteProjectAction = (project) => ({ - type: DELETE_PROJECT, - payload: project, -}); - -export const navigateToProjectAction = (project) => ({ - type: NAVIGATE_TO_PROJECT, - payload: project, -}); - -export const confirmAssignToProject = (project) => ({ - type: CONFIRM_ASSIGN_TO_PROJECT, - payload: project, -}); - -export const navigateToProjectSectionAction = ({ organizationSlug, projectSlug }, section) => ({ - type: PROJECT_DETAILS_PAGE, - payload: { - projectSlug, - projectSection: section, - organizationSlug, - }, -}); - -export const navigateToProjectSettingsAction = ({ organizationSlug, projectSlug }) => ({ - type: PROJECT_SETTINGS_PAGE, - payload: { - projectSlug, - organizationSlug, - }, -}); diff --git a/app/src/controllers/instance/projects/constants.js b/app/src/controllers/instance/projects/constants.js deleted file mode 100644 index 3c06d38596..0000000000 --- a/app/src/controllers/instance/projects/constants.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2019 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 { PAGE_KEY, SIZE_KEY } from 'controllers/pagination'; -import { formatSortingString, SORTING_ASC } from 'controllers/sorting'; - -export const FETCH_PROJECTS = 'fetchProjects'; -export const NAMESPACE = 'projects'; -export const DEFAULT_PAGE_SIZE = 12; -export const DEFAULT_PAGINATION = { - [PAGE_KEY]: 1, - [SIZE_KEY]: DEFAULT_PAGE_SIZE, -}; -export const TABLE_VIEW = 'table'; -export const GRID_VIEW = 'grid'; -export const USER_VIEW = 'projects_view_mode'; -export const SET_PROJECTS_VIEW_MODE = 'setProjectsViewMode'; -export const START_SET_VIEW_MODE = 'startSetProjectsViewMode'; -export const ADD_PROJECT = 'addProject'; -export const DELETE_PROJECT = 'deleteProject'; -export const DEFAULT_SORT_COLUMN = 'name'; -export const DEFAULT_SORTING = formatSortingString([DEFAULT_SORT_COLUMN], SORTING_ASC); -export const NAVIGATE_TO_PROJECT = 'navigateToProject'; -export const CONFIRM_ASSIGN_TO_PROJECT = 'confirmAssignToProject'; -export const ERROR_CODES = { - PROJECT_EXISTS: 4095, -}; diff --git a/app/src/controllers/instance/projects/index.js b/app/src/controllers/instance/projects/index.js deleted file mode 100644 index 91026173d8..0000000000 --- a/app/src/controllers/instance/projects/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 { - fetchProjectsAction, - startSetViewMode, - toggleProjectSelectionAction, - toggleAllProjectsAction, - unselectAllProjectsAction, - addProjectAction, - deleteItemsAction, - deleteProjectAction, - navigateToProjectAction, - navigateToProjectSectionAction, -} from './actionCreators'; -export { projectsReducer } from './reducer'; -export { - projectsPaginationSelector, - projectsSelector, - loadingSelector, - viewModeSelector, - selectedProjectsSelector, - querySelector, -} from './selectors'; -export { projectsSagas } from './sagas'; -export { - DEFAULT_PAGE_SIZE, - DEFAULT_PAGINATION, - GRID_VIEW, - TABLE_VIEW, - DEFAULT_SORT_COLUMN, -} from './constants'; diff --git a/app/src/controllers/instance/projects/reducer.js b/app/src/controllers/instance/projects/reducer.js deleted file mode 100644 index 0bec777db6..0000000000 --- a/app/src/controllers/instance/projects/reducer.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 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 { combineReducers } from 'redux'; -import { fetchReducer } from 'controllers/fetch'; -import { paginationReducer } from 'controllers/pagination'; -import { loadingReducer } from 'controllers/loading'; -import { PROJECTS_PAGE } from 'controllers/pages'; -import { groupOperationsReducer } from 'controllers/groupOperations'; -import { ASSIGN_TO_PROJECT_SUCCESS, UNASSIGN_FROM_PROJECT_SUCCESS } from 'controllers/user'; -import { queueReducers } from 'common/utils/queueReducers'; -import { createPageScopedReducer } from 'common/utils/createPageScopedReducer'; -import { NAMESPACE, SET_PROJECTS_VIEW_MODE, GRID_VIEW } from './constants'; - -export const setViewModeReducer = (state = GRID_VIEW, { type = '', payload = {} }) => { - switch (type) { - case SET_PROJECTS_VIEW_MODE: - return payload; - - default: - return state; - } -}; - -export const projectFetchReducer = fetchReducer(NAMESPACE, { - contentPath: 'content', - initialState: [], -}); - -export const assignProjectReducer = (state = [], { type = '', payload = {} }) => { - switch (type) { - case ASSIGN_TO_PROJECT_SUCCESS: - return state.map((project) => - project.projectKey === payload.projectKey - ? { ...project, usersQuantity: project.usersQuantity + 1 } - : project, - ); - case UNASSIGN_FROM_PROJECT_SUCCESS: { - const { projectKey } = payload; - return state.map((project) => - project.projectKey === projectKey - ? { ...project, usersQuantity: project.usersQuantity - 1 } - : project, - ); - } - default: - return state; - } -}; - -const reducer = combineReducers({ - projects: queueReducers(projectFetchReducer, assignProjectReducer), - pagination: paginationReducer(NAMESPACE), - loading: loadingReducer(NAMESPACE), - viewMode: setViewModeReducer, - groupOperations: groupOperationsReducer(NAMESPACE), -}); - -export const projectsReducer = createPageScopedReducer(reducer, PROJECTS_PAGE); diff --git a/app/src/controllers/instance/projects/reducer.test.js b/app/src/controllers/instance/projects/reducer.test.js deleted file mode 100644 index c24be45e84..0000000000 --- a/app/src/controllers/instance/projects/reducer.test.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2019 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 { ASSIGN_TO_PROJECT_SUCCESS, UNASSIGN_FROM_PROJECT_SUCCESS } from 'controllers/user'; -import { FETCH_SUCCESS } from 'controllers/fetch'; -import { SET_PROJECTS_VIEW_MODE, TABLE_VIEW, GRID_VIEW, NAMESPACE } from './constants'; -import { setViewModeReducer, assignProjectReducer, projectFetchReducer } from './reducer'; - -const VIEW_MODE_INITIAL_STATE = GRID_VIEW; -const PROJECTS = [ - { - id: 1, - projectName: 'superadmin_personal', - usersQuantity: 3, - launchesQuantity: 36, - launchesPerUser: null, - uniqueTickets: null, - launchesPerWeek: null, - lastRun: 1553585182771, - creationDate: 1553072627019, - entryType: 'PERSONAL', - organization: null, - }, -]; -const PROJECTS_AFTER_ASSIGN = [ - { - id: 1, - projectName: 'superadmin_personal', - usersQuantity: 4, - launchesQuantity: 36, - launchesPerUser: null, - uniqueTickets: null, - launchesPerWeek: null, - lastRun: 1553585182771, - creationDate: 1553072627019, - entryType: 'PERSONAL', - organization: null, - }, -]; - -const PROJECTS_AFTER_UNASSIGN = [ - { - id: 1, - projectName: 'superadmin_personal', - usersQuantity: 2, - launchesQuantity: 36, - launchesPerUser: null, - uniqueTickets: null, - launchesPerWeek: null, - lastRun: 1553585182771, - creationDate: 1553072627019, - entryType: 'PERSONAL', - organization: null, - }, -]; - -describe('projects reducer', () => { - describe('setViewModeReducer', () => { - test('should return initial state', () => { - expect(setViewModeReducer(undefined, {})).toBe(VIEW_MODE_INITIAL_STATE); - }); - - test('should return old state on unknown action', () => { - const oldState = TABLE_VIEW; - expect(setViewModeReducer(oldState, { type: 'foo' })).toBe(oldState); - }); - - test('should handle SET_PROJECTS_VIEW_MODE', () => { - const payload = TABLE_VIEW; - const newState = setViewModeReducer(VIEW_MODE_INITIAL_STATE, { - type: SET_PROJECTS_VIEW_MODE, - payload, - }); - expect(newState).toEqual(payload); - }); - }); - - describe('projectFetchReducer', () => { - test('should return initial state', () => { - expect(projectFetchReducer(undefined, {})).toEqual([]); - }); - - test('should return old state on unknown action', () => { - const oldState = PROJECTS; - expect(projectFetchReducer(oldState, {})).toBe(oldState); - }); - - test('should ignore unsuitable namespaces', () => { - const payload = PROJECTS; - const newState = projectFetchReducer(undefined, { - type: FETCH_SUCCESS, - payload, - meta: { - namespace: 'other', - }, - }); - expect(newState).toEqual([]); - }); - - test('should handle FETCH_SUCCESS', () => { - const payload = { content: PROJECTS }; - const newState = projectFetchReducer(undefined, { - type: FETCH_SUCCESS, - payload, - meta: { - namespace: NAMESPACE, - }, - }); - expect(newState).toEqual(PROJECTS); - }); - }); - - describe('assignProjectReducer', () => { - test('should return initial state', () => { - expect(assignProjectReducer(undefined, {})).toEqual([]); - }); - - test('should return old state on unknown action', () => { - const oldState = PROJECTS; - expect(assignProjectReducer(oldState, [{ id: 2 }])).toBe(oldState); - }); - - test('should handle ASSIGN_TO_PROJECT_SUCCESS', () => { - const payload = { - projectName: PROJECTS[0].projectName, - }; - const newState = assignProjectReducer(PROJECTS, { - type: ASSIGN_TO_PROJECT_SUCCESS, - payload, - }); - expect(newState).toEqual(PROJECTS_AFTER_ASSIGN); - }); - - test('should handle UNASSIGN_FROM_PROJECT_SUCCESS', () => { - const oldState = PROJECTS; - const payload = { - projectName: PROJECTS[0].projectName, - }; - const newState = assignProjectReducer(oldState, { - type: UNASSIGN_FROM_PROJECT_SUCCESS, - payload, - }); - expect(newState).toEqual(PROJECTS_AFTER_UNASSIGN); - }); - }); -}); diff --git a/app/src/controllers/instance/projects/sagas.js b/app/src/controllers/instance/projects/sagas.js deleted file mode 100644 index 27c97ee479..0000000000 --- a/app/src/controllers/instance/projects/sagas.js +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019 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 { takeEvery, takeLatest, all, put, select, call } from 'redux-saga/effects'; -import { fetchDataAction } from 'controllers/fetch'; -import { URLS } from 'common/urls'; -import { showNotification, NOTIFICATION_TYPES } from 'controllers/notification'; -import { assignToProjectSuccessAction } from 'controllers/user'; -import { PROJECT_TYPE_INTERNAL } from 'common/constants/projectsObjectTypes'; -import { MEMBERS } from 'common/constants/projectSections'; -import { fetch, getStorageItem, setStorageItem } from 'common/utils'; -import { PROJECT_PAGE, urlOrganizationAndProjectSelector } from 'controllers/pages'; -import { hideModalAction } from 'controllers/modal'; -import { PROJECT_MANAGER } from 'common/constants/projectRoles'; - -import { - NAMESPACE, - FETCH_PROJECTS, - START_SET_VIEW_MODE, - USER_VIEW, - GRID_VIEW, - SET_PROJECTS_VIEW_MODE, - ADD_PROJECT, - DELETE_PROJECT, - NAVIGATE_TO_PROJECT, - ERROR_CODES, -} from './constants'; -import { navigateToProjectSectionAction } from './actionCreators'; -import { querySelector } from './selectors'; - -function* fetchProjects() { - const query = yield select(querySelector); - yield put( - fetchDataAction(NAMESPACE)(URLS.projects(), { - params: { ...query }, - }), - ); -} - -function* watchFetchProjects() { - yield takeEvery(FETCH_PROJECTS, fetchProjects); -} - -function* setViewMode(action) { - const userView = getStorageItem(USER_VIEW) || GRID_VIEW; - const viewMode = action.payload.viewMode || userView; - - setStorageItem(USER_VIEW, viewMode); - - yield put({ type: SET_PROJECTS_VIEW_MODE, payload: viewMode }); -} -function* watchSetViewMode() { - yield takeEvery(START_SET_VIEW_MODE, setViewMode); -} - -function* addProject({ payload: projectName }) { - try { - yield call(fetch, URLS.project(), { - method: 'post', - data: { - entryType: PROJECT_TYPE_INTERNAL, - projectName, - }, - }); - const projectInfo = { - projectName: projectName.toLowerCase(), - projectRole: PROJECT_MANAGER, - entryType: PROJECT_TYPE_INTERNAL, - }; - const { organizationSlug, projectSlug } = yield select(urlOrganizationAndProjectSelector); - yield put(assignToProjectSuccessAction(projectInfo)); - yield put(hideModalAction()); - yield put( - showNotification({ - messageId: 'addProjectSuccess', - type: NOTIFICATION_TYPES.SUCCESS, - values: { name: projectName }, - }), - ); - yield put(navigateToProjectSectionAction({ organizationSlug, projectSlug }, MEMBERS)); - } catch (err) { - if (err.errorCode === ERROR_CODES.PROJECT_EXISTS) { - yield put( - showNotification({ - messageId: 'projectExists', - type: NOTIFICATION_TYPES.ERROR, - values: { name: projectName }, - }), - ); - } else { - yield put( - showNotification({ - messageId: 'failureDefault', - type: NOTIFICATION_TYPES.ERROR, - values: { error: err.message }, - }), - ); - } - } -} - -function* watchAddProject() { - yield takeEvery(ADD_PROJECT, addProject); -} - -function* deleteProject({ payload: project }) { - try { - yield call(fetch, URLS.project([project.id]), { - method: 'delete', - }); - } catch (err) { - const error = err.message; - yield put( - showNotification({ - messageId: 'deleteError', - type: NOTIFICATION_TYPES.ERROR, - values: { error }, - }), - ); - } - yield put({ type: FETCH_PROJECTS }); -} - -function* watchDeleteProject() { - yield takeLatest(DELETE_PROJECT, deleteProject); -} - -function* navigateToProject({ payload }) { - const { project } = payload; - const { organizationSlug, projectSlug } = project; - - yield put({ - type: PROJECT_PAGE, - payload: { organizationSlug, projectSlug }, - }); -} - -function* watchNavigateToProject() { - yield takeEvery(NAVIGATE_TO_PROJECT, navigateToProject); -} - -export function* projectsSagas() { - yield all([ - watchFetchProjects(), - watchSetViewMode(), - watchAddProject(), - watchDeleteProject(), - watchNavigateToProject(), - ]); -} diff --git a/app/src/controllers/instance/projects/selectors.js b/app/src/controllers/instance/projects/selectors.js deleted file mode 100644 index 39ba83e962..0000000000 --- a/app/src/controllers/instance/projects/selectors.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2019 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 { createQueryParametersSelector } from 'controllers/pages'; -import { createSelectedItemsSelector } from 'controllers/groupOperations'; -import { DEFAULT_PAGINATION, DEFAULT_SORTING } from './constants'; -import { administrateDomainSelector } from '../selectors'; - -const domainSelector = (state) => administrateDomainSelector(state).projects || {}; - -export const projectsPaginationSelector = (state) => domainSelector(state).pagination; -export const projectsSelector = (state) => domainSelector(state).projects; -export const loadingSelector = (state) => domainSelector(state).loading || false; -export const viewModeSelector = (state) => domainSelector(state).viewMode; - -const groupOperationsSelector = (state) => domainSelector(state).groupOperations; -export const selectedProjectsSelector = createSelectedItemsSelector(groupOperationsSelector); - -export const querySelector = createQueryParametersSelector({ - defaultPagination: DEFAULT_PAGINATION, - defaultSorting: DEFAULT_SORTING, -}); diff --git a/app/src/controllers/instance/reducer.js b/app/src/controllers/instance/reducer.js index db09167b07..1cc5dac45f 100644 --- a/app/src/controllers/instance/reducer.js +++ b/app/src/controllers/instance/reducer.js @@ -17,10 +17,8 @@ import { combineReducers } from 'redux'; import { eventsReducer } from './events'; import { allUsersReducer } from './allUsers'; -import { projectsReducer } from './projects'; export const instanceReducer = combineReducers({ events: eventsReducer, allUsers: allUsersReducer, - projects: projectsReducer, }); diff --git a/app/src/controllers/instance/sagas.js b/app/src/controllers/instance/sagas.js index e8c1b64c31..e7ec416c19 100644 --- a/app/src/controllers/instance/sagas.js +++ b/app/src/controllers/instance/sagas.js @@ -20,7 +20,6 @@ import { projectSectionSelector } from 'controllers/pages'; import { projectKeySelector, fetchProjectAction } from 'controllers/project'; import { fetchMembersAction } from 'controllers/members'; import { FETCH_PROJECT_DATA } from './constants'; -import { projectsSagas } from './projects'; import { allUsersSagas } from './allUsers'; import { eventsSagas, fetchEventsAction } from './events'; @@ -42,5 +41,5 @@ function* watchFetchProjectData() { } export function* instanceSagas() { - yield all([eventsSagas(), watchFetchProjectData(), allUsersSagas(), projectsSagas()]); + yield all([eventsSagas(), watchFetchProjectData(), allUsersSagas()]); } diff --git a/app/src/controllers/members/reducer.js b/app/src/controllers/members/reducer.js index d2a7ee0a7e..29f4ea01ce 100644 --- a/app/src/controllers/members/reducer.js +++ b/app/src/controllers/members/reducer.js @@ -19,7 +19,7 @@ import { createPageScopedReducer } from 'common/utils/createPageScopedReducer'; import { fetchReducer } from 'controllers/fetch'; import { paginationReducer } from 'controllers/pagination'; import { loadingReducer } from 'controllers/loading'; -import { PROJECT_MEMBERS_PAGE, PROJECT_DETAILS_PAGE } from 'controllers/pages'; +import { PROJECT_MEMBERS_PAGE } from 'controllers/pages'; import { NAMESPACE } from './constants'; const reducer = combineReducers({ @@ -29,7 +29,4 @@ const reducer = combineReducers({ }); // we must clear the members state when the page has changed from PROJECT_DETAILS_PAGE, as it is used there -export const membersReducer = createPageScopedReducer(reducer, [ - PROJECT_MEMBERS_PAGE, - PROJECT_DETAILS_PAGE, -]); +export const membersReducer = createPageScopedReducer(reducer, [PROJECT_MEMBERS_PAGE]); diff --git a/app/src/controllers/organizations/organization/actionCreators.js b/app/src/controllers/organization/actionCreators.js similarity index 100% rename from app/src/controllers/organizations/organization/actionCreators.js rename to app/src/controllers/organization/actionCreators.js diff --git a/app/src/controllers/organizations/organization/constants.js b/app/src/controllers/organization/constants.js similarity index 100% rename from app/src/controllers/organizations/organization/constants.js rename to app/src/controllers/organization/constants.js diff --git a/app/src/controllers/organizations/organization/index.js b/app/src/controllers/organization/index.js similarity index 100% rename from app/src/controllers/organizations/organization/index.js rename to app/src/controllers/organization/index.js diff --git a/app/src/controllers/organizations/projects/actionCreators.js b/app/src/controllers/organization/projects/actionCreators.js similarity index 79% rename from app/src/controllers/organizations/projects/actionCreators.js rename to app/src/controllers/organization/projects/actionCreators.js index 586181e6f6..6da9ce1c6e 100644 --- a/app/src/controllers/organizations/projects/actionCreators.js +++ b/app/src/controllers/organization/projects/actionCreators.js @@ -14,7 +14,6 @@ * limitations under the License. */ -import { PROJECT_DETAILS_PAGE } from 'controllers/pages'; import { CREATE_PROJECT, FETCH_ORGANIZATION_PROJECTS, NAVIGATE_TO_PROJECT } from './constants'; export const fetchOrganizationProjectsAction = (params) => { @@ -33,12 +32,3 @@ export const navigateToProjectAction = (project) => ({ type: NAVIGATE_TO_PROJECT, payload: project, }); - -export const navigateToProjectSectionAction = ({ organizationSlug, projectSlug }, section) => ({ - type: PROJECT_DETAILS_PAGE, - payload: { - projectSlug, - projectSection: section, - organizationSlug, - }, -}); diff --git a/app/src/controllers/organizations/projects/constants.js b/app/src/controllers/organization/projects/constants.js similarity index 96% rename from app/src/controllers/organizations/projects/constants.js rename to app/src/controllers/organization/projects/constants.js index a0e18b037f..18a785927c 100644 --- a/app/src/controllers/organizations/projects/constants.js +++ b/app/src/controllers/organization/projects/constants.js @@ -50,3 +50,7 @@ export const initialPaginationState = { totalElements: 0, totalPages: 0, }; + +export const ERROR_CODES = { + PROJECT_EXISTS: 4095, +}; diff --git a/app/src/controllers/organizations/projects/index.js b/app/src/controllers/organization/projects/index.js similarity index 88% rename from app/src/controllers/organizations/projects/index.js rename to app/src/controllers/organization/projects/index.js index 7b1d0e1770..4d223dc072 100644 --- a/app/src/controllers/organizations/projects/index.js +++ b/app/src/controllers/organization/projects/index.js @@ -14,11 +14,7 @@ * limitations under the License. */ -export { - fetchOrganizationProjectsAction, - navigateToProjectAction, - navigateToProjectSectionAction, -} from './actionCreators'; +export { fetchOrganizationProjectsAction, navigateToProjectAction } from './actionCreators'; export { projectsReducer } from './reducer'; export { projectsPaginationSelector, diff --git a/app/src/controllers/organizations/projects/reducer.js b/app/src/controllers/organization/projects/reducer.js similarity index 100% rename from app/src/controllers/organizations/projects/reducer.js rename to app/src/controllers/organization/projects/reducer.js diff --git a/app/src/controllers/organizations/projects/sagas.js b/app/src/controllers/organization/projects/sagas.js similarity index 90% rename from app/src/controllers/organizations/projects/sagas.js rename to app/src/controllers/organization/projects/sagas.js index 99de0ef08d..33e45c09ed 100644 --- a/app/src/controllers/organizations/projects/sagas.js +++ b/app/src/controllers/organization/projects/sagas.js @@ -20,12 +20,11 @@ import { URLS } from 'common/urls'; import { fetch } from 'common/utils'; import { hideModalAction } from 'controllers/modal'; import { NOTIFICATION_TYPES, showNotification } from 'controllers/notification'; -import { ERROR_CODES } from 'controllers/instance/projects/constants'; -import { fetchOrganizationBySlugAction } from '../organization'; -import { activeOrganizationSelector } from '../organization/selectors'; +import { fetchOrganizationBySlugAction } from '..'; +import { activeOrganizationSelector } from '../selectors'; import { fetchOrganizationProjectsAction } from './actionCreators'; import { querySelector } from './selectors'; -import { CREATE_PROJECT, FETCH_ORGANIZATION_PROJECTS, NAMESPACE } from './constants'; +import { CREATE_PROJECT, ERROR_CODES, FETCH_ORGANIZATION_PROJECTS, NAMESPACE } from './constants'; function* fetchOrganizationProjects({ payload: organizationId }) { const query = yield select(querySelector); diff --git a/app/src/controllers/organizations/projects/selectors.js b/app/src/controllers/organization/projects/selectors.js similarity index 96% rename from app/src/controllers/organizations/projects/selectors.js rename to app/src/controllers/organization/projects/selectors.js index 183f0155c9..3f450eab23 100644 --- a/app/src/controllers/organizations/projects/selectors.js +++ b/app/src/controllers/organization/projects/selectors.js @@ -19,7 +19,7 @@ import { createQueryParametersSelector } from 'controllers/pages'; import { SORTING_ASC } from 'controllers/sorting'; import { getAlternativePaginationAndSortParams, PAGE_KEY, SIZE_KEY } from 'controllers/pagination'; import { SORTING_KEY, DEFAULT_PAGINATION } from './constants'; -import { organizationSelector } from '../organization/selectors'; +import { organizationSelector } from '../selectors'; const domainSelector = (state) => organizationSelector(state).projects || {}; diff --git a/app/src/controllers/organizations/organization/reducer.js b/app/src/controllers/organization/reducer.js similarity index 96% rename from app/src/controllers/organizations/organization/reducer.js rename to app/src/controllers/organization/reducer.js index b600dc0546..c8514b38a3 100644 --- a/app/src/controllers/organizations/organization/reducer.js +++ b/app/src/controllers/organization/reducer.js @@ -18,7 +18,7 @@ import { combineReducers } from 'redux'; import { fetchReducer } from 'controllers/fetch'; import { loadingReducer } from 'controllers/loading'; import { queueReducers } from 'common/utils'; -import { projectsReducer } from '../projects/reducer'; +import { projectsReducer } from './projects/reducer'; import { FETCH_ORGANIZATION_BY_SLUG, SET_ACTIVE_ORGANIZATION } from './constants'; const setActiveOrganizationReducer = (state = [], { type = '', payload = {} }) => { diff --git a/app/src/controllers/organizations/organization/sagas.js b/app/src/controllers/organization/sagas.js similarity index 95% rename from app/src/controllers/organizations/organization/sagas.js rename to app/src/controllers/organization/sagas.js index e14841d845..68c0e6dbdf 100644 --- a/app/src/controllers/organizations/organization/sagas.js +++ b/app/src/controllers/organization/sagas.js @@ -17,10 +17,10 @@ import { takeEvery, all, put, select, take } from 'redux-saga/effects'; import { createFetchPredicate, fetchDataAction } from 'controllers/fetch'; import { redirect } from 'redux-first-router'; -import { PROJECTS_PAGE } from 'controllers/pages'; +import { ORGANIZATIONS_PAGE } from 'controllers/pages'; import { URLS } from 'common/urls'; import { showDefaultErrorNotification } from 'controllers/notification'; -import { fetchOrganizationProjectsAction } from 'controllers/organizations/projects'; +import { fetchOrganizationProjectsAction } from 'controllers/organization/projects'; import { FETCH_ORGANIZATION_BY_SLUG, PREPARE_ACTIVE_ORGANIZATION_PROJECTS } from './constants'; import { activeOrganizationSelector } from './selectors'; @@ -43,7 +43,7 @@ function* prepareActiveOrganizationProjects({ payload: { organizationSlug } }) { } catch (error) { yield put( redirect({ - type: PROJECTS_PAGE, + type: ORGANIZATIONS_PAGE, }), ); } diff --git a/app/src/controllers/organizations/organization/selectors.js b/app/src/controllers/organization/selectors.js similarity index 92% rename from app/src/controllers/organizations/organization/selectors.js rename to app/src/controllers/organization/selectors.js index d786e5cc42..10269b7e36 100644 --- a/app/src/controllers/organizations/organization/selectors.js +++ b/app/src/controllers/organization/selectors.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import { organizationsSelector } from 'controllers/organizations/selectors'; +import { organizationsSelector } from 'controllers/instance/organizations/selectors'; export const organizationSelector = (state) => organizationsSelector(state).organization || {}; diff --git a/app/src/controllers/pages/constants.js b/app/src/controllers/pages/constants.js index 7d648dc496..845842861d 100644 --- a/app/src/controllers/pages/constants.js +++ b/app/src/controllers/pages/constants.js @@ -22,8 +22,6 @@ export const CLEAR_PAGE_STATE = 'clearPageStateAction'; // undefined page export const NO_PAGE = undefined; // admin -export const PROJECTS_PAGE = 'PROJECTS_PAGE'; -export const PROJECT_DETAILS_PAGE = 'PROJECT_DETAILS_PAGE'; export const ALL_USERS_PAGE = 'ALL_USERS_PAGE'; export const SERVER_SETTINGS_PAGE = 'SERVER_SETTINGS_PAGE'; export const SERVER_SETTINGS_TAB_PAGE = 'SERVER_SETTINGS_TAB_PAGE'; @@ -32,6 +30,7 @@ export const PLUGINS_TAB_PAGE = 'PLUGINS_TAB_PAGE'; export const PLUGIN_UI_EXTENSION_ADMIN_PAGE = 'PLUGIN_UI_EXTENSION_ADMIN_PAGE'; // inside export const API_PAGE = 'API_PAGE'; +export const ORGANIZATIONS_PAGE = 'ORGANIZATIONS_PAGE'; export const ORGANIZATION_PROJECTS_PAGE = 'ORGANIZATION_PROJECTS_PAGE'; export const ORGANIZATION_MEMBERS_PAGE = 'ORGANIZATION_MEMBERS_PAGE'; export const ORGANIZATION_SETTINGS_PAGE = 'ORGANIZATION_SETTINGS_PAGE'; @@ -71,9 +70,8 @@ export const PROJECT_PLUGIN_PAGE = 'PROJECT_PLUGIN_PAGE'; export const pageNames = { [NOT_FOUND]: NOT_FOUND, + ORGANIZATIONS_PAGE, ORGANIZATION_PROJECTS_PAGE, - PROJECTS_PAGE, - PROJECT_DETAILS_PAGE, ALL_USERS_PAGE, SERVER_SETTINGS_PAGE, SERVER_SETTINGS_TAB_PAGE, @@ -112,8 +110,6 @@ export const pageNames = { }; export const adminPageNames = { - [PROJECTS_PAGE]: PROJECTS_PAGE, - [PROJECT_DETAILS_PAGE]: PROJECT_DETAILS_PAGE, [ALL_USERS_PAGE]: ALL_USERS_PAGE, [SERVER_SETTINGS_PAGE]: SERVER_SETTINGS_PAGE, [SERVER_SETTINGS_TAB_PAGE]: SERVER_SETTINGS_TAB_PAGE, diff --git a/app/src/controllers/pages/index.js b/app/src/controllers/pages/index.js index 47af91266d..a9540b4d42 100644 --- a/app/src/controllers/pages/index.js +++ b/app/src/controllers/pages/index.js @@ -50,8 +50,6 @@ export { updatePagePropertiesAction, clearPageStateAction } from './actionCreato export { NO_PAGE, - PROJECTS_PAGE, - PROJECT_DETAILS_PAGE, ALL_USERS_PAGE, SERVER_SETTINGS_PAGE, SERVER_SETTINGS_TAB_PAGE, @@ -92,6 +90,7 @@ export { CLEAR_PAGE_STATE, PLUGIN_UI_EXTENSION_ADMIN_PAGE, PROJECT_PLUGIN_PAGE, + ORGANIZATIONS_PAGE, ORGANIZATION_PROJECTS_PAGE, ORGANIZATION_MEMBERS_PAGE, ORGANIZATION_SETTINGS_PAGE, diff --git a/app/src/controllers/plugins/uiExtensions/createImportProps.js b/app/src/controllers/plugins/uiExtensions/createImportProps.js index 51269bfe80..97859abccc 100644 --- a/app/src/controllers/plugins/uiExtensions/createImportProps.js +++ b/app/src/controllers/plugins/uiExtensions/createImportProps.js @@ -122,7 +122,6 @@ import { DottedPreloader } from 'components/preloaders/dottedPreloader'; import { FieldProvider } from 'components/fields/fieldProvider'; import { FieldErrorHint } from 'components/fields/fieldErrorHint'; import { SimpleBreadcrumbs } from 'components/main/simpleBreadcrumbs'; - import { statisticsLinkSelector, defectLinkSelector, launchSelector } from 'controllers/testItem'; import { Grid } from 'components/main/grid'; import { InputCheckbox } from 'components/inputs/inputCheckbox'; @@ -130,7 +129,6 @@ import { AttributeListContainer as AttributeListField } from 'components/contain import { AsyncAutocomplete } from 'components/inputs/autocompletes/asyncAutocomplete'; import { InputSearch } from 'components/inputs/inputSearch'; import { PaginationToolbar } from 'components/main/paginationToolbar'; -import { ProjectName } from 'pages/instance/projectsPage/projectName'; import { debounce } from 'common/utils/debounce'; import { DotsMenuButton } from 'components/buttons/dotsMenuButton'; import { GhostMenuButton } from 'components/buttons/ghostMenuButton'; @@ -196,6 +194,7 @@ import { Tabs } from 'components/main/tabs'; import { withTooltip } from 'components/main/tooltips/tooltip'; import { Breadcrumbs } from 'componentLibrary/breadcrumbs'; import { PlainTable } from 'componentLibrary/plainTable'; +import { ProjectName } from 'pages/organization/organizationProjectsPage/projectsListTable/projectName'; const BUTTONS = { GhostButton, diff --git a/app/src/controllers/user/sagas.js b/app/src/controllers/user/sagas.js index 745e52b7bf..8eee8b4337 100644 --- a/app/src/controllers/user/sagas.js +++ b/app/src/controllers/user/sagas.js @@ -22,7 +22,7 @@ import { PROJECT_MANAGER } from 'common/constants/projectRoles'; import { getStorageItem, setStorageItem } from 'common/utils/storageUtils'; import { userAssignedSelector, urlOrganizationAndProjectSelector } from 'controllers/pages'; import { getLogTimeFormatFromStorage } from 'controllers/log/storageUtils'; -import { setActiveOrganizationAction } from 'controllers/organizations/organization/actionCreators'; +import { setActiveOrganizationAction } from 'controllers/organization/actionCreators'; import { assignToProjectSuccessAction, assignToProjectErrorAction, diff --git a/app/src/layouts/instanceLayout/instanceSidebar/instanceSidebar.jsx b/app/src/layouts/instanceLayout/instanceSidebar/instanceSidebar.jsx index 1439f24f70..e6a01821df 100644 --- a/app/src/layouts/instanceLayout/instanceSidebar/instanceSidebar.jsx +++ b/app/src/layouts/instanceLayout/instanceSidebar/instanceSidebar.jsx @@ -25,9 +25,9 @@ import { SERVER_SETTINGS_PAGE, PLUGINS_PAGE, ALL_USERS_PAGE, - PROJECTS_PAGE, PLUGIN_UI_EXTENSION_ADMIN_PAGE, USER_PROFILE_PAGE, + ORGANIZATIONS_PAGE, } from 'controllers/pages/constants'; import { SIDEBAR_EVENTS } from 'components/main/analytics/events'; import { @@ -63,7 +63,7 @@ export const InstanceSidebar = ({ onClickNavBtn }) => { { onClick: (isSidebarCollapsed) => onClickButton({ itemName: messages.organizations.defaultMessage, isSidebarCollapsed }), - link: { type: PROJECTS_PAGE }, + link: { type: ORGANIZATIONS_PAGE }, icon: OrganizationsIcon, message: formatMessage(messages.organizations), }, @@ -117,7 +117,7 @@ export const InstanceSidebar = ({ onClickNavBtn }) => { return sidebarItems; }; - const link = { type: PROJECTS_PAGE }; + const link = { type: ORGANIZATIONS_PAGE }; const linkToUserProfilePage = { type: USER_PROFILE_PAGE }; const titles = { shortTitle: formatMessage(messages.all), diff --git a/app/src/layouts/organizationLayout/organizationSidebar/organizationSidebar.jsx b/app/src/layouts/organizationLayout/organizationSidebar/organizationSidebar.jsx index f50ff36982..00c82d7075 100644 --- a/app/src/layouts/organizationLayout/organizationSidebar/organizationSidebar.jsx +++ b/app/src/layouts/organizationLayout/organizationSidebar/organizationSidebar.jsx @@ -25,8 +25,8 @@ import { ORGANIZATION_PROJECTS_PAGE, ORGANIZATION_MEMBERS_PAGE, ORGANIZATION_SETTINGS_PAGE, - PROJECTS_PAGE, USER_PROFILE_PAGE_ORGANIZATION_LEVEL, + ORGANIZATIONS_PAGE, } from 'controllers/pages/constants'; import { uiExtensionSidebarComponentsSelector } from 'controllers/plugins/uiExtensions'; import { AppSidebar } from 'layouts/common/appSidebar'; @@ -37,7 +37,7 @@ import ProjectsIcon from 'common/img/sidebar/projects-icon-inline.svg'; import { activeOrganizationNameSelector, activeOrganizationSelector, -} from 'controllers/organizations/organization'; +} from 'controllers/organization'; import { SIDEBAR_EVENTS } from 'components/main/analytics/events'; import { OrganizationsControlWithPopover } from '../../organizationsControl'; import { messages } from '../../messages'; @@ -107,7 +107,7 @@ export const OrganizationSidebar = ({ onClickNavBtn }) => { return sidebarItems; }; - const link = { type: PROJECTS_PAGE }; + const link = { type: ORGANIZATIONS_PAGE }; const linkToUserProfilePage = { type: USER_PROFILE_PAGE_ORGANIZATION_LEVEL, payload: { organizationSlug }, diff --git a/app/src/layouts/organizationsControl/organizationsControl.jsx b/app/src/layouts/organizationsControl/organizationsControl.jsx index 2ec45af4c7..4eec67c154 100644 --- a/app/src/layouts/organizationsControl/organizationsControl.jsx +++ b/app/src/layouts/organizationsControl/organizationsControl.jsx @@ -17,7 +17,7 @@ import PropTypes from 'prop-types'; import Parser from 'html-react-parser'; import classNames from 'classnames/bind'; -import Link from 'redux-first-router-link'; +import { NavLink } from 'components/main/navLink'; import { withPopover } from 'componentLibrary/popover'; import { useTracking } from 'react-tracking'; import { SIDEBAR_EVENTS } from 'components/main/analytics/events'; @@ -52,7 +52,7 @@ export const OrganizationsControl = ({ {isExtendedNav ? ( <> {Parser(ArrowLeftIcon)} - { @@ -61,7 +61,7 @@ export const OrganizationsControl = ({ }} >
{titles.topTitle}
- + ) : (
{titles.topTitle}
diff --git a/app/src/layouts/organizationsControl/organizationsPopover/organizationsPopover.jsx b/app/src/layouts/organizationsControl/organizationsPopover/organizationsPopover.jsx index 7b05fda389..287680e998 100644 --- a/app/src/layouts/organizationsControl/organizationsPopover/organizationsPopover.jsx +++ b/app/src/layouts/organizationsControl/organizationsPopover/organizationsPopover.jsx @@ -24,7 +24,7 @@ import { availableProjectsSelector } from 'controllers/user'; import { urlProjectSlugSelector, urlOrganizationSlugSelector, - PROJECTS_PAGE, + ORGANIZATIONS_PAGE, } from 'controllers/pages'; import { ScrollWrapper } from 'components/main/scrollWrapper'; import { NavLink } from 'components/main/navLink'; @@ -120,7 +120,7 @@ export const OrganizationsPopover = ({ closePopover, closeSidebar }) => { <>
{ onClose(); diff --git a/app/src/layouts/projectLayout/projectSidebar/projectSidebar.jsx b/app/src/layouts/projectLayout/projectSidebar/projectSidebar.jsx index 6f70503dcc..12296e956d 100644 --- a/app/src/layouts/projectLayout/projectSidebar/projectSidebar.jsx +++ b/app/src/layouts/projectLayout/projectSidebar/projectSidebar.jsx @@ -47,7 +47,7 @@ import DebugIcon from 'common/img/sidebar/debug-icon-inline.svg'; import MembersIcon from 'common/img/sidebar/members-icon-inline.svg'; import SettingsIcon from 'common/img/sidebar/settings-icon-inline.svg'; import { projectNameSelector } from 'controllers/project'; -import { activeOrganizationNameSelector } from 'controllers/organizations/organization'; +import { activeOrganizationNameSelector } from 'controllers/organization'; import { OrganizationsControlWithPopover } from '../../organizationsControl'; import { messages } from '../../messages'; diff --git a/app/src/pages/organization/emptyPageState/emptyPageState.jsx b/app/src/pages/common/emptyPageState/emptyPageState.jsx similarity index 100% rename from app/src/pages/organization/emptyPageState/emptyPageState.jsx rename to app/src/pages/common/emptyPageState/emptyPageState.jsx diff --git a/app/src/pages/organization/emptyPageState/emptyPageState.scss b/app/src/pages/common/emptyPageState/emptyPageState.scss similarity index 100% rename from app/src/pages/organization/emptyPageState/emptyPageState.scss rename to app/src/pages/common/emptyPageState/emptyPageState.scss diff --git a/app/src/pages/organization/emptyPageState/index.js b/app/src/pages/common/emptyPageState/index.js similarity index 100% rename from app/src/pages/organization/emptyPageState/index.js rename to app/src/pages/common/emptyPageState/index.js diff --git a/app/src/pages/instance/allUsersPage/allUsersGrid/projectsAndRolesColumn/rolesRow/rolesRow.jsx b/app/src/pages/instance/allUsersPage/allUsersGrid/projectsAndRolesColumn/rolesRow/rolesRow.jsx index c90822bb64..0fad2f73dd 100644 --- a/app/src/pages/instance/allUsersPage/allUsersGrid/projectsAndRolesColumn/rolesRow/rolesRow.jsx +++ b/app/src/pages/instance/allUsersPage/allUsersGrid/projectsAndRolesColumn/rolesRow/rolesRow.jsx @@ -30,7 +30,7 @@ import { isEmptyValue } from 'common/utils/isEmptyValue'; import Parser from 'html-react-parser'; import CrossIcon from 'common/img/cross-icon-inline.svg'; import CheckIcon from 'common/img/check-inline.svg'; -import { ProjectName } from 'pages/instance/projectsPage/projectName'; +import { ProjectName } from 'pages/organization/organizationProjectsPage/projectsListTable/projectName'; import styles from './rolesRow.scss'; const cx = classNames.bind(styles); diff --git a/app/src/pages/instance/organizationsPage/img/empty-organizations-inline.svg b/app/src/pages/instance/organizationsPage/img/empty-organizations-inline.svg new file mode 100644 index 0000000000..83e43b0f5b --- /dev/null +++ b/app/src/pages/instance/organizationsPage/img/empty-organizations-inline.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/src/pages/instance/projectsPage/projectMenu/index.js b/app/src/pages/instance/organizationsPage/index.js similarity index 91% rename from app/src/pages/instance/projectsPage/projectMenu/index.js rename to app/src/pages/instance/organizationsPage/index.js index 89d7ac444c..0eeb670fcb 100644 --- a/app/src/pages/instance/projectsPage/projectMenu/index.js +++ b/app/src/pages/instance/organizationsPage/index.js @@ -14,4 +14,4 @@ * limitations under the License. */ -export { ProjectMenu } from './projectMenu'; +export { OrganizationsPage } from './organizationsPage'; diff --git a/app/src/pages/instance/organizationsPage/messages.js b/app/src/pages/instance/organizationsPage/messages.js new file mode 100644 index 0000000000..b8a353c300 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/messages.js @@ -0,0 +1,64 @@ +/*! + * 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 { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + title: { + id: 'OrganizationsPage.title', + defaultMessage: 'All Organizations', + }, + description: { + id: 'OrganizationsPage.description', + defaultMessage: `The list of organizations available to you is currently empty. Please contact your Administrator to be assigned to an existing one.`, + }, + createOrganization: { + id: 'OrganizationsPage.createOrganization', + defaultMessage: 'Create Organization', + }, + createNewOrganization: { + id: 'OrganizationsPage.createNewOrganization', + defaultMessage: 'Create a new organization to begin your ReportPortal journey', + }, + noOrganizationsYet: { + id: 'OrganizationsPage.noOrganizationsYet', + defaultMessage: 'No organizations yet', + }, + noOrganizationsAvailableYet: { + id: 'OrganizationsPage.noOrganizationsAvailableYet', + defaultMessage: 'No organizations available yet', + }, + synchedOrganization: { + id: 'OrganizationsPage.synchedOrganization', + defaultMessage: 'Synched organization', + }, + lastLaunch: { + id: 'OrganizationsPage.lastLaunch', + defaultMessage: 'The last launch was executed more than 3 months ago', + }, + organizationUsers: { + id: 'OrganizationsPage.organizationUsers', + defaultMessage: 'Organization users', + }, + organizationProjects: { + id: 'OrganizationsPage.organizationProjects', + defaultMessage: 'Organization projects', + }, + latestLaunch: { + id: 'OrganizationsPage.latestLaunch', + defaultMessage: 'The latest launch execution', + }, +}); diff --git a/app/src/pages/instance/organizationsPage/organizationsPage.jsx b/app/src/pages/instance/organizationsPage/organizationsPage.jsx new file mode 100644 index 0000000000..bec6b9243d --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPage.jsx @@ -0,0 +1,81 @@ +/*! + * 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 { useSelector } from 'react-redux'; +import { userRolesSelector } from 'controllers/pages'; +import { canCreateOrganization } from 'common/utils/permissions'; +import classNames from 'classnames/bind'; +import { useIntl } from 'react-intl'; +import { BubblesLoader, PlusIcon } from '@reportportal/ui-kit'; +import { ScrollWrapper } from 'components/main/scrollWrapper'; +import { EmptyPageState } from 'pages/common/emptyPageState'; +import { + organizationsListLoadingSelector, + organizationsListSelector, +} from 'controllers/instance/organizations'; +import EmptyIcon from './img/empty-organizations-inline.svg'; +import { messages } from './messages'; +import styles from './organizationsPage.scss'; +import { OrganizationsPageHeader } from './organizationsPageHeader'; +import { OrganizationsPanelView } from './organizationsPanelView'; + +const cx = classNames.bind(styles); + +export const OrganizationsPage = () => { + const { formatMessage } = useIntl(); + const userRoles = useSelector(userRolesSelector); + const hasPermission = canCreateOrganization(userRoles); + const organizationsList = useSelector(organizationsListSelector); + const isOrganizationsLoading = useSelector(organizationsListLoadingSelector); + const isEmptyOrganizations = organizationsList.length === 0; + + const getEmptyPageState = () => + isOrganizationsLoading ? ( +
+ +
+ ) : ( + } + label={formatMessage( + hasPermission ? messages.noOrganizationsYet : messages.noOrganizationsAvailableYet, + )} + description={formatMessage( + hasPermission ? messages.createNewOrganization : messages.description, + )} + buttonTitle={formatMessage(messages.createOrganization)} + /> + ); + + return ( + +
+ + {isEmptyOrganizations ? ( + getEmptyPageState() + ) : ( + + )} +
+
+ ); +}; diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.scss b/app/src/pages/instance/organizationsPage/organizationsPage.scss similarity index 76% rename from app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.scss rename to app/src/pages/instance/organizationsPage/organizationsPage.scss index 5ace68b99d..2c8dbc2c44 100644 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.scss +++ b/app/src/pages/instance/organizationsPage/organizationsPage.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. @@ -14,14 +14,15 @@ * limitations under the License. */ -.caption { - font-family: $FONT-REGULAR; - color: $COLOR--black-2; - font-size: 13px; - padding-top: 3px; + .organizations-page { + display: flex; + flex-direction: column; + min-height: 100%; } -.sorting-block { + +.loader { display: flex; - justify-content: flex-end; + justify-content: center; align-items: center; + height: 100%; } diff --git a/app/src/pages/instance/projectsPage/projectName/index.js b/app/src/pages/instance/organizationsPage/organizationsPageHeader/index.js similarity index 89% rename from app/src/pages/instance/projectsPage/projectName/index.js rename to app/src/pages/instance/organizationsPage/organizationsPageHeader/index.js index d730662b5f..d271e1ac82 100644 --- a/app/src/pages/instance/projectsPage/projectName/index.js +++ b/app/src/pages/instance/organizationsPage/organizationsPageHeader/index.js @@ -14,4 +14,4 @@ * limitations under the License. */ -export { ProjectName } from './projectName'; +export { OrganizationsPageHeader } from './organizationsPageHeader'; diff --git a/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.jsx b/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.jsx new file mode 100644 index 0000000000..acb30ddfc4 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.jsx @@ -0,0 +1,55 @@ +/* + * 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 PropTypes from 'prop-types'; +import Parser from 'html-react-parser'; +import classNames from 'classnames/bind'; +import searchIcon from 'common/img/newIcons/search-outline-inline.svg'; +import filterIcon from 'common/img/newIcons/filters-outline-inline.svg'; +import { useIntl } from 'react-intl'; +import { messages } from '../messages'; +import styles from './organizationsPageHeader.scss'; + +const cx = classNames.bind(styles); + +export const OrganizationsPageHeader = ({ isEmpty }) => { + const { formatMessage } = useIntl(); + + return ( +
+
+ {formatMessage(messages.title)} +
+ {!isEmpty && ( +
+ {Parser(searchIcon)} + {Parser(filterIcon)} +
+ )} +
+
+
+ ); +}; + +OrganizationsPageHeader.propTypes = { + isEmpty: PropTypes.bool, +}; + +OrganizationsPageHeader.defaultProps = { + isEmpty: false, +}; diff --git a/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.scss b/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.scss new file mode 100644 index 0000000000..7b88e39bd6 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPageHeader/organizationsPageHeader.scss @@ -0,0 +1,74 @@ +/*! + * 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. + */ + +.organizations-page-header-container { + padding: 48px 32px 12px 32px; + border-bottom: 1px solid $COLOR--e-100; + background: $COLOR--bg-000; + box-sizing: border-box; + position: sticky; + top: 0; + z-index: 2; +} + +.header { + display: flex; + justify-content: space-between; +} + +.title { + font-family: $FONT-REGULAR; + font-size: 20px; + line-height: 31px; + color: $COLOR--almost-black; + text-transform: capitalize; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.actions { + display: flex; + align-items: center; + gap: 32px; + + .icons { + display: flex; + align-items: center; + justify-content: center; + + i { + display: flex; + align-items: center; + justify-content: center; + + &.search-icon { + width: 40px; + height: 36px; + } + + &.filters-icon { + width: 57px; + height: 36px; + } + + svg { + width: 16px; + height: 16px; + } + } + } +} diff --git a/app/src/pages/instance/projectsPage/projectStatisticButton/index.js b/app/src/pages/instance/organizationsPage/organizationsPanelView/index.js similarity index 90% rename from app/src/pages/instance/projectsPage/projectStatisticButton/index.js rename to app/src/pages/instance/organizationsPage/organizationsPanelView/index.js index 6058f26919..a3d475bbc4 100644 --- a/app/src/pages/instance/projectsPage/projectStatisticButton/index.js +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/index.js @@ -14,4 +14,4 @@ * limitations under the License. */ -export { ProjectStatisticButton } from './projectStatisticButton'; +export { OrganizationsPanelView } from './organizationsPanelView'; diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/last-update-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/last-update-inline.svg new file mode 100644 index 0000000000..408c1dc8db --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/last-update-inline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/outdated-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/outdated-inline.svg new file mode 100644 index 0000000000..680751e6fa --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/outdated-inline.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/personal-organization-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/personal-organization-inline.svg new file mode 100644 index 0000000000..75193f3a86 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/personal-organization-inline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/projects-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/projects-inline.svg new file mode 100644 index 0000000000..99eb213923 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/projects-inline.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/synched-organization-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/synched-organization-inline.svg new file mode 100644 index 0000000000..1218f81475 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/synched-organization-inline.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/user-inline.svg b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/user-inline.svg new file mode 100644 index 0000000000..3a7a3610f4 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/img/user-inline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/pages/instance/projectsPage/projects/index.js b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/index.js similarity index 91% rename from app/src/pages/instance/projectsPage/projects/index.js rename to app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/index.js index 309e61c5f4..38f1063de1 100644 --- a/app/src/pages/instance/projectsPage/projects/index.js +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/index.js @@ -14,4 +14,4 @@ * limitations under the License. */ -export { Projects } from './projects'; +export { OrganizationCard } from './organizationCard'; diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.jsx b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.jsx new file mode 100644 index 0000000000..0af3c72896 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.jsx @@ -0,0 +1,125 @@ +/*! + * 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 classNames from 'classnames/bind'; +import PropTypes from 'prop-types'; +import { useIntl, FormattedRelativeTime } from 'react-intl'; +import Parser from 'html-react-parser'; +import { useSelector } from 'react-redux'; +import { Tooltip } from '@reportportal/ui-kit'; +import { NavLink } from 'components/main/navLink'; +import { ORGANIZATION_PROJECTS_PAGE } from 'controllers/pages/constants'; +import { userRolesSelector } from 'controllers/pages'; +import { MANAGER } from 'common/constants/projectRoles'; +import { ADMINISTRATOR } from 'common/constants/accountRoles'; +import { getRelativeUnits } from 'common/utils/timeDateUtils'; +import { ORGANIZATION_EXTERNAL_TYPE } from 'common/constants/organizationTypes'; +import UserIcon from './img/user-inline.svg'; +import ProjectsIcon from './img/projects-inline.svg'; +import LastUpdateIcon from './img/last-update-inline.svg'; +import SynchedIcon from './img/synched-organization-inline.svg'; +import OutdatedIcon from './img/outdated-inline.svg'; +import PersonalIcon from './img/personal-organization-inline.svg'; +import { messages } from '../../messages'; +import styles from './organizationCard.scss'; + +const cx = classNames.bind(styles); +const THREE_MONTHS = 3600 * 24 * 30 * 1000; + +export const OrganizationCard = ({ organization }) => { + const { formatMessage } = useIntl(); + const { userRole, organizationRole } = useSelector(userRolesSelector); + const hasPermission = userRole === ADMINISTRATOR || organizationRole === MANAGER; + const usersCount = organization.relationships.users.meta.count; + const projectsCount = organization.relationships.projects.meta.count; + const lastLaunch = organization.relationships.launches.meta.last_occurred_at; + const { value: relativeTime, unit } = getRelativeUnits(new Date(lastLaunch)); + const isOutdated = Date.now() - new Date(lastLaunch).getTime() > THREE_MONTHS; + + const cartInfo = [ + { + icon: UserIcon, + className: cx('icon-wrapper'), + content: formatMessage(messages.organizationUsers), + bottomElement: {usersCount}, + }, + { + icon: ProjectsIcon, + className: cx('icon-wrapper'), + content: formatMessage(messages.organizationProjects), + bottomElement: {projectsCount}, + }, + { + icon: LastUpdateIcon, + className: cx('last-update'), + content: formatMessage(messages.latestLaunch), + bottomElement: , + }, + ]; + + return ( +
+
+ + {organization.name} + + {hasPermission && + (organization.type === ORGANIZATION_EXTERNAL_TYPE ? ( + + {Parser(SynchedIcon)} + + ) : ( + {Parser(PersonalIcon)} + ))} + {hasPermission && isOutdated && ( + + {Parser(OutdatedIcon)} + + )} +
+ {hasPermission && ( +
+ {cartInfo.map(({ icon, className, content, bottomElement }) => ( +
+ + {Parser(icon)} + + {bottomElement} +
+ ))} +
+ )} +
+ ); +}; + +OrganizationCard.propTypes = { + organization: PropTypes.object.isRequired, +}; diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.scss b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.scss new file mode 100644 index 0000000000..eb888c83ef --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationCard/organizationCard.scss @@ -0,0 +1,91 @@ +/*! + * 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. + */ + +.organization-card { + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 16px; + box-sizing: border-box; + height: 160px; + border-radius: 8px; + min-width: 320px; + max-width: 400px; + box-shadow: $BOX_SHADOW--item; + background-color: $COLOR--white-two; + + i { + display: block; + height: 16px; + } +} + +.last-update, +.icon-wrapper { + display: flex; + align-items: center; + gap: 8px; + font-family: $FONT-ROBOTO-REGULAR; + color: $COLOR--almost-black; + font-size: 13px; + line-height: 20px; + width: 98px; +} + +.last-update { + width: auto; + max-width: 152px; +} + +.icon { + width: 16px; + + svg { + width: 16px; + height: 16px; + } +} + +.date { + min-height: 20px; + font-family: $FONT-ROBOTO-REGULAR; + color: $COLOR--almost-black; + font-size: 13px; + line-height: 20px; +} + +.cart-info { + display: flex; + gap: 10px; +} + +.organization-name { + display: flex; + align-items: center; + gap: 16px; +} + +.tooltip-wrapper { + width: 16px; +} + +.organization-link { + text-decoration: none; + font-family: $FONT-ROBOTO-MEDIUM; + color: $COLOR--almost-black; + font-size: 13px; + line-height: 20px; +} diff --git a/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.jsx b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.jsx new file mode 100644 index 0000000000..9f89a8c345 --- /dev/null +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.jsx @@ -0,0 +1,81 @@ +/*! + * 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 classNames from 'classnames/bind'; +import PropTypes from 'prop-types'; +import { SORTING_ASC, withSortingURL } from 'controllers/sorting'; +import { DEFAULT_PAGINATION, PAGE_KEY, withPagination } from 'controllers/pagination'; +import { PaginationWrapper } from 'components/main/paginationWrapper'; +import { organizationsListPaginationSelector } from 'controllers/instance/organizations'; +import { + DEFAULT_LIMITATION, + DEFAULT_PAGE_SIZE_OPTIONS, +} from 'controllers/instance/organizations/constants'; +import styles from './organizationsPanelView.scss'; +import { OrganizationCard } from './organizationCard'; + +const cx = classNames.bind(styles); + +const OrganizationsPanelViewWrapped = ({ + organizationsList, + pageSize, + activePage, + itemCount, + pageCount, + onChangePage, + onChangePageSize, +}) => ( + 0} + pageSize={pageSize} + activePage={activePage} + totalItems={itemCount} + totalPages={pageCount} + pageSizeOptions={DEFAULT_PAGE_SIZE_OPTIONS} + changePage={onChangePage} + changePageSize={onChangePageSize} + > +
+ {organizationsList.map((organization) => ( + + ))} +
+
+); + +OrganizationsPanelViewWrapped.propTypes = { + organizationsList: PropTypes.array, + pageSize: PropTypes.number, + activePage: PropTypes.number, + itemCount: PropTypes.number.isRequired, + pageCount: PropTypes.number.isRequired, + onChangePage: PropTypes.func.isRequired, + onChangePageSize: PropTypes.func.isRequired, +}; + +OrganizationsPanelViewWrapped.defaultProps = { + organizationsList: [], + pageSize: DEFAULT_LIMITATION, + activePage: DEFAULT_PAGINATION[PAGE_KEY], +}; + +export const OrganizationsPanelView = withSortingURL({ + defaultDirection: SORTING_ASC, +})( + withPagination({ + paginationSelector: organizationsListPaginationSelector, + })(OrganizationsPanelViewWrapped), +); diff --git a/app/src/controllers/organizations/constants.js b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.scss similarity index 79% rename from app/src/controllers/organizations/constants.js rename to app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.scss index ca55b9008f..222187af4f 100644 --- a/app/src/controllers/organizations/constants.js +++ b/app/src/pages/instance/organizationsPage/organizationsPanelView/organizationsPanelView.scss @@ -1,4 +1,4 @@ -/* +/*! * Copyright 2024 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +14,11 @@ * limitations under the License. */ -export const NAMESPACE = 'organizations'; - -export const FETCH_ORGANIZATIONS = 'fetchOrganizations'; +.organizations-list { + display: flex; + flex-direction: row; + justify-content: center; + flex-wrap: wrap; + gap: 24px; + padding-top: 24px; +} diff --git a/app/src/pages/instance/projectsPage/messages.js b/app/src/pages/instance/projectsPage/messages.js deleted file mode 100644 index 78b7689334..0000000000 --- a/app/src/pages/instance/projectsPage/messages.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2019 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 { defineMessages } from 'react-intl'; -import { MEMBERS, MONITORING } from 'common/constants/projectSections'; - -export const messages = defineMessages({ - statisticButtonTooltip: { - id: 'ProjectStatisticButton.buttonTooltip', - defaultMessage: 'See detailed information', - }, - pageTitle: { - id: 'ProjectsPage.title', - defaultMessage: 'All projects', - }, - addProject: { - id: 'ProjectsPage.addProject', - defaultMessage: 'Create Project', - }, - addProjectTitle: { - id: 'ProjectsPage.addProjectTitle', - defaultMessage: 'Add project', - }, - projectNameLabel: { - id: 'ProjectsPage.projectNameLabel', - defaultMessage: 'Name', - }, - projectNamePlaceholder: { - id: 'ProjectsPage.projectNamePlaceholder', - defaultMessage: 'Enter project’s name', - }, - projectLengthHint: { - id: 'ProjectsPage.projectLengthHint', - defaultMessage: 'Project name ', - }, - [`${MEMBERS}HeaderButton`]: { - id: 'ProjectDetailsPageMembers.headerButton', - defaultMessage: 'Project Members', - }, - [`${MONITORING}HeaderButton`]: { - id: 'ProjectDetailsPageEvents.headerButton', - defaultMessage: 'Project Monitoring', - }, - deleteProjectsCount: { - id: 'ProjectsPage.deleteProjectsCount', - defaultMessage: '{count} items selected', - }, - deleteModalHeader: { - id: 'ProjectsPage.deleteModalHeader', - defaultMessage: 'Delete project', - }, - deleteModalMultipleHeader: { - id: 'ProjectsPage.deleteModalMultipleHeader', - defaultMessage: 'Delete projects', - }, - deleteModalContent: { - id: 'ProjectsPage.deleteModalContent', - defaultMessage: 'Are you sure you want to delete the project {name}?', - }, - deleteModalMultipleContent: { - id: 'ProjectsPage.deleteModalMultipleContent', - defaultMessage: 'Are you sure you want to delete the projects {names}?', - }, - deleteError: { - id: 'ProjectsPage.deleteError', - defaultMessage: 'Error when deleting projects', - }, - deleteSuccess: { - id: 'ProjectsPage.deleteSuccess', - defaultMessage: 'Project has been deleted', - }, - deleteSuccessMultiple: { - id: 'ProjectsPage.deleteSuccessMultiple', - defaultMessage: 'Projects have been deleted', - }, - deleteErrorMultiple: { - id: 'ProjectsPage.deleteErrorMultiple', - defaultMessage: 'Error when deleting projects', - }, - members: { - id: 'ProjectPanel.members', - defaultMessage: 'Members', - }, - settings: { - id: 'ProjectPanel.settings', - defaultMessage: 'Settings', - }, - assign: { - id: 'ProjectPanel.assign', - defaultMessage: 'Assign', - }, - unassign: { - id: 'ProjectPanel.unassign', - defaultMessage: 'Unassign', - }, - delete: { - id: 'ProjectPanel.delete', - defaultMessage: 'Delete', - }, - monitoring: { - id: 'ProjectPanel.monitoring', - defaultMessage: 'Monitoring', - }, - unassignFromPersonal: { - id: 'ProjectPanel.unassignFromPersonal', - defaultMessage: 'Impossible to unassign user from personal project', - }, - launchesQuantity: { - id: 'ProjectPanel.launchesQuantity', - defaultMessage: 'Launches', - }, - membersQuantity: { - id: 'ProjectPanel.membersQuantity', - defaultMessage: 'Members', - }, - linkedTooltip: { - id: 'ProjectPanel.linkedTooltip', - defaultMessage: 'Synced with UPSA', - }, - personalTooltip: { - id: 'ProjectPanel.personalTooltip', - defaultMessage: 'Personal project', - }, - personal: { - id: 'ProjectPanel.personal', - defaultMessage: 'Personal', - }, - internalTooltip: { - id: 'ProjectPanel.internalTooltip', - defaultMessage: 'Internal project', - }, - internal: { - id: 'ProjectPanel.internal', - defaultMessage: 'Internal', - }, - noMembers: { - id: 'ProjectPanel.noMembers', - defaultMessage: 'No members', - }, - noLaunches: { - id: 'ProjectPanel.noLaunches', - defaultMessage: 'No launches', - }, - lastLaunch: { - id: 'ProjectPanel.lastLaunch', - defaultMessage: '{date} run', - }, - searchPlaceholder: { - id: 'ProjectsPage.searchPlaceholder', - defaultMessage: 'Search by name', - }, - nameCol: { id: 'ProjectsGrid.nameCol', defaultMessage: 'Name' }, - projectTypeCol: { id: 'ProjectsGrid.projectTypeCol', defaultMessage: 'Project Type' }, - organizationCol: { id: 'ProjectsGrid.organizationCol', defaultMessage: 'Organization' }, - membersCol: { id: 'ProjectsGrid.membersCol', defaultMessage: 'Members' }, - membersColShort: { id: 'ProjectsGrid.membersColShort', defaultMessage: 'Mmbrs' }, - launchesCol: { id: 'ProjectsGrid.launchesCol', defaultMessage: 'Launches' }, - launchesColShort: { id: 'ProjectsGrid.launchesColShort', defaultMessage: 'Lnchs' }, - lastLaunchCol: { id: 'ProjectsGrid.lastLaunchCol', defaultMessage: 'Last Launch date' }, - lastLaunchColShort: { id: 'ProjectsGrid.lastLaunchColShort', defaultMessage: 'Lnch date' }, - sortBy: { id: 'ProjectsPage.sortBy', defaultMessage: 'Sort by' }, - dateCol: { id: 'ProjectsPage.dateCol', defaultMessage: 'Date' }, -}); diff --git a/app/src/pages/instance/projectsPage/projectMenu/projectMenu.jsx b/app/src/pages/instance/projectsPage/projectMenu/projectMenu.jsx deleted file mode 100644 index c0e78da4af..0000000000 --- a/app/src/pages/instance/projectsPage/projectMenu/projectMenu.jsx +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2019 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 { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { injectIntl } from 'react-intl'; -import track from 'react-tracking'; -import { - assignToProjectAction, - unassignFromProjectAction, - assignedProjectsSelector, - userIdSelector, -} from 'controllers/user'; -import { showModalAction } from 'controllers/modal'; -import { deleteProjectAction, navigateToProjectSectionAction } from 'controllers/instance/projects'; -import { fetchProjectSuccessAction } from 'controllers/project'; -import { MEMBERS, MONITORING } from 'common/constants/projectSections'; -import { DotsMenuButton, SEPARATOR_ITEM, DANGER_ITEM } from 'components/buttons/dotsMenuButton'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; -import { navigateToProjectSettingsAction } from 'controllers/instance/projects/actionCreators'; -import { fetch } from 'common/utils'; -import { URLS } from 'common/urls'; -import { messages } from '../messages'; - -@connect( - (state, ownProps) => ({ - isAssigned: !!assignedProjectsSelector(state)[ownProps.project.projectName], - userId: userIdSelector(state), - }), - { - showModal: showModalAction, - assignToProject: assignToProjectAction, - unassignFromProject: unassignFromProjectAction, - deleteProject: deleteProjectAction, - navigateToProjectSection: navigateToProjectSectionAction, - navigateToProjectSettings: navigateToProjectSettingsAction, - fetchProjectSuccess: fetchProjectSuccessAction, - }, -) -@injectIntl -@track() -export class ProjectMenu extends Component { - static propTypes = { - project: PropTypes.object.isRequired, - intl: PropTypes.object.isRequired, - isAssigned: PropTypes.bool.isRequired, - userId: PropTypes.string.isRequired, - navigateToProjectSection: PropTypes.func.isRequired, - navigateToProjectSettings: PropTypes.func.isRequired, - fetchProjectSuccess: PropTypes.func.isRequired, - assignToProject: PropTypes.func.isRequired, - unassignFromProject: PropTypes.func.isRequired, - deleteProject: PropTypes.func.isRequired, - showModal: PropTypes.func.isRequired, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - }; - - onAssign = () => { - const { assignToProject, project, tracking } = this.props; - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.ASSIGN_ACTION); - assignToProject(project); - }; - - onUnassign = () => { - const { unassignFromProject, project, tracking } = this.props; - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.UNASSIGN_ACTION); - unassignFromProject(project); - }; - - onDelete = () => { - const { showModal, intl, userId, project, tracking } = this.props; - const { projectName } = project; - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.DELETE_PROJECT_BTN); - showModal({ - id: 'deleteItemsModal', - data: { - items: [project], - onConfirm: this.onDeleteSubmit, - header: intl.formatMessage(messages.deleteModalHeader), - mainContent: intl.formatMessage(messages.deleteModalContent, { - name: `'${projectName}'`, - }), - userId, - eventsInfo: { - closeIcon: ADMIN_PROJECTS_PAGE_EVENTS.CLOSE_ICON_DELETE_MODAL, - cancelBtn: ADMIN_PROJECTS_PAGE_EVENTS.CANCEL_BTN_DELETE_MODAL, - deleteBtn: ADMIN_PROJECTS_PAGE_EVENTS.DELETE_BTN_DELETE_MODAL, - }, - }, - }); - }; - - onDeleteSubmit = () => { - this.props.deleteProject(this.props.project); - }; - - getMenuItems = () => { - const { intl, project, isAssigned, userId } = this.props; - const isPersonalProject = project.projectName === `${userId}_personal`; - return [ - { - onClick: this.navigateToMembers, - label: intl.formatMessage(messages.members), - value: 'action-members', - }, - { - onClick: this.navigateToEventsMonitoring, - label: intl.formatMessage(messages.monitoring), - value: 'action-monitoring', - }, - { - onClick: this.navigateToSettings, - label: intl.formatMessage(messages.settings), - value: 'action-settings', - }, - { - type: SEPARATOR_ITEM, - }, - { - onClick: this.onUnassign, - label: intl.formatMessage(messages.unassign), - value: 'action-unassign', - hidden: !isAssigned, - disabled: isPersonalProject, - title: isPersonalProject ? intl.formatMessage(messages.unassignFromPersonal) : '', - }, - { - onClick: this.onAssign, - label: intl.formatMessage(messages.assign), - value: 'action-assign', - hidden: isAssigned, - }, - { - onClick: this.onDelete, - label: intl.formatMessage(messages.delete), - value: 'action-delete', - type: DANGER_ITEM, - }, - ]; - }; - - navigateWithFetchProject = (projectKey, payload, path) => { - fetch(URLS.projectByName(projectKey)).then((project) => { - this.props.fetchProjectSuccess(project); - this.props.navigateToProjectSection(payload, path); - }); - }; - - navigateToMembers = () => { - const { - tracking: { trackEvent }, - project: { organizationSlug, projectSlug, projectKey }, - } = this.props; - - trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.MEMBERS_ACTION); - this.navigateWithFetchProject(projectKey, { organizationSlug, projectSlug }, MEMBERS); - }; - - navigateToSettings = () => { - const { - tracking: { trackEvent }, - project: { organizationSlug, projectSlug }, - } = this.props; - - trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.SETTINGS_ACTION); - this.props.navigateToProjectSettings({ organizationSlug, projectSlug }); - }; - - navigateToEventsMonitoring = () => { - const { - tracking: { trackEvent }, - project: { organizationSlug, projectSlug, projectKey }, - } = this.props; - - trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.CLICK_EVENT_MONITORING); - this.navigateWithFetchProject(projectKey, { organizationSlug, projectSlug }, MONITORING); - }; - - render() { - return ( - this.props.tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.PROJECT_MENU)} - /> - ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectName/projectName.jsx b/app/src/pages/instance/projectsPage/projectName/projectName.jsx deleted file mode 100644 index e293510ef2..0000000000 --- a/app/src/pages/instance/projectsPage/projectName/projectName.jsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2019 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, { Component } from 'react'; -import track from 'react-tracking'; -import classNames from 'classnames/bind'; -import PropTypes from 'prop-types'; -import Link from 'redux-first-router-link'; -import { connect } from 'react-redux'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; -import { SCREEN_XS_MAX_MEDIA } from 'common/constants/screenSizeVariables'; -import { navigateToProjectAction } from 'controllers/instance/projects'; -import { PROJECT_PAGE } from 'controllers/pages'; -import { assignedProjectsSelector } from 'controllers/user'; -import styles from './projectName.scss'; - -const cx = classNames.bind(styles); - -@connect( - (state, ownProps) => ({ - isAssigned: !!assignedProjectsSelector(state)[ownProps.project.projectKey], - }), - { - navigateToProject: navigateToProjectAction, - }, -) -@track() -export class ProjectName extends Component { - static propTypes = { - project: PropTypes.object.isRequired, - navigateToProject: PropTypes.func.isRequired, - isAssigned: PropTypes.bool, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - disableAnalytics: PropTypes.bool, - }; - - static defaultProps = { - isAssigned: false, - disableAnalytics: false, - }; - - onProjectClick = (event) => { - const { tracking, isAssigned, disableAnalytics } = this.props; - if (!isAssigned && window.matchMedia(SCREEN_XS_MAX_MEDIA).matches) { - event.preventDefault(); - return; - } - this.props.navigateToProject({ - project: this.props.project, - }); - if (!disableAnalytics) { - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.PROJECT_NAME); - } - event.preventDefault(); - }; - - render() { - const { - project: { projectSlug, organizationSlug, projectName }, - isAssigned, - } = this.props; - - return ( - - {projectName} - - ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectName/projectName.scss b/app/src/pages/instance/projectsPage/projectName/projectName.scss deleted file mode 100644 index 5faa7f726c..0000000000 --- a/app/src/pages/instance/projectsPage/projectName/projectName.scss +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.name { - font-family: $FONT-REGULAR; - text-decoration: none; - color: $COLOR--topaz; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: pointer; - &.mobile-disabled { - @media (max-width: $SCREEN_XS_MAX) { - cursor: default; - color: $COLOR--black-2; - } - } -} diff --git a/app/src/pages/instance/projectsPage/projectStatisticButton/projectStatisticButton.jsx b/app/src/pages/instance/projectsPage/projectStatisticButton/projectStatisticButton.jsx deleted file mode 100644 index 28d158d8ca..0000000000 --- a/app/src/pages/instance/projectsPage/projectStatisticButton/projectStatisticButton.jsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2019 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 PropTypes from 'prop-types'; -import Link from 'redux-first-router-link'; -import React from 'react'; -import { PROJECT_DETAILS_PAGE } from 'controllers/pages'; -import { Icon } from 'components/main/icon/icon'; -import { useIntl } from 'react-intl'; -import { messages } from '../messages'; - -export const ProjectStatisticButton = ({ projectSlug, onClick, organizationSlug }) => { - const { formatMessage } = useIntl(); - return ( - - - - ); -}; - -ProjectStatisticButton.propTypes = { - projectSlug: PropTypes.string.isRequired, - organizationSlug: PropTypes.string.isRequired, - onClick: PropTypes.func, -}; - -ProjectStatisticButton.defaultProps = { - onClick: () => {}, -}; diff --git a/app/src/pages/instance/projectsPage/projects/projects.jsx b/app/src/pages/instance/projectsPage/projects/projects.jsx deleted file mode 100644 index f0329a2510..0000000000 --- a/app/src/pages/instance/projectsPage/projects/projects.jsx +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2019 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, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { injectIntl } from 'react-intl'; -import track from 'react-tracking'; - -import { SIZE_KEY, withPagination, PAGE_KEY } from 'controllers/pagination'; -import { SORTING_ASC, withSortingURL } from 'controllers/sorting'; -import { - DEFAULT_PAGINATION, - TABLE_VIEW, - GRID_VIEW, - projectsPaginationSelector, - viewModeSelector, - loadingSelector, - projectsSelector, - DEFAULT_SORT_COLUMN, - unselectAllProjectsAction, -} from 'controllers/instance/projects'; -import { COMMON_LOCALE_KEYS } from 'common/constants/localization'; -import { PaginationToolbar } from 'components/main/paginationToolbar'; -import { NoItemMessage } from 'components/main/noItemMessage'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; - -import { ProjectsPanelView } from '../projectsPanelView'; -import { ProjectsGrid } from '../projectsGrid'; -import { ProjectsToolbar } from '../projectsToolbar'; - -@connect( - (state) => ({ - viewMode: viewModeSelector(state), - loading: loadingSelector(state), - projects: projectsSelector(state), - }), - { - unselectAllProjectsAction, - }, -) -@withSortingURL({ - defaultFields: [DEFAULT_SORT_COLUMN], - defaultDirection: SORTING_ASC, -}) -@withPagination({ - paginationSelector: projectsPaginationSelector, -}) -@injectIntl -@track() -export class Projects extends Component { - static propTypes = { - activePage: PropTypes.number, - itemCount: PropTypes.number, - pageCount: PropTypes.number, - pageSize: PropTypes.number, - onChangePage: PropTypes.func.isRequired, - onChangePageSize: PropTypes.func.isRequired, - sortingColumn: PropTypes.string, - sortingDirection: PropTypes.string, - onChangeSorting: PropTypes.func, - intl: PropTypes.object.isRequired, - viewMode: PropTypes.string, - loading: PropTypes.bool, - projects: PropTypes.arrayOf(PropTypes.object), - unselectAllProjectsAction: PropTypes.func.isRequired, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - }; - - static defaultProps = { - activePage: DEFAULT_PAGINATION[PAGE_KEY], - itemCount: null, - pageCount: null, - pageSize: DEFAULT_PAGINATION[SIZE_KEY], - viewMode: GRID_VIEW, - loading: false, - projects: [], - sortingColumn: null, - sortingDirection: null, - onChangeSorting: () => {}, - }; - - componentWillUnmount() { - this.props.unselectAllProjectsAction(); - } - - handleChangeSorting = (field) => { - this.props.onChangeSorting(field); - this.props.tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.CHANGE_SORTING); - }; - - render() { - const { - activePage, - itemCount, - pageCount, - pageSize, - onChangePage, - onChangePageSize, - viewMode, - loading, - intl, - projects, - sortingColumn, - sortingDirection, - } = this.props; - - return ( - - - - {viewMode === TABLE_VIEW ? ( - - ) : ( - - )} - - {!!pageCount && !loading && ( - - )} - {!projects.length && !loading && ( - - )} - - ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.jsx b/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.jsx deleted file mode 100644 index 6e14e09e1f..0000000000 --- a/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.jsx +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2019 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, { PureComponent } from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import { injectIntl } from 'react-intl'; -import classNames from 'classnames/bind'; -import { Grid } from 'components/main/grid'; -import { - loadingSelector, - projectsSelector, - selectedProjectsSelector, - toggleAllProjectsAction, - toggleProjectSelectionAction, -} from 'controllers/instance/projects'; -import { - NAME, - TYPE, - ORGANIZATION, - USERS_QUANTITY, - LAST_RUN, - LAUNCHES_QUANTITY, -} from 'common/constants/projectsObjectTypes'; -import { - NameColumn, - ProjectTypeColumn, - OrganizationColumn, - MembersColumn, - LaunchesColumn, - LastLaunchColumn, - MenuColumn, - StatisticColumn, -} from './projectsGridColumns'; - -import styles from './projectsGrid.scss'; -import { messages } from '../messages'; - -const cx = classNames.bind(styles); - -const STATISTIC_COLUMN = 'statistic'; -const MENU_COLUMN = 'menu'; - -@connect( - (state) => ({ - projects: projectsSelector(state), - loading: loadingSelector(state), - selectedProjects: selectedProjectsSelector(state), - }), - { - toggleProjectSelectionAction, - toggleAllProjectsAction, - }, -) -@injectIntl -export class ProjectsGrid extends PureComponent { - static propTypes = { - intl: PropTypes.object.isRequired, - projects: PropTypes.arrayOf(PropTypes.object), - selectedProjects: PropTypes.arrayOf(PropTypes.object), - loading: PropTypes.bool, - toggleProjectSelectionAction: PropTypes.func.isRequired, - toggleAllProjectsAction: PropTypes.func.isRequired, - sortingColumn: PropTypes.string, - sortingDirection: PropTypes.string, - onChangeSorting: PropTypes.func, - }; - - static defaultProps = { - projects: [], - selectedProjects: [], - loading: false, - sortingColumn: null, - sortingDirection: null, - onChangeSorting: () => {}, - }; - - getColumns = () => [ - { - id: STATISTIC_COLUMN, - component: StatisticColumn, - }, - { - id: NAME, - title: { - full: this.props.intl.formatMessage(messages.nameCol), - }, - maxHeight: 170, - component: NameColumn, - sortable: true, - }, - { - id: TYPE, - title: { - full: this.props.intl.formatMessage(messages.projectTypeCol), - }, - component: ProjectTypeColumn, - sortable: true, - }, - { - id: ORGANIZATION, - title: { - full: this.props.intl.formatMessage(messages.organizationCol), - }, - component: OrganizationColumn, - sortable: true, - }, - { - id: USERS_QUANTITY, - title: { - full: this.props.intl.formatMessage(messages.membersCol), - short: this.props.intl.formatMessage(messages.membersColShort), - }, - component: MembersColumn, - sortable: true, - }, - { - id: LAUNCHES_QUANTITY, - title: { - full: this.props.intl.formatMessage(messages.launchesCol), - short: this.props.intl.formatMessage(messages.launchesColShort), - }, - component: LaunchesColumn, - sortable: true, - }, - { - id: LAST_RUN, - title: { - full: this.props.intl.formatMessage(messages.lastLaunchCol), - short: this.props.intl.formatMessage(messages.lastLaunchColShort), - }, - component: LastLaunchColumn, - sortable: true, - }, - { - id: MENU_COLUMN, - component: MenuColumn, - }, - ]; - - COLUMNS = this.getColumns(); - - render() { - const { - projects, - loading, - selectedProjects, - sortingColumn, - sortingDirection, - onChangeSorting, - } = this.props; - - return ( - this.props.toggleAllProjectsAction(projects)} - className={cx('projects-grid')} - gridRowClassName={cx('projects-grid-row')} - headerClassName={cx('projects-header')} - sortingColumn={sortingColumn} - sortingDirection={sortingDirection} - onChangeSorting={onChangeSorting} - /> - ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.scss b/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.scss deleted file mode 100644 index 5d29d44e19..0000000000 --- a/app/src/pages/instance/projectsPage/projectsGrid/projectsGrid.scss +++ /dev/null @@ -1,115 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.projects-col { - font-size: 13px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.project-type-col { - &:first-letter { - text-transform: capitalize; - } -} - -.projects-grid { - @media (max-width: $SCREEN_SM_MAX) { - table-layout: fixed; - } -} - -.projects-grid-row { - > :first-child { - width: 1%; - } - > :last-child { - padding-top: 22px; - } - > :nth-child(2) { - max-width: 0; - } - > :nth-child(4) { - max-width: 0; - } - > :nth-child(7) { - width: 12%; - white-space: pre-wrap; - overflow: hidden; - text-overflow: ellipsis; - } - @media (max-width: $SCREEN_SM_MAX) { - display: table-row; - > :nth-child(3), - > :nth-child(4) { - display: none; - } - } -} - -.projects-header { - > :first-child { - width: 1%; - min-width: 20px; - } - > :nth-child(2) { - width: 30%; - } - > :nth-child(3), - > :nth-child(4) { - width: 15%; - } - - > :nth-child(5), - > :nth-child(6) { - width: 10%; - } - - > :nth-child(7) { - width: 15%; - } - > :nth-child(8), - > :nth-child(9) { - min-width: 20px; - } - @media (max-width: $SCREEN_SM_MAX) { - display: table-row; - > :first-child { - width: 10%; - min-width: 20px; - } - > :nth-child(2) { - width: 25%; - } - > :nth-child(3), - > :nth-child(4) { - display: none; - } - - > :nth-child(5), - > :nth-child(6) { - width: 14%; - } - - > :nth-child(7) { - width: 25%; - } - > :nth-child(8) { - min-width: 12%; - } - } -} diff --git a/app/src/pages/instance/projectsPage/projectsGrid/projectsGridColumns.jsx b/app/src/pages/instance/projectsPage/projectsGrid/projectsGridColumns.jsx deleted file mode 100644 index 9e6d258f64..0000000000 --- a/app/src/pages/instance/projectsPage/projectsGrid/projectsGridColumns.jsx +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2019 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 PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import classNames from 'classnames/bind'; -import { AbsRelTime } from 'components/main/absRelTime'; -import styles from './projectsGrid.scss'; -import { ProjectMenu } from '../projectMenu'; -import { ProjectStatisticButton } from '../projectStatisticButton'; -import { ProjectName } from '../projectName'; - -const cx = classNames.bind(styles); - -export const NameColumn = ({ className, value }) => ( -
- -
-); -NameColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -NameColumn.defaultProps = { - value: {}, -}; - -export const ProjectTypeColumn = ({ className, value }) => ( -
- {value.entryType.toLowerCase()} -
-); -ProjectTypeColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -ProjectTypeColumn.defaultProps = { - value: {}, -}; - -export const OrganizationColumn = ({ className, value }) => ( -
- {value.organization} -
-); -OrganizationColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -OrganizationColumn.defaultProps = { - value: {}, -}; - -export const MembersColumn = ({ className, value }) => ( -
{value.usersQuantity}
-); -MembersColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -MembersColumn.defaultProps = { - value: {}, -}; - -export const LaunchesColumn = ({ className, value }) => ( -
{value.launchesQuantity}
-); -LaunchesColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -LaunchesColumn.defaultProps = { - value: {}, -}; - -export const LastLaunchColumn = ({ className, value }) => ( -
- {value.lastRun ? ( - - ) : ( - - )} -
-); -LastLaunchColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -LastLaunchColumn.defaultProps = { - value: {}, -}; - -export const MenuColumn = ({ className, value }) => ( -
- -
-); -MenuColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -MenuColumn.defaultProps = { - value: {}, -}; - -export const StatisticColumn = ({ className, value }) => ( -
- -
-); -StatisticColumn.propTypes = { - className: PropTypes.string.isRequired, - value: PropTypes.object, -}; -StatisticColumn.defaultProps = { - value: {}, -}; diff --git a/app/src/pages/instance/projectsPage/projectsPage.jsx b/app/src/pages/instance/projectsPage/projectsPage.jsx deleted file mode 100644 index 346015091f..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPage.jsx +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2019 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, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { injectIntl } from 'react-intl'; -import classNames from 'classnames/bind'; -import track from 'react-tracking'; -import { ADMIN_PROJECTS_PAGE_EVENTS, ADMIN_PROJECTS_PAGE } from 'components/main/analytics/events'; -import { PageLayout, PageHeader, PageSection } from 'layouts/pageLayout'; -import { - PROJECTS_PAGE, - PROJECT_DETAILS_PAGE, - projectSectionSelector, - urlProjectSlugSelector, - urlOrganizationSlugSelector, -} from 'controllers/pages'; -import { showModalAction } from 'controllers/modal'; -import { MEMBERS, MONITORING } from 'common/constants/projectSections'; -import { GhostButton } from 'components/buttons/ghostButton'; -import AddProjectIcon from 'common/img/add-project-inline.svg'; -import ProjectUsersIcon from 'common/img/project-users-inline.svg'; -import ProjectMonitoringIcon from 'common/img/project-monitoring-inline.svg'; -import { MembersPage } from 'pages/common/membersPage'; -import { addProjectAction, navigateToProjectSectionAction } from 'controllers/instance/projects'; -import { projectNameSelector, projectKeySelector } from 'controllers/project'; -import { ProjectStatusPage } from '../projectStatusPage'; -import { ProjectEventsPage } from '../projectEventsPage'; -import { Projects } from './projects'; -import { messages } from './messages'; -import styles from './projectsPage.scss'; - -const cx = classNames.bind(styles); - -const HEADER_BUTTONS = [ - { - key: MONITORING, - icon: ProjectMonitoringIcon, - }, - { - key: MEMBERS, - icon: ProjectUsersIcon, - }, -]; - -@connect( - (state) => ({ - projectSlug: urlProjectSlugSelector(state), - organizationSlug: urlOrganizationSlugSelector(state), - projectName: projectNameSelector(state), - projectKey: projectKeySelector(state), - section: projectSectionSelector(state), - }), - { - addProject: addProjectAction, - showModal: showModalAction, - navigateToSection: navigateToProjectSectionAction, - }, -) -@injectIntl -@track({ page: ADMIN_PROJECTS_PAGE }) -export class ProjectsPage extends Component { - static propTypes = { - intl: PropTypes.object.isRequired, - navigateToSection: PropTypes.func.isRequired, - addProject: PropTypes.func.isRequired, - showModal: PropTypes.func.isRequired, - section: PropTypes.string, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - projectName: PropTypes.string.isRequired, - projectKey: PropTypes.string.isRequired, - organizationSlug: PropTypes.string.isRequired, - projectSlug: PropTypes.string.isRequired, - }; - - static defaultProps = { - section: undefined, - }; - - onHeaderButtonClick = (section) => () => { - this.props.tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.HEADER_BUTTON_CLICK(section)); - this.props.navigateToSection( - { - organizationSlug: this.props.organizationSlug, - projectSlug: this.props.projectSlug, - }, - section, - ); - }; - - getBreadcrumbs = () => { - const { - intl: { formatMessage }, - section, - organizationSlug, - projectSlug, - projectName, - } = this.props; - - const breadcrumbs = [ - { - title: formatMessage(messages.pageTitle), - link: { - type: PROJECTS_PAGE, - }, - }, - ]; - - if (projectSlug) { - breadcrumbs.push({ - title: projectName, - link: { - type: PROJECT_DETAILS_PAGE, - payload: { projectSlug, projectSection: null, organizationSlug }, - }, - }); - } - - if (section) { - breadcrumbs.push({ - title: formatMessage(messages[section]), - }); - } - - return breadcrumbs; - }; - - showAddProjectModal = () => { - this.props.tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.ADD_PROJECT_BTN); - this.props.showModal({ - id: 'addProjectModal', - data: { - onSubmit: (values) => this.props.addProject(values.projectName), - eventsInfo: { - addBtn: ADMIN_PROJECTS_PAGE_EVENTS.ADD_BTN_ADD_PROJECT_MODAL, - closeIcon: ADMIN_PROJECTS_PAGE_EVENTS.CLOSE_ICON_ADD_PROJECT_MODAL, - cancelBtn: ADMIN_PROJECTS_PAGE_EVENTS.CANCEL_BTN_ADD_PROJECT_MODAL, - }, - }, - }); - }; - - renderHeaderButtons = () => { - const { - intl: { formatMessage }, - projectSlug, - section, - } = this.props; - - if (!projectSlug) { - return ( -
- - {formatMessage(messages.addProject)} - -
- ); - } - - return ( -
- {HEADER_BUTTONS.map(({ key, icon }) => ( - - {formatMessage(messages[`${key}HeaderButton`])} - - ))} -
- ); - }; - - renderSection = () => { - const { projectSlug, projectKey, section } = this.props; - - if (!projectSlug) { - return ; - } - - switch (section) { - case MEMBERS: - return ; - case MONITORING: - return ; - default: - return ; - } - }; - - render() { - return ( - - {this.renderHeaderButtons()} - {this.renderSection()} - - ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsPage.scss b/app/src/pages/instance/projectsPage/projectsPage.scss deleted file mode 100644 index e5e5bf5ab2..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPage.scss +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.header-buttons { - white-space: nowrap; - button { - margin-left: 10px; - } -} - -.mobile-hide { - @media (max-width: $SCREEN_XS_MAX) { - display: none; - } -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/index.js b/app/src/pages/instance/projectsPage/projectsPanelView/index.js deleted file mode 100644 index 92e4e9f0fa..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2019 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 { ProjectsPanelView } from './projectsPanelView'; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/constants.js b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/constants.js deleted file mode 100644 index 48543e99e7..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/constants.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2019 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 const UPSA_PROJECT = 'UPSA'; -export const PERSONAL_PROJECT = 'PERSONAL'; -export const INTERNAL_PROJECT = 'INTERNAL'; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/index.js b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/index.js deleted file mode 100644 index 967490054c..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/index.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2019 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 { ProjectPanel } from './projectPanel'; -export { ConfirmationModal } from 'pages/common/modals/confirmationModal'; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.jsx b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.jsx deleted file mode 100644 index 9d2abc3501..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.jsx +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2019 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, { Component } from 'react'; -import track from 'react-tracking'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; -import { injectIntl } from 'react-intl'; -import classNames from 'classnames/bind'; -import PropTypes from 'prop-types'; -import { Icon } from 'components/main/icon/icon'; -import { getRelativeUnits } from 'common/utils/timeDateUtils'; -import { ProjectName } from 'pages/organization/organizationProjectsPage/projectsListTable/projectName'; -import { ProjectMenu } from '../../projectMenu'; -import { StatisticsItem } from './statisticsItem'; -import { ProjectTooltipIcon } from './projectTooltipIcon'; -import { ProjectStatisticButton } from '../../projectStatisticButton'; -import { UPSA_PROJECT, PERSONAL_PROJECT, INTERNAL_PROJECT } from './constants'; -import { messages } from '../../messages'; -import styles from './projectPanel.scss'; - -const cx = classNames.bind(styles); - -@injectIntl -@track() -export class ProjectPanel extends Component { - static propTypes = { - project: PropTypes.object.isRequired, - intl: PropTypes.object.isRequired, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - }; - - getProjectIcon() { - const { - intl, - project: { entryType }, - } = this.props; - switch (entryType) { - case UPSA_PROJECT: - return ( - - - - - - ); - case PERSONAL_PROJECT: - return ( - - - - - - ); - default: - return ( - - - - - - ); - } - } - getOrganization() { - const { - intl, - project: { entryType, organizationSlug }, - } = this.props; - return ( - organizationSlug || - (entryType === PERSONAL_PROJECT && intl.formatMessage(messages.personal)) || - (entryType === INTERNAL_PROJECT && intl.formatMessage(messages.internal)) - ); - } - - render() { - const { - project: { - projectSlug, - entryType, - lastRun, - launchesQuantity, - usersQuantity, - organizationSlug, - }, - intl, - } = this.props; - const { value: relativeTime, unit } = getRelativeUnits(new Date(lastRun).getTime()); - - const organization = this.getOrganization(); - return ( -
-
-
- {this.getProjectIcon(entryType)} - - {organization} - - - - -
-
- -
- {lastRun && ( -
- {intl.formatMessage(messages.lastLaunch, { - date: intl.formatRelativeTime(relativeTime, unit), - })} -
- )} -
-
-
-
- - - - this.props.tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.STATISTIC_ICON) - } - /> -
-
-
- ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.scss b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.scss deleted file mode 100644 index 8074c9a132..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectPanel.scss +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.container { - height: 160px; - width: 100%; - min-width: 200px; - border-radius: 4px; - box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.09); - background-color: $COLOR--white-two; -} - -.info-block { - padding: 15px; - text-align: left; - height: 62px; -} - -.header { - display: flex; - flex-wrap: nowrap; - line-height: 1; -} - -.header-item { - flex: 0 0 auto; -} - -.header-stretch-item { - flex: 1 1 auto; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.hr { - margin: 0px; - display: block; - height: 1px; - border: 0; - border-top: 1px solid $COLOR--gray-91; - padding: 0; -} - -.name { - display: block; - width: 100%; - font-family: $FONT-REGULAR; - font-size: 15px; - line-height: 1.5; - overflow: hidden; - text-overflow: ellipsis; -} - -.gray-text { - width: 100%; - height: 18px; - font-family: $FONT-REGULAR; - font-size: 12px; - line-height: 1.5; - color: $COLOR--gray-47; -} - -.statistic-items { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: flex-end; -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/index.js b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/index.js deleted file mode 100644 index 1a9f80ae02..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2019 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 { ProjectTooltipIcon } from './projectTooltipIcon'; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/projectTooltipIcon.jsx b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/projectTooltipIcon.jsx deleted file mode 100644 index fdfe41fdc2..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/projectTooltipIcon/projectTooltipIcon.jsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019 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 { Component } from 'react'; -import PropTypes from 'prop-types'; -import { withTooltip } from 'components/main/tooltips/tooltip'; -import { TextTooltip } from 'components/main/tooltips/textTooltip'; - -@withTooltip({ - TooltipComponent: TextTooltip, - data: { - width: 180, - noArrow: false, - }, -}) -export class ProjectTooltipIcon extends Component { - static propTypes = { - children: PropTypes.node, - }; - static defaultProps = { - children: '', - }; - - render() { - const { children } = this.props; - return
{children}
; - } -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/index.js b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/index.js deleted file mode 100644 index 7eea752653..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2019 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 { StatisticsItem } from './statisticsItem'; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.jsx b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.jsx deleted file mode 100644 index a094be2a8d..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.jsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019 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 PropTypes from 'prop-types'; -import classNames from 'classnames/bind'; -import styles from './statisticsItem.scss'; - -const cx = classNames.bind(styles); -export const StatisticsItem = ({ caption, value, emptyValueCaption }) => ( -
-
{caption}
- {value ? ( -
{value}
- ) : ( -
{emptyValueCaption}
- )} -
-); - -StatisticsItem.propTypes = { - caption: PropTypes.string.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - emptyValueCaption: PropTypes.string, -}; - -StatisticsItem.defaultProps = { - value: '0', - emptyValueCaption: '-', -}; diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.scss b/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.scss deleted file mode 100644 index a2618ccddf..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectPanel/statisticsItem/statisticsItem.scss +++ /dev/null @@ -1,37 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.caption { - font-family: $FONT-REGULAR; - font-size: 12px; - line-height: 1.5; - color: $COLOR--gray-47; -} - -.value { - font-family: $FONT-REGULAR; - font-size: 15px; - line-height: 18px; - color: $COLOR--black-2; - height: 19px; -} - -.empty-value { - font-family: $FONT-REGULAR; - font-size: 12px; - line-height: 18px; - color: $COLOR--black-2; -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.jsx b/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.jsx deleted file mode 100644 index c3958656e4..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.jsx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2019 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, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames/bind'; -import { connect } from 'react-redux'; -import { loadingSelector, projectsSelector } from 'controllers/instance/projects'; -import { organizationsListSelector } from 'controllers/organizations'; -import { SpinningPreloader } from 'components/preloaders/spinningPreloader'; -import { ProjectPanel } from './projectPanel'; -import styles from './projectsPanelView.scss'; - -const cx = classNames.bind(styles); - -@connect((state) => ({ - organizations: organizationsListSelector(state), - projects: projectsSelector(state), - loading: loadingSelector(state), -})) -export class ProjectsPanelView extends Component { - static propTypes = { - organizations: PropTypes.array, - projects: PropTypes.array, - loading: PropTypes.bool, - onMembers: PropTypes.func, - onSettings: PropTypes.func, - onAssign: PropTypes.func, - onUnassign: PropTypes.func, - onDelete: PropTypes.func, - }; - - static defaultProps = { - organizations: [], - projects: [], - loading: false, - onMembers: () => {}, - onSettings: () => {}, - onAssign: () => {}, - onUnassign: () => {}, - onDelete: () => {}, - }; - - getPanelList = (projects) => { - const { organizations } = this.props; - - return projects.map((project) => { - const { organizationId } = project; - - const organizationSlug = organizations.find(({ id }) => id === Number(organizationId))?.slug; - const projectWithOrganizationSlug = { - ...project, - organizationSlug, - }; - - return ( -
- -
- ); - }); - }; - - render() { - const { projects, loading } = this.props; - return ( - - {loading && ( -
- -
- )} - {!loading && - (projects.length ? ( -
{this.getPanelList(projects)}
- ) : ( - '' - ))} -
- ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.scss b/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.scss deleted file mode 100644 index 5fed59b1b3..0000000000 --- a/app/src/pages/instance/projectsPage/projectsPanelView/projectsPanelView.scss +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.panel-list { - height: 100%; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); - grid-gap: 30px 30px; - flex-wrap: wrap; - background-color: $COLOR--white-two; - padding: 15px; -} diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/index.js b/app/src/pages/instance/projectsPage/projectsToolbar/index.js deleted file mode 100644 index d8d2e3de2f..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2019 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 { ProjectsToolbar } from './projectsToolbar'; diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectEntities.jsx b/app/src/pages/instance/projectsPage/projectsToolbar/projectEntities.jsx deleted file mode 100644 index ee0f1e88ea..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectEntities.jsx +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2019 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 { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, defineMessages } from 'react-intl'; -import { commonValidators } from 'common/utils/validation'; -import { - EntityContains, - EntityInputConditional, - EntityDropdown, - EntityItemStartTime, -} from 'components/filterEntities'; -import { - CONDITION_CNT, - CONDITION_EQ, - CONDITION_BETWEEN, - CONDITION_GREATER_EQ, - CONDITION_LESS_EQ, - CONDITION_IN, -} from 'components/filterEntities/constants'; -import { bindDefaultValue } from 'components/filterEntities/utils'; -import { - PROJECT_TYPE_INTERNAL, - PROJECT_TYPE_PERSONAL, - PROJECT_TYPE_UPSA, - TYPE, - NAME, - ORGANIZATION, - USERS_QUANTITY, - LAST_RUN, - LAUNCHES_QUANTITY, - PROJECTS, -} from 'common/constants/projectsObjectTypes'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; - -const messages = defineMessages({ - contains: { id: 'projectsGrid.contains', defaultMessage: 'Contains' }, - type: { id: 'projectsGrid.type', defaultMessage: 'Type' }, - lastRun: { - id: 'projectsGrid.lastRun', - defaultMessage: 'Last run', - }, - numberOfLaunches: { - id: 'projectsGrid.numberOfLaunches', - defaultMessage: 'Launches', - }, - numberOfMembers: { - id: 'projectsGrid.numberOfMembers', - defaultMessage: 'Members', - }, - organizationName: { - id: 'projectsGrid.organizationName', - defaultMessage: 'Organization ', - }, - projectTypeInternal: { - id: 'projectsGrid.projectTypeInternal', - defaultMessage: 'Internal', - }, - projectName: { - id: 'projectsGrid.projectName', - defaultMessage: 'Project', - }, - projectTypePersonal: { - id: 'projectsGrid.projectTypePersonal', - defaultMessage: 'Personal', - }, - projectTypeUpsa: { - id: 'projectsGrid.projectTypeUpsa', - defaultMessage: 'UPSA', - }, -}); - -@injectIntl -export class ProjectEntities extends Component { - static propTypes = { - intl: PropTypes.object.isRequired, - filterValues: PropTypes.object, - render: PropTypes.func.isRequired, - }; - - static defaultProps = { - filterValues: {}, - }; - getEntities = () => { - const { intl } = this.props; - return [ - { - id: PROJECTS, - component: EntityContains, - value: this.bindDefaultValue(PROJECTS), - title: intl.formatMessage(messages.contains), - active: true, - removable: false, - }, - { - id: TYPE, - component: EntityDropdown, - value: this.bindDefaultValue(TYPE, { - condition: CONDITION_IN, - }), - title: intl.formatMessage(messages.type), - active: true, - removable: false, - customProps: { - options: [ - { - label: intl.formatMessage(messages.projectTypeInternal), - value: PROJECT_TYPE_INTERNAL, - }, - { - label: intl.formatMessage(messages.projectTypePersonal), - value: PROJECT_TYPE_PERSONAL, - }, - { - label: intl.formatMessage(messages.projectTypeUpsa), - value: PROJECT_TYPE_UPSA, - }, - ], - multiple: true, - selectAll: true, - }, - }, - { - id: LAST_RUN, - component: EntityItemStartTime, - value: this.bindDefaultValue(LAST_RUN, { - value: '', - condition: CONDITION_BETWEEN, - }), - title: intl.formatMessage(messages.lastRun), - active: true, - removable: false, - customProps: { - withoutDynamic: true, - events: ADMIN_PROJECTS_PAGE_EVENTS.REFINE_FILTERS_PANEL_EVENTS.commonEvents, - }, - }, - { - id: LAUNCHES_QUANTITY, - component: EntityInputConditional, - value: this.bindDefaultValue(LAUNCHES_QUANTITY, { - condition: CONDITION_GREATER_EQ, - }), - validationFunc: commonValidators.launchNumericEntity, - title: intl.formatMessage(messages.numberOfLaunches), - active: true, - removable: false, - customProps: { - conditions: [CONDITION_EQ, CONDITION_GREATER_EQ, CONDITION_LESS_EQ], - placeholder: null, - maxLength: 18, - }, - }, - { - id: USERS_QUANTITY, - component: EntityInputConditional, - value: this.bindDefaultValue(USERS_QUANTITY, { - condition: CONDITION_GREATER_EQ, - }), - validationFunc: commonValidators.launchNumericEntity, - title: intl.formatMessage(messages.numberOfMembers), - active: true, - removable: false, - customProps: { - conditions: [CONDITION_EQ, CONDITION_GREATER_EQ, CONDITION_LESS_EQ], - placeholder: null, - maxLength: 18, - }, - }, - { - id: NAME, - component: EntityInputConditional, - value: this.bindDefaultValue(NAME, { - condition: CONDITION_CNT, - }), - title: intl.formatMessage(messages.projectName), - active: true, - removable: false, - customProps: { - placeholder: null, - maxLength: 256, - }, - }, - { - id: ORGANIZATION, - component: EntityInputConditional, - value: this.bindDefaultValue(ORGANIZATION, { - condition: CONDITION_CNT, - }), - title: intl.formatMessage(messages.organizationName), - active: true, - removable: false, - customProps: { - placeholder: null, - maxLength: 256, - }, - }, - ]; - }; - bindDefaultValue = bindDefaultValue; - render() { - const { render, ...rest } = this.props; - return render({ - ...rest, - filterEntities: this.getEntities(), - }); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/index.js b/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/index.js deleted file mode 100644 index f3547dfc69..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2019 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 { ProjectsSorting } from './projectsSorting'; diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.jsx b/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.jsx deleted file mode 100644 index b94f4bebe6..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectsSorting/projectsSorting.jsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2019 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, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl } from 'react-intl'; -import classNames from 'classnames/bind'; -import { SORTING_ASC } from 'controllers/sorting'; -import { InputDropdownSorting } from 'components/inputs/inputDropdownSorting'; -import { - CREATION_DATE, - NAME, - TYPE, - ORGANIZATION, - USERS_QUANTITY, - LAST_RUN, - LAUNCHES_QUANTITY, -} from 'common/constants/projectsObjectTypes'; -import styles from './projectsSorting.scss'; -import { messages } from '../../messages'; - -const cx = classNames.bind(styles); - -@injectIntl -export class ProjectsSorting extends Component { - static propTypes = { - intl: PropTypes.object, - sortingColumn: PropTypes.string, - sortingDirection: PropTypes.string, - onChangeSorting: PropTypes.func, - }; - - static defaultProps = { - intl: {}, - sortingColumn: null, - sortingDirection: SORTING_ASC, - onChangeSorting: () => {}, - }; - - getSortOptions = () => [ - { - value: CREATION_DATE, - label: this.props.intl.formatMessage(messages.dateCol), - disabled: false, - }, - { - value: NAME, - label: this.props.intl.formatMessage(messages.nameCol), - disabled: false, - }, - { - value: USERS_QUANTITY, - label: this.props.intl.formatMessage(messages.membersCol), - disabled: false, - }, - { - value: LAUNCHES_QUANTITY, - label: this.props.intl.formatMessage(messages.launchesCol), - disabled: false, - }, - { - value: LAST_RUN, - label: this.props.intl.formatMessage(messages.lastLaunchCol), - disabled: false, - }, - { - value: TYPE, - label: this.props.intl.formatMessage(messages.projectTypeCol), - disabled: false, - }, - { - value: ORGANIZATION, - label: this.props.intl.formatMessage(messages.organizationCol), - disabled: false, - }, - ]; - - handleChange = (field) => { - this.props.onChangeSorting(field); - }; - - render() { - const { intl, sortingColumn, sortingDirection } = this.props; - - return ( -
-
{intl.formatMessage(messages.sortBy)}:
- -
- ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.jsx b/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.jsx deleted file mode 100644 index 771ace7b88..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.jsx +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2019 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, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { FormattedMessage, injectIntl } from 'react-intl'; -import classNames from 'classnames/bind'; -import track from 'react-tracking'; -import { InputFilter } from 'components/inputs/inputFilter'; -import { FilterEntitiesURLContainer } from 'components/filterEntities/containers'; -import { GhostButton } from 'components/buttons/ghostButton'; -import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; - -import { - GRID_VIEW, - TABLE_VIEW, - startSetViewMode, - viewModeSelector, - selectedProjectsSelector, - fetchProjectsAction, - deleteItemsAction, - unselectAllProjectsAction, - querySelector, -} from 'controllers/instance/projects'; -import { showScreenLockAction, hideScreenLockAction } from 'controllers/screenLock'; -import { showNotification, NOTIFICATION_TYPES } from 'controllers/notification'; - -import ExportIcon from 'common/img/export-inline.svg'; -import GridViewDashboardIcon from 'common/img/grid-inline.svg'; -import TableViewDashboardIcon from 'common/img/table-inline.svg'; -import { COMMON_LOCALE_KEYS } from 'common/constants/localization'; -import { URLS } from 'common/urls'; -import { fetch } from 'common/utils'; -import { PROJECTS } from 'common/constants/projectsObjectTypes'; -import { collectFilterEntities } from 'components/filterEntities/containers/utils'; -import { downloadFile } from 'common/utils/downloadFile'; -import { messages } from '../messages'; -import { ProjectEntities } from './projectEntities'; -import { ProjectsSorting } from './projectsSorting'; -import styles from './projectsToolbar.scss'; - -const cx = classNames.bind(styles); -@connect( - (state) => ({ - filterEntities: collectFilterEntities(querySelector(state)), - selectedProjects: selectedProjectsSelector(state), - viewMode: viewModeSelector(state), - }), - { - setViewMode: startSetViewMode, - deleteItemsAction, - showScreenLockAction, - hideScreenLockAction, - showNotification, - fetchProjectsAction, - unselectAllProjectsAction, - }, -) -@injectIntl -@track() -export class ProjectsToolbar extends Component { - static propTypes = { - intl: PropTypes.object.isRequired, - viewMode: PropTypes.string, - setViewMode: PropTypes.func.isRequired, - selectedProjects: PropTypes.arrayOf(PropTypes.object), - filterEntities: PropTypes.object, - deleteItemsAction: PropTypes.func.isRequired, - showScreenLockAction: PropTypes.func.isRequired, - hideScreenLockAction: PropTypes.func.isRequired, - showNotification: PropTypes.func.isRequired, - fetchProjectsAction: PropTypes.func.isRequired, - unselectAllProjectsAction: PropTypes.func.isRequired, - sortingColumn: PropTypes.string, - sortingDirection: PropTypes.string, - onChangeSorting: PropTypes.func, - tracking: PropTypes.shape({ - trackEvent: PropTypes.func, - getTrackingData: PropTypes.func, - }).isRequired, - }; - - static defaultProps = { - viewMode: GRID_VIEW, - selectedProjects: [], - filterEntities: {}, - sortingColumn: null, - sortingDirection: null, - onChangeSorting: () => {}, - }; - - onExportProjects = () => { - const { filterEntities, sortingColumn, sortingDirection, tracking } = this.props; - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.EXPORT_BTN); - downloadFile( - URLS.exportProjects( - filterEntities, - sortingColumn && - sortingDirection && { - 'page.sort': `${sortingColumn},${sortingDirection}`, - }, - ), - ); - }; - - getSelectedProjectsNames = () => - this.props.selectedProjects.map(({ projectName }) => `'${projectName}'`).join(', '); - - confirmDeleteItems = (projectsToDelete) => { - const ids = projectsToDelete.map((project) => project.id); - - this.props.showScreenLockAction(); - - fetch(URLS.project(ids), { - method: 'delete', - }) - .then(() => { - this.props.unselectAllProjectsAction(); - this.props.fetchProjectsAction(); - this.props.hideScreenLockAction(); - this.props.showNotification({ - message: - projectsToDelete.length === 1 - ? this.props.intl.formatMessage(messages.deleteSuccess) - : this.props.intl.formatMessage(messages.deleteSuccessMultiple), - type: NOTIFICATION_TYPES.SUCCESS, - }); - }) - .catch(() => { - this.props.hideScreenLockAction(); - this.props.showNotification({ - message: - projectsToDelete.length === 1 - ? this.props.intl.formatMessage(messages.deleteError) - : this.props.intl.formatMessage(messages.deleteErrorMultiple), - type: NOTIFICATION_TYPES.ERROR, - }); - }); - }; - - deleteProjects = () => { - const { selectedProjects, intl, tracking } = this.props; - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.DELETE_PROJECT_BTN); - - this.props.deleteItemsAction(selectedProjects, { - onConfirm: this.confirmDeleteItems, - header: - selectedProjects.length === 1 - ? intl.formatMessage(messages.deleteModalHeader) - : intl.formatMessage(messages.deleteModalMultipleHeader), - mainContent: - selectedProjects.length === 1 - ? intl.formatMessage(messages.deleteModalContent, { - name: this.getSelectedProjectsNames(), - }) - : intl.formatMessage(messages.deleteModalMultipleContent, { - names: this.getSelectedProjectsNames(), - }), - eventsInfo: { - closeIcon: ADMIN_PROJECTS_PAGE_EVENTS.CLOSE_ICON_DELETE_MODAL, - cancelBtn: ADMIN_PROJECTS_PAGE_EVENTS.CANCEL_BTN_DELETE_MODAL, - deleteBtn: ADMIN_PROJECTS_PAGE_EVENTS.DELETE_BTN_DELETE_MODAL, - }, - }); - }; - - render() { - const { - intl, - tracking, - viewMode, - setViewMode, - selectedProjects, - sortingColumn, - sortingDirection, - onChangeSorting, - } = this.props; - const selectedProjectsCount = selectedProjects.length; - - return ( -
-
- ( - - )} - /> -
-
- {selectedProjectsCount > 0 ? ( - -
- {intl.formatMessage(messages.deleteProjectsCount, { - count: selectedProjectsCount, - })} -
- -
- ) : ( - - {viewMode === GRID_VIEW && ( -
- -
- )} -
- - - -
-
- { - setViewMode(GRID_VIEW); - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.SET_TILE_VIEW); - }} - /> -
-
- { - setViewMode(TABLE_VIEW); - tracking.trackEvent(ADMIN_PROJECTS_PAGE_EVENTS.SET_TABLE_VIEW); - }} - /> -
-
- )} -
-
- ); - } -} diff --git a/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.scss b/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.scss deleted file mode 100644 index 862649e95c..0000000000 --- a/app/src/pages/instance/projectsPage/projectsToolbar/projectsToolbar.scss +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * Copyright 2019 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. - */ - -.toolbar { - padding: 10px 15px; - display: flex; - justify-content: space-between; - background-color: $COLOR--white-two; -} - -.search { - flex: 0 1 400px; - > :first-child { - width: 100%; - } - - @media (max-width: $SCREEN_XS_MAX) { - width: 100%; - } -} - -.buttons { - display: flex; - align-items: center; -} - -.toolbar-button { - margin-left: 10px; - - svg { - width: 20px; - height: 20px; - } -} - -.toolbar-active-button { - button { - background-color: $COLOR--topaz; - - path { - fill: $COLOR--white-two !important; - } - } -} - -.delete-info { - color: $COLOR--charcoal-grey; - margin-right: 20px; - font-size: 13px; -} - -.delete-button { - border: none; - background: none; - cursor: pointer; - font-family: $FONT-SEMIBOLD; - color: $COLOR--topaz; - font-size: 13px; - outline: none; - &:focus { - text-decoration: underline; - } -} - -.mobile-hide { - @media (max-width: $SCREEN_XS_MAX) { - display: none; - } -} diff --git a/app/src/pages/organization/organizationProjectsPage/organizationProjectsPage.jsx b/app/src/pages/organization/organizationProjectsPage/organizationProjectsPage.jsx index 94265d35b5..2cc2f9f8ef 100644 --- a/app/src/pages/organization/organizationProjectsPage/organizationProjectsPage.jsx +++ b/app/src/pages/organization/organizationProjectsPage/organizationProjectsPage.jsx @@ -20,16 +20,16 @@ import classNames from 'classnames/bind'; import Parser from 'html-react-parser'; import { BubblesLoader, PlusIcon } from '@reportportal/ui-kit'; import { useIntl } from 'react-intl'; -import { loadingSelector, projectsSelector } from 'controllers/organizations/projects/selectors'; -import { activeOrganizationLoadingSelector } from 'controllers/organizations/organization/selectors'; +import { loadingSelector, projectsSelector } from 'controllers/organization/projects/selectors'; +import { activeOrganizationLoadingSelector } from 'controllers/organization/selectors'; import { ScrollWrapper } from 'components/main/scrollWrapper'; import { userRolesSelector } from 'controllers/pages'; import { showModalAction } from 'controllers/modal'; -import { createProjectAction } from 'controllers/organizations/projects/actionCreators'; +import { createProjectAction } from 'controllers/organization/projects/actionCreators'; import { useState } from 'react'; import { COMMON_LOCALE_KEYS } from 'common/constants/localization'; +import { EmptyPageState } from 'pages/common/emptyPageState'; import { ProjectsPageHeader } from './projectsPageHeader'; -import { EmptyPageState } from '../emptyPageState'; import EmptyIcon from './img/empty-projects-icon-inline.svg'; import NoResultsIcon from './img/no-results-icon-inline.svg'; import { messages } from './messages'; diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectName/projectName.jsx b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectName/projectName.jsx index 2242bb5853..c044b8e789 100644 --- a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectName/projectName.jsx +++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectName/projectName.jsx @@ -21,7 +21,7 @@ import Link from 'redux-first-router-link'; import { useDispatch, useSelector } from 'react-redux'; import { ADMIN_PROJECTS_PAGE_EVENTS } from 'components/main/analytics/events'; import { SCREEN_XS_MAX_MEDIA } from 'common/constants/screenSizeVariables'; -import { navigateToProjectAction } from 'controllers/organizations/projects'; +import { navigateToProjectAction } from 'controllers/organization/projects'; import { setActiveProjectKeyAction } from 'controllers/user'; import { userAssignedSelector, PROJECT_PAGE } from 'controllers/pages'; import styles from './projectName.scss'; diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx index 7ef3928e65..905ddde36a 100644 --- a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx +++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx @@ -20,21 +20,21 @@ import { useIntl } from 'react-intl'; import { MeatballMenuIcon, Popover, Table } from '@reportportal/ui-kit'; import classNames from 'classnames/bind'; import { useDispatch, useSelector } from 'react-redux'; +import { AbsRelTime } from 'components/main/absRelTime'; +import { SORTING_ASC, withSortingURL } from 'controllers/sorting'; +import { DEFAULT_PAGINATION, PAGE_KEY, withPagination } from 'controllers/pagination'; +import { PaginationWrapper } from 'components/main/paginationWrapper'; import { activeOrganizationSelector, prepareActiveOrganizationProjectsAction, -} from 'controllers/organizations/organization'; -import { AbsRelTime } from 'components/main/absRelTime'; -import { projectsPaginationSelector } from 'controllers/organizations/projects/selectors'; -import { SORTING_ASC, withSortingURL } from 'controllers/sorting'; +} from 'controllers/organization'; import { - DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_LIMITATION, + DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_SORT_COLUMN, + projectsPaginationSelector, SORTING_KEY, -} from 'controllers/organizations/projects/constants'; -import { DEFAULT_PAGINATION, PAGE_KEY, withPagination } from 'controllers/pagination'; -import { PaginationWrapper } from 'components/main/paginationWrapper'; +} from 'controllers/organization/projects'; import { messages } from '../messages'; import { ProjectName } from './projectName'; import styles from './projectsListTable.scss'; diff --git a/app/src/pages/organization/organizationProjectsPage/projectsPageHeader/projectsPageHeader.jsx b/app/src/pages/organization/organizationProjectsPage/projectsPageHeader/projectsPageHeader.jsx index 9b661e556c..f85f4f871f 100644 --- a/app/src/pages/organization/organizationProjectsPage/projectsPageHeader/projectsPageHeader.jsx +++ b/app/src/pages/organization/organizationProjectsPage/projectsPageHeader/projectsPageHeader.jsx @@ -20,13 +20,13 @@ import { useSelector } from 'react-redux'; import Parser from 'html-react-parser'; import { Button, PlusIcon, FieldText } from '@reportportal/ui-kit'; import classNames from 'classnames/bind'; -import { PROJECTS_PAGE } from 'controllers/pages'; +import { ORGANIZATIONS_PAGE } from 'controllers/pages'; import searchIcon from 'common/img/newIcons/search-outline-inline.svg'; import filterIcon from 'common/img/newIcons/filters-outline-inline.svg'; import { Breadcrumbs } from 'componentLibrary/breadcrumbs'; -import { activeOrganizationSelector } from 'controllers/organizations/organization'; -import { SEARCH_KEY } from 'controllers/organizations/projects/constants'; -import { loadingSelector } from 'controllers/organizations/projects'; +import { activeOrganizationSelector } from 'controllers/organization'; +import { SEARCH_KEY } from 'controllers/organization/projects/constants'; +import { loadingSelector } from 'controllers/organization/projects'; import { useEffect, useState } from 'react'; import { withFilter } from 'controllers/filter'; import projectsIcon from './img/projects-inline.svg'; @@ -56,7 +56,7 @@ export const ProjectsPageHeaderWrapped = ({ const breadcrumbs = [ { title: formatMessage(messages.allOrganizations), - link: { type: PROJECTS_PAGE }, + link: { type: ORGANIZATIONS_PAGE }, }, { title: organizationName, diff --git a/app/src/pages/organization/projectTeamPage/projectTeamPage.jsx b/app/src/pages/organization/projectTeamPage/projectTeamPage.jsx index bf0dad265a..51c0ace9a0 100644 --- a/app/src/pages/organization/projectTeamPage/projectTeamPage.jsx +++ b/app/src/pages/organization/projectTeamPage/projectTeamPage.jsx @@ -23,8 +23,8 @@ import { useIntl } from 'react-intl'; import { BubblesLoader } from '@reportportal/ui-kit'; import { ScrollWrapper } from 'components/main/scrollWrapper'; import { showModalAction } from 'controllers/modal'; +import { EmptyPageState } from 'pages/common/emptyPageState'; import { ProjectTeamPageHeader } from './projectTeamPageHeader'; -import { EmptyPageState } from '../emptyPageState'; import { ProjectTeamListTable } from './projectTeamListTable'; import EmptyIcon from './img/empty-members-icon-inline.svg'; import { messages } from './messages'; diff --git a/app/src/routes/constants.js b/app/src/routes/constants.js index dfc8ed5910..33f5e3ffa0 100644 --- a/app/src/routes/constants.js +++ b/app/src/routes/constants.js @@ -17,7 +17,6 @@ import { NOT_FOUND } from 'redux-first-router'; import { EmptyLayout } from 'layouts/emptyLayout'; -import { ProjectsPage } from 'pages/instance/projectsPage'; import { AllUsersPage } from 'pages/instance/allUsersPage'; import { ServerSettingsPage } from 'pages/instance/serverSettingsPage'; import { PluginsPage } from 'pages/instance/pluginsPage'; @@ -44,7 +43,6 @@ import { PROJECT_USERDEBUG_LOG_PAGE, LAUNCHES_PAGE, HISTORY_PAGE, - PROJECT_DETAILS_PAGE, OAUTH_SUCCESS, PLUGIN_UI_EXTENSION_ADMIN_PAGE, PROJECT_PLUGIN_PAGE, @@ -65,6 +63,7 @@ import { ProjectTeamPage } from 'pages/organization/projectTeamPage'; import { ProjectLayout } from 'layouts/projectLayout'; import { OrganizationLayout } from 'layouts/organizationLayout'; import { InstanceLayout } from 'layouts/instanceLayout'; +import { OrganizationsPage } from 'pages/instance/organizationsPage'; export const ANONYMOUS_ACCESS = 'anonymous'; export const ADMIN_ACCESS = 'admin'; @@ -90,6 +89,11 @@ export const pageRendering = { [USER_PROFILE_PAGE_PROJECT_LEVEL]: { component: ProfilePage, layout: ProjectLayout }, [USER_PROFILE_SUB_PAGE_PROJECT_LEVEL]: { component: ProfilePage, layout: ProjectLayout }, API_PAGE: { component: ApiPage, layout: ProjectLayout }, + ORGANIZATIONS_PAGE: { + component: OrganizationsPage, + layout: InstanceLayout, + rawContent: true, + }, ORGANIZATION_PROJECTS_PAGE: { component: OrganizationProjectsPage, layout: OrganizationLayout, @@ -115,16 +119,6 @@ export const pageRendering = { }, PROJECT_USERDEBUG_PAGE: { component: LaunchesPage, layout: ProjectLayout }, PROJECT_USERDEBUG_TEST_ITEM_PAGE: { component: TestItemPage, layout: ProjectLayout }, - PROJECTS_PAGE: { - component: ProjectsPage, - layout: InstanceLayout, - rawContent: true, - }, - [PROJECT_DETAILS_PAGE]: { - component: ProjectsPage, - layout: InstanceLayout, - rawContent: true, - }, ALL_USERS_PAGE: { component: AllUsersPage, layout: InstanceLayout, diff --git a/app/src/routes/routesMap.js b/app/src/routes/routesMap.js index a31150077b..7d882453bc 100644 --- a/app/src/routes/routesMap.js +++ b/app/src/routes/routesMap.js @@ -37,8 +37,6 @@ import { PROJECT_USERDEBUG_PAGE, HISTORY_PAGE, UNIQUE_ERRORS_PAGE, - PROJECTS_PAGE, - PROJECT_DETAILS_PAGE, ALL_USERS_PAGE, SERVER_SETTINGS_PAGE, SERVER_SETTINGS_TAB_PAGE, @@ -66,10 +64,10 @@ import { USER_PROFILE_SUB_PAGE, USER_PROFILE_SUB_PAGE_ORGANIZATION_LEVEL, USER_PROFILE_SUB_PAGE_PROJECT_LEVEL, + ORGANIZATIONS_PAGE, } from 'controllers/pages'; import { GENERAL, AUTHORIZATION_CONFIGURATION, ANALYTICS } from 'common/constants/settingsTabs'; import { INSTALLED, STORE } from 'common/constants/pluginsTabs'; -import { MEMBERS, MONITORING } from 'common/constants/projectSections'; import { ANONYMOUS_REDIRECT_PATH_STORAGE_KEY, isAuthorizedSelector } from 'controllers/auth'; import { fetchDashboardsAction, @@ -86,13 +84,9 @@ import { fetchPluginsAction, fetchGlobalIntegrationsAction } from 'controllers/p import { fetchTestItemsAction, setLevelAction } from 'controllers/testItem'; import { fetchFiltersPageAction } from 'controllers/filter'; import { fetchMembersAction } from 'controllers/members'; -import { fetchProjectDataAction } from 'controllers/instance'; import { fetchAllUsersAction } from 'controllers/instance/allUsers/actionCreators'; import { fetchLogPageData } from 'controllers/log'; import { fetchHistoryPageInfoAction } from 'controllers/itemsHistory'; -import { fetchProjectsAction } from 'controllers/instance/projects'; -import { fetchOrganizationsAction } from 'controllers/organizations'; -import { startSetViewMode } from 'controllers/instance/projects/actionCreators'; import { setSessionItem, updateStorageItem } from 'common/utils/storageUtils'; import { fetchClustersAction } from 'controllers/uniqueErrors'; import { @@ -101,10 +95,11 @@ import { PROJECT_ASSIGNMENT_ROUTE, } from 'common/constants/userProfileRoutes'; import { parseQueryToFilterEntityAction } from 'controllers/filter/actionCreators'; +import { fetchOrganizationsAction } from 'controllers/instance/organizations'; import { fetchOrganizationBySlugAction, prepareActiveOrganizationProjectsAction, -} from 'controllers/organizations/organization/actionCreators'; +} from 'controllers/organization'; import { pageRendering, ANONYMOUS_ACCESS, ADMIN_ACCESS } from './constants'; const redirectRoute = (path, createNewAction, onRedirect = () => {}) => ({ @@ -155,20 +150,6 @@ const routesMap = { API_PAGE: '/api', - [PROJECTS_PAGE]: { - path: '/projects', - thunk: (dispatch) => { - dispatch(fetchProjectsAction()); - dispatch(fetchOrganizationsAction()); - dispatch(startSetViewMode()); - }, - }, - [PROJECT_DETAILS_PAGE]: { - path: `/organizations/projects/organizations/:organizationSlug/projects/:projectSlug/:projectSection(${MEMBERS}|${MONITORING})?`, - thunk: (dispatch) => { - dispatch(fetchProjectDataAction()); - }, - }, [ALL_USERS_PAGE]: { path: '/users', thunk: (dispatch) => dispatch(fetchAllUsersAction()), @@ -191,6 +172,13 @@ const routesMap = { ), [PLUGINS_TAB_PAGE]: `/plugins/:pluginsTab(${INSTALLED}|${STORE})`, + [ORGANIZATIONS_PAGE]: { + path: '/organizations', + thunk: (dispatch) => { + dispatch(fetchOrganizationsAction()); + }, + }, + [ORGANIZATION_PROJECTS_PAGE]: { path: '/organizations/:organizationSlug/projects', thunk: (dispatch, getState) => { diff --git a/app/src/store/reducers.js b/app/src/store/reducers.js index ac41f1abbf..62e6516228 100644 --- a/app/src/store/reducers.js +++ b/app/src/store/reducers.js @@ -37,7 +37,7 @@ import { instanceReducer } from 'controllers/instance'; import { pluginsReducer } from 'controllers/plugins'; import { initialDataReadyReducer } from 'controllers/initialData'; import { uniqueErrorsReducer } from 'controllers/uniqueErrors'; -import { organizationsReducer } from 'controllers/organizations'; +import { organizationsReducer } from 'controllers/instance/organizations'; export default { appInfo: appInfoReducer, diff --git a/app/src/store/rootSaga.js b/app/src/store/rootSaga.js index e071334974..70e84c979e 100644 --- a/app/src/store/rootSaga.js +++ b/app/src/store/rootSaga.js @@ -30,12 +30,12 @@ import { historySagas } from 'controllers/itemsHistory'; import { logSagas } from 'controllers/log'; import { instanceSagas } from 'controllers/instance'; import { userSagas } from 'controllers/user'; -import { organizationsSagas } from 'controllers/organizations'; import { projectSagas } from 'controllers/project'; import { initialDataSagas } from 'controllers/initialData'; import { pageSagas } from 'controllers/pages'; import { pluginSagas } from 'controllers/plugins'; import { uniqueErrorsSagas } from 'controllers/uniqueErrors'; +import { organizationsSagas } from 'controllers/instance/organizations'; const sagas = [ notificationSagas,