Commit 2188a7ee by Ali Arshad

integrated bootstrap and user login flow

parent 4d5a4ebd
......@@ -19,6 +19,9 @@
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap-less/bootstrap/bootstrap.less",
"../node_modules/font-awesome/less/font-awesome.less",
"../node_modules/ng2-toastr/bundles/ng2-toastr.min.css",
"styles.less"
],
"scripts": [],
......
......@@ -22,7 +22,12 @@
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"bootstrap": "^4.0.0",
"bootstrap-less": "^3.3.7",
"core-js": "^2.4.1",
"font-awesome": "^4.7.0",
"ng2-toastr": "^4.1.2",
"ngx-bootstrap": "^2.0.2",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
......
import { TestBed, inject } from '@angular/core/testing';
import { HttpService } from './http.service';
describe('HttpService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [HttpService]
});
});
it('should be created', inject([HttpService], (service: HttpService) => {
expect(service).toBeTruthy();
}));
});
import {Injectable} from '@angular/core';
import {Headers, Http, RequestOptions} from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/share';
import {ToastsManager} from 'ng2-toastr/ng2-toastr';
import {Constants} from '../constants';
import {Router} from '@angular/router';
@Injectable()
export class HttpService {
private accessTokken: string;
private headers;
private options;
private api = Constants.API_URL;
private sendAuthMsg;
constructor(private http: Http,
private toastr: ToastsManager,
private router: Router) {
this.sendAuthMsg = true;
}
setTokken(value) {
this.accessTokken = value;
}
createHeader() {
if (this.accessTokken) {
this.headers = new Headers({'Content-Type': 'application/json'});
this.headers.append('Authorization', this.accessTokken);
this.options = new RequestOptions({headers: this.headers});
}
else {
this.headers = new Headers({'Content-Type': 'application/json'});
this.options = new RequestOptions({headers: this.headers});
}
return this.options;
}
sendUnAuthorizedMessage(message) {
message = message || 'You need to login first.';
this.sendAuthMsg = false;
this.toastr.error(message, null, {showCloseButton: true});
setTimeout(() => {
this.sendAuthMsg = true;
}, 5000);
}
showError(error, showErrors) {
switch (error.status) {
case 401:
if (showErrors.e401 != false) {
if (this.sendAuthMsg) {
const body = error.json();
const errorMsg = body.message || 'You need to login first';
this.sendUnAuthorizedMessage(errorMsg);
}
this.router.navigate(['/']);
}
break;
case 400:
if (showErrors.e400 != false) {
const body = error.json();
const errorMsg = body.message || 'There seems to be some error.';
this.toastr.error(errorMsg, null, {showCloseButton: true});
}
break;
case 500:
if (showErrors.e500 != false) {
this.toastr.error('We are unable to process your request.', null, {showCloseButton: true});
}
break;
case 404:
if (showErrors.e404 != false) {
this.toastr.error('API resource does not exist', null, {showCloseButton: true});
}
break;
case 422:
if (showErrors.e422 != false) {
let errorMsg;
const body = error.json();
errorMsg = body.message;
this.toastr.error(errorMsg, 'Oops!', {showCloseButton: true});
}
break;
}
}
post(method, value, showErrors = {}) {
const url = this.api + method;
const observable = this.http.post(url, value, this.createHeader()).map((response) => {
return response.json();
}).share();
observable.subscribe(
data => {},
error => {
this.showError(error, showErrors);
}
);
return observable;
}
get(method, value = '', showErrors = {}) {
const url = this.api + method + value;
const observable = this.http.get(url, this.createHeader()).map((response) => {
return response.json();
}).share();
observable.subscribe(
data => {
},
error => {
this.showError(error, showErrors);
}
);
return observable;
}
delete(method, value = '', showErrors = {}) {
const url = this.api + method + value;
const observable = this.http.delete(url, this.createHeader()).map((response) => {
return response.json();
}).share();
observable.subscribe(
data => {
},
error => {
this.showError(error, showErrors);
}
);
return observable;
}
put(method, value, showErrors = {}) {
const url = this.api + method;
const observable = this.http.put(url, value, this.createHeader()).map((response) => {
return response.json();
}).share();
observable.subscribe(
data => {
},
error => {
this.showError(error, showErrors);
}
);
return observable;
}
}
import { TestBed, inject } from '@angular/core/testing';
import { LocalStoreService } from './local-store.service';
describe('LocalStoreService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [LocalStoreService]
});
});
it('should be created', inject([LocalStoreService], (service: LocalStoreService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
@Injectable()
export class LocalStoreService {
constructor() { }
get(key)// key -> 'userTokken' for access tokken & 'userDetail' for user detail
{
return localStorage.getItem(key);
}
set(key,value)
{
localStorage.setItem(key,value);
}
remove(key)
{
localStorage.removeItem(key);
}
}
import { Component } from '@angular/core';
import {Component, ViewContainerRef} from '@angular/core';
import {Router} from '@angular/router';
import {ToastsManager} from 'ng2-toastr';
import {BsModalService} from 'ngx-bootstrap/modal';
import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
import {Constants} from './constants';
import {UserService} from './users/user.service';
import {SessionModalComponent} from './shared/session-modal/session-modal.component';
@Component({
selector: 'app-root',
......@@ -6,5 +14,29 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.less']
})
export class AppComponent {
title = 'wah';
bsModalRef: BsModalRef;
constructor(public toastr: ToastsManager, vRef: ViewContainerRef,
private user: UserService,
private modalService: BsModalService,
private router: Router) {
this.toastr.setRootViewContainerRef(vRef);
this.user.onSessionDestroyed.subscribe(data => {
this.showLogoutPopup();
});
}
showLogoutPopup() {
const initialState = {
title: 'Session has been closed',
body: 'Session has been closed.',
closeBtnName: 'OK'
};
this.bsModalRef = this.modalService.show(SessionModalComponent, {initialState});
this.modalService.onHide.subscribe(data => {
this.router.navigate([Constants.APP_URLS.login]);
}, err => {
});
}
}
import {BrowserModule} from '@angular/platform-browser';
import {ToastModule} from 'ng2-toastr/ng2-toastr';
import {NgModule} from '@angular/core';
import {HttpModule} from '@angular/http';
import {FormsModule} from '@angular/forms';
import {LayoutsModule} from './layouts/layouts.module';
import {RoutesModule} from './routes/routes.module';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import { ModalModule } from 'ngx-bootstrap/modal';
import {HttpService} from './app-services/http.service';
import { SessionModalComponent } from './shared/session-modal/session-modal.component';
@NgModule({
declarations: [
AppComponent
AppComponent,
SessionModalComponent
],
imports: [
BrowserModule,
LayoutsModule,
RoutesModule
RoutesModule,
HttpModule,
FormsModule,
BrowserAnimationsModule,
ToastModule.forRoot(),
ModalModule.forRoot()
],
entryComponents: [ SessionModalComponent ],
providers: [
HttpService
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
......
let env = 'local';
let API_URL;
const APP_URLS = {
login: '/',
dashboard: '/dashboard',
forgetPassword: '/forgot',
newPassword: '/set-new'
};
if (window.location.hostname.indexOf('stage') === -1) {
env = 'stage';
} else {
env = 'prod';
}
if (env === 'prod') {
API_URL = 'http://localhost/';//will be set after deployment
} else {
API_URL = 'http://localhost/';
}
export class Constants {
public static get API_URL(): string {
return API_URL;
}
public static get APP_URLS(): any {
return APP_URLS;
}
}
......@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RoutesModule} from '../routes/routes.module';
import {UsersModule} from '../users/users.module';
import {PublicComponent} from './public/public.component';
import {ProtectedComponent} from './protected/protected.component';
......@@ -9,7 +10,8 @@ import {ProtectedComponent} from './protected/protected.component';
@NgModule({
imports: [
CommonModule,
RoutesModule
RoutesModule,
UsersModule
],
declarations: [PublicComponent, ProtectedComponent]
})
......
//below line will be removed with actual layout
p protected works!
router-outlet
.dashboard_body((click)='hideUserPopup()')
.container
.row
.navbar.navbar-default.navbar.menu.main
.navbar-header
button.navbar-toggle(type="button" data-toggle="collapse" data-target=".navbar-collapse")
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand([routerLink]='dashboardURL')
img.img-responsive(src="/assets/img/knauf-3.png" alt="knauf_logo")
.pull-left.dropdown
a#dropdownMenu1.btn-user.dropdown-toggle((click)="toggleUserPopup($event)")
.img-back
img(src="/assets/img/User_icon.png" alt="")
ul.dropdown-menu.user-dropdown(*ngIf="userPopup")
li
a(href="#")
span.u_name {{user.user_name}}
li
a((click)='logout()')
span.sign_out Sign Out
.navbar-collapse.collapse
ul.nav.navbar-nav
li
a(href="#") Admin
li
a(href="#") Management
li
a(href="#") Projects
.container.last
.row
.col-lg-3.col-lg-offset-9.col-md-3.col-md-offset-.col-sm-3.col-sm-offset-9.col-xs-5.col-xs-offset-7(style="padding-right: 0;")
.pull-right.projects
p.text-center Management Dashboard
.container.last
.row
.col-lg-12.col-md-12.col-sm-12.col-xs-12
p.lastP
| Copyright Drywall Aptitude © {{currentYear}} 
span.color Knauf Specification Author. 
| All rights reserved.
import { Component, OnInit } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {Constants} from '../../constants';
import {UserService} from '../../users/user.service';
import {Router} from '@angular/router';
@Component({
selector: 'app-protected',
......@@ -6,10 +9,31 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./protected.component.less']
})
export class ProtectedComponent implements OnInit {
currentYear;
userPopup = false;
dashboardURL = Constants.APP_URLS.dashboard;
constructor() { }
constructor(private user: UserService, private router: Router) {
}
ngOnInit() {
this.currentYear = new Date().getFullYear();
}
toggleUserPopup(event) {
console.log(event);
event.stopPropagation();
this.userPopup = !this.userPopup;
}
hideUserPopup() {
this.userPopup = false;
}
logout() {
this.user.logout().subscribe(data => {
this.router.navigate([Constants.APP_URLS.login]);
});
}
}
.login_body {
background: url(/assets/img/layer1.png) no-repeat center center fixed;
background-size: 100% 100%;
height: 100vh;
}
.login-wrapper {
min-height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
\ No newline at end of file
//below line will be removed with actual layout
p public works!
router-outlet
\ No newline at end of file
.login_body
.container.vertical-align.login-wrapper
.row.text-center.login-form-wrapper
.col-md-12
router-outlet
\ No newline at end of file
import {Injectable} from '@angular/core';
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {UserService} from '../users/user.service';
import {Constants} from '../constants';
@Injectable()
export class AppGuard implements CanActivate {
constructor(private router: Router,
public activatedRoute: ActivatedRoute) {
public activatedRoute: ActivatedRoute,
private userService: UserService) {
// nothing in constructor
}
......@@ -13,10 +16,11 @@ export class AppGuard implements CanActivate {
// const roles = next.data['roles']; // would be used later
// const user: any = {}; // would be used later
// specify route protection logic below
if (1) {
if (this.userService.isAuthenticated()) {
// logged in so return true
return true;
} else {
this.router.navigate([Constants.APP_URLS.login]);
// not logged in so redirect to login page with the return url
return false;
}
......
......@@ -5,10 +5,13 @@ import {AppGuard} from './app.guard';
import {PublicComponent} from '../layouts/public/public.component';
import {ProtectedComponent} from '../layouts/protected/protected.component';
import {LoginComponent} from '../users/login/login.component';
import {ForgotComponent} from '../users/forgot/forgot.component';
import {NewPasswordComponent} from '../users/new-password/new-password.component';
const routes: Routes = [
{
path: 'admin',
path: 'dashboard',
canActivate: [AppGuard],
data: {roles: ['admin']},
component: ProtectedComponent,
......@@ -17,7 +20,24 @@ const routes: Routes = [
{
path: '',
component: PublicComponent,
children: []
children: [
{
path: '',
component: LoginComponent,
},
{
path: 'forgot',
component: ForgotComponent,
},
{
path: 'set-new/:token',
component: NewPasswordComponent,
},
{
path: 'set-new/:token/:reason', // reason is optional, reason for 2 routes
component: NewPasswordComponent,
}
]
},
];
......
<div class="loaderWrapper">
<div [style.width.px]="width" style="margin:auto">
<svg class="lds-blocks" [style.width.px]="width" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-eclipse" style="background: none;">
<path stroke="none" d="M10 50A40 40 0 0 0 90 50A40 45 0 0 1 10 50" fill="#51CACC" transform="rotate(150 50 52.5)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 52.5;360 50 52.5" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</path>
</svg>
</div>
</div>
\ No newline at end of file
.loader-hidden {
visibility: hidden;
}
.loader-overlay {
position: absolute;
width: 100%;
top: 0;
left: 0;
z-index: 500000;
background: rgba(128,128,128,0.4);
border-radius: 10px;
}
.lds-blocks{
display: block;
margin: auto;
}
.loaderWrapper{
display: flex;
align-items: center;
justify-content: center;
height:100%
}
\ No newline at end of file
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-loader',
templateUrl: './loader.component.html',
styleUrls: ['./loader.component.less']
})
export class LoaderComponent implements OnInit {
@Input() width: number;
constructor() {
}
ngOnInit() {
if (this.width == undefined) {
this.width = 150;
}
}
}
\ No newline at end of file
.modal-content
.modal-header
button.close.pull-right((click)="bsModalRef.hide()")
span(aria-hidden="true") ×
h4.modal-title {{title}}
.modal-body
| {{body}}
.modal-footer
button.btn.btn-primary((click)="bsModalRef.hide()") {{closeBtnName}}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SessionModalComponent } from './session-modal.component';
describe('SessionModalComponent', () => {
let component: SessionModalComponent;
let fixture: ComponentFixture<SessionModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SessionModalComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SessionModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service';
@Component({
selector: 'app-session-modal',
templateUrl: './session-modal.component.pug',
styleUrls: ['./session-modal.component.less']
})
export class SessionModalComponent implements OnInit {
closeBtnName;
title;
body;
constructor(public bsModalRef: BsModalRef) {
}
ngOnInit() {
}
}
@import "../../../assets/css/magic-check_login";
.clear {
clear: both;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
bottom: 3px;
width: 100%;
}
.color {
color: #009EE2 !important;
font-weight: bold;
}
.form-control::-webkit-input-placeholder {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
}
.logo-img {
display: inline-block;
padding: 12% 0;
}
.login_form {
position: relative;
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 500px;
padding: 10px 45px;
}
.specification_Author {
font-size: 18px;
font-family: FuturaPT;
color: rgb(0, 158, 226);
line-height: 1.2;
margin-left: 4%;
display: inline-block;
margin-top: 23.5%;
position: absolute;
font-weight: 200;
}
.form-control {
padding: 24px 12px;
border-radius: 0;
border: 1px solid #bcb5ad;
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 15%;
background-position: 60px 10px;
}
.col-lg-6 {
padding-right: 0;
}
.radio-btn3 {
float: left;
}
.magic-radio + label, .magic-checkbox + label {
padding-left: 10px !important;
margin-top: -1%;
padding-bottom: 23%;
}
.magic-radio + label:before, .magic-checkbox + label:before {
top: 15px !important;
left: -13px !important;
}
.magic-radio + label:after {
top: 18px !important;
left: -10px !important;
}
#inputPassword {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
#inputEmai {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
.col-lg-8, .col-md-6, .col-sm-10, .col-xs-12 {
padding-right: 0;
}
.footer {
min-height: 0;
}
.no-backgroud{
background-image: none;
}
label {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
font-weight: 300;
/*padding-bottom: 5%;
text-align: left;*/
}
@media (min-width: 992px) and (max-width: 1900px) {
.lastP {
width: 90%;
text-align: center !important;
margin-left: 9%;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.logo-img {
width: 120%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (min-width: 560px) and (max-width: 767px) {
.logo-img {
width: 110%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 736px) {
body {
overflow-x: hidden;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 10px;
width: 100%;
}
}
@media (min-width: 480px) and (max-width: 559px) {
.logo-img {
width: 120%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 414px) {
.login_form {
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 100% !important;
padding: 10px 45px;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 58px 6px !important;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 90px !important;
width: 100%;
}
}
@media (max-width: 320px) {
button {
background-position: 53px 6px !important;
}
}
@media (min-width: 320px) and (max-width: 479px) {
.logo-img {
width: 150%;
}
.form-control {
padding: 5% 9%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
\ No newline at end of file
form.text-center('name'='forgotForm' '(ngSubmit)'="forgot()")
.login_form
.loaderAbsHolder('*ngIf'='loading')
app-loader
.row
.col-lg-12
.text-center.col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-8.col-xs-offset-2
img.img-responsive.logo-img(src="/assets/img/logo.png" alt="knauf_logo")
.form-group
input#inputEmai.form-control.question(type="email" placeholder="Email address" maxlength="50" 'name'='userEmail' '[(ngModel)]'='userEmail')
label.col-sm-12(class='text-right')
a('[routerLink]'='APP_URLS.login') Back to login...
.form-group
.text-right
button.btn_submit.pull-right.login(type="submit" '*ngIf'='!(loading)') Submit
button.btn_submit.pull-right.login(type="button" '*ngIf'='loading') Please wait!
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ForgotComponent } from './forgot.component';
describe('ForgotComponent', () => {
let component: ForgotComponent;
let fixture: ComponentFixture<ForgotComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ForgotComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ForgotComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser';
import {Router} from '@angular/router';
import {UserService} from '../user.service';
import {ToastsManager} from 'ng2-toastr';
import {Constants} from '../../constants';
@Component({
selector: 'app-forgot',
templateUrl: './forgot.component.pug',
styleUrls: ['./forgot.component.less']
})
export class ForgotComponent implements OnInit {
title = 'Recover Password | Knauf Specification Author';
APP_URLS = Constants.APP_URLS;
userEmail = '';
loading = false;
constructor(private titleService: Title,
private router: Router,
private user: UserService,
private toaster: ToastsManager) {
this.titleService.setTitle(this.title);
}
ngOnInit() {
}
forgot() {
if (!this.userEmail) {
this.toaster.error('Please provide your email address');
return;
}
this.loading = true;
this.user.sendPassword(this.userEmail).subscribe(data => {
this.toaster.success(data.message, '', {enableHTML: true});
this.loading = false;
}, err => {
this.loading = false;
});
}
}
@import "../../../assets/css/magic-check_login";
.clear {
clear: both;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
bottom: 3px;
width: 100%;
}
.color {
color: #009EE2 !important;
font-weight: bold;
}
.form-control::-webkit-input-placeholder {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
}
.logo-img {
display: inline-block;
padding: 12% 0;
}
.login_form {
position: relative;
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 500px;
padding: 10px 45px;
}
.specification_Author {
font-size: 18px;
font-family: FuturaPT;
color: rgb(0, 158, 226);
line-height: 1.2;
margin-left: 4%;
display: inline-block;
margin-top: 23.5%;
position: absolute;
font-weight: 200;
}
.form-control {
padding: 24px 12px;
border-radius: 0;
border: 1px solid #bcb5ad;
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 15%;
background-position: 60px 10px;
}
.col-lg-6 {
padding-right: 0;
}
.radio-btn3 {
float: left;
}
.magic-radio + label, .magic-checkbox + label {
padding-left: 10px !important;
margin-top: -1%;
padding-bottom: 23%;
}
.magic-radio + label:before, .magic-checkbox + label:before {
top: 15px !important;
left: -13px !important;
}
.magic-radio + label:after {
top: 18px !important;
left: -10px !important;
}
#inputPassword {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
#inputEmai {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
.col-lg-8, .col-md-6, .col-sm-10, .col-xs-12 {
padding-right: 0;
}
.footer {
min-height: 0;
}
.no-backgroud{
background-image: none;
}
label {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
font-weight: 300;
/*padding-bottom: 5%;
text-align: left;*/
}
@media (min-width: 992px) and (max-width: 1900px) {
.lastP {
width: 90%;
text-align: center !important;
margin-left: 9%;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.logo-img {
width: 120%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (min-width: 560px) and (max-width: 767px) {
.logo-img {
width: 110%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 736px) {
body {
overflow-x: hidden;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 10px;
width: 100%;
}
}
@media (min-width: 480px) and (max-width: 559px) {
.logo-img {
width: 120%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 414px) {
.login_form {
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 100% !important;
padding: 10px 45px;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 58px 6px !important;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 90px !important;
width: 100%;
}
}
@media (max-width: 320px) {
button {
background-position: 53px 6px !important;
}
}
@media (min-width: 320px) and (max-width: 479px) {
.logo-img {
width: 150%;
}
.form-control {
padding: 5% 9%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
\ No newline at end of file
form.text-center('name'='loginForm' '(ngSubmit)'="login()")
.login_form
.loaderAbsHolder('*ngIf'='showLoader')
app-loader
.row
.col-lg-12
.text-center.col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-8.col-xs-offset-2
img.img-responsive.logo-img(src="/assets/img/logo.png" alt="knauf_logo")
.form-group
input#inputEmai.form-control.question(type="email" placeholder="Email address" maxlength="255" 'name'='username' '[(ngModel)]'='loginData.username')
.form-group
input#inputPassword.form-control(type="password" placeholder="Password" maxlength="255" 'name'='password' '[(ngModel)]'='loginData.password')
.col-sm-6
.radio-btn3
input#3.magic-radio(type="checkbox" name="bth" value="option")
label(for="3")
| Remember me
.col-sm-6
label
a('[routerLink]'='APP_URLS.forgetPassword') Forgotten your password?
.form-group
.text-right
button.btn_submit.pull-right.login(type="submit" '*ngIf'='!(loading || showLoader)') Log in
button.btn_submit.pull-right.login(type="button" '*ngIf'='loading || showLoader') Please wait!
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {ToastsManager} from 'ng2-toastr/ng2-toastr';
import {UserService} from '../user.service';
import {Constants} from '../../constants';
@Component({
selector: 'app-login',
templateUrl: './login.component.pug',
styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {
title = 'User Login | Knauf Specification Author';
APP_URLS = Constants.APP_URLS;
loginData = {
username: '',
password: ''
};
loading = false;
showLoader = false;
constructor(private titleService: Title,
private router: Router,
private user: UserService,
private toaster: ToastsManager) {
this.titleService.setTitle(this.title);
}
ngOnInit() {
this.tryAutoLogin();
}
validateLoginForm() {
if (!this.loginData.username) {
return false;
}
if (!this.loginData.password) {
return false;
}
return true;
}
login() {
if (!this.validateLoginForm()) {
this.toaster.error('Username and/or password missing.');
return;
}
this.loading = true;
this.user.login(this.loginData.username, this.loginData.password).subscribe(data => {
this.loading = false;
this.toaster.success('Login Successful!');
this.router.navigate([this.APP_URLS.dashboard]);
}, error => {
error = error.json();
if (error.message.indexOf('security requirements') > -1 && error.token) {
this.router.navigate([this.APP_URLS.newPassword + '/' + error.token + '/' + this.user.PASSWORD_STRENGTH]);
}
this.loading = false;
});
}
tryAutoLogin() {
this.showLoader = true;
this.user.tryAutoLogin().subscribe(data => {
this.showLoader = false;
this.router.navigate([this.APP_URLS.dashboard]);
}, err => {
this.showLoader = false;
});
}
}
@import "../../../assets/css/magic-check_login";
.clear {
clear: both;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
bottom: 3px;
width: 100%;
}
.color {
color: #009EE2 !important;
font-weight: bold;
}
.form-control::-webkit-input-placeholder {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
}
.logo-img {
display: inline-block;
padding: 12% 0;
}
.login_form {
position: relative;
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 500px;
padding: 10px 45px;
}
.specification_Author {
font-size: 18px;
font-family: FuturaPT;
color: rgb(0, 158, 226);
line-height: 1.2;
margin-left: 4%;
display: inline-block;
margin-top: 23.5%;
position: absolute;
font-weight: 200;
}
.form-control {
padding: 24px 12px;
border-radius: 0;
border: 1px solid #bcb5ad;
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 15%;
background-position: 60px 10px;
}
.col-lg-6 {
padding-right: 0;
}
.radio-btn3 {
float: left;
}
.magic-radio + label, .magic-checkbox + label {
padding-left: 10px !important;
margin-top: -1%;
padding-bottom: 23%;
}
.magic-radio + label:before, .magic-checkbox + label:before {
top: 15px !important;
left: -13px !important;
}
.magic-radio + label:after {
top: 18px !important;
left: -10px !important;
}
#inputPassword {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
#inputEmai {
background-image: url(/assets/img/question.png) !important;
background-repeat: no-repeat;
background-size: 350%;
background-position: 47% 48%;
}
.col-lg-8, .col-md-6, .col-sm-10, .col-xs-12 {
padding-right: 0;
}
.footer {
min-height: 0;
}
.no-backgroud{
background-image: none;
}
label {
font-size: 15px;
font-family: MrEavesModOT;
color: rgb(178, 178, 178);
line-height: 3.02;
font-weight: 300;
/*padding-bottom: 5%;
text-align: left;*/
}
@media (min-width: 992px) and (max-width: 1900px) {
.lastP {
width: 90%;
text-align: center !important;
margin-left: 9%;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.logo-img {
width: 120%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (min-width: 560px) and (max-width: 767px) {
.logo-img {
width: 110%;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 736px) {
body {
overflow-x: hidden;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 10px;
width: 100%;
}
}
@media (min-width: 480px) and (max-width: 559px) {
.logo-img {
width: 120%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
@media (max-width: 414px) {
.login_form {
background-color: #ffffff;
border-top: 6px solid #009ee2;
vertical-align: middle;
padding: 30px;
width: 100% !important;
padding: 10px 45px;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 58px 6px !important;
}
.lastP {
margin-left: 3%;
color: #989898;
text-align: center;
position: absolute;
top: 90px !important;
width: 100%;
}
}
@media (max-width: 320px) {
button {
background-position: 53px 6px !important;
}
}
@media (min-width: 320px) and (max-width: 479px) {
.logo-img {
width: 150%;
}
.form-control {
padding: 5% 9%;
}
button {
font-size: 13px;
padding: 1.5% 0;
padding-left: 5%;
padding-right: 6%;
background-position: 51px 5px;
}
.lastP {
width: 90%;
text-align: center !important;
}
}
.message{
font-weight: bold;
color: #666;
}
\ No newline at end of file
form.text-center('name'='newPasswordForm' '(ngSubmit)'="updatePassword()")
.login_form
.row
.col-lg-12
.text-center.col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-8.col-xs-offset-2
img.img-responsive.logo-img(src="/assets/img/logo.png" alt="knauf_logo")
.col-lg-12.message(*ngIf='messages[reason]') {{messages[reason]}}
.form-group
input#inputEmai.form-control.question(type="password" placeholder='Enter new password.' 'name'='password' '[(ngModel)]'='data.password')
.form-group
input#inputPassword.form-control(type="password" placeholder='Re-Enter new password.' 'name'='cpassword' '[(ngModel)]'='data.cpassword')
.col-sm-12
label &nbsp;
.form-group
.text-right
button.btn_submit.pull-right.login(type="submit" '*ngIf'='!(showLoader)') Update
button.btn_submit.pull-right.login(type="button" '*ngIf'='showLoader') Please wait!
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NewPasswordComponent } from './new-password.component';
describe('NewPasswordComponent', () => {
let component: NewPasswordComponent;
let fixture: ComponentFixture<NewPasswordComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NewPasswordComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NewPasswordComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {ToastsManager} from 'ng2-toastr/ng2-toastr';
import {UserService} from '../user.service';
import {Constants} from '../../constants';
import {Title} from '@angular/platform-browser';
@Component({
selector: 'app-new-password',
templateUrl: './new-password.component.pug',
styleUrls: ['./new-password.component.less']
})
export class NewPasswordComponent implements OnInit, OnDestroy {
title = 'Recover Password | Knauf Specification Author';
data = {
password: '',
cpassword: ''
};
query;
token;
APP_URLS = Constants.APP_URLS;
messages = [];
reason;
showLoader = false;
constructor(private titleService: Title,
private route: ActivatedRoute,
private user: UserService,
private router: Router,
private toaster: ToastsManager) {
this.titleService.setTitle(this.title);
this.messages[this.user.CHANGE_REQUESTED] = '';
this.messages[this.user.PASSWORD_EXPIRED] = 'Your password has expired and should be changed.';
this.messages[this.user.PASSWORD_STRENGTH] = 'Your password doesn\'t meet the security requirements and should be changed.';
}
ngOnInit() {
this.query = this.route.params.subscribe(params => {
console.log(params);
this.token = params['token'];
this.reason = params['reason'] || this.user.CHANGE_REQUESTED;
});
}
ngOnDestroy() {
this.query.unsubscribe();
}
updatePassword() {
this.showLoader = true;
const validation = this.user.validatePassword(this.data.password);
if (!validation.valid) {
this.toaster.error(validation.message);
return;
}
if (this.data.password !== this.data.cpassword) {
this.toaster.error('Password & confirm password must match');
return;
}
this.user.updatePassword(this.token, this.data.password).subscribe(data => {
this.showLoader = false;
this.toaster.success('Password updated successfully');
this.router.navigate([this.APP_URLS.login]);
}, err => {
this.showLoader = false;
});
}
}
import { TestBed, inject } from '@angular/core/testing';
import { UserService } from './user.service';
describe('UserService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService]
});
});
it('should be created', inject([UserService], (service: UserService) => {
expect(service).toBeTruthy();
}));
});
import {Injectable} from '@angular/core';
import {HttpService} from '../app-services/http.service';
import {LocalStoreService} from '../app-services/local-store.service';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
@Injectable()
export class UserService {
public id;
public user_name;
public user_mail;
public contact;
public user_desig;
public user_region;
public user_terms;
public attempt;
public user_role;
public avatar;
public tooltip;
public token;
public token_expire;
public CHANGE_REQUESTED = 1;
public PASSWORD_EXPIRED = 2;
public PASSWORD_STRENGTH = 3;
private passwordValidations = [
{message: 'be at least 8 characters', rule: /^.{8}/},
{message: 'be at most 255 characters', rule: /^.{0,255}$/},
{message: 'contain at least one lowercase letter', rule: /[a-z]/},
{message: 'contain at least one uppercase letter', rule: /[A-Z]/},
{message: 'contain at least one number', rule: /[0-9]/}
];
private LOGIN = 'api-users/dologin';
private VERIFY = 'api-users/verifytoken';
private FORGOT = 'user/send-password';
private UPDATE_PASSWORD = 'api-users/new-password';
private ALIVE = 'api-users/alive';
private LOGOUT = 'api-users/logout';
private password;
private keepAliveTime = 160;
private pollingIntervalId;
private emitSessionDestroyed = new Subject<any>();
onSessionDestroyed = this.emitSessionDestroyed.asObservable();
constructor(private http: HttpService,
private localStoreService: LocalStoreService) {
}
isAuthenticated($checkFromServer = false) {
if (this.id) {
return true;
}
}
validatePassword(password) {
let valid = true;
let message = 'Password must ';
this.passwordValidations.forEach(data => {
if (data.rule.test(password) === false) {
valid = false;
message += data.message + ', ';
}
});
message = message.substr(0, message.length - 2);
message += '.';
return {
valid,
message
};
}
populate(data) {
const keys = Object.keys(data);
keys.forEach(key => {
this[key] = data[key];
});
}
sendPassword(email_address) {
const postData = {
email_address
};
const promise = this.http.post(this.FORGOT, postData);
return promise;
}
updatePassword(token, password) {
const postData = {
password,
token
};
return this.http.post(this.UPDATE_PASSWORD, postData);
}
alive() {
const showErrors = {
e401: false,
};
return this.http.get(this.ALIVE, '', showErrors).subscribe(data => {
if (data.active === false) {
this.emitSessionDestroyed.next('Session Destroyed');
if (this.pollingIntervalId) {
clearInterval(this.pollingIntervalId);
}
}
}, er => {
this.emitSessionDestroyed.next('Session Destroyed');
if (this.pollingIntervalId) {
clearInterval(this.pollingIntervalId);
}
});
}
startAlivePolling() {
this.pollingIntervalId = setInterval(() => {
this.alive();
}, this.keepAliveTime * 1000);
}
login(username, password) {
const postData = {
username,
password
};
const showErrors = {
e401: true
};
const promise = this.http.post(this.LOGIN, postData, showErrors);
promise.subscribe(data => {
this.http.setTokken(data.access_token);
this.populate(data.user);
this.localStoreService.set('access_token', data.access_token);
this.startAlivePolling();
}, error => {
});
return promise;
}
logout() {
const data = {
e401: false
};
if (this.pollingIntervalId) {
clearInterval(this.pollingIntervalId);
}
return this.http.get(this.LOGOUT, '', data);
}
tryAutoLogin() {
return new Observable(observer => {
const access_token = this.localStoreService.get('access_token');
if (access_token) {
const showErrors = {
e401: false
};
this.http.setTokken(access_token);
this.http.get(this.VERIFY, '', showErrors).subscribe(data => {
if (data.verified) {
this.populate(data.user);
observer.next(data.user);
observer.complete();
this.startAlivePolling();
} else {
this.localStoreService.remove('access_token');
observer.error('Unable to veryfy access_token');
}
}, err => {
this.localStoreService.remove('access_token');
observer.error('Unable to veryfy access_token');
});
} else {
observer.error('No access_token found');
}
});
}
}
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
import {LocalStoreService} from '../app-services/local-store.service';
import {LoginComponent} from './login/login.component';
import {UserService} from './user.service';
import {LoaderComponent} from '../shared/loader/loader.component';
import { ForgotComponent } from './forgot/forgot.component';
import { NewPasswordComponent } from './new-password/new-password.component';
@NgModule({
imports: [
CommonModule,
FormsModule,
RouterModule
],
providers: [
UserService,
LocalStoreService
],
declarations: [LoginComponent, LoaderComponent, ForgotComponent, NewPasswordComponent]
})
export class UsersModule {
}
\ No newline at end of file
@keyframes hover-color {
from {
border-color: #c0c0c0; }
to {
border-color: #3e97eb; } }
.magic-radio,
.magic-checkbox {
position: absolute;
display: none; }
.magic-radio[disabled],
.magic-checkbox[disabled] {
cursor: not-allowed; }
.magic-radio + label,
.magic-checkbox + label {
position: relative;
display: block;
padding-left: 30px;
cursor: pointer;
vertical-align: middle; }
.magic-radio + label:hover:before,
.magic-checkbox + label:hover:before {
animation-duration: 0.4s;
animation-fill-mode: both;
animation-name: hover-color; }
.magic-radio + label:before,
.magic-checkbox + label:before {
position: absolute;
top: 4px;
left: 2px;
display: inline-block;
width: 16px;
height: 16px;
content: '';
border: 1px solid #c0c0c0;
}
.magic-radio + label:after,
.magic-checkbox + label:after {
position: absolute;
display: none;
content: ''; }
.magic-radio[disabled] + label,
.magic-checkbox[disabled] + label {
cursor: not-allowed;
color: #e4e4e4; }
.magic-radio[disabled] + label:hover, .magic-radio[disabled] + label:before, .magic-radio[disabled] + label:after,
.magic-checkbox[disabled] + label:hover,
.magic-checkbox[disabled] + label:before,
.magic-checkbox[disabled] + label:after {
cursor: not-allowed; }
.magic-radio[disabled] + label:hover:before,
.magic-checkbox[disabled] + label:hover:before {
border: 1px solid #e4e4e4;
animation-name: none; }
.magic-radio[disabled] + label:before,
.magic-checkbox[disabled] + label:before {
border-color: #e4e4e4; }
.magic-radio:checked + label:before,
.magic-checkbox:checked + label:before {
animation-name: none; }
.magic-radio:checked + label:after,
.magic-checkbox:checked + label:after {
display: block; }
.magic-radio + label:before {
border-radius: 50%; }
.magic-radio + label:after {
top: 7px;
left: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #3e97eb;
}
.magic-radio:checked + label:before {
border: 1px solid #3e97eb; }
.magic-radio:checked[disabled] + label:before {
border: 1px solid #c9e2f9; }
.magic-radio:checked[disabled] + label:after {
background: #c9e2f9; }
.magic-checkbox + label:before {
border-radius: 3px; }
.magic-checkbox + label:after {
top: 2px;
left: 7px;
box-sizing: border-box;
width: 6px;
height: 12px;
transform: rotate(45deg);
border-width: 2px;
border-style: solid;
border-color: #fff;
border-top: 0;
border-left: 0; }
.magic-checkbox:checked + label:before {
border: #3e97eb;
background: #3e97eb; }
.magic-checkbox:checked[disabled] + label:before {
border: #c9e2f9;
background: #c9e2f9; }
<svg id="Слой_1" data-name="Слой 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10405.38 6166.66"><defs><style>.cls-1{fill:#45a2d1;}</style></defs><title>Монтажная область 4</title><path class="cls-1" d="M6894.6,1353.66h786.21c-20.28,138.69-43.06,289.61-62.58,418.91q-153.1,1014.2-306.65,2028.34c-30.88,204.32-60.4,408.85-92.62,613-17.37,110,11.17,166.33,99.46,183.67,126.76,24.9,213.54-23.09,230.14-133.09,39.54-262,75.63-524.52,114.07-786.69q93.9-640.39,189.08-1280.6,72.79-490.8,146.56-981.46c3.09-20.6,7.84-40.95,11.94-62h735.53c-23.07,157.94-45.17,311-67.81,463.95q-139.27,941.28-278.74,1882.53c-38.47,260.3-67.14,522.44-116.48,780.65C8220,4809,8010.41,5015.67,7695.65,5112.22c-257.24,78.91-520,79.09-780.16,12.56-349.18-89.28-516.77-363.05-459.94-719.94,65-407.87,119.94-817.32,179.65-1226Q6737,2481.89,6839.39,1785.06C6859.41,1648,6875.37,1489.37,6894.6,1353.66Z"/><polygon class="cls-1" points="752.03 0 1526.85 0 1155.39 2705.03 1645.35 1369.61 2370.04 1367.33 1693.21 3071.93 1923.38 4972.52 1118.93 4972.52 1036.89 3463.9 783.94 4972.52 0 4972.52 752.03 0"/><polygon class="cls-1" points="2695.35 1353.66 3510.62 1353.66 3611.46 2707.31 3838.78 1353.66 4512.19 1353.66 3990.89 4948.02 3184.17 4939.48 3083.33 3361.92 2823.53 4972.52 2139.87 4972.52 2695.35 1353.66"/><path class="cls-1" d="M5336,1353.66,4204.54,4972.52H5001L5182.18,4225l259.79,6.84-30.76,740.64h728.1l153.82-3618.87ZM5571.87,3685H5274.47L5701.76,2071.5Z"/><polygon class="cls-1" points="9092.74 1316.06 10405.38 1316.06 10330.17 1893.75 9837.94 1893.75 9749.06 2683.38 10217.37 2683.38 10138.75 3254.24 9649.93 3254.24 9226.06 6166.66 8361.22 6166.66 9092.74 1316.06"/></svg>
\ No newline at end of file
......@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>SPA</title>
<title>Knauf Specification Author</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
......
/* You can add global styles to this file, and also import other style files */
@font-face {
font-family: FuturaICGLight;
src: url(/assets/fonts/Futura_ICG_Light.ttf);
}
@font-face {
font-family: FuturaPT;
src: url(/assets/fonts/FuturaPTBook.otf);
}
@font-face {
font-family: MrEavesModOT;
src: url(/assets/fonts/MrEavesModOT-Reg.otf);
}
@font-face {
font-family: MrEavesModOTLight;
src: url(/assets/fonts/FuturaPTLight.otf);
}
.lastP {
margin-left: -1.5%;
margin-top: 4.5%;
color: #989898;
}
.color {
color: #009EE2;
font-weight: bold;
}
.loaderAbsHolder{
position: absolute;
width: 100%;
left: 0px;
top: 0px;
height: 100%;
z-index: 1000;
background: rgba(0,0,0,0.2);
}
button {
&.login{
background-image: url("/assets/img/right.png");
background-repeat: no-repeat;
border: none;
outline: none;
font-size: 15px;
font-family: FuturaPT;
color: rgb(255, 255, 255);
line-height: 1.2;
background-color: #009ee2;
padding: 1.6% 0;
padding-left: 4%;
padding-right: 4%;
letter-spacing: 0.9px;
font-weight: 500;
background-size: 13%;
background-position: calc(100% - 4px);
}
}
.modal{
background: rgba(0,0,0,0.4);
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment