Skip to content

Commit b31b8c6

Browse files
committed
feat: implement timeline visualization for compliance evaluations
1 parent 331849d commit b31b8c6

37 files changed

Lines changed: 1417 additions & 1685 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
* {
2+
font-size: .75rem !important;
3+
}
4+
5+
.span-small-icon i {
6+
font-size: 10px !important;
7+
}
8+
9+
.border-left-danger {
10+
border-left: 5px solid #dc3545 !important;
11+
}
12+
13+
.border-left-success{
14+
border-left: 5px solid #28a745 !important;
15+
}
16+
17+
.border-left-neutral {
18+
border-left: 5px solid #e9e9e9 !important;
19+
}
20+
21+
button {
22+
white-space: nowrap;
23+
}
24+
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<div class="w-100 d-flex flex-column flex-grow-1 h-100">
2+
<div [ngClass]="{
3+
'border-left-success': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.COMPLIANT,
4+
'border-left-danger': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.NON_COMPLIANT,
5+
'border-left-neutral': control.lastEvaluationStatus !== ComplianceStatusExtendedEnum.COMPLIANT
6+
&& control.lastEvaluationStatus !== ComplianceStatusExtendedEnum.NON_COMPLIANT
7+
}"
8+
>
9+
<div class="d-flex justify-content-around align-content-center py-1" style="border-bottom: 1px solid #ccc;">
10+
<div style="padding-left: 10px" class="w-100">
11+
<span class="text-blue-800 font-weight-light mr-2">Status:</span>
12+
<span [ngClass]="{
13+
'text-success': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.COMPLIANT,
14+
'text-danger': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.NON_COMPLIANT
15+
}"
16+
class="span-small-icon d-flex justify-content-start align-items-center">
17+
{{ control.lastEvaluationStatus | complianceStatusLabel }}
18+
</span>
19+
</div>
20+
21+
<div class="w-100" style="border-left: 1px solid #ccc; padding-left: 10px;">
22+
<span class="text-blue-800 font-weight-light mr-2">Last Evaluation:</span>
23+
<span *ngIf="dateFormat$ | async as dateFormat" class="span-small-icon d-flex justify-content-start align-items-center">
24+
<ng-container *ngIf="control.lastEvaluationTimestamp; else notEvaluated">
25+
{{ control.lastEvaluationTimestamp | date:dateFormat.dateFormat:dateFormat.timezone }}
26+
</ng-container>
27+
28+
<ng-template #notEvaluated>
29+
{{ control.lastEvaluationStatus | complianceStatusLabel }}
30+
</ng-template>
31+
</span>
32+
33+
</div>
34+
</div>
35+
</div>
36+
<div class="p-3">
37+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mt-3">
38+
<span class="text-blue-800 font-weight-light mr-2">Compliance section:</span>
39+
</div>
40+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mb-2">
41+
<p class="font-size-base">{{ control.section.standardSectionName }}</p>
42+
</div>
43+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mb-2">
44+
<app-utm-collapsible-text class="font-size-base" [maxLength]="200" [text]="control.section.standardSectionDescription"></app-utm-collapsible-text>
45+
</div>
46+
47+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mt-3">
48+
<span class="text-blue-800 font-weight-light mr-2">Compliance report:</span>
49+
</div>
50+
51+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mb-2">
52+
<app-utm-collapsible-text class="font-size-base" [maxLength]="200" [text]="control.controlSolution"></app-utm-collapsible-text>
53+
</div>
54+
55+
<ng-container *ngIf="control.lastEvaluationStatus === ComplianceStatusExtendedEnum.NON_COMPLIANT && control.controlRemediation">
56+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mt-3">
57+
<span class="text-blue-800 font-weight-light mr-2">Compliance remediation:</span>
58+
</div>
59+
60+
<div class="alert-details w-100 d-flex justify-content-start align-items-center mb-2">
61+
<app-utm-collapsible-text class="font-size-base" [maxLength]="200" [text]="control.controlRemediation"></app-utm-collapsible-text>
62+
</div>
63+
</ng-container>
64+
</div>
65+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {Component, Input, OnInit} from '@angular/core';
2+
import {Observable} from 'rxjs';
3+
import {TimezoneFormatService} from '../../../shared/services/utm-timezone.service';
4+
import {DatePipeDefaultOptions} from '../../../shared/types/date-pipe-default-options';
5+
import {ComplianceStatusExtendedEnum} from '../../shared/enums/compliance-status.enum';
6+
import {ComplianceControlEvaluationType} from '../../shared/type/compliance-control-evaluation.type';
7+
8+
9+
@Component({
10+
selector: 'app-compliance-evaluation-view-detail',
11+
templateUrl: './compliance-evaluation-view-detail.component.html',
12+
styleUrls: ['./compliance-evaluation-view-detail.component.css']
13+
})
14+
export class ComplianceEvaluationViewDetailComponent implements OnInit {
15+
@Input() control: ComplianceControlEvaluationType;
16+
dateFormat$: Observable<DatePipeDefaultOptions>;
17+
ComplianceStatusExtendedEnum = ComplianceStatusExtendedEnum;
18+
constructor(private timezoneFormatService: TimezoneFormatService) { }
19+
20+
ngOnInit() {
21+
this.dateFormat$ = this.timezoneFormatService.getDateFormatSubject();
22+
}
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
img{
3+
margin-right: 5px;
4+
}
5+
6+
table {
7+
tr {
8+
border-top: 1px solid #dee2e6;
9+
}
10+
td {
11+
border: none !important;
12+
}
13+
}
14+
15+
.card-title {
16+
font-weight: bold;
17+
}
18+
19+
.label-header {
20+
font-size: 12px;
21+
}
22+
23+
.border-left-danger {
24+
border-left: 5px solid #dc3545 !important;
25+
}
26+
27+
.border-left-success{
28+
border-left: 5px solid #28a745 !important;
29+
}
30+
31+
.border-left-neutral {
32+
border-left: 5px solid #e9e9e9 !important;
33+
}
34+
35+
.button-pdf {
36+
margin-right: 25px !important;
37+
margin-top: 0;
38+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<div class="container-fluid p-3" xmlns="http://www.w3.org/1999/html">
2+
<div class="w-100">
3+
<div class="card w-100 h-100">
4+
<div class="card-body p-0 position-relative">
5+
<div class="card bg-light border-0 m-0">
6+
<div class="d-flex p-2 m-1 justify-content-between align-items-center">
7+
<h5 class="card-title mb-0 text-uppercase label-header d-flex align-items-center">
8+
Compliance assessment
9+
</h5>
10+
<app-utm-search-input (searchFor)="onSearch($event)"
11+
[searching]="loading"
12+
placeholder="Search ...">
13+
</app-utm-search-input>
14+
15+
</div>
16+
</div>
17+
18+
<!-- Spinner -->
19+
<div *ngIf="loading"
20+
class="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center"
21+
style="background-color: rgba(255, 255, 255, 0.8); z-index: 10;">
22+
<app-utm-spinner [height]="'35px'"
23+
[label]="'Loading ...'"
24+
[loading]="loading"
25+
[width]="'35px'">
26+
</app-utm-spinner>
27+
</div>
28+
29+
<div [ngStyle]="{'min-height': getTableHeight(), 'height': getTableHeight()}" class="table-responsive">
30+
<table class="table mb-0">
31+
<thead>
32+
<tr>
33+
<th>
34+
Status
35+
</th>
36+
<th (sort)="onSortBy($event)"
37+
[isSortable]="true"
38+
[sortEvent]="sortEvent"
39+
[sortable]="'controlName'"
40+
appColumnSortable
41+
class="cursor-pointer">
42+
Security Control Name
43+
</th>
44+
<th>
45+
Last Evaluation
46+
</th>
47+
<th>
48+
Description
49+
</th>
50+
</tr>
51+
</thead>
52+
<tbody *ngIf="controls$ | async as controls;">
53+
<tr class="cursor-pointer" (click)="controlDetail = control"
54+
*ngFor="let control of controls; let index = index" style="position: relative; z-index: 1;">
55+
<td style="width: 12%; position: relative; z-index: 0;"
56+
[ngClass]="{
57+
'border-left-success': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.COMPLIANT,
58+
'border-left-danger': control.lastEvaluationStatus === ComplianceStatusExtendedEnum.NON_COMPLIANT,
59+
'border-left-neutral': control.lastEvaluationStatus !== ComplianceStatusExtendedEnum.COMPLIANT
60+
&& control.lastEvaluationStatus !== ComplianceStatusExtendedEnum.NON_COMPLIANT
61+
}"
62+
>
63+
{{ control.lastEvaluationStatus | complianceStatusLabel }}
64+
</td>
65+
<td style="width: 35%; position: relative; z-index: 0;">
66+
<div style="display: flex; align-items: center; gap: 8px;">
67+
<img src="assets/icons/compliance/shield.png" alt="shield" style="flex-shrink: 0;"/>
68+
<span>
69+
{{ control.controlName }}
70+
</span>
71+
</div>
72+
</td>
73+
<td style="width: 18%; position: relative; z-index: 0;">
74+
<span *ngIf="dateFormat$ | async as dateFormat">
75+
{{ control.lastEvaluationTimestamp | date:dateFormat.dateFormat:dateFormat.timezone }}
76+
</span>
77+
</td>
78+
<td style="width: 35%; position: relative; z-index: 0;">
79+
<div style="max-width: 350px" class="text-truncate">
80+
{{ control.controlSolution }}
81+
</div>
82+
</td>
83+
</tr>
84+
<tr *ngIf="noData && !loading">
85+
<td colspan="5">
86+
<app-no-data-found></app-no-data-found>
87+
</td>
88+
</tr>
89+
</tbody>
90+
</table>
91+
</div>
92+
<div [hidden]="noData" class="my-4">
93+
<div class="row justify-content-center">
94+
<ngb-pagination
95+
[(page)]="page"
96+
(pageChange)="loadPage($event)"
97+
[boundaryLinks]="true"
98+
[collectionSize]="totalItems"
99+
[maxSize]="5"
100+
[pageSize]="itemsPerPage"
101+
[rotate]="true"
102+
[size]="'sm'">
103+
</ngb-pagination>
104+
</div>
105+
</div>
106+
</div>
107+
</div>
108+
</div>
109+
</div>
110+
111+
112+
<div *ngIf="controlDetail" class="utm-right-container">
113+
<div (click)="controlDetail= undefined" class="overlay overlay-lg col-md-7"></div>
114+
<div class="card utm-right-action utm-right-action-lg ml-0">
115+
<div [ngClass]="{
116+
'border-left-success': controlDetail.lastEvaluationStatus === ComplianceStatusExtendedEnum.COMPLIANT,
117+
'border-left-danger': controlDetail.lastEvaluationStatus === ComplianceStatusExtendedEnum.NON_COMPLIANT,
118+
'border-left-neutral': controlDetail.lastEvaluationStatus !== ComplianceStatusExtendedEnum.COMPLIANT
119+
&& controlDetail.lastEvaluationStatus !== ComplianceStatusExtendedEnum.NON_COMPLIANT
120+
}" class="title d-flex justify-content-between align-items-center border-bottom-1
121+
border-bottom-grey-100 pl-3 pt-3 pr-3 pb-0">
122+
<h6 class="card-title text-blue-800 font-weight-light">
123+
<img src="assets/icons/compliance/shield.png" alt="shield"/>
124+
<label class="font-size-base text-wrap">
125+
{{ controlDetail.controlName }}
126+
</label>
127+
</h6>
128+
<button (click)="controlDetail = undefined" aria-label="Close"
129+
class="close button-close" type="button">
130+
<div class="close-icon"></div>
131+
</button>
132+
</div>
133+
<app-compliance-evaluation-view-detail [control]="controlDetail"></app-compliance-evaluation-view-detail>
134+
</div>
135+
</div>

0 commit comments

Comments
 (0)