Commit 5edb03d0 authored by Chris Hines's avatar Chris Hines
Browse files

ui overhaul

parent 49ed5c68
Pipeline #7795 passed with stages
in 4 minutes and 59 seconds
<div fxLayout=column fxLayoutAlign="space-between" style="width: 100%; height: 100%">
<div *ngIf="(identity.systemalerts | async) == null || (identity.accountalerts | async) == null; then loading else displayAlerts"></div>
<ng-template #loading><h2> Loading ...</h2></ng-template>
<ng-template #displayAlerts>
<!-- This is all the system level alerts-->
<!--<div style="padding-left: 5%; padding-right: 5%; padding-top: 5%">-->
<div >
<mat-list>
<div *ngFor="let h of (identity.systemalerts | async)">
<mat-list-item>
<div *ngIf="h.stat == 'error'">
<div class='health-warn'>
{{ h.msg }}
</div>
</div>
</mat-list-item>
</div>
</mat-list>
<mat-list>
<div *ngFor="let h of (identity.accountalerts | async)">
<div *ngIf="h.type === undefined || h.type != 'quota'">
<mat-list-item>
<div [ngClass]="h.stat == 'error' || h.stat == 'warn' ? 'health-warn': 'health-ok'">
{{ h.msg }}
</div>
</mat-list-item>
</div>
<div *ngIf="h.type == 'table'">
<h3>{{ h.title }}</h3>
<table mat-table [dataSource]="h.data.rows" style="width: 100%">
<ng-container *ngFor="let c of h.data.cols" matColumnDef="{{c.key}}">
<th mat-header-cell *matHeaderCellDef style="text-align: right">{{ c.header }} </th>
<td mat-cell *matCellDef="let row;" style="text-align: right"> {{row[c.key]}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="calculateCols(h.data.cols); sticky: true" ></tr>
<tr mat-row *matRowDef="let row; columns: calculateCols(h.data.cols)" [ngClass]="rowClass(row)" ></tr>
</table>
</div>
</div>
</mat-list>
</div>
</ng-template>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AccountinfoComponent } from './accountinfo.component';
describe('AccountinfoComponent', () => {
let component: AccountinfoComponent;
let fixture: ComponentFixture<AccountinfoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AccountinfoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AccountinfoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { Identity } from '../identity';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { TesService } from '../tes.service';
@Component({
selector: 'app-accountinfo',
templateUrl: './accountinfo.component.html',
styleUrls: ['./accountinfo.component.css']
})
export class AccountinfoComponent implements OnInit {
@Input() identity: Identity;
constructor(
private router: Router,
public tesService: TesService,
) { }
ngOnInit() {
}
calculateCols(data) {
return data.map(function(r) { return r.key });
}
humanKBytes(n: number) {
if (n > 1024*1024*1024) {
let v = n/1024/1024/1024;
return v.toFixed(0)+'TB';
}
if (n > 1024*1024) {
let v = n/1024/1024;
return v.toFixed(0)+'GB';
}
if (n > 1024) {
let v = n/1024;
return v.toFixed(0)+'MB';
}
return '0 MB';
}
rowClass(row) {
if (row.stat == 'error' || row.stat == 'warn') {
return 'health-warn';
} else {
return 'health-ok';
}
}
navLogin(o) {
if (o == null) {
return
}
if (o.length == 0) {
this.router.navigate(['/login']);
} else {
}
}
getHealth(o) {
if (o == null) {
return
}
for (let id of o) {
this.tesService.getHealthAlerts(id);
}
}
}
......@@ -18,13 +18,15 @@ const routes: Routes = [
{ path: '', redirectTo: 'launch', pathMatch: 'full'},
//{ path: 'launch', component: JoblistComponent},
{ path: 'launch', component: LauncherComponent},
{ path: 'launch/:site', component: LauncherComponent},
{ path: 'launch/:site/:app', component: LauncherComponent},
{ path: 'login', component: LoginComponent},
{ path: 'logout', component: LogoutComponent},
{ path: 'settings', component: SettingsComponent },
// { path: 'finishlaunch', component: LauncherComponent},
//{ path: 'cancellaunch', component: LauncherComponent},
{ path: 'sshauthz_callback', component: KeygenComponent},
// { path: 'transfer', component: TransferComponent },
{ path: 'transfer', component: TransferComponent },
//{ path: 'shareconnect', component: ShareconnectComponent }
......
......@@ -7,9 +7,37 @@
</div> -->
<!--<app-launcher></app-launcher>-->
<mat-sidenav-container style="height: 100%; width: 100%">
<mat-sidenav></mat-sidenav>
<mat-sidenav-container style="height: 100vh; width: 100%">
<!--<mat-sidenav-container>-->
<mat-sidenav closed>
1st sidenav
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
<div fxLayout="column" fxLayoutAlign="space-between stretch" style="height: 100%; width: 100%">
<mat-toolbar color="primary">
<div fxLayout="row" style="width: 100%">
<mat-toolbar-row style="width: 100%">
<img src="assets/MASSIVElogoTransparent128x128.png" style="width: auto; height: 90%">
<span>Strudel2</span>
<span fxFlex></span>
<span class="fill-horizontal-space"></span>
<button mat-icon-button [matMenuTriggerFor]="actionmenu">
<mat-icon>person</mat-icon>
</button>
<mat-menu #actionmenu="matMenu">
<div *ngFor="let az of (authService.loggedInAuthZ | async)">
<button mat-menu-item routerLink="/logout"><mat-icon>logout</mat-icon>Log out of {{ az.name }}</button>
</div>
<button mat-menu-item routerLink="/settings"><mat-icon>settings</mat-icon>Settings</button>
<button *ngIf="(authService.loggedOutAuthZ | async).length > 0" mat-menu-item routerLink="/login"><mat-icon>exit_to_app</mat-icon>More services</button>
</mat-menu>
</mat-toolbar-row>
</div>
</mat-toolbar>
<div style="height: 100%">
<router-outlet></router-outlet>
</div>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
......@@ -5,6 +5,7 @@ import { ComputesitesService} from './computesites.service';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import { MatSnackBar } from '@angular/material';
import {OverlayContainer} from '@angular/cdk/overlay';
import { Computesite, Health } from './computesite';
......@@ -69,6 +70,31 @@ export class AppComponent {
classList.add(theme);
}
countErrors(a: Health[], b: Health[]) {
var count: number = 0
var h: Health;
if (a != null) {
for (h of a) {
if (h.stat == 'error' || h.stat == 'warn') {
count++;
}
if(!isNaN(parseInt(h.stat))) {
count = count + parseInt(h.stat);
}
}
}
if (b != null) {
for (h of b) {
if (h.stat == 'error' || h.stat == 'warn') {
count++;
}
if(!isNaN(parseInt(h.stat))) {
count = count + parseInt(h.stat);
}
}
}
return count;
}
// login() {
......
......@@ -6,13 +6,14 @@ import { AppComponent } from './app.component';
import { LauncherComponent } from './launcher/launcher.component';
import { JoblistComponent } from './joblist/joblist.component';
import { MatButtonModule } from '@angular/material';
import { MatButtonToggleModule} from '@angular/material';
import { MatFormFieldModule } from '@angular/material';
import { MatOptionModule } from '@angular/material';
import { MatInputModule } from '@angular/material';
import { MatSelectModule } from '@angular/material';
import { MatListModule} from '@angular/material';
import { MatTableModule } from '@angular/material/table';
import { MatCardModule } from '@angular/material';
import { MatCardModule, MatTabsModule } from '@angular/material';
import { MatToolbarModule } from '@angular/material';
import { MatDialogModule, MatDialog } from '@angular/material';
import { MatSnackBarModule } from "@angular/material";
......@@ -53,6 +54,7 @@ import {OverlayModule} from '@angular/cdk/overlay';
import { LoginComponent } from './login/login.component';
import { SettingsComponent } from './settings/settings.component';
import { LogoutComponent } from './logout/logout.component';
import { AccountinfoComponent } from './accountinfo/accountinfo.component';
// import { FileExplorerModule } from './file-explorer/file-explorer.module';
......@@ -79,10 +81,12 @@ import { LogoutComponent } from './logout/logout.component';
LoginComponent,
SettingsComponent,
LogoutComponent,
AccountinfoComponent,
],
imports: [
BrowserModule,
MatButtonModule,
MatButtonToggleModule,
MatFormFieldModule,
MatOptionModule,
MatInputModule,
......@@ -90,6 +94,7 @@ import { LogoutComponent } from './logout/logout.component';
MatListModule,
MatTableModule,
MatCardModule,
MatTabsModule,
MatToolbarModule,
MatDialogModule,
MatProgressSpinnerModule,
......
......@@ -226,15 +226,12 @@ public getKeys(id?: Identity) {
let path=sessionStorage.getItem('path');
//skip1 because loggedInAuthZ is a behaviour subject and we don't want the current value but the value
this.loggedInAuthZ.pipe(skip(1),take(1)).subscribe( () => {this.readyToNavigate.next([true,path])});
console.log('cert generated, adding keycert to agent');
this.sshAdd(keyCert);
// only navigate once the agent contents has been refreshed
}
public querySshAgentError(error: any) {
this.agentContents.next([]);
console.log('querySshAgentError');
console.log(error);
if (error.status == 0) {
this.statusMsg.next("A network error occured. Are you connected to the internet?")
}
......@@ -253,7 +250,7 @@ public getKeys(id?: Identity) {
var anyvar: any;
this.http.get<any>(this.backendURI+'/sshagent',options)
.pipe(catchError(this.handleError(anyvar)))
.subscribe(resp => { this.agentContents.next(resp); console.log('agent response'); console.log(resp); this.statusMsg.next("") },
.subscribe(resp => { this.agentContents.next(resp); this.statusMsg.next("") },
error => this.querySshAgentError(error));
// .subscribe(resp => this.computeSitesService.updateIdentities(resp),
// error => this.httperror(error))
......
......@@ -156,9 +156,6 @@ export class ComputesitesService {
var appidentities: Identity[] = [];
var ftidentities: Identity[] = [];
// If the agent contents is set to null we are probably still updating it
console.log('updating app identities');
console.log(resp);
console.log(this.authorisationService.agentContents.value)
if (resp == null) {
return
......
:host {
/*:host {
height: 100%;
display: flex;
flex-direction: column;
......@@ -8,9 +8,9 @@
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
}*/
.file-or-folder {
/*.file-or-folder {
padding: 8px;
overflow: hidden;
}
......@@ -29,11 +29,9 @@
flex: 1 1 auto;
}
.file-table {
/* flex: 1 1 auto; */
height: 400px;
/* width: 100%; */
}
.basic-container {
height: 400px;
}
}*/
......@@ -95,6 +95,22 @@
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="navigate(row)"></tr>
<mat-menu #contextMenu="matMenu" [overlapTrigger]="false">
<ng-template matMenuContent let-element="element">
<!-- <button mat-menu-item [matMenuTriggerFor]="moveToMenu" [matMenuTriggerData]="{self: element}">
<mat-icon>open_with</mat-icon>
<span>Move To</span>
</button> -->
<button mat-menu-item (click)="emitSendFile(element)">
<mat-icon>send</mat-icon>
<span>Send file</span>
</button>
<button mat-menu-item (click)="openNewFolderDialog()">
<mat-icon>send</mat-icon>
<span>New directory</span>
</button>
</ng-template>
</mat-menu>
</table>
......@@ -121,19 +137,3 @@
<!-- </div> -->
<!-- </div> -->
<mat-menu #contextMenu="matMenu" [overlapTrigger]="false">
<ng-template matMenuContent let-element="element">
<!-- <button mat-menu-item [matMenuTriggerFor]="moveToMenu" [matMenuTriggerData]="{self: element}">
<mat-icon>open_with</mat-icon>
<span>Move To</span>
</button> -->
<button mat-menu-item (click)="emitSendFile(element)">
<mat-icon>send</mat-icon>
<span>Send file</span>
</button>
<button mat-menu-item (click)="openNewFolderDialog()">
<mat-icon>send</mat-icon>
<span>New directory</span>
</button>
</ng-template>
</mat-menu>
......@@ -10,7 +10,7 @@
<div> {{ timeremaining }} </div>
</div>
<div fxFlex></div>
<div fxFlex="15%" fxLayout="row">
<div fxFlex fxLayout="row">
<div fxFlex *ngIf="!nocancel"> <button mat-button (click)="onCancel()" > Cancel </button> </div>
<div *ngFor="let action of jobdata.app.actions">
<div fxFlex *ngIf="action.states === undefined || action.states === null || action.states.indexOf(jobdata.state) != -1">
......
......@@ -71,9 +71,7 @@ export class JobComponent implements OnInit, OnDestroy {
let end = this.parseDate(this.jobdata.endtime);
let remaining = end.valueOf() - Date.now().valueOf();
this.timeremaining = "Time remaining: "+this.secondsToHms(remaining/1000);
} else {
console.log('jobdata endtime is undefined');
}
}
}
secondsToHms(d: number) {
......
<mat-toolbar color="primary">
<mat-toolbar-row>
<button mat-icon-button ><mat-icon>menu</mat-icon></button>
<span>Strudel v2.0</span>
<span class="fill-horizontal-space"></span>
</mat-toolbar-row>
</mat-toolbar>
<mat-card>
Generating cryptographic tokens ... this should only take a few seconds
</mat-card>
......@@ -5,10 +5,14 @@
<iframe *ngIf="appconfigurl != null" [src]="batchcmdsafeurl" style="height: 1px; min-height: 0px; border: none" #batchbuilderiframe></iframe>-->
<div *ngIf="(appSubject | async ) !== null" style="width: 100%">
<iframe [src]="batchcmdsafeurl" style="border: none; border-style: none; border-width: 0px; width: 100%" [style.height]="height+'px'" #batchbuilderiframe></iframe>
<iframe [src]="batchcmdsafeurl" style="border: none; border-style: none; border-width: 0px; width: 100%; overflow: auto" [style.height]="height+'px'" #batchbuilderiframe></iframe>
<!--<div *ngIf="appconfigsafeurl !== null && configtoggle">
<iframe [src]="appconfigsafeurl" style="border: none; border-style: none; border-width: 0px; width: 100%" [style.height]="appconfigheight+'px'" #batchbuilderiframe></iframe>
</div>-->
<div fxLayout="row" fxLayoutAlign="space-around">
<button mat-flat-button (click)="launch()" color="primary" #launchbtn>Launch</button>
<button *ngIf="appconfigsafeurl !== null" mat-flat-button (click)="configdialog()" color="primary">Configure</button>
<button mat-flat-button (click)="launch()" color="primary" #launchbtn [disabled]="!readyToLaunch">Launch</button>
</div>
</div>
</div>
......@@ -3,11 +3,13 @@ import { DomSanitizer} from '@angular/platform-browser';
import { Renderer2 } from '@angular/core';
//import { MatDialogRef,MAT_DIALOG_DATA } from '@angular/material';
import { TesService } from '../tes.service';
import { timer, Subscription} from 'rxjs';
import { Observable, merge, pipe, timer, Subscription} from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { Identity } from '../identity';
import { Strudelapp } from '../strudelapp';
import { BatchInterface} from '../batchinterface';
import { fromEvent } from 'rxjs';
@Component({
......@@ -18,6 +20,7 @@ import { BatchInterface} from '../batchinterface';
export class LaunchDialogComponent implements OnInit {
@Input() identity: Identity;
@Input() appSubject: BehaviorSubject<Strudelapp>;
//@Input() app: Strudelapp;
app: Strudelapp;
batchcmdurl: string;
appconfigurl: string;
......@@ -30,9 +33,13 @@ export class LaunchDialogComponent implements OnInit {
subscriptions: Subscription[];
launchNext: Boolean;
width: number;
appconfigwidth: number;
height: number;
public appData: any;
public submitcmd: string;
public appData: BehaviorSubject<any>;
public submitcmd: BehaviorSubject<string>;
configtoggle: boolean;
configwindow: any;
public readyToLaunch: boolean;
//constructor( public dialogRef: MatDialogRef<LaunchDialogComponent>,
constructor(
......@@ -40,10 +47,13 @@ export class LaunchDialogComponent implements OnInit {
private renderer: Renderer2,
private tesService: TesService,
) {
this.submitcmd = null;
this.appData = null
this.submitcmd = new BehaviorSubject<string>(null);
this.appData = new BehaviorSubject<any>(null);
this.subscriptions = [];
this.launchNext = false;
this.height=0;
this.submitcmd.subscribe(() => this.canILaunch());
this.appData.subscribe(() => this.canILaunch());
}
......@@ -51,11 +61,23 @@ export class LaunchDialogComponent implements OnInit {
console.log('creating launch-dialog');
this.rmListen = this.renderer.listen('window','message',(event) => this.receiveMessage(event));
this.setFocus = false;
this.subscriptions.push(this.appSubject.subscribe(() => { this.setReady(false); this.app = this.appSubject.value; this.updateURLs()}));
this.updateURLs();
this.clearInfo();
this.subscriptions.push(this.appSubject.subscribe(() => { this.app = this.appSubject.value; this.updateURLs() ; this.clearInfo()}));
}
configdialog(){
console.log('open app config');
console.log(this.app.url);
this.configwindow = window.open(this.app.url);
this.configwindow.onunload = function () { console.log('config closed')};
this.subscriptions.push(fromEvent(this.configwindow,'message').subscribe((event) => this.receiveAppconfig(event)));
}
updateURLs() {
if (this.app == null) {
return;
}
let theme = "";
theme=localStorage.getItem('strudel-theme');
if (theme == null) {
......@@ -65,11 +87,16 @@ export class LaunchDialogComponent implements OnInit {
this.batchcmdurl = this.identity.site.url+"/"+this.app.name+"?theme="+theme;
}
this.batchcmdsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.batchcmdurl);
if (this.app !== null) {
if (this.app.url !== null) {
this.appconfigurl = this.app.url+"?theme="+theme;
} else {
this.appconfigurl = null;
}
if (!(this.appconfigurl == null)) {
this.appconfigsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.appconfigurl);
} else {
this.appconfigsafeurl = null;
}
console.log('updatedURLs');
}
......@@ -83,38 +110,33 @@ export class LaunchDialogComponent implements OnInit {
setReady(ready) {
console.log('call setReady',ready);
this.readyToLaunch = ready;
if (this.launchbtn === undefined) {
console.log('launchbtn is not defined, cant set ready');
return
}
if (ready && !(this.setFocus)) {
this.launchbtn.nativeElement._disabled = false;
this.launchbtn.nativeElement.focus();
this.setFocus = true;
} else {
this.launchbtn.nativeElement._disabled = true;
console.log('launch is still disabled');
}
}
receiveAppconfig(event) {
if (event.data['appData'] !== undefined) {
this.appData.next(event.data['appData']);
console.log('received appData');
}
}
receiveMessage(event) {
console.log(event);
if (event.data['batchcmd'] !== undefined) {
this.submitcmd = event.data['batchcmd'];
console.log('recieved a submit cmd');
console.log('recieveMessage'+this.submitcmd);
if (this.launchNext) {
this.launchThisCmd(this.submitcmd);
this.launchNext = false;
}
}
if (event.data['appData'] !== undefined) {
this.appData = event.data['appData'];
this.submitcmd.next(event.data['batchcmd']);
}
if (event.data['resize'] !== undefined) {
console.log('setting iframe size');