From 5b95994cacff214032284c9e6d7f9bc3b7f64d7f Mon Sep 17 00:00:00 2001
From: Bram Wiepjes <bramw@protonmail.com>
Date: Mon, 10 Jun 2019 16:58:28 +0200
Subject: [PATCH] created vue login and register pages with validation only

---
 .editorconfig                                |   2 +-
 web-frontend/.eslintrc.js                    |   1 +
 web-frontend/layouts/login.vue               |  10 ++
 web-frontend/nuxt.config.js                  |  12 +-
 web-frontend/package.json                    |   3 +-
 web-frontend/pages/login/index.vue           |  91 +++++++++++++
 web-frontend/pages/login/signup.vue          | 129 +++++++++++++++++++
 web-frontend/plugins/Vuelidate.js            |   4 +
 web-frontend/test/pages/index.spec.js        |   2 +-
 web-frontend/test/pages/login/index.spec.js  |   9 ++
 web-frontend/test/pages/login/signup.spec.js |   9 ++
 web-frontend/yarn.lock                       |  14 +-
 12 files changed, 267 insertions(+), 19 deletions(-)
 create mode 100644 web-frontend/layouts/login.vue
 create mode 100644 web-frontend/pages/login/index.vue
 create mode 100644 web-frontend/pages/login/signup.vue
 create mode 100644 web-frontend/plugins/Vuelidate.js
 create mode 100644 web-frontend/test/pages/login/index.spec.js
 create mode 100644 web-frontend/test/pages/login/signup.spec.js

diff --git a/.editorconfig b/.editorconfig
index 7a3d0e60d..5bd2bab6d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -11,7 +11,7 @@ insert_final_newline = true
 [Makefile]
 indent_style = tab
 
-[*.{js,yml,scss,json,eslintrc,stylelintrc,vue}]
+[*.{js,yml,scss,json,eslintrc,stylelintrc,vue,html}]
 indent_size = 2
 
 [*.md]
diff --git a/web-frontend/.eslintrc.js b/web-frontend/.eslintrc.js
index df2633137..17e51b0de 100644
--- a/web-frontend/.eslintrc.js
+++ b/web-frontend/.eslintrc.js
@@ -19,5 +19,6 @@ module.exports = {
   ],
   // add your custom rules here
   rules: {
+    'no-console': 0
   }
 }
diff --git a/web-frontend/layouts/login.vue b/web-frontend/layouts/login.vue
new file mode 100644
index 000000000..26a13a8d9
--- /dev/null
+++ b/web-frontend/layouts/login.vue
@@ -0,0 +1,10 @@
+<template>
+  <div>
+    <div class="box-page-header"></div>
+    <div class="box-page">
+      <div class="box login-page login-page-login">
+        <nuxt />
+      </div>
+    </div>
+  </div>
+</template>
diff --git a/web-frontend/nuxt.config.js b/web-frontend/nuxt.config.js
index 65051437a..d152bcad1 100644
--- a/web-frontend/nuxt.config.js
+++ b/web-frontend/nuxt.config.js
@@ -1,7 +1,5 @@
 import StyleLintPlugin from 'stylelint-webpack-plugin'
 
-import pkg from './package'
-
 export default {
   mode: 'universal',
 
@@ -9,13 +7,11 @@ export default {
    ** Headers of the page
    */
   head: {
-    title: pkg.name,
+    title: 'Baserow',
     meta: [
       { charset: 'utf-8' },
-      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
-      { hid: 'description', name: 'description', content: pkg.description }
-    ],
-    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
+      { name: 'viewport', content: 'width=device-width, initial-scale=1' }
+    ]
   },
 
   /*
@@ -31,7 +27,7 @@ export default {
   /*
    ** Plugins to load before mounting the App
    */
-  plugins: [],
+  plugins: [{ src: '@/plugins/Vuelidate.js' }],
 
   /*
    ** Nuxt.js modules
diff --git a/web-frontend/package.json b/web-frontend/package.json
index eb832cb88..424f49619 100644
--- a/web-frontend/package.json
+++ b/web-frontend/package.json
@@ -18,7 +18,8 @@
     "@nuxtjs/axios": "^5.3.6",
     "cross-env": "^5.2.0",
     "normalize-scss": "^7.0.1",
-    "nuxt": "^2.4.0"
+    "nuxt": "^2.4.0",
+    "vuelidate": "^0.7.4"
   },
   "devDependencies": {
     "@nuxtjs/eslint-config": "^0.0.1",
diff --git a/web-frontend/pages/login/index.vue b/web-frontend/pages/login/index.vue
new file mode 100644
index 000000000..db3c47e46
--- /dev/null
+++ b/web-frontend/pages/login/index.vue
@@ -0,0 +1,91 @@
+<template>
+  <div>
+    <h1 class="box-title">
+      <img src="@/static/img/logo.svg" alt="" />
+    </h1>
+    <form @submit.prevent="login">
+      <div class="control">
+        <label class="control-label">E-mail address</label>
+        <div class="control-elements">
+          <input
+            v-model="credentials.email"
+            :class="{ 'input-error': $v.credentials.email.$error }"
+            type="text"
+            class="input input-large"
+            @blur="$v.credentials.email.$touch()"
+          />
+          <div v-if="$v.credentials.email.$error" class="error">
+            Please enter a valid e-mail address.
+          </div>
+        </div>
+      </div>
+      <div class="control">
+        <label class="control-label">Password</label>
+        <div class="control-elements">
+          <input
+            v-model="credentials.password"
+            :class="{ 'input-error': $v.credentials.password.$error }"
+            type="password"
+            class="input input-large"
+            @blur="$v.credentials.password.$touch()"
+          />
+          <div v-if="$v.credentials.password.$error" class="error">
+            A password is required.
+          </div>
+        </div>
+      </div>
+      <div class="actions">
+        <ul class="action-links">
+          <li>
+            <nuxt-link :to="{ name: 'login-signup' }">
+              Sign up
+            </nuxt-link>
+          </li>
+        </ul>
+        <button
+          :class="{ 'button-loading': loading }"
+          class="button button-large"
+        >
+          Sign in
+          <i class="fas fa-lock-open"></i>
+        </button>
+      </div>
+    </form>
+  </div>
+</template>
+
+<script>
+import { required, email } from 'vuelidate/lib/validators'
+
+export default {
+  layout: 'login',
+  head() {
+    return {
+      title: 'Login'
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      credentials: {
+        email: '',
+        password: ''
+      }
+    }
+  },
+  validations: {
+    credentials: {
+      email: { required, email },
+      password: { required }
+    }
+  },
+  methods: {
+    login() {
+      this.$v.$touch()
+      if (!this.$v.$invalid) {
+        this.loading = true
+      }
+    }
+  }
+}
+</script>
diff --git a/web-frontend/pages/login/signup.vue b/web-frontend/pages/login/signup.vue
new file mode 100644
index 000000000..80fbe7d86
--- /dev/null
+++ b/web-frontend/pages/login/signup.vue
@@ -0,0 +1,129 @@
+<template>
+  <div>
+    <h1 class="box-title">Sign up</h1>
+    <form @submit.prevent="register">
+      <div class="control">
+        <label class="control-label">E-mail address</label>
+        <div class="control-elements">
+          <input
+            v-model="account.email"
+            :class="{ 'input-error': $v.account.email.$error }"
+            type="text"
+            class="input input-large"
+            @blur="$v.account.email.$touch()"
+          />
+          <div v-if="$v.account.email.$error" class="error">
+            Please enter a valid e-mail address.
+          </div>
+        </div>
+      </div>
+      <div class="control">
+        <label class="control-label">Your name</label>
+        <div class="control-elements">
+          <input
+            v-model="account.name"
+            :class="{ 'input-error': $v.account.name.$error }"
+            type="text"
+            class="input input-large"
+            @blur="$v.account.name.$touch()"
+          />
+          <div v-if="$v.account.name.$error" class="error">
+            A minimum of two characters is required here.
+          </div>
+        </div>
+      </div>
+      <div class="control">
+        <label class="control-label">Password</label>
+        <div class="control-elements">
+          <input
+            v-model="account.password"
+            :class="{ 'input-error': $v.account.password.$error }"
+            type="password"
+            class="input input-large"
+            @blur="$v.account.password.$touch()"
+          />
+          <div v-if="$v.account.password.$error" class="error">
+            A password is required.
+          </div>
+        </div>
+      </div>
+      <div class="control">
+        <label class="control-label">Repeat password</label>
+        <div class="control-elements">
+          <input
+            v-model="account.passwordConfirm"
+            :class="{ 'input-error': $v.account.passwordConfirm.$error }"
+            type="password"
+            class="input input-large"
+            @blur="$v.account.passwordConfirm.$touch()"
+          />
+          <div v-if="$v.account.passwordConfirm.$error" class="error">
+            This field must match your password field.
+          </div>
+        </div>
+      </div>
+      <div class="actions">
+        <ul class="action-links">
+          <li>
+            <nuxt-link :to="{ name: 'login' }">
+              <i class="fas fa-arrow-left"></i>
+              Back
+            </nuxt-link>
+          </li>
+        </ul>
+        <button
+          :class="{ 'button-loading': loading }"
+          class="button button-large"
+        >
+          Sign up
+          <i class="fas fa-user-plus"></i>
+        </button>
+      </div>
+    </form>
+  </div>
+</template>
+
+<script>
+import { required, email, sameAs, minLength } from 'vuelidate/lib/validators'
+
+export default {
+  layout: 'login',
+  head() {
+    return {
+      title: 'Create new account'
+    }
+  },
+  validations: {
+    account: {
+      email: { required, email },
+      name: {
+        required,
+        minLength: minLength(2)
+      },
+      password: { required },
+      passwordConfirm: {
+        sameAsPassword: sameAs('password')
+      }
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      account: {
+        email: '',
+        name: '',
+        password: '',
+        passwordConfirm: ''
+      }
+    }
+  },
+  methods: {
+    register() {
+      this.$v.$touch()
+      if (!this.$v.$invalid) {
+        this.loading = true
+      }
+    }
+  }
+}
+</script>
diff --git a/web-frontend/plugins/Vuelidate.js b/web-frontend/plugins/Vuelidate.js
new file mode 100644
index 000000000..bfbc65e98
--- /dev/null
+++ b/web-frontend/plugins/Vuelidate.js
@@ -0,0 +1,4 @@
+import Vue from 'vue'
+import Vuelidate from 'vuelidate'
+
+Vue.use(Vuelidate)
diff --git a/web-frontend/test/pages/index.spec.js b/web-frontend/test/pages/index.spec.js
index faad5805d..1b11c57cb 100644
--- a/web-frontend/test/pages/index.spec.js
+++ b/web-frontend/test/pages/index.spec.js
@@ -1,7 +1,7 @@
 import { mount } from '@vue/test-utils'
 import Index from '@/pages/index.vue'
 
-describe('Logo', () => {
+describe('Home', () => {
   test('is a Vue instance', () => {
     const wrapper = mount(Index)
     expect(wrapper.isVueInstance()).toBeTruthy()
diff --git a/web-frontend/test/pages/login/index.spec.js b/web-frontend/test/pages/login/index.spec.js
new file mode 100644
index 000000000..dd258ff8e
--- /dev/null
+++ b/web-frontend/test/pages/login/index.spec.js
@@ -0,0 +1,9 @@
+import { mount } from '@vue/test-utils'
+import Index from '@/pages/login/index.vue'
+
+describe('Login', () => {
+  test('is a Vue instance', () => {
+    const wrapper = mount(Index)
+    expect(wrapper.isVueInstance()).toBeTruthy()
+  })
+})
diff --git a/web-frontend/test/pages/login/signup.spec.js b/web-frontend/test/pages/login/signup.spec.js
new file mode 100644
index 000000000..3d21708d8
--- /dev/null
+++ b/web-frontend/test/pages/login/signup.spec.js
@@ -0,0 +1,9 @@
+import { mount } from '@vue/test-utils'
+import Signup from '@/pages/login/signup.vue'
+
+describe('Signup', () => {
+  test('is a Vue instance', () => {
+    const wrapper = mount(Signup)
+    expect(wrapper.isVueInstance()).toBeTruthy()
+  })
+})
diff --git a/web-frontend/yarn.lock b/web-frontend/yarn.lock
index 74b8b40fe..bca539b6f 100644
--- a/web-frontend/yarn.lock
+++ b/web-frontend/yarn.lock
@@ -4933,7 +4933,7 @@ html-webpack-plugin@^3.2.0:
     toposort "^1.0.0"
     util.promisify "1.0.0"
 
-htmlparser2@^3.10.0, htmlparser2@^3.3.0, htmlparser2@^3.9.1:
+htmlparser2@^3.10.0, htmlparser2@^3.3.0:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
   integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@@ -9724,13 +9724,6 @@ stylelint-config-standard@^18.2.0:
   dependencies:
     stylelint-config-recommended "^2.2.0"
 
-stylelint-processor-html@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/stylelint-processor-html/-/stylelint-processor-html-1.0.0.tgz#6892b6b2855a45f0291cd845191d6908130a2918"
-  integrity sha1-aJK2soVaRfApHNhFGR1pCBMKKRg=
-  dependencies:
-    htmlparser2 "^3.9.1"
-
 stylelint-webpack-plugin@^0.10.5:
   version "0.10.5"
   resolved "https://registry.yarnpkg.com/stylelint-webpack-plugin/-/stylelint-webpack-plugin-0.10.5.tgz#0b6e0d373ff5e03baa8197ebe0f2625981bd266b"
@@ -10640,6 +10633,11 @@ vue@^2.6.10:
   resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
   integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
 
+vuelidate@^0.7.4:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.4.tgz#5a0e54be09ac0192f1aa3387d74b92e0945bf8aa"
+  integrity sha512-QHZWYOL325Zo+2K7VBNEJTZ496Kd8Z31p85aQJFldKudUUGBmgw4zu4ghl4CyqPwjRCmqZ9lDdx4FSdMnu4fGg==
+
 vuex@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.1.tgz#0c264bfe30cdbccf96ab9db3177d211828a5910e"