mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-20 09:15:19 +01:00
Merge pull request #1361 from vmware/feature/sign_in_migration
Implement user sign-in component with clarity and Angular2
This commit is contained in:
commit
d689784d7f
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
coverage/
|
||||
dist/
|
||||
html-report/
|
||||
node_modules/
|
||||
typings/
|
||||
**/*npm-debug.log.*
|
||||
**/*yarn-error.log.*
|
||||
.idea/
|
||||
.DS_Store
|
||||
proxy.config.json
|
||||
*.log
|
||||
**/*.log
|
@ -1,58 +1,58 @@
|
||||
{
|
||||
"name": "clarity-seed",
|
||||
"version": "0.8.0",
|
||||
"description": "Angular-CLI starter for a Clarity project",
|
||||
"angular-cli": {},
|
||||
"scripts": {
|
||||
"start": "ng serve --host 0.0.0.0",
|
||||
"lint": "tslint \"src/**/*.ts\"",
|
||||
"test": "ng test --single-run",
|
||||
"pree2e": "webdriver-manager update",
|
||||
"e2e": "protractor"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^2.4.1",
|
||||
"@angular/compiler": "^2.4.1",
|
||||
"@angular/core": "^2.4.1",
|
||||
"@angular/forms": "^2.4.1",
|
||||
"@angular/http": "^2.4.1",
|
||||
"@angular/platform-browser": "^2.4.1",
|
||||
"@angular/platform-browser-dynamic": "^2.4.1",
|
||||
"@angular/router": "^3.4.1",
|
||||
"@webcomponents/custom-elements": "1.0.0-alpha.3",
|
||||
"clarity-angular": "^0.8.0",
|
||||
"clarity-icons": "^0.8.0",
|
||||
"clarity-ui": "^0.8.0",
|
||||
"core-js": "^2.4.1",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"rxjs": "^5.0.1",
|
||||
"ts-helpers": "^1.1.1",
|
||||
"web-animations-js": "^2.2.1",
|
||||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^2.4.1",
|
||||
"@types/core-js": "^0.9.34",
|
||||
"@types/jasmine": "^2.2.30",
|
||||
"@types/node": "^6.0.42",
|
||||
"angular-cli": "^1.0.0-beta.24",
|
||||
"bootstrap": "4.0.0-alpha.5",
|
||||
"codelyzer": "~1.0.0-beta.3",
|
||||
"enhanced-resolve": "^3.0.0",
|
||||
"jasmine-core": "2.4.1",
|
||||
"jasmine-spec-reporter": "2.5.0",
|
||||
"karma": "1.2.0",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-mocha-reporter": "^2.2.1",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-remap-istanbul": "^0.2.1",
|
||||
"protractor": "4.0.9",
|
||||
"ts-node": "1.2.1",
|
||||
"tslint": "^4.1.1",
|
||||
"typescript": "~2.0.3",
|
||||
"typings": "^1.4.0",
|
||||
"webdriver-manager": "10.2.5"
|
||||
}
|
||||
}
|
||||
"name": "clarity-seed",
|
||||
"version": "0.8.0",
|
||||
"description": "Angular-CLI starter for a Clarity project",
|
||||
"angular-cli": {},
|
||||
"scripts": {
|
||||
"start": "ng serve --host 0.0.0.0 --proxy-config proxy.config.json",
|
||||
"lint": "tslint \"src/**/*.ts\"",
|
||||
"test": "ng test --single-run",
|
||||
"pree2e": "webdriver-manager update",
|
||||
"e2e": "protractor"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^2.4.1",
|
||||
"@angular/compiler": "^2.4.1",
|
||||
"@angular/core": "^2.4.1",
|
||||
"@angular/forms": "^2.4.1",
|
||||
"@angular/http": "^2.4.1",
|
||||
"@angular/platform-browser": "^2.4.1",
|
||||
"@angular/platform-browser-dynamic": "^2.4.1",
|
||||
"@angular/router": "^3.4.1",
|
||||
"@webcomponents/custom-elements": "1.0.0-alpha.3",
|
||||
"clarity-angular": "^0.8.0",
|
||||
"clarity-icons": "^0.8.0",
|
||||
"clarity-ui": "^0.8.0",
|
||||
"core-js": "^2.4.1",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"rxjs": "^5.0.1",
|
||||
"ts-helpers": "^1.1.1",
|
||||
"web-animations-js": "^2.2.1",
|
||||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^2.4.1",
|
||||
"@types/core-js": "^0.9.34",
|
||||
"@types/jasmine": "^2.2.30",
|
||||
"@types/node": "^6.0.42",
|
||||
"angular-cli": "^1.0.0-beta.24",
|
||||
"bootstrap": "4.0.0-alpha.5",
|
||||
"codelyzer": "~1.0.0-beta.3",
|
||||
"enhanced-resolve": "^3.0.0",
|
||||
"jasmine-core": "2.4.1",
|
||||
"jasmine-spec-reporter": "2.5.0",
|
||||
"karma": "1.2.0",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-mocha-reporter": "^2.2.1",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-remap-istanbul": "^0.2.1",
|
||||
"protractor": "4.0.9",
|
||||
"ts-node": "1.2.1",
|
||||
"tslint": "^4.1.1",
|
||||
"typescript": "~2.0.3",
|
||||
"typings": "^1.4.0",
|
||||
"webdriver-manager": "10.2.5"
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SignInComponent } from './sign-in.component';
|
||||
import { SignInComponent } from './sign-in/sign-in.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
<div class="login-wrapper">
|
||||
<form class="login">
|
||||
<label class="title">
|
||||
VMware Harbor<span class="trademark">™</span>
|
||||
</label>
|
||||
<div class="login-group">
|
||||
<div class="auth-source select">
|
||||
<select id="login-auth-source-1">
|
||||
<option>Local Users</option>
|
||||
<option>Administrator</option>
|
||||
</select>
|
||||
</div>
|
||||
<input class="username" type="text" id="login_username" placeholder="Username">
|
||||
<input class="password" type="password" id="login_password" placeholder="Password">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="rememberme">
|
||||
<label for="rememberme">
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
<div class="error active">
|
||||
Invalid user name or password
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" [routerLink]="['/harbor', 'dashboard']">LOG IN</button>
|
||||
<a href="..." class="signup">Sign up for an account</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'sign-in',
|
||||
templateUrl: "sign-in.component.html"
|
||||
})
|
||||
export class SignInComponent {
|
||||
// constructor(private router: Router){}
|
||||
}
|
15
harbor-app/src/app/account/sign-in/sign-in-credential.ts
Normal file
15
harbor-app/src/app/account/sign-in/sign-in-credential.ts
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* Declare class for store the sign in data,
|
||||
* two prperties:
|
||||
* principal: The username used to sign in
|
||||
* password: The password used to sign in
|
||||
*
|
||||
* @export
|
||||
* @class SignInCredential
|
||||
*/
|
||||
|
||||
export class SignInCredential {
|
||||
principal: string;
|
||||
password: string;
|
||||
}
|
7
harbor-app/src/app/account/sign-in/sign-in.component.css
Normal file
7
harbor-app/src/app/account/sign-in/sign-in.component.css
Normal file
@ -0,0 +1,7 @@
|
||||
.progress-size-small {
|
||||
height: 0.5em !important;
|
||||
}
|
||||
|
||||
.visibility-hidden {
|
||||
visibility: hidden;
|
||||
}
|
39
harbor-app/src/app/account/sign-in/sign-in.component.html
Normal file
39
harbor-app/src/app/account/sign-in/sign-in.component.html
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="login-wrapper">
|
||||
<form #signInForm="ngForm" class="login">
|
||||
<label class="title">
|
||||
VMware Harbor<span class="trademark">™</span>
|
||||
</label>
|
||||
<div class="login-group">
|
||||
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="userNameInput.invalid && (userNameInput.dirty || userNameInput.touched)">
|
||||
<input class="username" type="text" required
|
||||
[(ngModel)]="signInCredential.principal"
|
||||
name="login_username" id="login_username" placeholder="Username"
|
||||
#userNameInput='ngModel'>
|
||||
<span class="tooltip-content">
|
||||
Username is required!
|
||||
</span>
|
||||
</label>
|
||||
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="passwordInput.invalid && (passwordInput.dirty || passwordInput.touched)">
|
||||
<input class="password" type="password" required
|
||||
[(ngModel)]="signInCredential.password"
|
||||
name="login_password" id="login_password" placeholder="Password"
|
||||
#passwordInput="ngModel">
|
||||
<span class="tooltip-content">
|
||||
Password is required!
|
||||
</span>
|
||||
</label>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="rememberme">
|
||||
<label for="rememberme">
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
<div [class.visibility-hidden]="signInStatus != statusError" class="error active">
|
||||
Invalid user name or password
|
||||
</div>
|
||||
<button [class.visibility-hidden]="signInStatus === statusOnGoing" [disabled]="signInStatus === statusOnGoing" type="submit" class="btn btn-primary" (click)="signIn()">LOG IN</button>
|
||||
<div [class.visibility-hidden]="signInStatus != statusOnGoing" class="progress loop progress-size-small"><progress></progress></div>
|
||||
<a href="javascript:void(0)" class="signup" (click)="signUp()">Sign up for an account</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
121
harbor-app/src/app/account/sign-in/sign-in.component.ts
Normal file
121
harbor-app/src/app/account/sign-in/sign-in.component.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Input, ViewChild, AfterViewChecked } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { SignInService } from './sign-in.service';
|
||||
import { SignInCredential } from './sign-in-credential'
|
||||
|
||||
//Define status flags for signing in states
|
||||
export const signInStatusNormal = 0;
|
||||
export const signInStatusOnGoing = 1;
|
||||
export const signInStatusError = -1;
|
||||
|
||||
@Component({
|
||||
selector: 'sign-in',
|
||||
templateUrl: "sign-in.component.html",
|
||||
styleUrls: ['sign-in.component.css'],
|
||||
|
||||
providers: [SignInService]
|
||||
})
|
||||
|
||||
export class SignInComponent implements AfterViewChecked {
|
||||
//Form reference
|
||||
signInForm: NgForm;
|
||||
@ViewChild('signInForm') currentForm: NgForm;
|
||||
|
||||
//Status flag
|
||||
signInStatus: number = 0;
|
||||
|
||||
//Initialize sign in credential
|
||||
@Input() signInCredential: SignInCredential = {
|
||||
principal: "",
|
||||
password: ""
|
||||
};
|
||||
|
||||
constructor(
|
||||
private signInService: SignInService,
|
||||
private router: Router
|
||||
) { }
|
||||
|
||||
//For template accessing
|
||||
get statusError(): number {
|
||||
return signInStatusError;
|
||||
}
|
||||
|
||||
get statusOnGoing(): number {
|
||||
return signInStatusOnGoing;
|
||||
}
|
||||
|
||||
//Validate the related fields
|
||||
private validate(): boolean {
|
||||
return true;
|
||||
//return this.signInForm.valid;
|
||||
}
|
||||
|
||||
//Hande form values changes
|
||||
private formChanged() {
|
||||
if (this.currentForm === this.signInForm) {
|
||||
return;
|
||||
}
|
||||
this.signInForm = this.currentForm;
|
||||
if (this.signInForm) {
|
||||
this.signInForm.valueChanges
|
||||
.subscribe(data => {
|
||||
this.updateState();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Implement interface
|
||||
//Watch the view change only when view is in error state
|
||||
ngAfterViewChecked() {
|
||||
if (this.signInStatus === signInStatusError) {
|
||||
this.formChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//Update the status if we have done some changes
|
||||
updateState(): void {
|
||||
if (this.signInStatus === signInStatusError) {
|
||||
this.signInStatus = signInStatusNormal; //reset
|
||||
}
|
||||
}
|
||||
|
||||
//Trigger the signin action
|
||||
signIn(): void {
|
||||
//Should validate input firstly
|
||||
if (!this.validate()) {
|
||||
console.info("return");
|
||||
return;
|
||||
}
|
||||
|
||||
//Start signing in progress
|
||||
this.signInStatus = signInStatusOnGoing;
|
||||
|
||||
//Call the service to send out the http request
|
||||
this.signInService.signIn(this.signInCredential)
|
||||
.then(() => {
|
||||
//Set status
|
||||
this.signInStatus = signInStatusNormal;
|
||||
|
||||
//Routing to the right location
|
||||
let nextRoute = ["/harbor", "dashboard"];
|
||||
this.router.navigate(nextRoute);
|
||||
})
|
||||
.catch(error => {
|
||||
//Set error status
|
||||
this.signInStatus = signInStatusError;
|
||||
|
||||
let message = error.status ? error.status + ":" + error.statusText : error;
|
||||
console.error("An error occurred when signing in:", message);
|
||||
});
|
||||
}
|
||||
|
||||
//Help user navigate to the sign up
|
||||
signUp(): void {
|
||||
let nextRoute = ["/harbor", "signup"];
|
||||
this.router.navigate(nextRoute);
|
||||
}
|
||||
}
|
41
harbor-app/src/app/account/sign-in/sign-in.service.ts
Normal file
41
harbor-app/src/app/account/sign-in/sign-in.service.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers, Http, URLSearchParams } from '@angular/http';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
|
||||
import { SignInCredential } from './sign-in-credential';
|
||||
|
||||
const signInUrl = '/login';
|
||||
/**
|
||||
*
|
||||
* Define a service to provide sign in methods
|
||||
*
|
||||
* @export
|
||||
* @class SignInService
|
||||
*/
|
||||
@Injectable()
|
||||
export class SignInService {
|
||||
private headers = new Headers({
|
||||
"Content-Type": 'application/x-www-form-urlencoded'
|
||||
});
|
||||
|
||||
constructor(private http: Http) {}
|
||||
|
||||
//Handle the related exceptions
|
||||
private handleError(error: any): Promise<any>{
|
||||
return Promise.reject(error.message || error);
|
||||
}
|
||||
|
||||
//Submit signin form to backend (NOT restful service)
|
||||
signIn(signInCredential: SignInCredential): Promise<any>{
|
||||
//Build the form package
|
||||
const body = new URLSearchParams();
|
||||
body.set('principal', signInCredential.principal);
|
||||
body.set('password', signInCredential.password);
|
||||
|
||||
//Trigger Http
|
||||
return this.http.post(signInUrl, body.toString(), { headers: this.headers })
|
||||
.toPromise()
|
||||
.then(()=>null)
|
||||
.catch(this.handleError);
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<p>Placeholder for signup</p>
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { SignInComponent } from './account/sign-in.component';
|
||||
import { SignInComponent } from './account/sign-in/sign-in.component';
|
||||
|
||||
const harborRoutes: Routes = [
|
||||
{ path: '', redirectTo: '/sign-in', pathMatch: 'full' },
|
||||
|
Loading…
Reference in New Issue
Block a user