Skip to content

Commit

Permalink
Impl [Projects] Need an option to delete project from project settings (
Browse files Browse the repository at this point in the history
#2876)



---------

Co-authored-by: ilan7empest <[email protected]>
  • Loading branch information
Taras-Hlukhovetskyi and ilan7empest authored Nov 12, 2024
1 parent 6c22eb1 commit 1d61473
Show file tree
Hide file tree
Showing 9 changed files with 492 additions and 327 deletions.
3 changes: 2 additions & 1 deletion src/common/Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ const Input = React.forwardRef(
<input
data-testid={id ? `${id}-input` : 'input'}
className={inputClassNames}
name={id}
onBlur={handleInputBlur}
onChange={handleInputChange}
onFocus={handleInputFocus}
Expand All @@ -247,7 +248,7 @@ const Input = React.forwardRef(
type,
value: typedValue
}}
style={floatingLabel ? {} : inputStyles }
style={floatingLabel ? {} : inputStyles}
/>
{label && (
<div className={labelClassNames}>
Expand Down
132 changes: 93 additions & 39 deletions src/components/ProjectSettings/ProjectSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import { connect, useDispatch } from 'react-redux'
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useLocation, useNavigate, useParams } from 'react-router-dom'

import ProjectSettingsGeneral from '../../elements/ProjectSettingsGeneral/ProjectSettingsGeneral'
Expand All @@ -27,6 +27,9 @@ import ProjectSettingsSecrets from '../../elements/ProjectSettingsSecrets/Projec
import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs'
import ContentMenu from '../../elements/ContentMenu/ContentMenu'

import { Button, ConfirmDialog } from 'igz-controls/components'
import { DANGER_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants'

import {
COMPLETED_STATE,
generateMembers,
Expand All @@ -35,6 +38,8 @@ import {
tabs,
validTabs
} from './projectSettings.util'
import { onDeleteProject } from '../ProjectsPage/projects.util'
import projectsAction from '../../actions/projects'
import {
initialMembersState,
membersActions,
Expand All @@ -47,14 +52,19 @@ import { showErrorNotification } from '../../utils/notifications.util'

import './projectSettings.scss'

const ProjectSettings = ({ frontendSpec }) => {
const ProjectSettings = () => {
const [projectMembersIsShown, setProjectMembersIsShown] = useState(false)
const [projectOwnerIsShown, setProjectOwnerIsShown] = useState(false)
const [confirmData, setConfirmData] = useState(null)
const [membersState, membersDispatch] = useReducer(membersReducer, initialMembersState)
const location = useLocation()
const navigate = useNavigate()
const params = useParams()
const dispatch = useDispatch()
const deletingProjectsRef = useRef({})
const terminatePollRef = useRef(null)
const projectStore = useSelector(state => state.projectStore)
const frontendSpec = useSelector(store => store.appStore.frontendSpec)

const projectMembershipIsEnabled = useMemo(
() => frontendSpec?.feature_flags?.project_membership === 'enabled',
Expand Down Expand Up @@ -122,7 +132,7 @@ const ProjectSettings = ({ frontendSpec }) => {
[]
) || [])
])
membersDispatch({
type: membersActions.SET_ACTIVE_USER,
payload: activeUser
Expand Down Expand Up @@ -208,6 +218,10 @@ const ProjectSettings = ({ frontendSpec }) => {
})
}, [])
const fetchMinimalProjects = useCallback(() => {
dispatch(projectsAction.fetchProjects({ format: 'minimal' }))
}, [dispatch])
useEffect(() => {
membersDispatch({
type: membersActions.GET_PROJECT_USERS_DATA_BEGIN
Expand All @@ -226,43 +240,83 @@ const ProjectSettings = ({ frontendSpec }) => {
}, [navigate, params.pageTab, params.projectName])
return (
<div className="settings">
<div className="settings__header">
<Breadcrumbs />
</div>
<div className="settings__content">
<ContentMenu
activeTab={params.pageTab}
location={location}
screen={page}
tabs={tabs(projectMembersTabIsShown)}
<>
{confirmData && (
<ConfirmDialog
cancelButton={{
handler: confirmData.rejectHandler,
label: 'Cancel',
variant: TERTIARY_BUTTON
}}
closePopUp={confirmData.rejectHandler}
confirmButton={{
handler: confirmData.confirmHandler,
label: confirmData.btnConfirmLabel,
variant: confirmData.btnConfirmType
}}
isOpen={confirmData}
header={confirmData.header}
message={confirmData.message}
/>
{params.pageTab === PROJECTS_SETTINGS_MEMBERS_TAB && projectMembersTabIsShown ? (
<ProjectSettingsMembers
changeMembersCallback={changeMembersCallback}
loading={membersState.loading}
membersState={membersState}
membersDispatch={membersDispatch}
projectMembersIsShown={projectMembersIsShown}
/>
) : params.pageTab === PROJECTS_SETTINGS_SECRETS_TAB ? (
<ProjectSettingsSecrets setNotification={setNotification} />
) : (
<ProjectSettingsGeneral
changeOwnerCallback={changeOwnerCallback}
membersState={membersState}
projectMembershipIsEnabled={projectMembershipIsEnabled}
projectOwnerIsShown={projectOwnerIsShown}
/>
)}
)}

<div className="settings">
<div className="settings__header">
<Breadcrumbs />
</div>
<div className="settings__content">
<div className="content__action-bar-wrapper">
<ContentMenu
activeTab={params.pageTab}
location={location}
screen={page}
tabs={tabs(projectMembersTabIsShown)}
/>
<div className="action-bar">
{projectMembershipIsEnabled && (
<Button
variant={DANGER_BUTTON}
label="Delete project"
onClick={event => {
event.stopPropagation()
onDeleteProject(
projectStore.project?.data,
setConfirmData,
dispatch,
deletingProjectsRef,
terminatePollRef,
fetchMinimalProjects,
navigate
)
}}
className="delete-project-btn"
disabled={projectStore.loading || projectStore.project.loading}
/>
)}
</div>
</div>
{params.pageTab === PROJECTS_SETTINGS_MEMBERS_TAB && projectMembersTabIsShown ? (
<ProjectSettingsMembers
changeMembersCallback={changeMembersCallback}
loading={membersState.loading}
membersState={membersState}
membersDispatch={membersDispatch}
projectMembersIsShown={projectMembersIsShown}
/>
) : params.pageTab === PROJECTS_SETTINGS_SECRETS_TAB ? (
<ProjectSettingsSecrets setNotification={setNotification} />
) : (
<ProjectSettingsGeneral
changeOwnerCallback={changeOwnerCallback}
membersState={membersState}
projectMembershipIsEnabled={projectMembershipIsEnabled}
projectOwnerIsShown={projectOwnerIsShown}
/>
)}
</div>
</div>
</div>
</>
)
}

export default connect(
({ appStore }) => ({
frontendSpec: appStore.frontendSpec
}),
null
)(ProjectSettings)
export default ProjectSettings
97 changes: 25 additions & 72 deletions src/components/ProjectsPage/Projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ import ProjectsView from './ProjectsView'
import {
generateMonitoringCounters,
generateProjectActionsMenu,
handleDeleteProjectError,
pollDeletingProjects,
projectDeletionKind,
projectDeletionWrapperKind,
projectsSortOptions
} from './projects.util'
import nuclioActions from '../../actions/nuclio'
import projectsAction from '../../actions/projects'
import { BG_TASK_RUNNING, isBackgroundTaskRunning } from '../../utils/poll.util'
import { BG_TASK_RUNNING } from '../../utils/poll.util'
import { onDeleteProject } from './projects.util'
import { PROJECT_ONLINE_STATUS } from '../../constants'
import { DANGER_BUTTON, FORBIDDEN_ERROR_STATUS_CODE, PRIMARY_BUTTON } from 'igz-controls/constants'
import { FORBIDDEN_ERROR_STATUS_CODE, PRIMARY_BUTTON } from 'igz-controls/constants'
import { fetchBackgroundTasks } from '../../reducers/tasksReducer'
import { setNotification } from '../../reducers/notificationReducer'
import { showErrorNotification } from '../../utils/notifications.util'
Expand Down Expand Up @@ -135,7 +135,9 @@ const Projects = () => {
backgroundTask =>
backgroundTask.metadata.kind.startsWith(
wrapperIsUsed ? projectDeletionWrapperKind : projectDeletionKind
) && backgroundTask?.status?.state === BG_TASK_RUNNING && deletingProjectsRef.current[backgroundTask.metadata.name]
) &&
backgroundTask?.status?.state === BG_TASK_RUNNING &&
deletingProjectsRef.current[backgroundTask.metadata.name]
)
.reduce((acc, backgroundTask) => {
acc[backgroundTask.metadata.name] = last(backgroundTask.metadata.kind.split('.'))
Expand Down Expand Up @@ -188,53 +190,6 @@ const Projects = () => {
[dispatch, fetchMinimalProjects]
)

const handleDeleteProject = useCallback(
(project, deleteNonEmpty) => {
setConfirmData(null)

dispatch(projectsAction.deleteProject(project.metadata.name, deleteNonEmpty))
.then(response => {
if (isBackgroundTaskRunning(response)) {
dispatch(
setNotification({
status: 200,
id: Math.random(),
message: 'Project deletion in progress'
})
)

const newDeletingProjects = {
...deletingProjectsRef.current,
[response.data.metadata.name]: last(response.data.metadata.kind.split('.'))
}

dispatch(projectsAction.setDeletingProjects(newDeletingProjects))
pollDeletingProjects(terminatePollRef, newDeletingProjects, refreshProjects, dispatch)
} else {
fetchMinimalProjects()
dispatch(
setNotification({
status: 200,
id: Math.random(),
message: `Project "${project}" was deleted successfully`
})
)
}
})
.catch(error => {
handleDeleteProjectError(
error,
handleDeleteProject,
project,
setConfirmData,
dispatch,
deleteNonEmpty
)
})
},
[dispatch, fetchMinimalProjects, refreshProjects]
)

const handleUnarchiveProject = useCallback(
project => {
dispatch(
Expand Down Expand Up @@ -276,23 +231,6 @@ const Projects = () => {
[handleArchiveProject]
)

const onDeleteProject = useCallback(
project => {
setConfirmData({
item: project,
header: 'Delete project?',
message: `You are trying to delete the project "${project.metadata.name}". Deleted projects cannot be restored`,
btnConfirmLabel: 'Delete',
btnConfirmType: DANGER_BUTTON,
rejectHandler: () => {
setConfirmData(null)
},
confirmHandler: handleDeleteProject
})
},
[handleDeleteProject]
)

const exportYaml = useCallback(
projectMinimal => {
if (projectMinimal?.metadata?.name) {
Expand All @@ -303,7 +241,7 @@ const Projects = () => {
FileSaver.saveAs(blob, `${projectMinimal.metadata.name}.yaml`)
})
.catch(error => {
showErrorNotification(dispatch, error, '', 'Failed to fetch project\'s YAML', () =>
showErrorNotification(dispatch, error, '', "Failed to fetch project's YAML", () =>
exportYaml(projectMinimal)
)
})
Expand All @@ -322,7 +260,7 @@ const Projects = () => {
.catch(error => {
setConvertedYaml('')

showErrorNotification(dispatch, error, '', 'Failed to fetch project\'s YAML', () =>
showErrorNotification(dispatch, error, '', "Failed to fetch project's YAML", () =>
viewYaml(projectMinimal)
)
})
Expand All @@ -338,6 +276,21 @@ const Projects = () => {
[dispatch]
)

const handleOnDeleteProject = useCallback(
project =>
onDeleteProject(
project,
setConfirmData,
dispatch,
deletingProjectsRef,
terminatePollRef,
fetchMinimalProjects,
null,
refreshProjects
),
[dispatch, fetchMinimalProjects, refreshProjects]
)

useEffect(() => {
setActionsMenu(
generateProjectActionsMenu(
Expand All @@ -347,17 +300,17 @@ const Projects = () => {
viewYaml,
onArchiveProject,
handleUnarchiveProject,
onDeleteProject
handleOnDeleteProject
)
)
}, [
convertToYaml,
handleOnDeleteProject,
projectStore.deletingProjects,
exportYaml,
handleUnarchiveProject,
isDemoMode,
onArchiveProject,
onDeleteProject,
projectStore.projects,
viewYaml
])
Expand Down
Loading

0 comments on commit 1d61473

Please sign in to comment.