Merged heads

This commit is contained in:
Philipp Czora
2018-08-07 16:20:12 +02:00
9 changed files with 142 additions and 131 deletions

View File

@@ -29,15 +29,15 @@
"edit-group-button": {
"label": "Edit"
},
"add-user-button": {
"label": "Add user"
"add-member-button": {
"label": "Add member"
},
"remove-user-button": {
"label": "Remove user"
"remove-member-button": {
"label": "Remove member"
},
"add-user-textfield": {
"label": "Add user",
"error": "Username invalid"
"add-member-textfield": {
"label": "Add member",
"error": "Invalid member name"
},
"group-form": {
"submit": "Submit",

View File

@@ -8,19 +8,19 @@ import { isMemberNameValid } from "./groupValidation"
type Props = {
t: string => string,
addUser: string => void
addMember: string => void
};
type State = {
userToAdd: string,
memberToAdd: string,
validationError: boolean
};
class AddUserField extends React.Component<Props, State> {
class AddMemberField extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
userToAdd: "",
memberToAdd: "",
validationError: false
};
}
@@ -30,16 +30,15 @@ class AddUserField extends React.Component<Props, State> {
return (
<div className="field">
<InputField
label={t("add-user-textfield.label")}
errorMessage={t("add-user-textfield.error")}
onChange={this.handleAddUserChange}
validationError={this.state.validationError}
value={this.state.userToAdd}
label={t("add-member-textfield.label")}
errorMessage={t("add-member-textfield.error")}
onChange={this.handleAddMemberChange}
validationError={this.state.validationError} //TODO: validate member name
value={this.state.memberToAdd}
onReturnPressed={this.appendMember}
/>
<AddButton
label={t("add-user-button.label")}
label={t("add-member-button.label")}
action={this.addButtonClicked}
/>
</div>
@@ -53,13 +52,14 @@ class AddUserField extends React.Component<Props, State> {
};
appendMember = () => {
this.props.addUser(this.state.userToAdd);
this.setState({ ...this.state, userToAdd: "" });
}
this.props.addMember(this.state.memberToAdd);
this.setState({ ...this.state, memberToAdd: "" });
};
handleAddUserChange = (username: string) => {
this.setState({ ...this.state, userToAdd: username, validationError: !isMemberNameValid(username)});
handleAddMemberChange = (membername: string) => {
this.setState({ ...this.state, memberToAdd: membername, validationError: !isMemberNameValid(membername) });
};
}
export default translate("groups")(AddUserField);
export default translate("groups")(AddMemberField);

View File

@@ -6,8 +6,8 @@ import { SubmitButton } from "../../components/buttons";
import { translate } from "react-i18next";
import type { Group } from "../types/Group";
import * as validator from "./groupValidation";
import AddUserField from "./AddUserField";
import UserNameTable from "./UserNameTable";
import AddMemberField from "./AddMemberField";
import MemberNameTable from "./MemberNameTable";
type Props = {
t: string => string,
@@ -95,11 +95,11 @@ class GroupForm extends React.Component<Props, State> {
value={group.description}
validationError={false}
/>
<UserNameTable
users={this.state.group.members}
userListChanged={this.userListChanged}
<MemberNameTable
members={this.state.group.members}
memberListChanged={this.memberListChanged}
/>
<AddUserField addUser={this.addUser} />
<AddMemberField addMember={this.addMember} />
<SubmitButton
disabled={!this.isValid()}
label={t("group-form.submit")}
@@ -109,19 +109,18 @@ class GroupForm extends React.Component<Props, State> {
);
}
userListChanged = usernames => {
memberListChanged = membernames => {
this.setState({
...this.state,
group: {
...this.state.group,
members: usernames
members: membernames
}
});
}
};
addUser = (username: string) => {
if (this.isMember(username)) {
addMember = (membername: string) => {
if (this.isMember(membername)) {
return;
}
@@ -129,13 +128,13 @@ class GroupForm extends React.Component<Props, State> {
...this.state,
group: {
...this.state.group,
members: [...this.state.group.members, username]
members: [...this.state.group.members, membername]
}
});
};
isMember = (username: string) => {
return this.state.group.members.includes(username);
isMember = (membername: string) => {
return this.state.group.members.includes(membername);
};
handleGroupNameChange = (name: string) => {

View File

@@ -0,0 +1,47 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import RemoveMemberButton from "./buttons/RemoveMemberButton";
type Props = {
members: string[],
t: string => string,
memberListChanged: (string[]) => void
};
type State = {};
class MemberNameTable extends React.Component<Props, State> {
render() {
const { t } = this.props;
return (
<div>
<label className="label">{t("group.members")}</label>
<table className="table is-hoverable is-fullwidth">
<tbody>
{this.props.members.map((member, index) => {
return (
<tr key={member}>
<td key={member}>{member}</td>
<td>
<RemoveMemberButton
membername={member}
removeMember={this.removeMember}
/>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
removeMember = (membername: string) => {
const newMembers = this.props.members.filter(name => name !== membername);
this.props.memberListChanged(newMembers);
};
}
export default translate("groups")(MemberNameTable);

View File

@@ -1,46 +0,0 @@
//@flow
import React from "react";
import { translate } from "react-i18next"
import RemoveUserButton from "./buttons/RemoveUserButton";
type Props = {
users: string[];
t: string => string,
userListChanged: (string[]) => void
};
type State = {
};
class UserNameTable extends React.Component<Props, State> {
render() {
const { t } = this.props;
return (
<div>
<label className="label">{t("group.members")}</label>
<table className="table is-hoverable is-fullwidth">
<tbody>
{this.props.users.map((user, index) => {
return (
<tr key={user}>
<td key={user}>{user}</td>
<td>
<RemoveUserButton username={user} removeUser={this.removeUser} />
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
removeUser = (username: string) => {
const newUsers = this.props.users.filter(name => name !== username);
this.props.userListChanged(newUsers);
}
}
export default translate("groups")(UserNameTable);

View File

@@ -6,24 +6,24 @@ import classNames from "classnames";
type Props = {
t: string => string,
username: string,
removeUser: string => void
membername: string,
removeMember: string => void
};
type State = {};
class RemoveUserButton extends React.Component<Props, State> {
class RemoveMemberButton extends React.Component<Props, State> {
render() {
const { t , username, removeUser} = this.props;
const { t , membername, removeMember} = this.props;
return (
<div className={classNames("is-pulled-right")}>
<DeleteButton
label={t("remove-user-button.label")}
label={t("remove-member-button.label")}
action={(event: Event) => {
event.preventDefault();
removeUser(username);
removeMember(membername);
}}
/>
</div>
@@ -31,4 +31,4 @@ class RemoveUserButton extends React.Component<Props, State> {
}
}
export default translate("groups")(RemoveUserButton);
export default translate("groups")(RemoveMemberButton);

View File

@@ -1,10 +1,10 @@
// @flow
import React from "react";
import { Link } from "react-router-dom";
import type { User } from "../../../users/types/User";
import type { Member } from "../../types/Group";
type Props = {
member: User
member: Member
};
export default class GroupMember extends React.Component<Props> {
@@ -12,7 +12,7 @@ export default class GroupMember extends React.Component<Props> {
return <Link to={to}>{label}</Link>;
}
showName(to: any, member: User) {
showName(to: any, member: Member) {
if (member._links.self) {
return this.renderLink(to, member.name);
} else {

View File

@@ -1,3 +1,4 @@
// @flow
import { apiClient } from "../../apiclient";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
@@ -5,7 +6,7 @@ import * as types from "../../modules/types";
import { combineReducers, Dispatch } from "redux";
import type { Action } from "../../types/Action";
import type { PagedCollection } from "../../types/Collection";
import type { Groups } from "../types/Groups";
import type { Group } from "../types/Group";
export const FETCH_GROUPS = "scm/groups/FETCH_GROUPS";
export const FETCH_GROUPS_PENDING = `${FETCH_GROUPS}_${types.PENDING_SUFFIX}`;
@@ -139,10 +140,11 @@ export function createGroup(group: Group, callback?: () => void) {
return apiClient
.postWithContentType(GROUPS_URL, group, CONTENT_TYPE_GROUP)
.then(() => {
dispatch(createGroupSuccess())
dispatch(createGroupSuccess());
if (callback) {
callback();
}})
}
})
.catch(error => {
dispatch(
createGroupFailure(
@@ -175,7 +177,7 @@ export function createGroupFailure(error: Error) {
export function createGroupReset() {
return {
type: CREATE_GROUP_RESET
}
};
}
// modify group
@@ -185,14 +187,19 @@ export function modifyGroup(group: Group, callback?: () => void) {
return apiClient
.putWithContentType(group._links.update.href, group, CONTENT_TYPE_GROUP)
.then(() => {
dispatch(modifyGroupSuccess(group))
dispatch(modifyGroupSuccess(group));
if (callback) {
callback()
callback();
}
})
.catch(cause => {
dispatch(modifyGroupFailure(group, new Error(`could not modify group ${group.name}: ${cause.message}`)))
})
dispatch(
modifyGroupFailure(
group,
new Error(`could not modify group ${group.name}: ${cause.message}`)
)
);
});
};
}
@@ -201,7 +208,7 @@ export function modifyGroupPending(group: Group): Action {
type: MODIFY_GROUP_PENDING,
payload: group,
itemId: group.name
}
};
}
export function modifyGroupSuccess(group: Group): Action {
@@ -209,7 +216,7 @@ export function modifyGroupSuccess(group: Group): Action {
type: MODIFY_GROUP_SUCCESS,
payload: group,
itemId: group.name
}
};
}
export function modifyGroupFailure(group: Group, error: Error): Action {
@@ -220,7 +227,7 @@ export function modifyGroupFailure(group: Group, error: Error): Action {
group
},
itemId: group.name
}
};
}
//delete group
@@ -274,7 +281,7 @@ export function deleteGroupFailure(group: Group, error: Error): Action {
//reducer
function extractGroupsByNames(
groups: Groups[],
groups: Group[],
groupNames: string[],
oldGroupsByNames: Object
) {
@@ -436,11 +443,11 @@ export function getCreateGroupFailure(state: Object) {
}
export function isModifyGroupPending(state: Object, name: string) {
return(isPending(state, MODIFY_GROUP, name))
return isPending(state, MODIFY_GROUP, name);
}
export function getModifyGroupFailure(state: Object, name: string) {
return(getFailure(state, MODIFY_GROUP, name))
return getFailure(state, MODIFY_GROUP, name);
}
export function getGroupByName(state: Object, name: string) {

View File

@@ -1,14 +1,18 @@
//@flow
import type { Collection } from "../../types/Collection";
import type { Links } from "../../types/hal";
import type { User } from "../../users/types/User";
export type Group = {
export type Member = {
name: string,
_links: Links
};
export type Group = Collection & {
name: string,
description: string,
type: string,
members: string[],
_links: Links,
_embedded: {
members: User[]
members: Member[]
}
};