Implemented components for adding/editing users

This commit is contained in:
Philipp Czora
2018-07-18 17:40:05 +02:00
parent 2889c6598d
commit 3c0ea782aa
7 changed files with 245 additions and 111 deletions

View File

@@ -10,6 +10,8 @@ import Logout from "../containers/Logout";
import { Switch } from "react-router-dom";
import ProtectedRoute from "../components/ProtectedRoute";
import EditUser from "../users/containers/EditUser";
import AddUser from "../users/containers/AddUser";
type Props = {
authenticated?: boolean
@@ -30,10 +32,21 @@ class Main extends React.Component<Props> {
<Route exact path="/login" component={Login} />
<Route path="/logout" component={Logout} />
<ProtectedRoute
exact
path="/users"
component={Users}
authenticated={authenticated}
/>
<ProtectedRoute
authenticated={authenticated}
path="/users/edit/:name"
component={EditUser}
/>
<ProtectedRoute
authenticated={authenticated}
path="/users/add"
component={AddUser}
/>
</Switch>
</div>
);

View File

@@ -0,0 +1,41 @@
//@flow
import React from "react";
import { connect } from "react-redux";
import UserForm from "./UserForm";
import type { User } from "../types/User";
import { addUser } from "../modules/users";
import { Route, Link } from "react-router-dom";
type Props = {
addUser: User => void
};
class AddUser extends React.Component<Props> {
render() {
const addUser = this.props.addUser;
return (
<div>
<UserForm submitForm={user => addUser(user)} />
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
addUser: (user: User) => {
dispatch(addUser(user));
}
};
};
const mapStateToProps = (state, ownProps) => {
return {};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(AddUser);

View File

@@ -0,0 +1,67 @@
//@flow
import React from "react";
import { connect } from "react-redux";
import UserForm from "./UserForm";
import type { User } from "../types/User";
import {
updateUser,
deleteUser,
editUser,
fetchUser,
getUsersFromState
} from "../modules/users";
import { Route, Link } from "react-router-dom";
type Props = {
name: string,
fetchUser: string => void,
usersByNames: Map<string, any>,
updateUser: User => void
};
class EditUser extends React.Component<Props> {
componentDidMount() {
this.props.fetchUser(this.props.name);
}
render() {
const submitUser = this.props.updateUser;
const { usersByNames, name } = this.props;
if (!usersByNames || usersByNames[name].loading) {
return <div>Loading...</div>;
} else {
const user = usersByNames[name].entry;
return (
<div>
<UserForm submitForm={user => submitUser(user)} user={user} />
</div>
);
}
}
}
const mapDispatchToProps = dispatch => {
return {
fetchUser: (name: string) => {
dispatch(fetchUser(name));
},
updateUser: (user: User) => {
dispatch(updateUser(user));
}
};
};
const mapStateToProps = (state, ownProps) => {
return {
usersByNames: state.users.usersByNames,
name: ownProps.match.params.name
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(EditUser);

View File

@@ -3,24 +3,24 @@ import React from "react";
import EditButton from "../../components/EditButton";
import type { User } from "../types/User";
import type { UserEntry } from "../types/UserEntry";
import { Link } from "react-router-dom";
type Props = {
entry: UserEntry,
editUser: User => void
entry: UserEntry
};
class EditUserButton extends React.Component<Props> {
render() {
const { entry } = this.props;
const link = "/users/edit/" + entry.entry.name;
if (!this.isEditable()) {
return "";
}
const { entry, editUser } = this.props;
return (
<EditButton
label="Edit"
action={e => editUser(entry.entry)}
loading={entry.loading}
/>
<Link to={link}>
<EditButton label="Edit" action={() => {}} loading={entry.loading} />
</Link>
);
}

View File

@@ -23,17 +23,18 @@ class UserForm extends React.Component<Props, User> {
};
}
componentDidMount() {
this.setState({ ...this.props.user });
}
submit = (event: Event) => {
event.preventDefault();
this.props.submitForm(this.state);
};
componentWillReceiveProps() {
this.setState(this.props.user);
}
render() {
const user = this.state;
if (user) {
return (
<div className="container">
<form onSubmit={this.submit}>
@@ -72,6 +73,9 @@ class UserForm extends React.Component<Props, User> {
</form>
</div>
);
} else {
return <div>Loading...</div>;
}
}
handleUsernameChange = (name: string) => {

View File

@@ -2,17 +2,9 @@
import React from "react";
import { connect } from "react-redux";
import {
fetchUsers,
addUser,
updateUser,
deleteUser,
editUser,
getUsersFromState
} from "../modules/users";
import { fetchUsers, deleteUser, getUsersFromState } from "../modules/users";
import Loading from "../../components/Loading";
import ErrorNotification from "../../components/ErrorNotification";
import UserForm from "./UserForm";
import UserTable from "./UserTable";
import type { User } from "../types/User";
import type { UserEntry } from "../types/UserEntry";
@@ -22,11 +14,7 @@ type Props = {
error: Error,
userEntries: Array<UserEntry>,
fetchUsers: () => void,
deleteUser: User => void,
addUser: User => void,
updateUser: User => void,
editUser: User => void,
userToEdit: User
deleteUser: User => void
};
class Users extends React.Component<Props, User> {
@@ -34,28 +22,6 @@ class Users extends React.Component<Props, User> {
this.props.fetchUsers();
}
addUser = (user: User) => {
this.props.addUser(user);
};
updateUser = (user: User) => {
this.props.updateUser(user);
};
componentDidUpdate(prevProps: Props) {
if (prevProps.userToEdit !== this.props.userToEdit) {
this.setState(this.props.userToEdit);
}
}
submitUser = (user: User) => {
if (user._links && user._links.update) {
this.updateUser(user);
} else {
this.addUser(user);
}
};
render() {
return (
<section className="section">
@@ -69,20 +35,12 @@ class Users extends React.Component<Props, User> {
}
renderContent() {
const { userEntries, deleteUser, editUser, userToEdit, error } = this.props;
const { userEntries, deleteUser, error } = this.props;
if (userEntries) {
return (
<div>
<ErrorNotification error={error} />
<UserTable
entries={userEntries}
deleteUser={deleteUser}
editUser={user => editUser(user)}
/>
<UserForm
submitForm={user => this.submitUser(user)}
user={userToEdit}
/>
<UserTable entries={userEntries} deleteUser={deleteUser} />
</div>
);
} else {
@@ -93,13 +51,8 @@ class Users extends React.Component<Props, User> {
const mapStateToProps = state => {
const userEntries = getUsersFromState(state);
const userToEdit = state.users.editUser;
if (!userEntries) {
return { userToEdit };
}
return {
userEntries,
userToEdit,
error: state.users.error
};
};
@@ -109,17 +62,8 @@ const mapDispatchToProps = dispatch => {
fetchUsers: () => {
dispatch(fetchUsers());
},
addUser: (user: User) => {
dispatch(addUser(user));
},
updateUser: (user: User) => {
dispatch(updateUser(user));
},
deleteUser: (user: User) => {
dispatch(deleteUser(user));
},
editUser: (user: User) => {
dispatch(editUser(user));
}
};
};

View File

@@ -9,6 +9,9 @@ export const FETCH_USERS_SUCCESS = "scm/users/FETCH_SUCCESS";
export const FETCH_USERS_FAILURE = "scm/users/FETCH_FAILURE";
export const FETCH_USERS_NOTFOUND = "scm/users/FETCH_NOTFOUND";
export const FETCH_USER = "scm/users/FETCH_USER";
export const FETCH_USER_SUCCESS = "scm/users/FETCH_USER_SUCCESS";
export const ADD_USER = "scm/users/ADD";
export const ADD_USER_SUCCESS = "scm/users/ADD_SUCCESS";
export const ADD_USER_FAILURE = "scm/users/ADD_FAILURE";
@@ -24,6 +27,7 @@ export const DELETE_USER_SUCCESS = "scm/users/DELETE_SUCCESS";
export const DELETE_USER_FAILURE = "scm/users/DELETE_FAILURE";
const USERS_URL = "users";
const USER_URL = "users/";
const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2";
@@ -81,6 +85,47 @@ function fetchUsersSuccess(users: any) {
};
}
function requestUser(name: string) {
return {
type: FETCH_USER,
payload: { name }
};
}
export function fetchUser(name: string) {
const userUrl = USER_URL + name;
return function(dispatch: any) {
dispatch(requestUsers());
return apiClient
.get(userUrl)
.then(response => {
return response;
})
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
dispatch(fetchUserSuccess(data));
})
.catch(err => {
if (err === NOT_FOUND_ERROR) {
dispatch(usersNotFound(userUrl));
} else {
dispatch(failedToFetchUsers(userUrl, err));
}
});
};
}
function fetchUserSuccess(user: User) {
return {
type: FETCH_USER_SUCCESS,
payload: user
};
}
function requestAddUser(user: User) {
return {
type: ADD_USER,
@@ -258,7 +303,13 @@ export default function reducer(state: any = {}, action: any = {}) {
error: null,
entry: action.payload
});
case FETCH_USER:
return reduceUsersByNames(state, action.payload.name, {
loading: true,
error: null
});
case FETCH_USERS_SUCCESS:
// return red(state, action.payload._embedded.users);
const users = action.payload._embedded.users;
const userNames = users.map(user => user.name);
const usersByNames = extractUsersByNames(
@@ -266,7 +317,6 @@ export default function reducer(state: any = {}, action: any = {}) {
userNames,
state.usersByNames
);
return {
...state,
users: {
@@ -276,6 +326,21 @@ export default function reducer(state: any = {}, action: any = {}) {
},
usersByNames
};
case FETCH_USER_SUCCESS:
const ubn = extractUsersByNames(
[action.payload],
[action.payload.name],
state.usersByNames
);
return {
...state,
users: {
error: null,
entries: [action.payload.name],
loading: false
},
usersByNames: ubn
};
case FETCH_USERS_FAILURE:
case DELETE_USER_FAILURE: