added side panels

This commit is contained in:
2020-03-17 10:03:24 -05:00
parent eb19039ed7
commit d6f5187090
7 changed files with 285 additions and 103 deletions

View File

@@ -1,54 +1,93 @@
<div class="container"> <div class="container">
<mat-sidenav-container <mat-sidenav-container fullscreen> <!--[hasBackdrop]="mobileQuery.matches">-->
fullscreen
[hasBackdrop]="mobileQuery.matches"> <mat-sidenav #lnav mode="side" class="sidenav" closed >
<mat-sidenav #lnav <div class="content">
mode="over" <mat-accordion>
class="sidenav"> <mat-expansion-panel *ngFor="let fields of this.sidebar | keyvalue">
<div class="content">
<mat-accordion >
<mat-expansion-panel *ngFor="let location of this.dataservice.serverMessages">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title>{{location.location}}</mat-panel-title> <mat-panel-title>{{fields.key}}</mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<p *ngFor="let keys of location | keyvalue">{{keys.key}} {{keys.value}}</p> <mat-accordion>
<mat-expansion-panel *ngFor="let location of fields.value"
cdkDropList #locationList="cdkDropList" [cdkDropListConnectedTo]="[chartList]" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel-header>
<mat-panel-title cdkDrag>{{location.location}}</mat-panel-title>
</mat-expansion-panel-header>
<div class="item-list">
<div class="item-box" *ngFor="let keys of location | keyvalue" cdkDrag>{{keys.key}}: {{keys.value}}</div>
</div>
</mat-expansion-panel>
</mat-accordion>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div> </div>
</mat-sidenav> </mat-sidenav>
<mat-sidenav #rnav mode="side" class="sidenav" position="end" closed >
<div *ngFor="let route of nav">
<a mat-button routerLink="{{route.path}}" routerLinkActive="active" (click)="toggleMobileNav(rnav)" >{{route.title}}</a>
</div>
<mat-menu #appMenu="matMenu">
<ng-container *ngFor="let item of this.dataservice.groups; let i = index">
<button mat-menu-item (click)="roleChange(i)"> {{ item }}</button>
</ng-container>
</mat-menu>
<button mat-button [matMenuTriggerFor]="appMenu" >
Role Change
</button>
<div></div>
<button mat-flat-button (click)="this.signOut(rnav)" color="accent">Sign Out</button>
</mat-sidenav>
<mat-sidenav-content class="sidenav-content"> <mat-sidenav-content class="sidenav-content">
<mat-toolbar
class="toolbar" <mat-toolbar class="toolbar" [class.app-is-mobile]="mobileQuery.matches" color="primary">
[class.app-is-mobile]="mobileQuery.matches" <button mat-icon-button (click)="lnav.toggle()" *ngIf="this.authService.loggedIn">
color="primary">
<button
mat-icon-button
(click)="lnav.toggle()"
*ngIf="mobileQuery.matches">
<mat-icon>menu</mat-icon> <mat-icon>menu</mat-icon>
</button> </button>
{{title}} {{title}}
<div *ngFor="let route of nav"> <div class="flex-spacer"></div>
<button mat-icon-button (click)="rnav.toggle()" *ngIf="this.authService.loggedIn">
<mat-icon>menu</mat-icon>
</button>
<!-- <div *ngFor="let route of nav">
<a mat-button routerLink="{{route.path}}" routerLinkActive="active" (click)="toggleMobileNav(lnav)">{{route.title}}</a> <a mat-button routerLink="{{route.path}}" routerLinkActive="active" (click)="toggleMobileNav(lnav)">{{route.title}}</a>
</div> </div>
<div class="fill-space"></div> <div class="fill-space"></div>
<span whoami></span> <div class="flex-spacer"></div>
<mat-menu #appMenu="matMenu">
<ng-container *ngFor="let item of this.dataservice.groups; let i = index">
<button mat-menu-item (click)="roleChange(i)"> {{ item }}</button>
</ng-container>
</mat-menu>
<button mat-icon-button [matMenuTriggerFor]="appMenu">
<mat-icon>more_vert</mat-icon>
</button>
<span whoami></span> -->
</mat-toolbar> </mat-toolbar>
<mat-drawer-container class="sidenav-container"> <mat-drawer-container class="sidenav-container">
<mat-drawer <!-- <mat-drawer mode="side" [opened]="!mobileQuery.matches">
mode="side"
[opened]="!mobileQuery.matches">
<div class="content"> <div class="content">
<mat-accordion > <mat-accordion>
<mat-expansion-panel *ngFor="let location of this.dataservice.serverMessages"> <mat-expansion-panel *ngFor="let fields of this.sidebar | keyvalue">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title>{{location.location}}</mat-panel-title> <mat-panel-title>{{fields.key}}</mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<p *ngFor="let keys of location | keyvalue">{{keys.key}}: {{keys.value}}</p> <mat-accordion>
</mat-expansion-panel> <mat-expansion-panel *ngFor="let location of fields.value">
<mat-expansion-panel-header>
<mat-panel-title>{{location.location}}</mat-panel-title>
</mat-expansion-panel-header>
<p *ngFor="let keys of location | keyvalue">{{keys.key}}: {{keys.value}}</p>
</mat-expansion-panel>
</mat-accordion>
</mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div> </div>
</mat-drawer> </mat-drawer> -->
<mat-drawer-content> <mat-drawer-content>
<div class="content"> <div class="content">
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@@ -77,4 +77,30 @@ a {
.active { .active {
color: mat-color($app-active); color: mat-color($app-active);
} }
.flex-spacer {
flex-grow: 1;
}
.item-list {
border: solid 1px #ccc;
min-height: 60px;
background: white;
border-radius: 4px;
overflow: hidden;
display: block;
}
.item-box {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}

View File

@@ -1,7 +1,11 @@
import { Component, ChangeDetectorRef, EventEmitter, Output } from '@angular/core'; import { Component, ChangeDetectorRef, EventEmitter, Output, OnInit } from '@angular/core';
import { MediaMatcher } from '@angular/cdk/layout'; import { MediaMatcher } from '@angular/cdk/layout';
import { MatSidenav } from '@angular/material/sidenav'; import { MatSidenav } from '@angular/material/sidenav';
import { DataService } from './services/data.service'; import { DataService, AWSData } from './services/data.service';
import { AuthService } from './auth/auth.service';
import { Router } from '@angular/router';
import { RangeValueAccessor } from '@angular/forms';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
@Component({ @Component({
@@ -9,8 +13,9 @@ import { DataService } from './services/data.service';
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent { export class AppComponent implements OnInit {
title = 'Henry Pump SCADA'; title = 'HP SCADA';
sidebar: AWSData = [];
mobileQuery: MediaQueryList; mobileQuery: MediaQueryList;
nav = [ nav = [
{ {
@@ -27,19 +32,48 @@ export class AppComponent {
} }
]; ];
private mobileQueryListener: () => void; private mobileQueryListener: () => void;
@Output() toggleSideNav = new EventEmitter(); @Output() toggleSideNav = new EventEmitter();
constructor( changeDetectorRef: ChangeDetectorRef, media: MediaMatcher , public dataservice: DataService) { constructor( changeDetectorRef: ChangeDetectorRef, media: MediaMatcher , public dataservice: DataService,
public authService: AuthService, private router: Router) {
this.mobileQuery = media.matchMedia('(max-width: 600px)'); this.mobileQuery = media.matchMedia('(max-width: 600px)');
this.mobileQueryListener = () => changeDetectorRef.detectChanges(); this.mobileQueryListener = () => changeDetectorRef.detectChanges();
this.mobileQuery.addListener(this.mobileQueryListener); this.mobileQuery.addListener(this.mobileQueryListener);
} }
ngOnInit() {
this.dataservice.message.subscribe((data) => {
data.forEach( element => {
if (this.sidebar[element.field]) {
this.sidebar[element.field].push(element);
} else {
this.sidebar[element.field] = [];
this.sidebar[element.field].push(element);
}
});
});
}
roleChange(index: number) {
this.sidebar = [];
this.dataservice.setRole(index);
}
toggleMobileNav(nav: MatSidenav) { toggleMobileNav(nav: MatSidenav) {
if (this.mobileQuery.matches) { if (this.mobileQuery.matches) {
nav.toggle(); nav.toggle();
} }
} }
signOut(nav: MatSidenav) {
nav.toggle();
this.sidebar = [];
this.dataservice.serverMessages = [];
this.authService.signOut()
.then(() => this.router.navigate(['auth/signin']));
}
drop(event: CdkDragDrop<string[]>) {
console.log(event);
}
} }

View File

@@ -3,6 +3,7 @@ import Auth, { CognitoHostedUIIdentityProvider } from '@aws-amplify/auth';
import { Hub, ICredentials } from '@aws-amplify/core'; import { Hub, ICredentials } from '@aws-amplify/core';
import { Subject, Observable } from 'rxjs'; import { Subject, Observable } from 'rxjs';
import { CognitoUser } from 'amazon-cognito-identity-js'; import { CognitoUser } from 'amazon-cognito-identity-js';
import { DataService } from '../services/data.service';
export interface NewUser { export interface NewUser {
email: string; email: string;
@@ -26,13 +27,14 @@ export class AuthService {
authState: Observable<CognitoUser|any> = this.authStateSubject.asObservable(); authState: Observable<CognitoUser|any> = this.authStateSubject.asObservable();
constructor() { constructor(private dataService: DataService) {
Hub.listen('auth', (data) => { Hub.listen('auth', (data) => {
const { channel, payload } = data; const { channel, payload } = data;
if (channel === 'auth') { if (channel === 'auth') {
this.authStateSubject.next(payload.event); this.authStateSubject.next(payload.event);
} }
}); });
Auth.currentAuthenticatedUser().then(() => this.loggedIn = true).catch(() => this.loggedIn = false);
} }
signUp(user: NewUser): Promise<CognitoUser|any> { signUp(user: NewUser): Promise<CognitoUser|any> {
@@ -53,6 +55,7 @@ export class AuthService {
Auth.signIn(username, password) Auth.signIn(username, password)
.then((user: CognitoUser|any) => { .then((user: CognitoUser|any) => {
this.loggedIn = true; this.loggedIn = true;
this.dataService.startUp();
resolve(user); resolve(user);
}).catch((error: any) => reject(error)); }).catch((error: any) => reject(error));
}); });

View File

@@ -1,19 +1,4 @@
<div class ="viewer"> <div class ="viewer">
<div class="container" fxLayout="row wrap" fxLayoutGap="30px"> <div id="charts" cdkDropList #chartList="cdkDropList" [cdkDropListConnectedTo]="[locationList]" (cdkDropListDropped)="drop($event)"></div>
Total Flow Rate {{ totalFlowRate }}
<div class="flex-spacer"></div>
<mat-menu #appMenu="matMenu">
<ng-container *ngFor="let item of groups; let i = index">
<button mat-menu-item (click)="setRole(i)"> {{ item }}</button>
</ng-container>
</mat-menu>
<button mat-icon-button [matMenuTriggerFor]="appMenu">
<mat-icon>more_vert</mat-icon>
</button>
</div>
<div id="charts"></div>
</div> </div>

View File

@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Chart } from 'chart.js'; import { Chart } from 'chart.js';
import { DataService, MyJSON } from '../services/data.service'; import { DataService, AWSData } from '../services/data.service';
import { CdkDropList } from '@angular/cdk/drag-drop';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
@@ -9,46 +10,111 @@ import { DataService, MyJSON } from '../services/data.service';
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit {
constructor(private dataservice: DataService) { } constructor(private dataservice: DataService) { }
title = 'Bar Chart'; title = 'Bar Chart';
charts = new Array<Chart>(); charts: Array<Chart>;
messages: Array<MyJSON>; messages: Array<AWSData> = [];
charttypes = ['bar', 'line', 'radar', 'doughnut', 'pie', 'polarArea', 'bubble', 'scatter']; charttypes = ['bar', 'line'];
ngOnInit() { userCharts = {
for (let i = 0; i < 8; i++) { 'arn:aws:iam::860246592755:role/HPIoT_CrownQuest_User': {
const chart = document.createElement('canvas'); 'chart-0': {
chart.id = 'chart-' + i.toString(); type: 'bar',
chart.style.width = '100%';
chart.style.height = '500px';
document.getElementById('charts').appendChild(chart);
this.charts.push(new Chart(document.getElementById('chart-' + i.toString()), {
type: this.charttypes[i],
data: { data: {
labels: [], labels: ['Horton 20 WS 9-8', 'Horton 20 WS 9-7', 'Horton 20 WS 10-20', 'Horton 20 WS 10-20'].sort((a, b) => a.localeCompare(b)),
datasets: [{ datasets: [{
label: '', label: 'Horton',
data: {
'Horton 20 WS 9-7': ['volumeflow'],
'Horton 20 WS 10-20': ['pressure', 'depth'],
'Horton 20 WS 9-8': ['volumeflow']
}
}]
}
},
'chart-1': {
type: 'line',
data: {
labels: ['LimeQuest 5 WS 1-1', 'Wilkinson 34 WS 2-2', 'Horton 20 WS 10-20'].sort((a, b) => a.localeCompare(b)),
datasets: [{
label: 'Horton',
data: {
'LimeQuest 5 WS 1-1': ['depth'],
'Horton 20 WS 10-20': ['pressure'],
'Wilkinson 34 WS 2-2': ['current']
}
}]
}
}
},
'arn:aws:iam::860246592755:role/HPIoT_QEP_User': {
'chart-0': {
type: 'radar',
data: {
labels: ['Frequency', 'Pressure', 'Current', 'Down Hole'],
datasets: [{
label: 'POE 1',
data: {
'POE 1': ['frequency', 'pressure', 'current', 'down_hole_level']
}}]
}
}
}
};
ngOnInit() {
this.dataservice.roleSubject.subscribe(() => {
this.initCharts();
});
this.initCharts();
this.dataservice.message.subscribe((data) => {
this.messages = data;
// console.log(this.messages);
this.populate();
});
}
getData(req) {
const x = [];
this.messages.forEach(location => {
if (Object.keys(req).indexOf(location.location) > -1) {
req[location.location].forEach((ele) => {
x.push(location[ele]);
});
}
});
return x;
}
initCharts() {
document.getElementById('charts').innerHTML = '';
this.charts = new Array<Chart>();
Object.keys(this.userCharts[this.dataservice.currentRole]).forEach( key => {
const chart = document.createElement('canvas');
chart.id = key;
chart.style.width = '100%';
chart.style.height = '25rem';
document.getElementById('charts').appendChild(chart);
this.charts.push(new Chart(document.getElementById(key), {
type: this.userCharts[this.dataservice.currentRole][key].type,
data: {
labels: this.userCharts[this.dataservice.currentRole][key].data.labels,
datasets: [{
label: this.userCharts[this.dataservice.currentRole][key].data.datasets.label,
data: [] data: []
}] }]
} }
})); }));
}
this.dataservice.message.subscribe((data) => {
this.messages = data;
this.populate();
}); });
} }
populate() { populate() {
this.charts.forEach(element => { this.charts.forEach(element => {
element.data.labels = this.messages.map((d) => d.location); element.type = this.userCharts[this.dataservice.currentRole][element.canvas.id].type;
element.data.datasets.forEach( dataset => { element.data.labels = this.userCharts[this.dataservice.currentRole][element.canvas.id].data.labels;
dataset.label = 'Volume Flow'; element.data.datasets.forEach( (dataset, ind) => {
dataset.data = this.messages.map( d => d.volumeflow); dataset.label = this.userCharts[this.dataservice.currentRole][element.canvas.id].data.datasets[ind].label;
dataset.data = this.getData(this.userCharts[this.dataservice.currentRole][element.canvas.id].data.datasets[ind].data);
dataset.backgroundColor = dataset.data.map((item, index) => 'rgba(' + dataset.backgroundColor = dataset.data.map((item, index) => 'rgba(' +
index * (255 / this.messages.length) + ',' + index * (255 / dataset.data.length) + ',' +
0 + ',' + 0 + ',' +
(this.messages.length - index) * (255 / this.messages.length) + ',' + '0.8)'); (dataset.data.length - index) * (255 / dataset.data.length) + ',' + '0.8)');
}); });
element.update(); element.update();
}); });

View File

@@ -1,8 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import Auth from '@aws-amplify/auth'; import Auth from '@aws-amplify/auth';
import { WebSocketSubject } from 'rxjs/webSocket'; import { WebSocketSubject } from 'rxjs/webSocket';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject, BehaviorSubject } from 'rxjs';
export interface MyJSON { import { PassThrough } from 'stream';
export interface AWSData {
[key: string]: any; [key: string]: any;
} }
@@ -12,30 +13,57 @@ export interface MyJSON {
export class DataService { export class DataService {
public serverMessages: Array<MyJSON> = []; public serverMessages: Array<AWSData> = [];
public message = new Subject<MyJSON[]>(); public message = new BehaviorSubject<AWSData[]>(this.serverMessages);
private socket$: WebSocketSubject<any>; private socket$: WebSocketSubject<any>;
totalFlowRate = 0; totalFlowRate = 0;
private roles: string[]; private roles: string[];
private groups: string[]; private groups: string[];
private currentRole: string; public currentRole: string;
public roleSubject = new Subject<string>();
private token: string; private token: string;
constructor() { constructor() {
Auth.currentAuthenticatedUser().then(data => { Auth.currentAuthenticatedUser().then(data => {
// console.log(data); // console.log(data);
this.roles = data.signInUserSession.idToken.payload['cognito:roles']; this.roles = data.signInUserSession.idToken.payload['cognito:roles'];
this.groups = data.signInUserSession.idToken.payload['cognito:groups']; this.groups = data.signInUserSession.idToken.payload['cognito:groups'];
this.groups.forEach( (element, index, array) => { this.groups.forEach( (element, index, array) => {
array[index] = element.replace(/_/g, ' '); array[index] = element.replace(/_/g, ' ');
}); });
this.currentRole = data.signInUserSession.idToken.payload['cognito:roles'][0]; this.currentRole = data.signInUserSession.idToken.payload['cognito:roles'][0];
this.token = data.signInUserSession.accessToken.jwtToken; this.token = data.signInUserSession.accessToken.jwtToken;
this.message.subscribe({ this.message.subscribe({
next: d => d next: d => d
}); });
this.connect(); this.roleSubject.subscribe({
}); } next: r => r
});
this.connect();
}).catch((err) =>
err
);
}
startUp() {
Auth.currentAuthenticatedUser().then(data => {
// console.log(data);
this.roles = data.signInUserSession.idToken.payload['cognito:roles'];
this.groups = data.signInUserSession.idToken.payload['cognito:groups'];
this.groups.forEach( (element, index, array) => {
array[index] = element.replace(/_/g, ' ');
});
this.currentRole = data.signInUserSession.idToken.payload['cognito:roles'][0];
this.token = data.signInUserSession.accessToken.jwtToken;
this.message.subscribe({
next: d => d
});
this.roleSubject.subscribe({
next: r => r
});
this.connect();
}).catch((error) => console.log(error));
}
connect() { connect() {
this.connectWS(this.token, this.currentRole); this.connectWS(this.token, this.currentRole);
@@ -68,7 +96,7 @@ export class DataService {
); );
} }
updateList(obj: MyJSON) { updateList(obj: AWSData) {
// console.log(this.serverMessages); // console.log(this.serverMessages);
// console.log(obj); // console.log(obj);
const index = this.serverMessages.findIndex((e) => e.location === obj.location); const index = this.serverMessages.findIndex((e) => e.location === obj.location);
@@ -85,6 +113,7 @@ export class DataService {
setRole(index: number) { setRole(index: number) {
this.currentRole = this.roles[index]; this.currentRole = this.roles[index];
this.roleSubject.next(this.currentRole);
this.socket$.complete(); this.socket$.complete();
this.serverMessages = []; this.serverMessages = [];
this.connect(); this.connect();