mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
Added group editing feature
This commit is contained in:
@@ -10,7 +10,8 @@ import * as validator from "./groupValidation";
|
||||
type Props = {
|
||||
t: string => string,
|
||||
submitForm: Group => void,
|
||||
loading?: boolean
|
||||
loading?: boolean,
|
||||
group?: Group
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -36,6 +37,13 @@ class GroupForm extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { group } = this.props
|
||||
if (group) {
|
||||
this.setState({group: {...group}})
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.props.submitForm(this.state.group);
|
||||
@@ -55,18 +63,27 @@ class GroupForm extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { t, loading } = this.props;
|
||||
return (
|
||||
<form onSubmit={this.onSubmit}>
|
||||
const { group } = this.state
|
||||
let nameField = null;
|
||||
if (!this.props.group) {
|
||||
nameField = (
|
||||
<InputField
|
||||
label={t("group.name")}
|
||||
errorMessage="group name invalid"
|
||||
errorMessage="group name invalid" // TODO: i18n
|
||||
onChange={this.handleGroupNameChange}
|
||||
value={group.name}
|
||||
validationError={this.state.nameValidationError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form onSubmit={this.onSubmit}>
|
||||
{nameField}
|
||||
<InputField
|
||||
label={t("group.description")}
|
||||
errorMessage=""
|
||||
onChange={this.handleDescriptionChange}
|
||||
value={group.description}
|
||||
validationError={false}
|
||||
/>
|
||||
<SubmitButton label={t("group-form.submit")} loading={loading}/>
|
||||
|
||||
56
scm-ui/src/groups/containers/EditGroup.js
Normal file
56
scm-ui/src/groups/containers/EditGroup.js
Normal file
@@ -0,0 +1,56 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import GroupForm from "../components/GroupForm";
|
||||
import { modifyGroup } from "../modules/groups"
|
||||
import type { History } from "history";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { Group } from "../types/Group"
|
||||
import { isModifyGroupPending, getModifyGroupFailure } from "../modules/groups"
|
||||
import ErrorNotification from "../../components/ErrorNotification";
|
||||
|
||||
type Props = {
|
||||
group: Group,
|
||||
modifyGroup: (group: Group, callback?: () => void) => void,
|
||||
history: History,
|
||||
loading?: boolean,
|
||||
error: Error
|
||||
};
|
||||
|
||||
class EditGroup extends React.Component<Props> {
|
||||
groupModified = (group: Group) => {
|
||||
this.props.history.push(`/group/${group.name}`)
|
||||
}
|
||||
|
||||
modifyGroup = (group: Group) => {
|
||||
this.props.modifyGroup(group, this.groupModified(group));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { group, loading, error } = this.props;
|
||||
return <div>
|
||||
<ErrorNotification error={error} />
|
||||
<GroupForm group={group} submitForm={(group) =>{this.modifyGroup(group)}} loading={loading}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const loading = isModifyGroupPending(state, ownProps.group.name)
|
||||
const error = getModifyGroupFailure(state, ownProps.group.name)
|
||||
return {
|
||||
loading,
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
modifyGroup: (group: Group, callback?: () => void) => {dispatch(modifyGroup(group, callback))}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(withRouter(EditGroup));
|
||||
@@ -18,6 +18,7 @@ import { Navigation, Section, NavLink } from "../../components/navigation";
|
||||
import ErrorPage from "../../components/ErrorPage";
|
||||
import { translate } from "react-i18next";
|
||||
import EditGroupNavLink from "../components/navLinks/EditGroupNavLink";
|
||||
import EditGroup from "./EditGroup";
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
@@ -74,6 +75,7 @@ class SingleGroup extends React.Component<Props> {
|
||||
<div className="columns">
|
||||
<div className="column is-three-quarters">
|
||||
<Route path={url} exact component={() => <Details group={group} />} />
|
||||
<Route path={`${url}/edit`} exact component={() => <EditGroup group={group} />} />
|
||||
</div>
|
||||
<div className="column">
|
||||
<Navigation>
|
||||
|
||||
@@ -177,6 +177,49 @@ export function createGroupReset() {
|
||||
type: CREATE_GROUP_RESET
|
||||
}
|
||||
}
|
||||
|
||||
// modify group
|
||||
export function modifyGroup(group: Group, callback?: () => void) {
|
||||
return function(dispatch: Dispatch) {
|
||||
dispatch(modifyGroupPending());
|
||||
return apiClient
|
||||
.putWithContentType(group._links.update.href, group, CONTENT_TYPE_GROUP)
|
||||
.then(() => {
|
||||
dispatch(modifyGroupSuccess(group))
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
.catch(cause => {
|
||||
dispatch(modifyGroupFailure(group, new Error(`could not modify group ${group.name}: ${cause.message}`)))
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyGroupPending(): Action {
|
||||
return {
|
||||
type: MODIFY_GROUP_PENDING
|
||||
}
|
||||
}
|
||||
|
||||
export function modifyGroupSuccess(group: Group): Action {
|
||||
return {
|
||||
type: MODIFY_GROUP_SUCCESS,
|
||||
payload: group
|
||||
}
|
||||
}
|
||||
|
||||
export function modifyGroupFailure(group: Group, error: Error): Action {
|
||||
return {
|
||||
type: MODIFY_GROUP_FAILURE,
|
||||
payload: {
|
||||
error,
|
||||
group
|
||||
},
|
||||
itemId: group.name
|
||||
}
|
||||
}
|
||||
|
||||
//delete group
|
||||
|
||||
export function deleteGroup(group: Group, callback?: () => void) {
|
||||
@@ -311,6 +354,8 @@ function byNamesReducer(state: any = {}, action: any = {}) {
|
||||
};
|
||||
case FETCH_GROUP_SUCCESS:
|
||||
return reducerByName(state, action.payload.name, action.payload);
|
||||
case MODIFY_GROUP_SUCCESS:
|
||||
return reducerByName(state, action.payload.name, action.payload);
|
||||
case DELETE_GROUP_SUCCESS:
|
||||
const newGroupByNames = deleteGroupInGroupsByNames(
|
||||
state,
|
||||
@@ -387,6 +432,14 @@ export function getCreateGroupFailure(state: Object) {
|
||||
return getFailure(state, CREATE_GROUP);
|
||||
}
|
||||
|
||||
export function isModifyGroupPending(state: Object, name: string) {
|
||||
return(isPending(state, MODIFY_GROUP, name))
|
||||
}
|
||||
|
||||
export function getModifyGroupFailure(state: Object, name: string) {
|
||||
return(getFailure(state, MODIFY_GROUP, name))
|
||||
}
|
||||
|
||||
export function getGroupByName(state: Object, name: string) {
|
||||
if (state.groups && state.groups.byNames) {
|
||||
return state.groups.byNames[name];
|
||||
|
||||
@@ -38,7 +38,11 @@ import reducer, {
|
||||
DELETE_GROUP,
|
||||
deleteGroupSuccess,
|
||||
isDeleteGroupPending,
|
||||
getDeleteGroupFailure
|
||||
getDeleteGroupFailure,
|
||||
modifyGroup,
|
||||
MODIFY_GROUP_PENDING,
|
||||
MODIFY_GROUP_SUCCESS,
|
||||
MODIFY_GROUP_FAILURE
|
||||
} from "./groups";
|
||||
const GROUPS_URL = "/scm/api/rest/v2/groups";
|
||||
|
||||
@@ -239,6 +243,55 @@ describe("groups fetch()", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should successfully modify group", () => {
|
||||
fetchMock.putOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", {
|
||||
status: 204
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
return store.dispatch(modifyGroup(humanGroup)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_GROUP_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_GROUP_SUCCESS);
|
||||
expect(actions[1].payload).toEqual(humanGroup)
|
||||
});
|
||||
})
|
||||
|
||||
it("should call the callback after modifying group", () => {
|
||||
fetchMock.putOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", {
|
||||
status: 204
|
||||
});
|
||||
|
||||
let called = false;
|
||||
const callback = () => {
|
||||
called = true;
|
||||
}
|
||||
const store = mockStore({});
|
||||
|
||||
return store.dispatch(modifyGroup(humanGroup, callback)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_GROUP_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_GROUP_SUCCESS);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
})
|
||||
|
||||
it("should fail modifying group on HTTP 500", () => {
|
||||
fetchMock.putOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
return store.dispatch(modifyGroup(humanGroup)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_GROUP_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_GROUP_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
})
|
||||
|
||||
it("should delete successfully group humanGroup", () => {
|
||||
fetchMock.deleteOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", {
|
||||
status: 204
|
||||
|
||||
Reference in New Issue
Block a user