diff --git a/ide/static/css/signup_style.css b/ide/static/css/signup_style.css
new file mode 100644
index 000000000..1a88371fc
--- /dev/null
+++ b/ide/static/css/signup_style.css
@@ -0,0 +1,126 @@
+.signup-container {
+ background-size: cover;
+ background: #f4f6ff;
+ height: 100%;
+ width: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+}
+
+.card {
+ background: #fff;
+ width: 40em;
+ height: 44em;
+ border-radius: 5px;
+ margin-top: 5em;
+ box-shadow: 1px 1px 132px -20px rgba(205, 207, 210, 0.8);
+}
+
+.card-top {
+ width: 100%;
+ height: 11em;
+ border-bottom: 1px solid #eee;
+}
+
+.card-top-left {
+ float: left;
+ border-right: 1px solid #eee;
+ width: 50%;
+}
+
+.card-top-left img {
+ width: 50%;
+ margin: 2.95em 0 2.95em 0;
+}
+
+.card-top-right h1{
+ color: #455062;
+ font-size: 1.4em;
+ margin-top: 0.8em;
+}
+
+.card-top-right img {
+ width: 15%;
+ margin-top: 1.4em;
+}
+
+.form {
+ margin-top: 3em;
+}
+
+.input {
+ border: 2px solid #eee;
+ border-radius: 10px;
+ width: 300px;
+ margin: 1em;
+ transition: all .3s ease;
+}
+
+.input img{
+ width: 15%;
+ margin-left: -0.4em;
+ transition: all .3s ease;
+}
+
+.field {
+ margin: 0;
+ border: 0;
+ outline: 0;
+ width: 245px;
+ position: relative;
+ top: 0.1em;
+ padding: 0.5em 0 0.5em 0;
+ color: #455060;
+ font-size: 1.1em;
+}
+
+.field::placeholder {
+ color: #455060;
+}
+
+.active-input {
+ border-color: #455060;
+ transition: all .3s ease;
+}
+
+.wrong-input {
+ border-color: #eb4d4b;
+ transition: all .3s ease;
+}
+
+.error-message {
+ color: #eb4d4b;
+ margin: 1.1em 0 0.2em 0;
+ opacity: 0;
+ transition: all .3s ease;
+}
+
+.show {
+ opacity: 1 !important;
+ transition: all .3s ease;
+}
+
+input[type=submit] {
+ background: #455062;
+ border: 0;
+ outline: 0;
+ box-shadow: none;
+ color: #fff;
+ margin: 0.4em;
+ text-transform: uppercase;
+ font-weight: bold;
+ padding: 1.2em 0 1.2em 0;
+ width: 300px;
+ border-radius: 10px;
+}
+
+.login-link {
+ margin: 2em;
+ color: #455062;
+ display: inline-block;
+}
+
+.login-link:hover {
+ color: #2c323d;
+}
diff --git a/ide/static/img/account.png b/ide/static/img/account.png
new file mode 100644
index 000000000..b0c5d31cd
Binary files /dev/null and b/ide/static/img/account.png differ
diff --git a/ide/static/img/email_icon.png b/ide/static/img/email_icon.png
new file mode 100644
index 000000000..6b0f97a68
Binary files /dev/null and b/ide/static/img/email_icon.png differ
diff --git a/ide/static/img/email_icon_active.png b/ide/static/img/email_icon_active.png
new file mode 100644
index 000000000..2557accaf
Binary files /dev/null and b/ide/static/img/email_icon_active.png differ
diff --git a/ide/static/img/email_icon_wrong.png b/ide/static/img/email_icon_wrong.png
new file mode 100644
index 000000000..741887386
Binary files /dev/null and b/ide/static/img/email_icon_wrong.png differ
diff --git a/ide/static/img/logo_without_bg.png b/ide/static/img/logo_without_bg.png
new file mode 100644
index 000000000..fba352238
Binary files /dev/null and b/ide/static/img/logo_without_bg.png differ
diff --git a/ide/static/img/password_icon.png b/ide/static/img/password_icon.png
new file mode 100644
index 000000000..3e3eab72e
Binary files /dev/null and b/ide/static/img/password_icon.png differ
diff --git a/ide/static/img/password_icon_active.png b/ide/static/img/password_icon_active.png
new file mode 100644
index 000000000..d1f8cc3ef
Binary files /dev/null and b/ide/static/img/password_icon_active.png differ
diff --git a/ide/static/img/password_icon_wrong.png b/ide/static/img/password_icon_wrong.png
new file mode 100644
index 000000000..eeaf24585
Binary files /dev/null and b/ide/static/img/password_icon_wrong.png differ
diff --git a/ide/static/img/username_icon.png b/ide/static/img/username_icon.png
new file mode 100644
index 000000000..4e3531c42
Binary files /dev/null and b/ide/static/img/username_icon.png differ
diff --git a/ide/static/img/username_icon_active.png b/ide/static/img/username_icon_active.png
new file mode 100644
index 000000000..350080f4d
Binary files /dev/null and b/ide/static/img/username_icon_active.png differ
diff --git a/ide/static/img/username_icon_wrong.png b/ide/static/img/username_icon_wrong.png
new file mode 100644
index 000000000..e97e5e059
Binary files /dev/null and b/ide/static/img/username_icon_wrong.png differ
diff --git a/ide/static/js/index.js b/ide/static/js/index.js
index d387a3637..da50782c8 100644
--- a/ide/static/js/index.js
+++ b/ide/static/js/index.js
@@ -3,10 +3,12 @@ import React from 'react';
import { render } from 'react-dom';
import { Router, Route, hashHistory } from 'react-router';
import App from './app.js';
+import Signup from './signup.js';
import '../css/style.css';
render(
+
, document.getElementById('app')
);
diff --git a/ide/static/js/login.js b/ide/static/js/login.js
index a31214afa..e0af648c4 100644
--- a/ide/static/js/login.js
+++ b/ide/static/js/login.js
@@ -26,6 +26,11 @@ class Login extends React.Component {
this.setState({ loginState: false });
this.props.setUserId(null);
this.props.setUserName(null);
+ localStorage.removeItem('userID');
+ localStorage.removeItem('username');
+ if (localStorage.hasOwnProperty('email')) {
+ localStorage.removeItem('email');
+ }
}
}.bind(this),
error: function () {
@@ -68,6 +73,8 @@ class Login extends React.Component {
this.setState({ loginState: response.result });
this.props.setUserId(response.user_id);
this.props.setUserName(response.username);
+ localStorage.setItem("userID",response.user_id);
+ localStorage.setItem("username",response.username);
if (showNotification) {
$('#successful-login-notification')[0].style.display = 'block';
diff --git a/ide/static/js/signup.js b/ide/static/js/signup.js
new file mode 100644
index 000000000..ca171edcd
--- /dev/null
+++ b/ide/static/js/signup.js
@@ -0,0 +1,295 @@
+import React from "react";
+import DjangoCSRFToken from "django-react-csrftoken";
+import $ from "jquery";
+
+class Signup extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ error_username: 0,
+ error_email: 0,
+ error_password: 0,
+ error_confirm_password: 0
+ };
+ this.submitForm = this.submitForm.bind(this);
+ this.validateUsernameMaxLen = this.validateUsernameMaxLen.bind(this);
+ this.onClickUsernameInput = this.onClickUsernameInput.bind(this);
+ this.validateEmailFormat = this.validateEmailFormat.bind(this);
+ this.onClickEmailInput = this.onClickEmailInput.bind(this);
+ this.validatePasswordFormat = this.validatePasswordFormat.bind(this);
+ this.onClickPasswordInput = this.onClickPasswordInput.bind(this);
+ this.validateSamePassword = this.validateSamePassword.bind(this);
+ this.onClickConfirmPasswordInput = this.onClickConfirmPasswordInput.bind(this);
+ }
+
+ onClickUsernameInput() {
+ document.getElementById("username-input").className = "input active-input";
+ document.getElementById("username-icon").src = "static/img/username_icon_active.png";
+ }
+
+ validateUsernameMaxLen() {
+ let username = document.getElementById("username-value").value;
+ let username_input = document.getElementById("username-input");
+ let username_icon = document.getElementById("username-icon");
+ let error_message = document.getElementById("error-message");
+ username_input.className = "input";
+ if (username.length == "") {
+ username_input.className = "input wrong-input";
+ error_message.innerHTML = "The username can't be empty.";
+ error_message.className = "error-message show";
+ username_icon.src = "static/img/username_icon_wrong.png";
+ this.setState({ error_username: 1 });
+ } else if (username.length > 150) {
+ username_input.className = "input wrong-input";
+ error_message.innerHTML = "The username can't be more than 150 characters.";
+ error_message.className = "error-message show";
+ username_icon.src = "static/img/username_icon_wrong.png";
+ this.setState({ error_username: 1 });
+ } else {
+ username_input.className = "input";
+ error_message.innerHTML = "";
+ error_message.className = "error-message";
+ this.setState({ error_username: 0 });
+ }
+ }
+
+ onClickEmailInput() {
+ document.getElementById("email-input").className = "input active-input";
+ document.getElementById("email-icon").src =
+ "static/img/email_icon_active.png";
+ }
+
+ validateEmailFormat() {
+ let email = document.getElementById("email-value").value;
+ let email_input = document.getElementById("email-input");
+ let email_icon = document.getElementById("email-icon");
+ let error_message = document.getElementById("error-message");
+ let symbol = email.indexOf("@");
+ let next = email.charAt(symbol + 1);
+ email_input.className = "input";
+ if (email.length == "") {
+ email_input.className = "input wrong-input";
+ error_message.innerHTML = "The email can't be empty.";
+ error_message.className = "error-message show";
+ email_icon.src = "static/img/email_icon_wrong.png";
+ this.setState({ error_email: 1 });
+ } else if (!email.includes("@")) {
+ email_input.className = "input wrong-input";
+ error_message.innerHTML = "The email should contain @ symbol.";
+ error_message.className = "error-message show";
+ email_icon.src = "static/img/email_icon_wrong.png";
+ this.setState({ error_email: 1 });
+ } else if (next == "") {
+ email_input.className = "input wrong-input";
+ error_message.innerHTML = "The email should contain the domain.";
+ error_message.className = "error-message show";
+ email_icon.src = "static/img/email_icon_wrong.png";
+ this.setState({ error_email: 1 });
+ } else {
+ email_input.className = "input";
+ error_message.innerHTML = "";
+ error_message.className = "error-message";
+ this.setState({ error_email: 0 });
+ }
+ }
+
+ onClickPasswordInput() {
+ document.getElementById("password-input").className = "input active-input";
+ document.getElementById("password-icon").src =
+ "static/img/password_icon_active.png";
+ }
+
+ validatePasswordFormat() {
+ let password = document.getElementById("password-value").value;
+ let password_input = document.getElementById("password-input");
+ let password_icon = document.getElementById("password-icon");
+ let error_message = document.getElementById("error-message");
+ password_input.className = "input";
+ if (password.length == "") {
+ password_input.className = "input wrong-input";
+ error_message.innerHTML = "The password can't be empty.";
+ error_message.className = "error-message show";
+ password_icon.src = "static/img/password_icon_wrong.png";
+ this.setState({ error_password: 1 });
+ } else if (password.length < 6) {
+ password_input.className = "input wrong-input";
+ error_message.innerHTML = "The password should be atleast 6 characters long.";
+ error_message.className = "error-message show";
+ password_icon.src = "static/img/password_icon_wrong.png";
+ this.setState({ error_password: 1 });
+ } else {
+ password_input.className = "input";
+ error_message.innerHTML = "";
+ error_message.className = "error-message";
+ this.setState({ error_password: 0 });
+ }
+ }
+
+ onClickConfirmPasswordInput() {
+ document.getElementById("confirm-password-input").className = "input active-input";
+ document.getElementById("confirm-password-icon").src = "static/img/password_icon_active.png";
+ }
+
+ validateSamePassword() {
+ let confirm_password = document.getElementById("confirm-password-value").value;
+ let password = document.getElementById("password-value").value;
+ let confirm_password_input = document.getElementById("confirm-password-input");
+ let confirm_password_icon = document.getElementById("confirm-password-icon");
+ let error_message = document.getElementById("error-message");
+ confirm_password_input.className = "input";
+ if (confirm_password != password) {
+ confirm_password_input.className = "input wrong-input";
+ error_message.innerHTML = "The passwords do not match.";
+ error_message.className = "error-message show";
+ confirm_password_icon.src = "static/img/password_icon_wrong.png";
+ this.setState({ error_confirm_password: 1 });
+ } else {
+ confirm_password_input.className = "input";
+ error_message.innerHTML = "";
+ error_message.className = "error-message";
+ this.setState({ error_confirm_password: 0 });
+ }
+ }
+
+ submitForm(e) {
+ e.preventDefault();
+ let username = document.getElementById("username-value").value;
+ let email = document.getElementById("email-value").value;
+ let password = document.getElementById("password-value").value;
+ password.replace(/[|&;$%@"<>()+,]/g, "");
+ let confirm_password = document.getElementById("confirm-password-value").value;
+ confirm_password.replace(/[|&;$%@"<>()+,]/g, "");
+ let cleaned_password = password.trim();
+ let cleaned_confirm_password = confirm_password.trim();
+ let csrfmiddlewaretoken = document.getElementsByName("csrfmiddlewaretoken")[0].value;
+ if (
+ this.state.error_username == 0 &&
+ this.state.error_email == 0 &&
+ this.state.error_password == 0 &&
+ this.state.error_confirm_password == 0
+ ) {
+ $.ajax({
+ method: "POST",
+ url: "/accounts/signup/",
+ data: {
+ csrfmiddlewaretoken: csrfmiddlewaretoken,
+ username: username,
+ email: email,
+ password1: cleaned_password,
+ password2: cleaned_confirm_password
+ },
+ success: function(result) {
+ if (result.location == "/") {
+ localStorage.setItem('email', email);
+ window.location = "/";
+ } else {
+ let error_message = document.getElementById("error-message");
+ error_message.innerHTML = "Network error";
+ error_message.className = "error-message show";
+ }
+ },
+ complete: function(xhr) {
+ if (xhr.status == 400) {
+ let error_message = document.getElementById("error-message");
+ error_message.innerHTML =
+ "An account with those details already exists.";
+ error_message.className = "error-message show";
+ }
+ }
+ });
+ } else {
+ let error_message = document.getElementById("error-message");
+ error_message.innerHTML = "Error";
+ error_message.className = "error-message show";
+ }
+ }
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default Signup;
diff --git a/ide/templates/index.html b/ide/templates/index.html
index 80ea64972..63bc0b032 100644
--- a/ide/templates/index.html
+++ b/ide/templates/index.html
@@ -13,6 +13,7 @@
rel="stylesheet">
+
diff --git a/package.json b/package.json
index 16e0bf723..c1b62e305 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"dependencies": {
"babel-polyfill": "^6.9.1",
"bootstrap": "^3.3.6",
+ "django-react-csrftoken": "^1.0.4",
"jquery": "^3.0.0",
"react": "^15.1.0",
"react-dom": "^15.1.0",