Added user add functionality

This commit is contained in:
Philipp Czora
2018-07-11 17:02:38 +02:00
parent d35a56e07e
commit 1b6df5ee08
9 changed files with 317 additions and 38 deletions

View File

@@ -42,6 +42,24 @@ class ApiClient {
return this.httpRequestWithJSONBody(url, payload, "POST");
}
postWithContentType(url: string, payload: any, contentType: string) {
return this.httpRequestWithContentType(
url,
"POST",
JSON.stringify(payload),
contentType
);
}
putWithContentType(url: string, payload: any, contentType: string) {
return this.httpRequestWithContentType(
url,
"PUT",
JSON.stringify(payload),
contentType
);
}
delete(url: string): Promise<Response> {
let options: RequestOptions = {
method: "DELETE"
@@ -54,14 +72,38 @@ class ApiClient {
url: string,
payload: any,
method: string
): Promise<Response> {
// let options: RequestOptions = {
// method: method,
// body: JSON.stringify(payload)
// };
// options = Object.assign(options, fetchOptions);
// // $FlowFixMe
// options.headers["Content-Type"] = "application/json";
// return fetch(createUrl(url), options).then(handleStatusCode);
return this.httpRequestWithContentType(
url,
method,
JSON.stringify(payload),
"application/json"
).then(handleStatusCode);
}
httpRequestWithContentType(
url: string,
method: string,
payload: any,
contentType: string
): Promise<Response> {
let options: RequestOptions = {
method: method,
body: JSON.stringify(payload)
body: payload
};
options = Object.assign(options, fetchOptions);
// $FlowFixMe
options.headers["Content-Type"] = "application/json";
options.headers["Content-Type"] = contentType;
return fetch(createUrl(url), options).then(handleStatusCode);
}

View File

@@ -0,0 +1,27 @@
//@flow
import React from "react";
type Props = {
label: string,
onChange: boolean => void
};
class Checkbox extends React.Component<Props> {
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.props.onChange(event.target.checked);
};
render() {
return (
<div className="field">
<div className="control">
<label className="checkbox">
<input type="checkbox" onChange={this.onCheckboxChange} />{" "}
{this.props.label}
</label>
</div>
</div>
);
}
}
export default Checkbox;

View File

@@ -22,7 +22,7 @@ class InputField extends React.Component<Props> {
renderLabel = () => {
const label = this.props.label;
if (label) {
return <label class="label">{label}</label>;
return <label className="label">{label}</label>;
}
return "";
};
@@ -31,11 +31,11 @@ class InputField extends React.Component<Props> {
const { type, placeholder } = this.props;
return (
<div class="field">
<div className="field">
{this.renderLabel()}
<div class="control">
<div className="control">
<input
class="input"
className="input"
type={type}
placeholder={placeholder}
onChange={this.handleInput}

View File

@@ -4,6 +4,7 @@ import Login from "./Login";
import { getIsAuthenticated } from "../modules/login";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { ThunkDispatch } from "redux-thunk";
import "./App.css";
import Header from "../components/Header";
@@ -44,7 +45,7 @@ class App extends Component<Props> {
}
}
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch: ThunkDispatch) => {
return {
getAuthState: () => dispatch(getIsAuthenticated())
};

View File

@@ -0,0 +1,86 @@
// @flow
import React from "react";
import type { User } from "../types/User";
import InputField from "../../components/InputField";
import Checkbox from "../../components/Checkbox";
import SubmitButton from "../../components/SubmitButton";
type Props = {
submitForm: User => void,
user?: User
};
class UserForm extends React.Component<Props, User> {
submit = (event: Event) => {
event.preventDefault();
this.props.submitForm(this.state);
};
render() {
const { submitForm, user } = this.props;
return (
<div className="container">
<form onSubmit={this.submit}>
<InputField
label="Username"
onChange={this.handleUsernameChange}
value={user !== undefined ? user.name : ""}
/>
<InputField
label="Display Name"
onChange={this.handleDisplayNameChange}
value={user !== undefined ? user.displayName : ""}
/>
<InputField
label="E-Mail"
onChange={this.handleEmailChange}
value={user !== undefined ? user.mail : ""}
/>
<InputField
label="Password"
type="password"
onChange={this.handlePasswordChange}
value={user !== undefined ? user.password : ""}
/>
<Checkbox
label="Admin"
onChange={this.handleAdminChange}
checked={user !== undefined ? user.admin : false}
/>
<Checkbox
label="Active"
onChange={this.handleActiveChange}
value={user !== undefined ? user.active : false}
/>
<SubmitButton value="Submit" />
</form>
</div>
);
}
handleUsernameChange = (name: string) => {
this.setState({ name });
};
handleDisplayNameChange = (displayName: string) => {
this.setState({ displayName });
};
handleEmailChange = (mail: string) => {
this.setState({ mail });
};
handlePasswordChange = (password: string) => {
this.setState({ password });
};
handleAdminChange = (admin: boolean) => {
this.setState({ admin });
};
handleActiveChange = (active: boolean) => {
this.setState({ active });
};
}
export default UserForm;

View File

@@ -0,0 +1,34 @@
// @flow
import React from "react";
import UserRow from "./UserRow";
import type { User } from "../types/User";
type Props = {
users: Array<User>,
deleteUser: string => void
};
class UserTable extends React.Component<Props> {
render() {
const { users, deleteUser } = this.props;
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Display Name</th>
<th>E-Mail</th>
<th>Admin</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => {
return <UserRow key={index} user={user} deleteUser={deleteUser} />;
})}
</tbody>
</table>
);
}
}
export default UserTable;

View File

@@ -2,9 +2,9 @@
import React from "react";
import { connect } from "react-redux";
import { fetchUsers, deleteUser } from "../modules/users";
import Login from "../../containers/Login";
import UserRow from "./UserRow";
import { fetchUsers, addUser, editUser, deleteUser } from "../modules/users";
import UserForm from "./UserForm";
import UserTable from "./UserTable";
import type { User } from "../types/User";
type Props = {
@@ -12,7 +12,9 @@ type Props = {
error: Error,
users: Array<User>,
fetchUsers: () => void,
deleteUser: string => void
deleteUser: string => void,
addUser: User => void,
editUser: User => void
};
class Users extends React.Component<Props> {
@@ -20,34 +22,35 @@ class Users extends React.Component<Props> {
this.props.fetchUsers();
}
addUser = (user: User) => {
this.props.addUser(user);
};
editUser = (user: User) => {
this.props.editUser(user);
};
render() {
if (this.props.users) {
const { users, deleteUser } = this.props;
const testUser: User = {
name: "user",
displayName: "user_display",
password: "pw",
mail: "mail@mail.de",
active: true,
admin: true
};
if (users) {
return (
<div>
<h1>SCM</h1>
<h2>Users</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Display Name</th>
<th>E-Mail</th>
<th>Admin</th>
</tr>
</thead>
<tbody>
{this.props.users.map((user, index) => {
return (
<UserRow
key={index}
user={user}
deleteUser={this.props.deleteUser}
/>
);
})}
</tbody>
</table>
<section className="section">
<div className="container">
<h1 className="title">SCM</h1>
<h2 className="subtitle">Users</h2>
<UserTable users={users} deleteUser={deleteUser} />
{/* <UserForm submitForm={this.submitForm} /> */}
<UserForm submitForm={user => {}} user={testUser} />
</div>
</section>
);
} else {
return <div>Loading...</div>;
@@ -66,6 +69,12 @@ const mapDispatchToProps = dispatch => {
fetchUsers: () => {
dispatch(fetchUsers());
},
addUser: (user: User) => {
dispatch(addUser(user));
},
editUser: (user: User) => {
dispatch(editUser(user));
},
deleteUser: (link: string) => {
dispatch(deleteUser(link));
}

View File

@@ -1,5 +1,6 @@
// @flow
import { apiClient, PAGE_NOT_FOUND_ERROR } from "../../apiclient";
import type { User } from "../types/User";
import { ThunkDispatch } from "redux-thunk";
const FETCH_USERS = "scm/users/FETCH";
@@ -7,12 +8,21 @@ const FETCH_USERS_SUCCESS = "scm/users/FETCH_SUCCESS";
const FETCH_USERS_FAILURE = "scm/users/FETCH_FAILURE";
const FETCH_USERS_NOTFOUND = "scm/users/FETCH_NOTFOUND";
const ADD_USER = "scm/users/ADD";
const ADD_USER_SUCCESS = "scm/users/ADD_SUCCESS";
const ADD_USER_FAILURE = "scm/users/ADD_FAILURE";
const EDIT_USER = "scm/users/EDIT";
const EDIT_USER_SUCCESS = "scm/users/EDIT_SUCCESS";
const EDIT_USER_FAILURE = "scm/users/EDIT_FAILURE";
const DELETE_USER = "scm/users/DELETE";
const DELETE_USER_SUCCESS = "scm/users/DELETE_SUCCESS";
const DELETE_USER_FAILURE = "scm/users/DELETE_FAILURE";
const USERS_URL = "users";
const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2";
function requestUsers() {
return {
type: FETCH_USERS
@@ -67,6 +77,74 @@ function fetchUsersSuccess(users: any) {
};
}
function requestAddUser(user: User) {
return {
type: ADD_USER,
user
};
}
export function addUser(user: User) {
return function(dispatch: ThunkDispatch) {
dispatch(requestAddUser(user));
return apiClient
.postWithContentType(USERS_URL, user, CONTENT_TYPE_USER)
.then(() => {
dispatch(addUserSuccess());
dispatch(fetchUsers());
})
.catch(err => dispatch(addUserFailure(user, err)));
};
}
function addUserSuccess() {
return {
type: ADD_USER_SUCCESS
};
}
function addUserFailure(user: User, err: Error) {
return {
type: ADD_USER_FAILURE,
payload: err,
user
};
}
function requestAddUser(user: User) {
return {
type: ADD_USER,
user
};
}
export function editUser(user: User) {
return function(dispatch: ThunkDispatch) {
dispatch(requestAddUser(user));
return apiClient
.putWithContentType(USERS_URL + "/" + user.name, user, CONTENT_TYPE_USER)
.then(() => {
dispatch(addUserSuccess());
dispatch(fetchUsers());
})
.catch(err => dispatch(addUserFailure(user, err)));
};
}
function editUserSuccess() {
return {
type: ADD_USER_SUCCESS
};
}
function addUserFailure(user: User, err: Error) {
return {
type: ADD_USER_FAILURE,
payload: err,
user
};
}
function requestDeleteUser(url: string) {
return {
type: DELETE_USER,

View File

@@ -5,6 +5,8 @@ export type User = {
displayName: string,
name: string,
mail: string,
password: string,
admin: boolean,
_links: Links
active: boolean,
_links?: Links
};