Skip to content

Commit 824cc8b

Browse files
UI Improvements for Section 508, API health check and embedded iframe display (#502)
* Made a few modifications to Steamfitter UI for Section 508 Compliance * hides vmId and vmName from results when taskAction starts with http, core, or linux * adds title to crucible logo * updates red background color to match player and corrects contrast issues * adds theme selection via url param * detects whether in iframe and removes topbar * adds close-circle-outline to exit administration page * adds settings cog * only show admin cog for superusers * implements healthcheck Co-authored-by: Nuria Pacheco <[email protected]>
1 parent 660d2e1 commit 824cc8b

File tree

19 files changed

+156
-24
lines changed

19 files changed

+156
-24
lines changed

src/app/app.component.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { OverlayContainer } from '@angular/cdk/overlay';
55
import { Component, HostBinding, OnDestroy } from '@angular/core';
66
import { MatIconRegistry } from '@angular/material/icon';
77
import { DomSanitizer } from '@angular/platform-browser';
8-
import { ComnAuthQuery, Theme } from '@cmusei/crucible-common';
8+
import { ComnAuthQuery, ComnAuthService, Theme } from '@cmusei/crucible-common';
99
import { Subject, Observable } from 'rxjs';
1010
import { takeUntil } from 'rxjs/operators';
11+
import { ActivatedRoute, Router } from '@angular/router';
1112

1213
@Component({
1314
selector: 'app-root',
@@ -17,13 +18,17 @@ import { takeUntil } from 'rxjs/operators';
1718
export class AppComponent implements OnDestroy {
1819
@HostBinding('class') componentCssClass: string;
1920
theme$: Observable<Theme> = this.authQuery.userTheme$;
21+
private paramTheme;
2022
unsubscribe$: Subject<null> = new Subject<null>();
2123

2224
constructor(
2325
iconRegistry: MatIconRegistry,
2426
sanitizer: DomSanitizer,
2527
private overlayContainer: OverlayContainer,
26-
private authQuery: ComnAuthQuery
28+
private authQuery: ComnAuthQuery,
29+
private activatedRoute: ActivatedRoute,
30+
private router: Router,
31+
private authService: ComnAuthService
2732
) {
2833
iconRegistry.setDefaultFontSetClass('mdi');
2934

@@ -259,10 +264,33 @@ export class AppComponent implements OnDestroy {
259264
'assets/svg-icons/ic_crucible_steamfitter.svg'
260265
)
261266
);
267+
iconRegistry.addSvgIcon(
268+
'close-circle-outline',
269+
sanitizer.bypassSecurityTrustResourceUrl(
270+
'assets/svg-icons/close-circle-outline.svg'
271+
)
272+
);
262273

263274
this.theme$.pipe(takeUntil(this.unsubscribe$)).subscribe((theme) => {
275+
if (this.paramTheme && this.paramTheme !== theme) {
276+
this.router.navigate([], {
277+
queryParams: { theme: theme },
278+
queryParamsHandling: 'merge',
279+
});
280+
}
264281
this.setTheme(theme);
265282
});
283+
this.activatedRoute.queryParamMap
284+
.pipe(takeUntil(this.unsubscribe$))
285+
.subscribe((params) => {
286+
const theme = params.get('theme');
287+
288+
if (theme != null) {
289+
this.paramTheme = theme === Theme.DARK ? Theme.DARK : Theme.LIGHT;
290+
this.authService.setUserTheme(this.paramTheme);
291+
}
292+
});
293+
266294
}
267295

268296
setTheme(theme: Theme) {
@@ -279,6 +307,7 @@ export class AppComponent implements OnDestroy {
279307
classList.remove(Theme.LIGHT);
280308
}
281309
}
310+
282311
ngOnDestroy(): void {
283312
throw new Error('Method not implemented.');
284313
}

src/app/components/admin/admin-container/admin-container.component.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
<a class="nolink" [routerLink]="['/']">
2020
<div fxLayout="row" fxLayoutAlign="start center">
2121
<mat-icon
22-
class="steamfitter-icon"
23-
svgIcon="ic_crucible_steamfitter"
22+
class="icon-35px"
23+
svgIcon="close-circle-outline"
2424
></mat-icon>
2525
<h2 class="icon-text">Administration</h2>
2626
</div>
@@ -45,13 +45,14 @@ <h2 class="icon-text">Administration</h2>
4545
</div>
4646

4747
<div>
48-
<button mat-icon-button>
48+
<button mat-icon-button aria-label="Users">
4949
<mat-icon class="icon-color" svgIcon="ic_chevron_right"></mat-icon>
5050
</button>
5151
</div>
5252
</mat-list-item>
5353
</mat-list>
5454
<img
55+
*ngIf="!hideTopbar"
5556
class="crucible-logo"
5657
[src]="
5758
(theme$ | async) === 'light-theme'
@@ -62,6 +63,7 @@ <h2 class="icon-text">Administration</h2>
6263
</mat-sidenav>
6364
<mat-sidenav-content>
6465
<app-topbar
66+
*ngIf="!hideTopbar"
6567
[title]="usersText"
6668
[topbarView]="TopbarView.STEAMFITTER_ADMIN"
6769
[topbarColor]="topbarColor"

src/app/components/admin/admin-container/admin-container.component.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export class AdminContainerComponent implements OnDestroy {
4040
pageIndex: Observable<number>;
4141
private unsubscribe$ = new Subject();
4242
TopbarView = TopbarView;
43-
topbarColor = '#ef3a47';
43+
hideTopbar = false;
44+
topbarColor = '#BB0000';
4445
topbarTextColor = '#FFFFFF';
4546
theme$: Observable<Theme>;
4647

@@ -53,6 +54,7 @@ export class AdminContainerComponent implements OnDestroy {
5354
private authQuery: ComnAuthQuery
5455
) {
5556
this.theme$ = this.authQuery.userTheme$;
57+
this.hideTopbar = this.inIframe();
5658

5759
this.userDataService.isSuperUser
5860
.pipe(takeUntil(this.unsubscribe$))
@@ -102,6 +104,14 @@ export class AdminContainerComponent implements OnDestroy {
102104
this.userDataService.logout();
103105
}
104106

107+
inIframe() {
108+
try {
109+
return window.self !== window.top;
110+
} catch (e) {
111+
return true;
112+
}
113+
}
114+
105115
selectUser(userId: string) {
106116
this.router.navigate([], {
107117
queryParams: { userId: userId },

src/app/components/admin/admin-users/admin-users.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class AdminUsersComponent implements OnInit {
3333
addingNewUser = false;
3434
newUser: User = { id: '', name: '' };
3535
isLoading = false;
36-
topbarColor = '#ef3a47';
36+
topbarColor = '#BB0000';
3737

3838
constructor(private settingsService: ComnSettingsService) {
3939
this.topbarColor = this.settingsService.settings.AppTopBarHexColor

src/app/components/history/history.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class HistoryComponent implements OnInit, OnDestroy {
7171
private unsubscribe$ = new Subject();
7272
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
7373
@ViewChild(MatSort, { static: true }) sort: MatSort;
74-
topbarColor = '#ef3a47';
74+
topbarColor = '#BB0000';
7575

7676
constructor(
7777
private resultQuery: ResultQuery,

src/app/components/home-app/home-app.component.html

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ <h2 class="icon-text">
2121
{{ titleText }}
2222
</h2>
2323
</div>
24+
<button
25+
*ngIf="isSuperUser"
26+
aria-label="Show Administration Page"
27+
mat-icon-button
28+
[routerLink]="['/admin']"
29+
style="outline: none;"
30+
title="Administration"
31+
>
32+
<mat-icon aria-hidden="false" class="mdi-24px" fontIcon="mdi-cog"></mat-icon>
33+
</button>
2434
<mat-divider></mat-divider>
2535
</mat-list-item>
2636
<mat-list-item
@@ -41,7 +51,7 @@ <h2 class="icon-text">
4151
</div>
4252

4353
<div>
44-
<button mat-icon-button>
54+
<button mat-icon-button aria-label="Scenario Templates">
4555
<mat-icon class="icon-color" svgIcon="ic_chevron_right"></mat-icon>
4656
</button>
4757
</div>
@@ -64,7 +74,7 @@ <h2 class="icon-text">
6474
</div>
6575

6676
<div>
67-
<button mat-icon-button>
77+
<button mat-icon-button aria-label="Scenarios">
6878
<mat-icon class="icon-color" svgIcon="ic_chevron_right"></mat-icon>
6979
</button>
7080
</div>
@@ -87,7 +97,7 @@ <h2 class="icon-text">
8797
</div>
8898

8999
<div>
90-
<button mat-icon-button>
100+
<button mat-icon-button aria-label="Tasks">
91101
<mat-icon class="icon-color" svgIcon="ic_chevron_right"></mat-icon>
92102
</button>
93103
</div>
@@ -110,13 +120,15 @@ <h2 class="icon-text">
110120
</div>
111121

112122
<div>
113-
<button mat-icon-button>
123+
<button mat-icon-button aria-label="History">
114124
<mat-icon class="icon-color" svgIcon="ic_chevron_right"></mat-icon>
115125
</button>
116126
</div>
117127
</mat-list-item>
118128
</mat-list>
119129
<img
130+
*ngIf="!hideTopbar"
131+
title="crucible logo"
120132
class="crucible-logo"
121133
[src]="
122134
(theme$ | async) === 'light-theme'
@@ -127,14 +139,27 @@ <h2 class="icon-text">
127139
</mat-sidenav>
128140
<mat-sidenav-content>
129141
<app-topbar
142+
*ngIf="!hideTopbar"
130143
[title]="selectedSection"
131144
[topbarView]="TopbarView.STEAMFITTER_HOME"
132145
[topbarColor]="topbarColor"
133146
[topbarTextColor]="topbarTextColor"
134147
[sidenav]="sidenav"
135148
(sidenavToggle)="sidenavToggleFn()"
136149
></app-topbar>
137-
<span *ngIf="isAuthorizedUser">
150+
151+
<div
152+
*ngIf="apiIsSick"
153+
class="app-header-container mat-elevation-z8"
154+
autosize
155+
>
156+
<h1>{{ apiMessage }}</h1>
157+
<h2>Please refresh this page.</h2>
158+
<h2>If the problem persists, please contact the site administrator.</h2>
159+
</div>
160+
161+
162+
<span *ngIf="!apiIsSick && isAuthorizedUser">
138163
<app-scenario-templates
139164
*ngIf="selectedSection === section.scenarioTemplates"
140165
></app-scenario-templates>

src/app/components/home-app/home-app.component.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
font-family: open_sansbold;
1616
}
1717

18+
.app-header-container {
19+
display: flex;
20+
flex-flow: column;
21+
justify-content: center;
22+
text-align: center;
23+
}
24+
1825
.appcontent-container {
1926
position: absolute;
2027
top: 0;

src/app/components/home-app/home-app.component.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { SignalRService } from 'src/app/services/signalr/signalr.service';
1515
import { UserDataService } from '../../data/user/user-data.service';
1616
import { TopbarView } from './../shared/top-bar/topbar.models';
17+
import { HealthService } from 'src/app/generated/steamfitter.api';
1718

1819
enum Section {
1920
taskBuilder = 'Tasks',
@@ -29,6 +30,8 @@ enum Section {
2930
})
3031
export class HomeAppComponent implements OnDestroy {
3132
@ViewChild('sidenav') sidenav: MatSidenav;
33+
apiIsSick = false;
34+
apiMessage = 'The API web service is not responding.';
3235
titleText = 'Steamfitter';
3336
section = Section;
3437
selectedSection: Section;
@@ -38,7 +41,8 @@ export class HomeAppComponent implements OnDestroy {
3841
isSidebarOpen = true;
3942
viewList = this.playerDataService.viewList;
4043
private unsubscribe$ = new Subject();
41-
topbarColor = '#ef3a47';
44+
hideTopbar = false;
45+
topbarColor = '#BB0000';
4246
topbarTextColor = '#FFFFFF';
4347
TopbarView = TopbarView;
4448
theme$: Observable<Theme>;
@@ -50,9 +54,13 @@ export class HomeAppComponent implements OnDestroy {
5054
private playerDataService: PlayerDataService,
5155
private settingsService: ComnSettingsService,
5256
private signalRService: SignalRService,
57+
private healthService: HealthService,
5358
private authQuery: ComnAuthQuery
5459
) {
60+
this.healthCheck();
61+
5562
this.theme$ = this.authQuery.userTheme$;
63+
this.hideTopbar = this.inIframe();
5664

5765
this.playerDataService.getViewsFromApi();
5866
activatedRoute.queryParamMap
@@ -100,6 +108,28 @@ export class HomeAppComponent implements OnDestroy {
100108
this.userDataService.logout();
101109
}
102110

111+
inIframe() {
112+
try {
113+
return window.self !== window.top;
114+
} catch (e) {
115+
return true;
116+
}
117+
}
118+
119+
healthCheck() {
120+
this.healthService
121+
.healthGetReadiness('body')
122+
.subscribe(
123+
(message) => {
124+
this.apiIsSick = message !== 'Healthy';
125+
this.apiMessage = message;
126+
},
127+
(error) => {
128+
this.apiIsSick = true;
129+
}
130+
);
131+
}
132+
103133
ngOnDestroy() {
104134
this.unsubscribe$.next(null);
105135
this.unsubscribe$.complete();

src/app/components/results/results.component.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@
4545
</button>
4646
<div *ngIf="showResults[result.id]">
4747
id: {{ result.id }}<br />
48+
<div *ngIf="showVmFields">
4849
vmId: {{ result.vmId }}<br />
4950
vmName: {{ result.vmName }}<br />
50-
apiUrl: {{ result.apiUrl }}<br />
51+
</div>
5152
actionParameters:<br />
5253
<span *ngFor="let k of result.actionParameters | keyvalue"
5354
>&nbsp;&nbsp;&nbsp;&nbsp;{{ k.key }}:
@@ -86,9 +87,10 @@
8687
</button>
8788
<div *ngIf="showResults[oldResult.id]">
8889
id: {{ oldResult.id }}<br />
90+
<div *ngIf="showVmFields">
8991
vmId: {{ oldResult.vmId }}<br />
9092
vmName: {{ oldResult.vmName }}<br />
91-
apiUrl: {{ oldResult.apiUrl }}<br />
93+
</div>
9294
actionParameters:<br />
9395
<span *ngFor="let k of result.actionParameters | keyvalue"
9496
>&nbsp;&nbsp;&nbsp;&nbsp;{{ k.key }}:

src/app/components/results/results.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum ResultStatus {
2323
export class ResultsComponent implements OnInit, OnDestroy {
2424
@Input() results: Observable<Result[]>;
2525
@Input() taskId: string;
26+
@Input() taskAction: string;
2627

2728
allResults: Result[];
2829
currentResults: Result[];
@@ -31,6 +32,7 @@ export class ResultsComponent implements OnInit, OnDestroy {
3132
showResults: Record<string, boolean | undefined> = {};
3233
private lastExecutionTime: Date;
3334
private unsubscribe$ = new Subject();
35+
showVmFields: boolean;
3436

3537
constructor(private playerDataService: PlayerDataService) {}
3638

@@ -43,6 +45,14 @@ export class ResultsComponent implements OnInit, OnDestroy {
4345
}
4446
this.filterCurrentResults();
4547
});
48+
this.showVmFields = this.hasVmFields();
49+
}
50+
51+
hasVmFields() {
52+
if (/^http|linux|core/.test(this.taskAction)) {
53+
return false;
54+
};
55+
return true;
4656
}
4757

4858
filterCurrentResults() {

0 commit comments

Comments
 (0)