mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-17 18:51:10 +01:00
Merged heads
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
@@ -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) => {
|
||||
|
||||
47
scm-ui/src/groups/components/MemberNameTable.js
Normal file
47
scm-ui/src/groups/components/MemberNameTable.js
Normal 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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user