* tsp board disabled

* multi assignee
This commit is contained in:
Dávid Danyi 2017-11-13 13:54:39 +01:00
parent cc8a3ddd03
commit 82184678b9
6 changed files with 161 additions and 91 deletions

View File

@ -3,25 +3,25 @@ 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 {ActivatedRoute, NavigationEnd, Router} from "@angular/router"; // import {ActivatedRoute, NavigationEnd, Router} from "@angular/router";
import {SelfUpdaterService} from "./kanban/shared/self-updater.service"; import {SelfUpdaterService} from "./kanban/shared/self-updater.service";
import {TspInfoService} from "./tsp-info/shared/tsp-info.service"; // import {TspInfoService} from "./tsp-info/shared/tsp-info.service";
const TIMER_DEPLOY_REFRESH = 30000; const TIMER_DEPLOY_REFRESH = 30000;
const TIMER_JIRA_REFRESH = 60000; const TIMER_JIRA_REFRESH = 60000;
const TIMER_TSPINFO_REFRESH = 60000; // const TIMER_TSPINFO_REFRESH = 60000;
const TIMER_PAGE_SWITCH_TICK = 1000; // const TIMER_PAGE_SWITCH_TICK = 1000;
/** /**
* Page switch timer in seconds * Page switch timer in seconds
* @type {number} * @type {number}
*/ */
const TIMESPENT_KANBAN = 120000; // const TIMESPENT_KANBAN = 120000;
const TIMESPENT_TSPINFO = 30000; // const TIMESPENT_TSPINFO = 30000;
const PAGE_KANBAN = '/kanban'; // const PAGE_KANBAN = '/kanban';
const PAGE_TSPINFO = '/tspinfopage'; // const PAGE_TSPINFO = '/tspinfopage';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -29,21 +29,21 @@ const PAGE_TSPINFO = '/tspinfopage';
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 currentPage: string = PAGE_KANBAN;
private lastNavOccured: number = Date.now(); // private lastNavOccured: number = Date.now();
private autoSwitchEnabled: boolean = true; // private autoSwitchEnabled: boolean = true;
private selfUpdateCheckerTimer: Subscription; private selfUpdateCheckerTimer: Subscription;
private reloadKanbanTimer: Subscription; private reloadKanbanTimer: Subscription;
private reloadTspInfoTimer: Subscription; // private reloadTspInfoTimer: Subscription;
private pageSwitchTimer: Subscription; // private pageSwitchTimer: Subscription;
constructor(private selfUpdaterService: SelfUpdaterService, constructor(private selfUpdaterService: SelfUpdaterService,
private kanbanService: KanbanService, private kanbanService: KanbanService,
private tspInfoService: TspInfoService, // private tspInfoService: TspInfoService,
private router: Router, // private router: Router,
private activatedRoute: ActivatedRoute) { // private activatedRoute: ActivatedRoute
} ) {}
/** /**
* Initialize application timers: * Initialize application timers:
@ -60,81 +60,81 @@ export class AppComponent implements OnInit, OnDestroy {
this.reloadKanbanTimer = timer1.subscribe(() => { this.reloadKanbanTimer = timer1.subscribe(() => {
this.kanbanService.reload(); this.kanbanService.reload();
}); });
let timer2 = TimerObservable.create(TIMER_TSPINFO_REFRESH, TIMER_TSPINFO_REFRESH); // let timer2 = TimerObservable.create(TIMER_TSPINFO_REFRESH, TIMER_TSPINFO_REFRESH);
this.reloadTspInfoTimer = timer2.subscribe(() => { // this.reloadTspInfoTimer = timer2.subscribe(() => {
this.tspInfoService.reload(); // this.tspInfoService.reload();
}); // });
this.router.events // this.router.events
.filter(e => e instanceof NavigationEnd) // .filter(e => e instanceof NavigationEnd)
.map(() => { // .map(() => {
let route = this.activatedRoute; // let route = this.activatedRoute;
while (route.firstChild) { // while (route.firstChild) {
route = route.firstChild; // route = route.firstChild;
} // }
return route; // return route;
}) // })
.filter((route) => route.outlet === 'primary') // .filter((route) => route.outlet === 'primary')
.mergeMap((route) => route.data) // .mergeMap((route) => route.data)
.subscribe(data => { // .subscribe(data => {
if (!data['disableAutoSwitch']) { // if (!data['disableAutoSwitch']) {
let timer4 = TimerObservable.create(TIMER_PAGE_SWITCH_TICK, TIMER_PAGE_SWITCH_TICK); // let timer4 = TimerObservable.create(TIMER_PAGE_SWITCH_TICK, TIMER_PAGE_SWITCH_TICK);
this.pageSwitchTimer = timer4.subscribe(this.switchSubscriber.bind(this)); // this.pageSwitchTimer = timer4.subscribe(this.switchSubscriber.bind(this));
} // }
}); // });
} }
public ngOnDestroy() { public ngOnDestroy() {
this.selfUpdateCheckerTimer.unsubscribe(); this.selfUpdateCheckerTimer.unsubscribe();
this.reloadKanbanTimer.unsubscribe(); this.reloadKanbanTimer.unsubscribe();
this.reloadTspInfoTimer.unsubscribe(); // this.reloadTspInfoTimer.unsubscribe();
if (this.pageSwitchTimer) { // if (this.pageSwitchTimer) {
this.pageSwitchTimer.unsubscribe(); // this.pageSwitchTimer.unsubscribe();
} // }
} }
private switchSubscriber() { // private switchSubscriber() {
let now = new Date(); // let now = new Date();
let weekDay = now.getDay(); // let weekDay = now.getDay();
// on weekdays // // on weekdays
if (weekDay > 0 && weekDay < 6) { // if (weekDay > 0 && weekDay < 6) {
if (now.getHours() == 10) { // if (now.getHours() == 10) {
if (this.autoSwitchEnabled && now.getMinutes() > 14) { // if (this.autoSwitchEnabled && now.getMinutes() > 14) {
this.navigateToPage(PAGE_KANBAN); // this.navigateToPage(PAGE_KANBAN);
this.autoSwitchEnabled = false; // this.autoSwitchEnabled = false;
} // }
if (!this.autoSwitchEnabled && now.getMinutes() > 30) { // if (!this.autoSwitchEnabled && now.getMinutes() > 30) {
this.autoSwitchEnabled = true; // this.autoSwitchEnabled = true;
} // }
} // }
} // }
if (this.autoSwitchEnabled) { // if (this.autoSwitchEnabled) {
let compareTimer: number = 0; // let compareTimer: number = 0;
let switchTo: string = ''; // let switchTo: string = '';
switch (this.currentPage) { // switch (this.currentPage) {
case PAGE_KANBAN: // case PAGE_KANBAN:
compareTimer = TIMESPENT_KANBAN; // compareTimer = TIMESPENT_KANBAN;
switchTo = PAGE_TSPINFO; // switchTo = PAGE_TSPINFO;
break; // break;
case PAGE_TSPINFO: // case PAGE_TSPINFO:
compareTimer = TIMESPENT_TSPINFO; // compareTimer = TIMESPENT_TSPINFO;
switchTo = PAGE_KANBAN; // switchTo = PAGE_KANBAN;
break; // break;
default: // default:
console.error("Unknown page in pageSwitcherTimer"); // console.error("Unknown page in pageSwitcherTimer");
return false; // return false;
} // }
if ((Date.now() - this.lastNavOccured) > compareTimer) { // if ((Date.now() - this.lastNavOccured) > compareTimer) {
this.navigateToPage(switchTo); // this.navigateToPage(switchTo);
} // }
} // }
} // }
//
private navigateToPage(page: string) { // private navigateToPage(page: string) {
if (page != this.currentPage) { // if (page != this.currentPage) {
this.router.navigate([page]); // this.router.navigate([page]);
this.currentPage = page; // this.currentPage = page;
this.lastNavOccured = Date.now(); // this.lastNavOccured = Date.now();
} // }
} // }
} }

View File

@ -57,3 +57,43 @@ a:hover {
/*.ui.divided.items > .item.trivial.bottom-separator {*/ /*.ui.divided.items > .item.trivial.bottom-separator {*/
/*border-bottom: 1px solid rgba(181, 204, 24, 0.5);*/ /*border-bottom: 1px solid rgba(181, 204, 24, 0.5);*/
/*}*/ /*}*/
.ui.images {
width: 45px;
height: 45px;
margin-left: 0px;
margin-right: 4px;
margin-bottom: 0;
border-radius: 4px;
overflow: hidden;
}
.ui.images .image {
margin: 0;
width: 45px;
height: 45px;
}
/* 2 images */
.ui.two.images .image {
width: 22px;
object-fit: cover;
}
/* 3 images */
.ui.three.images .image {
height: 22px;
}
.ui.three.images .image:first-child {
object-fit: cover;
}
.ui.three.images .image:not(:first-child) {
width: 22px;
margin-top: -14px;
}
/* 4 images */
.ui.four.images .image {
height: 22px;
width: 22px;
}

View File

@ -5,17 +5,27 @@
</ng-template> </ng-template>
</h1> </h1>
<div class="ui divided items"> <div class="ui divided items">
<div class="item {{kanbanEntry.issuePriority|lowercase}}" [ngClass]="entryClass(kanbanEntry)" *ngFor="let kanbanEntry of kanbanEntries"> <div *ngFor="let kanbanEntry of kanbanEntries"
class="item {{kanbanEntry.issuePriority|lowercase}}"
[ngClass]="entryClass(kanbanEntry)">
<div class="content"> <div class="content">
<div class="task-description"> <div class="task-description">
<ng-template [ngIf]="hasLabels(kanbanEntry)"> <ng-template [ngIf]="hasLabels(kanbanEntry)">
<span *ngFor="let label of kanbanEntry.labels" class="ui mini {{labelClass(label)}} right floated label">{{label|uppercase|blockedDays:kanbanEntry.daysBlocked}}</span> <span *ngFor="let label of kanbanEntry.labels"
class="ui mini {{labelClass(label)}} right floated label">{{label|uppercase|blockedDays:kanbanEntry.daysBlocked}}</span>
</ng-template> </ng-template>
<span *ngIf="wasBlocked(kanbanEntry)" class="ui mini {{labelClass('blocked')}} right floated label">{{kanbanEntry.daysBlocked}}D</span> <span *ngIf="wasBlocked(kanbanEntry)" class="ui mini {{labelClass('blocked')}} right floated label">{{kanbanEntry.daysBlocked}}D</span>
<div class="ui jira-avatar floated image"> <div *ngIf="!hasMultiAssignee(kanbanEntry)" class="ui jira-avatar floated image">
<img src="{{avatarUrl(kanbanEntry.assignee?.avatar)}}" [title]="kanbanEntry.assignee?.name"> <img src="{{avatarUrl(kanbanEntry.assignee?.avatar)}}" [title]="kanbanEntry.assignee?.name">
</div> </div>
<a [href]="jiraHref(kanbanEntry)" target="_blank" [innerHTML]="kanbanEntry.summary|shortenText|priorityColor:kanbanEntry.issuePriorityIcon:kanbanEntry.worklog" [title]="kanbanEntry.summary"></a> <div *ngIf="hasMultiAssignee(kanbanEntry)" class="ui jira-avatar floated {{assigneeCount(kanbanEntry)}} images">
<img *ngFor="let assignee of getAssignees(kanbanEntry)" class="image"
src="{{avatarUrl(assignee?.avatar)}}"
[title]="assignee?.name">
</div>
<a [href]="jiraHref(kanbanEntry)" target="_blank"
[innerHTML]="kanbanEntry.summary|shortenText|priorityColor:kanbanEntry.issuePriorityIcon:kanbanEntry.worklog"
[title]="kanbanEntry.summary"></a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import {Component, Input} from '@angular/core';
import {environment} from "../../../environments/environment"; import {environment} from "../../../environments/environment";
import {KanbanEntry} from "../shared"; import {KanbanEntry} from "../shared";
import { JiraAssignee } from "../shared/jira-assignee.model";
const DEFAULT_AVATAR = '/assets/riddler.png'; const DEFAULT_AVATAR = '/assets/riddler.png';
const JIRA_BOARD_BASE_HREF = 'https://jirapducc.mo.ca.am.ericsson.se/browse/'; const JIRA_BOARD_BASE_HREF = 'https://jirapducc.mo.ca.am.ericsson.se/browse/';
@ -97,4 +98,22 @@ export class KanbanEntryItemComponent {
return kanbanEntry.daysBlocked > 0 return kanbanEntry.daysBlocked > 0
&& kanbanEntry.labels.every(label => label.toUpperCase() != 'BLOCKED'); && kanbanEntry.labels.every(label => label.toUpperCase() != 'BLOCKED');
} }
public hasMultiAssignee(kanbanEntry: KanbanEntry): boolean {
return kanbanEntry.additionalAssignees.length > 0;
}
public assigneeCount(kanbanEntry: KanbanEntry): string {
let count = 1 + kanbanEntry.additionalAssignees.length;
switch (count) {
case 2: return "two";
case 3: return "three";
case 4:
default: return "four";
}
}
public getAssignees(kanbanEntry: KanbanEntry): Array<JiraAssignee> {
return [].concat([kanbanEntry.assignee], kanbanEntry.additionalAssignees);
}
} }

View File

@ -9,6 +9,7 @@ export class KanbanEntry {
public issueType: JiraIssueType; public issueType: JiraIssueType;
public status: JiraStatus; public status: JiraStatus;
public assignee: JiraAssignee; public assignee: JiraAssignee;
public additionalAssignees: Array<JiraAssignee> = [];
public issuePriority: string; public issuePriority: string;
public issuePriorityIcon: string; public issuePriorityIcon: string;
public labels: Array<string>; public labels: Array<string>;

View File

@ -5,5 +5,5 @@
export const environment = { export const environment = {
production: false, production: false,
apiUri: "http://localhost:8080", apiUri: "http://localhost:8888",
}; };