From b664c3c235c8f5a18c50f31dcd3b396033b77002 Mon Sep 17 00:00:00 2001
From: AllForNothing <sshijun@vmware.com>
Date: Mon, 17 Aug 2020 09:17:20 +0800
Subject: [PATCH] Fix UI bugs found on testing day for target 2.1

Signed-off-by: AllForNothing <sshijun@vmware.com>
---
 .../scanner/config-scanner.component.html      | 11 ++++++++++-
 .../distribution-instances.component.html      | 18 +++++++++++-------
 .../distribution-setup-modal.component.html    |  7 +++----
 .../distribution-setup-modal.component.ts      | 16 ++++++++++++++--
 .../list-project/list-project.component.html   |  2 +-
 .../list-project/list-project.component.ts     |  4 ++--
 .../add-p2p-policy.component.html              | 17 +++++++++--------
 .../add-p2p-policy.component.scss              |  3 +++
 .../add-p2p-policy.component.spec.ts           |  2 +-
 .../add-p2p-policy/add-p2p-policy.component.ts |  3 +++
 .../p2p-provider/policy/policy.component.html  |  6 +++---
 .../p2p-provider/policy/policy.component.ts    |  4 ++++
 src/portal/src/i18n/lang/en-us-lang.json       |  7 +++++--
 src/portal/src/i18n/lang/es-es-lang.json       |  7 +++++--
 src/portal/src/i18n/lang/fr-fr-lang.json       |  7 +++++--
 src/portal/src/i18n/lang/pt-br-lang.json       |  7 +++++--
 src/portal/src/i18n/lang/tr-tr-lang.json       |  7 +++++--
 src/portal/src/i18n/lang/zh-cn-lang.json       |  5 ++++-
 src/portal/src/i18n/lang/zh-tw-lang.json       |  7 +++++--
 .../endpoint/endpoint.component.html           |  9 +++++----
 src/portal/src/lib/services/interface.ts       |  1 +
 21 files changed, 104 insertions(+), 46 deletions(-)

diff --git a/src/portal/src/app/config/scanner/config-scanner.component.html b/src/portal/src/app/config/scanner/config-scanner.component.html
index 1148aa5cf..c7a843acf 100644
--- a/src/portal/src/app/config/scanner/config-scanner.component.html
+++ b/src/portal/src/app/config/scanner/config-scanner.component.html
@@ -81,7 +81,16 @@
                         </ng-template>
                     </ng-template>
                 </clr-dg-cell>
-                <clr-dg-cell>{{!scanner.disabled}}</clr-dg-cell>
+                <clr-dg-cell>
+                    <div *ngIf="!scanner.disabled" class="icon-wrap">
+                        <clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
+                        <span class="margin-left-5px">{{'WEBHOOK.ENABLED' | translate}}</span>
+                    </div>
+                    <div *ngIf="scanner.disabled" class="icon-wrap">
+                        <clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
+                        <span class="margin-left-5px">{{'WEBHOOK.DISABLED' | translate}}</span>
+                    </div>
+                </clr-dg-cell>
                 <clr-dg-cell>{{scanner.auth?scanner.auth:'None'}}</clr-dg-cell>
                 <scanner-metadata *clrIfExpanded [uid]="scanner.uuid"  ngProjectAs="clr-dg-row-detail"></scanner-metadata>
             </clr-dg-row>
diff --git a/src/portal/src/app/distribution/distribution-instances/distribution-instances.component.html b/src/portal/src/app/distribution/distribution-instances/distribution-instances.component.html
index 2b4d93567..40a041cdf 100644
--- a/src/portal/src/app/distribution/distribution-instances/distribution-instances.component.html
+++ b/src/portal/src/app/distribution/distribution-instances/distribution-instances.component.html
@@ -17,10 +17,6 @@
                               'DISTRIBUTION.ADD_ACTION' | translate
                                 }}
                             </button>
-                            <button id="set-default"
-                                    [disabled]="!(selectedRow && selectedRow.length === 1 && !selectedRow[0].default && selectedRow[0].enabled)"
-                                    class="btn  btn-secondary"
-                                    (click)="setAsDefault()">{{'SCANNER.SET_AS_DEFAULT' | translate}}</button>
                             <clr-dropdown
                                     [clrCloseMenuOnItemClick]="false"
                                     class="btn  btn-link"
@@ -78,9 +74,8 @@
                   'DISTRIBUTION.NOT_FOUND' | translate
                     }}</clr-dg-placeholder>
                 <clr-dg-row *ngFor="let instance of instances" [clrDgItem]="instance">
-                    <clr-dg-cell class="no-wrapper">
+                    <clr-dg-cell>
                         <span>{{ instance.name }}</span>
-                        <span *ngIf="instance.default" class="label label-info ml-1">{{'SCANNER.DEFAULT' | translate}}</span>
                     </clr-dg-cell>
                     <clr-dg-cell>{{ instance.endpoint }}</clr-dg-cell>
                     <clr-dg-cell class="no-wrapper">
@@ -122,7 +117,16 @@
                             </ng-template>
                         </ng-template>
                     </clr-dg-cell>
-                    <clr-dg-cell>{{ instance.enabled || false }}</clr-dg-cell>
+                    <clr-dg-cell>
+                        <div *ngIf="instance.enabled" class="icon-wrap">
+                            <clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
+                            <span class="margin-left-5px">{{'WEBHOOK.ENABLED' | translate}}</span>
+                        </div>
+                        <div *ngIf="!instance.enabled" class="icon-wrap">
+                            <clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
+                            <span class="margin-left-5px">{{'WEBHOOK.DISABLED' | translate}}</span>
+                        </div>
+                    </clr-dg-cell>
                     <clr-dg-cell>{{ instance.auth_mode }}</clr-dg-cell>
                     <clr-dg-cell>{{fmtTime(instance.setup_timestamp) | date: 'short'}}</clr-dg-cell>
                     <clr-dg-cell>{{ instance.description }}</clr-dg-cell>
diff --git a/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.html b/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.html
index cd7df4b3c..f253b595e 100644
--- a/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.html
+++ b/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.html
@@ -17,7 +17,6 @@
           name="provider"
           id="provider"
           [(ngModel)]="model.vendor"
-          [disabled]="editingMode"
           required
         >
           <option class="display-none" value=""></option>
@@ -45,7 +44,6 @@
                    [(ngModel)]="model.name"
                    name="name"
                    #nameNg="ngModel"
-                   [disabled]="editingMode"
                    (input)="inputName()"
             />
             <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
@@ -194,7 +192,7 @@
             class="width-280"
             clrInput
             required
-            type="text"
+            type="password"
             id="auth_data_token"
             [(ngModel)]="authData['token']"
             placeholder="{{
@@ -233,7 +231,7 @@
               <clr-tooltip>
                 <clr-icon class="color-57"  clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
                 <clr-tooltip-content  clrPosition="top-left" clrSize="md" *clrIfOpen>
-                  {{'SCANNER.SKIP_CERT_VERIFY' | translate}}
+                  {{'P2P_PROVIDER.SKIP_CERT_VERIFY' | translate}}
                 </clr-tooltip-content>
               </clr-tooltip>
             </label>
@@ -248,6 +246,7 @@
       {{ 'BUTTON.CANCEL' | translate }}
     </button>
     <button
+      id="instance-ok"
       [clrLoading]="saveBtnState"
       type="button"
       class="btn btn-primary"
diff --git a/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.ts b/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.ts
index 564f9f37c..319f7636e 100644
--- a/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.ts
+++ b/src/portal/src/app/distribution/distribution-setup-modal/distribution-setup-modal.component.ts
@@ -83,6 +83,9 @@ export class DistributionSetupModalComponent implements OnInit, OnDestroy {
            debounceTime(500),
            distinctUntilChanged(),
            filter(name => {
+             if (this.editingMode && this.originModelForEdit && this.originModelForEdit.name === name) {
+               return false;
+             }
             return  name.length > 0;
            }),
            switchMap((name) => {
@@ -106,6 +109,9 @@ export class DistributionSetupModalComponent implements OnInit, OnDestroy {
           debounceTime(500),
           distinctUntilChanged(),
           filter(endpoint => {
+            if (this.editingMode && this.originModelForEdit && this.originModelForEdit.endpoint === endpoint) {
+              return false;
+            }
             return this.instanceForm.control.get('endpoint').valid;
           }),
           switchMap((endpoint) => {
@@ -176,14 +182,12 @@ export class DistributionSetupModalComponent implements OnInit, OnDestroy {
       name: '',
       endpoint: '',
       enabled: true,
-      insecure: true,
       vendor: '',
       auth_mode: AuthMode.NONE,
       auth_info: this.authData
     };
     this.instanceForm.reset({
       enabled: true,
-      insecure: true,
     });
   }
 
@@ -201,6 +205,8 @@ export class DistributionSetupModalComponent implements OnInit, OnDestroy {
       this.operationService.publishInfo(operMessageForEdit);
       this.saveBtnState = ClrLoadingState.LOADING;
       const instance: Instance = clone(this.originModelForEdit);
+      instance.vendor = this.model.vendor;
+      instance.name = this.model.name;
       instance.endpoint = this.model.endpoint;
       instance.enabled = this.model.enabled;
       instance.description = this.model.description;
@@ -287,6 +293,12 @@ export class DistributionSetupModalComponent implements OnInit, OnDestroy {
 
   hasChangesForEdit(): boolean {
     if ( this.editingMode) {
+      if ( this.model.vendor !== this.originModelForEdit.vendor) {
+        return true;
+      }
+      if ( this.model.name !== this.originModelForEdit.name) {
+        return true;
+      }
       if ( this.model.description !== this.originModelForEdit.description) {
         return true;
       }
diff --git a/src/portal/src/app/project/list-project/list-project.component.html b/src/portal/src/app/project/list-project/list-project.component.html
index 88eb23f70..89aea317f 100644
--- a/src/portal/src/app/project/list-project/list-project.component.html
+++ b/src/portal/src/app/project/list-project/list-project.component.html
@@ -19,7 +19,7 @@
         </clr-dg-cell>
         <clr-dg-cell>{{ (p.metadata.public === 'true' ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
         <clr-dg-cell>{{ roleInfo[p.current_user_role_id]? (roleInfo[p.current_user_role_id] | translate): "-"}}</clr-dg-cell>
-        <clr-dg-cell>{{projectTypeMap[p.registry_id ? 1 : 0]}}</clr-dg-cell>
+        <clr-dg-cell>{{projectTypeMap[p.registry_id ? 1 : 0] | translate}}</clr-dg-cell>
         <clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
         <clr-dg-cell *ngIf="withChartMuseum">{{p.chart_count}}</clr-dg-cell>
         <clr-dg-cell>{{p.creation_time | date: 'short'}}</clr-dg-cell>
diff --git a/src/portal/src/app/project/list-project/list-project.component.ts b/src/portal/src/app/project/list-project/list-project.component.ts
index 8c2744cc6..a3c630981 100644
--- a/src/portal/src/app/project/list-project/list-project.component.ts
+++ b/src/portal/src/app/project/list-project/list-project.component.ts
@@ -62,8 +62,8 @@ export class ListProjectComponent implements OnDestroy {
     currentState: State;
     subscription: Subscription;
     projectTypeMap: any = {
-        0: "Project",
-        1: "Proxy Cache"
+        0: "PROJECT.PROJECT",
+        1: "PROJECT.PROXY_CACHE"
     };
 
     constructor(
diff --git a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.html b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.html
index 1fdb07522..ac99bf505 100644
--- a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.html
+++ b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.html
@@ -21,12 +21,13 @@
               {{'P2P_PROVIDER.PROVIDER_REQUIRED' | translate}}
             </clr-control-error>
           </clr-select-container>
-          <div class="clr-form-control mt-0" *ngIf="isSystemAdmin() &&!(providers && providers.length)">
+          <div class="clr-form-control mt-0" *ngIf="!(providers && providers.length)">
             <label class="clr-control-label width-6rem"></label>
             <div class="clr-control-container width-380">
               <div class="space-between">
-                <span class="alert-label">{{"P2P_PROVIDER.NO_PROVIDER" | translate}}</span>
-                <a class="go-link" routerLink="/harbor/distribution/instances">{{'P2P_PROVIDER.PROVIDER' | translate}}</a>
+                <span class="alert-label" *ngIf="isSystemAdmin()">{{"P2P_PROVIDER.NO_PROVIDER" | translate}}</span>
+                <span class="alert-label" *ngIf="!isSystemAdmin()">{{"P2P_PROVIDER.NEED_HELP" | translate}}</span>
+                <a *ngIf="isSystemAdmin()" class="go-link" routerLink="/harbor/distribution/instances">{{'P2P_PROVIDER.PROVIDER' | translate}}</a>
               </div>
             </div>
           </div>
@@ -43,7 +44,7 @@
               </div>
               <clr-control-error *ngIf="((name.dirty || name.touched) && name.invalid) || isNameExisting">
                 <span *ngIf="!((name.dirty || name.touched) && name.invalid) && isNameExisting">{{'SCANNER.NAME_EXISTS' | translate}}</span>
-                <span *ngIf="(name.dirty || name.touched) && name.invalid">{{ 'PROJECT.NAME_TOOLTIP' | translate }}</span>
+                <span *ngIf="(name.dirty || name.touched) && name.invalid">{{ 'P2P_PROVIDER.NAME_TOOLTIP' | translate }}</span>
               </clr-control-error>
             </div>
           </div>
@@ -57,10 +58,10 @@
           </div>
           <!-- filters-repo -->
           <div class="clr-form-control">
-            <label for="repo" class="clr-control-label required width-6rem">{{'P2P_PROVIDER.FILTERS' | translate}}</label>
+            <label for="repo" class="clr-control-label width-6rem">{{'P2P_PROVIDER.FILTERS' | translate}}</label>
             <div class="clr-control-container" [class.clr-error]="repo.errors && repo.errors.required && (repo.dirty || repo.touched)">
               <div class="clr-input-wrapper">
-                <label class="sub-label">{{'P2P_PROVIDER.REPOS' | translate}}</label>
+                <label class="sub-label required">{{'P2P_PROVIDER.REPOS' | translate}}</label>
                 <input placeholder="**" [disabled]="loading" autocomplete="off"  class="clr-input width-290" type="text" id="repo" [(ngModel)]="repos"
                        size="30" name="repo" #repo="ngModel" required>
                 <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
@@ -76,7 +77,7 @@
             <label for="repo" class="width-6rem"></label>
             <div class="clr-control-container" [class.clr-error]="tag.errors && tag.errors.required && (tag.dirty || tag.touched)">
               <div class="clr-input-wrapper">
-                <label class="sub-label">{{'P2P_PROVIDER.TAGS' | translate}}</label>
+                <label class="sub-label required">{{'P2P_PROVIDER.TAGS' | translate}}</label>
                 <input placeholder="**" [disabled]="loading" autocomplete="off"  class="clr-input width-290" type="text" id="tag" [(ngModel)]="tags"
                        size="30" name="tag" #tag="ngModel" required>
                 <clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
@@ -111,7 +112,7 @@
             <div class="clr-control-container opacity-054">
               <div class="clr-checkbox-wrapper">
                 <input class="clr-checkbox" disabled type="checkbox" id="onlySignedImages" [(ngModel)]="onlySignedImages" name="onlySignedImages">
-                <label for="onlySignedImages">{{'P2P_PROVIDER.ONLY_SIGNED' | translate}}</label>
+                <label class="font-size-12" for="onlySignedImages">{{'P2P_PROVIDER.ONLY_SIGNED' | translate}}</label>
               </div>
             </div>
           </div>
diff --git a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.scss b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.scss
index 3a395316e..0cf619132 100644
--- a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.scss
+++ b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.scss
@@ -83,3 +83,6 @@
 .opacity-054 {
   opacity: 0.54;
 }
+.font-size-12 {
+  font-size: 12px;
+}
diff --git a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.spec.ts b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.spec.ts
index b6d0d2cd9..48078df32 100644
--- a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.spec.ts
+++ b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.spec.ts
@@ -138,7 +138,7 @@ describe('AddP2pPolicyComponent', () => {
         nameInput.dispatchEvent(new Event('input'));
         nameInput.blur();
         const errorEle: HTMLElement = fixture.nativeElement.querySelector("clr-control-error");
-        expect(errorEle.innerText).toEqual('PROJECT.NAME_TOOLTIP');
+        expect(errorEle.innerText).toEqual('P2P_PROVIDER.NAME_TOOLTIP');
     });
     it("save button should work", async () => {
         fixture.autoDetectChanges(true);
diff --git a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts
index fdc1da6f5..ef8661564 100644
--- a/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts
+++ b/src/portal/src/app/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts
@@ -78,6 +78,8 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
   private _nameSubscription: Subscription;
   isNameExisting: boolean = false;
   checkNameOnGoing: boolean = false;
+  @Output()
+  hasInit: EventEmitter<boolean> = new EventEmitter<boolean>();
   constructor(private preheatService: PreheatService,
               private session: SessionService,
               private route: ActivatedRoute,
@@ -141,6 +143,7 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
           this.enableContentTrust = project.metadata.enable_content_trust === TRUE;
           this.severity = PROJECT_SEVERITY_LEVEL_MAP[this.projectSeverity];
         }
+        this.hasInit.emit(true);
       });
   }
 
diff --git a/src/portal/src/app/project/p2p-provider/policy/policy.component.html b/src/portal/src/app/project/p2p-provider/policy/policy.component.html
index 8a42d3fbc..1886463b1 100644
--- a/src/portal/src/app/project/p2p-provider/policy/policy.component.html
+++ b/src/portal/src/app/project/p2p-provider/policy/policy.component.html
@@ -12,7 +12,7 @@
         <clr-dg-action-bar>
             <div class="clr-row">
                 <div class="clr-col-7">
-                    <button (click)="newPolicy()" [disabled]="!hasCreatPermission" [clrLoading]="addBtnState"
+                    <button (click)="newPolicy()" [disabled]="!hasCreatPermission || !hasAddModalInit" [clrLoading]="addBtnState"
                             id="new-policy" type="button" class="btn  btn-secondary">
                         <clr-icon shape="plus" size="16"></clr-icon>
                         {{'P2P_PROVIDER.NEW_POLICY' | translate}}
@@ -198,7 +198,7 @@
             <clr-dg-cell>{{getTriggerTypeI18nForExecution(execution.trigger) | translate}}</clr-dg-cell>
             <clr-dg-cell>{{execution.start_time | date: 'short'}}</clr-dg-cell>
             <clr-dg-cell>{{getDuration(execution)}}</clr-dg-cell>
-            <clr-dg-cell>{{getSuccessRate(execution.metrics)}}</clr-dg-cell>
+            <clr-dg-cell>{{getSuccessRate(execution.metrics) | percent}}</clr-dg-cell>
             <clr-dg-cell>{{execution.vendor_type}}</clr-dg-cell>
         </clr-dg-row>
         <clr-dg-footer>
@@ -210,7 +210,7 @@
         </clr-dg-footer>
     </clr-datagrid>
 </div>
-<add-p2p-policy [providers]="providers" (notify)="success($event)"></add-p2p-policy>
+<add-p2p-policy (hasInit)="addModalInit()" [providers]="providers" (notify)="success($event)"></add-p2p-policy>
 <confirmation-dialog (confirmAction)="confirmSwitch($event)" #confirmationDialogComponent></confirmation-dialog>
 
 
diff --git a/src/portal/src/app/project/p2p-provider/policy/policy.component.ts b/src/portal/src/app/project/p2p-provider/policy/policy.component.ts
index 1245c173b..29ec04a53 100644
--- a/src/portal/src/app/project/p2p-provider/policy/policy.component.ts
+++ b/src/portal/src/app/project/p2p-provider/policy/policy.component.ts
@@ -81,6 +81,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
   project: Project;
   severity_map: any = PROJECT_SEVERITY_LEVEL_TO_TEXT_MAP;
   timeout: any;
+  hasAddModalInit: boolean = false;
   constructor(
     private route: ActivatedRoute,
     private router: Router,
@@ -108,6 +109,9 @@ export class PolicyComponent implements OnInit, OnDestroy {
      }
      this.clearLoop();
   }
+  addModalInit() {
+     this.hasAddModalInit = true;
+  }
   getPermissions() {
     const permissionsList: Observable<boolean>[] = [];
     permissionsList.push(this.userPermissionService.getPermission(this.projectId,
diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json
index e3f693975..661d7b345 100644
--- a/src/portal/src/i18n/lang/en-us-lang.json
+++ b/src/portal/src/i18n/lang/en-us-lang.json
@@ -1474,7 +1474,7 @@
         "UPDATE_FAILED": "Updating instance failed",
         "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
         "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-        "DESCRIPTION": "description",
+        "DESCRIPTION": "Description",
         "AUTH_MODE": "Auth Mode",
         "USERNAME": "Username",
         "PASSWORD": "Password",
@@ -1567,6 +1567,9 @@
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
         "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
         "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+        "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+        "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+        "NEED_HELP": "Please ask your system admin to add a provider first"
     }
 }
diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json
index fc50b4e3a..7521d58a9 100644
--- a/src/portal/src/i18n/lang/es-es-lang.json
+++ b/src/portal/src/i18n/lang/es-es-lang.json
@@ -1472,7 +1472,7 @@
         "UPDATE_FAILED": "Updating instance failed",
         "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
         "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-        "DESCRIPTION": "description",
+        "DESCRIPTION": "Description",
         "AUTH_MODE": "Auth Mode",
         "USERNAME": "Username",
         "PASSWORD": "Password",
@@ -1565,6 +1565,9 @@
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
         "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
         "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+        "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+        "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+        "NEED_HELP": "Please ask your system admin to add a provider first"
     }
 }
diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json
index 6ddc8aa0c..ecefc6f0c 100644
--- a/src/portal/src/i18n/lang/fr-fr-lang.json
+++ b/src/portal/src/i18n/lang/fr-fr-lang.json
@@ -1442,7 +1442,7 @@
         "UPDATE_FAILED": "Updating instance failed",
         "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
         "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-        "DESCRIPTION": "description",
+        "DESCRIPTION": "Description",
         "AUTH_MODE": "Auth Mode",
         "USERNAME": "Username",
         "PASSWORD": "Password",
@@ -1535,6 +1535,9 @@
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
         "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
         "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+        "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+        "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+        "NEED_HELP": "Please ask your system admin to add a provider first"
     }
 }
diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json
index 7c7bf23e1..cec83fb1f 100644
--- a/src/portal/src/i18n/lang/pt-br-lang.json
+++ b/src/portal/src/i18n/lang/pt-br-lang.json
@@ -1470,7 +1470,7 @@
         "UPDATE_FAILED": "Updating instance failed",
         "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
         "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-        "DESCRIPTION": "description",
+        "DESCRIPTION": "Description",
         "AUTH_MODE": "Auth Mode",
         "USERNAME": "Username",
         "PASSWORD": "Password",
@@ -1563,7 +1563,10 @@
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
         "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
         "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+        "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+        "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+        "NEED_HELP": "Please ask your system admin to add a provider first"
     }
 
 }
diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json
index 2a4ea2701..60833b7a6 100644
--- a/src/portal/src/i18n/lang/tr-tr-lang.json
+++ b/src/portal/src/i18n/lang/tr-tr-lang.json
@@ -1474,7 +1474,7 @@
         "UPDATE_FAILED": "Updating instance failed",
         "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
         "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-        "DESCRIPTION": "description",
+        "DESCRIPTION": "Description",
         "AUTH_MODE": "Auth Mode",
         "USERNAME": "Username",
         "PASSWORD": "Password",
@@ -1567,6 +1567,9 @@
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
         "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
         "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+        "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+        "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+        "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+        "NEED_HELP": "Please ask your system admin to add a provider first"
     }
 }
diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json
index af6a5da86..770b87737 100644
--- a/src/portal/src/i18n/lang/zh-cn-lang.json
+++ b/src/portal/src/i18n/lang/zh-cn-lang.json
@@ -1564,6 +1564,9 @@
         "TAG_SEPARATOR": "使用逗号分割 tags,tag* 和 **",
         "CONTENT_WARNING": "内容信任设置与项目设置冲突且将会被项目设置覆盖",
         "PREHEAT_EXPLAIN": "预热功能可加载镜像至 P2P 网络",
-        "CRITERIA_EXPLAIN": "由项目设置下的'部署安全'项所指定"
+        "CRITERIA_EXPLAIN": "由项目设置下的'部署安全'项所指定",
+        "SKIP_CERT_VERIFY": "当远端服务使用自签或不可信证书时可勾选此项以跳过证书认证。",
+        "DESTINATION_NAME_TOOLTIP": "策略名称由小写字符、数字和._-/组成且至少2个字符并以字符或者数字开头。",
+        "NEED_HELP": "请先让您的系统管理员添加提供商"
     }
 }
diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json
index 2f1f2e7ed..1374c74c5 100644
--- a/src/portal/src/i18n/lang/zh-tw-lang.json
+++ b/src/portal/src/i18n/lang/zh-tw-lang.json
@@ -1458,7 +1458,7 @@
     "UPDATE_FAILED": "Updating instance failed",
     "REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
     "REQUEST_PREHEAT_FAILED": "Preheat request failed",
-    "DESCRIPTION": "description",
+    "DESCRIPTION": "Description",
     "AUTH_MODE": "Auth Mode",
     "USERNAME": "Username",
     "PASSWORD": "Password",
@@ -1551,6 +1551,9 @@
     "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
     "CONTENT_WARNING": "Content trust settings here conflicts with the relevant project configuration that will override the settings here",
     "PREHEAT_EXPLAIN": "Preheat will migrate the image to the p2p network",
-    "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab"
+    "CRITERIA_EXPLAIN": "As specified in 'Deployment security' section under Configuration tab",
+    "SKIP_CERT_VERIFY": "Check this box to skip certificate verification when the remote provider uses a self-signed or untrusted certificate.",
+    "NAME_TOOLTIP": "Policy name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
+    "NEED_HELP": "Please ask your system admin to add a provider first"
   }
 }
diff --git a/src/portal/src/lib/components/endpoint/endpoint.component.html b/src/portal/src/lib/components/endpoint/endpoint.component.html
index ef0787c91..fe66f2815 100644
--- a/src/portal/src/lib/components/endpoint/endpoint.component.html
+++ b/src/portal/src/lib/components/endpoint/endpoint.component.html
@@ -27,10 +27,11 @@
                 <clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
                 <clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
                     <clr-dg-cell class="flex-min-width">{{t.name}}</clr-dg-cell>
-                    <clr-dg-cell [ngSwitch]="t.status" class="flex-min-width">
-                        <div *ngSwitchCase="'unhealthy'"><clr-icon shape="exclamation-circle" class="is-error text-alignment" size="22"></clr-icon> Unhealthy</div>
-                        <div *ngSwitchCase="'healthy'"><clr-icon shape="success-standard" class="is-success text-alignment" size="18"></clr-icon> Healthy</div>
-                        <div *ngSwitchCase="'unknown' || ''"><clr-icon shape="exclamation-triangle" class="is-warning text-alignment" size="22"></clr-icon> Unknown</div>
+                    <clr-dg-cell class="flex-min-width">
+                        <span *ngIf="t.status === 'healthy';else elseBlock" class="label label-success">{{'SCANNER.HEALTHY' | translate}}</span>
+                        <ng-template #elseBlock>
+                            <span class="label label-danger">{{'SCANNER.UNHEALTHY' | translate}}</span>
+                        </ng-template>
                     </clr-dg-cell>
                     <clr-dg-cell class="flex-min-width">{{t.url}}</clr-dg-cell>
                     <clr-dg-cell>{{getAdapterText(t.type)}}</clr-dg-cell>
diff --git a/src/portal/src/lib/services/interface.ts b/src/portal/src/lib/services/interface.ts
index 6d440ee5b..b74de9a35 100644
--- a/src/portal/src/lib/services/interface.ts
+++ b/src/portal/src/lib/services/interface.ts
@@ -34,6 +34,7 @@ export interface Endpoint extends Base {
   name: string;
   type: string;
   url: string;
+  status?: string;
 }
 
 export interface PingEndpoint extends Base {