* 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": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
@ -5560,6 +5569,11 @@
"integrity": "sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI=",
"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": {
"version": "3.2.1",
"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/router": "^4.0.0",
"core-js": "^2.4.1",
"gify-parse": "^1.0.6",
"rxjs": "^5.4.1",
"semantic-ui": "^2.2.11",
"zone.js": "^0.8.14"

View File

@ -3,13 +3,23 @@ import {Subscription} from "rxjs/Subscription";
import {TimerObservable} from "rxjs/observable/TimerObservable";
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";
const TIMER_DEPLOY_REFRESH = 30000;
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({
selector: 'app-root',
@ -17,23 +27,25 @@ const TIMER_PAGE_SWITCH = 300000;
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
private currentPage: string = PAGE_KANBAN;
private lastNavOccured: number = Date.now();
private autoSwitchEnabled: boolean = true;
selfUpdateCheckerTimer: Subscription;
reloadJiraIssueTimer: Subscription;
pageSwitchTimer: Subscription;
private selfUpdateCheckerTimer: Subscription;
private reloadJiraIssueTimer: Subscription;
private pageSwitchTimer: Subscription;
constructor(
private selfUpdaterService: SelfUpdaterService,
private kanbanService: KanbanService,
private router: Router
) {}
constructor(private selfUpdaterService: SelfUpdaterService,
private kanbanService: KanbanService,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
/**
* Initialize application timers:
* - 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
* - pageSwitchTimer handles switching back and forth between page views
* @todo: pageSwitchTimer
*/
public ngOnInit() {
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.kanbanService.reload();
});
let timer2 = TimerObservable.create(TIMER_PAGE_SWITCH, TIMER_PAGE_SWITCH);
this.pageSwitchTimer = timer2.subscribe(() => {
// navigate to next page
// this.router.navigate();
console.log("pageSwitch");
this.activatedRoute.queryParamMap.subscribe(params => {
if (!params.has("disableAutoSwitch")) {
let timer2 = TimerObservable.create(TIMER_PAGE_SWITCH_TICK, TIMER_PAGE_SWITCH_TICK);
// this.pageSwitchTimer = timer2.subscribe(this.switchSubscriber.bind(this));
}
});
}
public ngOnDestroy() {
this.selfUpdateCheckerTimer.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>
animgif works!
</p>
<div class="widget camera">
<img [src]="cameraUrls[0]">
<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 {AnimGif} from "../shared/anim-gif.model";
// import gifyParse from "gify-parse/gify-parse";
@Component({
selector: 'app-animgif',
templateUrl: './animgif.component.html',
styleUrls: [
'../info-box/info-box.component.css',
'./animgif.component.css'
]
selector: 'app-animgif',
templateUrl: './animgif.component.html',
styleUrls: [
'../info-box/info-box.component.css',
'./animgif.component.css'
]
})
export class AnimgifComponent extends InfoBoxComponent implements OnInit {
export class AnimgifComponent extends InfoBoxComponent implements OnInit, OnDestroy {
constructor() {
super();
}
@Input() data: Array<AnimGif> = [];
@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>
<h2>{{data.all}}</h2>
<h3>{{data.unassigned}}</h3>
<p>unassigned</p>
<div class="widget {{widgetClass}}">
<h1>{{title}}</h1>
<h2>{{data.all}}</h2>
<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() {}
get widgetClass(): string {
return this.data.unassigned > 0
? 'critical'
: this.data.all > 0
? 'warn'
: 'ok';
}
}

View File

@ -1,5 +1,107 @@
:host {
display: inline-block;
padding: 1.25em;
background-color: #0E566C;
position: absolute;
z-index: 2;
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>
info-box works!
</p>
<p>info-box works!</p>

View File

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

View File

@ -1,17 +1,22 @@
<p>
<app-tr-progress
title="OVERDUE PROGRESS INFOS (DAYS)"
[data]="tspInfo.trProgressInfo"></app-tr-progress>
<app-tr-flow-errors
title="TR FLOW ERRORS"
[data]="tspInfo.trFlowErrors"></app-tr-flow-errors>
class="sizex2 col1 row1"
title="TR INFO"
[data]="{
topData: tspInfo.trProgressInfo,
bottomData: tspInfo.trFlowErrors
}"></app-tr-progress>
<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>
<app-expedites
class="col3 row1"
title="NUMBER OF EXPEDITES (W/O PA)"
[data]="tspInfo.expedites"></app-expedites>
<app-animgif
title=""
[data]="tspInfo.animGifs"></app-animgif>
class="col3 row2"
title="ANIMATED IMPORTANCE"
[data]="tspInfo.animGifs"
[cameraUrls]="tspInfo.cameraUrls"></app-animgif>
</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>
<p>
<span class="heading">CORE</span>
<span class="heading">SIG</span>
<span class="heading">TADE</span>
</p>
<p>
<span class="heading">{{data.core.A}}</span>
<span class="heading">{{data.sig.A}}</span>
<span class="heading">{{data.tade.A}}</span>
</p>
<h2>{{sumA}}</h2>
<p>
<span class="heading">{{data.core.B}}</span>
<span class="heading">{{data.sig.B}}</span>
<span class="heading">{{data.tade.B}}</span>
</p>
<h2>{{sumB}}</h2>
<p>
<span class="heading">{{data.core.C}}</span>
<span class="heading">{{data.sig.C}}</span>
<span class="heading">{{data.tade.C}}</span>
</p>
<h2>{{sumC}}</h2>
<div class="widget">
<h1>{{title}}</h1>
<p class="header">
<span>CORE</span>
<span>SIG</span>
<span>TADE</span>
</p>
<p class="smallnum">
<span [class]="getClass(data.core.A)">{{data.core.A}}</span>
<span [class]="getClass(data.sig.A)">{{data.sig.A}}</span>
<span [class]="getClass(data.tade.A)">{{data.tade.A}}</span>
</p>
<h2 class="bignum {{getClass(sumA)}}">{{sumA}}</h2>
<p class="smallnum">
<span [class]="getClass(data.core.B)">{{data.core.B}}</span>
<span [class]="getClass(data.sig.B)">{{data.sig.B}}</span>
<span [class]="getClass(data.tade.B)">{{data.tade.B}}</span>
</p>
<h2 class="bignum {{getClass(sumB)}}">{{sumB}}</h2>
<p class="smallnum">
<span [class]="getClass(data.core.C)">{{data.core.C}}</span>
<span [class]="getClass(data.sig.C)">{{data.sig.C}}</span>
<span [class]="getClass(data.tade.C)">{{data.tade.C}}</span>
</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) {
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>
<ul>
<li *ngFor="let flowError of data">
<span class="label">{{flowError.label}}</span>
<span class="value">{{flowError.value}}</span>
</li>
</ul>
<div class="widget {{widgetClass}}">
<h1>{{title}} ({{errorSum}})</h1>
<ul>
<li *ngFor="let flowError of data">
<span class="label">{{flowError.label}}</span>
<span class="value">{{flowError.value}}</span>
</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 {
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>
<ul>
<li *ngFor="let progress of data">
<span class="label">{{progress.eriref}} {{progress.heading}}</span>
<span class="value">{{progress.lastProgressInDays}}</span>
</li>
</ul>
<div class="widget {{widgetClass}}">
<h1>THE MOST OVERDUE PROGRESS INFOS (DAYS)</h1>
<ul>
<li *ngFor="let progress of data.topData">
<span class="label">{{progress.eriref}} {{progress.heading}}</span>
<span class="value">{{progress.lastProgressInDays}}</span>
</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 {InfoBoxComponent} from "../info-box/info-box.component";
import {TrProgress} from "../shared/tr-progress.model";
import {TrFlowError} from "../shared/tr-flow-error.model";
@Component({
selector: 'app-tr-progress',
templateUrl: './tr-progress.component.html',
styleUrls: [
'../info-box/info-box.component.css',
'./tr-progress.component.css'
]
selector: 'app-tr-progress',
templateUrl: './tr-progress.component.html',
styleUrls: [
'../info-box/info-box.component.css',
'./tr-progress.component.css'
]
})
export class TrProgressComponent extends InfoBoxComponent implements OnInit {
@Input() data: Array<TrProgress> = [];
@Input() data: {
topData: Array<TrProgress>,
bottomData: Array<TrFlowError>,
};
constructor() {
super();
}
constructor() {
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 */
@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,
body {
height: 100% !important;
overflow: hidden;
height: 100% !important;
overflow: hidden;
}
body {
background-color: #303030 !important;
color: #eeeeee !important;
margin-top: 1em !important;
margin-bottom: 1em !important;
font-weight: bold !important;
background-color: #303030 !important;
color: #eeeeee !important;
margin-top: 1em !important;
margin-bottom: 1em !important;
font-weight: bold !important;
}
app-kanban-board {
overflow: hidden;
overflow: hidden;
}
.ui.fullwide-container {
margin-left: 1em;
margin-right: 1em;
margin-left: 1em;
margin-right: 1em;
}
.match-mhr {
text-justify: none;
color: mediumpurple;
text-justify: none;
color: mediumpurple;
}
.match-s {
text-justify: none;
color: #00b5ad;
text-justify: none;
color: #00b5ad;
}
.match-m {
text-justify: none;
color: #0ea432;
text-justify: none;
color: #0ea432;
}
.match-l {
text-justify: none;
color: #ffbf00;
text-justify: none;
color: #ffbf00;
}
.match-xl {
text-justify: none;
color: coral;
text-justify: none;
color: coral;
}
.over-wip {
background-color: rgba(194,59,34, 0.3);
background-color: rgba(194, 59, 34, 0.3);
}
.prio-icon {
display: inline-block;
vertical-align: middle;
display: inline-block;
vertical-align: middle;
}