Create/Update worktypes

This commit is contained in:
Alexander Wong
2017-09-21 15:01:49 -06:00
parent 48cc050c47
commit 0e6fb475b7
17 changed files with 689 additions and 25 deletions

View File

@@ -0,0 +1,74 @@
import {
IS_SENDING_WORKTYPE_REQUEST,
SET_WORKTYPE_REQUEST_ERROR,
CLEAR_WORKTYPE_REQUEST_ERROR,
SET_WORKTYPE_REQUEST_SUCCESS,
CLEAR_WORKTYPE_REQUEST_SUCCESS,
SET_WORKTYPE_UUID,
SET_FORM_WORKTYPE_COLOR,
SET_FORM_WORKTYPE_LABEL
} from "../../constants/worktype.constants";
import { parseError } from "../common.actions";
export function isSendingWorktypeRequest(sendingRequest) {
return {
type: IS_SENDING_WORKTYPE_REQUEST,
data: sendingRequest
};
}
export function setWorktypeRequestError(exception) {
let error = parseError(exception);
if (error.label) {
error["Label"] = error.label;
delete error["label"];
}
if (error.color) {
error["Color"] = error.color;
delete error["color"];
}
return {
type: SET_WORKTYPE_REQUEST_ERROR,
data: error
};
}
export function clearWorktypeRequestError() {
return {
type: CLEAR_WORKTYPE_REQUEST_ERROR
};
}
export function setWorktypeRequestSuccess(response) {
return {
type: SET_WORKTYPE_REQUEST_SUCCESS,
data: response.detail || response
};
}
export function clearWorktypeRequestSuccess() {
return {
type: CLEAR_WORKTYPE_REQUEST_SUCCESS
};
}
export function setWorktypeUUID(uuid) {
return {
type: SET_WORKTYPE_UUID,
data: uuid
};
}
export function setFormWorktypeColor(color) {
return {
type: SET_FORM_WORKTYPE_COLOR,
data: color
};
}
export function setFormWorktypeLabel(label) {
return {
type: SET_FORM_WORKTYPE_LABEL,
data: label
};
}

View File

@@ -0,0 +1,34 @@
import {
CREATE_WORKTYPE_REQUEST,
READ_WORKTYPE_REQUEST,
UPDATE_WORKTYPE_REQUEST,
DELETE_WORKTYPE_REQUEST
} from "../../constants/worktype.constants";
export function createWorktypeRequest(postBody) {
return {
type: CREATE_WORKTYPE_REQUEST,
data: postBody
};
}
export function readWorktypeRequest(payload) {
return {
type: READ_WORKTYPE_REQUEST,
data: payload
};
}
export function updateWorktypeRequest(payload) {
return {
type: UPDATE_WORKTYPE_REQUEST,
data: payload
};
}
export function deleteWorktypeRequest(payload) {
return {
type: DELETE_WORKTYPE_REQUEST,
data: payload
};
}

View File

@@ -46,6 +46,13 @@ export function put(url, data) {
.catch(error => Promise.reject(error));
}
export function patch(url, data) {
return apiInstance
.patch(url, data, { headers: headers() })
.then(response => response.data)
.catch(error => Promise.reject(error));
}
export function del(url) {
return apiInstance
.delete(url, { headers: headers() })

39
src/api/worktype.api.js Normal file
View File

@@ -0,0 +1,39 @@
import { post, get, patch, del } from "./baseApi";
/**
* Function wrapping POST request for creating worktypes
* @param {string} color - color value of worktype
* @param {string} label - label text of worktype
*/
export function createWorktype(color, label) {
return post("/worktype/", { color, label }).then(resp =>
Promise.resolve(resp)
);
}
/**
* Function wrapping GET request for retreiving worktype data
* @param {string} uuid - worktype unique identifier
*/
export function getWorktype(uuid) {
return get(`/worktype/${uuid}/`).then(resp => Promise.resolve(resp));
}
/**
* Function wrapping PATCH request for updating worktype
* @param {string} uuid - worktype unique identifier
* @param {string} color - color value of worktype
*/
export function updateWorktype(uuid, color) {
return patch(`/worktype/${uuid}/`, { color }).then(resp =>
Promise.resolve(resp)
);
}
/**
* Function wrapping DELETE request for removing worktypes
* @param {string} uuid - worktype unique identifier
*/
export function deleteWorktype(uuid) {
return del(`/worktype/${uuid}`).then(resp => Promise.resolve(resp));
}

View File

@@ -7,6 +7,8 @@ import Register from "./Auth/Register";
import ResetPassword from "./Auth/ResetPassword";
import Settings from "./Auth/Settings";
import VerifyEmail from "./Auth/VerifyEmail";
import CreateWorkTypeForm from "./User/Client/CreateWorkTypeForm";
import UpdateWorkTypeForm from "./User/Client/UpdateWorkTypeForm";
import CompleteRegistration from "./User/CompleteRegistration";
import EditProfile from "./User/EditProfile";
import Profile from "./User/Profile";
@@ -46,12 +48,23 @@ class App extends Component {
path="/auth/reset-password/:uid/:token"
component={ResetPassword}
/>
<PrivateRoute
path="/user/profile/client/update-worktype/:uuid"
component={UpdateWorkTypeForm}
/>
<PrivateRoute
path="/user/profile/client/create-worktype"
component={CreateWorkTypeForm}
/>
<PrivateRoute path="/user/profile/edit" component={EditProfile} />
<PrivateRoute path="/user/profile" component={Profile} />
<Route path='/user/complete-registration' component={Profile} />
<Route path="/user/complete-registration" component={Profile} />
<Route component={NoMatch} />
</Switch>
<Route path='/user/complete-registration' component={CompleteRegistration} />
<Route
path="/user/complete-registration"
component={CompleteRegistration}
/>
</div>
<Footer />
</div>

View File

@@ -0,0 +1,75 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { Card, Button, Label } from "semantic-ui-react";
class ClientProfile extends Component {
render() {
const { selfUser } = this.props;
return <ClientProfileView user={selfUser} />;
}
}
function mapStateToProps(state) {
return { ...state.user };
}
const ClientProfileView = ({ user }) => (
<div>
<Card fluid={true}>
<Card.Content>
<Card.Header>Client Information</Card.Header>
<Card.Meta>Business Number: {user.client.business_number}</Card.Meta>
<Card.Description>
<Button
basic
color="green"
size="small"
as={Link}
to="/user/profile/client/create-worktype"
>
Create Worktype
</Button>
</Card.Description>
</Card.Content>
</Card>
{(user.client.work_types || []).length &&
<Card.Group>
{user.client.work_types.map((worktype, index) => (
<Card key={index}>
<Card.Content>
<Card.Header as="h4">
<Label
circular
empty
style={{
backgroundColor: worktype.color,
borderColor: worktype.color
}}
/>{" " + worktype.label}
</Card.Header>
<Card.Meta>
Worktype
</Card.Meta>
</Card.Content>
<Card.Content extra>
<Button.Group>
<Button
basic
color="yellow"
size="small"
as={Link}
to={`/user/profile/client/update-worktype/${worktype.uuid}`}
>
Edit
</Button>
<Button basic color="red" size="small">Delete</Button>
</Button.Group>
</Card.Content>
</Card>
))}
</Card.Group>}
</div>
);
export default connect(mapStateToProps)(ClientProfile);

View File

@@ -0,0 +1,113 @@
import React, { Component } from "react";
import { GithubPicker } from "react-color";
import { connect } from "react-redux";
import { Container, Form, Header, Label, Message } from "semantic-ui-react";
import {
clearWorktypeRequestError,
clearWorktypeRequestSuccess,
setFormWorktypeColor,
setFormWorktypeLabel
} from "../../../actions/worktype/reducer.actions";
import { createWorktypeRequest } from "../../../actions/worktype/saga.actions";
import Error from "../../Shared/Error";
class CreateWorkTypeForm extends Component {
componentWillUnmount = () => {
this.props.dispatch(clearWorktypeRequestError());
this.props.dispatch(clearWorktypeRequestSuccess());
};
changeLabel = event => {
this.props.dispatch(setFormWorktypeLabel(event.target.value));
};
changeColor = color => {
this.props.dispatch(setFormWorktypeColor(color.hex));
};
onSubmitWorktype = event => {
event.preventDefault();
const { label, color } = this.props;
this.props.dispatch(createWorktypeRequest({ label, color }));
};
render() {
const {
isSendingWorktypeRequest,
worktypeRequestError,
worktypeRequestSuccess,
label,
color
} = this.props;
return (
<CreateWorkTypeFormView
isSendingWorktypeRequest={isSendingWorktypeRequest}
worktypeRequestError={worktypeRequestError}
worktypeRequestSuccess={worktypeRequestSuccess}
label={label}
color={color}
changeLabel={this.changeLabel}
changeColor={this.changeColor}
onSubmitWorktype={this.onSubmitWorktype}
/>
);
}
}
function mapStateToProps(state) {
return { ...state.worktype };
}
const CreateWorkTypeFormView = ({
isSendingWorktypeRequest,
worktypeRequestError,
worktypeRequestSuccess,
label,
color,
changeLabel,
changeColor,
onSubmitWorktype
}) => (
<Container>
<Header>Create Worktype</Header>
<Form
loading={isSendingWorktypeRequest}
onSubmit={onSubmitWorktype}
error={!!worktypeRequestError}
success={!!worktypeRequestSuccess}
>
<Form.Field>
<label>Label</label>
<input
placeholder="My worktype"
type="text"
value={label}
onChange={changeLabel}
/>
</Form.Field>
<Form.Field>
<label>Color</label>
<Label
circular
empty
size="massive"
style={{
backgroundColor: color,
borderColor: color
}}
/>
<GithubPicker color={color} onChangeComplete={changeColor} />
</Form.Field>
<Error header="Create Worktype failed!" error={worktypeRequestError} />
<Message success>
<Message.Header>Create Worktype successful!</Message.Header>
<p>Set worktype successfully.</p>
</Message>
<Form.Button>Submit Worktype</Form.Button>
</Form>
</Container>
);
export default connect(mapStateToProps)(CreateWorkTypeForm);

View File

@@ -0,0 +1,108 @@
import React, { Component } from "react";
import { GithubPicker } from "react-color";
import { connect } from "react-redux";
import { Container, Form, Header, Label, Message } from "semantic-ui-react";
import {
clearWorktypeRequestError,
clearWorktypeRequestSuccess,
setFormWorktypeColor
} from "../../../actions/worktype/reducer.actions";
import { updateWorktypeRequest, readWorktypeRequest } from "../../../actions/worktype/saga.actions";
import Error from "../../Shared/Error";
class UpdateWorkTypeForm extends Component {
componentWillMount = () => {
const uuid = this.props.match.params.uuid;
this.props.dispatch(readWorktypeRequest(uuid));
}
componentWillUnmount = () => {
this.props.dispatch(clearWorktypeRequestError());
this.props.dispatch(clearWorktypeRequestSuccess());
};
changeColor = color => {
this.props.dispatch(setFormWorktypeColor(color.hex));
};
onSubmitWorktype = event => {
event.preventDefault();
const { uuid, color } = this.props;
this.props.dispatch(updateWorktypeRequest({ uuid, color }));
};
render() {
const {
isSendingWorktypeRequest,
worktypeRequestError,
worktypeRequestSuccess,
uuid,
label,
color
} = this.props;
return (
<UpdateWorkTypeFormView
isSendingWorktypeRequest={isSendingWorktypeRequest}
worktypeRequestError={worktypeRequestError}
worktypeRequestSuccess={worktypeRequestSuccess}
uuid={uuid}
label={label}
color={color}
changeColor={this.changeColor}
onSubmitWorktype={this.onSubmitWorktype}
/>
);
}
}
function mapStateToProps(state) {
return { ...state.worktype };
}
const UpdateWorkTypeFormView = ({
isSendingWorktypeRequest,
worktypeRequestError,
worktypeRequestSuccess,
label,
color,
changeColor,
onSubmitWorktype
}) => (
<Container>
<Header>Update Worktype</Header>
<Form
loading={isSendingWorktypeRequest}
onSubmit={onSubmitWorktype}
error={!!worktypeRequestError}
success={!!worktypeRequestSuccess}
>
<Form.Field>
<label>Label</label>
<input type="text" disabled value={label}/>
</Form.Field>
<Form.Field>
<label>Color</label>
<Label
circular
empty
size="massive"
style={{
backgroundColor: color,
borderColor: color
}}
/>
<GithubPicker color={color} onChangeComplete={changeColor} />
</Form.Field>
<Error header="Update Worktype failed!" error={worktypeRequestError} />
<Message success>
<Message.Header>Update Worktype successful!</Message.Header>
<p>Update worktype successfully.</p>
</Message>
<Form.Button>Update Worktype</Form.Button>
</Form>
</Container>
);
export default connect(mapStateToProps)(UpdateWorkTypeForm);

View File

@@ -3,6 +3,8 @@ import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { Card, Container, Header, Label, List } from "semantic-ui-react";
import ClientProfile from "./Client/ClientProfile";
class Profile extends Component {
render() {
const { selfUser } = this.props;
@@ -35,38 +37,24 @@ const ProfileView = ({ user }) => (
"User Registration Not Completed"}
</span>
{user.first_name} {user.last_name}
<List>
{user.userinfo &&
Object.keys(user.userinfo).map(function(key) {
{user.userinfo &&
<List>
{Object.keys(user.userinfo).map(function(key) {
return (
<List.Item key={key}>
{key}: {user.userinfo[key]}
</List.Item>
);
})}
{user.client &&
Object.keys(user.client).map(function(key) {
return (
<List.Item key={key}>
{key}: {user.client[key]}
</List.Item>
);
})}
{user.provider &&
Object.keys(user.provider).map(function(key) {
return (
<List.Item key={key}>
{key}: {user.provider[key]}
</List.Item>
);
})}
</List>
</List>}
</Card.Description>
</Card.Content>
<Card.Content extra>
<Link to="/user/profile/edit">Edit Profile</Link>
</Card.Content>
</Card>
{user.client && <ClientProfile />}
{user.provider && <div />}
</Container>
);

View File

@@ -0,0 +1,15 @@
// Reducer Worktype Action Constants
export const IS_SENDING_WORKTYPE_REQUEST = "IS_SENDING_WORKTYPE_REQUEST";
export const SET_WORKTYPE_REQUEST_ERROR = "SET_WORKTYPE_REQUEST_ERROR";
export const CLEAR_WORKTYPE_REQUEST_ERROR = "CLEAR_WORKTYPE_REQUEST_ERROR";
export const SET_WORKTYPE_REQUEST_SUCCESS = "SET_WORKTYPE_REQUEST_SUCCESS";
export const CLEAR_WORKTYPE_REQUEST_SUCCESS = "CLEAR_WORKTYPE_REQUEST_SUCCESS";
export const SET_WORKTYPE_UUID = "SET_WORKTYPE_UUID";
export const SET_FORM_WORKTYPE_LABEL = "SET_FORM_WORKTYPE_LABEL";
export const SET_FORM_WORKTYPE_COLOR = "SET_FORM_WORKTYPE_COLOR";
// Saga Worktype Action Constants
export const CREATE_WORKTYPE_REQUEST = "CREATE_WORKTYPE_REQUEST";
export const READ_WORKTYPE_REQUEST = "READ_WORKTYPE_REQUEST";
export const UPDATE_WORKTYPE_REQUEST = "UPDATE_WORKTYPE_REQUEST";
export const DELETE_WORKTYPE_REQUEST = "DELETE_WORKTYPE_REQUEST";

View File

@@ -1,10 +1,12 @@
import { combineReducers } from "redux";
import authReducer from "./authReducer";
import userReducer from "./userReducer";
import worktypeReducer from "./worktypeReducer";
const reducer = combineReducers({
auth: authReducer,
user: userReducer
user: userReducer,
worktype: worktypeReducer
});
export default reducer;

View File

@@ -0,0 +1,68 @@
import {
IS_SENDING_WORKTYPE_REQUEST,
SET_WORKTYPE_REQUEST_ERROR,
CLEAR_WORKTYPE_REQUEST_ERROR,
SET_WORKTYPE_REQUEST_SUCCESS,
CLEAR_WORKTYPE_REQUEST_SUCCESS,
SET_WORKTYPE_UUID,
SET_FORM_WORKTYPE_COLOR,
SET_FORM_WORKTYPE_LABEL
} from "../constants/worktype.constants";
const initialState = {
isSendingWorktypeRequest: false,
worktypeRequestError: "",
worktypeRequestSuccess: "",
uuid: "",
color: "",
label: ""
};
function worktypeReducer(state = initialState, action) {
switch (action.type) {
case IS_SENDING_WORKTYPE_REQUEST:
return {
...state,
isSendingWorktypeRequest: action.data
};
case SET_WORKTYPE_REQUEST_ERROR:
return {
...state,
worktypeRequestError: action.data
};
case CLEAR_WORKTYPE_REQUEST_ERROR:
return {
...state,
worktypeRequestError: ""
};
case SET_WORKTYPE_REQUEST_SUCCESS:
return {
...state,
worktypeRequestSuccess: action.data
};
case CLEAR_WORKTYPE_REQUEST_SUCCESS:
return {
...state,
worktypeRequestSuccess: ""
};
case SET_WORKTYPE_UUID:
return {
...state,
uuid: action.data
};
case SET_FORM_WORKTYPE_COLOR:
return {
...state,
color: action.data
};
case SET_FORM_WORKTYPE_LABEL:
return {
...state,
label: action.data
};
default:
return state;
}
}
export default worktypeReducer;

View File

@@ -15,7 +15,7 @@ import {
logoutUserFlow,
changePasswordFlow,
forgotPasswordFlow,
resetPasswordFlow,
resetPasswordFlow
} from "./auth.sagas";
import {
GET_SELF_USER_REQUEST,
@@ -35,6 +35,16 @@ import {
createProviderFlow,
updateProviderFlow
} from "./user.sagas";
import {
CREATE_WORKTYPE_REQUEST,
READ_WORKTYPE_REQUEST,
UPDATE_WORKTYPE_REQUEST
} from "../constants/worktype.constants";
import {
createWorktypeFlow,
readWorktypeFlow,
updateWorktypeFlow
} from "./worktype.sagas";
export default function* rootSaga() {
yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow);
@@ -51,4 +61,7 @@ export default function* rootSaga() {
yield takeLatest(UPDATE_CLIENT_REQUEST, updateClientFlow);
yield takeLatest(CREATE_PROVIDER_REQUEST, createProviderFlow);
yield takeLatest(UPDATE_PROVIDER_REQUEST, updateProviderFlow);
yield takeLatest(CREATE_WORKTYPE_REQUEST, createWorktypeFlow);
yield takeLatest(READ_WORKTYPE_REQUEST, readWorktypeFlow);
yield takeLatest(UPDATE_WORKTYPE_REQUEST, updateWorktypeFlow);
}

View File

@@ -0,0 +1,88 @@
import { effects } from "redux-saga";
import {
isSendingWorktypeRequest,
setWorktypeRequestError,
setWorktypeRequestSuccess,
clearWorktypeRequestError,
clearWorktypeRequestSuccess,
setWorktypeUUID,
setFormWorktypeColor,
setFormWorktypeLabel
} from "../actions/worktype/reducer.actions";
import { getSelfUserRequest } from "../actions/user/saga.actions";
import {
createWorktype,
getWorktype,
updateWorktype,
deleteWorktype
} from "../api/worktype.api";
function* createWorktypeCall(postBody) {
yield effects.put(isSendingWorktypeRequest(true));
const { color, label } = postBody;
try {
return yield effects.call(createWorktype, color, label);
} catch (exception) {
yield effects.put(setWorktypeRequestError(exception));
return false;
} finally {
yield effects.put(isSendingWorktypeRequest(false));
}
}
function* readWorktypeCall(uuid) {
yield effects.put(isSendingWorktypeRequest(true));
try {
return yield effects.call(getWorktype, uuid);
} catch (exception) {
yield effects.put(setWorktypeRequestError(exception));
return false;
} finally {
yield effects.put(isSendingWorktypeRequest(false));
}
}
function* updateWorktypeCall(payload) {
yield effects.put(isSendingWorktypeRequest(true));
const { uuid, color } = payload;
try {
return yield effects.call(updateWorktype, uuid, color);
} catch (exception) {
yield effects.put(setWorktypeRequestError(exception));
return false;
} finally {
yield effects.put(isSendingWorktypeRequest(false));
}
}
export function* createWorktypeFlow(request) {
yield effects.put(clearWorktypeRequestSuccess());
yield effects.put(clearWorktypeRequestError());
const wasSuccessful = yield effects.call(createWorktypeCall, request.data);
if (wasSuccessful) {
yield effects.put(getSelfUserRequest());
yield effects.put(setWorktypeRequestSuccess(wasSuccessful));
yield effects.put(setFormWorktypeColor(""));
yield effects.put(setFormWorktypeLabel(""));
yield effects.put(clearWorktypeRequestError());
}
}
export function* readWorktypeFlow(request) {
const wasSuccessful = yield effects.call(readWorktypeCall, request.data);
if (wasSuccessful) {
yield effects.put(setWorktypeUUID(wasSuccessful.uuid));
yield effects.put(setFormWorktypeColor(wasSuccessful.color));
yield effects.put(setFormWorktypeLabel(wasSuccessful.label));
}
}
export function* updateWorktypeFlow(request) {
yield effects.put(clearWorktypeRequestSuccess());
yield effects.put(clearWorktypeRequestError());
const wasSuccessful = yield effects.call(updateWorktypeCall, request.data);
if (wasSuccessful) {
yield effects.put(getSelfUserRequest());
yield effects.put(setWorktypeRequestSuccess(wasSuccessful));
}
}