Web auth pages redesign (#1532)

This commit is contained in:
Nikolay Yurchenko 2019-03-20 18:16:30 +02:00 committed by GitHub
parent 6bf46e80ee
commit 26da16be2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 796 additions and 461 deletions

View File

@ -13,7 +13,7 @@ export async function updateAccountRequest(user: User): Promise<RequestResponse<
data: {
firstName: '',
lastName: '',
email: ''
email: '',
}
};
@ -93,7 +93,7 @@ export async function changePasswordRequest(password: string, newPassword: strin
// Performs Create user graqhQL request.
// Throws an exception if error occurs
// Returns object with newly created user
export async function createUserRequest(user: User, password: string): Promise<RequestResponse<null>> {
export async function createUserRequest(user: User, password: string, secret: string): Promise<RequestResponse<null>> {
let result: RequestResponse<null> = {
errorMessage: '',
isSuccess: false,
@ -111,7 +111,8 @@ export async function createUserRequest(user: User, password: string): Promise<R
password: "${password}",
firstName: "${user.firstName}",
lastName: "${user.lastName}",
}
},
secret: "${secret}",
){email}
}`
),
@ -179,7 +180,7 @@ export async function getUserRequest(): Promise<RequestResponse<User>> {
data: {
firstName: '',
lastName: '',
email: ''
email: '',
}
};

View File

@ -5,15 +5,16 @@
<div class="input-wrap">
<div class="label-container">
<img v-if="error" src="../../../static/images/register/ErrorInfo.svg"/>
<h3 v-if="!error && label">{{label}}</h3>
<h3 class="label-container__error" v-if="error">{{error}}</h3>
<h3 v-if="!error && label" :style="style.labelStyle">{{label}}</h3>
<h3 class="label-container__error" v-if="error" :style="style.errorStyle">{{error}}</h3>
</div>
<input
v-bind:class="[error ? 'inputError' : null]"
@input="onInput"
:placeholder="this.$props.placeholder"
v-model="value"
v-bind:type="[isPassword ? passwordType : textType]"
:style="style"/>
:style="style.inputStyle"/>
<!--2 conditions of eye image (crossed or not) -->
<svg v-if="isPassword && !isPasswordShown" v-on:click="changeVision()" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1"/>
@ -78,12 +79,27 @@ import { Component, Vue } from 'vue-property-decorator';
type: String,
default: '100%'
},
isWhite: {
type: Boolean,
default: false
},
label: String,
error: String
},
computed: {
style: function () {
return {width: this.$props.width, height: this.$props.height};
return {
inputStyle: {
width: this.$props.width,
height: this.$props.height
},
labelStyle: {
color: this.$props.isWhite ? 'white' : '#354049'
},
errorStyle: {
color: this.$props.isWhite ? 'white' : '#FF5560'
},
};
}
}
},
@ -112,6 +128,10 @@ input::placeholder {
color: #384B65;
opacity: 0.4;
}
.inputError::placeholder {
color: #EB5757;
opacity: 0.4;
}
h3 {
font-family: 'montserrat_regular';
font-size: 16px;
@ -132,7 +152,6 @@ h3 {
}
&__error {
color: #FF5560;
margin-left: 10px;
}
}

View File

@ -23,7 +23,7 @@
Please check your inbox and use the verification link we shared with you in last email.</p>
<p>Didnt receive a verification email?</p>
<div class="register-success-popup__form-container__button-container">
<Button label="Resend Email" width="450px" height="50px" :onPress="onCloseClick" isWhite />
<Button label="Go to Login" width="450px" height="50px" :onPress="onCloseClick" isWhite />
</div>
</div>
<div class="register-success-popup__close-cross-container">

View File

@ -4,7 +4,7 @@
declare type User = {
firstName: string,
lastName: string,
email: string
email: string,
};
// Used in users module to pass parameters to action

View File

@ -0,0 +1,7 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
export const LOADING_CLASSES = {
LOADING_OVERLAY: 'loading-overlay',
LOADING_OVERLAY_ACTIVE: 'loading-overlay active',
};

View File

@ -230,4 +230,9 @@ export const EMPTY_STATE_IMAGES = {
<path d="M136.881 125.405H138.679C140.232 125.405 141.54 124.015 141.54 122.38C141.54 120.663 140.314 119.355 138.679 119.355H136.881C135.328 119.355 134.02 120.745 134.02 122.38C134.101 124.097 135.328 125.405 136.881 125.405Z" fill="#2683FF"/>
<path d="M116.361 136.03C115.952 136.03 115.625 136.03 115.298 136.111C114.644 136.193 113.99 135.784 113.745 135.212C106.061 121.233 91.1824 111.75 74.015 111.75C58.4826 111.75 44.7487 119.516 36.6555 131.37C36.2468 131.942 35.511 132.187 34.857 132.106C32.8133 131.697 30.6878 131.452 28.5623 131.452C12.5395 131.37 -0.376953 144.123 -0.376953 159.982C-0.376953 159.982 -0.376953 159.982 -0.376953 160.064C-0.376953 160.963 0.358791 161.699 1.33978 161.699H138.761C139.66 161.699 140.477 160.963 140.477 160.064V159.982C140.477 146.739 129.686 136.03 116.361 136.03Z" fill="#DEDEFC"/>
</svg>`,
INFO: `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="19" height="19" rx="9.5" fill="#007AFF" stroke="#007AFF"/>
<path d="M10.0004 11.7359C10.6112 11.7359 11.111 11.2035 11.111 10.5527V5.81169C11.111 5.75253 11.1073 5.68942 11.0999 5.63026C11.0185 5.06623 10.5557 4.62842 10.0004 4.62842C9.38964 4.62842 8.88989 5.16089 8.88989 5.81169V10.5487C8.88989 11.2035 9.38964 11.7359 10.0004 11.7359Z" fill="white"/>
<path d="M9.99983 12.8904C9.13953 12.8904 8.44507 13.5848 8.44507 14.4452C8.44507 15.3055 9.13953 15.9999 9.99983 15.9999C10.8601 15.9999 11.5546 15.3055 11.5546 14.4452C11.5581 13.5848 10.8636 12.8904 9.99983 12.8904Z" fill="white"/>
</svg>`,
};

View File

@ -3,36 +3,52 @@
<template>
<div class="login-container" v-on:keyup.enter="onLogin">
<div v-bind:class="loadingClassName">
<img class="loading-overlay__logo" src="../../static/images/login/Logo.svg" alt="loading-logo">
</div>
<img class="planet" src="../../static/images/Mars.png" alt="" >
<div class="login-container__wrapper">
<img class="login-container__logo" src="../../static/images/login/Logo.svg" alt="logo" v-on:click="onLogoClick">
<div class="login-area">
<div class="login-area__title-container">
<h1>Welcome to Storj</h1>
<div class="login-container__header">
<img class="login-container__logo" src="../../static/images/login/Logo.svg" alt="logo" v-on:click="onLogoClick">
<div class="login-container__register-button" v-on:click.prevent="onSignUpClick">
<p>Create Account</p>
</div>
<HeaderlessInput
class="login-area__email-input"
placeholder="Email"
@setData="setEmail"
height="46px"
width="100%">
</HeaderlessInput>
<HeaderlessInput
class="login-area__password-input"
placeholder="Password"
@setData="setPassword"
width="100%"
height="46px"
isPassword>
</HeaderlessInput>
<Button class="login-area__login-button" label="Login" width="100%" height="48px" :onPress="onLogin"/>
<!-- start of navigation area -->
<div class="login-area__navigation-area">
<router-link to="/register" class="login-area__navigation-area__nav-link bold" exact><h3>Create
account</h3></router-link>
<router-link to="" class="login-area__navigation-area__nav-link" exact><h3><strong>Forgot
password</strong></h3></router-link>
</div>
<div class="login-area-wrapper">
<div class="login-area">
<div class="login-area__title-container">
<h1>Login to Storj</h1>
<p>Satellite:<b>Mars</b></p>
</div>
<HeaderlessInput
class="login-area__email-input"
placeholder="Email"
@setData="setEmail"
height="46px"
width="100%">
</HeaderlessInput>
<HeaderlessInput
class="login-area__password-input"
placeholder="Password"
@setData="setPassword"
width="100%"
height="46px"
isPassword>
</HeaderlessInput>
<div class="login-area__submit-area">
<router-link to="" class="login-area__navigation-area__nav-link" exact>
<h3><strong>Forgot password?</strong></h3>
</router-link>
<div class="login-area__submit-area__login-button" v-on:click.prevent="onLogin">
<p>Login</p>
</div>
</div>
<div class="login-area__info-area">
<p class="login-area__info-area__signature">Storj Labs Inc 2019.</p>
<a class="login-area__info-area__terms">Terms & Conditions</a>
<a class="login-area__info-area__help">Help</a>
</div>
</div>
<!-- end of navigation area -->
</div>
</div>
</div>
@ -40,13 +56,13 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HeaderlessInput from '@/components/common/HeaderlessInput.vue';
import Button from '@/components/common/Button.vue';
import { setToken } from '@/utils/tokenManager';
import ROUTES from '../utils/constants/routerConstants';
import { NOTIFICATION_ACTIONS } from '../utils/constants/actionNames';
import { NOTIFICATION_ACTIONS } from '@/utils/constants/actionNames';
import { getTokenRequest } from '@/api/users';
import { LOADING_CLASSES } from '@/utils/constants/classConstants';
@Component({
data: function () {
@ -54,20 +70,32 @@ import { getTokenRequest } from '@/api/users';
return {
email: '',
password: '',
token: ''
loadingClassName: LOADING_CLASSES.LOADING_OVERLAY,
};
},
methods: {
onLogoClick: function (): void {
location.reload();
},
setEmail: function (value: string) {
setEmail: function (value: string): void {
this.$data.email = value;
},
setPassword: function (value: string) {
setPassword: function (value: string): void {
this.$data.password = value;
},
onLogin: async function () {
activateLoadingOverlay: function(): void {
this.$data.loadingClassName = LOADING_CLASSES.LOADING_OVERLAY_ACTIVE;
setTimeout(() => {
this.$data.loadingClassName = LOADING_CLASSES.LOADING_OVERLAY;
}, 2000);
},
onLogin: async function (): Promise<any> {
if (!this.$data.email || !this.$data.password) {
return;
}
(this as any).activateLoadingOverlay();
let loginResponse = await getTokenRequest(this.$data.email, this.$data.password);
if (!loginResponse.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, loginResponse.errorMessage);
@ -77,8 +105,10 @@ import { getTokenRequest } from '@/api/users';
setToken(loginResponse.data);
this.$router.push(ROUTES.DASHBOARD.path);
}
},
onSignUpClick: function (): void {
this.$router.push(ROUTES.REGISTER.path);
},
},
components: {
HeaderlessInput,
@ -97,59 +127,198 @@ export default class Login extends Vue {
height: 100%;
left: 0;
top: 0;
background: rgba(51, 51, 51, 0.7);
z-index: 10;
background-image: url(../../static/images/login/1920.svg);
background-repeat: no-repeat;
background-size: contain;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: flex-start;
padding: 60px 0px 190px 104px;
padding: 60px 0px 0px 104px;
background-image: url("../../static/images/Background.png");
background-repeat: no-repeat;
background-size: auto;
&__wrapper {
min-width: 50%;
height: 86vh;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row;
width: 100%;
}
&__logo {
cursor: pointer;
width: 139px;
height: 62px;
}
&__register-button {
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
border-radius: 6px;
border: 1px solid white;
cursor: pointer;
width: 160px;
height: 48px;
p {
font-family: 'montserrat_bold';
font-size: 14px;
line-height: 19px;
margin-block-start: 0;
margin-block-end: 0;
color: white;
}
&:hover {
background-color: white;
p {
color: #2683FF;
}
}
}
}
.planet {
position: absolute;
top: -161px;
right: -257px;
z-index: -100;
}
.login-area-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: flex-end;
}
.login-area {
background-color: #fff;
margin-top: 50px;
max-width: 500px;
width: 100%;
padding: 120px;
background-color: transparent;
width: 620px;
border-radius: 6px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: flex-start;
padding-bottom: 50px;
&__title-container {
height: 48px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
margin-bottom: 32px;
justify-content: space-between;
align-items: flex-end;
flex-direction: row;
margin-bottom: 20px;
width: 100%;
h1 {
font-family: 'montserrat_bold';
font-size: 32px;
color: #384B65;
line-height: 39px;
font-size: 22px;
color: white;
line-height: 27px;
margin-block-start: 0;
margin-block-end: 0;
}
p {
font-family: 'montserrat_regular';
font-size: 16px;
color: white;
line-height: 21px;
margin-block-start: 0;
margin-block-end: 0;
b {
font-family: 'montserrat_bold';
margin-left: 7px;
}
}
}
&__password-input {
margin-top: 22px;
}
&__login-button {
&__submit-area {
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: center;
width: 100%;
margin-top: 22px;
align-self: center;
&__login-button {
display: flex;
align-items: center;
justify-content: center;
background-color: #2683FF;
border-radius: 6px;
cursor: pointer;
width: 160px;
height: 48px;
box-shadow: 0px 16px 24px #3A54DF;
p {
font-family: 'montserrat_bold';
font-size: 14px;
line-height: 19px;
margin-block-start: 0;
margin-block-end: 0;
color: white;
}
&:hover {
box-shadow: none;
}
}
}
&__info-area {
width: 100%;
height: 42px;
margin-top: 300px;
display: flex;
align-items: flex-end;
justify-content: flex-start;
flex-direction: row;
p {
font-family: 'montserrat_regular';
font-size: 12px;
line-height: 18px;
text-align: center;
text-decoration: none;
color: white;
margin-block-start: 0;
margin-block-end: 0;
}
a {
font-family: 'montserrat_regular';
font-size: 15px;
line-height: 22px;
text-align: center;
text-decoration: none;
color: white;
}
&__signature {
margin-right: 50px;
}
&__terms {
margin-right: 35px;
}
}
&__login-button.container {
@ -170,17 +339,13 @@ export default class Login extends Vue {
font-family: 'montserrat_regular';
font-size: 14px;
line-height: 20px;
color: #2683FF;
height: 48px;
text-align: center;
text-justify: center;
padding-left: 15px;
padding-right: 15px;
min-width: 140px;
&:hover {
text-decoration: underline;
}
text-decoration: none;
color: white;
.bold {
font-family: 'montserrat_medium';
@ -189,52 +354,36 @@ export default class Login extends Vue {
}
}
@media screen and (max-width: 1440px) {
.login-container {
background-size: auto;
background-image: url(../../static/images/login/Background.svg);
.loading-overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
left: 0;
height: 100vh;
width: 0;
z-index: 100;
background-color: rgba(134, 134, 148, 0.7);
visibility: hidden;
opacity: 0;
-webkit-transition: all 0.5s linear;
-moz-transition: all 0.5s linear;
-o-transition: all 0.5s linear;
transition: all 0.5s linear;
&__logo {
width: 240px;
height: 110px;
z-index: 200;
}
}
@media screen and (max-width: 1280px) {
.login-container {
background-image: url(../../static/images/login/1280.svg);
background-size: auto;
}
.login-area {
padding: 86px;
max-width: 444px;
}
}
@media screen and (max-width: 1024px) {
.login-container {
background-image: url(../../static/images/login/1024.svg);
}
}
@media screen and (max-width: 800px) {
.login-container {
padding: 0;
justify-content: flex-start;
display: block;
padding: 77px 50px 0 50px;
background-image: url(../../static/images/login/800.svg);
background-position-y: 0px;
width: auto;
height: 1450px;
position: relative;
&__wrapper {
margin: 0 auto;
max-width: 600px;
}
}
.login-area {
max-width: auto;
width: auto;
margin: 0 auto;
margin-top: 80px;
}
.loading-overlay.active {
width: 100vw;
visibility: visible;
opacity: 1;
}
</style>

View File

@ -2,164 +2,116 @@
// See LICENSE for copying information.
<template>
<div class="register" v-on:keyup.enter="onCreateClick">
<div class="register-area">
<div class="register-area__scrollable">
<div class="register-area__scrollable__navLabel">
<router-link to="/" exact>
<svg class="register-area__scrollable__navLabel__back-image" width="19" height="19"
viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M10.5607 0.43934C11.1464 1.02513 11.1464 1.97487 10.5607 2.56066L5.12132 8H17.5C18.3284 8 19 8.67157 19 9.5C19 10.3284 18.3284 11 17.5 11H5.12132L10.5607 16.4393C11.1464 17.0251 11.1464 17.9749 10.5607 18.5607C9.97487 19.1464 9.02513 19.1464 8.43934 18.5607L0.43934 10.5607C-0.146447 9.97487 -0.146447 9.02513 0.43934 8.43934L8.43934 0.43934C9.02513 -0.146447 9.97487 -0.146447 10.5607 0.43934Z"
fill="#384B65"/>
</svg>
</router-link>
<h1>Sign Up</h1>
<div class="register-container" v-on:keyup.enter="onCreateClick">
<div v-bind:class="loadingClassName">
<img src="../../static/images/register/Loading.gif">
</div>
<img class="planet" src="../../static/images/Mars.png" alt="" >
<div class="register-container__wrapper">
<div class="register-container__header">
<img class="register-container__logo" src="../../static/images/login/Logo.svg" alt="logo" v-on:click="onLogoClick">
<div class="register-container__register-button" v-on:click.prevent="onLoginClick">
<p>Login</p>
</div>
<div class="register-area__scrollable__form-area">
<HeaderedInput
class="full-input"
label="First name"
placeholder="Enter First Name"
:error="firstNameError"
@setData="setFirstName">
</HeaderedInput>
<HeaderedInput
class="full-input"
label="Last Name"
placeholder="Enter Last Name"
@setData="setLastName">
</HeaderedInput>
<HeaderedInput
class="full-input"
label="Email"
placeholder="Enter Email"
:error="emailError"
@setData="setEmail">
</HeaderedInput>
<HeaderedInput
</div>
<div class="register-area-wrapper">
<div class="register-area">
<div class="register-area__title-container">
<h1>Sign Up to Storj</h1>
<p>Satellite:<b>Mars</b></p>
</div>
<HeaderlessInput
class="full-input"
label="First name"
placeholder="Enter First Name"
:error="firstNameError"
@setData="setFirstName"
width="100%"
height="46px"
isWhite>
</HeaderlessInput>
<HeaderlessInput
class="full-input"
label="Last Name"
placeholder="Enter Short Name"
@setData="setLastName"
width="100%"
height="46px"
isWhite>
</HeaderlessInput>
<HeaderlessInput
class="full-input"
label="Email"
placeholder="Enter Email"
:error="emailError"
@setData="setEmail"
width="100%"
height="46px"
isWhite>
</HeaderlessInput>
<div class="register-input">
<HeaderlessInput
class="full-input"
label="Password"
placeholder="Enter Password"
:error="passwordError"
@setData="setPassword"
width="100%"
height="46px"
isWhite
isPassword>
</HeaderedInput>
<HeaderedInput
</HeaderlessInput>
<span
v-html="infoImage"
title="Use 6 or more characters with a mix of letters and numbers"></span>
</div>
<div class="register-input">
<HeaderlessInput
class="full-input"
label="Repeat Password"
placeholder="Repeat Password"
:error="repeatedPasswordError"
@setData="setRepeatedPassword"
isPassword>
</HeaderedInput>
<!-- end of optional area -->
<div class="register-area__scrollable__form-area__terms-area">
<Checkbox
class="register-area__scrollable__form-area__terms-area__checkbox"
@setData="setTermsAccepted"
:isCheckboxError="isTermsAcceptedError"/>
<h2>I agree to the Storj Bridge Hosting <a>Terms & Conditions</a></h2>
width="100%"
height="46px"
isPassword
isWhite >
</HeaderlessInput>
<span v-html="infoImage"></span>
</div>
<div class="register-area__submit-container">
<div class="register-area__submit-container__terms-area">
<label class="container">
<input type="checkbox" v-model="isTermsAccepted">
<span v-bind:class="[isTermsAcceptedError ? 'checkmark error': 'checkmark']"></span>
</label>
<h2>I agree to the <a>Terms & Conditions</a></h2>
</div>
<div id="createAccountButton" class="register-area__submit-container__create-button" v-on:click.prevent="onCreateClick">
<p>Create Account</p>
</div>
</div>
<Button id="createAccountButton" class="register-area__scrollable__form-area__create-button" label="Create Account"
width="100%" height="48px" :onPress="onCreateClick"/>
</div>
</div>
</div>
<img class="layout-image" src="../../static/images/register/RegisterImage.svg"/>
<RegistrationSuccessPopup />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HeaderedInput from '@/components/common/HeaderedInput.vue';
import Checkbox from '@/components/common/Checkbox.vue';
import Button from '@/components/common/Button.vue';
import HeaderlessInput from '@/components/common/HeaderlessInput.vue';
import { EMPTY_STATE_IMAGES } from '@/utils/constants/emptyStatesImages';
import RegistrationSuccessPopup from '@/components/common/RegistrationSuccessPopup.vue';
import { validateEmail, validatePassword } from '@/utils/validation';
import ROUTES from '@/utils/constants/routerConstants';
import { LOADING_CLASSES } from '@/utils/constants/classConstants';
import { APP_STATE_ACTIONS, NOTIFICATION_ACTIONS } from '@/utils/constants/actionNames';
import { createUserRequest } from '@/api/users';
@Component(
{
methods: {
setEmail: function (value: string) {
this.$data.email = value;
this.$data.emailError = '';
},
setFirstName: function (value: string) {
this.$data.firstName = value;
this.$data.firstNameError = '';
},
setLastName: function (value: string) {
this.$data.lastName = value;
},
setPassword: function (value: string) {
this.$data.password = value;
this.$data.passwordError = '';
},
setRepeatedPassword: function (value: string) {
this.$data.repeatedPassword = value;
this.$data.repeatedPasswordError = '';
},
setTermsAccepted: function (value: boolean) {
this.$data.isTermsAccepted = value;
this.$data.isTermsAcceptedError = false;
},
onCreateClick: async function () {
let hasError = false;
const firstName = this.$data.firstName.trim();
const email = this.$data.email.trim();
const lastName = this.$data.lastName.trim();
if (!firstName) {
this.$data.firstNameError = 'Invalid First Name';
hasError = true;
}
if (!validateEmail(email)) {
this.$data.emailError = 'Invalid Email';
hasError = true;
}
if (!validatePassword(this.$data.password)) {
this.$data.passwordError = 'Invalid Password';
hasError = true;
}
if (this.$data.repeatedPassword !== this.$data.password) {
this.$data.repeatedPasswordError = 'Password doesn\'t match';
hasError = true;
}
if (!this.$data.isTermsAccepted) {
this.$data.isTermsAcceptedError = true;
hasError = true;
}
if (hasError) return;
let user = {
email,
firstName,
lastName,
};
let response = await createUserRequest(user, this.$data.password);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, response.errorMessage);
return;
}
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP);
}
},
data: function (): RegisterData {
data: function () {
return {
firstName: '',
firstNameError: '',
@ -172,14 +124,106 @@ import { createUserRequest } from '@/api/users';
repeatedPasswordError: '',
isTermsAccepted: false,
isTermsAcceptedError: false,
secret: '',
loadingClassName: LOADING_CLASSES.LOADING_OVERLAY,
};
},
computed: {},
methods: {
setEmail: function (value: string): void {
this.$data.email = value;
this.$data.emailError = '';
},
setFirstName: function (value: string): void {
this.$data.firstName = value;
this.$data.firstNameError = '';
},
setLastName: function (value: string): void {
this.$data.lastName = value;
},
setPassword: function (value: string): void {
this.$data.password = value;
this.$data.passwordError = '';
},
setRepeatedPassword: function (value: string): void {
this.$data.repeatedPassword = value;
this.$data.repeatedPasswordError = '';
},
validateFields: function (): boolean {
let isNoErrors = true;
if (!this.$data.firstName.trim()) {
this.$data.firstNameError = 'Invalid First Name';
isNoErrors = false;
}
if (!validateEmail(this.$data.email.trim())) {
this.$data.emailError = 'Invalid Email';
isNoErrors = false;
}
if (!validatePassword(this.$data.password)) {
this.$data.passwordError = 'Invalid Password';
isNoErrors = false;
}
if (this.$data.repeatedPassword !== this.$data.password) {
this.$data.repeatedPasswordError = 'Password doesn\'t match';
isNoErrors = false;
}
if (!this.$data.isTermsAccepted) {
this.$data.isTermsAcceptedError = true;
isNoErrors = false;
}
return isNoErrors;
},
createUser: async function(): Promise<any> {
let user = {
email: this.$data.email.trim(),
firstName: this.$data.firstName.trim(),
lastName: this.$data.lastName.trim(),
};
let response = await createUserRequest(user, this.$data.password, this.$data.secret);
if (!response.isSuccess) {
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, response.errorMessage);
this.$data.loadingClassName = LOADING_CLASSES.LOADING_OVERLAY;
return;
}
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION_POPUP);
},
onCreateClick: function (): any {
let self = this as any;
if (!self.validateFields()) return;
this.$data.loadingClassName = LOADING_CLASSES.LOADING_OVERLAY_ACTIVE;
self.createUser();
},
onLogoClick: function (): void {
location.reload();
},
onLoginClick: function (): void {
this.$router.push(ROUTES.LOGIN.path);
},
},
computed: {
infoImage: function() {
return EMPTY_STATE_IMAGES.INFO;
},
},
components: {
HeaderedInput,
Checkbox,
Button,
HeaderlessInput,
RegistrationSuccessPopup
},
mounted(): void {
if (this.$route.query.token) {
this.$data.secret = this.$route.query.token.toString();
}
}
})
@ -194,239 +238,334 @@ export default class Register extends Vue {
margin: 0 !important;
}
.register {
.register-container {
position: fixed;
background-color: #fff;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 100vh;
width: 100%;
overflow: hidden;
height: 100%;
left: 0;
top: 0;
z-index: 10;
background-size: contain;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: flex-start;
padding: 60px 0px 0px 104px;
background-image: url("../../static/images/Background.png");
background-repeat: no-repeat;
background-size: auto;
.register-input {
position: relative;
width: 100%;
span {
position: absolute;
top: 66px;
right: 43px;
}
}
&__wrapper {
min-width: 50%;
height: 86vh;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row;
width: 100%;
}
&__logo {
cursor: pointer;
width: 139px;
height: 62px;
}
&__register-button {
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
border-radius: 6px;
border: 1px solid white;
cursor: pointer;
width: 160px;
height: 48px;
p {
font-family: 'montserrat_bold';
font-size: 14px;
line-height: 19px;
margin-block-start: 0;
margin-block-end: 0;
color: white;
}
&:hover {
background-color: white;
p {
color: #2683FF;
}
}
}
}
.register-area-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-end;
margin-top: 50px;
}
.register-area {
background-color: white;
width: 100%;
max-height: 100vh;
background-color: transparent;
width: 620px;
border-radius: 6px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: flex-start;
&__scrollable {
height: 100vh;
&__title-container {
height: 48px;
display: flex;
flex-direction: column;
justify-content: flex-start;
justify-content: space-between;
align-items: flex-end;
flex-direction: row;
margin-bottom: 20px;
width: 100%;
&__navLabel {
h1 {
font-family: 'montserrat_bold';
font-size: 22px;
color: white;
line-height: 27px;
margin-block-start: 0;
margin-block-end: 0;
}
p {
font-family: 'montserrat_regular';
font-size: 16px;
color: white;
line-height: 21px;
margin-block-start: 0;
margin-block-end: 0;
b {
font-family: 'montserrat_bold';
margin-left: 7px;
}
}
}
&__submit-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
margin-top: 20px;
&__terms-area {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
&__checkbox {
align-self: center;
}
h2 {
font-family: 'montserrat_regular';
font-size: 14px;
line-height: 20px;
margin-top: 14px;
margin-left: 10px;
color: white;
}
a {
color: white;
font-family: 'montserrat_bold';
&:hover {
text-decoration: underline;
}
}
.container {
display: block;
position: relative;
padding-left: 20px;
height: 25px;
width: 25px;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none;
}
.container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
border: 2px solid white;
border-radius: 4px;
}
.container:hover input ~ .checkmark {
background-color: white;
}
.container input:checked ~ .checkmark {
border: 2px solid white;
background-color: transparent;
}
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.checkmark.error {
border-color: red;
}
.container input:checked ~ .checkmark:after {
display: block;
}
.container .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
}
&__create-button {
display: flex;
align-items: center;
flex-direction: row;
justify-content: flex-start;
align-self: center;
width: 68%;
margin-top: 70px;
h1 {
color: #384B65;
margin-left: 24px;
font-family: 'montserrat_bold'
justify-content: center;
background-color: #2683FF;
border-radius: 6px;
cursor: pointer;
width: 160px;
height: 48px;
box-shadow: 0px 16px 24px #3A54DF;
p {
font-family: 'montserrat_bold';
font-size: 14px;
line-height: 19px;
margin-block-start: 0;
margin-block-end: 0;
color: white;
}
&__back-image {
width: 21px;
height: 21px;
&:hover path {
fill: #2683FF !important;
}
}
}
&__form-area {
margin-top: 50px;
align-self: center;
width: 35vw;
&__company-area {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
margin-top: 32px;
h2 {
font-family: 'montserrat_bold';
font-size: 20px;
line-height: 27px;
margin-right: 11px;
}
;
&__details-area {
cursor: pointer;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
&__text {
font-size: 16px;
line-height: 23px;
}
&__expander-area {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 4px;
&:hover {
background-color: #E2ECF7;
}
}
}
;
}
&__terms-area {
display: flex;
flex-direction: row;
justify-content: flex-start;
margin-top: 20px;
&__checkbox {
align-self: center;
}
;
h2 {
font-family: 'montserrat_regular';
font-size: 14px;
line-height: 20px;
margin-top: 30px;
margin-left: 10px;
}
;
a {
color: #2683FF;
font-family: 'montserrat_bold';
&:hover {
text-decoration: underline;
}
}
}
&__create-button {
margin-top: 30px;
margin-bottom: 100px;
&:hover {
box-shadow: none;
}
}
}
}
.input-container.full-input {
.input-wrap.full-input {
width: 100%;
}
.layout-image {
background-color: #2683FF;
display: block;
.planet {
position: absolute;
bottom: -61px;
right: -257px;
z-index: -100;
}
.loading-overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
left: 0;
height: 100vh;
z-index: 100;
background-color: rgba(134, 134, 148, 0.7);
visibility: hidden;
opacity: 0;
-webkit-transition: all 0.5s linear;
-moz-transition: all 0.5s linear;
-o-transition: all 0.5s linear;
transition: all 0.5s linear;
img {
z-index: 200;
}
}
a {
cursor: pointer;
}
#optional-area {
height: auto;
.loading-overlay.active {
visibility: visible;
opacity: 1;
transition: 0.5s;
}
#optional-area.optional-area--active {
animation: mymove 0.5s ease-in-out;
}
@media screen and (max-height: 840px) {
.register-container {
overflow: hidden;
#optional-area.optional-area {
height: 0px;
visibility: hidden;
position: absolute;
animation: mymoveout 0.5s ease-in-out;
}
&__wrapper {
height: 770px;
overflow-y: scroll;
overflow-x: hidden;
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
@keyframes mymove {
from {
height: 0px;
visibility: hidden;
opacity: 0;
}
to {
height: 100%;
visibility: visible;
opacity: 1;
}
}
@keyframes mymoveout {
from {
height: 100%;
visibility: visible;
opacity: 1;
}
to {
height: 0px;
visibility: hidden;
opacity: 0;
}
}
@media screen and (max-width: 1440px) {
.register-area {
background-color: white;
width: 100%;
max-height: 100vh;
&__scrollable {
width: 60vw;
}
}
}
@media screen and (max-width: 720px), screen and (max-height: 880px) {
.register {
flex-direction: column;
}
.layout-image {
position: absolute;
display: block;
bottom: 0;
left: 0;
width: 100%;
height: 200px;
}
.register-area {
width: 100vw;
overflow-y: scroll;
height: calc(100vh - 200px);
&__scrollable {
width: auto;
&__form-area {
&__create-button {
margin-bottom: 50px;
}
}
&__form-area {
width: 75vw;
&::-webkit-scrollbar {
width: 0 !important;
display: none;
}
}
}
}
</style>
@media screen and (max-height: 810px) {
.register-container {
&__wrapper {
height: 700px;
}
}
.register-area__submit-container {
margin-bottom: 25px;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -1,14 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`HeaderlessInput.vue renders correctly with default props 1`] = `
<div class="input-wrap"><input placeholder="default" type="text" style="width: 100%; height: 48px;">
<div class="input-wrap">
<div class="label-container">
<!---->
<!---->
<!---->
</div> <input placeholder="default" type="text" class="" style="width: 100%; height: 48px;">
<!---->
<!---->
</div>
`;
exports[`HeaderlessInput.vue renders correctly with isPassword prop 1`] = `
<div class="input-wrap"><input placeholder="default" type="password" style="width: 100%; height: 48px;"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<div class="input-wrap">
<div class="label-container">
<!---->
<!---->
<!---->
</div> <input placeholder="default" type="password" class="" style="width: 100%; height: 48px;"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 4C4.70642 4 1 10 1 10C1 10 3.6999 16 10 16C16.3527 16 19 10 19 10C19 10 15.3472 4 10 4ZM10 13.8176C7.93537 13.8176 6.2946 12.1271 6.2946 10C6.2946 7.87285 7.93537 6.18239 10 6.18239C12.0646 6.18239 13.7054 7.87285 13.7054 10C13.7054 12.1271 12.0646 13.8176 10 13.8176Z" fill="#AFB7C1"></path>
<path d="M11.6116 9.96328C11.6116 10.8473 10.8956 11.5633 10.0116 11.5633C9.12763 11.5633 8.41162 10.8473 8.41162 9.96328C8.41162 9.07929 9.12763 8.36328 10.0116 8.36328C10.8956 8.36328 11.6116 9.07929 11.6116 9.96328Z" fill="#AFB7C1"></path>
</svg>
@ -17,7 +27,12 @@ exports[`HeaderlessInput.vue renders correctly with isPassword prop 1`] = `
`;
exports[`HeaderlessInput.vue renders correctly with size props 1`] = `
<div class="input-wrap"><input placeholder="test" type="text" style="width: 30px; height: 20px;">
<div class="input-wrap">
<div class="label-container">
<!---->
<!---->
<!---->
</div> <input placeholder="test" type="text" class="" style="width: 30px; height: 20px;">
<!---->
<!---->
</div>

View File

@ -19,7 +19,7 @@ describe('mutations', () => {
user: {
firstName: '',
lastName: '',
email: ''
email: '',
}
};
@ -28,7 +28,7 @@ describe('mutations', () => {
const user = {
firstName: 'firstName',
lastName: 'lastName',
email: 'email'
email: 'email',
};
store.commit(USER_MUTATIONS.SET_USER_INFO, user);
@ -61,13 +61,13 @@ describe('mutations', () => {
user: {
firstName: '',
lastName: '',
email: ''
email: '',
}
};
const user = {
firstName: 'firstName',
lastName: 'lastName',
email: 'email'
email: 'email',
};
const store = new Vuex.Store({state, mutations});
@ -98,7 +98,7 @@ describe('actions', () => {
const user = {
firstName: '',
lastName: '',
email: ''
email: '',
};
const dispatchResponse = await usersModule.actions.updateAccount({commit}, user);
@ -107,7 +107,7 @@ describe('actions', () => {
expect(commit).toHaveBeenCalledWith(USER_MUTATIONS.UPDATE_USER_INFO, {
firstName: 'firstName',
lastName: 'lastName',
email: 'email'
email: 'email',
});
});
@ -121,7 +121,7 @@ describe('actions', () => {
const user = {
firstName: '',
lastName: '',
email: ''
email: '',
};
const dispatchResponse = await usersModule.actions.updateAccount({commit}, user);
@ -166,7 +166,7 @@ describe('actions', () => {
data: {
firstName: '',
lastName: '',
email: ''
email: '',
}
})
);

View File

@ -37,8 +37,8 @@ module.exports = {
// the "scss" and "sass" values for the lang attribute to the right configs here.
// other preprocessors should work out of the box, no loader config like this necessary.
ts:'ts-loader!tslint-loader',
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
scss: 'vue-style-loader!css-loader!sass-loader',
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
}
// other vue-loader options go here
}