Commit 3a621fab authored by kunw's avatar kunw
Browse files

Fixed some issues and merged latest codes.

parent 3c112f2a
#!/bin/bash
rm -rf dist/*
ng build
cp index.html dist/index.html
......@@ -29,6 +29,7 @@ func initRouters() {
beego.SetStaticPath("static/resources", "static/resources")
beego.SetStaticPath("static/vendors", "static/vendors")
beego.SetStaticPath("/ng", "static/dist")
beego.SetStaticPath("/ng/harbor", "static/dist")
beego.SetStaticPath("/ng/harbor/dashboard", "static/dist")
beego.SetStaticPath("/ng/harbor/projects", "static/dist")
......
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalSize]="'lg'">
<h3 class="modal-title">Accout Settings</h3>
<div class="modal-body">
<form #accountSettingsFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="account_settings_username" class="col-md-4">Username</label>
<input type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username" size="51">
</div>
<div class="form-group">
<label for="account_settings_email" class="col-md-4 required">Email</label>
<label for="account_settings_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-right" [class.invalid]="eamilInput.invalid && (eamilInput.dirty || eamilInput.touched)">
<input name="account_settings_email" type="text" #eamilInput="ngModel" [(ngModel)]="account.email"
required
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$' id="account_settings_email" size="48">
<span class="tooltip-content">
Email should be a valid email address like name@example.com
</span>
</label>
</div>
<div class="form-group">
<label for="account_settings_full_name" class="col-md-4 required">Full name</label>
<label for="account_settings_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-right" [class.invalid]="fullNameInput.invalid && (fullNameInput.dirty || fullNameInput.touched)">
<input type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" required maxLength="20" maxLengthExt="20" id="account_settings_full_name" size="48">
<span class="tooltip-content">
Max length of full name is 20
</span>
</label>
</div>
<div class="form-group">
<label for="account_settings_comments" class="col-md-4">Comments</label>
<input type="text" name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments" size="51">
</div>
</section>
</form>
<clr-alert [clrAlertType]="'alert-danger'" [clrAlertClosable]="true" [hidden]='errorMessage === ""'>
<div class="alert-item">
<span class="alert-text">
This alert indicates success.
</span>
</div>
</clr-alert>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
<button type="button" class="btn btn-outline" (click)="close()">Cancel</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="submit()">Ok</button>
</div>
</clr-modal>
\ No newline at end of file
import { Component, OnInit, ViewChild, AfterViewChecked } from '@angular/core';
import { NgForm } from '@angular/forms';
import { SessionUser } from '../../shared/session-user';
import { SessionService } from '../../shared/session.service';
@Component({
selector: "account-settings-modal",
templateUrl: "account-settings-modal.component.html"
})
export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
opened: boolean = false;
staticBackdrop: boolean = true;
account: SessionUser;
error: any;
private isOnCalling: boolean = false;
private formValueChanged: boolean = false;
accountFormRef: NgForm;
@ViewChild("accountSettingsFrom") accountForm: NgForm;
constructor(private session: SessionService) { }
ngOnInit(): void {
//Value copy
this.account = Object.assign({}, this.session.getCurrentUser());
}
public get isValid(): boolean {
return this.accountForm && this.accountForm.valid;
}
public get showProgress(): boolean {
return this.isOnCalling;
}
public get errorMessage(): string {
return this.error ? (this.error.message ? this.error.message : this.error) : "";
}
ngAfterViewChecked(): void {
if (this.accountFormRef != this.accountForm) {
this.accountFormRef = this.accountForm;
if (this.accountFormRef) {
this.accountFormRef.valueChanges.subscribe(data => {
if (this.error) {
this.error = null;
}
this.formValueChanged = true;
});
}
}
}
open() {
this.account = Object.assign({}, this.session.getCurrentUser());
this.formValueChanged = false;
this.opened = true;
}
close() {
this.opened = false;
}
submit() {
if (!this.isValid || this.isOnCalling) {
return;
}
//Double confirm session is valid
let cUser = this.session.getCurrentUser();
if (!cUser) {
return;
}
this.isOnCalling = true;
this.session.updateAccountSettings(this.account)
.then(() => {
this.isOnCalling = false;
this.close();
})
.catch(error => {
this.isOnCalling = false;
this.error = error
});
}
}
\ No newline at end of file
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { RouterModule } from '@angular/router';
import { CoreModule } from '../core/core.module';
import { SignInComponent } from './sign-in/sign-in.component';
import { PasswordSettingComponent } from './password/password-setting.component';
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
import { PasswordSettingService } from './password/password-setting.service';
@NgModule({
imports: [
SharedModule,
CoreModule,
RouterModule
],
declarations: [SignInComponent],
exports: [SignInComponent]
declarations: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
exports: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
providers: [PasswordSettingService]
})
export class AccountModule { }
\ No newline at end of file
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
<h3 class="modal-title">Change Password</h3>
<div class="modal-body" style="min-height: 250px; overflow-y: hidden;">
<form #changepwdForm="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="oldPassword">Current Password</label>
<label for="oldPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
<input type="password" id="oldPassword" placeholder="Enter current password"
required
name="oldPassword"
[(ngModel)]="oldPwd"
#oldPassInput="ngModel" size="25">
<span class="tooltip-content">
Current password is Required.
</span>
</label>
</div>
<div class="form-group">
<label for="newPassword">New Password</label>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="newPassInput.invalid && (newPassInput.dirty || newPassInput.touched)">
<input type="password" id="newPassword" placeholder="Enter new password"
required
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
name="newPassword"
[(ngModel)]="newPwd"
#newPassInput="ngModel" size="25">
<span class="tooltip-content">
Password should be at least 7 characters with 1 uppercase, 1 lowercase letter and 1 number
</span>
</label>
</div>
<div class="form-group">
<label for="reNewPassword">Confirm Password</label>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="(reNewPassInput.invalid && (reNewPassInput.dirty || reNewPassInput.touched)) || (!newPassInput.invalid && reNewPassInput.value != newPassInput.value)">
<input type="password" id="reNewPassword" placeholder="Confirm new password"
required
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
name="reNewPassword"
[(ngModel)]="reNewPwd"
#reNewPassInput="ngModel" size="25">
<span class="tooltip-content">
Password should be at least 7 characters with 1 uppercase, 1 lowercase letter and 1 number and same with new password
</span>
</label>
</div>
</section>
</form>
</div>
<div class="modal-footer">
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
<button type="button" class="btn btn-outline" (click)="close()">Cancel</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="doOk()">Ok</button>
</div>
</clr-modal>
\ No newline at end of file
import { Component, ViewChild, AfterViewChecked, Output, EventEmitter } from '@angular/core';
import { Router } from '@angular/router';
import { NgForm } from '@angular/forms';
import { PasswordSettingService } from './password-setting.service';
import { SessionService } from '../../shared/session.service';
@Component({
selector: 'password-setting',
templateUrl: "password-setting.component.html"
})
export class PasswordSettingComponent implements AfterViewChecked {
opened: boolean = false;
oldPwd: string = "";
newPwd: string = "";
reNewPwd: string = "";
private formValueChanged: boolean = false;
private onCalling: boolean = false;
pwdFormRef: NgForm;
@ViewChild("changepwdForm") pwdForm: NgForm;
@Output() private pwdChange = new EventEmitter<any>();
constructor(private passwordService: PasswordSettingService, private session: SessionService){}
//If form is valid
public get isValid(): boolean {
if (this.pwdForm && this.pwdForm.form.get("newPassword")) {
return this.pwdForm.valid &&
this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value;
}
return false;
}
public get valueChanged(): boolean {
return this.formValueChanged;
}
public get showProgress(): boolean {
return this.onCalling;
}
ngAfterViewChecked() {
if (this.pwdFormRef != this.pwdForm) {
this.pwdFormRef = this.pwdForm;
if (this.pwdFormRef) {
this.pwdFormRef.valueChanges.subscribe(data => {
this.formValueChanged = true;
});
}
}
}
//Open modal dialog
open(): void {
this.opened = true;
this.pwdForm.reset();
}
//Close the moal dialog
close(): void {
this.opened = false;
}
//handle the ok action
doOk(): void {
if (this.onCalling) {
return;//To avoid duplicate click events
}
if (!this.isValid) {
return;//Double confirm
}
//Double confirm session is valid
let cUser = this.session.getCurrentUser();
if(!cUser){
return;
}
//Call service
this.onCalling = true;
this.passwordService.changePassword(cUser.user_id,
{
new_password: this.pwdForm.value.newPassword,
old_password: this.pwdForm.value.oldPassword
})
.then(() => {
this.onCalling = false;
//Tell shell to reset current view
this.pwdChange.emit(true);
this.close();
})
.catch(error => {
this.onCalling = false;
console.error(error);//TODO:
});
//TODO:publish the successful message to general messae box
}
}
\ No newline at end of file
import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { PasswordSetting } from './password-setting';
const passwordChangeEndpoint = "/api/users/:user_id/password";
@Injectable()
export class PasswordSettingService {
private headers: Headers = new Headers({
"Accept": 'application/json',
"Content-Type": 'application/json'
});
private options: RequestOptions = new RequestOptions({
'headers': this.headers
});
constructor(private http: Http) { }
changePassword(userId: number, setting: PasswordSetting): Promise<any> {
if(!setting || setting.new_password.trim()==="" || setting.old_password.trim()===""){
return Promise.reject("Invalid data");
}
let putUrl = passwordChangeEndpoint.replace(":user_id", userId+"");
return this.http.put(putUrl, JSON.stringify(setting), this.options)
.toPromise()
.then(() => null)
.catch(error=>{
return Promise.reject(error);
});
}
}
/**
*
* Struct for password change
*
* @export
* @class PasswordSetting
*/
export class PasswordSetting {
old_password: string;
new_password: string;
}
\ No newline at end of file
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'reset-password',
templateUrl: "reset-password.component.html"
})
export class ResetPasswordComponent {
// constructor(private router: Router){}
}
\ No newline at end of file
......@@ -3,9 +3,8 @@ 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'
import { SessionService } from '../../shared/session.service';
import { SignInCredential } from '../../shared/sign-in-credential';
//Define status flags for signing in states
export const signInStatusNormal = 0;
......@@ -15,9 +14,7 @@ export const signInStatusError = -1;
@Component({
selector: 'sign-in',
templateUrl: "sign-in.component.html",
styleUrls: ['sign-in.component.css'],
providers: [SignInService]
styleUrls: ['sign-in.component.css']
})
export class SignInComponent implements AfterViewChecked {
......@@ -35,7 +32,6 @@ export class SignInComponent implements AfterViewChecked {
};
constructor(
private signInService: SignInService,
private router: Router,
private session: SessionService
) { }
......@@ -97,8 +93,7 @@ export class SignInComponent implements AfterViewChecked {
//Trigger the signin action
signIn(): void {
//Should validate input firstly
if (!this.validate()) {
console.info("return");
if (!this.validate() || this.signInStatus === signInStatusOnGoing) {
return;
}
......@@ -106,21 +101,25 @@ export class SignInComponent implements AfterViewChecked {
this.signInStatus = signInStatusOnGoing;
//Call the service to send out the http request
this.signInService.signIn(this.signInCredential)
this.session.signIn(this.signInCredential)
.then(() => {
//Set status
this.signInStatus = signInStatusNormal;
//Validate the sign-in session
this.session.retrieveUser()
.then(() => {
.then(user => {
//Routing to the right location
let nextRoute = ["/harbor", "dashboard"];
let nextRoute = ["/harbor", "projects"];
this.router.navigate(nextRoute);
})
.catch(this.handleError);
.catch(error => {
this.handleError(error);
});
})
.catch(this.handleError);
.catch(error => {
this.handleError(error);
});
}
//Help user navigate to the sign up
......
......@@ -4,7 +4,7 @@ import 'rxjs/add/operator/toPromise';
import { SignInCredential } from './sign-in-credential';
const url_prefix = '';
const url_prefix = '/ng';
const signInUrl = url_prefix + '/login';
/**
*
......
......@@ -4,25 +4,23 @@ import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { ClarityModule } from 'clarity-angular';
import { AppComponent } from './app.component';
import { AccountModule } from './account/account.module';
import { BaseModule } from './base/base.module';
import { HarborRoutingModule } from './harbor-routing.module';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
@NgModule({
declarations: [
AppComponent,
],
imports: [
CoreModule,
AccountModule,
SharedModule,
BaseModule,
HarborRoutingModule
],
providers: [],
bootstrap: [ AppComponent ]
bootstrap: [AppComponent]
})
export class AppModule {
}
import { Injectable } from '@angular/core';
import {
Router, Resolve, ActivatedRouteSnapshot, RouterStateSnapshot
} from '@angular/router';
import { SessionService } from '../shared/session.service';
import { SessionUser } from '../shared/session-user';
@Injectable()
export class BaseRoutingResolver implements Resolve<SessionUser> {
constructor(private session: SessionService, private router: Router) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SessionUser> {
return this.session.retrieveUser()
.then(sessionUser => {
return sessionUser;
})
.catch(error => {
console.info("Anonymous user");
});
}
}
\ No newline at end of file
......@@ -5,12 +5,21 @@ import { HarborShellComponent } from './harbor-shell/harbor-shell.component';
import { DashboardComponent } from '../dashboard/dashboard.component';
import { ProjectComponent } from '../project/project.component';
import { BaseRoutingResolver } from './base-routing-resolver.service';
const baseRoutes: Routes = [
{
path: 'harbor', component: HarborShellComponent,
{
path: 'harbor',
component: HarborShellComponent,
children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'projects', component: ProjectComponent }
{
path: 'dashboard',
component: DashboardComponent
},
{
path: 'projects',
component: ProjectComponent
}
]
}];
......@@ -18,7 +27,9 @@ const baseRoutes: Routes = [
imports: [