add 'delete branch' function to frontend

This commit is contained in:
Eduard Heimbuch
2020-11-11 15:58:58 +01:00
parent bb82c18e2b
commit 20d2f6b5d5
12 changed files with 249 additions and 32 deletions

View File

@@ -62,11 +62,11 @@ export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close }) => {
const footer = (
<div className="field is-grouped">
{buttons.map((button, i) => (
<p className="control">
{buttons.map((button, index) => (
<p className="control" key={index}>
<a
className={classNames("button", "is-info", button.className)}
key={i}
key={index}
onClick={() => handleClickButton(button)}
>
{button.label}

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -44,7 +44,7 @@ const BranchRow: FC<Props> = ({ baseUrl, branch, onDelete }) => {
deleteButton = (
<a className="level-item" onClick={() => onDelete(url)}>
<span className="icon is-small">
<Icon name="trash" className="fas" title={t("branch.delete")} />
<Icon name="trash" className="fas" title={t("branch.delete.button")} />
</span>
</a>
);

View File

@@ -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<Props> = ({ baseUrl, branches, onDelete }) => {
const BranchTable: FC<Props> = ({ baseUrl, branches, fetchBranches }) => {
const [t] = useTranslation("repos");
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
const [error, setError] = useState<Error | undefined>();
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,7 +65,30 @@ const BranchTable: FC<Props> = ({ baseUrl, branches, onDelete }) => {
}
return rowContent;
};
const confirmAlert = (
<ConfirmAlert
title={t("branch.delete.confirmAlert.title")}
message={t("branch.delete.confirmAlert.message")}
buttons={[
{
className: "is-outlined",
label: t("branch.delete.confirmAlert.submit"),
onClick: () => deleteBranch()
},
{
label: t("branch.delete.confirmAlert.cancel"),
onClick: () => abortDelete()
}
]}
close={() => abortDelete()}
/>
);
return (
<>
{showConfirmAlert && confirmAlert}
{error && <ErrorNotification error={error} />}
<table className="card-table table is-hoverable is-fullwidth is-word-break">
<thead>
<tr>
@@ -53,6 +97,7 @@ const BranchTable: FC<Props> = ({ baseUrl, branches, onDelete }) => {
</thead>
<tbody>{renderRow()}</tbody>
</table>
</>
);
};

View File

@@ -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<Props> {
render() {
const { repository, branch } = this.props;
return (
<div>
<BranchDetail repository={repository} branch={branch} />
@@ -49,6 +49,7 @@ class BranchView extends React.Component<Props> {
}}
/>
</div>
<BranchDangerZone repository={repository} branch={branch} />
</div>
);
}

View File

@@ -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<Props> = ({ repository, branch }) => {
const [t] = useTranslation("repos");
const dangerZone = [];
if (branch?._links?.delete) {
dangerZone.push(<DeleteBranch repository={repository} branch={branch} key={dangerZone.length} />);
}
if (dangerZone.length === 0) {
return null;
}
return (
<>
<hr />
<Subtitle subtitle={t("branch.dangerZone")} />
<DangerZoneContainer>{dangerZone}</DangerZoneContainer>
</>
);
};
export default BranchDangerZone;

View File

@@ -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;

View File

@@ -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<Props> {
);
}
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 <BranchTable baseUrl={baseUrl} branches={branches} onDelete={this.onDelete} />;
return <BranchTable baseUrl={baseUrl} branches={branches} fetchBranches={() => fetchBranches(repository)} />;
}
return <Notification type="info">{t("branches.overview.noBranches")}</Notification>;
}

View File

@@ -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<Props> = ({ repository, branch }: Props) => {
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
const [error, setError] = useState<Error | undefined>();
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 = (
<ConfirmAlert
title={t("branch.delete.confirmAlert.title")}
message={t("branch.delete.confirmAlert.message")}
buttons={[
{
className: "is-outlined",
label: t("branch.delete.confirmAlert.submit"),
onClick: () => deleteBranch()
},
{
label: t("branch.delete.confirmAlert.cancel"),
onClick: () => null
}
]}
close={() => setShowConfirmAlert(false)}
/>
);
}
return (
<>
<ErrorNotification error={error} />
{showConfirmAlert && confirmAlert}
<Level
left={
<p>
<strong>{t("branch.delete.subtitle")}</strong>
<br />
{t("branch.delete.description")}
</p>
}
right={<DeleteButton label={t("branch.delete.button")} action={() => setShowConfirmAlert(true)} />}
/>
</>
);
};
export default DeleteBranch;

View File

@@ -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<Props> {
}}
/>
<ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} />
<DangerZone repository={repository} indexLinks={indexLinks} />
<RepositoryDangerZone repository={repository} indexLinks={indexLinks} />
</>
);
}

View File

@@ -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<Props> = ({ repository, indexLinks }) => {
const RepositoryDangerZone: FC<Props> = ({ repository, indexLinks }) => {
const [t] = useTranslation("repos");
const dangerZone = [];
@@ -81,4 +81,4 @@ const DangerZone: FC<Props> = ({ repository, indexLinks }) => {
);
};
export default DangerZone;
export default RepositoryDangerZone;