+
+
+
+
+
+
+ );
+};
+
+DeleteProjectModal.propTypes = {
+ data: PropTypes.object,
+ handleSubmit: PropTypes.func,
+};
+
+export default withModal('deleteProjectModal')(
+ reduxForm({
+ form: DELETE_PROJECT_FORM,
+ validate: ({ projectName: inputProjectValue }, { data: { projectDetails } }) => {
+ return {
+ projectName: commonValidators.createKeywordMatcherValidator(projectDetails.projectName)(
+ inputProjectValue,
+ ),
+ };
+ },
+ })(DeleteProjectModal),
+);
diff --git a/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/deleteProjectModal.scss b/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/deleteProjectModal.scss
new file mode 100644
index 0000000000..e1a300694b
--- /dev/null
+++ b/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/deleteProjectModal.scss
@@ -0,0 +1,19 @@
+/*!
+ * 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.
+ */
+
+.message{
+ margin-bottom: 16px;
+}
\ No newline at end of file
diff --git a/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/index.js b/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/index.js
new file mode 100644
index 0000000000..5fff546a18
--- /dev/null
+++ b/app/src/pages/organization/organizationProjectsPage/modals/deleteProjectModal/index.js
@@ -0,0 +1,17 @@
+/*!
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { DeleteProjectModal } from './deleteProjectModal';
diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/index.js b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/index.js
new file mode 100644
index 0000000000..9311928e9d
--- /dev/null
+++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/index.js
@@ -0,0 +1,18 @@
+/*!
+ * Copyright 2024 EPAM Systems
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { ProjectActionMenu } from './projectActionMenu';
+export { DeleteProjectModal } from '../../modals/deleteProjectModal';
diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.jsx b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.jsx
new file mode 100644
index 0000000000..9c80d6fff0
--- /dev/null
+++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.jsx
@@ -0,0 +1,133 @@
+/*!
+ * 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 { MeatballMenuIcon, Popover } from '@reportportal/ui-kit';
+import classNames from 'classnames/bind';
+import Link from 'redux-first-router-link';
+import { useDispatch, useSelector } from 'react-redux';
+import React, { useMemo } from 'react';
+import PropTypes from 'prop-types';
+import { setActiveProjectKeyAction } from 'controllers/user';
+import {
+ canDeleteProject,
+ canInviteUserToProject,
+ canRenameProject,
+} from 'common/utils/permissions/permissions';
+import { userRolesSelector } from 'controllers/pages';
+import { showModalAction } from 'controllers/modal';
+import { deleteProjectAction } from 'controllers/organization/projects/actionCreators';
+import { useIntl } from 'react-intl';
+import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
+import { messages } from '../../messages';
+import styles from './projectActionMenu.scss';
+
+const cx = classNames.bind(styles);
+
+export const ProjectActionMenu = ({ details }) => {
+ const { projectName, projectKey, projectId, projectSlug, organizationSlug } = details;
+ const userRoles = useSelector(userRolesSelector);
+ const dispatch = useDispatch();
+ const { formatMessage } = useIntl();
+ const actionsButtons = useMemo(() => {
+ return [
+ {
+ actionLabel: formatMessage(COMMON_LOCALE_KEYS.RENAME),
+ onclick: () => {},
+ hasPermission: canRenameProject(userRoles),
+ },
+ {
+ actionLabel: formatMessage(messages.actionInviteUser),
+ onclick: () => {},
+ hasPermission: canInviteUserToProject(userRoles),
+ },
+ {
+ actionLabel: formatMessage(messages.actionUnassign),
+ onclick: () => {},
+ hasPermission: true,
+ },
+ {
+ actionLabel: formatMessage(COMMON_LOCALE_KEYS.DELETE),
+ onclick: () => {
+ dispatch(
+ showModalAction({
+ id: 'deleteProjectModal',
+ data: {
+ projectDetails: details,
+ onSave: () => {
+ dispatch(deleteProjectAction({ projectName, projectId }));
+ },
+ },
+ }),
+ );
+ },
+ className: cx('delete-button'),
+ hasPermission: canDeleteProject(userRoles),
+ },
+ ];
+ }, [userRoles]);
+
+ return (
+
+ dispatch(setActiveProjectKeyAction(projectKey))}
+ >
+ Settings
+
+ dispatch(setActiveProjectKeyAction(projectKey))}
+ >
+ Team
+
+
+ {actionsButtons.map(
+ (button) =>
+ button.hasPermission && (
+
+ ),
+ )}
+
+ }
+ className={cx('actions-popover')}
+ >
+
+
+
+
+ );
+};
+
+ProjectActionMenu.propTypes = {
+ details: PropTypes.object,
+};
diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.scss b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.scss
new file mode 100644
index 0000000000..7d6f13287d
--- /dev/null
+++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu/projectActionMenu.scss
@@ -0,0 +1,53 @@
+/*!
+ * 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.
+ */
+.actions-popover {
+ padding: 8px 0;
+}
+
+.action-dropdown {
+ display: flex;
+ flex-direction: column;
+ width: 120px;
+
+ .action-item {
+ padding: 8px 16px;
+ align-items: center;
+ font-size: 13px;
+ line-height: 20px;
+ font-family: $FONT-ROBOTO-REGULAR;
+ color: $COLOR--almost-black;
+ cursor: pointer;
+ text-decoration: none;
+ outline: none;
+ background: none;
+ border: none;
+ text-align: start;
+
+ &.delete-button {
+ color: $COLOR--error;
+ }
+
+ &:hover {
+ filter: brightness(80%);
+ }
+ }
+}
+
+.divider {
+ height: 1px;
+ background-color: $COLOR--e-100;
+ margin: 8px 12px;
+}
\ No newline at end of file
diff --git a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx
index cdb6b09e29..d1e0d2439e 100644
--- a/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx
+++ b/app/src/pages/organization/organizationProjectsPage/projectsListTable/projectsListTable.jsx
@@ -17,7 +17,7 @@
import { useMemo } from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
-import { MeatballMenuIcon, Popover, Table } from '@reportportal/ui-kit';
+import { Table } from '@reportportal/ui-kit';
import classNames from 'classnames/bind';
import { useDispatch, useSelector } from 'react-redux';
import { AbsRelTime } from 'components/main/absRelTime';
@@ -36,6 +36,7 @@ import {
SORTING_KEY,
} from 'controllers/organization/projects';
import { NAMESPACE } from 'controllers/organization/projects/constants';
+import { ProjectActionMenu } from 'pages/organization/organizationProjectsPage/projectsListTable/projectActionMenu';
import { messages } from '../messages';
import { ProjectName } from './projectName';
import styles from './projectsListTable.scss';
@@ -64,20 +65,20 @@ export const ProjectsListTable = ({
() =>
projects.map((project) => {
const lastLaunch = project.stats.launch_stats.last_occurred_at;
+ const metaData = {
+ projectName: project.name,
+ projectSlug: project.slug,
+ projectKey: project.key,
+ projectId: project.id,
+ organizationSlug,
+ };
return {
id: project.id,
name: {
content: project.name,
component: (