mirror of
https://github.com/rjNemo/devbook_ts
synced 2026-06-12 13:36:43 +00:00
signup page functional
This commit is contained in:
parent
5aa90a6047
commit
b1039eb959
7 changed files with 150 additions and 636 deletions
|
|
@ -24,6 +24,7 @@
|
|||
"moment": "^2.25.3",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-google-button": "^0.7.1",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-redux-firebase": "^3.4.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import React, {FC} from 'react';
|
||||
|
||||
interface IProps {
|
||||
state: string | number | string[] | undefined;
|
||||
setState: React.Dispatch<React.SetStateAction<string>>;
|
||||
placeholder: string;
|
||||
type?: string;
|
||||
required?: boolean;
|
||||
autoFocus?: boolean;
|
||||
minLength?: number;
|
||||
}
|
||||
|
||||
const InputField: FC<IProps> = ({
|
||||
state,
|
||||
setState,
|
||||
placeholder,
|
||||
autoFocus,
|
||||
required,
|
||||
minLength,
|
||||
type = 'text',
|
||||
}) => {
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setState(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="form-group">
|
||||
<input
|
||||
value={state}
|
||||
onChange={handleChange}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
autoFocus={autoFocus}
|
||||
required={required}
|
||||
minLength={minLength}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default InputField;
|
||||
|
|
@ -1,8 +1,20 @@
|
|||
interface User {
|
||||
name: string;
|
||||
displayName: string;
|
||||
email: string;
|
||||
picture: string;
|
||||
avatarUrl: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
/** User constructor */
|
||||
export const newUser = (
|
||||
displayName: string,
|
||||
email: string,
|
||||
avatarUrl: string = '',
|
||||
): User => ({
|
||||
displayName,
|
||||
email,
|
||||
avatarUrl,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
export default User;
|
||||
|
|
|
|||
|
|
@ -1,63 +1,140 @@
|
|||
import React, {FC, useState} from 'react';
|
||||
// Routing
|
||||
import {Link} from 'react-router-dom';
|
||||
import {Link, useHistory, Redirect} from 'react-router-dom';
|
||||
import * as ROUTES from '../constants/routes';
|
||||
// Redux
|
||||
import {compose} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import {withFirebase, WithFirebaseProps} from 'react-redux-firebase';
|
||||
import {selectProfile} from '../store/firebase';
|
||||
import User, {newUser} from '../models/User';
|
||||
// Style
|
||||
import Header from '../components/Header';
|
||||
import InputField from '../components/InputField';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
|
||||
import GoogleButton from 'react-google-button';
|
||||
|
||||
// extends withFirebaseProps type to ad profile info
|
||||
interface IProps extends WithFirebaseProps<User> {
|
||||
isEmpty: boolean;
|
||||
isLoaded: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign up form
|
||||
* Sign up form recieves firebase from withFirebase HOC
|
||||
*/
|
||||
const SignUp: FC = () => {
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password1, setPassword1] = useState('');
|
||||
const [password2, setPassword2] = useState('');
|
||||
const [error, setError] = useState(false);
|
||||
const SignUp: FC<IProps> = ({firebase, isEmpty, isLoaded}) => {
|
||||
const history = useHistory();
|
||||
|
||||
const isDisabled: boolean =
|
||||
name === '' || email === '' || password1 === '' || password1 !== password2;
|
||||
const initFormData = {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
error: false,
|
||||
};
|
||||
const [formData, setFormData] = useState(initFormData);
|
||||
|
||||
/** update each input state value onChange */
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void =>
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
|
||||
/** clean form after successful submition */
|
||||
const resetForm = () => setFormData(initFormData);
|
||||
|
||||
const {name, email, password, password2, error} = formData;
|
||||
|
||||
// prevent submitting invalid forms
|
||||
const isDisabled: boolean = name === '' || email === '' || password === '';
|
||||
|
||||
/** create user with password */
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (password !== password2) {
|
||||
setFormData({...formData, error: true});
|
||||
} else {
|
||||
// pass the info to store into the second argument
|
||||
firebase
|
||||
.createUser({email, password}, newUser(name, email))
|
||||
.then(() => {
|
||||
resetForm();
|
||||
// redirect to dashboard
|
||||
history.push(ROUTES.DASHBOARD);
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
};
|
||||
|
||||
const loginWithGoogle = () =>
|
||||
firebase.login({provider: 'google', type: 'popup'});
|
||||
|
||||
if (isLoaded && !isEmpty) {
|
||||
return <Redirect to={ROUTES.DASHBOARD} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
{error && <div className="alert alert-danger">Invalid credentials</div>}
|
||||
{error && (
|
||||
<div className="alert alert-danger">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} /> Passwords don't
|
||||
match!
|
||||
</div>
|
||||
)}
|
||||
<Header title="Sign Up" lead="Create your account" />
|
||||
<form action="dashboard.html" className="form">
|
||||
<InputField
|
||||
state={name}
|
||||
setState={setName}
|
||||
placeholder="Name"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
<GoogleButton type="light" className="my-1" onClick={loginWithGoogle} />
|
||||
<form className="form" onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<input
|
||||
name="name"
|
||||
value={name}
|
||||
onChange={handleChange}
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<InputField
|
||||
state={email}
|
||||
setState={setEmail}
|
||||
placeholder="Email Address"
|
||||
required
|
||||
/>
|
||||
<small className="form-text">
|
||||
This site uses Gravatar, so use a Gravatar email.
|
||||
</small>
|
||||
<div className="form-group">
|
||||
<input
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={handleChange}
|
||||
placeholder="Email Address"
|
||||
type="email"
|
||||
required
|
||||
/>
|
||||
<small className="form-text">
|
||||
This site uses Gravatar, so use a Gravatar email.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<InputField
|
||||
state={password1}
|
||||
setState={setPassword1}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
minLength={6}
|
||||
required
|
||||
/>
|
||||
<InputField
|
||||
state={password2}
|
||||
setState={setPassword2}
|
||||
placeholder="Confirm Password"
|
||||
type="password"
|
||||
minLength={6}
|
||||
required
|
||||
/>
|
||||
<div className="form-group">
|
||||
<input
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={handleChange}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
minLength={6}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<input
|
||||
name="password2"
|
||||
value={password2}
|
||||
onChange={handleChange}
|
||||
placeholder="Confirm Password"
|
||||
type="password"
|
||||
minLength={6}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
|
|
@ -73,4 +150,7 @@ const SignUp: FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
||||
/** subscribe to store and firebase */
|
||||
const enhance = compose<FC<IProps>>(connect(selectProfile), withFirebase);
|
||||
|
||||
export default enhance(SignUp);
|
||||
|
|
|
|||
548
src/static/css/style.min.css
vendored
548
src/static/css/style.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
@import "_config";
|
||||
@import '_config';
|
||||
|
||||
// Backgrounds
|
||||
|
||||
|
|
|
|||
|
|
@ -9558,6 +9558,13 @@ react-error-overlay@^6.0.7:
|
|||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
|
||||
|
||||
react-google-button@^0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/react-google-button/-/react-google-button-0.7.1.tgz#e84b4ce270a66e345489dc86e47235e877fcc81a"
|
||||
integrity sha512-FN8/9Va6oAGKL561yddNzcjz0iGlt2GZFwiDPczEu0fDVnR21/nBrPs7Y5x97V1S4//nvtkvv6ohfwtxIHpbfg==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
|
|
|||
Loading…
Reference in a new issue