diff --git a/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectSettings.tsx b/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectSettings.tsx index ebf1e8a34..c361ce8ab 100644 --- a/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectSettings.tsx +++ b/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectSettings.tsx @@ -59,6 +59,7 @@ import useProjectPermissions from "../../utils/useProjectPermissions.hook"; import ProjectSessionSecrets from "../SessionSecrets/ProjectSessionSecrets"; import ProjectPageDelete from "./ProjectDelete"; import ProjectPageSettingsMembers from "./ProjectSettingsMembers"; +import ProjectUnlinkTemplate from "./ProjectUnlinkTemplate"; function notificationProjectUpdated( notifications: NotificationsManager, @@ -381,30 +382,6 @@ function ProjectSettingsDisplay({ project }: ProjectPageSettingsProps) { ); } -function ProjectSettingsTemplateLink({ project }: { project: Project }) { - if (project.template_id === null) return null; - return ( - - -

- - Break template link -

-

- This will break the link between this project and the template it was - created from. -

-
- -
-
(Not yet implemented)
-
-
-
-
- ); -} - function ProjectSettingsMetadata({ project }: ProjectPageSettingsProps) { const permissions = useProjectPermissions({ projectId: project.id }); @@ -480,7 +457,7 @@ export default function ProjectPageSettings() { } + enabled={} requestedPermission="write" userPermissions={permissions} /> diff --git a/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectUnlinkTemplate.tsx b/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectUnlinkTemplate.tsx new file mode 100644 index 000000000..3562d2e2b --- /dev/null +++ b/client/src/features/ProjectPageV2/ProjectPageContent/Settings/ProjectUnlinkTemplate.tsx @@ -0,0 +1,123 @@ +/*! + * Copyright 2024 - Swiss Data Science Center (SDSC) + * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and + * Eidgenössische Technische Hochschule Zürich (ETHZ). + * + * 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 cx from "classnames"; +import { useCallback, useContext, useEffect, useState } from "react"; +import { Diagram3Fill, NodeMinus } from "react-bootstrap-icons"; +import { useNavigate } from "react-router-dom-v5-compat"; +import { Button, Card, CardBody, CardHeader, Input } from "reactstrap"; + +import { Loader } from "../../../../components/Loader"; +import { NOTIFICATION_TOPICS } from "../../../../notifications/Notifications.constants"; +import { NotificationsManager } from "../../../../notifications/notifications.types"; +import AppContext from "../../../../utils/context/appContext"; +import { Project } from "../../../projectsV2/api/projectV2.api"; +import { usePatchProjectsByProjectIdMutation } from "../../../projectsV2/api/projectV2.enhanced-api"; + +export function notificationProjectDeleted( + notifications: NotificationsManager, + projectName: string +) { + notifications.addSuccess( + NOTIFICATION_TOPICS.PROJECT_UPDATED, + <> + Project {projectName} successfully unlinked. + + ); +} + +interface ProjectUnlinkTemplateProps { + project: Project; +} +export default function ProjectUnlinkTemplate({ + project, +}: ProjectUnlinkTemplateProps) { + const [patchProject, result] = usePatchProjectsByProjectIdMutation(); + const navigate = useNavigate(); + const { notifications } = useContext(AppContext); + const onUnlink = useCallback(() => { + patchProject({ + projectId: project.id, + "If-Match": project.etag ?? "", + projectPatch: { + template_id: "", + }, + }); + }, [patchProject, project.etag, project.id]); + + useEffect(() => { + if (result.isSuccess) { + if (notifications) + notificationProjectDeleted(notifications, project.name); + } + }, [result.isSuccess, navigate, notifications, project.name]); + + const [typedName, setTypedName] = useState(""); + const onChange = useCallback( + (e: React.ChangeEvent) => { + setTypedName(e.target.value.trim()); + }, + [setTypedName] + ); + + if (project.template_id === null) return null; + return ( + + +

+ + Break template link +

+

+ This will break the link between this project and the template it was + created from. +

+
+ +

+ Are you sure you want to unlink this project from its template? +

+

+ This cannot be undone. Please type {project.slug}, + the slug of the project, to confirm. +

+
+ +
+
+ +
+
+
+ ); +} diff --git a/tests/cypress/e2e/projectV2.spec.ts b/tests/cypress/e2e/projectV2.spec.ts index 40a751481..7e1bd891c 100644 --- a/tests/cypress/e2e/projectV2.spec.ts +++ b/tests/cypress/e2e/projectV2.spec.ts @@ -753,12 +753,20 @@ describe("Project templates and copies", () => { }); it("break the template link", () => { - fixtures.getProjectV2Permissions().listNamespaceV2().copyProjectV2(); + fixtures + .getProjectV2Permissions() + .listNamespaceV2() + .copyProjectV2() + .updateProjectV2(); cy.visit("/v2/projects/user1-uuid/test-2-v2-project"); cy.wait("@readProjectV2"); cy.get("a[title='Settings']").should("be.visible").click(); - cy.contains("Break template link").should("be.visible").click(); + cy.contains("Break template link").should("be.visible"); + cy.contains("Unlink project").should("be.disabled"); + cy.getDataCy("unlink-confirmation-input").clear().type("test-2-v2-project"); + cy.contains("Unlink project").should("be.enabled").click(); + cy.wait("@updateProjectV2"); }); }); diff --git a/tests/cypress/support/renkulab-fixtures/projectV2.ts b/tests/cypress/support/renkulab-fixtures/projectV2.ts index d548fb211..c835dce93 100644 --- a/tests/cypress/support/renkulab-fixtures/projectV2.ts +++ b/tests/cypress/support/renkulab-fixtures/projectV2.ts @@ -66,7 +66,7 @@ interface ProjectV2DeleteFixture extends NameOnlyFixture { interface ProjectV2ListCopiesFixture extends Omit { writeable?: boolean; - count?: 0 | 1 | undefined | null; + count?: number | null; } interface ProjectV2NameArgs extends SimpleFixture {