diff --git a/package.json b/package.json index 8b3c84b..31db85a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/InputField.tsx b/src/components/InputField.tsx deleted file mode 100644 index 3fce3d7..0000000 --- a/src/components/InputField.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, {FC} from 'react'; - -interface IProps { - state: string | number | string[] | undefined; - setState: React.Dispatch>; - placeholder: string; - type?: string; - required?: boolean; - autoFocus?: boolean; - minLength?: number; -} - -const InputField: FC = ({ - state, - setState, - placeholder, - autoFocus, - required, - minLength, - type = 'text', -}) => { - const handleChange = (e: React.ChangeEvent): void => { - setState(e.target.value); - }; - - return ( -
- -
- ); -}; -export default InputField; diff --git a/src/models/User.ts b/src/models/User.ts index 3b89aee..58ddd81 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -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; diff --git a/src/pages/SignUp.tsx b/src/pages/SignUp.tsx index f2c036e..2f9d3e8 100644 --- a/src/pages/SignUp.tsx +++ b/src/pages/SignUp.tsx @@ -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 { + 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 = ({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): 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) => { + 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 ; + } return (
- {error &&
Invalid credentials
} + {error && ( +
+ Passwords don't + match! +
+ )}
-
- + + +
+ +
- - - This site uses Gravatar, so use a Gravatar email. - +
+ + + This site uses Gravatar, so use a Gravatar email. + +
- - +
+ +
+ +
+ +
{ ); }; -export default SignUp; +/** subscribe to store and firebase */ +const enhance = compose>(connect(selectProfile), withFirebase); + +export default enhance(SignUp); diff --git a/src/static/css/style.min.css b/src/static/css/style.min.css index e6d5d3e..9847217 100644 --- a/src/static/css/style.min.css +++ b/src/static/css/style.min.css @@ -1,547 +1 @@ -.m-1 { - margin: 1rem; -} -.my-1 { - margin: 1rem 0; -} -.p-1 { - padding: 1rem; -} -.py-1 { - padding: 1rem 0; -} -.m-2 { - margin: 2rem; -} -.my-2 { - margin: 2rem 0; -} -.p-2 { - padding: 2rem; -} -.py-2 { - padding: 2rem 0; -} -.m-3 { - margin: 3rem; -} -.my-3 { - margin: 3rem 0; -} -.p-3 { - padding: 3rem; -} -.py-3 { - padding: 3rem 0; -} -.m-4 { - margin: 4rem; -} -.my-4 { - margin: 4rem 0; -} -.p-4 { - padding: 4rem; -} -.py-4 { - padding: 4rem 0; -} -.m-5 { - margin: 5rem; -} -.my-5 { - margin: 5rem 0; -} -.p-5 { - padding: 5rem; -} -.py-5 { - padding: 5rem 0; -} -.m-1 { - margin: 1rem; -} -.my-1 { - margin: 1rem 0; -} -.p-1 { - padding: 1rem; -} -.py-1 { - padding: 1rem 0; -} -.m-2 { - margin: 2rem; -} -.my-2 { - margin: 2rem 0; -} -.p-2 { - padding: 2rem; -} -.py-2 { - padding: 2rem 0; -} -.m-3 { - margin: 3rem; -} -.my-3 { - margin: 3rem 0; -} -.p-3 { - padding: 3rem; -} -.py-3 { - padding: 3rem 0; -} -.m-4 { - margin: 4rem; -} -.my-4 { - margin: 4rem 0; -} -.p-4 { - padding: 4rem; -} -.py-4 { - padding: 4rem 0; -} -.m-5 { - margin: 5rem; -} -.my-5 { - margin: 5rem 0; -} -.p-5 { - padding: 5rem; -} -.py-5 { - padding: 5rem 0; -} -.bg-primary { - background-color: #17a2b8; - color: #fff; -} -.bg-light { - background-color: #f4f4f4; - color: #333; - border: #ccc 1px solid; -} -.bg-dark { - background-color: #343a40; - color: #fff; -} -.bg-danger { - background-color: #dc3545; - color: #fff; -} -.bg-success { - background-color: #28a745; - color: #fff; -} -.bg-white { - background-color: #fff; - color: #333; - border: #ccc 1px solid; -} -.dark-overlay { - height: 100%; - width: 100%; - position: absolute; - top: 0; - left: 0; - background-color: rgba(0, 0, 0, 0.7); -} -.x-large { - font-size: 4rem; - line-height: 1.2; - margin-bottom: 1rem; -} -.large { - font-size: 3rem; - line-height: 1.2; - margin-bottom: 1rem; -} -.lead { - font-size: 1.5rem; - margin-bottom: 1rem; -} -.text-primary { - color: #17a2b8; -} -.btn { - display: inline-block; - background-color: #f4f4f4; - color: #333; - padding: 0.4rem 1.3rem; - border: none; - cursor: pointer; - font-size: 1rem; - margin-right: 0.5rem; - outline: none; - transition: all 0.3s ease-in; -} -.btn.btn-primary { - background-color: #17a2b8; - color: #fff; -} -.btn.btn-primary:hover { - background: #1ab6cf; -} -.btn.btn-dark { - background-color: #343a40; - color: #fff; -} -.btn.btn-dark:hover { - background: #3f474e; -} -.btn.btn-light { - background-color: #f4f4f4; - color: #333; -} -.btn.btn-light:hover { - background: #626d78; - color: white; -} -.btn.btn-danger { - background-color: #dc3545; - color: #fff; -} -.btn.btn-danger:hover { - background: #e04b59; -} -.btn.btn-success { - background-color: #28a745; - color: #fff; -} -.btn.btn-success:hover { - background: #2dbc4e; -} -.container { - max-width: 1100px; - margin: auto; - overflow: hidden; - padding: 0 2rem; - margin-top: 5rem; - margin-bottom: 3rem; -} -.alert { - padding: 0.8rem; - margin: 1rem; - opacity: 0.9; - background-color: #f4f4f4; - color: #333; -} -.alert.alert-primary { - background-color: #17a2b8; - color: #fff; -} -.alert.alert-dark { - background-color: #343a40; - color: #fff; -} -.alert.alert-success { - background-color: #28a745; - color: #fff; -} -.alert.alert-danger { - background-color: #dc3545; - color: #fff; -} -.round-img { - border-radius: 50%; -} -.line { - height: 1px; - background: #ccc; - margin: 1.5rem 0; -} -.badge { - font-size: 0.8rem; - padding: 0.1rem; - text-align: center; - margin: 0.3rem; - background: #f4f4f4; - color: #333; - border-radius: 1rem; -} -.badge.badge-primary { - background-color: #17a2b8; - color: #fff; -} -.badge.badge-dark { - background-color: #343a40; - color: #fff; -} -.badge.badge-success { - background-color: #28a745; - color: #fff; -} -.badge.badge-danger { - background-color: #dc3545; - color: #fff; -} -.table th, -.table td { - padding: 1rem; - text-align: left; -} -.table th { - background: #f4f4f4; -} -.form-group { - margin: 1.2rem 0; -} -.form-text { - display: block; - margin-top: 0.3rem; - color: #888; -} -.form input[type='text'], -.form input[type='email'], -.form input[type='password'], -.form input[type='date'], -.form select, -.form textarea { - display: block; - width: 100%; - padding: 0.4rem; - font-size: 1.2rem; - border: 1px solid #ccc; - border-radius: 0.3rem; -} -.form input[type='submit'] { - font: inherit; -} -.form .social-input { - display: flex; -} -.form .social-input svg { - padding: 0.5rem; - width: 3rem; -} -.form .social-input svg.fa-twitter { - color: #38a1f3; -} -.form .social-input svg.fa-facebook { - color: #3b5998; -} -.form .social-input svg.fa-instagram { - color: #3f729b; -} -.form .social-input svg.fa-youtube { - color: #c4302b; -} -.form .social-input svg.fa-linkedin { - color: #0077b5; -} -@media (max-width: 700px) { - .hide-sm { - display: none; - } - .container { - margin-top: 8rem; - } - .x-large { - font-size: 3rem; - } - .large { - font-size: 2rem; - } - .lead { - font-size: 1rem; - } - .navbar { - flex-direction: column; - text-align: center; - } - .navbar ul { - text-align: center; - justify-content: center; - } - .navbar h1 { - margin-bottom: 1rem; - } - .dash-buttons a { - display: block; - width: 100%; - margin-bottom: 0.2rem; - } - .profile { - grid-template-columns: 1fr; - text-align: center; - } - .profile ul { - display: none; - } - .profile-grid { - grid-template-areas: 'top' 'about' 'exp' 'edu' 'github'; - } - .profile-about .skills { - flex-direction: column; - } - .post { - grid-template-columns: 1fr; - } - .post a, - .post button { - padding: 0.3rem 0.4rem; - } -} -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} -body { - font-family: 'Raleway', sans-serif; - font-size: 1rem; - line-height: 1.6; - background-color: white; - color: #333; -} -a { - text-decoration: none; - color: #17a2b8; -} -ul { - list-style: none; -} -img { - width: 100%; -} -.navbar { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.7rem 2rem; - position: fixed; - z-index: 1; - width: 100%; - top: 0; - border-bottom: solid 1px #17a2b8; - opacity: 0.9; -} -.navbar ul { - display: flex; -} -.navbar a { - color: white; - padding: 0.45rem; - margin: 0 0.25rem; -} -.navbar a:hover { - color: #17a2b8; -} -.landing { - position: relative; - background: url('../img/showcase.jpg') no-repeat center center/cover; - height: 100vh; -} -.landing-inner { - color: white; - height: 100%; - display: flex; - flex-direction: column; - width: 80%; - margin: auto; - align-items: center; - justify-content: center; - text-align: center; -} -.profile { - display: grid; - grid-template-columns: 2fr 4fr 2fr; - grid-gap: 2rem; - padding: 1rem; - line-height: 1.8; - margin-bottom: 1rem; - align-items: center; -} -.profile-grid { - display: grid; - grid-template-areas: 'top top' 'about about' 'exp edu' 'github github'; - grid-gap: 1rem; -} -.profile-grid .profile-top { - grid-area: top; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; -} -.profile-grid .profile-top img { - width: 250px; -} -.profile-grid .profile-top .icons a { - color: white; - margin: 0 0.3rem; -} -.profile-grid .profile-top .icons a:hover { - color: #343a40; -} -.profile-grid .profile-about { - grid-area: about; - text-align: center; -} -.profile-grid .profile-about .skills { - display: flex; - justify-content: center; - align-items: center; - text-align: center; -} -.profile-grid .profile-exp { - grid-area: exp; -} -.profile-grid .profile-edu { - grid-area: edu; -} -.profile-grid .profile-exp > div, -.profile-grid .profile-edu > div { - margin-bottom: 1rem; - padding-bottom: 1rem; - border-bottom: #ccc 1px dotted; -} -.profile-grid .profile-exp > div:last-child, -.profile-grid .profile-edu > div:last-child { - border: none; -} -.profile-grid .profile-exp > div p, -.profile-grid .profile-edu > div p { - margin: 0.5rem 0; -} -.profile-grid .profile-github { - grid-area: github; -} -.profile-grid .profile-github .repo { - display: flex; -} -.profile-grid .profile-github .repo > div:first-child { - flex: 7; - flex-basis: 70%; -} -.profile-grid .profile-github .repo > div:last-child { - flex: 3; - flex-basis: 30%; -} -.post-form-header { - padding: 0.5rem; -} -.post { - display: grid; - grid-template-columns: 1fr 4fr; - grid-gap: 2rem; - align-items: center; -} -.post > div:first-child { - text-align: center; -} -.post img { - width: 150px; -} -.not-found { - position: relative; - background: url('../img/404.jpg') no-repeat center center/cover; - height: 100vh; -} +.m-1{margin:1rem}.my-1{margin:1rem 0}.p-1{padding:1rem}.py-1{padding:1rem 0}.m-2{margin:2rem}.my-2{margin:2rem 0}.p-2{padding:2rem}.py-2{padding:2rem 0}.m-3{margin:3rem}.my-3{margin:3rem 0}.p-3{padding:3rem}.py-3{padding:3rem 0}.m-4{margin:4rem}.my-4{margin:4rem 0}.p-4{padding:4rem}.py-4{padding:4rem 0}.m-5{margin:5rem}.my-5{margin:5rem 0}.p-5{padding:5rem}.py-5{padding:5rem 0}.m-1{margin:1rem}.my-1{margin:1rem 0}.p-1{padding:1rem}.py-1{padding:1rem 0}.m-2{margin:2rem}.my-2{margin:2rem 0}.p-2{padding:2rem}.py-2{padding:2rem 0}.m-3{margin:3rem}.my-3{margin:3rem 0}.p-3{padding:3rem}.py-3{padding:3rem 0}.m-4{margin:4rem}.my-4{margin:4rem 0}.p-4{padding:4rem}.py-4{padding:4rem 0}.m-5{margin:5rem}.my-5{margin:5rem 0}.p-5{padding:5rem}.py-5{padding:5rem 0}.bg-primary{background-color:#17a2b8;color:#fff}.bg-light{background-color:#f4f4f4;color:#333;border:#ccc 1px solid}.bg-dark{background-color:#343a40;color:#fff}.bg-danger{background-color:#dc3545;color:#fff}.bg-success{background-color:#28a745;color:#fff}.bg-white{background-color:#fff;color:#333;border:#ccc 1px solid}.dark-overlay{height:100%;width:100%;position:absolute;top:0;left:0;background-color:rgba(0,0,0,0.7)}.x-large{font-size:4rem;line-height:1.2;margin-bottom:1rem}.large{font-size:3rem;line-height:1.2;margin-bottom:1rem}.lead{font-size:1.5rem;margin-bottom:1rem}.text-primary{color:#17a2b8}.btn{display:inline-block;background-color:#f4f4f4;color:#333;padding:0.4rem 1.3rem;border:none;cursor:pointer;font-size:1rem;margin-right:0.5rem;outline:none;transition:all 0.3s ease-in}.btn:disabled{background-color:#333}.btn.btn-primary{background-color:#17a2b8;color:#fff}.btn.btn-primary:hover{background:#1ab6cf}.btn.btn-dark{background-color:#343a40;color:#fff}.btn.btn-dark:hover{background:#3f474e}.btn.btn-light{background-color:#f4f4f4;color:#333}.btn.btn-light:hover{background:#626d78;color:white}.btn.btn-danger{background-color:#dc3545;color:#fff}.btn.btn-danger:hover{background:#e04b59}.btn.btn-success{background-color:#28a745;color:#fff}.btn.btn-success:hover{background:#2dbc4e}.container{max-width:1100px;margin:auto;overflow:hidden;padding:0 2rem;margin-top:5rem;margin-bottom:3rem}.alert{padding:0.8rem;margin:1rem;opacity:0.9;background-color:#f4f4f4;color:#333}.alert.alert-primary{background-color:#17a2b8;color:#fff}.alert.alert-dark{background-color:#343a40;color:#fff}.alert.alert-success{background-color:#28a745;color:#fff}.alert.alert-danger{background-color:#dc3545;color:#fff}.round-img{border-radius:50%}.line{height:1px;background:#ccc;margin:1.5rem 0}.badge{font-size:0.8rem;padding:0.1rem;text-align:center;margin:0.3rem;background:#f4f4f4;color:#333;border-radius:1rem}.badge.badge-primary{background-color:#17a2b8;color:#fff}.badge.badge-dark{background-color:#343a40;color:#fff}.badge.badge-success{background-color:#28a745;color:#fff}.badge.badge-danger{background-color:#dc3545;color:#fff}.table th,.table td{padding:1rem;text-align:left}.table th{background:#f4f4f4}.form-group{margin:1.2rem 0}.form-text{display:block;margin-top:0.3rem;color:#888}.form input[type='text'],.form input[type='email'],.form input[type='password'],.form input[type='date'],.form select,.form textarea{display:block;width:100%;padding:0.4rem;font-size:1.2rem;border:1px solid #ccc;border-radius:0.3rem}.form input[type='submit']{font:inherit}.form .social-input{display:flex}.form .social-input svg{padding:0.5rem;width:3rem}.form .social-input svg.fa-twitter{color:#38a1f3}.form .social-input svg.fa-facebook{color:#3b5998}.form .social-input svg.fa-instagram{color:#3f729b}.form .social-input svg.fa-youtube{color:#c4302b}.form .social-input svg.fa-linkedin{color:#0077b5}@media (max-width: 700px){.hide-sm{display:none}.container{margin-top:8rem}.x-large{font-size:3rem}.large{font-size:2rem}.lead{font-size:1rem}.navbar{flex-direction:column;text-align:center}.navbar ul{text-align:center;justify-content:center}.navbar h1{margin-bottom:1rem}.dash-buttons a{display:block;width:100%;margin-bottom:0.2rem}.profile{grid-template-columns:1fr;text-align:center}.profile ul{display:none}.profile-grid{grid-template-areas:"top" "about" "exp" "edu" "github"}.profile-about .skills{flex-direction:column}.post{grid-template-columns:1fr}.post a,.post button{padding:0.3rem 0.4rem}}*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Raleway', sans-serif;font-size:1rem;line-height:1.6;background-color:white;color:#333}a{text-decoration:none;color:#17a2b8}ul{list-style:none}img{width:100%}.navbar{display:flex;align-items:center;justify-content:space-between;padding:0.7rem 2rem;position:fixed;z-index:1;width:100%;top:0;border-bottom:solid 1px #17a2b8;opacity:0.9}.navbar ul{display:flex}.navbar a{color:white;padding:0.45rem;margin:0 0.25rem}.navbar a:hover{color:#17a2b8}.landing{position:relative;background:url("../img/showcase.jpg") no-repeat center center/cover;height:100vh}.landing-inner{color:white;height:100%;display:flex;flex-direction:column;width:80%;margin:auto;align-items:center;justify-content:center;text-align:center}.profile{display:grid;grid-template-columns:2fr 4fr 2fr;grid-gap:2rem;padding:1rem;line-height:1.8;margin-bottom:1rem;align-items:center}.profile-grid{display:grid;grid-template-areas:'top top' 'about about' 'exp edu' 'github github';grid-gap:1rem}.profile-grid .profile-top{grid-area:top;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.profile-grid .profile-top img{width:250px}.profile-grid .profile-top .icons a{color:white;margin:0 0.3rem}.profile-grid .profile-top .icons a:hover{color:#343a40}.profile-grid .profile-about{grid-area:about;text-align:center}.profile-grid .profile-about .skills{display:flex;justify-content:center;align-items:center;text-align:center}.profile-grid .profile-exp{grid-area:exp}.profile-grid .profile-edu{grid-area:edu}.profile-grid .profile-exp>div,.profile-grid .profile-edu>div{margin-bottom:1rem;padding-bottom:1rem;border-bottom:#ccc 1px dotted}.profile-grid .profile-exp>div:last-child,.profile-grid .profile-edu>div:last-child{border:none}.profile-grid .profile-exp>div p,.profile-grid .profile-edu>div p{margin:0.5rem 0}.profile-grid .profile-github{grid-area:github}.profile-grid .profile-github .repo{display:flex}.profile-grid .profile-github .repo>div:first-child{flex:7;flex-basis:70%}.profile-grid .profile-github .repo>div:last-child{flex:3;flex-basis:30%}.post-form-header{padding:0.5rem}.post{display:grid;grid-template-columns:1fr 4fr;grid-gap:2rem;align-items:center}.post>div:first-child{text-align:center}.post img{width:150px}.not-found{position:relative;background:url("../img/404.jpg") no-repeat center center/cover;height:100vh} diff --git a/src/static/scss/_utils.scss b/src/static/scss/_utils.scss index f8efe6d..821a924 100644 --- a/src/static/scss/_utils.scss +++ b/src/static/scss/_utils.scss @@ -1,4 +1,4 @@ -@import "_config"; +@import '_config'; // Backgrounds diff --git a/yarn.lock b/yarn.lock index ac8c167..8522ce0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"