From 12eaeadf12d4b61b9843fa65679ffae41bc78239 Mon Sep 17 00:00:00 2001
From: L4168 <L4168@student.jamk.fi>
Date: Sun, 2 Jun 2019 09:37:45 +0300
Subject: [PATCH] Added login-demo with Dockerfile

---
 .dockerignore                  |   2 +
 Dockerfile                     |  14 ++++
 package-lock.json              |  66 ++++++++++++++++
 package.json                   |   4 +-
 src/App.css                    | 137 ++++++++++++++++++++++++++++++++-
 src/App.js                     |  68 +++-------------
 src/LoginForm.js               | 100 ------------------------
 src/components/Header.js       | 100 ++++++++++++++++++++++++
 src/components/LoginForm.js    | 117 ++++++++++++++++++++++++++++
 src/components/RegisterForm.js | 133 ++++++++++++++++++++++++++++++++
 src/components/UserMap.js      |  34 ++++----
 src/index.css                  |   6 +-
 src/index.js                   |   8 +-
 13 files changed, 603 insertions(+), 186 deletions(-)
 create mode 100644 .dockerignore
 create mode 100644 Dockerfile
 delete mode 100644 src/LoginForm.js
 create mode 100644 src/components/Header.js
 create mode 100644 src/components/LoginForm.js
 create mode 100644 src/components/RegisterForm.js

diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..5171c54
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+node_modules
+npm-debug.log
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..3ef2135
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+FROM node:10.15.3-alpine as build
+WORKDIR /usr/src/app
+ENV PATH /usr/src/app/node_modules/.bin:$PATH
+COPY package*.json ./
+RUN npm install
+RUN npm install react-scripts@3.0.1 -g
+COPY . .
+RUN npm run build
+
+# production environment
+FROM nginx:1.16.0-alpine
+COPY --from=build /usr/src/app/build /usr/share/nginx/html
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/package-lock.json b/package-lock.json
index e4b02e8..6139953 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1310,6 +1310,20 @@
         "@babel/types": "^7.3.0"
       }
     },
+    "@types/cookie": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
+      "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+    },
+    "@types/hoist-non-react-statics": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+      "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+      "requires": {
+        "@types/react": "*",
+        "hoist-non-react-statics": "^3.3.0"
+      }
+    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
@@ -1337,11 +1351,30 @@
       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.3.tgz",
       "integrity": "sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw=="
     },
+    "@types/object-assign": {
+      "version": "4.0.30",
+      "resolved": "https://registry.npmjs.org/@types/object-assign/-/object-assign-4.0.30.tgz",
+      "integrity": "sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI="
+    },
+    "@types/prop-types": {
+      "version": "15.7.1",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz",
+      "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg=="
+    },
     "@types/q": {
       "version": "1.5.2",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
       "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
     },
+    "@types/react": {
+      "version": "16.8.19",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.19.tgz",
+      "integrity": "sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ==",
+      "requires": {
+        "@types/prop-types": "*",
+        "csstype": "^2.2.0"
+      }
+    },
     "@types/stack-utils": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -3881,6 +3914,11 @@
         "cssom": "0.3.x"
       }
     },
+    "csstype": {
+      "version": "2.6.5",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz",
+      "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA=="
+    },
     "cyclist": {
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
@@ -9994,6 +10032,16 @@
         "whatwg-fetch": "3.0.0"
       }
     },
+    "react-cookie": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.0.tgz",
+      "integrity": "sha512-om/HB4MBHt4k+moR8Mb5h1kmKxcmOxK2U6aaQZ8Y+f+igICcE5bpng7yCiAo3kKN0btFpzvQ70XnpONOC0xkdA==",
+      "requires": {
+        "@types/hoist-non-react-statics": "^3.0.1",
+        "hoist-non-react-statics": "^3.0.0",
+        "universal-cookie": "^4.0.0"
+      }
+    },
     "react-dev-utils": {
       "version": "9.0.1",
       "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.0.1.tgz",
@@ -11881,6 +11929,24 @@
       "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz",
       "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ=="
     },
+    "universal-cookie": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.0.tgz",
+      "integrity": "sha512-6JVx+3oGPjslGqFhQ8YSIBHmYTx8HbyAEH++2/b6SKNXsbsdQ7lU7wRG2bYcRB5JVCz8GYgQ+Ixew91hn3Dy9w==",
+      "requires": {
+        "@types/cookie": "^0.3.1",
+        "@types/object-assign": "^4.0.30",
+        "cookie": "^0.3.1",
+        "object-assign": "^4.1.0"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+          "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+        }
+      }
+    },
     "universalify": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
diff --git a/package.json b/package.json
index ec811f0..fd4f1ef 100644
--- a/package.json
+++ b/package.json
@@ -5,9 +5,11 @@
   "dependencies": {
     "leaflet": "^1.5.1",
     "react": "^16.8.6",
+    "react-cookie": "^4.0.0",
     "react-dom": "^16.8.6",
     "react-leaflet": "^2.3.0",
-    "react-scripts": "3.0.1"
+    "react-scripts": "3.0.1",
+    "universal-cookie": "^4.0.0"
   },
   "scripts": {
     "start": "react-scripts start",
diff --git a/src/App.css b/src/App.css
index eda5bc7..8541116 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,4 +1,139 @@
+body {
+  margin: 0;
+  padding: 0;
+}
+
+.hidden {
+  display: none;
+}
+
+/* UserMap */
 .map {
+  position: absolute;
+  margin-top: 50px;
+  height: 95vh;
+  width: 100vw;
+}
+
+/* Header */
+div.header {
+  position: absolute;
+  top: 0;
+  height: 50px;
+  width: 100%;
+  background: white;
+  z-index: 1000;
+}
+
+div.header button {
+  transition-property: background-color;
+  transition-duration: 0.5s;
+  transition-timing-function: ease;
+  background-color: #279fd9;
+  color: white;
+  font-weight: bold;
+  border: 0px;
+  padding: 0.5em;
+  margin: 0em 1em;
+  width: 10%;
+  height: 100%;
+  font-size: 18px;
+  float: right;
+}
+
+div.header button:hover {
+  background-color: darkblue;
+  cursor: pointer;
+}
+
+/* Login&RegisterForm */
+div.fade-main {
+  position: absolute;
+  top: 0;
+  z-index: 1000;
   height: 100vh;
   width: 100vw;
-}
\ No newline at end of file
+  margin: auto;
+  text-align: center;
+  background-color: rgba(0, 0, 0, 0.85);
+}
+
+div.sticky {
+    position: fixed;
+    top: 0px;
+    right: 0px;
+    height: 100px;
+    width: 100px;
+    margin-right: 150px;
+}
+
+.close {
+    position: fixed;
+    color: #f1f1f1;
+    height: 100px;
+    font-size: 100px;
+    font-weight: bold;
+    transition: transform 0.4s ease-in-out;
+    line-height: 70%;
+}
+
+.close:hover,
+.close:focus {
+  color: #bbb;
+  text-decoration: none;
+  cursor: pointer;
+  transform: rotateZ(90deg);
+}
+
+div.login {
+  height: 60vh;
+  width: 40vw;
+  margin: auto;
+  margin-top: 5%;
+  padding-top: 25px;
+}
+
+div.login h1 {
+  color: rgb(0, 200, 255);
+  margin-bottom: 0.5em;
+  font-size: 3em;
+}
+
+div.login h2 {
+  color: rgb(255, 0, 0);
+  margin-top: 2em;
+  font-size: 1.5em;
+}
+
+div.login input {
+  background: none;
+  border: none;
+  outline: none;
+  width: 60%;
+  padding: 12px 20px;
+  margin-bottom: 1em;
+  display: inline-block;
+  box-sizing: border-box;
+  font-size: 180%;
+  color: white;
+  border-bottom: 3px solid rgb(0, 200, 255);
+}
+
+div.login button {
+  transition-property: background-color;
+  transition-duration: 0.5s;
+  transition-timing-function: ease;
+  background-color: #279fd9;
+  color: white;
+  font-weight: bold;
+  border: 0px;
+  padding: 0.5em;
+  margin: 0em 2em;
+  width: 20%;
+  font-size: 20px;
+}
+
+div.login button:hover {
+  background-color: darkblue;
+  cursor: pointer;
+}
diff --git a/src/App.js b/src/App.js
index 02a6ba3..4ee8619 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,9 +1,8 @@
 import React, { Component } from 'react';
-import styles from './App.css';
-
-import UserMap from './components/UserMap.js'
-import {LoginForm} from './LoginForm';
 
+import styles from './App.css';
+import UserMap from './components/UserMap';
+import Header from './components/Header';
 
 class App extends Component {
   constructor() {
@@ -12,68 +11,19 @@ class App extends Component {
     this.state = {
       lat: 62.2416479,
       lng: 25.7597186,
-      zoom: 13,
-      logged: false,
-      name: ''
-    }
-
-    this.login = this.login.bind(this);
-  }
-
-  componentDidMount(){
-    // log in if cookie with username is found
-    let name = getCookie('username');
-    if(name !== ''){
-      this.login({
-        logged: true,
-        name: name
-      });
-    }
+      zoom: 13
+    };
   }
 
-  login(loginInfo){
-    if(loginInfo.logged !== this.state.logged){
-      this.setState({
-        logged: loginInfo.logged,
-        name: loginInfo.name
-      });
-      document.cookie = 'username='+loginInfo.name+';path=/;';
-    }
-    else{
-      console.log('Wrong info');
-    }
-  }
-
-  render(){
-  const initialPosition = [this.state.lat, this.state.lng];
+  render() {
+    const initialPosition = [this.state.lat, this.state.lng];
     return (
       <div>
-        <p>{this.state.logged ? 'Logged in: ' + this.state.name : ''}</p>
-        {!this.state.logged && <LoginForm onSubmit={this.login}/>}
-        {this.state.logged && <button onClick={() => this.login({
-          logged: false,
-          name: ''
-        })}>Logout</button>}
-        <UserMap position={initialPosition} zoom={this.state.zoom}/>
+        <UserMap position={initialPosition} zoom={this.state.zoom} />,
+        <Header />
       </div>
     );
   }
 }
 
-function getCookie(cname){
-  let name = cname + "=";
-  let decodedCookie = decodeURIComponent(document.cookie);
-  let ca = decodedCookie.split(";");
-  for (let i = 0; i < ca.length; i++) {
-    let c = ca[i];
-    while (c.charAt(0) === " ") {
-      c = c.substring(1);
-    }
-    if(c.indexOf(name) === 0){
-      return c.substring(name.length, c.length);
-    }
-  }
-  return "";
-}
-
 export default App;
diff --git a/src/LoginForm.js b/src/LoginForm.js
deleted file mode 100644
index 05962f7..0000000
--- a/src/LoginForm.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import React from "react";
-
-//TODO: remove this when not needed
-const user = {
-    name: "user",
-    password: "password"
-}
-
-const style = {
-    backgroundColor: 'red',
-    maxHeight: '100%',
-    maxWidth: '350px'
-}
-
-export class LoginForm extends React.Component{
-    constructor(props){
-        super(props);
-        this.handleLogin = this.handleLogin.bind(this);
-    }
-
-    handleLogin(e){
-        const name = e.target.elements.name.value;
-        const password = e.target.elements.password.value;
-
-        e.preventDefault();
-
-        // Don't send user info to the server if used hardcoded info
-        if(name === user.name && password === user.password){
-            this.props.onSubmit({
-                logged: true,
-                name: name
-            });
-        }else{
-            this.props.onSubmit({
-                logged: false,
-                name: ""
-            });
-
-            
-            // This did NOT work !!!!
-
-            // (async() =>{
-            //     const asd = await fetch('http://172.20.2.143:3000/user/login',{
-            //         method: 'POST',
-            //         headers: {
-            //             'Accept': 'application/json',
-            //             'Content-Type': 'application/json',
-            //         },
-            //         credentials: 'same-origin',
-            //         body: JSON.stringify({
-            //             name: name,
-            //             password: password
-            //         })
-            //     });
-            //     const content = await asd;
-            //     console.log(content);
-            // })();
-
-            // This worked :)
-            // Send login info to the server
-            fetch("http://172.20.2.143:3000/user/login",{
-                method: 'POST',
-                headers: {
-                    'Accept': 'application/json',
-                    'Content-Type': 'application/json',
-                },
-                body: JSON.stringify({
-                    name: name,
-                    password: password
-                })
-            })
-            .then(res => res.json())
-            .then((result) => {
-                    console.log(result);
-                },
-                // Note: it's important to handle errors here
-                // instead of a catch() block so that we don't swallow
-                // exceptions from actual bugs in components.
-                (error) => {
-                    console.log(error);
-                }
-            )
-        }
-    }
-    
-    render(){
-        return(
-            <form onSubmit={this.handleLogin} style={style}>
-                <label>LOGIN</label>
-                <br></br>
-                <label>Name:</label>
-                <input name="name"/>
-                <br></br>
-                <label>Password:</label>
-                <input type="password" name="password"/>
-                <button type="submit">Submit</button>
-            </form>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/components/Header.js b/src/components/Header.js
new file mode 100644
index 0000000..4406f82
--- /dev/null
+++ b/src/components/Header.js
@@ -0,0 +1,100 @@
+import React from 'react';
+
+import LoginForm from './LoginForm';
+import RegisterForm from './RegisterForm';
+
+class Header extends React.Component {
+  state = {
+    login: false,
+    register: false,
+    username: null,
+    token: null
+  };
+
+  // toggles the login/register view
+  toggleView = view => {
+    this.setState(prevState => {
+      return {
+        [view]: view === 'login' ? !prevState.login : !prevState.register
+      };
+    });
+  };
+
+  handleState = data => {
+    sessionStorage.setItem('name', data.name);
+    sessionStorage.setItem('token', data.token);
+    this.setState({ username: data.name, token: data.token });
+  };
+
+  handleLogout = () => {
+    this.setState({ username: null, token: null });
+    sessionStorage.removeItem('token');
+  };
+
+  // verifies the token (if it exists) on element mount with backend server
+  componentDidMount() {
+    let token = sessionStorage.getItem('token');
+    if (token) {
+      fetch('http://localhost:5000/user/verify', {
+        headers: {
+          Authorization: 'Bearer ' + token
+        }
+      })
+        .then(res => res.json())
+        .then(
+          result => {
+            // if token is still valid, login user
+            if (result === true) {
+              this.setState({
+                username: sessionStorage.getItem('name'),
+                token: token
+              });
+              // logout user if token has expired / is invalid
+            } else {
+              this.handleLogout();
+            }
+          },
+          error => {
+            console.log(error);
+          }
+        );
+    }
+  }
+
+  render() {
+    return (
+      <div>
+        <div className='header'>
+          {!this.state.username && (
+            <button onClick={() => this.toggleView('register')}>
+              register
+            </button>
+          )}
+          {!this.state.username && (
+            <button onClick={() => this.toggleView('login')}>login</button>
+          )}
+          {this.state.username && (
+            <button onClick={this.handleLogout}>logout</button>
+          )}
+          {this.state.username && <button>{this.state.username}</button>}
+        </div>
+        {this.state.register && (
+          <RegisterForm
+            view='register'
+            handleState={this.handleState}
+            toggleView={this.toggleView}
+          />
+        )}
+        {this.state.login && (
+          <LoginForm
+            view='login'
+            handleState={this.handleState}
+            toggleView={this.toggleView}
+          />
+        )}
+      </div>
+    );
+  }
+}
+
+export default Header;
diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js
new file mode 100644
index 0000000..7893bb8
--- /dev/null
+++ b/src/components/LoginForm.js
@@ -0,0 +1,117 @@
+import React from 'react';
+
+export class LoginForm extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      errorMsg: '',
+      username: '',
+      password: ''
+    };
+  }
+
+  handleError = error => {
+    this.setState({ errorMsg: error });
+  };
+
+  handleChange = e => {
+    const { name, value } = e.target;
+    this.setState({ [name]: value });
+  };
+
+  // show/hide this form
+  handleView = e => {
+    this.props.toggleView(this.props.view);
+  };
+
+  // remove login view with ESC
+  handleEsc = e => {
+    if (e.keyCode === 27) {
+      this.handleView();
+    }
+  };
+
+  handleLogin = e => {
+    const name = this.state.username;
+    const password = this.state.password;
+    e.preventDefault();
+
+    // Send login info to the server
+    fetch('http://localhost:5000/user/login', {
+      method: 'POST',
+      headers: {
+        Accept: 'application/json',
+        'Content-Type': 'application/json'
+      },
+      body: JSON.stringify({
+        name: name,
+        password: password
+      })
+    })
+      .then(res => res.json())
+      .then(
+        result => {
+          if (result.name) {
+            this.props.handleState(result);
+            this.handleView();
+          } else {
+            this.handleError(result.errorResponse.message);
+          }
+        },
+        // Note: it's important to handle errors here
+        // instead of a catch() block so that we don't swallow
+        // exceptions from actual bugs in components.
+        error => {
+          console.log(error);
+        }
+      );
+  };
+
+  componentDidMount() {
+    document.addEventListener('keyup', this.handleEsc);
+  }
+
+  componentWillUnmount() {
+    document.removeEventListener('keyup', this.handleEsc);
+  }
+
+  render() {
+    return (
+      <div className='fade-main'>
+        <div className='sticky'>
+          <span className='close' onClick={this.handleView}>
+            ×
+          </span>
+        </div>
+        <div className='login'>
+          <form onSubmit={this.handleLogin}>
+            <h1>demo login</h1>
+            <br />
+            <input
+              placeholder='Enter Username'
+              name='username'
+              value={this.state.username}
+              onChange={this.handleChange}
+              required
+            />
+            <br />
+            <input
+              placeholder='Enter password'
+              type='password'
+              name='password'
+              value={this.state.password}
+              onChange={this.handleChange}
+              required
+            />
+            <br />
+            <button type='submit'>login</button>
+            <h2>{this.state.errorMsg}</h2>
+          </form>
+        </div>
+      </div>
+    );
+  }
+}
+
+export default LoginForm;
diff --git a/src/components/RegisterForm.js b/src/components/RegisterForm.js
new file mode 100644
index 0000000..f0ead02
--- /dev/null
+++ b/src/components/RegisterForm.js
@@ -0,0 +1,133 @@
+import React from 'react';
+
+export class RegisterForm extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      errorMsg: '',
+      username: '',
+      password: '',
+      password2: ''
+    };
+  }
+
+  // shows error messages associated with registering
+  handleError = error => {
+    this.setState({ errorMsg: error });
+  };
+
+  // updates state with input values
+  handleChange = e => {
+    const { name, value } = e.target;
+    this.setState({ [name]: value });
+  };
+
+  // show/hide this form
+  handleView = e => {
+    this.props.toggleView(this.props.view);
+  };
+
+  // remove register view with ESC
+  handleEsc = e => {
+    if (e.keyCode === 27) {
+      this.handleView();
+    }
+  };
+
+  handleRegister = e => {
+    const name = this.state.username;
+    const password = this.state.password;
+    e.preventDefault();
+
+    if (this.state.password !== this.state.password2) {
+      this.handleError('Passwords do not match');
+    } else {
+      // Send register info to the server
+      fetch('http://localhost:5000/user/register', {
+        method: 'POST',
+        headers: {
+          Accept: 'application/json',
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify({
+          name: name,
+          password: password
+        })
+      })
+        .then(res => res.json())
+        .then(
+          result => {
+            if (result.name) {
+              this.props.handleState(result);
+              this.handleView();
+            } else {
+              this.handleError(result.errorResponse.message);
+            }
+          },
+          // Note: it's important to handle errors here
+          // instead of a catch() block so that we don't swallow
+          // exceptions from actual bugs in components.
+          error => {
+            console.log(error);
+          }
+        );
+    }
+  };
+
+  componentDidMount() {
+    document.addEventListener('keyup', this.handleEsc);
+  }
+
+  componentWillUnmount() {
+    document.removeEventListener('keyup', this.handleEsc);
+  }
+
+  render() {
+    return (
+      <div className='fade-main'>
+        <div className='sticky'>
+          <span className='close' onClick={this.handleView}>
+            ×
+          </span>
+        </div>
+        <div className='login'>
+          <form onSubmit={this.handleRegister}>
+            <h1>register new user</h1>
+            <br />
+            <input
+              placeholder='Enter Username'
+              name='username'
+              value={this.state.username}
+              onChange={this.handleChange}
+              required
+            />
+            <br />
+            <input
+              placeholder='Enter password'
+              type='password'
+              name='password'
+              value={this.state.password}
+              onChange={this.handleChange}
+              required
+            />
+            <br />
+            <input
+              placeholder='Verify password'
+              type='password'
+              name='password2'
+              value={this.state.password2}
+              onChange={this.handleChange}
+              required
+            />
+            <br />
+            <button type='submit'>register</button>
+            <h2>{this.state.errorMsg}</h2>
+          </form>
+        </div>
+      </div>
+    );
+  }
+}
+
+export default RegisterForm;
diff --git a/src/components/UserMap.js b/src/components/UserMap.js
index 1ac5bcb..3f7e559 100644
--- a/src/components/UserMap.js
+++ b/src/components/UserMap.js
@@ -1,22 +1,22 @@
-import React, {Component} from 'react';
-import { Map, TileLayer, Marker, Popup } from 'react-leaflet'
+import React, { Component } from 'react';
+import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
 
-class UserMap extends Component{  
-  render(){  
+class UserMap extends Component {
+  render() {
     return (
-    <Map className="map" center={this.props.position} zoom={this.props.zoom}>
-      <TileLayer
-        attribution='Maanmittauslaitoksen kartta'
-        url=" https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg"
-      />
-      <Marker position={this.props.position}>
-        <Popup>
-          Se on perjantai, my dudes <br /> 
-        </Popup>
-      </Marker>
-    </Map>)
+      <Map className='map' center={this.props.position} zoom={this.props.zoom}>
+        <TileLayer
+          attribution='Maanmittauslaitoksen kartta'
+          url=' https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg'
+        />
+        <Marker position={this.props.position}>
+          <Popup>
+            Se on perjantai, my dudes <br />
+          </Popup>
+        </Marker>
+      </Map>
+    );
   }
 }
 
-
-export default UserMap;
\ No newline at end of file
+export default UserMap;
diff --git a/src/index.css b/src/index.css
index 4a1df4d..ec2585e 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +1,13 @@
 body {
   margin: 0;
-  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
-    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
     sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 }
 
 code {
-  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
     monospace;
 }
diff --git a/src/index.js b/src/index.js
index b22efa5..0b8e652 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,13 +11,11 @@ import * as serviceWorker from './serviceWorker';
 // make the default marker work with react (dunno if this is a weird hack)
 delete L.Icon.Default.prototype._getIconUrl;
 L.Icon.Default.mergeOptions({
-    iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
-    iconUrl: require('leaflet/dist/images/marker-icon.png'),
-    shadowUrl: require('leaflet/dist/images/marker-shadow.png')
+  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
+  iconUrl: require('leaflet/dist/images/marker-icon.png'),
+  shadowUrl: require('leaflet/dist/images/marker-shadow.png')
 });
 
-
-
 ReactDOM.render(<App />, document.getElementById('root'));
 
 // If you want your app to work offline and load faster, you can change
-- 
GitLab