From 20d2f6b5d5fc839b7a5854e8cc5a4d19e07514c1 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 11 Nov 2020 15:58:58 +0100 Subject: [PATCH] add 'delete branch' function to frontend --- .../ui-components/src/modals/ConfirmAlert.tsx | 6 +- scm-ui/ui-webapp/public/locales/de/repos.json | 14 ++- scm-ui/ui-webapp/public/locales/en/repos.json | 14 ++- .../repos/branches/components/BranchRow.tsx | 2 +- .../repos/branches/components/BranchTable.tsx | 67 ++++++++++--- .../repos/branches/components/BranchView.tsx | 3 +- .../branches/containers/BranchDangerZone.tsx | 59 ++++++++++++ .../repos/branches/containers/BranchRoot.tsx | 3 +- .../branches/containers/BranchesOverview.tsx | 9 +- .../branches/containers/DeleteBranch.tsx | 94 +++++++++++++++++++ .../src/repos/containers/EditRepo.tsx | 4 +- ...angerZone.tsx => RepositoryDangerZone.tsx} | 6 +- 12 files changed, 249 insertions(+), 32 deletions(-) create mode 100644 scm-ui/ui-webapp/src/repos/branches/containers/BranchDangerZone.tsx create mode 100644 scm-ui/ui-webapp/src/repos/branches/containers/DeleteBranch.tsx rename scm-ui/ui-webapp/src/repos/containers/{DangerZone.tsx => RepositoryDangerZone.tsx} (93%) diff --git a/scm-ui/ui-components/src/modals/ConfirmAlert.tsx b/scm-ui/ui-components/src/modals/ConfirmAlert.tsx index 89f8f0a117..290f024b63 100644 --- a/scm-ui/ui-components/src/modals/ConfirmAlert.tsx +++ b/scm-ui/ui-components/src/modals/ConfirmAlert.tsx @@ -62,11 +62,11 @@ export const ConfirmAlert: FC = ({ title, message, buttons, close }) => { const footer = (
- {buttons.map((button, i) => ( -

+ {buttons.map((button, index) => ( +

handleClickButton(button)} > {button.label} diff --git a/scm-ui/ui-webapp/public/locales/de/repos.json b/scm-ui/ui-webapp/public/locales/de/repos.json index d3ec72b23f..eceb488c3f 100644 --- a/scm-ui/ui-webapp/public/locales/de/repos.json +++ b/scm-ui/ui-webapp/public/locales/de/repos.json @@ -68,7 +68,19 @@ "name": "Name:", "commits": "Commits", "sources": "Sources", - "defaultTag": "Default" + "defaultTag": "Default", + "dangerZone": "Branch löschen", + "delete": { + "button": "Branch löschen", + "subtitle": "Branch löschen", + "description": "Gelöschte Branches können nicht wiederhergestellt werden.", + "confirmAlert": { + "title": "Branch löschen", + "message": "Möchten Sie diesen Branch wirklich löschen?", + "cancel": "Nein", + "submit": "Ja" + } + } }, "tags": { "overview": { diff --git a/scm-ui/ui-webapp/public/locales/en/repos.json b/scm-ui/ui-webapp/public/locales/en/repos.json index 5f83ad7ba1..6845bf8b42 100644 --- a/scm-ui/ui-webapp/public/locales/en/repos.json +++ b/scm-ui/ui-webapp/public/locales/en/repos.json @@ -68,7 +68,19 @@ "name": "Name:", "commits": "Commits", "sources": "Sources", - "defaultTag": "Default" + "defaultTag": "Default", + "dangerZone": "Delete branch", + "delete": { + "button": "Delete branch", + "subtitle": "Delete branch", + "description": "Deleted branches can not be restored.", + "confirmAlert": { + "title": "Delete branch", + "message": "Do you really want to delete the branch?", + "cancel": "No", + "submit": "Yes" + } + } }, "tags": { "overview": { diff --git a/scm-ui/ui-webapp/src/repos/branches/components/BranchRow.tsx b/scm-ui/ui-webapp/src/repos/branches/components/BranchRow.tsx index 6237bbe077..1c2df84b00 100644 --- a/scm-ui/ui-webapp/src/repos/branches/components/BranchRow.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/components/BranchRow.tsx @@ -44,7 +44,7 @@ const BranchRow: FC = ({ baseUrl, branch, onDelete }) => { deleteButton = ( onDelete(url)}> - + ); diff --git a/scm-ui/ui-webapp/src/repos/branches/components/BranchTable.tsx b/scm-ui/ui-webapp/src/repos/branches/components/BranchTable.tsx index 05589cffdd..ca45a8ee1c 100644 --- a/scm-ui/ui-webapp/src/repos/branches/components/BranchTable.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/components/BranchTable.tsx @@ -21,19 +21,40 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import React, { FC } from "react"; +import React, { FC, useState } from "react"; import { useTranslation } from "react-i18next"; import BranchRow from "./BranchRow"; import { Branch } from "@scm-manager/ui-types"; +import { apiClient, ConfirmAlert, ErrorNotification } from "@scm-manager/ui-components"; type Props = { baseUrl: string; branches: Branch[]; - onDelete: (url: string) => void; + fetchBranches: () => void; }; -const BranchTable: FC = ({ baseUrl, branches, onDelete }) => { +const BranchTable: FC = ({ baseUrl, branches, fetchBranches }) => { const [t] = useTranslation("repos"); + const [showConfirmAlert, setShowConfirmAlert] = useState(false); + const [error, setError] = useState(); + const [deleteBranchUrl, setDeleteBranchUrl] = useState(""); + + const onDelete = (url: string) => { + setDeleteBranchUrl(url); + setShowConfirmAlert(true); + }; + + const abortDelete = () => { + setDeleteBranchUrl(""); + setShowConfirmAlert(false); + }; + + const deleteBranch = () => { + apiClient + .delete(deleteBranchUrl) + .then(() => fetchBranches()) + .catch(setError); + }; const renderRow = () => { let rowContent = null; @@ -44,15 +65,39 @@ const BranchTable: FC = ({ baseUrl, branches, onDelete }) => { } return rowContent; }; + + const confirmAlert = ( + deleteBranch() + }, + { + label: t("branch.delete.confirmAlert.cancel"), + onClick: () => abortDelete() + } + ]} + close={() => abortDelete()} + /> + ); + return ( - - - - - - - {renderRow()} -
{t("branches.table.branches")}
+ <> + {showConfirmAlert && confirmAlert} + {error && } + + + + + + + {renderRow()} +
{t("branches.table.branches")}
+ ); }; diff --git a/scm-ui/ui-webapp/src/repos/branches/components/BranchView.tsx b/scm-ui/ui-webapp/src/repos/branches/components/BranchView.tsx index 9a88c08c2d..32ff0d8f7a 100644 --- a/scm-ui/ui-webapp/src/repos/branches/components/BranchView.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/components/BranchView.tsx @@ -25,6 +25,7 @@ import React from "react"; import BranchDetail from "./BranchDetail"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { Branch, Repository } from "@scm-manager/ui-types"; +import BranchDangerZone from "../containers/BranchDangerZone"; type Props = { repository: Repository; @@ -34,7 +35,6 @@ type Props = { class BranchView extends React.Component { render() { const { repository, branch } = this.props; - return (

@@ -49,6 +49,7 @@ class BranchView extends React.Component { }} />
+
); } diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchDangerZone.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchDangerZone.tsx new file mode 100644 index 0000000000..9e6c0e167a --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchDangerZone.tsx @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React, { FC } from "react"; +import { Branch, Repository } from "@scm-manager/ui-types"; +import { Subtitle } from "@scm-manager/ui-components"; +import { useTranslation } from "react-i18next"; +import { DangerZoneContainer } from "../../containers/RepositoryDangerZone"; +import DeleteBranch from "./DeleteBranch"; + +type Props = { + repository: Repository; + branch: Branch; +}; + +const BranchDangerZone: FC = ({ repository, branch }) => { + const [t] = useTranslation("repos"); + + const dangerZone = []; + + if (branch?._links?.delete) { + dangerZone.push(); + } + + if (dangerZone.length === 0) { + return null; + } + + return ( + <> +
+ + {dangerZone} + + ); +}; + +export default BranchDangerZone; diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx index cb2847a712..9a9aa02d24 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchRoot.tsx @@ -28,10 +28,9 @@ import { compose } from "redux"; import { Redirect, Route, Switch, withRouter } from "react-router-dom"; import { Branch, Repository } from "@scm-manager/ui-types"; import { fetchBranch, getBranch, getFetchBranchFailure, isFetchBranchPending } from "../modules/branches"; -import { ErrorNotification, Loading, NotFoundError } from "@scm-manager/ui-components"; +import { ErrorNotification, Loading, NotFoundError, urls } from "@scm-manager/ui-components"; import { History } from "history"; import queryString from "query-string"; -import { urls } from "@scm-manager/ui-components"; type Props = { repository: Repository; diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx index e18593a701..8bbcac53bf 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx @@ -37,7 +37,6 @@ import { } from "../modules/branches"; import { orderBranches } from "../util/orderBranches"; import BranchTable from "../components/BranchTable"; -import { apiClient } from "@scm-manager/ui-components/src"; type Props = WithTranslation & { repository: Repository; @@ -81,15 +80,11 @@ class BranchesOverview extends React.Component { ); } - onDelete(url: string) { - apiClient.delete(url).catch(error => this.setState({ error })); - } - renderBranchesTable() { - const { baseUrl, branches, t } = this.props; + const { baseUrl, branches, repository, fetchBranches, t } = this.props; if (branches && branches.length > 0) { orderBranches(branches); - return ; + return fetchBranches(repository)} />; } return {t("branches.overview.noBranches")}; } diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/DeleteBranch.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/DeleteBranch.tsx new file mode 100644 index 0000000000..2ff9ff9284 --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/branches/containers/DeleteBranch.tsx @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import React, { FC, useState } from "react"; +import { useHistory } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Branch, Link, Repository } from "@scm-manager/ui-types"; +import { apiClient, ConfirmAlert, DeleteButton, ErrorNotification, Level } from "@scm-manager/ui-components"; + +type Props = { + repository: Repository; + branch: Branch; +}; + +const DeleteBranch: FC = ({ repository, branch }: Props) => { + const [showConfirmAlert, setShowConfirmAlert] = useState(false); + const [error, setError] = useState(); + const [t] = useTranslation("repos"); + const history = useHistory(); + + console.log("branchview", repository, branch); + + const deleteBranch = () => { + apiClient + .delete((branch._links.delete as Link).href) + .then(() => history.push(`/repo/${repository.namespace}/${repository.name}/branches/`)) + .catch(setError); + }; + + if (!branch._links.delete) { + return null; + } + + let confirmAlert = null; + if (showConfirmAlert) { + confirmAlert = ( + deleteBranch() + }, + { + label: t("branch.delete.confirmAlert.cancel"), + onClick: () => null + } + ]} + close={() => setShowConfirmAlert(false)} + /> + ); + } + + return ( + <> + + {showConfirmAlert && confirmAlert} + + {t("branch.delete.subtitle")} +
+ {t("branch.delete.description")} +

+ } + right={ setShowConfirmAlert(true)} />} + /> + + ); +}; + +export default DeleteBranch; diff --git a/scm-ui/ui-webapp/src/repos/containers/EditRepo.tsx b/scm-ui/ui-webapp/src/repos/containers/EditRepo.tsx index f2334c5d7e..2f91c8421b 100644 --- a/scm-ui/ui-webapp/src/repos/containers/EditRepo.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/EditRepo.tsx @@ -31,7 +31,7 @@ import { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { compose } from "redux"; -import DangerZone from "./DangerZone"; +import RepositoryDangerZone from "./RepositoryDangerZone"; import { getLinks } from "../../modules/indexResource"; import { urls } from "@scm-manager/ui-components"; @@ -80,7 +80,7 @@ class EditRepo extends React.Component { }} /> - + ); } diff --git a/scm-ui/ui-webapp/src/repos/containers/DangerZone.tsx b/scm-ui/ui-webapp/src/repos/containers/RepositoryDangerZone.tsx similarity index 93% rename from scm-ui/ui-webapp/src/repos/containers/DangerZone.tsx rename to scm-ui/ui-webapp/src/repos/containers/RepositoryDangerZone.tsx index 0b2bda04b9..cb883a904f 100644 --- a/scm-ui/ui-webapp/src/repos/containers/DangerZone.tsx +++ b/scm-ui/ui-webapp/src/repos/containers/RepositoryDangerZone.tsx @@ -35,7 +35,7 @@ type Props = { indexLinks: Links; }; -const DangerZoneContainer = styled.div` +export const DangerZoneContainer = styled.div` padding: 1.5rem 1rem; border: 1px solid #ff6a88; border-radius: 5px; @@ -56,7 +56,7 @@ const DangerZoneContainer = styled.div` } `; -const DangerZone: FC = ({ repository, indexLinks }) => { +const RepositoryDangerZone: FC = ({ repository, indexLinks }) => { const [t] = useTranslation("repos"); const dangerZone = []; @@ -81,4 +81,4 @@ const DangerZone: FC = ({ repository, indexLinks }) => { ); }; -export default DangerZone; +export default RepositoryDangerZone;