From da3ebdcdcd16e836393fc95f85afd4dfc29a41f2 Mon Sep 17 00:00:00 2001 From: Steven Zou Date: Tue, 7 Mar 2017 17:20:33 +0800 Subject: [PATCH] merge ui_ng --- src/ui_ng/.editorconfig | 20 -- src/ui_ng/.gitignore | 9 - src/ui_ng/.travis.yml | 10 - .../account-settings-modal.component.html | 9 +- .../account-settings-modal.component.ts | 75 +++-- src/ui_ng/src/app/account/account.module.ts | 19 +- .../password/forgot-password.component.html | 32 +++ .../password/forgot-password.component.ts | 74 ++++- .../password/password-setting.component.html | 1 + .../password/password-setting.component.ts | 69 +++-- .../password/password-setting.service.ts | 45 ++- .../account/password/password.component.css | 3 + .../password/reset-password.component.html | 51 ++++ .../password/reset-password.component.ts | 123 ++++++++ .../app/account/sign-in/sign-in.component.css | 8 + .../account/sign-in/sign-in.component.html | 23 +- .../app/account/sign-in/sign-in.component.ts | 52 ++-- .../app/account/sign-in/sign-in.service.ts | 5 +- .../account/sign-up/sign-up.component.html | 13 +- .../app/account/sign-up/sign-up.component.ts | 104 ++++++- src/ui_ng/src/app/app.component.ts | 5 +- src/ui_ng/src/app/app.module.ts | 4 +- src/ui_ng/src/app/base/auth-guard.service.ts | 22 -- .../app/base/base-routing-resolver.service.ts | 24 -- src/ui_ng/src/app/base/base-routing.module.ts | 46 --- src/ui_ng/src/app/base/base.module.ts | 7 +- .../global-search/search-result.component.css | 3 +- .../harbor-shell/harbor-shell.component.html | 12 +- .../harbor-shell/harbor-shell.component.ts | 27 +- src/ui_ng/src/app/base/modal-event.ts | 4 +- src/ui_ng/src/app/base/modal-events.const.ts | 5 +- .../base/navigator/navigator.component.html | 8 +- .../app/base/navigator/navigator.component.ts | 16 +- .../config/auth/config-auth.component.html | 117 ++++++++ .../app/config/auth/config-auth.component.ts | 33 +++ src/ui_ng/src/app/config/config.component.css | 0 .../src/app/config/config.component.html | 54 ++++ src/ui_ng/src/app/config/config.component.ts | 267 ++++++++++++++++++ src/ui_ng/src/app/config/config.module.ts | 22 ++ src/ui_ng/src/app/config/config.service.ts | 33 +++ src/ui_ng/src/app/config/config.ts | 77 +++++ .../config/email/config-email.component.html | 74 +++++ .../config/email/config-email.component.ts | 25 ++ .../app/global-message/message.component.html | 14 +- .../app/global-message/message.component.ts | 113 ++++++-- src/ui_ng/src/app/global-message/message.ts | 1 + src/ui_ng/src/app/harbor-routing.module.ts | 93 +++++- src/ui_ng/src/app/i18n/lang/en-lang.json | 133 --------- src/ui_ng/src/app/i18n/lang/zh-lang.json | 134 --------- .../src/app/i18n/missing-trans.handler.ts | 4 +- .../src/app/log/audit-log.component.html | 3 +- src/ui_ng/src/app/log/audit-log.component.ts | 3 + src/ui_ng/src/app/log/audit-log.service.ts | 35 ++- src/ui_ng/src/app/log/log.module.ts | 16 +- .../src/app/log/recent-log.component.css | 32 +++ .../src/app/log/recent-log.component.html | 36 +++ src/ui_ng/src/app/log/recent-log.component.ts | 96 +++++++ .../action-project.component.html | 11 - .../action-project.component.ts | 40 --- .../create-project.component.html | 22 +- .../create-project.component.ts | 13 +- .../list-project/list-project.component.html | 9 +- .../add-member/add-member.component.html | 17 +- .../member/add-member/add-member.component.ts | 17 +- .../app/project/member/member.component.html | 66 ++--- .../app/project/member/member.component.ts | 87 +++--- .../src/app/project/member/member.service.ts | 10 +- .../src/app/project/project-routing.module.ts | 69 ----- .../src/app/project/project.component.html | 5 +- .../src/app/project/project.component.ts | 59 +++- src/ui_ng/src/app/project/project.css | 3 - src/ui_ng/src/app/project/project.module.ts | 18 +- src/ui_ng/src/app/project/project.service.ts | 12 +- .../create-edit-destination.component.html | 52 ++++ .../create-edit-destination.component.ts | 128 +++++++++ .../create-edit-policy.component.html | 54 ---- .../create-edit-policy.component.ts | 19 -- .../destination/destination.component.html | 30 ++ .../destination/destination.component.ts | 103 +++++++ .../list-policy/custom-highlight.directive.ts | 23 -- .../list-policy/list-policy.component.html | 29 -- .../list-policy/list-policy.component.ts | 21 -- .../replication-management.component.html | 12 + .../replication-management.component.ts | 8 + .../replication-management.css | 32 +++ .../replication/replication.component.html | 54 ++-- .../app/replication/replication.component.ts | 164 +++++++++-- .../src/app/replication/replication.module.ts | 21 +- .../app/replication/replication.service.ts | 150 +++++++++- .../total-replication.component.html | 12 + .../total-replication.component.ts | 71 +++++ .../app/repository/repository.component.html | 7 +- .../about-dialog/about-dialog.component.css | 37 +++ .../about-dialog/about-dialog.component.html | 25 ++ .../about-dialog/about-dialog.component.ts | 20 ++ .../create-edit-policy.component.html | 81 ++++++ .../create-edit-policy.component.ts | 228 +++++++++++++++ .../create-edit-policy/create-edit-policy.ts | 11 + .../deletion-dialog.component.ts | 39 ++- .../deletion-dialog.service.ts | 6 +- .../deletion-dialog/deletion-message.ts | 8 +- .../inline-alert/inline-alert.component.html | 10 + .../inline-alert/inline-alert.component.ts | 71 +++++ .../list-policy/list-policy.component.html | 24 ++ .../list-policy/list-policy.component.ts | 88 ++++++ .../new-user-form/new-user-form.component.css | 5 + .../new-user-form.component.html | 51 ++-- .../new-user-form}/new-user-form.component.ts | 25 +- .../shared/not-found/not-found.component.css | 33 +++ .../shared/not-found/not-found.component.html | 10 + .../shared/not-found/not-found.component.ts | 35 +++ src/ui_ng/src/app/shared/port.directive.ts | 37 +++ .../route/base-routing-resolver.service.ts | 37 +++ .../route/system-admin-activate.service.ts | 29 ++ src/ui_ng/src/app/shared/session.service.ts | 71 +++-- src/ui_ng/src/app/shared/shared.const.ts | 18 +- src/ui_ng/src/app/shared/shared.module.ts | 33 ++- src/ui_ng/src/app/shared/shared.utils.ts | 62 ++++ .../app/user/new-user-modal.component.html | 11 +- .../src/app/user/new-user-modal.component.ts | 85 ++++-- src/ui_ng/src/app/user/user.component.ts | 58 +++- src/ui_ng/src/app/user/user.module.ts | 9 +- src/ui_ng/src/app/user/user.ts | 1 + src/ui_ng/src/ng/i18n/lang/en-lang.json | 228 +++++++++++++++ src/ui_ng/src/ng/i18n/lang/zh-lang.json | 230 +++++++++++++++ 125 files changed, 4345 insertions(+), 1162 deletions(-) delete mode 100644 src/ui_ng/.editorconfig delete mode 100644 src/ui_ng/.gitignore delete mode 100644 src/ui_ng/.travis.yml create mode 100644 src/ui_ng/src/app/account/password/password.component.css create mode 100644 src/ui_ng/src/app/account/password/reset-password.component.html create mode 100644 src/ui_ng/src/app/account/password/reset-password.component.ts delete mode 100644 src/ui_ng/src/app/base/auth-guard.service.ts delete mode 100644 src/ui_ng/src/app/base/base-routing-resolver.service.ts delete mode 100644 src/ui_ng/src/app/base/base-routing.module.ts create mode 100644 src/ui_ng/src/app/config/auth/config-auth.component.html create mode 100644 src/ui_ng/src/app/config/auth/config-auth.component.ts create mode 100644 src/ui_ng/src/app/config/config.component.css create mode 100644 src/ui_ng/src/app/config/config.component.html create mode 100644 src/ui_ng/src/app/config/config.component.ts create mode 100644 src/ui_ng/src/app/config/config.module.ts create mode 100644 src/ui_ng/src/app/config/config.service.ts create mode 100644 src/ui_ng/src/app/config/config.ts create mode 100644 src/ui_ng/src/app/config/email/config-email.component.html create mode 100644 src/ui_ng/src/app/config/email/config-email.component.ts delete mode 100644 src/ui_ng/src/app/i18n/lang/en-lang.json delete mode 100644 src/ui_ng/src/app/i18n/lang/zh-lang.json create mode 100644 src/ui_ng/src/app/log/recent-log.component.css create mode 100644 src/ui_ng/src/app/log/recent-log.component.html create mode 100644 src/ui_ng/src/app/log/recent-log.component.ts delete mode 100644 src/ui_ng/src/app/project/action-project/action-project.component.html delete mode 100644 src/ui_ng/src/app/project/action-project/action-project.component.ts delete mode 100644 src/ui_ng/src/app/project/project-routing.module.ts create mode 100644 src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.html create mode 100644 src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.ts delete mode 100644 src/ui_ng/src/app/replication/create-edit-policy/create-edit-policy.component.html delete mode 100644 src/ui_ng/src/app/replication/create-edit-policy/create-edit-policy.component.ts create mode 100644 src/ui_ng/src/app/replication/destination/destination.component.html create mode 100644 src/ui_ng/src/app/replication/destination/destination.component.ts delete mode 100644 src/ui_ng/src/app/replication/list-policy/custom-highlight.directive.ts delete mode 100644 src/ui_ng/src/app/replication/list-policy/list-policy.component.html delete mode 100644 src/ui_ng/src/app/replication/list-policy/list-policy.component.ts create mode 100644 src/ui_ng/src/app/replication/replication-management/replication-management.component.html create mode 100644 src/ui_ng/src/app/replication/replication-management/replication-management.component.ts create mode 100644 src/ui_ng/src/app/replication/replication-management/replication-management.css create mode 100644 src/ui_ng/src/app/replication/total-replication/total-replication.component.html create mode 100644 src/ui_ng/src/app/replication/total-replication/total-replication.component.ts create mode 100644 src/ui_ng/src/app/shared/about-dialog/about-dialog.component.css create mode 100644 src/ui_ng/src/app/shared/about-dialog/about-dialog.component.html create mode 100644 src/ui_ng/src/app/shared/about-dialog/about-dialog.component.ts create mode 100644 src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.html create mode 100644 src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.ts create mode 100644 src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.ts create mode 100644 src/ui_ng/src/app/shared/inline-alert/inline-alert.component.html create mode 100644 src/ui_ng/src/app/shared/inline-alert/inline-alert.component.ts create mode 100644 src/ui_ng/src/app/shared/list-policy/list-policy.component.html create mode 100644 src/ui_ng/src/app/shared/list-policy/list-policy.component.ts create mode 100644 src/ui_ng/src/app/shared/new-user-form/new-user-form.component.css rename src/ui_ng/src/app/{user => shared/new-user-form}/new-user-form.component.html (63%) rename src/ui_ng/src/app/{user => shared/new-user-form}/new-user-form.component.ts (64%) create mode 100644 src/ui_ng/src/app/shared/not-found/not-found.component.css create mode 100644 src/ui_ng/src/app/shared/not-found/not-found.component.html create mode 100644 src/ui_ng/src/app/shared/not-found/not-found.component.ts create mode 100644 src/ui_ng/src/app/shared/port.directive.ts create mode 100644 src/ui_ng/src/app/shared/route/base-routing-resolver.service.ts create mode 100644 src/ui_ng/src/app/shared/route/system-admin-activate.service.ts create mode 100644 src/ui_ng/src/app/shared/shared.utils.ts create mode 100644 src/ui_ng/src/ng/i18n/lang/en-lang.json create mode 100644 src/ui_ng/src/ng/i18n/lang/zh-lang.json diff --git a/src/ui_ng/.editorconfig b/src/ui_ng/.editorconfig deleted file mode 100644 index 2fef24f1d..000000000 --- a/src/ui_ng/.editorconfig +++ /dev/null @@ -1,20 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 4 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - - -[*.md] -max_line_length = 0 -trim_trailing_whitespace = true - -# Indentation override -#[lib/**.js] -#[{package.json,.travis.yml}] -#[**/**.js] diff --git a/src/ui_ng/.gitignore b/src/ui_ng/.gitignore deleted file mode 100644 index 5d90f1648..000000000 --- a/src/ui_ng/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -coverage/ -dist/ -html-report/ -node_modules/ -typings/ -**/*npm-debug.log.* -**/*yarn-error.log.* -.idea/ -.DS_Store diff --git a/src/ui_ng/.travis.yml b/src/ui_ng/.travis.yml deleted file mode 100644 index 6e54af80b..000000000 --- a/src/ui_ng/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -node_js: - - "6.9" - -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 diff --git a/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html index f8562bcf0..4e7be441d 100644 --- a/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html +++ b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html @@ -38,14 +38,7 @@ -
- -
- - {{errorMessage}} - -
-
+ + \ No newline at end of file + +> + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.component.ts b/src/ui_ng/src/app/account/sign-in/sign-in.component.ts index 4bc8b51ef..40cd828a1 100644 --- a/src/ui_ng/src/app/account/sign-in/sign-in.component.ts +++ b/src/ui_ng/src/app/account/sign-in/sign-in.component.ts @@ -1,11 +1,15 @@ -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; import { Input, ViewChild, AfterViewChecked } from '@angular/core'; import { NgForm } from '@angular/forms'; import { SessionService } from '../../shared/session.service'; import { SignInCredential } from '../../shared/sign-in-credential'; +import { SignUpComponent } from '../sign-up/sign-up.component'; +import { harborRootRoute } from '../../shared/shared.const'; +import { ForgotPasswordComponent } from '../password/forgot-password.component'; + //Define status flags for signing in states export const signInStatusNormal = 0; export const signInStatusOnGoing = 1; @@ -17,13 +21,16 @@ export const signInStatusError = -1; styleUrls: ['sign-in.component.css'] }) -export class SignInComponent implements AfterViewChecked { +export class SignInComponent implements AfterViewChecked, OnInit { + private redirectUrl: string = ""; //Form reference signInForm: NgForm; @ViewChild('signInForm') currentForm: NgForm; + @ViewChild('signupDialog') signUpDialog: SignUpComponent; + @ViewChild('forgotPwdDialog') forgotPwdDialog: ForgotPasswordComponent; //Status flag - signInStatus: number = 0; + signInStatus: number = signInStatusNormal; //Initialize sign in credential @Input() signInCredential: SignInCredential = { @@ -33,9 +40,17 @@ export class SignInComponent implements AfterViewChecked { constructor( private router: Router, - private session: SessionService + private session: SessionService, + private route: ActivatedRoute ) { } + ngOnInit(): void { + this.route.queryParams + .subscribe(params => { + this.redirectUrl = params["redirect_url"] || ""; + }); + } + //For template accessing public get isError(): boolean { return this.signInStatus === signInStatusError; @@ -105,25 +120,26 @@ export class SignInComponent implements AfterViewChecked { //Set status this.signInStatus = signInStatusNormal; - //Validate the sign-in session - this.session.retrieveUser() - .then(user => { - //Routing to the right location - let nextRoute = ["/harbor", "projects"]; - this.router.navigate(nextRoute); - }) - .catch(error => { - this.handleError(error); - }); + //Redirect to the right route + if (this.redirectUrl === "") { + //Routing to the default location + this.router.navigateByUrl(harborRootRoute); + }else{ + this.router.navigateByUrl(this.redirectUrl); + } }) .catch(error => { this.handleError(error); }); } - //Help user navigate to the sign up + //Open sign up dialog signUp(): void { - let nextRoute = ["/harbor", "signup"]; - this.router.navigate(nextRoute); + this.signUpDialog.open(); + } + + //Open forgot password dialog + forgotPassword(): void { + this.forgotPwdDialog.open(); } } \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.service.ts b/src/ui_ng/src/app/account/sign-in/sign-in.service.ts index e67e86304..cf4f171ef 100644 --- a/src/ui_ng/src/app/account/sign-in/sign-in.service.ts +++ b/src/ui_ng/src/app/account/sign-in/sign-in.service.ts @@ -2,10 +2,9 @@ import { Injectable } from '@angular/core'; import { Headers, Http, URLSearchParams } from '@angular/http'; import 'rxjs/add/operator/toPromise'; -import { SignInCredential } from './sign-in-credential'; +import { SignInCredential } from '../../shared/sign-in-credential'; -const url_prefix = '/ng'; -const signInUrl = url_prefix + '/login'; +const signInUrl = '/login'; /** * * Define a service to provide sign in methods diff --git a/src/ui_ng/src/app/account/sign-up/sign-up.component.html b/src/ui_ng/src/app/account/sign-up/sign-up.component.html index fae4c1f44..ddd5b5d48 100644 --- a/src/ui_ng/src/app/account/sign-up/sign-up.component.html +++ b/src/ui_ng/src/app/account/sign-up/sign-up.component.html @@ -1 +1,12 @@ -

Placeholder for signup

\ No newline at end of file + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-up/sign-up.component.ts b/src/ui_ng/src/app/account/sign-up/sign-up.component.ts index 3d97950fa..86cb22ed8 100644 --- a/src/ui_ng/src/app/account/sign-up/sign-up.component.ts +++ b/src/ui_ng/src/app/account/sign-up/sign-up.component.ts @@ -1,10 +1,108 @@ -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, Output, ViewChild } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { NewUserFormComponent } from '../../shared/new-user-form/new-user-form.component'; +import { User } from '../../user/user'; + +import { SessionService } from '../../shared/session.service'; +import { UserService } from '../../user/user.service'; +import { errorHandler } from '../../shared/shared.utils'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; @Component({ selector: 'sign-up', templateUrl: "sign-up.component.html" }) export class SignUpComponent { - // constructor(private router: Router){} + opened: boolean = false; + staticBackdrop: boolean = true; + private error: any; + private onGoing: boolean = false; + private formValueChanged: boolean = false; + + constructor( + private session: SessionService, + private userService: UserService) { } + + @ViewChild(NewUserFormComponent) + private newUserForm: NewUserFormComponent; + + @ViewChild(InlineAlertComponent) + private inlienAlert: InlineAlertComponent; + + private getNewUser(): User { + return this.newUserForm.getData(); + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.newUserForm.isValid && this.error == null; + } + + formValueChange(flag: boolean): void { + if (flag) { + this.formValueChanged = true; + } + if (this.error != null) { + this.error = null;//clear error + } + this.inlienAlert.close();//Close alert if being shown + } + + open(): void { + this.newUserForm.reset();//Reset form + this.formValueChanged = false; + this.opened = true; + } + + close(): void { + if (this.formValueChanged) { + if (this.newUserForm.isEmpty()) { + this.opened = false; + } else { + //Need user confirmation + this.inlienAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + } + } else { + this.opened = false; + } + } + + confirmCancel(): void { + this.opened = false; + } + + //Create new user + create(): void { + //Double confirm everything is ok + //Form is valid + if (!this.isValid) { + return; + } + + //We have new user data + let u = this.getNewUser(); + if (!u) { + return; + } + + //Start process + this.onGoing = true; + + this.userService.addUser(u) + .then(() => { + this.onGoing = false; + this.close(); + }) + .catch(error => { + this.onGoing = false; + this.error = error; + this.inlienAlert.showInlineError(error); + }); + } } \ No newline at end of file diff --git a/src/ui_ng/src/app/app.component.ts b/src/ui_ng/src/app/app.component.ts index 78ce6b3fd..d6c331f3c 100644 --- a/src/ui_ng/src/app/app.component.ts +++ b/src/ui_ng/src/app/app.component.ts @@ -24,7 +24,10 @@ export class AppComponent { //Use browser lang langSetting = translate.getBrowserLang(); } - translate.use(this.isLangMatch(langSetting, supportedLangs) ? langSetting : enLang); + + let selectedLang = this.isLangMatch(langSetting, supportedLangs) ? langSetting : enLang; + translate.use(selectedLang); + //this.session.switchLanguage(selectedLang).catch(error => console.error(error)); } private isLangMatch(browserLang: string, supportedLangs: string[]) { diff --git a/src/ui_ng/src/app/app.module.ts b/src/ui_ng/src/app/app.module.ts index bbd77180b..7733d7e6c 100644 --- a/src/ui_ng/src/app/app.module.ts +++ b/src/ui_ng/src/app/app.module.ts @@ -9,6 +9,7 @@ import { BaseModule } from './base/base.module'; import { HarborRoutingModule } from './harbor-routing.module'; import { SharedModule } from './shared/shared.module'; import { AccountModule } from './account/account.module'; +import { ConfigurationModule } from './config/config.module'; import { TranslateModule, TranslateLoader, MissingTranslationHandler } from "@ngx-translate/core"; import { MyMissingTranslationHandler } from './i18n/missing-trans.handler'; @@ -16,7 +17,7 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { Http } from '@angular/http'; export function HttpLoaderFactory(http: Http) { - return new TranslateHttpLoader(http, 'app/i18n/lang/', '-lang.json'); + return new TranslateHttpLoader(http, 'ng/i18n/lang/', '-lang.json'); } @NgModule({ @@ -28,6 +29,7 @@ export function HttpLoaderFactory(http: Http) { BaseModule, AccountModule, HarborRoutingModule, + ConfigurationModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/ui_ng/src/app/base/auth-guard.service.ts b/src/ui_ng/src/app/base/auth-guard.service.ts deleted file mode 100644 index 3fcd1e888..000000000 --- a/src/ui_ng/src/app/base/auth-guard.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - CanActivate, Router, - ActivatedRouteSnapshot, - RouterStateSnapshot, - CanActivateChild -} from '@angular/router'; -import { SessionService } from '../shared/session.service'; - -@Injectable() -export class AuthGuard implements CanActivate, CanActivateChild { - constructor(private authService: SessionService, private router: Router) {} - - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { - console.info("canActivate",route, state); - return true; - } - - canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { - return this.canActivate(route, state); - } -} diff --git a/src/ui_ng/src/app/base/base-routing-resolver.service.ts b/src/ui_ng/src/app/base/base-routing-resolver.service.ts deleted file mode 100644 index f47baa3b1..000000000 --- a/src/ui_ng/src/app/base/base-routing-resolver.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -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 { - - constructor(private session: SessionService, private router: Router) { } - - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { - console.info("resolver...."); - return this.session.retrieveUser() - .then(sessionUser => { - return sessionUser; - }) - .catch(error => { - console.info("Anonymous user"); - }); - } -} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/base-routing.module.ts b/src/ui_ng/src/app/base/base-routing.module.ts deleted file mode 100644 index 5ad51064a..000000000 --- a/src/ui_ng/src/app/base/base-routing.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { HarborShellComponent } from './harbor-shell/harbor-shell.component'; - -import { DashboardComponent } from '../dashboard/dashboard.component'; -import { ProjectComponent } from '../project/project.component'; -import { UserComponent } from '../user/user.component'; - -import { BaseRoutingResolver } from './base-routing-resolver.service'; -import { AuthGuard } from './auth-guard.service'; - -const baseRoutes: Routes = [ - { - path: 'harbor', - component: HarborShellComponent, - resolve: { - rootResolver: BaseRoutingResolver - }, - children: [ - { - path: 'dashboard', - component: DashboardComponent - }, - { - path: 'projects', - component: ProjectComponent - }, - { - path: 'users', - component: UserComponent, - canActivate: [AuthGuard] - } - ] - }]; - -@NgModule({ - imports: [ - RouterModule.forChild(baseRoutes) - ], - exports: [RouterModule], - - providers: [BaseRoutingResolver, AuthGuard] -}) -export class BaseRoutingModule { - -} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/base.module.ts b/src/ui_ng/src/app/base/base.module.ts index 4a34f4558..0a623551d 100644 --- a/src/ui_ng/src/app/base/base.module.ts +++ b/src/ui_ng/src/app/base/base.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; +import { RouterModule } from '@angular/router'; import { DashboardModule } from '../dashboard/dashboard.module'; import { ProjectModule } from '../project/project.module'; @@ -12,16 +13,14 @@ import { FooterComponent } from './footer/footer.component'; import { HarborShellComponent } from './harbor-shell/harbor-shell.component'; import { SearchResultComponent } from './global-search/search-result.component'; -import { BaseRoutingModule } from './base-routing.module'; - @NgModule({ imports: [ SharedModule, DashboardModule, ProjectModule, UserModule, - BaseRoutingModule, - AccountModule + AccountModule, + RouterModule ], declarations: [ NavigatorComponent, diff --git a/src/ui_ng/src/app/base/global-search/search-result.component.css b/src/ui_ng/src/app/base/global-search/search-result.component.css index 8ec5634e1..a02153e93 100644 --- a/src/ui_ng/src/app/base/global-search/search-result.component.css +++ b/src/ui_ng/src/app/base/global-search/search-result.component.css @@ -1,12 +1,13 @@ .search-overlay { display: block; position: absolute; - height: 94%; + height: 100%; width: 97%; /*shoud be lesser than 1000 to aoivd override the popup menu*/ z-index: 999; box-sizing: border-box; background: #fafafa; + top: 0px; } .search-header { diff --git a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html index e6ad6b49c..200e8592f 100644 --- a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html +++ b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html @@ -1,23 +1,24 @@ -
+
-
\ No newline at end of file diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.ts b/src/ui_ng/src/app/base/navigator/navigator.component.ts index ae069ea82..588ad54b9 100644 --- a/src/ui_ng/src/app/base/navigator/navigator.component.ts +++ b/src/ui_ng/src/app/base/navigator/navigator.component.ts @@ -4,7 +4,7 @@ import { TranslateService } from '@ngx-translate/core'; import { ModalEvent } from '../modal-event'; import { SearchEvent } from '../search-event'; -import { modalAccountSettings, modalPasswordSetting } from '../modal-events.const'; +import { modalEvents } from '../modal-events.const'; import { SessionUser } from '../../shared/session-user'; import { SessionService } from '../../shared/session.service'; @@ -62,7 +62,7 @@ export class NavigatorComponent implements OnInit { //Open the account setting dialog openAccountSettingsModal(): void { this.showAccountSettingsModal.emit({ - modalName: modalAccountSettings, + modalName: modalEvents.USER_PROFILE, modalFlag: true }); } @@ -70,7 +70,15 @@ export class NavigatorComponent implements OnInit { //Open change password dialog openChangePwdModal(): void { this.showPwdChangeModal.emit({ - modalName: modalPasswordSetting, + modalName: modalEvents.CHANGE_PWD, + modalFlag: true + }); + } + + //Open about dialog + openAboutDialog(): void { + this.showPwdChangeModal.emit({ + modalName: modalEvents.ABOUT, modalFlag: true }); } @@ -100,6 +108,8 @@ export class NavigatorComponent implements OnInit { //TODO: console.error('Language '+lang.trim()+' is not suppoted'); } + //Try to switch backend lang + //this.session.switchLanguage(lang).catch(error => console.error(error)); } //Handle the home action diff --git a/src/ui_ng/src/app/config/auth/config-auth.component.html b/src/ui_ng/src/app/config/auth/config-auth.component.html new file mode 100644 index 000000000..707257de8 --- /dev/null +++ b/src/ui_ng/src/app/config/auth/config-auth.component.html @@ -0,0 +1,117 @@ +
+
+
+ +
+ +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ + + + + {{'CONFIG.SELF_REGISTRATION_TOOLTIP' | translate}} + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/auth/config-auth.component.ts b/src/ui_ng/src/app/config/auth/config-auth.component.ts new file mode 100644 index 000000000..7c2b3cfac --- /dev/null +++ b/src/ui_ng/src/app/config/auth/config-auth.component.ts @@ -0,0 +1,33 @@ +import { Component, Input, ViewChild } from '@angular/core'; +import { NgForm } from '@angular/forms'; +import { Subscription } from 'rxjs/Subscription'; + +import { Configuration } from '../config'; + +@Component({ + selector: 'config-auth', + templateUrl: "config-auth.component.html", + styleUrls: ['../config.component.css'] +}) +export class ConfigurationAuthComponent { + private changeSub: Subscription; + @Input("ldapConfig") currentConfig: Configuration = new Configuration(); + + @ViewChild("authConfigFrom") authForm: NgForm; + + constructor() { } + + public get showLdap(): boolean { + return this.currentConfig && + this.currentConfig.auth_mode && + this.currentConfig.auth_mode.value === 'ldap'; + } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } + + public isValid(): boolean { + return this.authForm && this.authForm.valid; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.css b/src/ui_ng/src/app/config/config.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/ui_ng/src/app/config/config.component.html b/src/ui_ng/src/app/config/config.component.html new file mode 100644 index 000000000..cffe632bf --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.html @@ -0,0 +1,54 @@ +

{{'CONFIG.TITLE' | translate }}

+ + + {{'CONFIG.AUTH' | translate }} + {{'CONFIG.REPLICATION' | translate }} + {{'CONFIG.EMAIL' | translate }} + {{'CONFIG.SYSTEM' | translate }} + + + + + +
+
+
+ + + + + {{'CONFIG.VERIFY_REMOTE_CERT_TOOLTIP' | translate }} + + +
+
+
+
+ + + + +
+
+
+ + +
+
+
+
+
+
+ + + +
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.ts b/src/ui_ng/src/app/config/config.component.ts new file mode 100644 index 000000000..10925359b --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.ts @@ -0,0 +1,267 @@ +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgForm } from '@angular/forms'; + +import { ConfigurationService } from './config.service'; +import { Configuration } from './config'; +import { MessageService } from '../global-message/message.service'; +import { AlertType, DeletionTargets } from '../shared/shared.const'; +import { errorHandler, accessErrorHandler } from '../shared/shared.utils'; +import { StringValueItem } from './config'; +import { DeletionDialogService } from '../shared/deletion-dialog/deletion-dialog.service'; +import { Subscription } from 'rxjs/Subscription'; +import { DeletionMessage } from '../shared/deletion-dialog/deletion-message' + +import { ConfigurationAuthComponent } from './auth/config-auth.component'; +import { ConfigurationEmailComponent } from './email/config-email.component'; + +const fakePass = "fakepassword"; + +@Component({ + selector: 'config', + templateUrl: "config.component.html", + styleUrls: ['config.component.css'] +}) +export class ConfigurationComponent implements OnInit, OnDestroy { + private onGoing: boolean = false; + allConfig: Configuration = new Configuration(); + private currentTabId: string = ""; + private originalCopy: Configuration; + private confirmSub: Subscription; + + @ViewChild("repoConfigFrom") repoConfigForm: NgForm; + @ViewChild("systemConfigFrom") systemConfigForm: NgForm; + @ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent; + @ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent; + + constructor( + private configService: ConfigurationService, + private msgService: MessageService, + private confirmService: DeletionDialogService) { } + + ngOnInit(): void { + //First load + this.retrieveConfig(); + + this.confirmSub = this.confirmService.deletionConfirm$.subscribe(confirmation => { + this.reset(confirmation.data); + }); + } + + ngOnDestroy(): void { + if (this.confirmSub) { + this.confirmSub.unsubscribe(); + } + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public isValid(): boolean { + return this.repoConfigForm && + this.repoConfigForm.valid && + this.systemConfigForm && + this.systemConfigForm.valid && + this.mailConfig && + this.mailConfig.isValid() && + this.authConfig && + this.authConfig.isValid(); + } + + public hasChanges(): boolean { + return !this.isEmpty(this.getChanges()); + } + + public isMailConfigValid(): boolean { + return this.mailConfig && + this.mailConfig.isValid(); + } + + public get showTestServerBtn(): boolean { + return this.currentTabId === 'config-email'; + } + + public tabLinkChanged(tabLink: any) { + this.currentTabId = tabLink.id; + } + + /** + * + * Save the changed values + * + * @memberOf ConfigurationComponent + */ + public save(): void { + let changes = this.getChanges(); + if (!this.isEmpty(changes)) { + this.onGoing = true; + this.configService.saveConfiguration(changes) + .then(response => { + this.onGoing = false; + //API should return the updated configurations here + //Unfortunately API does not do that + //To refresh the view, we can clone the original data copy + //or force refresh by calling service. + //HERE we choose force way + this.retrieveConfig(); + this.msgService.announceMessage(response.status, "CONFIG.SAVE_SUCCESS", AlertType.SUCCESS); + }) + .catch(error => { + this.onGoing = false; + if (!accessErrorHandler(error, this.msgService)) { + this.msgService.announceMessage(error.status, errorHandler(error), AlertType.DANGER); + } + }); + } else { + //Inprop situation, should not come here + console.error("Save obort becasue nothing changed"); + } + } + + /** + * + * Discard current changes if have and reset + * + * @memberOf ConfigurationComponent + */ + public cancel(): void { + let changes = this.getChanges(); + if (!this.isEmpty(changes)) { + let msg = new DeletionMessage( + "CONFIG.CONFIRM_TITLE", + "CONFIG.CONFIRM_SUMMARY", + "", + changes, + DeletionTargets.EMPTY + ); + this.confirmService.openComfirmDialog(msg); + } else { + //Inprop situation, should not come here + console.error("Nothing changed"); + } + } + + /** + * + * Test the connection of specified mail server + * + * + * @memberOf ConfigurationComponent + */ + public testMailServer(): void { + + } + + private retrieveConfig(): void { + this.onGoing = true; + this.configService.getConfiguration() + .then(configurations => { + this.onGoing = false; + + //Add two password fields + configurations.email_password = new StringValueItem(fakePass, true); + configurations.ldap_search_password = new StringValueItem(fakePass, true); + this.allConfig = configurations; + + //Keep the original copy of the data + this.originalCopy = this.clone(configurations); + }) + .catch(error => { + this.onGoing = false; + if (!accessErrorHandler(error, this.msgService)) { + this.msgService.announceMessage(error.status, errorHandler(error), AlertType.DANGER); + } + }); + } + + /** + * + * Get the changed fields and return a map + * + * @private + * @returns {*} + * + * @memberOf ConfigurationComponent + */ + private getChanges(): any { + let changes = {}; + if (!this.allConfig || !this.originalCopy) { + return changes; + } + + for (let prop in this.allConfig) { + let field = this.originalCopy[prop]; + if (field && field.editable) { + if (field.value != this.allConfig[prop].value) { + changes[prop] = this.allConfig[prop].value; + //Fix boolean issue + if (typeof field.value === "boolean") { + changes[prop] = changes[prop] ? "1" : "0"; + } + } + } + } + + return changes; + } + + /** + * + * Deep clone the configuration object + * + * @private + * @param {Configuration} src + * @returns {Configuration} + * + * @memberOf ConfigurationComponent + */ + private clone(src: Configuration): Configuration { + let dest = new Configuration(); + if (!src) { + return dest;//Empty + } + + for (let prop in src) { + if (src[prop]) { + dest[prop] = Object.assign({}, src[prop]); //Deep copy inner object + } + } + + return dest; + } + + /** + * + * Reset the configuration form + * + * @private + * @param {*} changes + * + * @memberOf ConfigurationComponent + */ + private reset(changes: any): void { + if (!this.isEmpty(changes)) { + for (let prop in changes) { + if (this.originalCopy[prop]) { + this.allConfig[prop] = Object.assign({}, this.originalCopy[prop]); + } + } + } else { + //force reset + this.retrieveConfig(); + } + } + + private isEmpty(obj) { + for (let key in obj) { + if (obj.hasOwnProperty(key)) + return false; + } + return true; + } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.module.ts b/src/ui_ng/src/app/config/config.module.ts new file mode 100644 index 000000000..3b1d216c2 --- /dev/null +++ b/src/ui_ng/src/app/config/config.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { CoreModule } from '../core/core.module'; +import { SharedModule } from '../shared/shared.module'; + +import { ConfigurationComponent } from './config.component'; +import { ConfigurationService } from './config.service'; +import { ConfigurationAuthComponent } from './auth/config-auth.component'; +import { ConfigurationEmailComponent } from './email/config-email.component'; + +@NgModule({ + imports: [ + CoreModule, + SharedModule + ], + declarations: [ + ConfigurationComponent, + ConfigurationAuthComponent, + ConfigurationEmailComponent], + exports: [ConfigurationComponent], + providers: [ConfigurationService] +}) +export class ConfigurationModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.service.ts b/src/ui_ng/src/app/config/config.service.ts new file mode 100644 index 000000000..36e065610 --- /dev/null +++ b/src/ui_ng/src/app/config/config.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { Configuration } from './config'; + +const configEndpoint = "/api/configurations"; + +@Injectable() +export class ConfigurationService { + private headers: Headers = new Headers({ + "Accept": 'application/json', + "Content-Type": 'application/json' + }); + private options: RequestOptions = new RequestOptions({ + 'headers': this.headers + }); + + constructor(private http: Http) { } + + public getConfiguration(): Promise { + return this.http.get(configEndpoint, this.options).toPromise() + .then(response => response.json() as Configuration) + .catch(error => Promise.reject(error)); + } + + public saveConfiguration(values: any): Promise { + return this.http.put(configEndpoint, JSON.stringify(values), this.options) + .toPromise() + .then(response => response) + .catch(error => Promise.reject(error)); + } +} diff --git a/src/ui_ng/src/app/config/config.ts b/src/ui_ng/src/app/config/config.ts new file mode 100644 index 000000000..dfaf64d85 --- /dev/null +++ b/src/ui_ng/src/app/config/config.ts @@ -0,0 +1,77 @@ +export class StringValueItem { + value: string; + editable: boolean; + + public constructor(v: string, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class NumberValueItem { + value: number; + editable: boolean; + + public constructor(v: number, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class BoolValueItem { + value: boolean; + editable: boolean; + + public constructor(v: boolean, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class Configuration { + auth_mode: StringValueItem; + project_creation_restriction: StringValueItem; + self_registration: BoolValueItem; + ldap_base_dn: StringValueItem; + ldap_filter?: StringValueItem; + ldap_scope: NumberValueItem; + ldap_search_dn?: StringValueItem; + ldap_search_password?: StringValueItem; + ldap_timeout: NumberValueItem; + ldap_uid: StringValueItem; + ldap_url: StringValueItem; + email_host: StringValueItem; + email_identity: StringValueItem; + email_from: StringValueItem; + email_port: NumberValueItem; + email_ssl: BoolValueItem; + email_username?: StringValueItem; + email_password?: StringValueItem; + verify_remote_cert: BoolValueItem; + token_expiration: NumberValueItem; + cfg_expiration: NumberValueItem; + + public constructor() { + this.auth_mode = new StringValueItem("db_auth", true); + this.project_creation_restriction = new StringValueItem("everyone", true); + this.self_registration = new BoolValueItem(false, true); + this.ldap_base_dn = new StringValueItem("", true); + this.ldap_filter = new StringValueItem("", true); + this.ldap_scope = new NumberValueItem(0, true); + this.ldap_search_dn = new StringValueItem("", true); + this.ldap_search_password = new StringValueItem("", true); + this.ldap_timeout = new NumberValueItem(5, true); + this.ldap_uid = new StringValueItem("", true); + this.ldap_url = new StringValueItem("", true); + this.email_host = new StringValueItem("", true); + this.email_identity = new StringValueItem("", true); + this.email_from = new StringValueItem("", true); + this.email_port = new NumberValueItem(25, true); + this.email_ssl = new BoolValueItem(false, true); + this.email_username = new StringValueItem("", true); + this.email_password = new StringValueItem("", true); + this.token_expiration = new NumberValueItem(5, true); + this.cfg_expiration = new NumberValueItem(30, true); + this.verify_remote_cert = new BoolValueItem(false, true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/email/config-email.component.html b/src/ui_ng/src/app/config/email/config-email.component.html new file mode 100644 index 000000000..f95ef09f4 --- /dev/null +++ b/src/ui_ng/src/app/config/email/config-email.component.html @@ -0,0 +1,74 @@ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + {{'CONFIG.SSL_TOOLTIP' | translate}} + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/email/config-email.component.ts b/src/ui_ng/src/app/config/email/config-email.component.ts new file mode 100644 index 000000000..34e70592a --- /dev/null +++ b/src/ui_ng/src/app/config/email/config-email.component.ts @@ -0,0 +1,25 @@ +import { Component, Input, ViewChild } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { Configuration } from '../config'; + +@Component({ + selector: 'config-email', + templateUrl: "config-email.component.html", + styleUrls: ['../config.component.css'] +}) +export class ConfigurationEmailComponent { + @Input("mailConfig") currentConfig: Configuration = new Configuration(); + + @ViewChild("mailConfigFrom") mailForm: NgForm; + + constructor() { } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } + + public isValid(): boolean { + return this.mailForm && this.mailForm.valid; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.component.html b/src/ui_ng/src/app/global-message/message.component.html index 5762d50db..2859e90a1 100644 --- a/src/ui_ng/src/app/global-message/message.component.html +++ b/src/ui_ng/src/app/global-message/message.component.html @@ -1,8 +1,10 @@ -
- - {{globalMessage.message}} - - Sign In -
+
+ + {{message}} + +
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.component.ts b/src/ui_ng/src/app/global-message/message.component.ts index 6dbe56494..1b6c7d9d5 100644 --- a/src/ui_ng/src/app/global-message/message.component.ts +++ b/src/ui_ng/src/app/global-message/message.component.ts @@ -1,43 +1,102 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { TranslateService } from '@ngx-translate/core'; import { Message } from './message'; import { MessageService } from './message.service'; -import { AlertType, dismissInterval } from '../shared/shared.const'; +import { AlertType, dismissInterval, httpStatusCode } from '../shared/shared.const'; @Component({ selector: 'global-message', templateUrl: 'message.component.html' }) -export class MessageComponent { - +export class MessageComponent implements OnInit { + @Input() isAppLevel: boolean; - globalMessage: Message = new Message(); globalMessageOpened: boolean; - - constructor(messageService: MessageService) { - - messageService.appLevelAnnounced$.subscribe( - message=>{ - this.globalMessageOpened = this.isAppLevel && true; - this.globalMessage = message; - console.log('received app level message:' + message); - } - ) - - messageService.messageAnnounced$.subscribe( - message=>{ - this.globalMessageOpened = !this.isAppLevel && true; - this.globalMessage = message; - console.log('received message:' + message); - } - ); - - // Make the message alert bar dismiss after several intervals. - setInterval(()=>this.onClose(), dismissInterval); + messageText: string = ""; + + constructor( + private messageService: MessageService, + private router: Router, + private translate: TranslateService) { } + + ngOnInit(): void { + //Only subscribe application level message + if (this.isAppLevel) { + this.messageService.appLevelAnnounced$.subscribe( + message => { + this.globalMessageOpened = true; + this.globalMessage = message; + this.messageText = message.message; + + this.translateMessage(message); + } + ) + } else { + //Only subscribe general messages + this.messageService.messageAnnounced$.subscribe( + message => { + this.globalMessageOpened = true; + this.globalMessage = message; + this.messageText = message.message; + + this.translateMessage(message); + + // Make the message alert bar dismiss after several intervals. + //Only for this case + setInterval(() => this.onClose(), dismissInterval); + } + ); + } } - + + //Translate or refactor the message shown to user + translateMessage(msg: Message): void { + if (!msg) { + return; + } + + let key = ""; + if (!msg.message) { + key = "UNKNOWN_ERROR"; + } else { + key = typeof msg.message === "string" ? msg.message.trim() : msg.message; + if (key === "") { + key = "UNKNOWN_ERROR"; + } + } + + //Override key for HTTP 401 and 403 + if (this.globalMessage.statusCode === httpStatusCode.Unauthorized) { + key = "UNAUTHORIZED_ERROR"; + } + + if (this.globalMessage.statusCode === httpStatusCode.Forbidden) { + key = "FORBIDDEN_ERROR"; + } + + this.translate.get(key).subscribe((res: string) => this.messageText = res); + } + + public get needAuth(): boolean { + return this.globalMessage ? + (this.globalMessage.statusCode === httpStatusCode.Unauthorized) || + (this.globalMessage.statusCode === httpStatusCode.Forbidden) : false; + } + + //Show message text + public get message(): string { + return this.messageText; + } + + signIn(): void { + this.router.navigate(['sign-in']); + } + onClose() { this.globalMessageOpened = false; } diff --git a/src/ui_ng/src/app/global-message/message.ts b/src/ui_ng/src/app/global-message/message.ts index 6f6167f1a..0ba400baa 100644 --- a/src/ui_ng/src/app/global-message/message.ts +++ b/src/ui_ng/src/app/global-message/message.ts @@ -4,6 +4,7 @@ export class Message { statusCode: number; message: string; alertType: AlertType; + isAppLevel: boolean = false; get type(): string { switch (this.alertType) { diff --git a/src/ui_ng/src/app/harbor-routing.module.ts b/src/ui_ng/src/app/harbor-routing.module.ts index 1282b5c29..80779aab8 100644 --- a/src/ui_ng/src/app/harbor-routing.module.ts +++ b/src/ui_ng/src/app/harbor-routing.module.ts @@ -4,16 +4,101 @@ import { RouterModule, Routes } from '@angular/router'; import { SignInComponent } from './account/sign-in/sign-in.component'; import { HarborShellComponent } from './base/harbor-shell/harbor-shell.component'; +import { ProjectComponent } from './project/project.component'; +import { UserComponent } from './user/user.component'; +import { ReplicationManagementComponent } from './replication/replication-management/replication-management.component'; -import { BaseRoutingResolver } from './base/base-routing-resolver.service'; +import { TotalReplicationComponent } from './replication/total-replication/total-replication.component'; +import { DestinationComponent } from './replication/destination/destination.component'; + +import { ProjectDetailComponent } from './project/project-detail/project-detail.component'; + +import { RepositoryComponent } from './repository/repository.component'; +import { ReplicationComponent } from './replication/replication.component'; +import { MemberComponent } from './project/member/member.component'; +import { AuditLogComponent } from './log/audit-log.component'; + +import { BaseRoutingResolver } from './shared/route/base-routing-resolver.service'; +import { ProjectRoutingResolver } from './project/project-routing-resolver.service'; +import { SystemAdminGuard } from './shared/route/system-admin-activate.service'; +import { SignUpComponent } from './account/sign-up/sign-up.component'; +import { ResetPasswordComponent } from './account/password/reset-password.component'; +import { RecentLogComponent } from './log/recent-log.component'; +import { ConfigurationComponent } from './config/config.component'; +import { PageNotFoundComponent } from './shared/not-found/not-found.component' const harborRoutes: Routes = [ + { path: '', redirectTo: '/harbor', pathMatch: 'full' }, + { path: 'sign-in', component: SignInComponent }, + { path: 'sign-up', component: SignUpComponent}, + { path: 'reset_password', component: ResetPasswordComponent}, { path: 'harbor', - component: HarborShellComponent + component: HarborShellComponent, + resolve: { + authResolver: BaseRoutingResolver + }, + children: [ + { + path: 'projects', + component: ProjectComponent + }, + { + path: 'logs', + component: RecentLogComponent + }, + { + path: 'users', + component: UserComponent, + canActivate: [SystemAdminGuard] + }, + { + path: 'replications', + component: ReplicationManagementComponent, + canActivate: [SystemAdminGuard], + children: [ + { + path: 'rules', + component: TotalReplicationComponent + }, + { + path: 'endpoints', + component: DestinationComponent + } + ] + }, + { + path: 'projects/:id', + component: ProjectDetailComponent, + resolve: { + projectResolver: ProjectRoutingResolver + }, + children: [ + { + path: 'repository', + component: RepositoryComponent + }, + { + path: 'replication', + component: ReplicationComponent + }, + { + path: 'member', + component: MemberComponent + }, + { + path: 'log', + component: AuditLogComponent + } + ] + }, + { + path: 'configs', + component: ConfigurationComponent + } + ] }, - { path: '', redirectTo: '/harbor', pathMatch: 'full' }, - { path: 'sign-in', component: SignInComponent } + { path: "**", component: PageNotFoundComponent} ]; @NgModule({ diff --git a/src/ui_ng/src/app/i18n/lang/en-lang.json b/src/ui_ng/src/app/i18n/lang/en-lang.json deleted file mode 100644 index 1e6463fc9..000000000 --- a/src/ui_ng/src/app/i18n/lang/en-lang.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "LOG_IN": "LOG IN", - "SIGN_UP": "Sign up for an account", - "BUTTON": { - "CANCEL": "Cancel", - "OK": "Ok", - "DELETE": "DELETE" - }, - "TOOLTIP": { - "EMAIL": "Email should be a valid email address like name@example.com", - "USER_NAME": "Can not contain \"~#$% and max length should be less than 20", - "FULL_NAME": "Max length should be less than 20", - "COMMENT": "Length of comment should be less than 20", - "CURRENT_PWD": "Current password is Required", - "PASSWORD": "Password should be at least 7 characters with 1 uppercase, 1 lowercase letter and 1 number", - "CONFIRM_PWD": "Password input here should be same with above password" - }, - "PLACEHOLDER": { - "CURRENT_PWD": "Enter current password", - "NEW_PWD": "Enter new password", - "CONFIRM_PWD": "Confirm new password", - "USER_NAME": "Enter username", - "MAIL": "Enter email address", - "FULL_NAME": "Enter full name" - }, - "PROFILE": { - "TITLE": "User Profile", - "USER_NAME": "Username", - "EMAIL": "Email", - "FULL_NAME": "Full name", - "COMMENT": "Comments", - "PASSWORD": "Password" - }, - "CHANGE_PWD": { - "TITLE": "Change Password", - "CURRENT_PWD": "Current Password", - "NEW_PWD": "New Password", - "CONFIRM_PWD": "Confirm Password" - }, - "ACCOUNT_SETTINGS": { - "PROFILE": "User Profile", - "CHANGE_PWD": "Change Password", - "ABOUT": "About", - "LOGOUT": "Log Out" - }, - "GLOBAL_SEARCH": { - "PLACEHOLDER": "Search Harbor..." - }, - "SIDE_NAV": { - "PROJECTS": "Projects", - "SYSTEM_MGMT": { - "NAME": "System Managements", - "USERS": "Users", - "REPLICATIONS": "Replications", - "CONFIGS": "Configurations" - } - }, - "USER": { - "ADD_ACTION": "USER", - "ENABLE_ADMIN_ACTION": "Enable administrator", - "DISABLE_ADMIN_ACTION": "Disable administrator", - "DEL_ACTION": "Delete", - "FILTER_PLACEHOLDER": "Filter users", - "COLUMN_NAME": "Name", - "COLUMN_ADMIN": "Administrator", - "COLUMN_EMAIL": "Email", - "COLUMN_REG_NAME": "Registration time", - "IS_ADMIN": "Yes", - "IS_NOT_ADMIN": "No", - "ADD_USER_TITLE": "Add User" - }, - "PROJECT": { - "PROJECTS": "Projects", - "NAME": "Project Name", - "PUBLIC_OR_PRIVATE": "Public/Private", - "REPO_COUNT": "Repositories Count", - "CREATION_TIME": "Creation Time", - "DESCRIPTION": "Description", - "PUBLIC": "Public", - "PRIVATE": "Private", - "MAKE": "Make", - "NEW_POLICY": "New Policy", - "DELETE": "Delete", - "MY_PROJECTS": "My Projects", - "PUBLIC_PROJECTS": "Public Projects", - "NEW_PROJECT": "New Project", - "NAME_ALREADY_EXISTS": "Project name already exists.", - "NAME_IS_ILLEGAL": "Project name is illegal.", - "UNKNOWN_ERROR": "Unknown error occurred while creating project.", - "ITEMS": "item(s)", - "DELETE_TITLE": "Delete Project", - "DELETE_MESSAGE": "Are you sure to delete the project?", - "FILTER_PLACEHOLDER": "Filter Projects" - }, - "PROJECT_DETAIL": { - "REPOSITORIES": "Repositories", - "REPLICATION": "Replication", - "USERS": "Users", - "LOGS": "Logs" - }, - "MEMBER": { - "NEW_MEMBER": "New Member", - "NAME": "Name", - "ROLE": "Role", - "PROJECT_ADMIN": "Project Admin", - "DEVELOPER": "Developer", - "GUEST": "Guest", - "DELETE": "Delete", - "ITEMS": "item(s)", - "ACTIONS": "Actions", - "USERNAME_DOES_NOT_EXISTS": "Username does not exist.", - "USERNAME_ALREADY_EXISTS": "Username already exists.", - "UNKNOWN_ERROR": "Unknown error occurred while adding member.", - "FILTER_PLACEHOLDER": "Filter Members" - }, - "AUDIT_LOG": { - "USERNAME": "Username", - "REPOSITORY_NAME": "Repository Name", - "TAGS": "Tags", - "OPERATION": "Operation", - "TIMESTAMP": "Timestamp", - "ALL_OPERATIONS": "All Operations", - "PULL": "Pull", - "PUSH": "Push", - "CREATE": "Create", - "DELETE": "Delete", - "OTHERS": "Others", - "ADVANCED": "Advanced", - "SIMPLE": "Simple", - "ITEMS": "item(s)", - "FILTER_PLACEHOLDER": "Filter Logs" - } -} \ No newline at end of file diff --git a/src/ui_ng/src/app/i18n/lang/zh-lang.json b/src/ui_ng/src/app/i18n/lang/zh-lang.json deleted file mode 100644 index 7098a8712..000000000 --- a/src/ui_ng/src/app/i18n/lang/zh-lang.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "LOG_IN": "登录", - "SIGN_UP": "注册账号", - "BUTTON": { - "CANCEL": "取消", - "OK": "确定", - "DELETE": "删除" - }, - "TOOLTIP": { - "EMAIL": "请使用正确的邮箱地址,比如name@example.com", - "USER_NAME": "不能包含\"~#$%特殊字符且长度不能超过20", - "FULL_NAME": "长度不能超过20", - "COMMENT": "长度不能超过20", - "CURRENT_PWD": "当前密码必需", - "PASSWORD": "密码长度至少为7且需包含至少一个大写字符,一个小写字符和一个数字", - "CONFIRM_PWD": "当前密码须与上述输入密码一致" - }, - "PLACEHOLDER": { - "CURRENT_PWD": "输入当前密码", - "NEW_PWD": "输入新密码", - "CONFIRM_PWD": "确认新密码", - "USER_NAME": "输入用户名称", - "MAIL": "输入邮箱地址", - "FULL_NAME": "输入全名" - }, - "PROFILE": { - "TITLE": "用户设置", - "USER_NAME": "用户名", - "EMAIL": "邮箱", - "FULL_NAME": "全名", - "COMMENT": "注释", - "PASSWORD": "密码" - }, - "CHANGE_PWD": { - "TITLE": "修改密码", - "CURRENT_PWD": "当前密码", - "NEW_PWD": "新密码", - "CONFIRM_PWD": "确认密码" - }, - "ACCOUNT_SETTINGS": { - "PROFILE": "用户设置", - "CHANGE_PWD": "修改密码", - "ABOUT": "关于", - "LOGOUT": "退出" - }, - "GLOBAL_SEARCH": { - "PLACEHOLDER": "搜索 Harbor..." - }, - "SIDE_NAV": { - "PROJECTS": "项目", - "SYSTEM_MGMT": { - "NAME": "系统管理", - "USERS": "用户管理", - "REPLICATIONS": "复制管理", - "CONFIGS": "配置管理" - } - }, - "USER": { - "ADD_ACTION": "用户", - "ENABLE_ADMIN_ACTION": "设置为管理员", - "DISABLE_ADMIN_ACTION": "取消管理员", - "DEL_ACTION": "删除", - "FILTER_PLACEHOLDER": "过滤用户", - "COLUMN_NAME": "用户名", - "COLUMN_ADMIN": "管理员", - "COLUMN_EMAIL": "邮件", - "COLUMN_REG_NAME": "注册时间", - "IS_ADMIN": "是", - "IS_NOT_ADMIN": "否", - "ADD_USER_TITLE": "添加用户" - }, - "PROJECT": { - "PROJECTS": "项目", - "NAME": "项目名称", - "PUBLIC_OR_PRIVATE": "公开/私有", - "REPO_COUNT": "镜像仓库数", - "CREATION_TIME": "创建时间", - "DESCRIPTION": "描述", - "PUBLIC": "公开", - "PRIVATE": "私有", - "MAKE": "设为", - "NEW_POLICY": "新建策略", - "DELETE": "删除", - "MY_PROJECTS": "我的项目", - "PUBLIC_PROJECTS": "公开项目", - "NEW_PROJECT": "新建项目", - "NAME_ALREADY_EXISTS": "项目名称已存在。", - "NAME_IS_ILLEGAL": "项目名称非法。", - "UNKNOWN_ERROR": "创建项目时发生未知错误。", - "ITEMS": "条记录", - "DELETE_TITLE": "删除项目", - "DELETE_MESSAGE": "确认删除项目吗?", - "FILTER_PLACEHOLDER": "过滤项目" - }, - "PROJECT_DETAIL": { - "REPOSITORIES": "镜像仓库", - "REPLICATION": "复制", - "USERS": "用户", - "LOGS": "日志" - }, - "MEMBER": { - "NEW_MEMBER": "新增成员", - "NAME": "姓名", - "ROLE": "角色", - "SYS_ADMIN": "系统管理员", - "PROJECT_ADMIN": "项目管理员", - "DEVELOPER": "开发人员", - "GUEST": "访客", - "DELETE": "删除", - "ITEMS": "条记录", - "ACTIONS": "操作", - "USERNAME_DOES_NOT_EXISTS": "用户名不存在", - "USERNAME_ALREADY_EXISTS": "用户名已存在", - "UNKNOWN_ERROR": "添加成员时发生未知错误。", - "FILTER_PLACEHOLDER": "过滤成员" - }, - "AUDIT_LOG": { - "USERNAME": "用户名", - "REPOSITORY_NAME": "镜像名称", - "TAGS": "标签", - "OPERATION": "操作", - "TIMESTAMP": "时间戳", - "ALL_OPERATIONS": "所有操作", - "PULL": "Pull", - "PUSH": "Push", - "CREATE": "Create", - "DELETE": "Delete", - "OTHERS": "其他", - "ADVANCED": "高级检索", - "SIMPLE": "简单检索", - "ITEMS": "条记录", - "FILTER_PLACEHOLDER": "过滤日志" - } -} \ No newline at end of file diff --git a/src/ui_ng/src/app/i18n/missing-trans.handler.ts b/src/ui_ng/src/app/i18n/missing-trans.handler.ts index 99600306f..eb7be632a 100644 --- a/src/ui_ng/src/app/i18n/missing-trans.handler.ts +++ b/src/ui_ng/src/app/i18n/missing-trans.handler.ts @@ -1,8 +1,8 @@ -import {MissingTranslationHandler, MissingTranslationHandlerParams} from '@ngx-translate/core'; +import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx-translate/core'; export class MyMissingTranslationHandler implements MissingTranslationHandler { handle(params: MissingTranslationHandlerParams) { const missingText = "{Miss Harbor Text}"; - return missingText; + return params.key || missingText; } } \ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.component.html b/src/ui_ng/src/app/log/audit-log.component.html index cb65b61be..046394238 100644 --- a/src/ui_ng/src/app/log/audit-log.component.html +++ b/src/ui_ng/src/app/log/audit-log.component.html @@ -5,7 +5,8 @@
- + +
diff --git a/src/ui_ng/src/app/log/audit-log.component.ts b/src/ui_ng/src/app/log/audit-log.component.ts index cab83e24b..d20de2ada 100644 --- a/src/ui_ng/src/app/log/audit-log.component.ts +++ b/src/ui_ng/src/app/log/audit-log.component.ts @@ -136,4 +136,7 @@ export class AuditLogComponent implements OnInit { } this.doSearchByOptions(); } + refresh(): void { + this.retrieve(this.queryParam); + } } \ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.service.ts b/src/ui_ng/src/app/log/audit-log.service.ts index ce0ac3e7a..ce4002710 100644 --- a/src/ui_ng/src/app/log/audit-log.service.ts +++ b/src/ui_ng/src/app/log/audit-log.service.ts @@ -10,26 +10,39 @@ import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/map'; import 'rxjs/add/observable/throw'; -export const urlPrefix = ''; +export const logEndpoint = "/api/logs"; @Injectable() export class AuditLogService extends BaseService { - + private httpOptions = new RequestOptions({ + headers: new Headers({ + "Content-Type": 'application/json', + "Accept": 'application/json' + }) + }); + constructor(private http: Http) { super(); } listAuditLogs(queryParam: AuditLog): Observable { return this.http - .post(urlPrefix + `/api/projects/${queryParam.project_id}/logs/filter`, { - begin_timestamp: queryParam.begin_timestamp, - end_timestamp: queryParam.end_timestamp, - keywords: queryParam.keywords, - operation: queryParam.operation, - project_id: queryParam.project_id, - username: queryParam.username }) - .map(response=>response.json() as AuditLog[]) - .catch(error=>this.handleError(error)); + .post(`/api/projects/${queryParam.project_id}/logs/filter`, { + begin_timestamp: queryParam.begin_timestamp, + end_timestamp: queryParam.end_timestamp, + keywords: queryParam.keywords, + operation: queryParam.operation, + project_id: queryParam.project_id, + username: queryParam.username + }) + .map(response => response.json() as AuditLog[]) + .catch(error => this.handleError(error)); + } + + getRecentLogs(lines: number): Observable { + return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions) + .map(response => response.json() as AuditLog[]) + .catch(error => this.handleError(error)); } } \ No newline at end of file diff --git a/src/ui_ng/src/app/log/log.module.ts b/src/ui_ng/src/app/log/log.module.ts index 307df9dea..78a0bdb31 100644 --- a/src/ui_ng/src/app/log/log.module.ts +++ b/src/ui_ng/src/app/log/log.module.ts @@ -2,10 +2,16 @@ import { NgModule } from '@angular/core'; import { AuditLogComponent } from './audit-log.component'; import { SharedModule } from '../shared/shared.module'; import { AuditLogService } from './audit-log.service'; +import { RecentLogComponent } from './recent-log.component'; + @NgModule({ - imports: [ SharedModule ], - declarations: [ AuditLogComponent ], - providers: [ AuditLogService ], - exports: [ AuditLogComponent ] + imports: [SharedModule], + declarations: [ + AuditLogComponent, + RecentLogComponent], + providers: [AuditLogService], + exports: [ + AuditLogComponent, + RecentLogComponent] }) -export class LogModule {} \ No newline at end of file +export class LogModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.css b/src/ui_ng/src/app/log/recent-log.component.css new file mode 100644 index 000000000..b92cb32ec --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.css @@ -0,0 +1,32 @@ +.h2-log-override { + margin-top: 0px !important; +} + +.filter-log { + float: right; + margin-right: 24px; + position: relative; + top: 8px; +} + +.action-head-pos { + position: relative; + top: 20px; +} + +.refresh-btn { + position: absolute; + right: -4px; + top: 8px; + cursor: pointer; +} + +.custom-lines-button { + padding: 0px !important; + min-width: 25px !important; +} + +.lines-button-toggole { + font-size: 16px; + text-decoration: underline; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.html b/src/ui_ng/src/app/log/recent-log.component.html new file mode 100644 index 000000000..10bfe1963 --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.html @@ -0,0 +1,36 @@ +
+

{{'SIDE_NAV.LOGS' | translate}}

+
+ + + + + + + + + + + + + + +
+
+ + {{'AUDIT_LOG.USERNAME' | translate}} + {{'AUDIT_LOG.REPOSITORY_NAME' | translate}} + {{'AUDIT_LOG.TAGS' | translate}} + {{'AUDIT_LOG.OPERATION' | translate}} + {{'AUDIT_LOG.TIMESTAMP' | translate}} + + {{l.username}} + {{l.repo_name}} + {{l.repo_tag}} + {{l.operation}} + {{formatDateTime(l.op_time)}} + + {{ (recentLogs ? recentLogs.length : 0) }} {{'AUDIT_LOG.ITEMS' | translate}} + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.ts b/src/ui_ng/src/app/log/recent-log.component.ts new file mode 100644 index 000000000..b0bbeb474 --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +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'; +import { AlertType } from '../shared/shared.const'; +import { errorHandler, accessErrorHandler } from '../shared/shared.utils'; + +@Component({ + selector: 'recent-log', + templateUrl: './recent-log.component.html', + styleUrls: ['recent-log.component.css'] +}) + +export class RecentLogComponent implements OnInit { + private sessionUser: SessionUser = null; + private recentLogs: AuditLog[]; + private logsCache: AuditLog[]; + private onGoing: boolean = false; + private lines: number = 10; //Support 10, 25 and 50 + + constructor( + private session: SessionService, + private msgService: MessageService, + private logService: AuditLogService) { + this.sessionUser = this.session.getCurrentUser();//Initialize session + } + + ngOnInit(): void { + this.retrieveLogs(); + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public setLines(lines: number): void { + this.lines = lines; + if (this.lines < 10) { + this.lines = 10; + } + + this.retrieveLogs(); + } + + public doFilter(terms: string): void { + if (terms.trim() === "") { + this.recentLogs = this.logsCache.filter(log => log.username != ""); + return; + } + + this.recentLogs = this.logsCache.filter(log => this.isMatched(terms, log)); + } + + public refresh(): void { + this.retrieveLogs(); + } + + public formatDateTime(dateTime: string){ + let dt: Date = new Date(dateTime); + return dt.toLocaleString(); + } + + private retrieveLogs(): void { + if (this.lines < 10) { + this.lines = 10; + } + + this.onGoing = true; + this.logService.getRecentLogs(this.lines) + .subscribe( + response => { + this.onGoing = false; + this.logsCache = response; //Keep the data + this.recentLogs = this.logsCache.filter(log => log.username != "");//To display + }, + error => { + this.onGoing = false; + if (!accessErrorHandler(error, this.msgService)) { + this.msgService.announceMessage(error.status, errorHandler(error), AlertType.DANGER); + } + } + ); + } + + private isMatched(terms: string, log: AuditLog): boolean { + let reg = new RegExp('.*' + terms + '.*', 'i'); + return reg.test(log.username) || + reg.test(log.repo_name) || + reg.test(log.operation); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/action-project/action-project.component.html b/src/ui_ng/src/app/project/action-project/action-project.component.html deleted file mode 100644 index 1df167df6..000000000 --- a/src/ui_ng/src/app/project/action-project/action-project.component.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/ui_ng/src/app/project/action-project/action-project.component.ts b/src/ui_ng/src/app/project/action-project/action-project.component.ts deleted file mode 100644 index 562192f8a..000000000 --- a/src/ui_ng/src/app/project/action-project/action-project.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Project } from '../project'; -import { ProjectService } from '../project.service'; - -import { TranslateService } from '@ngx-translate/core'; -import { DeletionDialogService } from '../../shared/deletion-dialog/deletion-dialog.service'; -import { DeletionMessage } from '../../shared/deletion-dialog/deletion-message'; - -@Component({ - selector: 'action-project', - templateUrl: 'action-project.component.html' -}) -export class ActionProjectComponent { - - @Output() togglePublic = new EventEmitter(); - @Output() deleteProject = new EventEmitter(); - - @Input() project: Project; - - constructor(private projectService: ProjectService, - private deletionDialogService: DeletionDialogService, - private translateService: TranslateService) { - deletionDialogService.deletionConfirm$.subscribe(project=>this.deleteProject.emit(project)); - } - - toggle() { - if(this.project) { - this.project.public === 0 ? this.project.public = 1 : this.project.public = 0; - this.togglePublic.emit(this.project); - } - } - - delete() { - // if(this.project) { - // this.deleteProject.emit(this.project); - // } - let deletionMessage = new DeletionMessage('Delete Project', 'Do you confirm to delete project?', this.project); - this.deletionDialogService.openComfirmDialog(deletionMessage); - } -} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/create-project/create-project.component.html b/src/ui_ng/src/app/project/create-project/create-project.component.html index 63f0954a8..59bdfd416 100644 --- a/src/ui_ng/src/app/project/create-project/create-project.component.html +++ b/src/ui_ng/src/app/project/create-project/create-project.component.html @@ -1,14 +1,24 @@ diff --git a/src/ui_ng/src/app/project/create-project/create-project.component.ts b/src/ui_ng/src/app/project/create-project/create-project.component.ts index 819393bc0..35a0307cb 100644 --- a/src/ui_ng/src/app/project/create-project/create-project.component.ts +++ b/src/ui_ng/src/app/project/create-project/create-project.component.ts @@ -20,8 +20,8 @@ export class CreateProjectComponent { project: Project = new Project(); createProjectOpened: boolean; + errorMessageOpened: boolean; errorMessage: string; - hasError: boolean; @Output() create = new EventEmitter(); @@ -30,7 +30,6 @@ export class CreateProjectComponent { private translateService: TranslateService) {} onSubmit() { - this.hasError = false; this.projectService .createProject(this.project.name, this.project.public ? 1 : 0) .subscribe( @@ -39,7 +38,7 @@ export class CreateProjectComponent { this.createProjectOpened = false; }, error=>{ - this.hasError = true; + this.errorMessageOpened = true; if (error instanceof Response) { switch(error.status) { case 409: @@ -59,9 +58,15 @@ export class CreateProjectComponent { } newProject() { - this.hasError = false; this.project = new Project(); this.createProjectOpened = true; + this.errorMessageOpened = false; + this.errorMessage = ''; + } + + onErrorMessageClose(): void { + this.errorMessageOpened = false; + this.errorMessage = ''; } } diff --git a/src/ui_ng/src/app/project/list-project/list-project.component.html b/src/ui_ng/src/app/project/list-project/list-project.component.html index 5c3d870f3..68ff6382a 100644 --- a/src/ui_ng/src/app/project/list-project/list-project.component.html +++ b/src/ui_ng/src/app/project/list-project/list-project.component.html @@ -15,9 +15,12 @@ {{p.creation_time}} {{p.description}} - - - + + {{'PROJECT.NEW_POLICY' | translate}} + {{'PROJECT.MAKE' | translate}} {{(p.public === 0 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} + + {{'PROJECT.DELETE' | translate}} + {{ (projects ? projects.length : 0) }} {{'PROJECT.ITEMS' | translate}} diff --git a/src/ui_ng/src/app/project/member/add-member/add-member.component.html b/src/ui_ng/src/app/project/member/add-member/add-member.component.html index de11bc85c..389efb505 100644 --- a/src/ui_ng/src/app/project/member/add-member/add-member.component.html +++ b/src/ui_ng/src/app/project/member/add-member/add-member.component.html @@ -1,14 +1,21 @@