* team customized labels added
* team editor interface minor redesign
This commit is contained in:
parent
0f535881a4
commit
ea05a47086
57
src/app/admin/team-editor/team-editor.component.html
Normal file → Executable file
57
src/app/admin/team-editor/team-editor.component.html
Normal file → Executable file
@ -11,22 +11,33 @@
|
|||||||
<input id="filter_id" type="number" name="filter_id" [(ngModel)]="team.filterId">
|
<input id="filter_id" type="number" name="filter_id" [(ngModel)]="team.filterId">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="ui header">1st column</h5>
|
<div class="six wide field">
|
||||||
|
<label for="team_name"> </label>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="team_is_active" name="team_is_active"
|
||||||
|
[(ngModel)]="team.isActive">
|
||||||
|
<label for="team_is_active">Active</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="ui dividing header">Column configuration</h4>
|
||||||
<div class="four fields">
|
<div class="four fields">
|
||||||
<div class="six wide field">
|
<div class="six wide field">
|
||||||
|
<label>Jira status</label>
|
||||||
<input type="text" name="column1_js"
|
<input type="text" name="column1_js"
|
||||||
placeholder="Jira column name" [(ngModel)]="team.backlogColumn.jiraStatusName">
|
placeholder="Jira column name" [(ngModel)]="team.backlogColumn.jiraStatusName">
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide field">
|
<div class="four wide field">
|
||||||
|
<label>Display name</label>
|
||||||
<input type="text" name="column1_l"
|
<input type="text" name="column1_l"
|
||||||
placeholder="Kanban board header" [(ngModel)]="team.backlogColumn.label">
|
placeholder="Kanban board header" [(ngModel)]="team.backlogColumn.label">
|
||||||
</div>
|
</div>
|
||||||
<div class="two wide field">
|
<div class="two wide field">
|
||||||
|
<label>WIP limit</label>
|
||||||
<input type="text" name="column1_wip"
|
<input type="text" name="column1_wip"
|
||||||
placeholder="WIP limit" [(ngModel)]="team.backlogColumn.wipLimit">
|
placeholder="WIP limit" [(ngModel)]="team.backlogColumn.wipLimit">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="ui header">2nd column</h5>
|
|
||||||
<div class="four fields">
|
<div class="four fields">
|
||||||
<div class="six wide field">
|
<div class="six wide field">
|
||||||
<input type="text" name="column2_js"
|
<input type="text" name="column2_js"
|
||||||
@ -41,7 +52,6 @@
|
|||||||
placeholder="WIP limit" [(ngModel)]="team.inprogressColumn.wipLimit">
|
placeholder="WIP limit" [(ngModel)]="team.inprogressColumn.wipLimit">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="ui header">3rd column</h5>
|
|
||||||
<div class="four fields">
|
<div class="four fields">
|
||||||
<div class="six wide field">
|
<div class="six wide field">
|
||||||
<input type="text" name="column3_js"
|
<input type="text" name="column3_js"
|
||||||
@ -56,7 +66,6 @@
|
|||||||
placeholder="WIP limit" [(ngModel)]="team.verificationColumn.wipLimit">
|
placeholder="WIP limit" [(ngModel)]="team.verificationColumn.wipLimit">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h5 class="ui header">4th column</h5>
|
|
||||||
<div class="four fields">
|
<div class="four fields">
|
||||||
<div class="six wide field">
|
<div class="six wide field">
|
||||||
<input type="text" name="column4_js"
|
<input type="text" name="column4_js"
|
||||||
@ -72,13 +81,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="six wide field">
|
<h4 class="ui dividing header">Labels</h4>
|
||||||
<label for="team_name"> </label>
|
<div class="three inline fields">
|
||||||
<div class="ui checkbox">
|
<div class="two wide field">
|
||||||
<input type="checkbox" id="team_is_active" name="team_is_active"
|
<button type="button" class="ui fluid button"
|
||||||
[(ngModel)]="team.isActive">
|
[class.positive]="canAddLabel"
|
||||||
<label for="team_is_active">Active</label>
|
[class.disabled]="!canAddLabel"
|
||||||
|
(keydown.enter)="handleEnter($event)"
|
||||||
|
(click)="addLabel()">Add
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="five wide field">
|
||||||
|
<input type="text" #labelInput
|
||||||
|
name="label_name"
|
||||||
|
placeholder="Label text"
|
||||||
|
(keydown.enter)="handleLabelEnter($event)"
|
||||||
|
[(ngModel)]="label.name">
|
||||||
|
</div>
|
||||||
|
<ng-template let-option #optionTemplate>
|
||||||
|
<span class="ui tiny {{option}} label">{{option}}</span>
|
||||||
|
</ng-template>
|
||||||
|
<sui-select class="ui right floated selection"
|
||||||
|
id="label_color"
|
||||||
|
name="label_color"
|
||||||
|
[(ngModel)]="label.color"
|
||||||
|
[optionTemplate]="optionTemplate"
|
||||||
|
[isSearchable]="false"
|
||||||
|
#labelSelect>
|
||||||
|
<sui-select-option *ngFor="let labelColor of labelColors" [value]="labelColor"></sui-select-option>
|
||||||
|
</sui-select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span *ngFor="let label of team.labels"
|
||||||
|
class="ui medium {{label.color}} label">{{label.name}}<i class="large delete icon"
|
||||||
|
(click)="removeLabel(label)"></i></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="ui dividing header">Team members</h4>
|
<h4 class="ui dividing header">Team members</h4>
|
||||||
@ -106,6 +142,7 @@
|
|||||||
[(ngModel)]="member.name">
|
[(ngModel)]="member.name">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="ui dividing header"></h4>
|
<h4 class="ui dividing header"></h4>
|
||||||
<table class="ui celled definition table" *ngIf="team.members.length">
|
<table class="ui celled definition table" *ngIf="team.members.length">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
59
src/app/admin/team-editor/team-editor.component.ts
Normal file → Executable file
59
src/app/admin/team-editor/team-editor.component.ts
Normal file → Executable file
@ -1,11 +1,11 @@
|
|||||||
import { Component, ElementRef, HostBinding, OnInit, ViewChild } from '@angular/core';
|
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { TeamService } from '../../shared/service/team.service';
|
import { TeamService } from '../../shared/service/team.service';
|
||||||
import { Team } from '../../shared/team';
|
import { Team } from '../../shared/team';
|
||||||
import { Member } from '../../shared/member';
|
import { Member } from '../../shared/member';
|
||||||
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
import { Label } from '../../shared/label';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-team-editor',
|
selector: 'app-team-editor',
|
||||||
@ -13,8 +13,10 @@ import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
|||||||
styleUrls: ['./team-editor.component.css']
|
styleUrls: ['./team-editor.component.css']
|
||||||
})
|
})
|
||||||
export class TeamEditorComponent implements OnInit {
|
export class TeamEditorComponent implements OnInit {
|
||||||
|
@ViewChild('labelInput') labelInputElement: ElementRef;
|
||||||
@ViewChild('signumInput') signumInputElement: ElementRef;
|
@ViewChild('signumInput') signumInputElement: ElementRef;
|
||||||
public team: Team;
|
public team: Team;
|
||||||
|
public label: Label = new Label();
|
||||||
public member: Member = new Member();
|
public member: Member = new Member();
|
||||||
|
|
||||||
constructor(private teamService: TeamService,
|
constructor(private teamService: TeamService,
|
||||||
@ -28,6 +30,57 @@ export class TeamEditorComponent implements OnInit {
|
|||||||
this.route.data.subscribe((data: { team: Team }) => this.team = data.team ? data.team : new Team());
|
this.route.data.subscribe((data: { team: Team }) => this.team = data.team ? data.team : new Team());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get labelColors(): Array<string> {
|
||||||
|
return [
|
||||||
|
'red',
|
||||||
|
'orange',
|
||||||
|
'yellow',
|
||||||
|
'olive',
|
||||||
|
'green',
|
||||||
|
'teal',
|
||||||
|
'blue',
|
||||||
|
'violet',
|
||||||
|
'purple',
|
||||||
|
'pink',
|
||||||
|
'brown',
|
||||||
|
'grey',
|
||||||
|
'black',
|
||||||
|
'white'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get canAddLabel(): boolean {
|
||||||
|
try {
|
||||||
|
return [this.label.name, this.label.color].every(field => field.length !== 0)
|
||||||
|
&& this.team.labels.every(label => label.name !== this.label.name);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public addLabel() {
|
||||||
|
this.team.labels = this.team.labels
|
||||||
|
.concat(Object.assign({}, this.label))
|
||||||
|
.sort((a: Label, b: Label) => a.name < b.name ? -1 : 1);
|
||||||
|
this.label = new Label();
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeLabel(label: Label) {
|
||||||
|
this.team.labels = this.team.labels.filter(teamLabel => teamLabel !== label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public handleLabelEnter(ev: KeyboardEvent) {
|
||||||
|
ev.preventDefault();
|
||||||
|
if (this.canAddLabel) {
|
||||||
|
this.addLabel();
|
||||||
|
this.focusLabelField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public focusLabelField() {
|
||||||
|
this.labelInputElement.nativeElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
get canAddMember(): boolean {
|
get canAddMember(): boolean {
|
||||||
try {
|
try {
|
||||||
return [this.member.name, this.member.signum].every(field => field.length !== 0)
|
return [this.member.name, this.member.signum].every(field => field.length !== 0)
|
||||||
@ -66,7 +119,7 @@ export class TeamEditorComponent implements OnInit {
|
|||||||
return [
|
return [
|
||||||
this.team.name.trim(),
|
this.team.name.trim(),
|
||||||
this.team.members
|
this.team.members
|
||||||
].every(field => field.length > 0);
|
].every(field => field.length > 0) && this.team.filterId > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveTeam() {
|
public saveTeam() {
|
||||||
|
|||||||
@ -12,10 +12,8 @@
|
|||||||
<div class="task-description">
|
<div class="task-description">
|
||||||
<span *ngIf="kanbanEntry.epicName"
|
<span *ngIf="kanbanEntry.epicName"
|
||||||
class="ui mini olive right floated label">{{kanbanEntry.epicName}}</span>
|
class="ui mini olive right floated label">{{kanbanEntry.epicName}}</span>
|
||||||
<ng-template [ngIf]="hasLabels(kanbanEntry)">
|
<span *ngFor="let label of filteredLabels(kanbanEntry)"
|
||||||
<span *ngFor="let label of kanbanEntry.labels"
|
class="ui mini {{labelClass(label)}} right floated label">{{label|blockedDays:kanbanEntry.daysBlocked}}</span>
|
||||||
class="ui mini {{labelClass(label)}} right floated label">{{label|uppercase|blockedDays:kanbanEntry.daysBlocked}}</span>
|
|
||||||
</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 *ngIf="!hasMultiAssignee(kanbanEntry)" 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">
|
||||||
|
|||||||
@ -2,23 +2,11 @@ import { Component, Input } from '@angular/core';
|
|||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { JiraAssignee, KanbanEntry } from '../shared';
|
import { JiraAssignee, KanbanEntry } from '../shared';
|
||||||
|
import { SettingsService } from '../../shared/service/settings.service';
|
||||||
|
|
||||||
const DEFAULT_AVATAR = '/assets/riddler.png';
|
const DEFAULT_AVATAR = '/assets/riddler.png';
|
||||||
const JIRA_BOARD_BASE_HREF = 'https://cc-jira.rnd.ki.sw.ericsson.se/browse/';
|
const JIRA_BOARD_BASE_HREF = 'https://cc-jira.rnd.ki.sw.ericsson.se/browse/';
|
||||||
|
|
||||||
const labelColors = {
|
|
||||||
TSP: 'teal',
|
|
||||||
MTAS: 'orange',
|
|
||||||
INTERNAL: 'yellow',
|
|
||||||
TEAM: 'yellow',
|
|
||||||
BLOCKED: 'red',
|
|
||||||
SPIKE: 'purple',
|
|
||||||
EXPEDITE: 'pink',
|
|
||||||
|
|
||||||
'MTAS-GUARDIAN': 'pink',
|
|
||||||
'MTAS-GUARDIANACTIVE': 'yellow',
|
|
||||||
};
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-kanban-entry-item,[app-kanban-entry-item]',
|
selector: 'app-kanban-entry-item,[app-kanban-entry-item]',
|
||||||
templateUrl: './kanban-entry-item.component.html',
|
templateUrl: './kanban-entry-item.component.html',
|
||||||
@ -30,8 +18,7 @@ export class KanbanEntryItemComponent {
|
|||||||
@Input() wipLimit = 0;
|
@Input() wipLimit = 0;
|
||||||
@Input() wipCount = 0;
|
@Input() wipCount = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor(private settingService: SettingsService) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the full url of the assignee avatar,
|
* Returns the full url of the assignee avatar,
|
||||||
@ -41,17 +28,9 @@ export class KanbanEntryItemComponent {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
public avatarUrl(avatarPath: string): string {
|
public avatarUrl(avatarPath: string): string {
|
||||||
return environment.apiUrl + (avatarPath ? avatarPath : DEFAULT_AVATAR);
|
return environment.apiUrl + (avatarPath
|
||||||
}
|
? avatarPath
|
||||||
|
: DEFAULT_AVATAR);
|
||||||
/**
|
|
||||||
* Returns true if issue has any labels attached
|
|
||||||
*
|
|
||||||
* @param {KanbanEntry} entry
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
public hasLabels(entry: KanbanEntry): boolean {
|
|
||||||
return entry.labels.length > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,11 +40,16 @@ export class KanbanEntryItemComponent {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
public labelClass(label: string): string {
|
public labelClass(label: string): string {
|
||||||
try {
|
if (this.settingService.team.labels) {
|
||||||
return labelColors[label.toUpperCase()];
|
const color = this.settingService.team.labels.find(
|
||||||
} catch (e) {
|
teamLabel => teamLabel.name.toLocaleLowerCase() === label.toLocaleLowerCase()
|
||||||
return 'white';
|
).color;
|
||||||
|
|
||||||
|
if (color !== null) {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return 'white';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,6 +64,15 @@ export class KanbanEntryItemComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public filteredLabels(kanbanEntry: KanbanEntry): Array<string> {
|
||||||
|
if (this.settingService.team.labels) {
|
||||||
|
return kanbanEntry.labels.filter(entryLabel => this.settingService.team.labels.some(
|
||||||
|
teamLabel => teamLabel.name.toLocaleLowerCase() === entryLabel.toLocaleLowerCase()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate jira issue href
|
* Generate jira issue href
|
||||||
*
|
*
|
||||||
@ -122,4 +115,5 @@ export class KanbanEntryItemComponent {
|
|||||||
public getAssignees(kanbanEntry: KanbanEntry): Array<JiraAssignee> {
|
public getAssignees(kanbanEntry: KanbanEntry): Array<JiraAssignee> {
|
||||||
return [].concat([kanbanEntry.assignee], kanbanEntry.additionalAssignees);
|
return [].concat([kanbanEntry.assignee], kanbanEntry.additionalAssignees);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
:host.preview {
|
:host.preview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
padding: 30px;
|
||||||
}
|
}
|
||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import { Slide } from '../shared/slide';
|
import {Slide, SlideVisibility} from '../shared/slide';
|
||||||
import { SlideService } from '../shared/service/slide.service';
|
import {SlideService} from '../shared/service/slide.service';
|
||||||
import { Router } from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import { SettingsService } from '../shared/service/settings.service';
|
import {SettingsService} from '../shared/service/settings.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SlideShowService {
|
export class SlideShowService {
|
||||||
@ -39,7 +39,7 @@ export class SlideShowService {
|
|||||||
const team = this.settingsService.team;
|
const team = this.settingsService.team;
|
||||||
this.slideService.list().subscribe(
|
this.slideService.list().subscribe(
|
||||||
slides => this.slides = slides.filter(
|
slides => this.slides = slides.filter(
|
||||||
slide => slide.teams === null || slide.teams.some(s => s.id === team.id) && slide.isVisible
|
slide => slide.isVisible && (slide.visibility === SlideVisibility.Public || slide.teams.some(s => s.id === team.id))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/app/shared/label.ts
Executable file
4
src/app/shared/label.ts
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
export class Label {
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
6
src/app/shared/team.ts
Normal file → Executable file
6
src/app/shared/team.ts
Normal file → Executable file
@ -1,16 +1,18 @@
|
|||||||
import { Member } from './member';
|
import { Member } from './member';
|
||||||
import { KanbanColumn } from './kanban-column';
|
import { KanbanColumn } from './kanban-column';
|
||||||
|
import { Label } from './label';
|
||||||
|
|
||||||
export class Team {
|
export class Team {
|
||||||
id: number = null;
|
id: number = null;
|
||||||
name: String = '';
|
name: String = '';
|
||||||
members: Array<Member> = [];
|
members: Array<Member> = [];
|
||||||
filterId: number;
|
filterId = 0;
|
||||||
backlogColumn: KanbanColumn = new KanbanColumn();
|
backlogColumn: KanbanColumn = new KanbanColumn();
|
||||||
inprogressColumn: KanbanColumn = new KanbanColumn();
|
inprogressColumn: KanbanColumn = new KanbanColumn();
|
||||||
verificationColumn: KanbanColumn = new KanbanColumn();
|
verificationColumn: KanbanColumn = new KanbanColumn();
|
||||||
doneColumn: KanbanColumn = new KanbanColumn();
|
doneColumn: KanbanColumn = new KanbanColumn();
|
||||||
isActive = false;
|
labels: Array<Label> = [];
|
||||||
|
isActive = true;
|
||||||
createdAt: String = null;
|
createdAt: String = null;
|
||||||
updatedAt: String = null;
|
updatedAt: String = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user