diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index cf4d65b964a020b2d606a6c67c844f60a5b78356..75ecb96f68401264ce52f616f65432b7176199ca 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -7,6 +7,7 @@ import { ShareconnectComponent } from './shareconnect/shareconnect.component';
 import { JoblistComponent } from './joblist/joblist.component';
 
 import {LoginComponent} from './login/login.component';
+import {LogoutComponent} from './logout/logout.component';
 import {SettingsComponent} from './settings/settings.component';
 
 
@@ -18,6 +19,7 @@ const routes: Routes = [
   //{ path: 'launch', component: JoblistComponent},
   { path: 'launch', component: LauncherComponent},
   { path: 'login', component: LoginComponent},
+  { path: 'logout', component: LogoutComponent},
   { path: 'settings', component: SettingsComponent },
   //  { path: 'finishlaunch', component: LauncherComponent},
   //{ path: 'cancellaunch', component: LauncherComponent},
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 15ee5479ca6771fa4eba908193faab178ed637f5..32f26bec817208fa0f22d8a1ebdb160d8d8b5b35 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -25,6 +25,7 @@ import { AuthorisationService } from './authorisation.service';
 import { FlexLayoutModule } from '@angular/flex-layout';
 import { FileExplorerModule } from './file-explorer/file-explorer.module';
 import { MatMenuModule } from '@angular/material';
+import { MatBadgeModule } from '@angular/material';
 
 
 import { TesService} from './tes.service';
@@ -51,6 +52,7 @@ import { ModaldialogComponent } from './modaldialog/modaldialog.component'
 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 { FileExplorerModule } from './file-explorer/file-explorer.module';
 
@@ -76,6 +78,7 @@ import { SettingsComponent } from './settings/settings.component';
     ModaldialogComponent,
     LoginComponent,
     SettingsComponent,
+    LogoutComponent,
   ],
   imports: [
   BrowserModule,
@@ -103,10 +106,11 @@ import { SettingsComponent } from './settings/settings.component';
   MatProgressBarModule,
   OverlayModule,
   MatMenuModule,
+  MatBadgeModule,
 
 
   ],
-    entryComponents: [ LogoutdialogComponent, LaunchDialogComponent, ModaldialogComponent],
+    entryComponents: [ LogoutdialogComponent, ModaldialogComponent],
   providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService],
   bootstrap: [AppComponent]
 })
diff --git a/src/app/authorisation.service.ts b/src/app/authorisation.service.ts
index f7ddffb437bfa4c1e15e2fec100cfa9a8dcab4a9..817ebe829a2fc53193816279101fc37fac317201 100644
--- a/src/app/authorisation.service.ts
+++ b/src/app/authorisation.service.ts
@@ -43,7 +43,6 @@ export class AuthorisationService {
               private router: Router,
               private backendSelectionService: BackendSelectionService,
               private location: Location) {
-                console.log('created AuthorisationService');
                 // this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
                 this.readyToNavigate = new Subject<[Boolean,string]>();
                 this.readyToNavigate.next([false,'']);
@@ -77,7 +76,6 @@ export class AuthorisationService {
  }
  removeLocalAuthZ() {
    localStorage.removeItem('localauthservers');
-   console.log('removed local computesites');
    this.getSshAuthzServers();
  }
 
@@ -97,12 +95,9 @@ export class AuthorisationService {
      var found: boolean;
      for (let s of authzServers) {
          found=false;
-         console.log('server fp',s.cafp)
          for (let cert of agentContents) {
-           console.log('cert',cert);
            if ('Signing CA' in cert) {
              for (let ca of cert['Signing CA']) {
-               console.log('ca',ca);
                if (ca == s.cafp) {
                  loggedin.push(s)
                  found=true;
@@ -118,11 +113,6 @@ export class AuthorisationService {
            loggedout.push(s)
          }
      }
-     console.log('updating which authz servers are logged in and out');
-     console.log('logged in');
-     console.log(loggedin);
-     console.log('logged out');
-     console.log(loggedout);
      this.loggedOutAuthZ.next(loggedout);
      this.loggedInAuthZ.next(loggedin);
  }
@@ -177,32 +167,25 @@ export class AuthorisationService {
  }
 
  public getCert(token: AuthToken) {
-    console.log('in getCert');
     if (token.token === undefined || token.token === '' || token.token == null) {
-      console.log('no authtoken available, we wont be able to generate a cert');
-      console.log(token);
       return
     }
-    console.log("Generating key matter");
 
     let starttime = new Date();
     let newkeypair = keypair();
     let publicKey = forge.pki.publicKeyFromPem(newkeypair.public);
     let sshpub = forge.ssh.publicKeyToOpenSSH(publicKey, 'sv2@monash.edu');
     let endtime = new Date();
-    console.log("generating new keys took", endtime.valueOf() - starttime.valueOf())
 
     let headers = new HttpHeaders();
     let options = { headers: headers, withCredentials: true};
 
     let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign};
 
-    console.log('posting to getcert',this.backendURI);
     this.http.post<any>(this.backendURI+'/getcert',data, options)
                        .pipe(catchError(this.handleError([])))
                        .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice),
                                   error => this.signingError(error,token.sshauthzservice));
-   console.log('getcert complete');
  }
 
 public getKeys(id?: Identity) {
@@ -217,8 +200,6 @@ public getKeys(id?: Identity) {
    let keyCert = new KeyCert()
    keyCert.key = key;
    keyCert.cert = resp['cert'];
-   console.log('in make KeyCert',resp);
-   console.log('updating keycert',keyCert);
    var keys: KeyCert[] = [];
    try{
      keys = JSON.parse(sessionStorage.getItem('keys'));
@@ -246,20 +227,18 @@ public getKeys(id?: Identity) {
      this.statusMsg.next("A network error occured. Are you connected to the internet?")
    }
    this.statusMsg.next("Error querying ssh agent");
-   console.log(error);
  }
 
  public updateAgentContents() {
    if (this.statusMsg !== undefined) {
     this.statusMsg.next("Updating the list of available accounts");
    };
-   console.log('querying ',this.backendURI+'/sshagent');
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    var anyvar: any;
    this.http.get<any>(this.backendURI+'/sshagent',options)
                      .pipe(catchError(this.handleError(anyvar)))
-                     .subscribe(resp => { console.log('sshagent returned',resp); this.agentContents.next(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))
@@ -285,7 +264,6 @@ public getKeys(id?: Identity) {
 
 
  public sshAdd(keyCert: KeyCert) {
-   console.log('in authorisation service sshAdd');
    if (keyCert.key == undefined) {
      return;
    }
@@ -305,12 +283,10 @@ public getKeys(id?: Identity) {
    let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback";
    let nonce=Math.random().toString(36).substring(2, 15)
 
-   console.log(this.location.path());
 
 
    localStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
    localStorage.setItem('path', this.location.path());
-   console.log('login activated, pathname stored as',window.location.pathname);
    if (authservice.scope == null) {
      window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id);
    } else {
@@ -342,13 +318,11 @@ public getKeys(id?: Identity) {
  //   }
 
  private httperror(error: any) {
-   console.log('authorsation service got an error');
    this.statusMsg.next('There was an error logging in or generating crypto tokens');
    console.error(error);
  }
  private signingError(error: any,sshauthzservice: SshAuthzServer) {
    this.statusMsg.next('You don\'t appear to have an account on '+sshauthzservice.name);
-   console.log(error);
    if (!(sshauthzservice.logout === null)) {
      window.open(sshauthzservice.logout);
    }
@@ -359,8 +333,6 @@ public getKeys(id?: Identity) {
 
  private handleError<T> (result?: T) {
    return (error: any): Observable<T> => {
-     console.log(error)
-     console.log('handleError activated')
      if (error.status == 500) {
        return throwError("The backend server encountered and error. Please try again in a few minutes")
      }
diff --git a/src/app/backend-selection.service.ts b/src/app/backend-selection.service.ts
index 0433cb89d1cd1d698d55b19f00d19ee1fa98efc9..cd339314aa05d2a9bf42a497b6071c353f6155c3 100644
--- a/src/app/backend-selection.service.ts
+++ b/src/app/backend-selection.service.ts
@@ -22,10 +22,8 @@ export class BackendSelectionService {
     private initApiServer() {
         try {
             this.apiserver.next(<APIServer>JSON.parse(localStorage.getItem('apiserver')));
-            console.log('apiserver set to localstorage value',this.apiserver.value);
         } catch {
             this.apiserver.next(<APIServer>environment.apiserver);
-            console.log('api server to set environement',this.apiserver.value);
         }
         if (this.apiserver.value == null) {
             this.apiserver.next(<APIServer>environment.apiserver);
@@ -33,14 +31,11 @@ export class BackendSelectionService {
     }
 
     private saveLastApiServer(s: APIServer) {
-        console.log('saving api server value to local storage')
         localStorage.setItem('apiserver', JSON.stringify(s));
     }
 
      setApiServer(server: APIServer) {
-         console.log('calling setAPIServer');
          this.apiserver.next(server);
-         console.log(this.apiserver);
          this.saveLastApiServer(this.apiserver.value)
      }
 
diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts
index 62a22380eb98b4e3a54398fe90e46999eece4f7d..c4c9d3c130942246193434e87053bfc859a34451 100644
--- a/src/app/computesites.service.ts
+++ b/src/app/computesites.service.ts
@@ -39,7 +39,6 @@ export class ComputesitesService {
 
   getStrudelApps(computesites: Computesite[]) {
     for (let s of computesites) {
-      console.log('retrieving apps');
       let headers = new HttpHeaders();
       let options = { headers: headers, withCredentials: false};
       if (s.appCatalogUri !== null) {
@@ -109,7 +108,6 @@ export class ComputesitesService {
 
   removeLocalComputeSites() {
       localStorage.removeItem('localcomputesites');
-      console.log('removed local computesites');
       this.getComputeSites();
   }
 
@@ -157,8 +155,6 @@ export class ComputesitesService {
     var identities: Identity[] = [];
     var appidentities: Identity[] = [];
     var ftidentities: Identity[] = [];
-    console.log('compute sites value', this.computesites.value);
-    console.log('certs',certs);
     for (cs of this.computesites.value) {
       for (let i in certs) {
         let principal = this.siteMatch(certs[i],cs);
@@ -176,8 +172,6 @@ export class ComputesitesService {
       }
       this.identities.next(identities);
       this.ftidentities.next(ftidentities);
-      console.log('setting ftidentities');
-      console.log(this.ftidentities.value);
       this.appidentities.next(appidentities);
 
     }
diff --git a/src/app/identity.ts b/src/app/identity.ts
index f85d6d5f5b0252e469e67131f4410199d20f69d7..4712592ae02772f5776cfc272ec23daa22bd2ce7 100644
--- a/src/app/identity.ts
+++ b/src/app/identity.ts
@@ -1,4 +1,6 @@
-import {Computesite} from './computesite';
+import {Computesite, Health} from './computesite';
+import {Job} from './job';
+import {BehaviorSubject} from 'rxjs';
 // Identities are defined by a username on a computer, but rather than just
 // DNS entry, there is extra info in the Computesite
 export class Identity {
@@ -6,15 +8,21 @@ export class Identity {
   site: Computesite;
   authservice: SshAuthzServer;
   keyCerts: KeyCert[];
+  healthalerts: BehaviorSubject<Health[]>;
+  joblist: BehaviorSubject<Job[]>;
   constructor( username: string, site: Computesite) {
     this.username = username;
     this.site = site;
     this.keyCerts = [];
+    this.healthalerts = new BehaviorSubject<Health[]>([]);
+    this.joblist = new BehaviorSubject<Job[]>([]);
   }
 
   copy_skip_catalog(): Identity {
     let id = new Identity(null,null);
     id.username = this.username;
+    id.healthalerts = null;
+    id.joblist = null;
     id.site = new Computesite();
     id.site.url = this.site.url;
     id.site.host = this.site.host;
diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html
index 2dd24fda00632fd3dda318049632f38488be94c6..a7536627842229aa6a151e0df30129212aa6b19a 100644
--- a/src/app/job/job.component.html
+++ b/src/app/job/job.component.html
@@ -1,11 +1,13 @@
-<mat-card>
-  <mat-card-title>{{ jobdata.name }}</mat-card-title>
+<mat-list-item>
+    <!--<mat-card-title>{{ jobdata.name }}</mat-card-title>-->
+  <div style="width: 100%">
   <div *ngIf="jobdata.connectionState == 0">
-  <div gdAreas="name name name name|
+    <div gdAreas="name name name name|
                 status resources space buttons"
-       gdColumns="20% 60% auto 20%">
+       gdColumns="20% 60% auto 20%"
+       >
        <div gdArea="name">
-           {{ jobdata.desc }}
+           {{ jobdata.name }} {{ jobdata.desc }}
        </div>
        <div gdArea="status">
            {{ jobdata.state }}
@@ -36,6 +38,7 @@
        </div>
 
   </div>
+</div>
 <!--  <table>
     <tr>
       <td width="100%">
@@ -60,18 +63,31 @@
       </td>
   </tr>
 </table>  -->
-</div>
-<div *ngIf="jobdata.connectionState == 1">
-  Getting app parameters
-  <mat-progress-bar mode="determinate" value=30></mat-progress-bar>
-</div>
-<div *ngIf="jobdata.connectionState == 2">
-  Creating secure tunnels
-  <mat-progress-bar mode="determinate" value=60></mat-progress-bar>
+    <div *ngIf="jobdata.connectionState == 1">
+      <div gdArea="status">
+      Getting app parameters
+      </div>
+      <div gdArea="resources">
+      <mat-progress-bar mode="determinate" value=30></mat-progress-bar>
+      </div>
+    </div>
 
-</div>
-<div *ngIf="jobdata.connectionState == 3">
-  Determining correct URL
-  <mat-progress-bar mode="determinate" value=90></mat-progress-bar>
-</div>
-</mat-card>
+    <div *ngIf="jobdata.connectionState == 2">
+      <div gdArea="status">
+      Creating secure tunnels
+      </div>
+      <div gdArea="resources">
+      <mat-progress-bar mode="determinate" value=60></mat-progress-bar>
+      </div>
+    </div>
+
+    <div *ngIf="jobdata.connectionState == 3">
+      <div gdArea="status">
+      Determining correct URL
+      </div>
+      <div gdArea="resources">
+      <mat-progress-bar mode="determinate" value=90></mat-progress-bar>
+      </div>
+    </div>
+  </div>
+</mat-list-item>
diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts
index 22363a50b0b6a6089e83e1f436eb48ffd1fe18fc..6ba50d286e7c755f5a8027886b17f90b016b2ae2 100644
--- a/src/app/job/job.component.ts
+++ b/src/app/job/job.component.ts
@@ -1,29 +1,35 @@
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, OnInit, Input, OnDestroy } from '@angular/core';
 import { Job } from '../job';
 import {TesService} from '../tes.service';
 import { StrudelappsService } from '../strudelapps.service';
-import { timer } from 'rxjs/observable/timer';
+import { timer, Subscription } from 'rxjs';
+import { repeat } from 'rxjs/operators';
 
 @Component({
   selector: 'app-job',
   templateUrl: './job.component.html',
   styleUrls: ['./job.component.css']
 })
-export class JobComponent implements OnInit {
+export class JobComponent implements OnInit, OnDestroy {
 
   @Input() jobdata: Job;
   public available: Boolean;
   public nocancel: Boolean;
   public resources: string;
   public timeremaining: string;
+  private updatesub: Subscription;
   constructor(private tesService: TesService, private strudelAppsService: StrudelappsService) {
 
 
 
 }
 
+
+  ngOnDestroy() {
+    this.updatesub.unsubscribe();
+  }
   ngOnInit() {
-    this.tesService.joblist.subscribe(() => this.updateFields());
+    this.updatesub = timer(1000).pipe(repeat()).subscribe(() => this.updateFields());
   }
 
   updateFields() {
diff --git a/src/app/joblist/joblist.component.html b/src/app/joblist/joblist.component.html
index 1229da1784522f06e86761885b3fba27780d1f7f..fabe6c5f88097fd439f3555f0b6f96b9be138101 100644
--- a/src/app/joblist/joblist.component.html
+++ b/src/app/joblist/joblist.component.html
@@ -1,33 +1,14 @@
-<mat-card style="width: 100%" style="box-sizing: border-box;">
-   <div *ngIf="(tesService.identitySubject | async) === null" >
-      Click login and select a provider or select one you've already logged in to.
-    </div>
-   <div *ngIf="(tesService.identitySubject | async) !== null" >
-        <div *ngFor="let h of tesService.cachetincidents | async">
-            <div *ngIf="h.stat == 'error'">
-                <mat-card style='width: 100%'>
-                    <div class='health-warn'>
-                  {{ h.msg }}
-                    </div>
-                </mat-card>
-            </div>
-        </div>
-        <div *ngFor="let h of tesService.userhealth | async">
-            <div *ngIf="h.stat == 'error'">
-                <mat-card style='width: 100%'>
-                    <div class='health-warn'>
-                  {{ h.msg }}
-                    </div>
-                </mat-card>
-            </div>
-        </div>
-        Jobs running as {{ (tesService.identitySubject | async).displayName() }}      
-      <div *ngFor="let job of tesService.joblist | async">
+<!--<div *ngIf="(identitySubject | async) === null" >
+   Click login and select a provider or select one you've already logged in to.
+</div>-->
+<div *ngIf="(identitySubject | async) !== null" >
+   <mat-list>
+   <div *ngFor="let job of ((identitySubject | async).joblist | async)">
+       <div *ngIf="job.state != 'Finished'">
         <app-job [jobdata]=job></app-job>
-      </div>
-      <button mat-button (click)="refreshJobs()" style="width: 100%; text-align: right">Refresh Job list</button>
-
+       </div>
    </div>
-</mat-card>
+   </mat-list>
+   <button mat-button (click)="refreshJobs()" style="width: 100%; text-align: right">Refresh Job list</button>
 
-<!-- <iframe src="http://localhost:5200" frameborder="0" style="width: 100%"></iframe> -->
+</div>
diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts
index d77c229350b27e3b37f7d67ccb4b6410f96fee86..da9384fceb9c3505cc1467bcdf5675a451bbd2c5 100644
--- a/src/app/joblist/joblist.component.ts
+++ b/src/app/joblist/joblist.component.ts
@@ -1,12 +1,15 @@
 import { Component, OnInit, Input, OnDestroy } from '@angular/core';
-import {TesService} from '../tes.service';
-import { Job } from '../job';
 import { Observable } from 'rxjs/Observable';
 import { delay } from 'rxjs/operators';
 import { Subscription, interval } from 'rxjs';
-import { Identity } from '../identity';
 import { Renderer2} from '@angular/core';
-import { BehaviorSubject } from 'rxjs';
+import { BehaviorSubject, timer } from 'rxjs';
+import { repeat } from 'rxjs/operators';
+
+import { Strudelapp } from '../strudelapp';
+import { TesService } from '../tes.service';
+import { Job } from '../job';
+import { Identity } from '../identity';
 
 
 
@@ -17,21 +20,31 @@ import { BehaviorSubject } from 'rxjs';
 })
 
 export class JoblistComponent implements OnInit {
-  @Input() identity: Identity;
+  @Input() identitySubject: BehaviorSubject<Identity>;
+  @Input() appSubject: BehaviorSubject<Strudelapp>;
+  private sub: Subscription;
 
 
-  constructor(private tesService: TesService, private renderer: Renderer2) {
-}
+  constructor(private tesService: TesService ) {
+  }
 
   public ngOnInit(): void {
+    this.sub = timer(5000).pipe(repeat()).subscribe( () => this.getJobs() );
   }
 
-
+  public getJobs(): void {
+    if (this.identitySubject.value !== null) {
+      this.tesService.getJobs(this.identitySubject.value);
+    }
+  }
   public ngOnDestroy(): void {
+    if (this.sub !== undefined) {
+      this.sub.unsubscribe();
+    }
   }
 
   public refreshJobs(): void {
-    this.tesService.getJobs(this.tesService.identitySubject.value);
+    this.tesService.getJobs(this.identitySubject.value);
   }
 
 }
diff --git a/src/app/keygen/keygen.component.html b/src/app/keygen/keygen.component.html
index 872fc6343930c1a70058ba865d6cbe10a54e47c5..a6af706e56e2c53cdca6b8094407a064be211b7b 100644
--- a/src/app/keygen/keygen.component.html
+++ b/src/app/keygen/keygen.component.html
@@ -1,3 +1,13 @@
+
+<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>
diff --git a/src/app/launch-dialog/launch-dialog.component.html b/src/app/launch-dialog/launch-dialog.component.html
index b881ef7ebf5696e9b2543b737cd449c61037c0b2..c08367aee52ae4f0e43f25aa9e05a93a927983d8 100644
--- a/src/app/launch-dialog/launch-dialog.component.html
+++ b/src/app/launch-dialog/launch-dialog.component.html
@@ -1,30 +1,14 @@
-<!--
-<div gdAreas="appconfig  appconfig |
-                batchconfig batchconfig |
-                cancel launch"
-     gdRows="auto auto auto">
-<div gdArea="appconfig">
-  <iframe></iframe>
-</div>
-<div gdArea="batchconfig" style="width: 100%">
-  <iframe></iframe>
-</div>
-<div gdArea="launch">
-  <button mat-button (click)="launch()" enabled="submitAppService.readyToLaunch | async">Launch</button>
-</div>
-<div gdArea="cancel">
-  <button mat-button (click)="cancel()" >Cancel</button>
-</div>
-</div> -->
 
-<div fxLayout="column" fxLayoutAlign="space-between stretch" style="height: 100%; width: 100%">
+<div fxLayout="column" fxLayoutAlign="space-between stretch" style="width: 100%">
     <!-- <div fxFlex style="background: blue">app config</div> -->
-    <iframe *ngIf="appconfigurl != null"  fxGrow=10 fxFlex [src]="appconfigsafeurl" style="border: none"></iframe>
-    <iframe *ngIf="appconfigurl != null" [src]="batchcmdsafeurl" style="height: 1px; min-height: 0px; border: none" #batchbuilderiframe></iframe>
+    <!--<iframe *ngIf="appconfigurl != null"  fxGrow=10 fxFlex [src]="appconfigsafeurl" style="border: none"></iframe>
+    <iframe *ngIf="appconfigurl != null" [src]="batchcmdsafeurl" style="height: 1px; min-height: 0px; border: none" #batchbuilderiframe></iframe>-->
 
-    <iframe *ngIf="appconfigurl == null" fxFlex [src]="batchcmdsafeurl" fxGrow=1 style="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>
 
-    <div fxLayout="row" fxLayoutAlign="space-around">
-      <button mat-button (click)="cancel()">Cancel</button><button mat-button (click)="launch()" #launchbtn>Launch</button>
+        <div fxLayout="row" fxLayoutAlign="space-around">
+          <button mat-button (click)="launch()" #launchbtn>Launch</button>
+        </div>
     </div>
 </div>
diff --git a/src/app/launch-dialog/launch-dialog.component.ts b/src/app/launch-dialog/launch-dialog.component.ts
index 5c87699160923811c06192a51d3c338cdc580e70..2fe127870f7e1f4190ae9790e4c0acfb36487203 100644
--- a/src/app/launch-dialog/launch-dialog.component.ts
+++ b/src/app/launch-dialog/launch-dialog.component.ts
@@ -1,9 +1,13 @@
-import { Component, OnInit, AfterViewInit, ViewChild, ElementRef, Inject } from '@angular/core';
+import { Component, OnInit, ViewChild, ElementRef, Inject, Input } from '@angular/core';
 import { DomSanitizer} from '@angular/platform-browser';
 import { Renderer2 } from '@angular/core';
-import { MatDialogRef,MAT_DIALOG_DATA } from '@angular/material';
-import { SubmitAppService } from '../submit-app.service';
+//import { MatDialogRef,MAT_DIALOG_DATA } from '@angular/material';
+import { TesService } from '../tes.service';
 import { timer, Subscription} from 'rxjs';
+import { BehaviorSubject } from 'rxjs';
+import { Identity } from '../identity';
+import { Strudelapp } from '../strudelapp';
+import { BatchInterface} from '../batchinterface';
 
 
 @Component({
@@ -12,6 +16,9 @@ import { timer, Subscription} from 'rxjs';
   styleUrls: ['./launch-dialog.component.css']
 })
 export class LaunchDialogComponent implements OnInit {
+  @Input() identity: Identity;
+  @Input() appSubject: BehaviorSubject<Strudelapp>;
+  app: Strudelapp;
   batchcmdurl: string;
   appconfigurl: string;
   batchcmdsafeurl: any;
@@ -20,67 +27,64 @@ export class LaunchDialogComponent implements OnInit {
   @ViewChild('launchbtn', { read: ElementRef, static: false }) launchbtn: ElementRef;
   @ViewChild('batchbuilderiframe', { read: ElementRef, static: false }) batchbuilderiframe: ElementRef;
   setFocus: Boolean;
-  sub: Subscription;
-
-  constructor( public dialogRef: MatDialogRef<LaunchDialogComponent>,
-               private domSanitizer: DomSanitizer,
-               private renderer: Renderer2,
-               private submitAppService: SubmitAppService) {
-      this.batchcmdurl = this.submitAppService.identity.value.site.url+"/"+this.submitAppService.app.value.name;
-      this.batchcmdsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.batchcmdurl);
-      this.appconfigurl = this.submitAppService.app.value.url;
-      if (!(this.appconfigurl == null)) {
-       this.appconfigsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.appconfigurl);
-      }
-      console.log('creating the laucnch dialog with urls',this.batchcmdsafeurl,this.appconfigsafeurl);
+  subscriptions: Subscription[];
+  width: number;
+  height: number;
+  public appData: any;
+  public submitcmd: string;
+
+  //constructor( public dialogRef: MatDialogRef<LaunchDialogComponent>,
+  constructor( 
+    private domSanitizer: DomSanitizer,
+    private renderer: Renderer2,
+    private tesService: TesService,
+  ) {
+    this.submitcmd = null;
+    this.appData = null
+    this.subscriptions = [];
 
   }
 
   ngOnInit() {
+    console.log('creating launch-dialog');
     this.rmListen = this.renderer.listen('window','message',(event) => this.receiveMessage(event));
     this.setFocus = false;
-    this.sub = this.submitAppService.readyToLaunch.subscribe((ready) => this.becomesReady(ready))
-  }
-
-  ngAfterViewInit() {
-    console.log('in AfterViewInit, disabling the button');
-    this.launchbtn.nativeElement._disabled = true;
-  }
-      
-
-  cancel() {
-    console.log('cause app cancel');
-    this.submitAppService.submitcmd.next(null);
-    this.submitAppService.appData.next(null)
-    this.dialogRef.close()
+    this.subscriptions.push(this.appSubject.subscribe(() => { this.setReady(false); this.app = this.appSubject.value; this.updateURLs()}));
+    
   }
 
-  launch() {
-    console.log('cause app submission');
-    console.log(this.batchbuilderiframe);
-    this.batchbuilderiframe.nativeElement.contentWindow.postMessage('launch selected',"*");
-    //this.dialogRef.close();
-    //this.submitAppService.launch();
+  updateURLs() {
+    let theme = "";
+    theme=localStorage.getItem('strudel-theme');
+    if (theme == null) {
+      theme = 'strudel-theme-light';
+    }
+    if (this.identity !== null && this.app !== null) {
+      this.batchcmdurl = this.identity.site.url+"/"+this.app.name+"?theme="+theme;
+    }
+    this.batchcmdsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.batchcmdurl);
+    if (this.app !== null) {
+      this.appconfigurl = this.app.url+"?theme="+theme;
+    }
+    if (!(this.appconfigurl == null)) {
+      this.appconfigsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.appconfigurl);
+    }
+    console.log('updatedURLs');
   }
 
-  
   ngOnDestroy() {
-    console.log('dialog in onDestroy');
     this.rmListen();
-    this.sub.unsubscribe();
+    for (let s of this.subscriptions) {
+      s.unsubscribe();
+    }
   }
 
 
-
-
-
-  becomesReady(ready) {
-      
+  setReady(ready) {
       if (this.launchbtn === undefined) {
           return
       }
       if (ready && !(this.setFocus)) {
-        console.log('reenabling the launch button');
         this.launchbtn.nativeElement._disabled = false;
         this.launchbtn.nativeElement.focus();
         this.setFocus = true;
@@ -93,17 +97,47 @@ export class LaunchDialogComponent implements OnInit {
 
   receiveMessage(event) {
       if (event.data['batchcmd'] !== undefined)  {
-        this.submitAppService.submitcmd.next(event.data['batchcmd']);
+        this.submitcmd = event.data['batchcmd'];
         console.log('recieved a submit cmd');
-        console.log('recieveMessage'+this.submitAppService.submitcmd.value);
+        console.log('recieveMessage'+this.submitcmd);
       }
       if (event.data['appData'] !== undefined)  {
-        this.submitAppService.appData.next(event.data['appData']);
+        this.appData = event.data['appData'];
       }
-      if (event.data == "close iframe") {
-        console.log('recieved close iframe');
-        timer(0).subscribe(() => { console.log("timer "+this.submitAppService.submitcmd.value) ; this.dialogRef.close(); this.submitAppService.launch()}); // complete the current event loop, ie receive all messages to get teh correct time before submitting
-        //this.submitAppService.launch();
+      if (event.data['resize'] !== undefined) {
+        console.log('setting iframe size');
+        this.height = event.data['resize']['height'];
+        this.width = event.data['resize']['width'];
+        console.log(this.height);
       }
   }
+
+  launch() {
+    // tell the batchbuilder iframe that lanch was selected
+    // This is the queue to save the values as defaults for next launch
+    this.batchbuilderiframe.nativeElement.contentWindow.postMessage('launch selected',"*");
+    let bi = new BatchInterface();
+    bi.submitcmd = this.submitcmd;
+    if (this.appData != null){ 
+      let appparams = this.appData;
+      this.tesService.submit(this.app,this.identity,bi,appparams);
+    } else {
+        this.tesService.submit(this.app,this.identity,bi);
+    }
+  }
+
+  clearInfo() {
+      this.appData = null;
+      this.submitcmd = null;
+  }
+
+  canILaunch() {
+    if (this.submitcmd != null) {
+        if (this.app.url == null || this.appData != null ) {
+            this.setReady(true);
+            return;
+        }
+    }
+    this.setReady(false);
+  }
 }
diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html
index ff29b7a5dcd29e0457ac752a9be8a9065569dcff..e19f1d79f842ca026bbd04ca83e24013b89a0247 100644
--- a/src/app/launcher/launcher.component.html
+++ b/src/app/launcher/launcher.component.html
@@ -15,37 +15,74 @@
     <div fxLayout="column" style="height: 100%">
         <div>
             <mat-accordion>
-                <div *ngFor="let id of computeSitesService.appidentities | async">
-                <mat-expansion-panel (click)=selectId(id)>
+            <div *ngFor="let id of (computeSitesService.appidentities | async)">
+                <mat-expansion-panel (opened)="selectId(id)">
                     <mat-expansion-panel-header>
-                        {{ id.displayName() }}
+                        <mat-panel-title>
+                            <button mat-button style="width: 100%">
+                            <div fxLayout="row">
+                                <div style="width: 10px;"></div>
+                                <div fxFlex fxLayout="column">
+                                    <div style="height: 10px"></div>
+                                    <span fxFlex matBadge="{{ (id.healthalerts | async).length }}" 
+                                                              [matBadgeHidden]= "(id.healthalerts | async).length == 0"  
+                                                              matBadgePosition="above before" 
+                                                              matBadgeColor="warn" style=" text-align: left">
+                                        {{ id.displayName() }}
+                                    </span>
+                                </div>
+                            </div>
+                            </button>
+                        </mat-panel-title>
                     </mat-expansion-panel-header>
-                    <app-strudelapplist [applist]=id.site.appCatalog [identity]="id"></app-strudelapplist>
+                    <app-strudelapplist [applist]=id.site.appCatalog [identity]="id" (appChange)="selectApp($event)"></app-strudelapplist>
                 </mat-expansion-panel>
-                </div>
+            </div>
             </mat-accordion>
         </div>
         <div fxFlex></div>
-            <mat-menu #actionmenu="matMenu">
-
-                <div *ngIf="(computeSitesService.appidentities | async).length > 0">
-                    <button mat-menu-item (click)="logout()"><mat-icon>logout</mat-icon>Logout</button>
-                </div>
-                <button mat-menu-item routerLink="/settings"><mat-icon>settings</mat-icon>Settings</button>
-                <button mat-menu-item routerLink="/login">Login</button>
-            </mat-menu>
-            <button mat-icon-button [matMenuTriggerFor]="actionmenu">
-                <mat-icon>more_vert</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 mat-menu-item routerLink="/login"><mat-icon>exit_to_app</mat-icon>Login</button>
+        </mat-menu>
+        <button  mat-icon-button [matMenuTriggerFor]="actionmenu">
+            <mat-icon>person</mat-icon>
+        </button>
     </div>
   </mat-sidenav>
+  <mat-sidenav-content>
+    <div fxLayout="column" style="height: 100%">
+        
+    <!-- The dialog asking how many resources to use to run a job -->
+    <div  *ngIf="app !== null && identity  !== null" >
+      <app-launch-dialog [identity]="identitySubject | async" [appSubject]="appSubject"></app-launch-dialog>
+    </div>
+
+
+    <!-- the list of warning either on the computer system or the users account -->
+    <div *ngIf="(identitySubject | async) !== null" >
+      <mat-list>
+      <div *ngFor="let h of ((identitySubject | async).healthalerts | async)">
+         <mat-list-item>
+         <div *ngIf="h.stat == 'error'">
+           <div class='health-warn'>
+            {{ h.msg }}
+           </div>
+         </div>
+         </mat-list-item>
+      </div>
+      </mat-list>
+    </div>
+
+    <!-- the list of running jobs -->
+    <app-joblist [identitySubject]="identitySubject" [appSubject]="appSubject"></app-joblist>
 
-<app-joblist [identity]="(identitySubject | async)"></app-joblist>
-<!--<router-outlet></router-outlet>-->
-  <div fxFlex></div>
+  </div>
+  </mat-sidenav-content>
 
 </mat-sidenav-container>
-</div>
-</div>
 
 <!-- <app-joblist></app-joblist> -->
diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts
index 598eb7a344540d71183c6464cb4d97ded6c622e2..192f99c9518e73e52be553b960874bc2e8d14c63 100644
--- a/src/app/launcher/launcher.component.ts
+++ b/src/app/launcher/launcher.component.ts
@@ -3,9 +3,11 @@ import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogCon
 import { Location } from '@angular/common';
 // import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
 import { Observable } from 'rxjs/Observable';
+import { Subscription } from 'rxjs';
 import { fromEvent } from 'rxjs';
 import { timer } from 'rxjs/observable/timer';
 import { repeat } from 'rxjs/operators';
+import { Router, NavigationStart } from '@angular/router';
 
 import {Strudelapp} from '../strudelapp';
 import { StrudelappsService } from '../strudelapps.service';
@@ -21,8 +23,6 @@ import { ComputesitesService } from '../computesites.service';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
 
 
-import {OverlayContainer} from '@angular/cdk/overlay';
-
 
 
 
@@ -37,41 +37,42 @@ export class LauncherComponent implements OnInit {
   public strudelapps: Strudelapp[];
   @Input() menuopen: boolean;
 
-  public app: Strudelapp;
+  public appSubject: BehaviorSubject<Strudelapp>;
   public authorised: boolean;
-  public identity: Identity;
-  //  public identitySubject: BehaviorSubject<Identity>;
+  //public identity: Identity;
+  public identitySubject: BehaviorSubject<Identity>;
 
   public identities: Identity[];
   public sshauthzservers: SshAuthzServer[];
   private launchwindow: any;
   private launchwindowWatcher: any;
+  private subscriptions: Subscription[];
 
   constructor( public dialog: MatDialog,
                 public tesService: TesService,
                 public backendSelectionService: BackendSelectionService,
                 public authService: AuthorisationService,
                 public computeSitesService: ComputesitesService,
-                public overlayContainer: OverlayContainer,
+                private router: Router,
                 ) {
-      this.authService.sshAuthzServers.subscribe(o => {this.updateSshAuthZServers(o)});
-      this.computeSitesService.appidentities.subscribe(o => this.verifyIdValid(o));
+      this.subscriptions = [];
+      this.subscriptions.push(this.authService.sshAuthzServers.subscribe(o => {this.updateSshAuthZServers(o)}));
+      this.subscriptions.push(this.computeSitesService.appidentities.subscribe(o => this.navLogin(o)));
+      this.subscriptions.push(this.computeSitesService.appidentities.subscribe(o => this.getHealth(o)));
+      this.appSubject = new BehaviorSubject<Strudelapp>(null);
+      this.identitySubject = new BehaviorSubject<Identity>(null);
+  }
+
+  navLogin(o) {
+    if (o.length == 0) {
+      this.router.navigate(['/login']);
+    }
   }
 
-  verifyIdValid(o) {
-      let currentId = this.tesService.identitySubject.value
-      if (currentId === undefined || currentId == null) {
-          return;
-      }
-      for (let id of this.computeSitesService.appidentities.value) {
-          if (currentId == id) {
-              return;
-          }
-      }
-      this.tesService.identitySubject.next(null);
-      //if (!(this.identitySubject.value in this.computeSitesService.appidentities.value)) {
-      //    this.identitySubject.next(null);
-      //}
+  getHealth(o) {
+    for (let id of o) {
+      this.tesService.getHealthAlerts(id);
+    }
   }
 
   updateSshAuthZServers(o) {
@@ -83,31 +84,20 @@ export class LauncherComponent implements OnInit {
 
   }
 
-
-  logout() {
-      let dialogRef = this.dialog.open(LogoutdialogComponent, {
-        width: '250px',
-        height: '400px',
-      });
+  ngOnDestroy() {
+    for (let s of this.subscriptions) {
+      s.unsubscribe();
+    }
   }
 
-  login (sshauthzserver) {
-    this.authService.login(sshauthzserver);
+  selectId(id: Identity) {
+    this.identitySubject.next(id);
+    this.appSubject.next(null);
   }
 
-  signup(sshauthzserver) {
-      window.open(sshauthzserver.signup);
+  selectApp(app: Strudelapp) {
+    this.appSubject.next(app);
   }
 
 
-  selectId(id: Identity) {
-    this.identity=id;
-    this.tesService.identitySubject.next(id);
-    this.tesService.clearJobs();
-    this.tesService.getJobs(id);
-    this.tesService.cancelHealthRequests();
-    this.tesService.getUserHealth(id);
-    this.tesService.getCachetIncidents(id);
-  }
-
 }
diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html
index f61ef29bbdb343dee5196e86e35eeffde715521d..93f7c3b39d4592b6cbc9e19cb6d05a9afb34db3c 100644
--- a/src/app/login/login.component.html
+++ b/src/app/login/login.component.html
@@ -1,3 +1,12 @@
+
+<mat-toolbar color="primary" style="width: 100%">
+  <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-sidenav-container style="height: 100%; width: 100%">
     <mat-sidenav>
     </mat-sidenav>
@@ -18,8 +27,10 @@
                             </mat-form-field>
                         </div>
                         <div style="width: 100%">
-                            <button fxFlex mat-button  (click)="login()">Login</button>
-                            <button fxFlex mat-button  routerLink="/launch">Cancel</button>
+                            <button fxFlex mat-button (click)="login()">Login</button>
+                            <div fxFlex *ngIf="(authService.loggedInAuthZ | async).length > 0">
+                                <button fxFlex mat-button  routerLink="/launch">Cancel</button>
+                            </div>
                         </div>
                         <div *ngIf="selected !== undefined">
                             {{ selected.desc }}
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
index a0dd4a5f5aa076a67d3b3da103cd73cefb2144b2..6b2f81307e2ef5564676b520bf1efca6b0c7fa9a 100644
--- a/src/app/login/login.component.ts
+++ b/src/app/login/login.component.ts
@@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material';
 import {OverlayContainer} from '@angular/cdk/overlay';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+import { Subscription } from 'rxjs';
+import { Router, NavigationStart } from '@angular/router';
 
 import {Strudelapp} from '../strudelapp';
 import { StrudelappsService } from '../strudelapps.service';
@@ -21,12 +23,10 @@ import { ComputesitesService } from '../computesites.service';
 })
 export class LoginComponent implements OnInit {
   public strudelapps: Strudelapp[];
-  public themeSubject: BehaviorSubject<string>;
 
   public app: Strudelapp;
   public authorised: boolean;
   public identity: Identity;
-  //  public identitySubject: BehaviorSubject<Identity>;
 
   public identities: Identity[];
   public sshauthzservers: SshAuthzServer[];
@@ -39,6 +39,7 @@ export class LoginComponent implements OnInit {
   public themes: any[] = [ {'name':'Light','value':'strudel-light-theme'},
                    {'name': 'Dark','value':'strudel-dark-theme'}]
   public selected: any;
+  private subscriptions: Subscription[];
 
 
   constructor( public dialog: MatDialog,
@@ -47,14 +48,13 @@ export class LoginComponent implements OnInit {
                 public authService: AuthorisationService,
                 public computeSitesService: ComputesitesService,
                 public overlayContainer: OverlayContainer,
+                private router: Router,
                 ) {
 
 
-      this.authService.sshAuthzServers.subscribe(o => {this.updateSshAuthZServers(o)});
-      this.computeSitesService.appidentities.subscribe(o => this.verifyIdValid(o));
-      this.backendSelectionService.apiserver.subscribe((s) => this.selectedApiServer = s);
-      this.themeSubject = new BehaviorSubject<string>('strudel-dark-theme');
-      this.themeSubject.subscribe(v => this.setTheme(v));
+      this.subscriptions = []
+      this.subscriptions.push(this.authService.sshAuthzServers.subscribe(o => {this.updateSshAuthZServers(o)}));
+       
   }
     
   updateSshAuthZServers(o) {
@@ -62,54 +62,29 @@ export class LoginComponent implements OnInit {
   }
 
   ngOnInit() {
-    this.strudelapps = [];
-    var lstheme: string;
-    try {
-      console.log('retrieve theme from localStorage');
-            lstheme=localStorage.getItem('strudel-theme');
-      console.log(lstheme)
-    } catch {
+    //this.subscriptions.push(this.computeSitesService.appidentities.subscribe(o => this.navLaunch(o)));
+    let navlaunch = localStorage.getItem('strudel-navlaunch')
+    if (navlaunch == 'true') {
+      localStorage.setItem('strudel-navlaunch',null)
+      this.subscriptions.push(this.computeSitesService.appidentities.subscribe(o => this.navLaunch(o)));
     }
-    if (lstheme == null) {
-      console.log('no theme set, using light');
-      lstheme = 'strudel-light-theme';
-    }
-    //Set the value of this.theme so that the selection shows a value rather than a blank
-    console.log('trying to find the initial value for theme');
-    for (let t of this.themes) {
-      if (t.value == lstheme) {
-        console.log('found the initial value',t);
-        this.theme = t;
-      }
-    }
-    this.setTheme(lstheme);
-
   }
 
-  verifyIdValid(o) {
-      let currentId = this.tesService.identitySubject.value
-      if (currentId === undefined || currentId == null) {
-          return;
-      }
-      for (let id of this.computeSitesService.appidentities.value) {
-          if (currentId == id) {
-              return;
-          }
-      }
-      this.tesService.identitySubject.next(null);
-      //if (!(this.identitySubject.value in this.computeSitesService.appidentities.value)) {
-      //    this.identitySubject.next(null);
-      //}
+  ngOnDestroy() {
+    for (let s of this.subscriptions) {
+      s.unsubscribe();
+    }
   }
 
-  logout() {
-      let dialogRef = this.dialog.open(LogoutdialogComponent, {
-        width: '250px',
-        height: '400px',
-      });
+  navLaunch(o) {
+    if (this.computeSitesService.appidentities.value.length > 0) {
+      this.router.navigate(['/launch']);
+    }
   }
 
+
   login () {
+    localStorage.setItem('strudel-navlaunch','true');
     this.authService.login(this.selected);
   }
 
@@ -117,22 +92,6 @@ export class LoginComponent implements OnInit {
       window.open(sshauthzserver.signup);
   }
 
-  selectSite(event: any) {
-    console.log('selecteSite');
-    console.log(event);
-    console.log(this.selected);
-  }
-
-  selectId(id: Identity) {
-    this.identity=id;
-    this.tesService.identitySubject.next(id);
-    this.tesService.clearJobs();
-    this.tesService.getJobs(id);
-    this.tesService.cancelHealthRequests();
-    this.tesService.getUserHealth(id);
-    this.tesService.getCachetIncidents(id);
-  }
-
   loadConfig(event) {
       const reader = new FileReader();
       reader.onload = (e: any) => {
@@ -183,7 +142,6 @@ export class LoginComponent implements OnInit {
 }
 
   selectTheme(event) {
-    this.themeSubject.next(event.value);
     localStorage.setItem('strudel-theme',event.value);
   }
 }
diff --git a/src/app/logout/logout.component.css b/src/app/logout/logout.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/logout/logout.component.html b/src/app/logout/logout.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f90533663255ada7158eb5e0dffde6a7d10851e6
--- /dev/null
+++ b/src/app/logout/logout.component.html
@@ -0,0 +1,10 @@
+<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>
+
+<button mat-button (click)="logout()">Logout</button><button mat-button routerLink="/launch">Cancel</button>
diff --git a/src/app/logout/logout.component.spec.ts b/src/app/logout/logout.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c04dbe281b80e2b989d1b16dab894b5b425813b9
--- /dev/null
+++ b/src/app/logout/logout.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LogoutComponent } from './logout.component';
+
+describe('LogoutComponent', () => {
+  let component: LogoutComponent;
+  let fixture: ComponentFixture<LogoutComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LogoutComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogoutComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/logout/logout.component.ts b/src/app/logout/logout.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..780cc93219a2a054d7e686381d71a3760b38de90
--- /dev/null
+++ b/src/app/logout/logout.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnInit } from '@angular/core';
+import { Router, NavigationStart } from '@angular/router';
+
+import { AuthorisationService } from '../authorisation.service';
+
+@Component({
+  selector: 'app-logout',
+  templateUrl: './logout.component.html',
+  styleUrls: ['./logout.component.css']
+})
+export class LogoutComponent implements OnInit {
+
+  constructor(
+    private authService: AuthorisationService,
+    private router: Router,
+  ) {
+ }
+
+  ngOnInit() {
+  }
+
+  logout() {
+    this.authService.logout();
+    this.router.navigate(['/launch']);
+  }
+
+}
diff --git a/src/app/strudelapplist/strudelapplist.component.html b/src/app/strudelapplist/strudelapplist.component.html
index 836982dceeb8d721a07e84c221ab04ea09646ae1..f5dac24669ac08b503755f600a4cfdd8ffeadc66 100644
--- a/src/app/strudelapplist/strudelapplist.component.html
+++ b/src/app/strudelapplist/strudelapplist.component.html
@@ -1,16 +1,11 @@
+<mat-list style="margin-left: 30px">
 <div *ngFor="let app of applist | async">
   <div *ngIf="app.startscript != null">
-    <button mat-button (click)=configure(app)>{{ app.name }}</button>
+    <button mat-button (click)=select(app) style="width: 100%; text-align: right">{{ app.name }}</button>
   </div>
   <!-- The following allows for a nested set of apps -->
   <div *ngIf="app.applist != null">
-    <mat-accordion>
-    <mat-expansion-panel>
-      <mat-expansion-panel-header>
-      {{ app.name }}
-      </mat-expansion-panel-header>
-      <app-strudelapplist [applist]=app.applist [identity]=identity></app-strudelapplist>
-    </mat-expansion-panel>
-  </mat-accordion>
+      <app-strudelapplist [applist]=app.applist [identity]=identity (appChange)="select($event)"></app-strudelapplist>
   </div>
 </div>
+</mat-list>
diff --git a/src/app/strudelapplist/strudelapplist.component.ts b/src/app/strudelapplist/strudelapplist.component.ts
index d2add9f2b1faac2edf9e414ccd2e7cf1dfc0e941..23c39ea48e154631d3c642da38f27a7db6161442 100644
--- a/src/app/strudelapplist/strudelapplist.component.ts
+++ b/src/app/strudelapplist/strudelapplist.component.ts
@@ -1,8 +1,7 @@
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
 import { Observable } from 'rxjs/Observable';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import {Strudelapp} from '../strudelapp';
-import { SubmitAppService } from '../submit-app.service';
 import { Identity } from '../identity';
 import { repeat } from 'rxjs/operators';
 import { BatchInterface} from '../batchinterface';
@@ -10,6 +9,7 @@ import { timer } from 'rxjs/observable/timer';
 import { LaunchDialogComponent } from '../launch-dialog/launch-dialog.component';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
 import { DomSanitizer} from '@angular/platform-browser';
+import { TesService } from '../tes.service';
 
 
 
@@ -22,36 +22,21 @@ import { DomSanitizer} from '@angular/platform-browser';
 export class StrudelapplistComponent implements OnInit {
   @Input() applist: Strudelapp[];
   @Input() identity: Identity;
+  @Output() appChange: EventEmitter<Strudelapp>;
+  @Input() app: Strudelapp;
 
 
-  constructor(private submitAppService: SubmitAppService, public dialog: MatDialog,
-  private domSanitizer: DomSanitizer) {
+  constructor(
+    private tesService: TesService,
+    private domSanitizer: DomSanitizer) {
+    this.appChange = new EventEmitter<Strudelapp>();
   }
 
   ngOnInit() {
   }
 
-  // openLaunchWindow(app: Strudelapp) {
-  //   this.submitAppService.launchAppModal(app,this.identity);
-  // }
-
-  configure(app: Strudelapp) {
-    // this.submitAppService.configApp.next({app: app, this.identity});
-    this.submitAppService.app.next(app);
-    this.submitAppService.identity.next(this.identity);
-    if (app.url == null) {
-      let dialogRef = this.dialog.open(LaunchDialogComponent, {
-          width: '40vw',
-          height: '60vh'
-          })
-        } else {
-          let dialogRef = this.dialog.open(LaunchDialogComponent, {
-              width: '95vw',
-              height: '95vh'
-              })
-        }
-    // this.route.navigate['configlaunch'];
+  select(app: Strudelapp) {
+    this.appChange.emit(app);
   }
 
-
 }
diff --git a/src/app/submit-app.service.ts b/src/app/submit-app.service.ts
index 97d34acce07e40529112882509a659b2a191629a..43b2e68eb3e22246b389772d3aae91834d8cd286 100644
--- a/src/app/submit-app.service.ts
+++ b/src/app/submit-app.service.ts
@@ -59,6 +59,7 @@ export class SubmitAppService {
     this.identity.subscribe(() => this.clearInfo());
     this.submitcmd.subscribe(() => this.canILaunch());
     this.appData.subscribe(()=> this.canILaunch());
+    this.app.subscribe(() => { console.log('app change'); console.log(this.app.value);});
   }
 
   launch() {
diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts
index cde58cbe9aa4ed51018a410b1fd86b0f68ea5efd..9a633270e9b4c143a7c40edbd8800016413be9b2 100644
--- a/src/app/tes.service.ts
+++ b/src/app/tes.service.ts
@@ -37,14 +37,12 @@ private twsproxy: string;
 // public Base='http://localhost:5000';
 public statusMsg: BehaviorSubject<any>;
 public jobs: any[];
-public busy: BehaviorSubject<boolean> ;
 // public joblist: BehaviorSubject<{ [id: string ]: Job[]}>;
-public joblist: BehaviorSubject<Job[]>;
-public userhealth: BehaviorSubject<Health[]>;
+//public joblist: BehaviorSubject<Job[]>;
+//public userhealth: BehaviorSubject<Health[]>;
 
 private timerSubscription: any;
 private appwindow: any;
-private appwindowWatcher: any;
 public apiserver: BehaviorSubject<APIServer>;
 public apiservers: BehaviorSubject<APIServer[]>;
 private updateJobSub: Subscription;
@@ -54,6 +52,7 @@ private nextUpdate: Subscription;
 private cancelRequests: Subject<string>;
 public theme: BehaviorSubject<string>;
 public identitySubject: BehaviorSubject<Identity>;
+public appSubject: BehaviorSubject<Strudelapp>;
 
 // public batchinterface: {[id: string] : BatchInterface};
 
@@ -65,18 +64,16 @@ public identitySubject: BehaviorSubject<Identity>;
               private backendSelectionService: BackendSelectionService,
               private location: Location ) {
 
-    this.busy = new BehaviorSubject<boolean>(false);
     // this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({});
-    this.joblist = new BehaviorSubject<Job[]>([]);
-    this.cachetincidents = new BehaviorSubject<Health[]>([]);
-    this.userhealth = new BehaviorSubject<Health[]>([{'stat':'ok','msg':''}])
+    //this.joblist = new BehaviorSubject<Job[]>([]);
+    //this.cachetincidents = new BehaviorSubject<Health[]>([]);
+    //this.userhealth = new BehaviorSubject<Health[]>([{'stat':'ok','msg':''}])
     this.apiserver = new BehaviorSubject<APIServer>(null);
     this.apiservers = new BehaviorSubject<APIServer[]>([]);
     this.cancelRequests = new Subject<string>();
-    this.identitySubject = new BehaviorSubject<Identity>(null);
+    //this.identitySubject = new BehaviorSubject<Identity>(null);
+    //this.appSubject = new BehaviorSubject<Strudelapp>(null);
 
-    this.timerSubscription = null;
-    this.appwindowWatcher = null;
       this.backendSelectionService.apiserver.subscribe( (value) => { if (value != null) {this.twsproxy = value.tws ; this.Base = value.tes }});
     this.theme = new BehaviorSubject('strudel-light-theme');
     // this.batchinterface = {};
@@ -115,7 +112,7 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
    
    var joblist: Job[] = []
    var jobquery: Job[] = <Job[]>resp;
-   var lastjoblist: Job[] = this.joblist.value;
+   var lastjoblist: Job[] = identity.joblist.value;
    var qjobids: any[] = [];
    var jobids: any[] = [];
    var j: Job;
@@ -158,9 +155,8 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
      }
    }
    joblist = joblist.sort((a,b) =>  (a.jobid < b.jobid)? 1:-1);
-   this.joblist.next(joblist);
+   identity.joblist.next(joblist);
    this.statusMsg.next(null);
-   this.nextUpdate = timer(5000).subscribe(() => this.getJobs(identity));
  }
 
  private getBatchInterfaceError(error: any) {
@@ -168,28 +164,25 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
  }
 
 
- public clearJobs() {
-     this.joblist.next([]);
- }
 
- private getCachetIncidentsError(error: any) {
-   return this.getJobsError(error)
+ private getCachetIncidentsError(error: any, identity: Identity) {
+   return this.getJobsError(error, identity)
  }
 
  
  public cancelHealthRequests() {
-    this.cancelRequests.next('cancel')
-    this.cachetincidents.next([])
-    this.userhealth.next([])
+    this.cancelRequests.next('cancel');
  }
 
- private getUserHealthError(error: any) {
+ private getUserHealthError(error: any, identity: Identity) {
    console.log('user health error');
-   return this.getJobsError(error)
+   identity.healthalerts.next([]);
+   this.statusMsg.next("There was an error checking your user account");
+   //return this.getJobsError(error,identity)
  }
 
- private getJobsError(error: any) {
-   this.joblist.next([]);
+ private getJobsError(error: any, identity: Identity) {
+   identity.joblist.next([]);
    if (error.status == 0) {
      this.statusMsg.next("A network error occurred. Please try again later");
      return
@@ -212,11 +205,7 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
   // remove from the job list any jobs for identities that we don't know about
-  let oldjobs = this.joblist.value;
-  if ( identity === undefined) {
-    this.joblist.next([]);
-    return
-  }
+  let oldjobs = identity.joblist.value;
   let bi = new BatchInterface();
   bi.cancelcmd = identity.site.cancelcmd;
   bi.statcmd = identity.site.statcmd;
@@ -225,24 +214,9 @@ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
   params.set('host',JSON.stringify(identity.site.host));
   params.set('username',JSON.stringify(identity.username));
   
-  // updateJobSub indicates that a request is already in process. We can cancel it
-  // we've probably moved to a different server
-  if (this.updateJobSub !== undefined) {
-      if (!this.updateJobSub.closed) {
-          this.updateJobSub.unsubscribe();
-      }
-  }    // next update indicates that we will refresh the joblist in 5 seconds (or whatever)
-     // In theory I think it should unsubsribe on its own since its a timer that should emit one value
-     // but unsubscribe just to be sure.
-   if (this.nextUpdate !== undefined) {
-       if (!(this.nextUpdate.closed)) {
-            this.nextUpdate.unsubscribe()
-       }
-   }
-
   this.updateJobSub = this.http.get<Job[]>(this.Base+'/stat'+'?'+params.toString(),options)
                 .subscribe(resp => this.updateJoblist(resp, identity),
-                           error => this.getJobsError(error));
+                           error => this.getJobsError(error, identity));
 }
 
 getUserHealth(identity: Identity)  {
@@ -254,17 +228,21 @@ getUserHealth(identity: Identity)  {
     return
   }
   if (identity.site.userhealth === undefined) {
-    this.userhealth.next([{'stat':'ok','msg':''}])
     return
   }
-  console.log('in get User Health');
   params.set('statcmd',JSON.stringify(identity.site.userhealth));
   params.set('host',JSON.stringify(identity.site.host));
   params.set('username',JSON.stringify(identity.username));
   
   this.updateUserHealthSub = this.http.get<Health[]>(this.Base+'/stat'+'?'+params.toString(),options)
     .pipe(takeUntil(this.cancelRequests))
-    .subscribe(resp => this.userhealth.next(resp), error => this.getUserHealthError(error));
+    .subscribe(resp => this.addUserHealth(identity,resp), error => this.getUserHealthError(error,identity));
+}
+
+getHealthAlerts(identity: Identity) {
+  identity.healthalerts.next([]);
+  this.getCachetIncidents(identity);
+  this.getUserHealth(identity);
 }
 
 getCachetIncidents(identity: Identity) {
@@ -273,37 +251,43 @@ getCachetIncidents(identity: Identity) {
   // remove from the job list any jobs for identities that we don't know about
   let params = new URLSearchParams();
 
-
-  if (identity === undefined) {
-    return
-  }
   if (identity.site.cacheturis === undefined ||  identity.site.cacheturis.length == 0) {
-    this.cachetincidents.next([])
     return
   }
   for (let uri of identity.site.cacheturis) {
     this.http.get(uri,options)
       .pipe(takeUntil(this.cancelRequests))
-      .subscribe(resp => this.addCachetIncidents(resp), error => this.getCachetIncidentsError(error));
+      .subscribe(resp => this.addCachetIncidents(identity,resp), error => this.getCachetIncidentsError(error,identity));
   }
 }
 
-addCachetIncidents(resp) {
-  let ci = this.cachetincidents.value
-  console.log(resp);
+addCachetIncidents(identity,resp) {
+  let ci = identity.healthalerts.value;
   for (let i of resp.data) {
-    console.log(i);
     if (i.status == 3 || i.status == 4) {
-      console.log('incident resolved, continuing');
       continue;
     }
     let h = new Health();
-    console.log('creating new health');
     h.stat = 'error';
     h.msg = i.message;
     ci.push(h);
   }
-  this.cachetincidents.next(ci)
+  identity.healthalerts.next(ci);
+}
+
+addUserHealth(identity,resp) {
+  let ci = identity.healthalerts.value;
+  console.log('in add User HEalth');
+  for (let i of resp) {
+    if (i.stat != 'ok') {
+      let h = new Health();
+      h.stat = 'error';
+      h.msg = i.message;
+      ci.push(h);
+    }
+  }
+  identity.healthalerts.next(ci);
+  console.log('id health is',identity.healthalerts.value);
 }
 
 
@@ -322,7 +306,6 @@ addCachetIncidents(resp) {
        } else {
          this.statusMsg.next('Job submission failed');
        }
-     this.busy.next(false);
    }
  }
  cancelError(error: any) {
@@ -333,7 +316,6 @@ addCachetIncidents(resp) {
        } else {
          this.statusMsg.next('Job submission failed');
        }
-     this.busy.next(false);
    }
  }
 
@@ -345,7 +327,6 @@ addCachetIncidents(resp) {
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    this.statusMsg.next('Submitting job');
-   this.busy.next(true);
    let paramstr = this.buildParams(app,identity,batchinterface);
    // let body = this.buildBody(app,appparams)
    let keys = JSON.stringify(this.authorisationService.getKeys());
@@ -357,7 +338,6 @@ addCachetIncidents(resp) {
    let body = {'app': app, 'appparams': appparams, 'keys': keys, 'ids': JSON.stringify(JSON.stringify(ids))}
    this.http.post<any>(this.Base+'/submit'+'?'+paramstr, body, options)
                                           .subscribe(resp => {
-                                                              this.busy.next(false);
                                                               this.statusMsg.next(null)
                                                             },
                                                     error => this.submissionError(error));
@@ -365,7 +345,6 @@ addCachetIncidents(resp) {
 
  submitted(resp: any, identity: Identity ) {
    console.log('submitted',resp);
-   this.busy.next(false);
    this.getJobs(identity);
  }
 
@@ -393,13 +372,14 @@ addCachetIncidents(resp) {
    let username = job.identity.username;
    let loginhost = job.identity.site.host;
    let batchhost = job.batch_host;
+   let jobid = job.jobid;
    let params = new URLSearchParams;
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    params.set('cmd',JSON.stringify(job.app.paramscmd));
    let paramstr = params.toString();
    job.connectionState = 1;
-   this.http.get<string>(this.Base+'/appinstance/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, options)
+   this.http.get<string>(this.Base+'/appinstance/'+username+'/'+loginhost+'/'+batchhost+'/'+jobid+'?'+paramstr, options)
                 // .pipe(catchError(this.handleError))
          .subscribe(resp =>  { job.appinst = resp; this.createTunnel(job) },
              error => { this.handleAppInstanceError(job,error) })
@@ -453,32 +433,18 @@ addCachetIncidents(resp) {
      var re = /^https:\/\/([a-z0-9\.-]+)\/?/;
      let twshost = this.twsproxy.replace(re,"$1");
      let windowloc = url.replace(/\{twsproxy\}/g,this.twsproxy).replace(/twshost/g,twshost);
-     //let windowloc = url
-
-   // let windowloc = this.router.config
-   this.appwindow = window.open(windowloc);
+     this.appwindow = window.open(windowloc);
      if (this.appwindow == null) {
          this.statusMsg.next('It looks like a window failed to open. Please check your popup blocker settings (Strudel 2 needs to be able to open a window to your application');
          return;
      }
      if (this.appwindow.closed) {
      }
-   if (!(this.appwindowWatcher === null)) {
-     this.appwindowWatcher.unsubscribe()
-   }
-
-   let dialogRef = this.dialog.open(ModaldialogComponent, {
-     width: '600px',
-   });
-   this.appwindowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchAppwindow(this.appwindow,dialogRef));
-   // this.appwindow.location.assign(windowloc);
  }
 
  public connect(job: Job, appinst?: any) {
-   // this.statusMsg.next(null);
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
-   // this.busy.next(true);
    this.getInterface(job); // getInterface will subsequently called getAppInstance, which will call createTunnel, which will openAppWindow
  }
 
diff --git a/src/assets/config/authservers.json b/src/assets/config/authservers.json
index 56c5c679c99270c9bca18adf10d7e961ced74d12..b6b8aa52d0500fae3ca2f0cebb6d52ff7f93ad78 100644
--- a/src/assets/config/authservers.json
+++ b/src/assets/config/authservers.json
@@ -1,7 +1,7 @@
 [
 
     {
-      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/aafcentral",
+      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/choose",
       "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/",
       "client_id": "Q96kt2Vtw6S78dpORktM81DH",
       "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key",
@@ -11,17 +11,5 @@
       "scope": "user:email",
       "cafp": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
       "signup": "https://docs.massive.org.au/M3/requesting-an-account.html"
-    },
-    {
-      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/google",
-      "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/",
-      "client_id": "Q96kt2Vtw6S78dpORktM81DH",
-      "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key",
-      "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout",
-      "name": "CVL with Google ID",
-      "icon": null,
-      "scope": "user:email",
-      "cafp": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
-      "signup": "https://docs.massive.org.au/M3/requesting-an-account.html"
     }
 ]
diff --git a/src/health-warn.scss b/src/health-warn.scss
index 170b6ca40dca128d7df01a35a7afee1aa9c08f2a..c5cf7b0ed9eb0718f3c36196ddc42d1da1d05d3a 100644
--- a/src/health-warn.scss
+++ b/src/health-warn.scss
@@ -7,4 +7,9 @@
   .health-warn {
     color: mat-color($warn);
   }
+  .mat-menu-panel {
+      .mat-menu-item {
+          background-color: mat-color($background);
+      }
+  }
 }