-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add a remove project button on the project settings page #15316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,8 +4,8 @@ | |||||
* See License.AGPL.txt in the project root for license information. | ||||||
*/ | ||||||
|
||||||
import { useContext, useEffect, useState } from "react"; | ||||||
import { useLocation } from "react-router"; | ||||||
import { useCallback, useContext, useEffect, useState } from "react"; | ||||||
import { useLocation, useHistory } from "react-router"; | ||||||
import { Project, ProjectSettings, Team } from "@gitpod/gitpod-protocol"; | ||||||
import CheckBox from "../components/CheckBox"; | ||||||
import { getGitpodService } from "../service/service"; | ||||||
|
@@ -17,6 +17,7 @@ import SelectWorkspaceClass from "../settings/selectClass"; | |||||
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; | ||||||
import Alert from "../components/Alert"; | ||||||
import { Link } from "react-router-dom"; | ||||||
import { RemoveProjectModal } from "./RemoveProjectModal"; | ||||||
|
||||||
export function getProjectSettingsMenu(project?: Project, team?: Team) { | ||||||
const teamOrUserSlug = !!team ? "t/" + team.slug : "projects"; | ||||||
|
@@ -51,8 +52,11 @@ export function ProjectSettingsPage(props: { project?: Project; children?: React | |||||
export default function () { | ||||||
const { project, setProject } = useContext(ProjectContext); | ||||||
const [billingMode, setBillingMode] = useState<BillingMode | undefined>(undefined); | ||||||
const [showRemoveModal, setShowRemoveModal] = useState(false); | ||||||
const { teams } = useContext(TeamsContext); | ||||||
const team = getCurrentTeam(useLocation(), teams); | ||||||
const history = useHistory(); | ||||||
|
||||||
useEffect(() => { | ||||||
if (team) { | ||||||
getGitpodService().server.getBillingModeForTeam(team.id).then(setBillingMode); | ||||||
|
@@ -61,33 +65,52 @@ export default function () { | |||||
} | ||||||
}, [team]); | ||||||
|
||||||
if (!project) return null; | ||||||
const updateProjectSettings = useCallback( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrapped each of these functions with |
||||||
(settings: ProjectSettings) => { | ||||||
if (!project) return; | ||||||
|
||||||
const updateProjectSettings = (settings: ProjectSettings) => { | ||||||
if (!project) return; | ||||||
const newSettings = { ...project.settings, ...settings }; | ||||||
getGitpodService().server.updateProjectPartial({ id: project.id, settings: newSettings }); | ||||||
setProject({ ...project, settings: newSettings }); | ||||||
}, | ||||||
[project, setProject], | ||||||
); | ||||||
|
||||||
const newSettings = { ...project.settings, ...settings }; | ||||||
getGitpodService().server.updateProjectPartial({ id: project.id, settings: newSettings }); | ||||||
setProject({ ...project, settings: newSettings }); | ||||||
}; | ||||||
const setWorkspaceClass = useCallback( | ||||||
async (value: string) => { | ||||||
if (!project) { | ||||||
return value; | ||||||
} | ||||||
const before = project.settings?.workspaceClasses?.regular; | ||||||
updateProjectSettings({ workspaceClasses: { ...project.settings?.workspaceClasses, regular: value } }); | ||||||
return before; | ||||||
}, | ||||||
[project, updateProjectSettings], | ||||||
); | ||||||
|
||||||
const setWorkspaceClass = async (value: string) => { | ||||||
if (!project) { | ||||||
return value; | ||||||
} | ||||||
const before = project.settings?.workspaceClasses?.regular; | ||||||
updateProjectSettings({ workspaceClasses: { ...project.settings?.workspaceClasses, regular: value } }); | ||||||
return before; | ||||||
}; | ||||||
const setWorkspaceClassForPrebuild = useCallback( | ||||||
async (value: string) => { | ||||||
if (!project) { | ||||||
return value; | ||||||
} | ||||||
const before = project.settings?.workspaceClasses?.prebuild; | ||||||
updateProjectSettings({ workspaceClasses: { ...project.settings?.workspaceClasses, prebuild: value } }); | ||||||
return before; | ||||||
}, | ||||||
[project, updateProjectSettings], | ||||||
); | ||||||
|
||||||
const setWorkspaceClassForPrebuild = async (value: string) => { | ||||||
if (!project) { | ||||||
return value; | ||||||
const onProjectRemoved = useCallback(() => { | ||||||
// if there's a current team, navigate to team projects | ||||||
if (team) { | ||||||
history.push(`/t/${team.slug}/projects`); | ||||||
} else { | ||||||
history.push("/projects"); | ||||||
} | ||||||
const before = project.settings?.workspaceClasses?.prebuild; | ||||||
updateProjectSettings({ workspaceClasses: { ...project.settings?.workspaceClasses, prebuild: value } }); | ||||||
return before; | ||||||
}; | ||||||
}, [history, team]); | ||||||
|
||||||
// TODO: Render a generic error screen for when an entity isn't found | ||||||
if (!project) return null; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should live after all hooks, so bumped it down since I added a few useCallback() wrappers on the fns that weren't there before. |
||||||
|
||||||
return ( | ||||||
<ProjectSettingsPage project={project}> | ||||||
|
@@ -237,6 +260,22 @@ export default function () { | |||||
</Alert> | ||||||
)} | ||||||
</div> | ||||||
<div> | ||||||
<h3 className="mt-12">Delete Project</h3> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: What do you think of using a different verb here to avoid causing any confusion whether this is also going to delete a project or repository on the provider?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like it. I’ll include this change in a follow up pr |
||||||
<p className="text-base text-gray-500 dark:text-gray-400 pb-4"> | ||||||
Removing the project from this team will also remove team members access to it. | ||||||
</p> | ||||||
<button className="danger secondary" onClick={() => setShowRemoveModal(true)}> | ||||||
Delete Project | ||||||
</button> | ||||||
</div> | ||||||
{showRemoveModal && ( | ||||||
<RemoveProjectModal | ||||||
project={project} | ||||||
onRemoved={onProjectRemoved} | ||||||
onClose={() => setShowRemoveModal(false)} | ||||||
/> | ||||||
)} | ||||||
</ProjectSettingsPage> | ||||||
); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* Copyright (c) 2022 Gitpod GmbH. All rights reserved. | ||
* Licensed under the GNU Affero General Public License (AGPL). | ||
* See License.AGPL.txt in the project root for license information. | ||
*/ | ||
|
||
import { FunctionComponent, useCallback, useState } from "react"; | ||
import type { Project } from "@gitpod/gitpod-protocol"; | ||
import { projectsService } from "../service/public-api"; | ||
import { getGitpodService } from "../service/service"; | ||
import ConfirmationModal from "../components/ConfirmationModal"; | ||
import { useFeatureFlags } from "../contexts/FeatureFlagContext"; | ||
|
||
type RemoveProjectModalProps = { | ||
project: Project; | ||
onClose: () => void; | ||
onRemoved: () => void; | ||
}; | ||
|
||
export const RemoveProjectModal: FunctionComponent<RemoveProjectModalProps> = ({ project, onClose, onRemoved }) => { | ||
const { usePublicApiProjectsService } = useFeatureFlags(); | ||
const [disabled, setDisabled] = useState(false); | ||
|
||
const removeProject = useCallback(async () => { | ||
setDisabled(true); | ||
usePublicApiProjectsService | ||
? await projectsService.deleteProject({ projectId: project.id }) | ||
: await getGitpodService().server.deleteProject(project.id); | ||
setDisabled(false); | ||
onRemoved(); | ||
}, [onRemoved, project.id, usePublicApiProjectsService]); | ||
|
||
return ( | ||
<ConfirmationModal | ||
title="Remove Project" | ||
areYouSureText="Are you sure you want to remove this project from this team? Team members will also lose access to this project." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, I'll update the confirmation modal in the follow up too. |
||
children={{ | ||
name: project?.name ?? "", | ||
description: project?.cloneUrl ?? "", | ||
}} | ||
buttonText="Remove Project" | ||
buttonDisabled={disabled} | ||
onClose={onClose} | ||
onConfirm={removeProject} | ||
visible | ||
/> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes it just a bit simpler to access feature flags from a component.