Finished functionally complete client registration workflow

This commit is contained in:
Alexander Wong
2017-08-29 19:51:45 -06:00
parent aaad6ea3eb
commit 415aea74a3
25 changed files with 587 additions and 66 deletions

View File

@@ -1,23 +1,32 @@
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import Login from "./Auth/Login";
import Register from "./Auth/Register";
import About from "./Static/About";
import Footer from "./Static/Footer";
import Home from "./Static/Home";
import NoMatch from "./Static/NoMatch";
import Navbar from "./Navbar";
import Footer from "./Footer";
import Home from "./Home";
import About from "./About";
import Topics from "./Topics";
class App extends Component {
render() {
const footSmash = {
display: "flex",
minHeight: "calc(100vh - 1px)",
flexDirection: "column"
};
return (
<div>
<Navbar />
<div style={{ display: "flex", minHeight: "calc(100vh - 1px)", flexDirection: "column" }}>
<div style={footSmash}>
<div style={{ flex: "1" }}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
<Route path="/auth/login" component={Login} />
<Route path="/auth/register" component={Register} />
<Route component={NoMatch} />
</Switch>
</div>
<Footer />

View File

@@ -0,0 +1,16 @@
import React, { Component } from "react";
import { Container } from "semantic-ui-react";
class Login extends Component {
render() {
return <LoginView />;
}
}
const LoginView = () => (
<Container>
<p>Login</p>
</Container>
);
export default Login;

View File

@@ -0,0 +1,135 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { Container, Form, Header, Message } from "semantic-ui-react";
import {
clearAuthRequestError,
setFormEmail,
setFormPassword,
setFormPasswordConfirmation
} from "../../actions/auth/reducer.actions";
import { sendRegisterRequest } from "../../actions/auth/saga.actions";
import Error from "../Shared/Error";
class Register extends Component {
constructor(props) {
super(props);
this.props.dispatch(clearAuthRequestError());
}
changeEmail = event => {
this.props.dispatch(setFormEmail(event.target.value));
};
changePassword = event => {
this.props.dispatch(setFormPassword(event.target.value));
};
changePasswordConfirmation = event => {
this.props.dispatch(setFormPasswordConfirmation(event.target.value));
};
onSubmitRegistration = event => {
event.preventDefault();
const { dispatch, email, password, passwordConfirmation } = this.props;
dispatch(
sendRegisterRequest({
email,
password1: password,
password2: passwordConfirmation
})
);
};
render() {
const {
isSendingAuthRequest,
authRequestError,
authRequestSuccess,
email,
password,
passwordConfirmation
} = this.props;
return (
<RegisterView
isSendingAuthRequest={isSendingAuthRequest}
authRequestError={authRequestError}
authRequestSuccess={authRequestSuccess}
email={email}
password={password}
passwordConfirmation={passwordConfirmation}
changeEmail={this.changeEmail}
changePassword={this.changePassword}
changePasswordConfirmation={this.changePasswordConfirmation}
onSubmitRegistration={this.onSubmitRegistration}
/>
);
}
}
function mapStateToProps(state) {
return { ...state.auth };
}
/**
* Functional view component for Register logic
*/
const RegisterView = ({
isSendingAuthRequest,
authRequestError,
authRequestSuccess,
email,
password,
passwordConfirmation,
changeEmail,
changePassword,
changePasswordConfirmation,
onSubmitRegistration
}) => (
<Container>
<Header>Register</Header>
<Form
loading={isSendingAuthRequest}
onSubmit={onSubmitRegistration}
error={!!authRequestError}
success={!!authRequestSuccess}
>
<Form.Field>
<label>Email</label>
<input
placeholder="bob@gmail.com"
type="email"
value={email}
onChange={changeEmail}
/>
</Form.Field>
<Form.Field>
<label>Password</label>
<input
placeholder="••••••••"
type="password"
value={password}
onChange={changePassword}
/>
</Form.Field>
<Form.Field>
<label>Password Confirmation</label>
<input
placeholder="••••••••"
type="password"
value={passwordConfirmation}
onChange={changePasswordConfirmation}
/>
</Form.Field>
<Error header="Register failed!" error={authRequestError} />
<Message
success
header="Registration Sent"
content="A confirmation email has been sent to confirm your registration."
/>
<Form.Button>Submit</Form.Button>
</Form>
</Container>
);
export default connect(mapStateToProps)(Register);

View File

@@ -12,9 +12,14 @@ class Navbar extends Component {
<Menu.Item as={Link} to="/about">
About
</Menu.Item>
<Menu.Item as={Link} to="/topics">
Topics
</Menu.Item>
<Menu.Menu position="right">
<Menu.Item as={Link} to="/auth/login">
Login
</Menu.Item>
<Menu.Item as={Link} to="/auth/register">
Register
</Menu.Item>
</Menu.Menu>
</Menu>
);
}

View File

@@ -0,0 +1,41 @@
import PropTypes from "prop-types";
import React from "react";
import { Message } from "semantic-ui-react";
const propTypes = {
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
header: PropTypes.string
};
const defaultProps = {
error: "",
header: ""
};
const Error = ({ error, header }) => {
if (typeof error === "string") {
const hasError = !!error;
if (hasError) {
return <Message error={hasError} header={header} content={error} />;
}
} else if (typeof error === "object" && Object.keys(error).length > 0) {
const hasError = !!Object.keys(error);
if (hasError) {
return (
<Message
error={hasError}
header={header}
list={Object.keys(error).map(p => (
<Message.Item key={p}> {p}: {error[p]}</Message.Item>
))}
/>
);
}
}
return null;
};
Error.propTypes = propTypes;
Error.defaultProps = defaultProps;
export default Error;

View File

@@ -0,0 +1,13 @@
import React from "react";
import { Link } from "react-router-dom";
import { Container } from "semantic-ui-react";
const NoMatch = ({ location }) => (
<Container>
<h3>Page not found!</h3>
<p>No match found for <code>{location.pathname}</code></p>
<p><Link to="/">Go to the home page </Link></p>
</Container>
);
export default NoMatch;

View File

@@ -1,41 +0,0 @@
import React from "react";
import { Route, Link } from "react-router-dom";
import { Container } from "semantic-ui-react";
const Topics = ({ match }) => (
<Container>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>
Rendering with React
</Link>
</li>
<li>
<Link to={`${match.url}/components`}>
Components
</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic} />
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</Container>
);
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
);
export default Topics;