Added Complete Registration stepper component, all stepper logic

This commit is contained in:
Alexander Wong 2017-09-03 21:47:18 -06:00
parent e69773ac8e
commit 561664a701
9 changed files with 251 additions and 18 deletions

View File

@ -15,10 +15,23 @@ Now you can visit `localhost:3000` from your browser.
The environment variables are embedded during the build time. For more information, please refer to the [docs](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables). The environment variables are embedded during the build time. For more information, please refer to the [docs](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables).
| Environment Variable | Default Value | Description | | Environment Variable | Default Value | Description |
| ------------------------- |:-------------------------:| -------------------:| | ------------------------- |:-------------------------:| -------------------------------:|
| `REACT_APP_API_ENDPOINT` | `"http://localhost:8000"` | Server API endpoint | | `REACT_APP_API_ENDPOINT` | `"http://localhost:8000"` | Server API endpoint |
| `REACT_APP_REDUX_LOGGING` | ` ` | Set for Redux Log | | `REACT_APP_REDUX_LOGGING` | ` ` | Set any value for Redux logging |
## Production
To build the production instance of this application, run the following:
```bash
export REACT_APP_API_ENDPOINT="http://dev.tannercollin.com:8000/" && yarn build
# then, to serve the production site locally
yarn global add serve
serve -s build --port 3000
```
## Testing ## Testing

View File

@ -4,7 +4,12 @@ import {
CLEAR_USER_REQUEST_ERROR, CLEAR_USER_REQUEST_ERROR,
SET_USER_REQUEST_SUCCESS, SET_USER_REQUEST_SUCCESS,
CLEAR_USER_REQUEST_SUCCESS, CLEAR_USER_REQUEST_SUCCESS,
SET_SELF_USER SET_SELF_USER,
SET_COMPLETE_REGISTRATION_STEP,
SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER,
SET_FORM_PHONE_NUMBER,
SET_FORM_BUSINESS_NUMBER,
SET_FORM_SOCIAL_INSURANCE_NUMBER
} from "../../constants/user.constants"; } from "../../constants/user.constants";
import { parseError } from "../common.actions"; import { parseError } from "../common.actions";
@ -48,3 +53,38 @@ export function setSelfUser(selfUser) {
data: selfUser data: selfUser
}; };
} }
export function setCompleteRegistrationStep(step) {
return {
type: SET_COMPLETE_REGISTRATION_STEP,
data: step
};
}
export function setCompleteRegistrationClientOrProvider(clientOrProvider) {
return {
type: SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER,
data: clientOrProvider
};
}
export function setFormPhoneNumber(phoneNumber) {
return {
type: SET_FORM_PHONE_NUMBER,
data: phoneNumber
};
}
export function setFormBusinessNumber(businessNumber) {
return {
type: SET_FORM_BUSINESS_NUMBER,
data: businessNumber
};
}
export function setFormSocialInsuranceNumber(socialInsuranceNumber) {
return {
type: SET_FORM_SOCIAL_INSURANCE_NUMBER,
data: socialInsuranceNumber
};
}

View File

@ -7,6 +7,7 @@ import Register from "./Auth/Register";
import ResetPassword from "./Auth/ResetPassword"; import ResetPassword from "./Auth/ResetPassword";
import Settings from "./Auth/Settings"; import Settings from "./Auth/Settings";
import VerifyEmail from "./Auth/VerifyEmail"; import VerifyEmail from "./Auth/VerifyEmail";
import CompleteRegistration from "./User/CompleteRegistration";
import Profile from "./User/Profile"; import Profile from "./User/Profile";
import PrivateRoute from "./Shared/PrivateRoute"; import PrivateRoute from "./Shared/PrivateRoute";
import About from "./Static/About"; import About from "./Static/About";
@ -17,15 +18,16 @@ import Navbar from "./Navbar";
class App extends Component { class App extends Component {
render() { render() {
const footSmash = {
display: "flex",
minHeight: "calc(100vh - 1px)",
flexDirection: "column"
};
return ( return (
<div> <div>
<Navbar /> <Navbar />
<div style={footSmash}> <div
style={{
display: "flex",
minHeight: "calc(100vh - 1px)",
flexDirection: "column"
}}
>
<div style={{ flex: "1" }}> <div style={{ flex: "1" }}>
<Switch> <Switch>
<Route exact path="/" component={Home} /> <Route exact path="/" component={Home} />
@ -44,8 +46,10 @@ class App extends Component {
component={ResetPassword} component={ResetPassword}
/> />
<PrivateRoute path="/user/profile" component={Profile} /> <PrivateRoute path="/user/profile" component={Profile} />
<Route path='/user/complete-registration' component={Profile} />
<Route component={NoMatch} /> <Route component={NoMatch} />
</Switch> </Switch>
<Route path='/user/complete-registration' component={CompleteRegistration} />
</div> </div>
<Footer /> <Footer />
</div> </div>

View File

@ -3,9 +3,18 @@ import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Dropdown, Menu } from "semantic-ui-react"; import { Dropdown, Menu } from "semantic-ui-react";
import { sendGetSelfUserRequest } from "../actions/user/saga.actions";
import { sendLogoutRequest } from "../actions/auth/saga.actions"; import { sendLogoutRequest } from "../actions/auth/saga.actions";
class Navbar extends Component { class Navbar extends Component {
componentWillMount() {
const { dispatch, userToken, selfUser } = this.props;
// If the user token exists and the self user object isn't loaded, dispatch
if (userToken && Object.keys(selfUser).length === 0) {
dispatch(sendGetSelfUserRequest());
}
}
dispatchLogoutRequest = () => { dispatchLogoutRequest = () => {
this.props.dispatch(sendLogoutRequest()); this.props.dispatch(sendLogoutRequest());
}; };
@ -22,7 +31,10 @@ class Navbar extends Component {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
return { ...state.auth }; return {
userToken: state.auth.userToken,
selfUser: state.user.selfUser
};
} }
const NavbarView = ({ isAuthenticated, dispatchLogoutRequest }) => ( const NavbarView = ({ isAuthenticated, dispatchLogoutRequest }) => (

View File

@ -38,11 +38,18 @@ class PrivateRoute extends Component {
// * we are still sending user request // * we are still sending user request
// show loading spinner // show loading spinner
if ( if (
!!userToken && userToken && (Object.keys(selfUser).length === 0 || isSendingUserRequest)
(Object.keys(selfUser).length === 0 || isSendingUserRequest)
) { ) {
return <Loader active />; return <Loader active />;
} }
// If the user exists but they aren't a client or provider yet, confirm-registration
if (
userToken &&
Object.keys(selfUser).length &&
(!selfUser.client || !selfUser.provider)
) {
return <Redirect to="/user/complete-registration" />;
}
return ( return (
<Route <Route
{...rest} {...rest}

View File

@ -0,0 +1,102 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { Grid, Modal, Step } from "semantic-ui-react";
import {
setCompleteRegistrationStep
} from "../../actions/user/reducer.actions";
import {
USER_INFO_STEP,
CLIENT_OR_PROVIDER_STEP,
COMPLETE_INFORMATION_STEP
} from "../../constants/user.constants";
class CompleteRegistration extends Component {
onChangeStep = (event, data) => {
event.preventDefault();
this.props.dispatch(setCompleteRegistrationStep(data.step));
};
render() {
const {
userToken,
completeRegistrationCurrentStep,
completeRegistrationMaxStep,
completeRegistrationClientOrProvider
} = this.props;
if (!userToken) return <Redirect to="/auth/login" />;
return (
<CompleteRegistrationView
currentStep={completeRegistrationCurrentStep}
maxStep={completeRegistrationMaxStep}
completeRegistrationClientOrProvider={
completeRegistrationClientOrProvider
}
onChangeStep={this.onChangeStep}
/>
);
}
}
function mapStateToProps(state) {
return {
...state.user,
userToken: state.auth.userToken
};
}
const CompleteRegistrationView = ({
currentStep,
maxStep,
completeRegistrationClientOrProvider,
onChangeStep
}) => (
<Modal defaultOpen={true} closeOnDimmerClick={false} size="large">
<Modal.Header>Complete Registration</Modal.Header>
<Modal.Content>
<Grid>
<Grid.Row>
<Grid.Column stretched={true}>
<Step.Group stackable="tablet" ordered>
<Step
completed={maxStep > USER_INFO_STEP}
active={currentStep === USER_INFO_STEP}
title="Basic User Info"
description="Fill out your user information"
onClick={onChangeStep}
disabled={maxStep < USER_INFO_STEP}
step={USER_INFO_STEP}
/>
<Step
completed={maxStep > CLIENT_OR_PROVIDER_STEP}
active={currentStep === CLIENT_OR_PROVIDER_STEP}
title="Client or Provider"
description="Choose to be a Client or a Provider"
onClick={onChangeStep}
disabled={maxStep < CLIENT_OR_PROVIDER_STEP}
step={CLIENT_OR_PROVIDER_STEP}
/>
<Step
completed={maxStep > COMPLETE_INFORMATION_STEP}
active={currentStep === COMPLETE_INFORMATION_STEP}
title="Complete Information"
description="Fill out the client or provider information"
onClick={onChangeStep}
disabled={maxStep < COMPLETE_INFORMATION_STEP}
step={COMPLETE_INFORMATION_STEP}
/>
</Step.Group>
</Grid.Column>
</Grid.Row>
<Grid.Row>
{currentStep === USER_INFO_STEP && <p>User Info</p>}
{currentStep === CLIENT_OR_PROVIDER_STEP && <p>Client or Provider</p>}
{currentStep === COMPLETE_INFORMATION_STEP && <p>Complete Info</p>}
</Grid.Row>
</Grid>
</Modal.Content>
</Modal>
);
export default connect(mapStateToProps)(CompleteRegistration);

View File

@ -29,7 +29,7 @@ const ProfileView = ({ user }) => (
{user.userinfo && <List> {user.userinfo && <List>
{Object.keys(user.userinfo).map(function(key) { {Object.keys(user.userinfo).map(function(key) {
return (<List.Item key={key}> return (<List.Item key={key}>
{user.userinfo[key]} {key}: {user.userinfo[key]}
</List.Item>) </List.Item>)
})} })}
</List>} </List>}

View File

@ -5,6 +5,20 @@ export const CLEAR_USER_REQUEST_ERROR = "CLEAR_USER_REQUEST_ERROR";
export const SET_USER_REQUEST_SUCCESS = "SET_USER_REQUEST_SUCCESS"; export const SET_USER_REQUEST_SUCCESS = "SET_USER_REQUEST_SUCCESS";
export const CLEAR_USER_REQUEST_SUCCESS = "CLEAR_USER_REQUEST_SUCCESS"; export const CLEAR_USER_REQUEST_SUCCESS = "CLEAR_USER_REQUEST_SUCCESS";
export const SET_SELF_USER = "SET_SELF_USER"; export const SET_SELF_USER = "SET_SELF_USER";
export const SET_COMPLETE_REGISTRATION_STEP = "SET_COMPLETE_REGISTRATION_STEP";
export const SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER =
"SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER";
export const SET_FORM_PHONE_NUMBER = "SET_FORM_PHONE_NUMBER";
export const SET_FORM_BUSINESS_NUMBER = "SET_FORM_BUSINESS_NUMBER";
export const SET_FORM_SOCIAL_INSURANCE_NUMBER =
"SET_FORM_SOCIAL_INSURANCE_NUMBER";
// Saga User Action Constants // Saga User Action Constants
export const SEND_GET_SELF_USER_REQUEST = "SEND_GET_SELF_USER_REQUEST"; export const SEND_GET_SELF_USER_REQUEST = "SEND_GET_SELF_USER_REQUEST";
// Misc. User Constants (int so we can mark prior as completed)
export const USER_INFO_STEP = 1;
export const CLIENT_OR_PROVIDER_STEP = 2;
export const COMPLETE_INFORMATION_STEP = 3;
export const CLIENT = "CLIENT";
export const PROVIDER = "PROVIDER";

View File

@ -4,14 +4,26 @@ import {
CLEAR_USER_REQUEST_ERROR, CLEAR_USER_REQUEST_ERROR,
SET_USER_REQUEST_SUCCESS, SET_USER_REQUEST_SUCCESS,
CLEAR_USER_REQUEST_SUCCESS, CLEAR_USER_REQUEST_SUCCESS,
SET_SELF_USER SET_SELF_USER,
SET_COMPLETE_REGISTRATION_STEP,
SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER,
SET_FORM_PHONE_NUMBER,
SET_FORM_BUSINESS_NUMBER,
SET_FORM_SOCIAL_INSURANCE_NUMBER,
USER_INFO_STEP
} from "../constants/user.constants"; } from "../constants/user.constants";
const initialState = { const initialState = {
isSendingUserRequest: false, isSendingUserRequest: false,
userRequestError: "", userRequestError: "",
userRequestSuccess: "", userRequestSuccess: "",
selfUser: {} selfUser: {},
completeRegistrationCurrentStep: USER_INFO_STEP,
completeRegistrationMaxStep: USER_INFO_STEP,
completeRegistrationClientOrProvider: "",
phoneNumber: "",
businessNumber: "",
socialInsuranceNumber: ""
}; };
function userReducer(state = initialState, action) { function userReducer(state = initialState, action) {
@ -46,6 +58,35 @@ function userReducer(state = initialState, action) {
...state, ...state,
selfUser: action.data selfUser: action.data
}; };
case SET_COMPLETE_REGISTRATION_STEP:
return {
...state,
completeRegistrationCurrentStep: action.data,
completeRegistrationMaxStep: Math.max(
action.data,
state.completeRegistrationMaxStep
)
};
case SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER:
return {
...state,
completeRegistrationClientOrProvider: action.data
};
case SET_FORM_PHONE_NUMBER:
return {
...state,
phoneNumber: action.data
};
case SET_FORM_BUSINESS_NUMBER:
return {
...state,
businessNumber: action.data
};
case SET_FORM_SOCIAL_INSURANCE_NUMBER:
return {
...state,
socialInsuranceNumber: action.data
};
default: default:
return state; return state;
} }