* widget layout implemented

This commit is contained in:
Dávid Danyi 2017-09-08 09:43:50 +02:00
parent 7e82c57b0e
commit 985383797e
24 changed files with 436 additions and 124 deletions

14
package-lock.json generated
View File

@ -3246,6 +3246,15 @@
} }
} }
}, },
"gify-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/gify-parse/-/gify-parse-1.0.6.tgz",
"integrity": "sha1-NHRHheiiBLUhtJNtMFBwpqU0fos=",
"requires": {
"commander": "2.11.0",
"jdataview": "2.5.0"
}
},
"glob": { "glob": {
"version": "7.1.2", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
@ -5560,6 +5569,11 @@
"integrity": "sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI=", "integrity": "sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI=",
"dev": true "dev": true
}, },
"jdataview": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jdataview/-/jdataview-2.5.0.tgz",
"integrity": "sha1-MIGz/qZR+TF+xr1P6y3cmKpB1ZU="
},
"jquery": { "jquery": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",

View File

@ -22,6 +22,7 @@
"@angular/platform-browser-dynamic": "^4.0.0", "@angular/platform-browser-dynamic": "^4.0.0",
"@angular/router": "^4.0.0", "@angular/router": "^4.0.0",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"gify-parse": "^1.0.6",
"rxjs": "^5.4.1", "rxjs": "^5.4.1",
"semantic-ui": "^2.2.11", "semantic-ui": "^2.2.11",
"zone.js": "^0.8.14" "zone.js": "^0.8.14"

View File

@ -3,13 +3,23 @@ import {Subscription} from "rxjs/Subscription";
import {TimerObservable} from "rxjs/observable/TimerObservable"; import {TimerObservable} from "rxjs/observable/TimerObservable";
import {KanbanService} from "./kanban/shared/kanban.service"; import {KanbanService} from "./kanban/shared/kanban.service";
import {Router} from "@angular/router"; import {ActivatedRoute, Router} from "@angular/router";
import {SelfUpdaterService} from "./kanban/shared/self-updater.service"; import {SelfUpdaterService} from "./kanban/shared/self-updater.service";
const TIMER_DEPLOY_REFRESH = 30000; const TIMER_DEPLOY_REFRESH = 30000;
const TIMER_JIRA_REFRESH = 60000; const TIMER_JIRA_REFRESH = 60000;
const TIMER_PAGE_SWITCH = 300000; const TIMER_PAGE_SWITCH_TICK = 1000;
/**
* Page switch timer in seconds
* @type {number}
*/
const TIMESPENT_KANBAN = 120000;
const TIMESPENT_TSPINFO = 60000;
const PAGE_KANBAN = '/kanban';
const PAGE_TSPINFO = '/tsp-info-page';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -17,23 +27,25 @@ const TIMER_PAGE_SWITCH = 300000;
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css']
}) })
export class AppComponent implements OnInit, OnDestroy { export class AppComponent implements OnInit, OnDestroy {
private currentPage: string = PAGE_KANBAN;
private lastNavOccured: number = Date.now();
private autoSwitchEnabled: boolean = true;
selfUpdateCheckerTimer: Subscription; private selfUpdateCheckerTimer: Subscription;
reloadJiraIssueTimer: Subscription; private reloadJiraIssueTimer: Subscription;
pageSwitchTimer: Subscription; private pageSwitchTimer: Subscription;
constructor( constructor(private selfUpdaterService: SelfUpdaterService,
private selfUpdaterService: SelfUpdaterService, private kanbanService: KanbanService,
private kanbanService: KanbanService, private router: Router,
private router: Router private activatedRoute: ActivatedRoute) {
) {} }
/** /**
* Initialize application timers: * Initialize application timers:
* - selfUpdateCheckerTimer is used to see if there is a newer revision deployed on the server * - selfUpdateCheckerTimer is used to see if there is a newer revision deployed on the server
* - reloadJiraIssueTimer is used to refresh the status of the jira board * - reloadJiraIssueTimer is used to refresh the status of the jira board
* - pageSwitchTimer handles switching back and forth between page views * - pageSwitchTimer handles switching back and forth between page views
* @todo: pageSwitchTimer
*/ */
public ngOnInit() { public ngOnInit() {
let timer0 = TimerObservable.create(TIMER_DEPLOY_REFRESH, TIMER_JIRA_REFRESH); let timer0 = TimerObservable.create(TIMER_DEPLOY_REFRESH, TIMER_JIRA_REFRESH);
@ -44,17 +56,63 @@ export class AppComponent implements OnInit, OnDestroy {
this.reloadJiraIssueTimer = timer1.subscribe(() => { this.reloadJiraIssueTimer = timer1.subscribe(() => {
this.kanbanService.reload(); this.kanbanService.reload();
}); });
let timer2 = TimerObservable.create(TIMER_PAGE_SWITCH, TIMER_PAGE_SWITCH); this.activatedRoute.queryParamMap.subscribe(params => {
this.pageSwitchTimer = timer2.subscribe(() => { if (!params.has("disableAutoSwitch")) {
// navigate to next page let timer2 = TimerObservable.create(TIMER_PAGE_SWITCH_TICK, TIMER_PAGE_SWITCH_TICK);
// this.router.navigate(); // this.pageSwitchTimer = timer2.subscribe(this.switchSubscriber.bind(this));
console.log("pageSwitch"); }
}); });
} }
public ngOnDestroy() { public ngOnDestroy() {
this.selfUpdateCheckerTimer.unsubscribe(); this.selfUpdateCheckerTimer.unsubscribe();
this.reloadJiraIssueTimer.unsubscribe(); this.reloadJiraIssueTimer.unsubscribe();
this.pageSwitchTimer.unsubscribe(); if (this.pageSwitchTimer) {
// this.pageSwitchTimer.unsubscribe();
}
}
private switchSubscriber() {
let now = new Date();
let weekDay = now.getDay();
// on weekdays
if (weekDay > 0 && weekDay < 6) {
if (now.getHours() == 9 && now.getMinutes() >= 14 && this.autoSwitchEnabled) {
this.navigateToPage(PAGE_KANBAN);
this.autoSwitchEnabled = false;
}
if (now.getHours() == 9 && now.getMinutes() >= 29 && !this.autoSwitchEnabled) {
this.navigateToPage(PAGE_TSPINFO);
this.autoSwitchEnabled = true;
}
}
if (this.autoSwitchEnabled) {
let compareTimer: number = 0;
let switchTo: string = '';
switch (this.currentPage) {
case PAGE_KANBAN:
compareTimer = TIMESPENT_KANBAN;
switchTo = PAGE_TSPINFO;
break;
case PAGE_TSPINFO:
compareTimer = TIMESPENT_TSPINFO;
switchTo = PAGE_KANBAN;
break;
default:
console.error("Unknown page in pageSwitcherTimer");
return false;
}
if ((Date.now() - this.lastNavOccured) > compareTimer) {
this.navigateToPage(switchTo);
}
}
}
private navigateToPage(page: string) {
if (page != this.currentPage) {
this.router.navigate([page]);
this.currentPage = page;
this.lastNavOccured = Date.now();
}
} }
} }

View File

@ -0,0 +1,3 @@
.widget.camera {
padding: 0;
}

View File

@ -1,3 +1,4 @@
<p> <div class="widget camera">
animgif works! <img [src]="cameraUrls[0]">
</p> <img [src]="cameraUrls[1]">
</div>

View File

@ -1,21 +1,37 @@
import { Component, OnInit } from '@angular/core'; import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {InfoBoxComponent} from "../info-box/info-box.component"; import {InfoBoxComponent} from "../info-box/info-box.component";
import {AnimGif} from "../shared/anim-gif.model";
// import gifyParse from "gify-parse/gify-parse";
@Component({ @Component({
selector: 'app-animgif', selector: 'app-animgif',
templateUrl: './animgif.component.html', templateUrl: './animgif.component.html',
styleUrls: [ styleUrls: [
'../info-box/info-box.component.css', '../info-box/info-box.component.css',
'./animgif.component.css' './animgif.component.css'
] ]
}) })
export class AnimgifComponent extends InfoBoxComponent implements OnInit { export class AnimgifComponent extends InfoBoxComponent implements OnInit, OnDestroy {
constructor() { @Input() data: Array<AnimGif> = [];
super(); @Input() cameraUrls: Array<string> = [];
}
ngOnInit() { constructor() {
} super();
// gifyParse.getInfo();
}
ngOnInit() {
// @todo subscriber
}
ngOnDestroy() {
}
get imgHref(): string {
return this.cameraUrls[1];
}
} }

View File

@ -0,0 +1,16 @@
h2 {
text-transform: uppercase;
font-size: 76px;
font-weight: 700
}
h3 {
font-weight: 500;
font-size: 30px;
color: white;
}
p {
font-size: 15px;
color: rgba(255, 255, 255, 0.7);
}

View File

@ -1,4 +1,7 @@
<h1>{{title}}</h1> <div class="widget {{widgetClass}}">
<h2>{{data.all}}</h2> <h1>{{title}}</h1>
<h3>{{data.unassigned}}</h3> <h2>{{data.all}}</h2>
<p>unassigned</p> <h3>{{data.unassigned}}</h3>
<p>unassigned</p>
<i class="warning sign icon icon-background"></i>
</div>

View File

@ -20,4 +20,11 @@ export class ExpeditesComponent extends InfoBoxComponent implements OnInit {
ngOnInit() {} ngOnInit() {}
get widgetClass(): string {
return this.data.unassigned > 0
? 'critical'
: this.data.all > 0
? 'warn'
: 'ok';
}
} }

View File

@ -1,5 +1,107 @@
:host { :host {
display: inline-block; position: absolute;
padding: 1.25em; z-index: 2;
background-color: #0E566C; display: table;
width: 468px;
height: 525px;
background-color: #00285F;
color: rgba(255, 255, 255, 0.7);
font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 20px;
box-shadow: 5px 5px 10px #222;
}
:host-context(.sizex2) {
width: 946px;
}
:host-context(.sizey2) {
height: 1060px;
}
:host-context(.col1) {
left: 10px;
}
:host-context(.col2) {
left: 488px;
}
:host-context(.col3) {
left: 966px;
}
:host-context(.col4) {
left: 1444px;
}
:host-context(.row1) {
top: 10px;
}
:host-context(.row2) {
top: 545px;
}
h1 {
font-family: 'EricssonCapital', "Helvetica Neue", Helvetica, Arial, sans-serif;
margin-bottom: 12px;
text-align: center;
font-size: 30px;
font-weight: bold;
padding: 0;
}
ul {
list-style: none;
margin: 0 15px;
padding: 0;
overflow: hidden;
}
ul > li {
margin: 0 0 5px;
overflow: hidden;
}
ul > li > span.label {
float: left;
}
ul > li > span.value {
float: right;
}
.widget {
width: 100%;
height: 100%;
padding: 52px 12px;
display: table-cell;
vertical-align: middle;
text-align: center;
position: relative;
}
.icon-background {
width: 100% !important;
height: 100%;
position: absolute;
left: 0;
top: 0;
opacity: 0.1;
font-size: 300px;
text-align: center;
margin-top: 255px;
}
.ok {
background-color: rgb(137, 186, 23);
}
.warn {
background-color: #E95C38;
}
.critical {
background-color: rgb(227, 33, 25);
} }

View File

@ -1,3 +1 @@
<p> <p>info-box works!</p>
info-box works!
</p>

View File

@ -15,4 +15,7 @@ export class InfoBoxComponent implements OnInit {
ngOnInit() { ngOnInit() {
} }
get widgetClass(): string {
return '';
}
} }

View File

@ -1,17 +1,22 @@
<p> <p>
<app-tr-progress <app-tr-progress
title="OVERDUE PROGRESS INFOS (DAYS)" class="sizex2 col1 row1"
[data]="tspInfo.trProgressInfo"></app-tr-progress> title="TR INFO"
<app-tr-flow-errors [data]="{
title="TR FLOW ERRORS" topData: tspInfo.trProgressInfo,
[data]="tspInfo.trFlowErrors"></app-tr-flow-errors> bottomData: tspInfo.trFlowErrors
}"></app-tr-progress>
<app-pra-goals <app-pra-goals
title="PRA TR GOALS(A,B,C PRIO)" class="sizey2 col4 row1"
title="PRA TR GOALS (A,B,C PRIO)"
[data]="tspInfo.praGoals"></app-pra-goals> [data]="tspInfo.praGoals"></app-pra-goals>
<app-expedites <app-expedites
class="col3 row1"
title="NUMBER OF EXPEDITES (W/O PA)" title="NUMBER OF EXPEDITES (W/O PA)"
[data]="tspInfo.expedites"></app-expedites> [data]="tspInfo.expedites"></app-expedites>
<app-animgif <app-animgif
title="" class="col3 row2"
[data]="tspInfo.animGifs"></app-animgif> title="ANIMATED IMPORTANCE"
[data]="tspInfo.animGifs"
[cameraUrls]="tspInfo.cameraUrls"></app-animgif>
</p> </p>

View File

@ -0,0 +1,28 @@
p > span {
margin: 0 30px;
}
.header {
margin: 0 20px 0;
font-size: 20px;
font-weight: 200;
}
.smallnum {
font-size: 30px;
font-weight: 500;
}
.bignum {
text-transform: uppercase;
font-size: 76px;
font-weight: 700;
}
.numok {
color: rgb(137, 186, 23);
}
.numbad {
color: rgb(227, 33, 25);
}

View File

@ -1,24 +1,27 @@
<h1>{{title}}</h1> <div class="widget">
<p> <h1>{{title}}</h1>
<span class="heading">CORE</span> <p class="header">
<span class="heading">SIG</span> <span>CORE</span>
<span class="heading">TADE</span> <span>SIG</span>
</p> <span>TADE</span>
<p> </p>
<span class="heading">{{data.core.A}}</span> <p class="smallnum">
<span class="heading">{{data.sig.A}}</span> <span [class]="getClass(data.core.A)">{{data.core.A}}</span>
<span class="heading">{{data.tade.A}}</span> <span [class]="getClass(data.sig.A)">{{data.sig.A}}</span>
</p> <span [class]="getClass(data.tade.A)">{{data.tade.A}}</span>
<h2>{{sumA}}</h2> </p>
<p> <h2 class="bignum {{getClass(sumA)}}">{{sumA}}</h2>
<span class="heading">{{data.core.B}}</span> <p class="smallnum">
<span class="heading">{{data.sig.B}}</span> <span [class]="getClass(data.core.B)">{{data.core.B}}</span>
<span class="heading">{{data.tade.B}}</span> <span [class]="getClass(data.sig.B)">{{data.sig.B}}</span>
</p> <span [class]="getClass(data.tade.B)">{{data.tade.B}}</span>
<h2>{{sumB}}</h2> </p>
<p> <h2 class="bignum {{getClass(sumB)}}">{{sumB}}</h2>
<span class="heading">{{data.core.C}}</span> <p class="smallnum">
<span class="heading">{{data.sig.C}}</span> <span [class]="getClass(data.core.C)">{{data.core.C}}</span>
<span class="heading">{{data.tade.C}}</span> <span [class]="getClass(data.sig.C)">{{data.sig.C}}</span>
</p> <span [class]="getClass(data.tade.C)">{{data.tade.C}}</span>
<h2>{{sumC}}</h2> </p>
<h2 class="bignum {{getClass(sumC)}}">{{sumC}}</h2>
<i class="cocktail icon icon-background"></i>
</div>

View File

@ -40,4 +40,8 @@ export class PraGoalsComponent extends InfoBoxComponent implements OnInit {
public sum(prio: string) { public sum(prio: string) {
return ['core', 'sig', 'tade'].reduce((sum,unit) => sum + this.data[unit][prio], 0); return ['core', 'sig', 'tade'].reduce((sum,unit) => sum + this.data[unit][prio], 0);
} }
public getClass(value: number) {
return value < 1 ? 'numok' : 'numbad';
}
} }

View File

@ -1,7 +1,10 @@
<h1>{{title}} ({{errorSum}})</h1> <div class="widget {{widgetClass}}">
<ul> <h1>{{title}} ({{errorSum}})</h1>
<li *ngFor="let flowError of data"> <ul>
<span class="label">{{flowError.label}}</span> <li *ngFor="let flowError of data">
<span class="value">{{flowError.value}}</span> <span class="label">{{flowError.label}}</span>
</li> <span class="value">{{flowError.value}}</span>
</ul> </li>
</ul>
<i class="fire icon icon-background"></i>
</div>

View File

@ -24,4 +24,8 @@ export class TrFlowErrorsComponent extends InfoBoxComponent implements OnInit {
get errorSum(): number { get errorSum(): number {
return this.data.reduce((sum: number, flowError: TrFlowError) => sum + flowError.value, 0); return this.data.reduce((sum: number, flowError: TrFlowError) => sum + flowError.value, 0);
} }
get widgetClass(): string {
return this.data.length ? 'warn' : 'ok';
}
} }

View File

@ -0,0 +1,3 @@
.separator {
margin-top: 65px;
}

View File

@ -1,7 +1,17 @@
<h1>THE MOST OVERDUE PROGRESS INFOS (DAYS)</h1> <div class="widget {{widgetClass}}">
<ul> <h1>THE MOST OVERDUE PROGRESS INFOS (DAYS)</h1>
<li *ngFor="let progress of data"> <ul>
<span class="label">{{progress.eriref}} {{progress.heading}}</span> <li *ngFor="let progress of data.topData">
<span class="value">{{progress.lastProgressInDays}}</span> <span class="label">{{progress.eriref}} {{progress.heading}}</span>
</li> <span class="value">{{progress.lastProgressInDays}}</span>
</ul> </li>
</ul>
<h1 class="separator">TR FLOW ERRORS ({{errorSum}})</h1>
<ul>
<li *ngFor="let flowError of data.bottomData">
<span class="label">{{flowError.label}}</span>
<span class="value">{{flowError.value}}</span>
</li>
</ul>
<i class="warning sign icon icon-background"></i>
</div>

View File

@ -1,24 +1,38 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {InfoBoxComponent} from "../info-box/info-box.component"; import {InfoBoxComponent} from "../info-box/info-box.component";
import {TrProgress} from "../shared/tr-progress.model"; import {TrProgress} from "../shared/tr-progress.model";
import {TrFlowError} from "../shared/tr-flow-error.model";
@Component({ @Component({
selector: 'app-tr-progress', selector: 'app-tr-progress',
templateUrl: './tr-progress.component.html', templateUrl: './tr-progress.component.html',
styleUrls: [ styleUrls: [
'../info-box/info-box.component.css', '../info-box/info-box.component.css',
'./tr-progress.component.css' './tr-progress.component.css'
] ]
}) })
export class TrProgressComponent extends InfoBoxComponent implements OnInit { export class TrProgressComponent extends InfoBoxComponent implements OnInit {
@Input() data: Array<TrProgress> = []; @Input() data: {
topData: Array<TrProgress>,
bottomData: Array<TrFlowError>,
};
constructor() { constructor() {
super(); super();
} }
ngOnInit() { ngOnInit() {
} }
get widgetClass(): string {
return [
this.data.topData,
this.data.bottomData
].every(data => data.length > 0) ? 'critical' : 'ok';
}
get errorSum(): number {
return this.data.bottomData.reduce((sum: number, flowError: TrFlowError) => sum + flowError.value, 0);
}
} }

BIN
src/assets/font/impact.ttf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,57 +1,73 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@import "//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700";
@font-face {
font-family: 'EricssonCapital';
src: url("./assets/font/utm-ericsson-capital.ttf");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Impact';
src: url("./assets/font/impact.ttf");
font-weight: normal;
font-style: normal;
}
html, html,
body { body {
height: 100% !important; height: 100% !important;
overflow: hidden; overflow: hidden;
} }
body { body {
background-color: #303030 !important; background-color: #303030 !important;
color: #eeeeee !important; color: #eeeeee !important;
margin-top: 1em !important; margin-top: 1em !important;
margin-bottom: 1em !important; margin-bottom: 1em !important;
font-weight: bold !important; font-weight: bold !important;
} }
app-kanban-board { app-kanban-board {
overflow: hidden; overflow: hidden;
} }
.ui.fullwide-container { .ui.fullwide-container {
margin-left: 1em; margin-left: 1em;
margin-right: 1em; margin-right: 1em;
} }
.match-mhr { .match-mhr {
text-justify: none; text-justify: none;
color: mediumpurple; color: mediumpurple;
} }
.match-s { .match-s {
text-justify: none; text-justify: none;
color: #00b5ad; color: #00b5ad;
} }
.match-m { .match-m {
text-justify: none; text-justify: none;
color: #0ea432; color: #0ea432;
} }
.match-l { .match-l {
text-justify: none; text-justify: none;
color: #ffbf00; color: #ffbf00;
} }
.match-xl { .match-xl {
text-justify: none; text-justify: none;
color: coral; color: coral;
} }
.over-wip { .over-wip {
background-color: rgba(194,59,34, 0.3); background-color: rgba(194, 59, 34, 0.3);
} }
.prio-icon { .prio-icon {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }