mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 19:15:52 +01:00
fixed branch selection urls and styles branch selector
This commit is contained in:
@@ -3,16 +3,16 @@ import React from "react";
|
||||
import {translate} from "react-i18next";
|
||||
import type {PagedCollection} from "@scm-manager/ui-types";
|
||||
import {Button} from "./buttons";
|
||||
import {withRouter} from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
collection: PagedCollection,
|
||||
t: string => string,
|
||||
match: any
|
||||
page: number,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class LinkPaginator extends React.Component<Props> {
|
||||
//TODO: HATEOAS-Links verwenden
|
||||
|
||||
renderFirstButton() {
|
||||
return (
|
||||
@@ -26,30 +26,32 @@ class LinkPaginator extends React.Component<Props> {
|
||||
}
|
||||
|
||||
renderPreviousButton(label?: string) {
|
||||
const { match } = this.props;
|
||||
const page = parseInt(match.params.page) || 1;
|
||||
const { page } = this.props;
|
||||
const previousPage = page - 1;
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-previous"}
|
||||
label={label ? label : previousPage.toString()}
|
||||
disabled={previousPage < 1}
|
||||
disabled={!this.hasLink("prev")}
|
||||
link={`${previousPage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderNextButton(label?: string) {
|
||||
const { match, collection } = this.props;
|
||||
let page = parseInt(match.params.page) || 1;
|
||||
hasLink(name: string) {
|
||||
const { collection } = this.props;
|
||||
return collection._links[name];
|
||||
}
|
||||
|
||||
renderNextButton(label?: string) {
|
||||
const { page } = this.props;
|
||||
const nextPage = page + 1;
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-next"}
|
||||
label={label ? label : nextPage.toString()}
|
||||
disabled={nextPage >= collection.pageTotal + 1}
|
||||
disabled={!this.hasLink("next")}
|
||||
link={`${nextPage}`}
|
||||
/>
|
||||
);
|
||||
@@ -128,4 +130,4 @@ class LinkPaginator extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(translate("commons")(LinkPaginator));
|
||||
export default translate("commons")(LinkPaginator);
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
"mail": "Mail"
|
||||
}
|
||||
},
|
||||
"branch-selector": {
|
||||
"label": "Branches"
|
||||
},
|
||||
"branch-chooser": {
|
||||
"error-title": "Error",
|
||||
"error-subtitle": "Could not fetch branches"
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
options: string[],
|
||||
optionSelected: string => void,
|
||||
preselectedOption?: string
|
||||
preselectedOption?: string,
|
||||
className: any
|
||||
};
|
||||
|
||||
class DropDown extends React.Component<Props> {
|
||||
render() {
|
||||
const { options, preselectedOption } = this.props;
|
||||
const { options, preselectedOption, className } = this.props;
|
||||
return (
|
||||
<div className="select">
|
||||
<div className={classNames(className, "select")}>
|
||||
<select
|
||||
value={preselectedOption ? preselectedOption : ""}
|
||||
onChange={this.change}
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "../modules/branches";
|
||||
import DropDown from "../components/DropDown";
|
||||
import type { History } from "history";
|
||||
import { ErrorPage, Loading } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
label: string, //TODO: Should this be here?
|
||||
onChange: string => void,
|
||||
children: React.Node,
|
||||
selected?: string,
|
||||
|
||||
// State props
|
||||
branches: Branch[],
|
||||
error: Error,
|
||||
loading: boolean,
|
||||
|
||||
// Dispatch props
|
||||
fetchBranches: Repository => void,
|
||||
|
||||
// Context props
|
||||
t: string => string
|
||||
};
|
||||
type State = {
|
||||
selectedBranch?: Branch
|
||||
};
|
||||
|
||||
class BranchChooser extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchBranches(this.props.repository);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const { loading, error, t, repository } = this.props;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("branch-chooser.error-title")}
|
||||
subtitle={t("branch-chooser.error-subtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!repository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const { selectedBranch } = this.state;
|
||||
|
||||
const childrenWithBranch = React.Children.map(
|
||||
this.props.children,
|
||||
child => {
|
||||
return React.cloneElement(child, {
|
||||
branch: selectedBranch
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderBranchChooser()}
|
||||
{childrenWithBranch}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderBranchChooser() {
|
||||
const { label, branches, selected } = this.props;
|
||||
|
||||
if (!branches || branches.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"box"}>
|
||||
<label className="label">{label}</label>
|
||||
<DropDown
|
||||
options={branches.map(b => b.name)}
|
||||
preselectedOption={
|
||||
this.state.selectedBranch
|
||||
? this.state.selectedBranch.name
|
||||
: selected
|
||||
}
|
||||
optionSelected={(branchName: string) => {
|
||||
this.branchSelected(branchName, true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
branchSelected = (branch: string, changed: boolean) => {
|
||||
for (let b of this.props.branches) {
|
||||
if (b.name === branch) {
|
||||
this.updateBranch(branch, b, changed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateBranch = (branchName: string, branch: Branch, changed: boolean) => {
|
||||
this.setState(
|
||||
prevState => {
|
||||
if (
|
||||
!prevState.selectedBranch ||
|
||||
branchName !== prevState.selectedBranch.name
|
||||
) {
|
||||
return { selectedBranch: branch };
|
||||
}
|
||||
},
|
||||
() => {
|
||||
if (changed) {
|
||||
this.props.onChange(branch.name);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranches: (repo: Repository) => {
|
||||
dispatch(fetchBranches(repo));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { repository } = ownProps;
|
||||
const loading = isFetchBranchesPending(state, repository);
|
||||
const error = getFetchBranchesFailure(state, repository);
|
||||
|
||||
const branches = getBranches(state, repository);
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
branches
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(BranchChooser));
|
||||
@@ -19,6 +19,8 @@ type Props = {
|
||||
repository: Repository,
|
||||
baseUrl: string,
|
||||
selected: string,
|
||||
baseUrlWithBranch: string,
|
||||
baseUrlWithoutBranch: string,
|
||||
|
||||
// State props
|
||||
branches: Branch[],
|
||||
@@ -49,15 +51,16 @@ class BranchRoot extends React.Component<Props> {
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.baseUrl);
|
||||
};
|
||||
|
||||
branchSelected = (branch: Branch) => {
|
||||
const url = this.matchedUrl();
|
||||
this.props.history.push(
|
||||
`${url}/${encodeURIComponent(branch.name)}/changesets/`
|
||||
);
|
||||
branchSelected = (branch?: Branch) => {
|
||||
let url;
|
||||
if (branch) {
|
||||
url = `${this.props.baseUrlWithBranch}/${encodeURIComponent(
|
||||
branch.name
|
||||
)}/changesets/`;
|
||||
} else {
|
||||
url = `${this.props.baseUrlWithoutBranch}/`;
|
||||
}
|
||||
this.props.history.push(url);
|
||||
};
|
||||
|
||||
findSelectedBranch = () => {
|
||||
|
||||
@@ -3,10 +3,24 @@
|
||||
import React from "react";
|
||||
import type { Branch } from "@scm-manager/ui-types";
|
||||
import DropDown from "../components/DropDown";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
import { compose } from "redux";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
zeroflex: {
|
||||
flexGrow: 0
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
branches: Branch[], // TODO: Use generics?
|
||||
selected?: Branch => void
|
||||
selected?: Branch => void,
|
||||
|
||||
// context props
|
||||
classes: Object,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
type State = { selectedBranch?: Branch };
|
||||
@@ -18,19 +32,33 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { branches } = this.props;
|
||||
const { branches, classes, t } = this.props;
|
||||
|
||||
if (branches) {
|
||||
return (
|
||||
<>
|
||||
<DropDown
|
||||
options={branches.map(b => b.name)}
|
||||
optionSelected={this.branchSelected}
|
||||
preselectedOption={
|
||||
this.state.selectedBranch ? this.state.selectedBranch.name : ""
|
||||
}
|
||||
/>
|
||||
</>
|
||||
<div className="box field is-horizontal">
|
||||
<div
|
||||
className={classNames("field-label", "is-normal", classes.zeroflex)}
|
||||
>
|
||||
<label className="label">{t("branch-selector.label")}</label>
|
||||
</div>
|
||||
<div className="field-body">
|
||||
<div className="field is-narrow">
|
||||
<div className="control">
|
||||
<DropDown
|
||||
className="is-fullwidth"
|
||||
options={branches.map(b => b.name)}
|
||||
optionSelected={this.branchSelected}
|
||||
preselectedOption={
|
||||
this.state.selectedBranch
|
||||
? this.state.selectedBranch.name
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,11 +67,12 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
const { branches, selected } = this.props;
|
||||
const branch = branches.find(b => b.name === branchName);
|
||||
|
||||
if (branch) {
|
||||
selected(branch);
|
||||
this.setState({ selectedBranch: branch });
|
||||
}
|
||||
selected(branch);
|
||||
this.setState({ selectedBranch: branch });
|
||||
};
|
||||
}
|
||||
|
||||
export default BranchSelector;
|
||||
export default compose(
|
||||
injectSheet(styles),
|
||||
translate("repos")
|
||||
)(BranchSelector);
|
||||
|
||||
@@ -111,12 +111,23 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
path={`${url}/edit`}
|
||||
component={() => <Edit repository={repository} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/changesets`}
|
||||
render={() => (
|
||||
<BranchRoot
|
||||
repository={repository}
|
||||
baseUrlWithBranch={`${url}/branches`}
|
||||
baseUrlWithoutBranch={`${url}/changesets`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/branches/:branch/changesets`}
|
||||
render={() => (
|
||||
<BranchRoot
|
||||
repository={repository}
|
||||
baseUrl={`${url}/branches`}
|
||||
baseUrlWithBranch={`${url}/branches`}
|
||||
baseUrlWithoutBranch={`${url}/changesets`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -128,7 +139,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
<NavLink to={url} label={t("repository-root.information")} />
|
||||
<NavLink
|
||||
activeOnlyWhenExact={false}
|
||||
to={`${url}/branches/master/changesets/1`}
|
||||
to={`${url}/changesets/`}
|
||||
label={t("repository-root.history")}
|
||||
activeWhenMatch={this.matches}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user