mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
add 'delete branch' function to frontend
This commit is contained in:
@@ -62,11 +62,11 @@ export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close }) => {
|
|||||||
|
|
||||||
const footer = (
|
const footer = (
|
||||||
<div className="field is-grouped">
|
<div className="field is-grouped">
|
||||||
{buttons.map((button, i) => (
|
{buttons.map((button, index) => (
|
||||||
<p className="control">
|
<p className="control" key={index}>
|
||||||
<a
|
<a
|
||||||
className={classNames("button", "is-info", button.className)}
|
className={classNames("button", "is-info", button.className)}
|
||||||
key={i}
|
key={index}
|
||||||
onClick={() => handleClickButton(button)}
|
onClick={() => handleClickButton(button)}
|
||||||
>
|
>
|
||||||
{button.label}
|
{button.label}
|
||||||
|
|||||||
@@ -68,7 +68,19 @@
|
|||||||
"name": "Name:",
|
"name": "Name:",
|
||||||
"commits": "Commits",
|
"commits": "Commits",
|
||||||
"sources": "Sources",
|
"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": {
|
"tags": {
|
||||||
"overview": {
|
"overview": {
|
||||||
|
|||||||
@@ -68,7 +68,19 @@
|
|||||||
"name": "Name:",
|
"name": "Name:",
|
||||||
"commits": "Commits",
|
"commits": "Commits",
|
||||||
"sources": "Sources",
|
"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": {
|
"tags": {
|
||||||
"overview": {
|
"overview": {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const BranchRow: FC<Props> = ({ baseUrl, branch, onDelete }) => {
|
|||||||
deleteButton = (
|
deleteButton = (
|
||||||
<a className="level-item" onClick={() => onDelete(url)}>
|
<a className="level-item" onClick={() => onDelete(url)}>
|
||||||
<span className="icon is-small">
|
<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>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,19 +21,40 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React, { FC } from "react";
|
import React, { FC, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import BranchRow from "./BranchRow";
|
import BranchRow from "./BranchRow";
|
||||||
import { Branch } from "@scm-manager/ui-types";
|
import { Branch } from "@scm-manager/ui-types";
|
||||||
|
import { apiClient, ConfirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
branches: Branch[];
|
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 [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 = () => {
|
const renderRow = () => {
|
||||||
let rowContent = null;
|
let rowContent = null;
|
||||||
@@ -44,15 +65,39 @@ const BranchTable: FC<Props> = ({ baseUrl, branches, onDelete }) => {
|
|||||||
}
|
}
|
||||||
return rowContent;
|
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 (
|
return (
|
||||||
<table className="card-table table is-hoverable is-fullwidth is-word-break">
|
<>
|
||||||
<thead>
|
{showConfirmAlert && confirmAlert}
|
||||||
<tr>
|
{error && <ErrorNotification error={error} />}
|
||||||
<th>{t("branches.table.branches")}</th>
|
<table className="card-table table is-hoverable is-fullwidth is-word-break">
|
||||||
</tr>
|
<thead>
|
||||||
</thead>
|
<tr>
|
||||||
<tbody>{renderRow()}</tbody>
|
<th>{t("branches.table.branches")}</th>
|
||||||
</table>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{renderRow()}</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import React from "react";
|
|||||||
import BranchDetail from "./BranchDetail";
|
import BranchDetail from "./BranchDetail";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
|
import BranchDangerZone from "../containers/BranchDangerZone";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
@@ -34,7 +35,6 @@ type Props = {
|
|||||||
class BranchView extends React.Component<Props> {
|
class BranchView extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { repository, branch } = this.props;
|
const { repository, branch } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BranchDetail repository={repository} branch={branch} />
|
<BranchDetail repository={repository} branch={branch} />
|
||||||
@@ -49,6 +49,7 @@ class BranchView extends React.Component<Props> {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<BranchDangerZone repository={repository} branch={branch} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -28,10 +28,9 @@ import { compose } from "redux";
|
|||||||
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
|
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
|
||||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
import { fetchBranch, getBranch, getFetchBranchFailure, isFetchBranchPending } from "../modules/branches";
|
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 { History } from "history";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { urls } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import {
|
|||||||
} from "../modules/branches";
|
} from "../modules/branches";
|
||||||
import { orderBranches } from "../util/orderBranches";
|
import { orderBranches } from "../util/orderBranches";
|
||||||
import BranchTable from "../components/BranchTable";
|
import BranchTable from "../components/BranchTable";
|
||||||
import { apiClient } from "@scm-manager/ui-components/src";
|
|
||||||
|
|
||||||
type Props = WithTranslation & {
|
type Props = WithTranslation & {
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
@@ -81,15 +80,11 @@ class BranchesOverview extends React.Component<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelete(url: string) {
|
|
||||||
apiClient.delete(url).catch(error => this.setState({ error }));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBranchesTable() {
|
renderBranchesTable() {
|
||||||
const { baseUrl, branches, t } = this.props;
|
const { baseUrl, branches, repository, fetchBranches, t } = this.props;
|
||||||
if (branches && branches.length > 0) {
|
if (branches && branches.length > 0) {
|
||||||
orderBranches(branches);
|
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>;
|
return <Notification type="info">{t("branches.overview.noBranches")}</Notification>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -31,7 +31,7 @@ import { History } from "history";
|
|||||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
import { compose } from "redux";
|
import { compose } from "redux";
|
||||||
import DangerZone from "./DangerZone";
|
import RepositoryDangerZone from "./RepositoryDangerZone";
|
||||||
import { getLinks } from "../../modules/indexResource";
|
import { getLinks } from "../../modules/indexResource";
|
||||||
import { urls } from "@scm-manager/ui-components";
|
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} />
|
<ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} />
|
||||||
<DangerZone repository={repository} indexLinks={indexLinks} />
|
<RepositoryDangerZone repository={repository} indexLinks={indexLinks} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ type Props = {
|
|||||||
indexLinks: Links;
|
indexLinks: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DangerZoneContainer = styled.div`
|
export const DangerZoneContainer = styled.div`
|
||||||
padding: 1.5rem 1rem;
|
padding: 1.5rem 1rem;
|
||||||
border: 1px solid #ff6a88;
|
border: 1px solid #ff6a88;
|
||||||
border-radius: 5px;
|
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 [t] = useTranslation("repos");
|
||||||
|
|
||||||
const dangerZone = [];
|
const dangerZone = [];
|
||||||
@@ -81,4 +81,4 @@ const DangerZone: FC<Props> = ({ repository, indexLinks }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DangerZone;
|
export default RepositoryDangerZone;
|
||||||
Reference in New Issue
Block a user