diff --git a/src/components/CreateUser.js b/src/components/CreateUser.js deleted file mode 100644 index 0d45fd7d..00000000 --- a/src/components/CreateUser.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import Footer from "./common/Footer.js"; -import CreateUserForm from "./CreateUser/CreateUserForm.js"; -import LoginGuy from "../img/blueguy.png"; - -/**--------Props-------- - * None - */ - -class CreateUser extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return ( -
-
- -
-
- ); - } -} - -export default CreateUser; diff --git a/src/components/CreateUser.test.js b/src/components/CreateUser.test.js deleted file mode 100644 index b788b3fc..00000000 --- a/src/components/CreateUser.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -import { shallow } from "enzyme"; -import CreateUser from "./CreateUser"; - -describe("CreateUser", () => { - it("smoke test", () => { - const component = shallow(); - expect(component.exists()).toBe(true); - }); - - it("should render correctly", () => { - const component = shallow(); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/components/Login.js b/src/components/Login.js index 675f2aed..08db0528 100644 --- a/src/components/Login.js +++ b/src/components/Login.js @@ -1,20 +1,146 @@ import React from "react"; -import Footer from "./common/Footer"; import "styles/Login.scss"; import LoginForm from "./Login/LoginForm"; -import LoginGuy from "img/blueguy.png"; +import CreateUserForm from "./Login/CreateUserForm.js"; + +import { faCode, faHeart, faPaintBrush, faRedo, faRocket } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import LoginImg1 from "img/login1.svg"; +import LoginImg2 from "img/login2.svg"; +import LoginImg3 from "img/login3.svg"; +import LoginImg4 from "img/login4.svg"; +import LoginImg5 from "img/login5.svg"; + +const loginArt = [LoginImg1, LoginImg2, LoginImg3, LoginImg4, LoginImg5]; +const loginArtAlts = [ + "girl on tablet", + "boy using VR headset", + "girl controlling drone", + "boy developing an app", + "girl coding for robot", +]; + +const gradientColors = { + 0: ["#FFB5B5", "#FFF7F3"], + 1: ["#54C9FE", "#D8F6FF"], + 2: ["#ACF53F", "#DAFFA4"], + 3: ["#FFF065", "#FFF7F3"], + 4: ["#FF94DB", "#FFE4EE"], +}; + +const themeColors = { + 0: ["#FF5F54", "white"], + 1: ["#36C5FC", "white"], + 2: ["#ACF53F", "white"], + 3: ["#FFF065", "black"], + 4: ["#FF94DB", "white"], +}; class Login extends React.Component { - render() { + state = { + dummy: false, + index: Math.floor(Math.random() * 5), + }; + // basically, when the window resizes, we should recalculate get SVG - the window parameters change! + componentDidMount = () => { + window.addEventListener("resize", () => this.setState({ dummy: !this.state.dummy })); + }; + componentWillUnmount = () => { + window.removeEventListener("resize", () => this.setState({ dummy: !this.state.dummy })); + }; + getSVG = () => { return ( -
-
- + + + + + + + + + + ); + }; + + render = () => { + const textHighlightStyle = { + background: `linear-gradient(180deg, rgba(255,255,255,0) 80%, ${ + gradientColors[this.state.index][0] + } 50%)`, + }; + return ( +
+
+
this.setState({ index: Math.floor(Math.random() * 5) })} + > + +
+
+
+

+ The ACM Teach LA{" "} + Online Editor +

+

a web IDE that lets you write and run Python & Processing code, anywhere.

+ {this.props.create ? ( + + ) : ( + + )} +
+
+
+ {" "} + by{" "} + + ACM Teach LA + {" "} + with +
+
+
+ {`decorative + {this.getSVG()}
-
); - } + }; } export default Login; diff --git a/src/components/CreateUser/CreateUserForm.js b/src/components/Login/CreateUserForm.js similarity index 83% rename from src/components/CreateUser/CreateUserForm.js rename to src/components/Login/CreateUserForm.js index 485adc40..c9792a54 100644 --- a/src/components/CreateUser/CreateUserForm.js +++ b/src/components/Login/CreateUserForm.js @@ -5,7 +5,7 @@ import { Link } from "react-router-dom"; import * as firebase from "firebase/app"; import "firebase/auth"; import SHA256 from "crypto-js/sha256"; -import LoginInput from "../Login/LoginInput.js"; +import LoginInput from "./LoginInput.js"; import { MINIMUM_USERNAME_LENGTH, MINIMUM_PASSWORD_LENGTH, @@ -100,7 +100,7 @@ export default class CreateUserForm extends React.Component { * @return {void} submit returns early if the inputs passed by a prospective user * are bad. */ - submit = e => { + submit = (e) => { e.preventDefault(); this.setState({ @@ -129,7 +129,7 @@ export default class CreateUserForm extends React.Component { .auth() .createUserWithEmailAndPassword(email, passHash) .then(({ user }) => {}) - .catch(err => { + .catch((err) => { console.error(err); let newMsg = err.message; switch (err.code) { @@ -185,9 +185,9 @@ export default class CreateUserForm extends React.Component { return addBreak ?
: null; }; - updateUsername = username => this.setState({ username }); - updatePassword = password => this.setState({ password }); - updateConfirmPassword = confirmPassword => this.setState({ confirmPassword }); + updateUsername = (username) => this.setState({ username }); + updatePassword = (password) => this.setState({ password }); + updateConfirmPassword = (confirmPassword) => this.setState({ confirmPassword }); renderInputs = () => (
@@ -221,37 +221,50 @@ export default class CreateUserForm extends React.Component { if (this.state.waiting) { return (
- +
); } else { + const unclickedStyle = { + backgroundColor: "white", + borderColor: this.props.themeColor, + borderWidth: "medium", + borderRadius: "4px", + color: "black", + }; + + const clickedStyle = { + backgroundColor: this.props.themeColor, + borderColor: this.props.themeColor, + borderWidth: "medium", + borderRadius: "4px", + color: this.props.textColor, + }; return ( - +
+ + + or, login with an existing account + +
); } }; - renderLink = () => ( - - Already have an account? Click here to login. - - ); - render() { return ( -
-
-

Create a new account

-
- {this.renderInputs()} - {this.renderAction()} -
-
- {this.renderLink()} -
-
+
+ {this.renderInputs()} + {this.renderAction()} +
); } } diff --git a/src/components/CreateUser/CreateUserForm.test.js b/src/components/Login/CreateUserForm.test.js similarity index 94% rename from src/components/CreateUser/CreateUserForm.test.js rename to src/components/Login/CreateUserForm.test.js index ec03a62d..80594fce 100644 --- a/src/components/CreateUser/CreateUserForm.test.js +++ b/src/components/Login/CreateUserForm.test.js @@ -31,12 +31,7 @@ describe("CreateUserForm", () => { expect(component.state().usernameMessage).toBe( `Username must only use upper case and lower case letters, numbers, and/or the special characters !@#$%`, ); - expect( - component - .find(".login-form-input-error") - .at(0) - .text(), - ).toBe( + expect(component.find(".login-form-input-error").at(0).text()).toBe( `Username must only use upper case and lower case letters, numbers, and/or the special characters !@#$%`, ); @@ -60,12 +55,7 @@ describe("CreateUserForm", () => { expect(component.state().passwordMessage).toBe( `Password must only use upper case and lower case letters, numbers, and/or the special characters !@#$%`, ); - expect( - component - .find(".login-form-input-error") - .at(0) - .text(), - ).toBe( + expect(component.find(".login-form-input-error").at(0).text()).toBe( `Password must only use upper case and lower case letters, numbers, and/or the special characters !@#$%`, ); diff --git a/src/components/Login/LoginForm.js b/src/components/Login/LoginForm.js index 88655449..f665fee9 100644 --- a/src/components/Login/LoginForm.js +++ b/src/components/Login/LoginForm.js @@ -9,10 +9,6 @@ import * as firebase from "firebase/app"; import "firebase/auth"; import "styles/Login.scss"; -/**-------Props-------- - * provider: Firebase Provider that allows the app to do Facebook Logins - */ - export default class LoginModal extends React.Component { constructor(props) { super(props); @@ -22,10 +18,11 @@ export default class LoginModal extends React.Component { password: "", errorMsg: "", waiting: false, + hoverButton: false, }; } - handleEmailLogin = e => { + handleEmailLogin = (e) => { this.setState({ waiting: true, errorMsg: "" }); e.preventDefault(); //prevents page from reloading after submitting form @@ -37,7 +34,7 @@ export default class LoginModal extends React.Component { .auth() .signInWithEmailAndPassword(email, passwordHash) .then(() => {}) - .catch(err => { + .catch((err) => { console.error(err); let newMsg = err.message; switch (err.code) { @@ -80,18 +77,8 @@ export default class LoginModal extends React.Component { } }; - handleSocialLogin = e => { - this.setState({ waiting: true }); - firebase - .auth() - .signInWithPopup(this.props.provider) - .catch(function(err) { - this.setState({ errorMsg: "Failed to use Facebook login provider", waiting: false }); - }); - }; - - updateUsername = username => this.setState({ username }); - updatePassword = password => this.setState({ password }); + updateUsername = (username) => this.setState({ username }); + updatePassword = (password) => this.setState({ password }); renderErrorMessage = (msg, addBreak) => { if (msg) @@ -126,45 +113,61 @@ export default class LoginModal extends React.Component { ); renderAction = () => { + const unclickedStyle = { + backgroundColor: "white", + borderColor: this.props.themeColor, + borderWidth: "medium", + borderRadius: "4px", + color: "black", + }; + + const clickedStyle = { + backgroundColor: this.props.themeColor, + borderColor: this.props.themeColor, + borderWidth: "medium", + borderRadius: "4px", + color: this.props.textColor, + }; + if (this.state.waiting) { return (
- +
); } else { return ( - +
+ + + or, create an account + +
); } }; render() { return ( -
+
-

- Welcome to <Teach LA> -

-
{this.renderInputs()} {this.renderAction()} -
-
- - Don't have an account? Create one now! - -
-
-
+
Forgot your password?

Send us an email at acmteachla@gmail.com{" "} diff --git a/src/components/__snapshots__/CreateUser.test.js.snap b/src/components/__snapshots__/CreateUser.test.js.snap deleted file mode 100644 index fed014ea..00000000 --- a/src/components/__snapshots__/CreateUser.test.js.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CreateUser should render correctly 1`] = ` -

-
- -
- <_default /> -
-`; diff --git a/src/components/app.js b/src/components/app.js index 81454c70..8a868acc 100644 --- a/src/components/app.js +++ b/src/components/app.js @@ -5,7 +5,6 @@ import LoginPage from "./containers/LoginContainer"; import MainContainer from "./containers/MainContainer"; import ViewOnlyContainer from "./containers/ViewOnlyContainer"; import LoadingPage from "./common/LoadingPage"; -import CreateUserPage from "./CreateUser"; import Error from "./Error"; import PageNotFound from "./PageNotFound"; import * as firebase from "firebase/app"; @@ -26,7 +25,7 @@ class App extends React.Component { //==============React Lifecycle Functions===================// componentDidMount = () => { - firebase.auth().onAuthStateChanged(async user => { + firebase.auth().onAuthStateChanged(async (user) => { await this.onAuthHandler(user); }); window.addEventListener("resize", this.handleResize, true); @@ -48,7 +47,7 @@ class App extends React.Component { * application. * @param {firebase.auth().currentUser} user - a user object as passed by firebase.auth() */ - onAuthHandler = async user => { + onAuthHandler = async (user) => { console.log("checking auth"); if (user) { console.log("found user"); @@ -67,12 +66,12 @@ class App extends React.Component { } }; - showErrorPage = err => { + showErrorPage = (err) => { console.log(err); this.props.loadFailure(err); }; - renderHome = isValidUser => { + renderHome = (isValidUser) => { return isValidUser ? : ; }; @@ -138,7 +137,7 @@ class App extends React.Component { ) : isValidUser ? ( ) : ( - + ) } /> @@ -158,7 +157,7 @@ class App extends React.Component { {/* Get program endpoint */} ( + render={(props) => ( )} /> diff --git a/src/img/background1.svg b/src/img/background1.svg new file mode 100644 index 00000000..368125dc --- /dev/null +++ b/src/img/background1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/img/login1.svg b/src/img/login1.svg new file mode 100644 index 00000000..760caf37 --- /dev/null +++ b/src/img/login1.svg @@ -0,0 +1 @@ +login1_2 \ No newline at end of file diff --git a/src/img/login2.svg b/src/img/login2.svg new file mode 100644 index 00000000..354a09fe --- /dev/null +++ b/src/img/login2.svg @@ -0,0 +1 @@ +login2 \ No newline at end of file diff --git a/src/img/login3.svg b/src/img/login3.svg new file mode 100644 index 00000000..9ba85b7d --- /dev/null +++ b/src/img/login3.svg @@ -0,0 +1 @@ +login3 \ No newline at end of file diff --git a/src/img/login4.svg b/src/img/login4.svg new file mode 100644 index 00000000..cbac254b --- /dev/null +++ b/src/img/login4.svg @@ -0,0 +1 @@ +Asset 6 \ No newline at end of file diff --git a/src/img/login5.svg b/src/img/login5.svg new file mode 100644 index 00000000..e3c74fd9 --- /dev/null +++ b/src/img/login5.svg @@ -0,0 +1 @@ +login5 \ No newline at end of file diff --git a/src/img/tla-logo-green.svg b/src/img/tla-logo-green.svg new file mode 100644 index 00000000..2987b11c --- /dev/null +++ b/src/img/tla-logo-green.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/styles/Login.scss b/src/styles/Login.scss index 507be2c8..d71ed762 100644 --- a/src/styles/Login.scss +++ b/src/styles/Login.scss @@ -1,13 +1,7 @@ -@import 'styles/variables.scss'; - -.login-page { - margin: 0px; - width: 100%; - margin-left: auto; - margin-right: auto; -} +@import "styles/variables.scss"; .login-page-content { + display: flex; width: 100%; height: 100vh; background-repeat: no-repeat; @@ -15,22 +9,31 @@ background-size: auto 93%; overflow-y: auto; box-sizing: border-box; - background-color: $theme-dark; - padding-bottom: 8vh; + background-color: white; + background-size: auto 100%; + background-position: right; + padding-left: 4em; } -@media only screen and (max-width: 1024px) { - .login-page-content { - background-image: none; - } +.login-page-content-main { + flex: 1 0 auto; + display: flex; + flex-direction: column; + justify-content: center; } -.login-form { - padding: 0 1.5em; - color: $off-white; +.login-page-content-footer{ + flex-shrink: 0; + padding-bottom: 1em; } @media only screen and (max-width: 1024px) { + .login-page-content { + padding-top: 3em; + background-image: none; + flex-direction: column; + padding-left: 0em; + } .login-form-loader { width: 100%; align-items: center; @@ -41,9 +44,9 @@ } .login-form-input-header { - color: $off-white; - font-size: 36px; - margin-bottom: 1rem; + color: $theme-dark; + font-size: 24px; + margin-bottom: 0.2rem; } .login-form-input-error { @@ -51,37 +54,73 @@ font-size: 1rem; } -.login-form-container { - display: block; - margin-top: 30px; /*modal distance from top of the screen*/ - box-sizing: border-box; - width: 50%; +.login-page-content-container { + display: flex; + flex-direction: column; + width: 80%; + z-index: 5; } @media only screen and (max-width: 1024px) { - .login-form-container { + .login-page-content-container { text-align: center; + align-items: center; width: 100%; } } +.login-page-images { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.login-page-art { + position: absolute; + top: 120px; + right: 30px; + width: 1000px; + max-height: 500px; + z-index: 1; + @media only screen and (max-width: 1024px) { + position: relative; + top: -30px; + right: 0px; + max-width: 300px; + max-height: 200px; + } +} +.background-svg { + max-width: 1084px; + height: 100%; + position: absolute; + top: 0; + right: 0; + + @media only screen and (max-width: 1024px) { + display: none; + } +} + .login-form-input { font-size: 1.5em; line-height: normal; - padding: 0.25rem 0.1rem 0px 0.1rem; + padding: 0.2rem 0.5rem; border: 0px; + border-radius: 8px; background-color: $theme-dark; border-top: 0px; border-left: 0px; border-right: 0px; - border-bottom: 2px solid $off-white; -webkit-appearance: none; -moz-appearance: none; appearance: none; outline: 0; width: 50%; min-width: 300px; - max-width: 500px; + max-width: 400px; margin: 0 2px 1rem; /*last value controls how far the next input starts*/ font-variant-ligatures: none; -webkit-transition: box-shadow 70ms ease-out, border-color 70ms ease-out; @@ -102,18 +141,6 @@ color: #c0e2fd; } -.login-form-button { - background-color: $theme-light; - border-color: $theme-light; - text-align: left; -} - -.login-form-button:hover, .login-form-button:active { - color: $theme-light !important; - border-color: $off-white !important; - background-color: $off-white !important; -} - .login-form-link { color: inherit; text-decoration: none; @@ -124,3 +151,40 @@ color: inherit; text-decoration: underline; } + +.bottom-right-toggle{ + position: absolute; + top: 20px; + right: 20px; + @media only screen and (max-width: 1024px) { + display: none; + } +} + +.beating-heart{ + color: red; + animation: heartbeat 2s infinite; +} + +/* The keyframes + class for the heartbeat animation I use across the site. */ +@keyframes heartbeat { + 0% { + transform: scale(0.75); + } + + 25% { + transform: scale(1); + } + + 50% { + transform: scale(0.75); + } + + 75% { + transform: scale(1); + } + + 100% { + transform: scale(0.75); + } +} \ No newline at end of file diff --git a/src/styles/global.scss b/src/styles/global.scss index 5c7e29e5..13782147 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -23,4 +23,16 @@ h1, h2, h3, h4, h5, h6 { .force-no-wrap { white-space: nowrap; +} + +.teachla-green{ + color: #92D32B; +} + +.black-divider{ + border:0; + border-top: 1px solid $theme-dark; + margin:0; + height:1px; + margin-top: 1em; } \ No newline at end of file