Commit 5619e4e7 authored by kunw's avatar kunw
Browse files

Added lastest UI codes.

parent 23f0ff1e
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalSize]="'lg'"> <clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalSize]="'lg'">
<h3 class="modal-title">Accout Settings</h3> <h3 class="modal-title">User Profile</h3>
<div class="modal-body"> <div class="modal-body" style="overflow-y: hidden;">
<form #accountSettingsFrom="ngForm" class="form"> <form #accountSettingsFrom="ngForm" class="form">
<section class="form-block"> <section class="form-block">
<div class="form-group"> <div class="form-group">
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<div class="form-group"> <div class="form-group">
<label for="account_settings_full_name" class="col-md-4 required">Full name</label> <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)"> <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"> <input type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" required maxLengthExt="20" id="account_settings_full_name" size="48">
<span class="tooltip-content"> <span class="tooltip-content">
Max length of full name is 20 Max length of full name is 20
</span> </span>
...@@ -29,14 +29,20 @@ ...@@ -29,14 +29,20 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="account_settings_comments" class="col-md-4">Comments</label> <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"> <label for="account_settings_comments" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-right" [class.invalid]="commentInput.invalid && (commentInput.dirty || commentInput.touched)">
<input type="text" #commentInput="ngModel" maxLengthExt="20" name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments" size="48">
<span class="tooltip-content">
Length of comment should be less than 20
</span>
</label>
</div> </div>
</section> </section>
</form> </form>
<clr-alert [clrAlertType]="'alert-danger'" [clrAlertClosable]="true" [hidden]='errorMessage === ""'> <div style="height: 30px;"></div>
<clr-alert [clrAlertType]="'alert-danger'" [clrAlertClosable]="true" [(clrAlertClosed)]="alertClose">
<div class="alert-item"> <div class="alert-item">
<span class="alert-text"> <span class="alert-text">
This alert indicates success. {{errorMessage}}
</span> </span>
</div> </div>
</clr-alert> </clr-alert>
......
...@@ -14,6 +14,7 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked { ...@@ -14,6 +14,7 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
staticBackdrop: boolean = true; staticBackdrop: boolean = true;
account: SessionUser; account: SessionUser;
error: any; error: any;
alertClose: boolean = true;
private isOnCalling: boolean = false; private isOnCalling: boolean = false;
private formValueChanged: boolean = false; private formValueChanged: boolean = false;
...@@ -37,7 +38,16 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked { ...@@ -37,7 +38,16 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
} }
public get errorMessage(): string { public get errorMessage(): string {
return this.error ? (this.error.message ? this.error.message : this.error) : ""; if(this.error){
if(this.error.message){
return this.error.message;
}else{
if(this.error._body){
return this.error._body;
}
}
}
return "";
} }
ngAfterViewChecked(): void { ngAfterViewChecked(): void {
...@@ -85,7 +95,8 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked { ...@@ -85,7 +95,8 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
}) })
.catch(error => { .catch(error => {
this.isOnCalling = false; this.isOnCalling = false;
this.error = error this.error = error;
this.alertClose = false;
}); });
} }
......
...@@ -5,13 +5,15 @@ import { CoreModule } from '../core/core.module'; ...@@ -5,13 +5,15 @@ import { CoreModule } from '../core/core.module';
import { SignInComponent } from './sign-in/sign-in.component'; import { SignInComponent } from './sign-in/sign-in.component';
import { PasswordSettingComponent } from './password/password-setting.component'; import { PasswordSettingComponent } from './password/password-setting.component';
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component'; import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
import { SharedModule } from '../shared/shared.module';
import { PasswordSettingService } from './password/password-setting.service'; import { PasswordSettingService } from './password/password-setting.service';
@NgModule({ @NgModule({
imports: [ imports: [
CoreModule, CoreModule,
RouterModule RouterModule,
SharedModule
], ],
declarations: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent], declarations: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
exports: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent], exports: [SignInComponent, PasswordSettingComponent, AccountSettingsModalComponent],
......
import { Component, ViewChild, AfterViewChecked, Output, EventEmitter } from '@angular/core'; import { Component, ViewChild, AfterViewChecked } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NgForm } from '@angular/forms'; import { NgForm } from '@angular/forms';
...@@ -20,9 +20,6 @@ export class PasswordSettingComponent implements AfterViewChecked { ...@@ -20,9 +20,6 @@ export class PasswordSettingComponent implements AfterViewChecked {
pwdFormRef: NgForm; pwdFormRef: NgForm;
@ViewChild("changepwdForm") pwdForm: NgForm; @ViewChild("changepwdForm") pwdForm: NgForm;
@Output() private pwdChange = new EventEmitter<any>();
constructor(private passwordService: PasswordSettingService, private session: SessionService){} constructor(private passwordService: PasswordSettingService, private session: SessionService){}
//If form is valid //If form is valid
...@@ -90,9 +87,6 @@ export class PasswordSettingComponent implements AfterViewChecked { ...@@ -90,9 +87,6 @@ export class PasswordSettingComponent implements AfterViewChecked {
}) })
.then(() => { .then(() => {
this.onCalling = false; this.onCalling = false;
//Tell shell to reset current view
this.pwdChange.emit(true);
this.close(); this.close();
}) })
.catch(error => { .catch(error => {
......
...@@ -31,8 +31,7 @@ ...@@ -31,8 +31,7 @@
<div [class.visibility-hidden]="signInStatus != statusError" class="error active"> <div [class.visibility-hidden]="signInStatus != statusError" class="error active">
Invalid user name or password Invalid user name or password
</div> </div>
<button [class.visibility-hidden]="signInStatus === statusOnGoing" [disabled]="signInStatus === statusOnGoing" type="submit" class="btn btn-primary" (click)="signIn()">LOG IN</button> <button [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> <a href="javascript:void(0)" class="signup" (click)="signUp()">Sign up for an account</a>
</div> </div>
</form> </form>
......
...@@ -6,9 +6,9 @@ import { ClarityModule } from 'clarity-angular'; ...@@ -6,9 +6,9 @@ import { ClarityModule } from 'clarity-angular';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { BaseModule } from './base/base.module'; import { BaseModule } from './base/base.module';
import { HarborRoutingModule } from './harbor-routing.module'; import { HarborRoutingModule } from './harbor-routing.module';
import { SharedModule } from './shared/shared.module'; import { SharedModule } from './shared/shared.module';
import { AccountModule } from './account/account.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
...@@ -17,6 +17,7 @@ import { SharedModule } from './shared/shared.module'; ...@@ -17,6 +17,7 @@ import { SharedModule } from './shared/shared.module';
imports: [ imports: [
SharedModule, SharedModule,
BaseModule, BaseModule,
AccountModule,
HarborRoutingModule HarborRoutingModule
], ],
providers: [], providers: [],
......
...@@ -4,6 +4,7 @@ import { HarborShellComponent } from './harbor-shell/harbor-shell.component'; ...@@ -4,6 +4,7 @@ import { HarborShellComponent } from './harbor-shell/harbor-shell.component';
import { DashboardComponent } from '../dashboard/dashboard.component'; import { DashboardComponent } from '../dashboard/dashboard.component';
import { ProjectComponent } from '../project/project.component'; import { ProjectComponent } from '../project/project.component';
import { UserComponent } from '../user/user.component';
import { BaseRoutingResolver } from './base-routing-resolver.service'; import { BaseRoutingResolver } from './base-routing-resolver.service';
...@@ -19,6 +20,13 @@ const baseRoutes: Routes = [ ...@@ -19,6 +20,13 @@ const baseRoutes: Routes = [
{ {
path: 'projects', path: 'projects',
component: ProjectComponent component: ProjectComponent
},
{
path: 'users',
component: UserComponent,
resolve: {
projectsResolver: BaseRoutingResolver
}
} }
] ]
}]; }];
......
...@@ -4,6 +4,7 @@ import { SharedModule } from '../shared/shared.module'; ...@@ -4,6 +4,7 @@ import { SharedModule } from '../shared/shared.module';
import { DashboardModule } from '../dashboard/dashboard.module'; import { DashboardModule } from '../dashboard/dashboard.module';
import { ProjectModule } from '../project/project.module'; import { ProjectModule } from '../project/project.module';
import { UserModule } from '../user/user.module'; import { UserModule } from '../user/user.module';
import { AccountModule } from '../account/account.module';
import { NavigatorComponent } from './navigator/navigator.component'; import { NavigatorComponent } from './navigator/navigator.component';
import { GlobalSearchComponent } from './global-search/global-search.component'; import { GlobalSearchComponent } from './global-search/global-search.component';
...@@ -19,7 +20,8 @@ import { BaseRoutingModule } from './base-routing.module'; ...@@ -19,7 +20,8 @@ import { BaseRoutingModule } from './base-routing.module';
DashboardModule, DashboardModule,
ProjectModule, ProjectModule,
UserModule, UserModule,
BaseRoutingModule BaseRoutingModule,
AccountModule
], ],
declarations: [ declarations: [
NavigatorComponent, NavigatorComponent,
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<input id="tabsystem" type="checkbox"> <input id="tabsystem" type="checkbox">
<label for="tabsystem">System Managements</label> <label for="tabsystem">System Managements</label>
<ul class="nav-list"> <ul class="nav-list">
<li><a class="nav-link">Users</a></li> <li><a class="nav-link" routerLink="/harbor/users" routerLinkActive="active">Users</a></li>
<li><a class="nav-link">Replications</a></li> <li><a class="nav-link">Replications</a></li>
<li><a class="nav-link">Quarantine[*]</a></li> <li><a class="nav-link">Quarantine[*]</a></li>
<li><a class="nav-link">Configurations[*]</a></li> <li><a class="nav-link">Configurations[*]</a></li>
...@@ -27,4 +27,4 @@ ...@@ -27,4 +27,4 @@
</div> </div>
</clr-main-container> </clr-main-container>
<account-settings-modal></account-settings-modal> <account-settings-modal></account-settings-modal>
<password-setting (pwdChange)="watchPwdChange($event)"></password-setting> <password-setting></password-setting>
\ No newline at end of file \ No newline at end of file
...@@ -77,11 +77,4 @@ export class HarborShellComponent implements OnInit { ...@@ -77,11 +77,4 @@ export class HarborShellComponent implements OnInit {
this.isSearchResultsOpened = false; this.isSearchResultsOpened = false;
} }
} }
//Watch password whether changed
watchPwdChange(event: any): void {
if (event) {
this.navigator.logOut(true);
}
}
} }
\ No newline at end of file
...@@ -12,19 +12,6 @@ ...@@ -12,19 +12,6 @@
<span class="custom-divider"></span> <span class="custom-divider"></span>
<a href="javascript:void(0)" class="nav-link nav-text sign-up-override" routerLink="/sign-up" routerLinkActive="active">Sign Up</a> <a href="javascript:void(0)" class="nav-link nav-text sign-up-override" routerLink="/sign-up" routerLinkActive="active">Sign Up</a>
</div> </div>
<clr-dropdown [clrMenuPosition]="'bottom-left'" class="dropdown" *ngIf="isSessionValid">
<button class="nav-text" clrDropdownToggle>
<clr-icon shape="user" class="is-inverse" size="24" style="left: -2px;"></clr-icon>
<span>Administrator</span>
<clr-icon shape="caret down"></clr-icon>
</button>
<div class="dropdown-menu">
<a href="javascript:void(0)" clrDropdownItem (click)="openAccountSettingsModal()">Account Settings</a>
<a href="javascript:void(0)" clrDropdownItem (click)="openChangePwdModal()">Change Password</a>
<div class="dropdown-divider"></div>
<a href="javascript:void(0)" clrDropdownItem (click)="logOut(false)">Log out</a>
</div>
</clr-dropdown>
<clr-dropdown class="dropdown bottom-left"> <clr-dropdown class="dropdown bottom-left">
<button class="nav-icon" clrDropdownToggle style="width: 90px;"> <button class="nav-icon" clrDropdownToggle style="width: 90px;">
<clr-icon shape="world" style="left:-5px;"></clr-icon> <clr-icon shape="world" style="left:-5px;"></clr-icon>
...@@ -37,15 +24,18 @@ ...@@ -37,15 +24,18 @@
<a href="javascript:void(0)" clrDropdownItem>中文繁體</a> <a href="javascript:void(0)" clrDropdownItem>中文繁體</a>
</div> </div>
</clr-dropdown> </clr-dropdown>
<clr-dropdown class="dropdown bottom-right"> <clr-dropdown [clrMenuPosition]="'bottom-right'" class="dropdown" *ngIf="isSessionValid">
<button class="nav-icon" clrDropdownToggle> <button class="nav-text" clrDropdownToggle>
<clr-icon shape="cog"></clr-icon> <clr-icon shape="user" class="is-inverse" size="24" style="left: -2px;"></clr-icon>
<clr-icon shape="caret down"></clr-icon> <span>{{accountName}}</span>
<clr-icon shape="caret down"></clr-icon>
</button> </button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a href="javascript:void(0)" clrDropdownItem>Preferences</a> <a href="javascript:void(0)" clrDropdownItem (click)="openAccountSettingsModal()">User Profile</a>
<a href="javascript:void(0)" clrDropdownItem>Add User</a> <a href="javascript:void(0)" clrDropdownItem (click)="openChangePwdModal()">Change Password</a>
<a href="javascript:void(0)" clrDropdownItem>About</a> <a href="javascript:void(0)" clrDropdownItem>About</a>
<div class="dropdown-divider"></div>
<a href="javascript:void(0)" clrDropdownItem (click)="logOut()">Log out</a>
</div> </div>
</clr-dropdown> </clr-dropdown>
</div> </div>
......
...@@ -32,6 +32,10 @@ export class NavigatorComponent implements OnInit { ...@@ -32,6 +32,10 @@ export class NavigatorComponent implements OnInit {
return this.sessionUser != null; return this.sessionUser != null;
} }
public get accountName(): string {
return this.sessionUser?this.sessionUser.username: "";
}
//Open the account setting dialog //Open the account setting dialog
openAccountSettingsModal(): void { openAccountSettingsModal(): void {
this.showAccountSettingsModal.emit({ this.showAccountSettingsModal.emit({
...@@ -54,17 +58,12 @@ export class NavigatorComponent implements OnInit { ...@@ -54,17 +58,12 @@ export class NavigatorComponent implements OnInit {
} }
//Log out system //Log out system
logOut(reSignIn: boolean): void { logOut(): void {
this.session.signOff() this.session.signOff()
.then(() => { .then(() => {
this.sessionUser = null; this.sessionUser = null;
if (reSignIn) { //Naviagte to the sign in route
//Naviagte to the sign in route this.router.navigate(["/sign-in"]);
this.router.navigate(["/sign-in"]);
} else {
//Naviagte to the default route
this.router.navigate(["/harbor"]);
}
}) })
.catch()//TODO: .catch()//TODO:
} }
......
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-between"> <div class="row flex-items-xs-right">
<div class="col-md-2 push-md-8"> <div class="col-xs-3 push-md-2 flex-xs-middle">
<input type="text" placeholder="Search for user"> <button class="btn btn-link" (click)="toggleOptionalName(currentOption)">{{toggleName[currentOption]}}</button>
</div>
<div class="col-xs-3 flex-xs-middle">
<clr-icon shape="filter" style="position: relative; left: 15px;"></clr-icon><input style="padding-left: 20px;" type="text" placeholder="Filter logs" #searchUsername (keyup.enter)="doSearchAuditLogs(searchUsername.value)">
</div>
</div>
<div class="row flex-items-xs-right advance-option" [hidden]="currentOption === 0">
<div class="col-xs-2 push-md-1">
<clr-dropdown [clrMenuPosition]="'bottom-left'" >
<button class="btn btn-link" clrDropdownToggle>
All Operations
<clr-icon shape="caret down"></clr-icon>
</button>
<div class="dropdown-menu">
<a href="javascript:void(0)" clrDropdownItem *ngFor="let f of filterOptions" (click)="toggleFilterOption(f.key)"><clr-icon shape="check" [hidden]="!f.checked"></clr-icon> {{f.description}}</a>
</div>
</clr-dropdown>
</div>
<div class="col-xs-5 push-md-1">
<clr-icon shape="date"></clr-icon><input type="date" #fromTime (change)="doSearchByTimeRange(fromTime.value, 'begin')">
<clr-icon shape="date"></clr-icon><input type="date" #toTime (change)="doSearchByTimeRange(toTime.value, 'end')">
</div> </div>
</div> </div>
<clr-datagrid> <clr-datagrid>
...@@ -13,12 +33,12 @@ ...@@ -13,12 +33,12 @@
<clr-dg-column>Timestamp</clr-dg-column> <clr-dg-column>Timestamp</clr-dg-column>
<clr-dg-row *ngFor="let l of auditLogs"> <clr-dg-row *ngFor="let l of auditLogs">
<clr-dg-cell>{{l.username}}</clr-dg-cell> <clr-dg-cell>{{l.username}}</clr-dg-cell>
<clr-dg-cell>{{l.repoName}}</clr-dg-cell> <clr-dg-cell>{{l.repo_name}}</clr-dg-cell>
<clr-dg-cell>{{l.tag}}</clr-dg-cell> <clr-dg-cell>{{l.repo_tag}}</clr-dg-cell>
<clr-dg-cell>{{l.operation}}</clr-dg-cell> <clr-dg-cell>{{l.operation}}</clr-dg-cell>
<clr-dg-cell>{{l.timestamp}}</clr-dg-cell> <clr-dg-cell>{{l.op_time}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer>{{auditLogs.length}} item(s)</clr-dg-footer> <clr-dg-footer>{{ (auditLogs ? auditLogs.length : 0) }} item(s)</clr-dg-footer>
</clr-datagrid> </clr-datagrid>
</div> </div>
</div> </div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { AuditLog } from './audit-log'; import { AuditLog } from './audit-log';
import { SessionUser } from '../shared/session-user';
import { AuditLogService } from './audit-log.service';
import { SessionService } from '../shared/session.service';
import { MessageService } from '../global-message/message.service';
export const optionalSearch: {} = {0: 'Advanced', 1: 'Simple'};
export class FilterOption {
key: string;
description: string;
checked: boolean;
constructor(private iKey: string, private iDescription: string, private iChecked: boolean) {
this.key = iKey;
this.description = iDescription;
this.checked = iChecked;
}
toString(): string {
return 'key:' + this.key + ', description:' + this.description + ', checked:' + this.checked + '\n';
}
}
@Component({ @Component({
templateUrl: './audit-log.component.html' selector: 'audit-log',
templateUrl: './audit-log.component.html',
styleUrls: [ 'audit-log.css' ]
}) })
export class AuditLogComponent implements OnInit { export class AuditLogComponent implements OnInit {
currentUser: SessionUser;
projectId: number;
queryParam: AuditLog = new AuditLog();
auditLogs: AuditLog[]; auditLogs: AuditLog[];
toggleName = optionalSearch;
currentOption: number = 0;
filterOptions: FilterOption[] = [
new FilterOption('all', 'All Operations', true),
new FilterOption('pull', 'Pull', true),
new FilterOption('push', 'Push', true),
new FilterOption('create', 'Create', true),
new FilterOption('delete', 'Delete', true),
new FilterOption('others', 'Others', true)
];
constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageService: MessageService) {
//Get current user from registered resolver.
this.route.data.subscribe(data=>this.currentUser = <SessionUser>data['auditLogResolver']);
}
ngOnInit(): void { ngOnInit(): void {
this.auditLogs = [ this.projectId = +this.route.snapshot.parent.params['id'];
{ username: 'Admin', repoName: 'project01', tag: '', operation: 'create', timestamp: '2016-12-23 12:05:17' }, console.log('Get projectId from route params snapshot:' + this.projectId);
{ username: 'Admin', repoName: 'project01/ubuntu', tag: '14.04', operation: 'push', timestamp: '2016-12-30 14:52:23' }, this.queryParam.project_id = this.projectId;
{ username: 'user1', repoName: 'project01/mysql', tag: '5.6', operation: 'pull', timestamp: '2016-12-30 12:12:33' } this.retrieve(this.queryParam);
]; }
retrieve(queryParam: AuditLog): void {
this.auditLogService
.listAuditLogs(queryParam)
.subscribe(
response=>this.auditLogs = response,
error=>{
this.router.navigate(['/harbor', 'projects']);
this.messageService.announceMessage('Failed to list audit logs with project ID:' + queryParam.project_id);
}
);
}
doSearchAuditLogs(searchUsername: string): void {
this.queryParam.username = searchUsername;
this.retrieve(this.queryParam);
}
doSearchByTimeRange(strDate: string, target: string): void {
let oneDayOffset = 3600 * 24;
switch(target) {