You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by dg...@apache.org on 2020/04/29 15:54:03 UTC

[incubator-dlab] 01/01: [DLAB-1758]: Added audit page

This is an automated email from the ASF dual-hosted git repository.

dgnatyshyn pushed a commit to branch DLAB-1758
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git

commit 299b1db193671275845298868dc1538431342b13
Author: Dmytro Gnatyshyn <di...@ukr.net>
AuthorDate: Wed Apr 29 18:53:23 2020 +0300

    [DLAB-1758]: Added audit page
---
 .../main/resources/webapp/src/app/app.module.ts    |   4 +-
 .../resources/webapp/src/app/app.routing.module.ts |  10 +-
 .../app/audit/audit-grid/audit-grid.component.html |  97 +++++++++
 .../app/audit/audit-grid/audit-grid.component.scss | 219 +++++++++++++++++++++
 .../app/audit/audit-grid/audit-grid.component.ts   |  50 +++++
 .../audit-toolbar/audit-toolbar.component.html}    |  10 +-
 .../audit-toolbar/audit-toolbar.component.scss     | 140 +++++++++++++
 .../audit/audit-toolbar/audit-toolbar.component.ts | 113 +++++++++++
 .../webapp/src/app/audit/audit.component.ts        |  94 +++++++++
 .../resources/webapp/src/app/audit/audit.module.ts |  48 +++++
 .../webapp/src/app/audit/filter-audit.model.ts     |  21 ++
 .../webapp/src/app/core/services/audit.service.ts  |  23 +++
 .../src/app/reporting/reporting.component.ts       |   1 -
 .../app/reporting/toolbar/toolbar.component.html   |   4 +-
 .../src/app/shared/navbar/navbar.component.html    |   5 +
 15 files changed, 828 insertions(+), 11 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/app.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.module.ts
index e23e14a..767eba1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.module.ts
@@ -30,7 +30,7 @@ import { AppComponent } from './app.component';
 import { AppRoutingModule } from './app.routing.module';
 
 import { LoginModule } from './login/login.module';
-import { LayoutModule } from './layout/layout.module'
+import { LayoutModule } from './layout/layout.module';
 
 import { GuidesModule } from './help';
 import { ServicePagesModule } from './service-pages/service-pages.module';
@@ -41,6 +41,7 @@ import { AdministrationModule } from './administration/administration.module';
 import { WebterminalModule } from './webterminal';
 import { CoreModule } from './core/core.module';
 import { SwaggerAPIModule } from './swagger';
+import {AuditModule} from './audit/audit.module';
 
 @NgModule({
   declarations: [AppComponent],
@@ -56,6 +57,7 @@ import { SwaggerAPIModule } from './swagger';
     GuidesModule,
     ServicePagesModule,
     ReportingModule,
+    AuditModule,
     AdministrationModule,
     WebterminalModule,
     SwaggerAPIModule,
diff --git a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
index 3c8ae3f..0cc0f4a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/app.routing.module.ts
@@ -21,7 +21,7 @@ import { ModuleWithProviders } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 
 import { LoginComponent } from './login/login.module';
-import { LayoutComponent } from './layout/layout.component'
+import { LayoutComponent } from './layout/layout.component';
 import { ResourcesComponent } from './resources/resources.component';
 import { AccessNotebookGuideComponent, PublicKeyGuideComponent } from './help';
 import { NotFoundComponent } from './service-pages/not-found/not-found.component';
@@ -34,6 +34,7 @@ import { RolesComponent } from './administration/roles/roles.component';
 import { SwaggerComponent } from './swagger/swagger.component';
 
 import { AuthorizationGuard, CheckParamsGuard, CloudProviderGuard, AdminGuard } from './core/services';
+import {AuditComponent} from './audit/audit.component';
 
 const routes: Routes = [{
   path: 'login',
@@ -79,7 +80,12 @@ const routes: Routes = [{
       path: 'help/accessnotebookguide',
       component: AccessNotebookGuideComponent,
       canActivate: [AuthorizationGuard]
-    }
+    },
+    {
+      path: 'audit',
+      component: AuditComponent,
+      canActivate: [AuthorizationGuard, AdminGuard],
+    },
   ]
 }, {
   path: 'terminal/:id/:endpoint',
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.html
new file mode 100644
index 0000000..c8d33e8
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.html
@@ -0,0 +1,97 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<section class="audit-table-wrapper">
+  <table mat-table [dataSource]="auditData" class="data-grid audit mat-elevation-z6">
+
+    <ng-container matColumnDef="user">
+      <th mat-header-cell *matHeaderCellDef class="th_user label-header">
+        <div class="label"><span> User</span></div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.users.length > 0; else user_filtered">filter_list</span>
+            <ng-template #user_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef="let element"><span class="table-item user-col">{{element.user}}</span></td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="action">
+      <th mat-header-cell *matHeaderCellDef class="th_action label-header">
+<!--        <div class="sort">-->
+<!--          <div class="sort-arrow up" (click)="sortBy('user', 'down')" [ngClass]="{'active': !!this.active['userdown']}"></div>-->
+<!--          <div class="sort-arrow down" (click)="sortBy('user', 'up')" [ngClass]="{'active': !!this.active['userup']}"></div>-->
+<!--        </div>-->
+        <div class="label">
+          <span class="text"> Action </span>
+        </div>
+        <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+          <i class="material-icons">
+            <span *ngIf="filterAuditData.actions.length > 0; else action_filtered">filter_list</span>
+            <ng-template #action_filtered>more_vert</ng-template>
+          </i>
+        </button>
+      </th>
+      <td mat-cell *matCellDef=" let element"> {{element.action}} </td>
+      <td mat-footer-cell *matFooterCellDef  class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="date">
+      <th mat-header-cell *matHeaderCellDef class="th_date label-header">
+<!--        <div class="sort">-->
+<!--          <div class="sort-arrow up" (click)="sortBy('project', 'down')" [ngClass]="{'active': !!this.active['projectdown']}"></div>-->
+<!--          <div class="sort-arrow down" (click)="sortBy('project', 'up')" [ngClass]="{'active': !!this.active['projectup']}"></div>-->
+<!--        </div>-->
+        <div class="label"><span class="text">Date</span></div>
+      </th>
+      <td mat-cell *matCellDef="let element"> {{element.date}} </td>
+      <td mat-footer-cell *matFooterCellDef class="table-footer"></td>
+    </ng-container>
+
+    <ng-container matColumnDef="user-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'users'"
+                               [items]="filterConfiguration.users" [model]="filterAuditData.users"></multi-select-dropdown>
+      </th>
+    </ng-container>
+    <ng-container matColumnDef="action-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+        <multi-select-dropdown *ngIf="filterConfiguration" (selectionChange)="onUpdate($event)" [type]="'actions'"
+                               [items]="filterConfiguration.actions" [model]="filterAuditData.actions"></multi-select-dropdown>
+      </th>
+    </ng-container>
+    <ng-container matColumnDef="date-filter">
+      <th mat-header-cell *matHeaderCellDef class="filter-row-item">
+
+      </th>
+    </ng-container>
+
+    <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr>
+
+    <tr [hidden]="!collapseFilterRow" mat-header-row *matHeaderRowDef="displayedFilterColumns; sticky: true"
+      class="filter-row"></tr>
+    <tr mat-row *matRowDef="let row; columns: displayedColumns;" class="content-row"></tr>
+
+<!--    <tr [hidden]="!auditData?.length" mat-footer-row *matFooterRowDef="displayedColumns; sticky: true"-->
+<!--      class="header-row"></tr>-->
+<!--    <tr [hidden]="reportData?.length" mat-footer-row *matFooterRowDef="['placeholder']"></tr>-->
+  </table>
+</section>
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.scss
new file mode 100644
index 0000000..352384a
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.scss
@@ -0,0 +1,219 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.audit-table-wrapper {
+  width: 100%;
+
+  .audit {
+    width: 100%;
+    min-width: 1100px;
+    overflow: auto;
+    border-collapse: inherit;
+
+    .mat-cell {
+      vertical-align: middle;
+    }
+
+    tr {
+      .th_user {
+        width: 25%;
+      }
+
+      .th_action {
+        width: 50%;
+      }
+
+      .th_date {
+        width: 20%;
+      }
+
+      th {
+        padding-right: 5px;
+        z-index: 2 !important;
+
+        &.th_charges {
+          z-index: 3 !important;
+        }
+      }
+
+      td {
+        font-size: 13px;
+        padding-left: 15px;
+
+        &.info {
+          z-index: 1 !important;
+          text-align: center;
+          padding: 40px;
+        }
+      }
+
+      &.filter-row {
+        th {
+          padding: 5px;
+          font-size: 13px;
+        }
+
+        .filter-field {
+          font-size: 13px;
+        }
+
+      }
+
+      &.header-row {
+        th {
+          font-size: 11px;
+
+          .label {
+            padding-left: 0;
+          }
+        }
+      }
+    }
+
+
+    .tags {
+      .label {
+        padding-top: 0;
+      }
+    }
+
+    .service {
+      min-width: 175px;
+    }
+
+    .env_name {
+      width: 16%;
+      min-width: 200px;
+    }
+
+    .th_project {
+      width: 12%;
+    }
+
+    .th_type {
+      width: 10%;
+      min-width: 150px;
+    }
+
+    .th_status {
+      width: 8%;
+      min-width: 150px;
+    }
+
+    .th_charges {
+      width: 10%;
+      min-width: 155px;
+      text-align: right;
+
+      .label {
+        padding-top: 0;
+      }
+    }
+
+    .th_project {
+      min-width: 150px;
+    }
+
+    .tags-col {
+      padding: 5px;
+
+      mat-chip {
+        min-height: 20px;
+        padding: 5px 10px;
+        font-size: 13px;
+        max-width: 110px !important;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        display: inline-block;
+        line-height: 10px;
+        margin: 2px;
+      }
+    }
+
+    .mat-column-charge {
+      text-align: right;
+    }
+
+    .header-row {
+      position: unset;
+
+      .th_charges {
+        padding-top: 0;
+
+        .label {
+          padding-top: 12px;
+        }
+      }
+
+      .label {
+        display: inline-block;
+        padding-top: 13px;
+        vertical-align: super !important;
+
+        .text {
+          padding-left: 15px;
+        }
+      }
+
+      .sort {
+        position: absolute;
+        bottom: 20px;
+
+        &-arrow {
+          width: 6px;
+          height: 6px;
+          border: 3px solid transparent;
+          border-bottom: 3px solid rgba(0, 0, 0, .54);
+          border-left: 3px solid rgba(0, 0, 0, .54);
+          cursor: pointer;
+
+          &.active {
+            border-bottom: 3px solid #35afd5;
+            border-left: 3px solid #35afd5;
+          }
+        }
+
+        .down {
+          transform: rotate(-45deg);
+        }
+
+        .up {
+          transform: rotate(135deg);
+        }
+      }
+    }
+  }
+
+  .dashboard_table_body {
+    td:first-child {
+      cursor: default;
+    }
+
+    .dropdown-multiselect {
+      button {
+        font-size: 14px;
+        height: 34px;
+        padding: 7px 20px;
+      }
+    }
+  }
+  .user-col{
+    padding-left: 5px;
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.ts
new file mode 100644
index 0000000..7e8b575
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-grid/audit-grid.component.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Component, OnInit} from '@angular/core';
+import {FilterAuditModel} from '../filter-audit.model';
+
+@Component({
+  selector: 'dlab-audit-grid',
+  templateUrl: './audit-grid.component.html',
+  styleUrls: ['./audit-grid.component.scss'],
+
+})
+export class AuditGridComponent implements OnInit {
+  public auditData: Array<object>;
+  public displayedColumns: string[] = ['user', 'action', 'date'];
+  public displayedFilterColumns: string[] = ['user-filter', 'action-filter', 'date-filter'];
+  public collapseFilterRow: boolean = true;
+  public filterConfiguration: FilterAuditModel = new FilterAuditModel([], [], '', '');
+  public filterAuditData: FilterAuditModel = new FilterAuditModel([], [], '', '');
+
+  ngOnInit() {}
+
+  public refreshAudit(auditData) {
+    this.auditData = auditData;
+  }
+
+  toggleFilterRow(): void {
+    this.collapseFilterRow = !this.collapseFilterRow;
+  }
+
+  onUpdate($event): void {
+    this.filterAuditData[$event.type] = $event.model;
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.html
similarity index 86%
copy from services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
copy to services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.html
index 583371e..601a4dd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.html
@@ -17,8 +17,8 @@
   ~ under the License.
   -->
 <section class="toolbar">
-  <div class="info_color" *ngIf="reportData">
-    <div class="general">
+  <div class="info_color">
+    <div class="general" *ngIf="reportData">
       <div><span>Service base name: </span><strong>{{ reportData.sbn }}</strong></div>
       <div *ngIf="reportData.tag_resource_id"><span>Resource tag ID:
         </span><strong>{{ reportData.tag_resource_id }}</strong></div>
@@ -34,9 +34,9 @@
     <ng-daterangepicker [(ngModel)]="value" [options]="options" (ngModelChange)="onChange($event)"></ng-daterangepicker>
   </div>
   <div class="action-butt">
-    <button mat-raised-button class="butt" (click)="export($event)" [disabled]="!reportData?.report_lines.length">
-      <i class="material-icons">file_download</i>Export
-    </button>
+<!--    <button mat-raised-button class="butt" (click)="export($event)" [disabled]="!reportData?.report_lines.length">-->
+<!--      <i class="material-icons">file_download</i>Export-->
+<!--    </button>-->
     <button mat-raised-button class="butt" (click)="rebuild($event)">
       <i class="material-icons">autorenew</i>Refresh
     </button>
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.scss b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.scss
new file mode 100644
index 0000000..4a150bf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.scss
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+section.toolbar {
+  display: flex;
+  justify-content: space-between;
+  font-weight: 300;
+  width: 100%;
+
+  >div {
+    width: 33%;
+  }
+
+  >div:nth-child(2) {
+    text-align: center;
+  }
+
+  >div:nth-child(3) {
+    text-align: right;
+  }
+
+  .action-butt {
+    align-self: center;
+  }
+
+  button {
+    &:first-child {
+      margin-right: 10px;
+    }
+  }
+
+  .butt-report-range {
+    width: 240px;
+    margin-right: 10px;
+  }
+
+  .general {
+    font-size: 13px;
+
+    div {
+      span {
+        width: 190px;
+        display: inline-block;
+      }
+    }
+  }
+}
+
+/* daterangepicker themes */
+#range-picker {
+  margin-top: 5px;
+}
+
+#range-picker path#Shape {
+  fill: #36afd5;
+}
+
+#range-picker .ng-daterangepicker,
+#range-picker .ng-daterangepicker.is-active,
+#range-picker .ng-daterangepicker .calendar {
+  border: none;
+  border-radius: 0;
+  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12);
+}
+
+#range-picker .ng-daterangepicker .calendar::after {
+  border-top: 1px solid rgba(234, 234, 234, 0.64);
+  border-left: 1px solid rgba(234, 234, 234, 0.64);
+}
+
+#range-picker .ng-daterangepicker .calendar .side-container .side-button {
+  background: #fff;
+  color: #718ba6;
+  border: none;
+  border-radius: 0px;
+  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12)
+}
+
+#range-picker .ng-daterangepicker .calendar .side-container .side-button.is-active,
+#range-picker .ng-daterangepicker .input-section .label-txt {
+  color: #35afd5;
+}
+
+#range-picker .ng-daterangepicker .calendar .calendar-container .day-num.is-active,
+#range-picker .ng-daterangepicker .calendar .calendar-container .days .day-num:hover {
+  background: #35afd5;
+  background-clip: padding-box;
+
+}
+
+#range-picker .ng-daterangepicker .calendar .calendar-container .day-names,
+#range-picker .ng-daterangepicker .calendar .calendar-container .days {
+  width: 310px;
+}
+
+#range-picker .ng-daterangepicker .calendar .day.is-within-range.is-first-weekday,
+#range-picker .ng-daterangepicker .calendar .day.is-within-range.is-last-weekday {
+  background-clip: padding-box;
+}
+
+#range-picker .ng-daterangepicker .calendar .calendar-container .day.is-within-range {
+  background: #e9f8fc
+}
+
+#range-picker .ng-daterangepicker .input-section .cal-icon svg path {
+  fill: #35afd5;
+}
+
+#range-picker .ng-daterangepicker .input-section .value-txt {
+  color: #718ba6;
+}
+
+#range-picker .ng-daterangepicker .input-section .value-txt.untouched,
+#range-picker .ng-daterangepicker .input-section .label-txt.untouched {
+  color: #fff;
+}
+
+#range-picker .ng-daterangepicker .input-section .value-txt.untouched::after {
+  content: 'Select date';
+  position: absolute;
+  top: 22px;
+  left: 34px;
+  color: #718ba6;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.ts b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.ts
new file mode 100644
index 0000000..3de049f
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit-toolbar/audit-toolbar.component.ts
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Component, OnInit, AfterViewInit, Output, EventEmitter, ViewEncapsulation, ViewChild } from '@angular/core';
+import { NgDateRangePickerOptions } from 'ng-daterangepicker';
+import { DICTIONARY } from '../../../dictionary/global.dictionary';
+import {skip} from 'rxjs/operators';
+import {Subscription} from 'rxjs';
+import {HealthStatusService} from '../../core/services';
+import {GeneralEnvironmentStatus} from '../../administration/management/management.model';
+
+@Component({
+  selector: 'audit-toolbar',
+  templateUrl: './audit-toolbar.component.html',
+  styleUrls: ['./audit-toolbar.component.scss'],
+  encapsulation: ViewEncapsulation.None
+})
+export class AuditToolbarComponent implements OnInit, AfterViewInit {
+  readonly DICTIONARY = DICTIONARY;
+  value: any;
+  reportData: any;
+  availablePeriodFrom: string;
+  availablePeriodTo: string;
+  subscriptions: Subscription = new Subscription();
+  healthStatus: GeneralEnvironmentStatus;
+
+  rangeOptions = { 'YTD': 'Year To Date', 'QTD': 'Quarter To Date', 'MTD': 'Month To Date', 'reset': 'All Period Report' };
+  options: NgDateRangePickerOptions;
+  rangeLabels: any;
+
+  @Output() rebuildReport: EventEmitter<{}> = new EventEmitter();
+  @Output() exportReport: EventEmitter<{}> = new EventEmitter();
+  @Output() setRangeOption: EventEmitter<{}> = new EventEmitter();
+
+  constructor(private healthStatusService: HealthStatusService) {
+    this.options = {
+      theme: 'default',
+      range: 'tm',
+      dayNames: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+      presetNames: ['This Month', 'Last Month', 'This Week', 'Last Week', 'This Year', 'Last Year', 'From', 'To'],
+      dateFormat: 'dd MMM y',
+      outputFormat: 'YYYY/MM/DD',
+      startOfWeek: 1
+    };
+  }
+
+  ngOnInit() {
+    // if (localStorage.getItem('report_period')) {
+    //   const availableRange = JSON.parse(localStorage.getItem('report_period'));
+    //   this.availablePeriodFrom = availableRange.start_date;
+    //   this.availablePeriodTo = availableRange.end_date;    }
+    // this.subscriptions.add(this.healthStatusService.statusData.pipe(skip(1)).subscribe(result => {
+    //   this.healthStatus = result;
+    // }));
+  }
+
+  ngAfterViewInit() {
+    this.clearRangePicker();
+  }
+
+  setDateRange() {
+    const availableRange = JSON.parse(localStorage.getItem('report_period'));
+
+    this.availablePeriodFrom = availableRange.start_date;
+    this.availablePeriodTo = availableRange.end_date;
+  }
+
+  clearRangePicker(): void {
+    const rangeLabels = <NodeListOf<Element>>document.querySelectorAll('.value-txt');
+
+    for (let label = 0; label < rangeLabels.length; ++label)
+      rangeLabels[label].classList.add('untouched');
+  }
+
+  onChange(dateRange: string): void {
+    const rangeLabels = <NodeListOf<Element>>document.querySelectorAll('.value-txt');
+
+    for (let label = 0; label < rangeLabels.length; ++label)
+      if (rangeLabels[label].classList.contains('untouched')) {
+        rangeLabels[label].classList.remove('untouched');
+      }
+
+    const reportDateRange = dateRange.split('-');
+    this.setRangeOption.emit({
+      start_date: reportDateRange[0].split('/').join('-'),
+      end_date: reportDateRange[1].split('/').join('-')
+    });
+  }
+
+  rebuild($event): void {
+    this.rebuildReport.emit($event);
+  }
+
+  // export($event): void {
+  //   this.exportReport.emit($event);
+  // }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit.component.ts b/services/self-service/src/main/resources/webapp/src/app/audit/audit.component.ts
new file mode 100644
index 0000000..92ca006
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit.component.ts
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
+import { ToastrService } from 'ngx-toastr';
+import {HealthStatusService} from '../core/services';
+import { DICTIONARY} from '../../dictionary/global.dictionary';
+import {AuditToolbarComponent} from './audit-toolbar/audit-toolbar.component';
+import {AuditGridComponent} from './audit-grid/audit-grid.component';
+import {AuditService} from '../core/services/audit.service';
+
+
+@Component({
+  selector: 'dlab-reporting',
+  template: `
+  <div class="base-retreat">
+<!--    <dlab-toolbar (rebuildReport)="rebuildBillingReport()"-->
+<!--                  (exportReport)="exportBillingReport()"-->
+<!--                  (setRangeOption)="setRangeOption($event)">-->
+<!--    </dlab-toolbar>-->
+<!--    <mat-divider></mat-divider>-->
+<!--    <dlab-reporting-grid (filterReport)="filterReport($event)" (resetRangePicker)="resetRangePicker()"></dlab-reporting-grid>-->
+    <audit-toolbar>
+    </audit-toolbar>
+    <mat-divider></mat-divider>
+    <dlab-audit-grid></dlab-audit-grid>
+  </div>
+
+  `,
+  styles: [`
+    footer {
+      position: fixed;
+      left: 0;
+      bottom: 0;
+      width: 100%;
+      background: #a1b7d1;
+      color: #ffffff;
+      text-align: right;
+      padding: 5px 15px;
+      font-size: 18px;
+      box-shadow: 0 9px 18px 15px #f5f5f5;
+    }
+  `]
+})
+export class AuditComponent implements OnInit, OnDestroy {
+  readonly DICTIONARY = DICTIONARY;
+
+  @ViewChild(AuditGridComponent, { static: true }) auditGrid: AuditGridComponent;
+  @ViewChild(AuditToolbarComponent, { static: true }) auditToolbar: AuditToolbarComponent;
+
+  public auditData;
+
+  constructor(
+    private healthStatusService: HealthStatusService,
+    private auditService: AuditService,
+    public toastr: ToastrService,
+  ) { }
+
+  ngOnInit() {
+    this.getEnvironmentHealthStatus();
+    this.buildAuditReport();
+  }
+
+  ngOnDestroy() {
+  }
+
+  public buildAuditReport() {
+    this.auditData = this.auditService.getAuditData();
+    this.auditGrid.refreshAudit(this.auditData);
+
+  }
+
+  private getEnvironmentHealthStatus() {
+    this.healthStatusService.getEnvironmentHealthStatus()
+      .subscribe((result: any) => {});
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/audit.module.ts b/services/self-service/src/main/resources/webapp/src/app/audit/audit.module.ts
new file mode 100644
index 0000000..c799d23
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/audit.module.ts
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { NgDateRangePickerModule } from 'ng-daterangepicker';
+import { MaterialModule } from '../shared/material.module';
+import { FormControlsModule } from '../shared/form-controls';
+import { KeysPipeModule, LineBreaksPipeModule } from '../core/pipes';
+import {AuditComponent} from './audit.component';
+import {AuditGridComponent} from './audit-grid/audit-grid.component';
+import {AuditToolbarComponent} from './audit-toolbar/audit-toolbar.component';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    FormControlsModule,
+    KeysPipeModule,
+    LineBreaksPipeModule,
+    NgDateRangePickerModule,
+    MaterialModule
+  ],
+  declarations: [
+    AuditGridComponent,
+    AuditToolbarComponent,
+    AuditComponent
+  ],
+  exports: [AuditComponent]
+})
+export class AuditModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/audit/filter-audit.model.ts b/services/self-service/src/main/resources/webapp/src/app/audit/filter-audit.model.ts
new file mode 100644
index 0000000..37f07f9
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/audit/filter-audit.model.ts
@@ -0,0 +1,21 @@
+export class FilterAuditModel {
+
+  static getDefault(): FilterAuditModel {
+    return new FilterAuditModel([], [], '', '');
+  }
+
+  constructor(
+    public users: Array<string>,
+    public actions: Array<string>,
+    public date_start: string,
+    public date_end: string,
+  ) { }
+
+  defaultConfigurations(): void {
+    this.users = [];
+    this.actions = [];
+    this.date_start = '';
+    this.date_end = '';
+
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts
new file mode 100644
index 0000000..87f970f
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/audit.service.ts
@@ -0,0 +1,23 @@
+import { Injectable } from '@angular/core';
+import {ApplicationServiceFacade} from './applicationServiceFacade.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuditService {
+  constructor(private applicationServiceFacade: ApplicationServiceFacade) {
+  }
+
+  public getAuditData() {
+    return [
+      {user: 'Dlab-test-user1', action: 'Created project ProjectA', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user2', action: 'Created notebook Rstudio', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user1', action: 'Stopped notebook Rstudio', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user1', action: 'Started notebook Rstudio', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user3', action: 'Created EMR Emr1', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user1', action: 'Created notebook Rstudio2', date: new Date().toLocaleString()},
+      {user: 'Dlab-test-user2', action: 'Terminated notebook Rstudio', date: new Date().toLocaleString()},
+      ];
+  }
+
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
index 03f7ca9..1692ef1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/reporting/reporting.component.ts
@@ -156,7 +156,6 @@ export class ReportingComponent implements OnInit, OnDestroy {
         types.push(item['resource_type']);
 
       if (item.shape && types.indexOf(item.shape)) {
-        console.log(item);
        if (item.shape.indexOf('Master') > -1) {
           for (let shape of item.shape.split(/(?=Slave)/g)) {
             shape = shape.replace('Master: ', '');
diff --git a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html b/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
index 583371e..d912f78 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/reporting/toolbar/toolbar.component.html
@@ -17,8 +17,8 @@
   ~ under the License.
   -->
 <section class="toolbar">
-  <div class="info_color" *ngIf="reportData">
-    <div class="general">
+  <div class="info_color" >
+    <div class="general" *ngIf="reportData">
       <div><span>Service base name: </span><strong>{{ reportData.sbn }}</strong></div>
       <div *ngIf="reportData.tag_resource_id"><span>Resource tag ID:
         </span><strong>{{ reportData.tag_resource_id }}</strong></div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
index f2d715f..1028d72 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
@@ -100,6 +100,11 @@
             <span *ngIf="isExpanded; else billing">Billing Report</span>
             <ng-template #billing><i class="material-icons">account_balance_wallet</i></ng-template>
           </a>
+          <a  class="nav-item" [routerLink]="['/audit']"
+             [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}">
+            <span *ngIf="isExpanded; else audit">Audit Report</span>
+            <ng-template #audit><i class="material-icons">library_books</i></ng-template>
+          </a>
         </div>
         <div>
           <a class="nav-item" [routerLink]="['/swagger']" [routerLinkActive]="['active']"


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org