Compare commits

...

15 Commits

Author SHA1 Message Date
Dávid Danyi
e346f84eeb * code cleanup 2018-08-06 18:02:24 +02:00
Dávid Danyi
5f2639061b * removed funservice
* prio color pipe fixed with svg sizes
* prefix pipe added
2018-04-17 11:51:33 +02:00
Dávid Danyi
41c057c5e5 * prio color divider removed 2017-11-29 17:18:41 +01:00
Dávid Danyi
04aa27bd2e * show wip limit even if there is zero items in a row 2017-11-17 11:22:26 +01:00
Dávid Danyi
82184678b9 * tsp board disabled
* multi assignee
2017-11-13 13:54:39 +01:00
Dávid Danyi
cc8a3ddd03 * fixed forced tspinfo between 10:30 and 11:00 2017-09-20 11:04:12 +02:00
Dávid Danyi
f4ce7ec951 * favicon changed to miniature team logo 2017-09-19 17:54:32 +02:00
Dávid Danyi
9f1d5d4d9a * added kanban-fixed route 2017-09-14 12:46:55 +02:00
Dávid Danyi
0d628456c4 * sanitize the timers a bit 2017-09-08 18:28:42 +02:00
Dávid Danyi
6494c5c75c * funService created to share animgifs and cameraurl between the two modules
* funService is provider for both modules
* animgifs are still TBD, camera is visible in kanbanview between 11:30-12:30
2017-09-08 17:54:50 +02:00
Dávid Danyi
d37a6f68c6 * route name change, there seems to be a bug involving dashes in the route name 2017-09-08 15:42:27 +02:00
Dávid Danyi
97d946a324 * missing kanban routing module... 2017-09-08 10:48:56 +02:00
Dávid Danyi
59b463c3b2 * kanban routing moved to kanban module, tsp-info routing remained in its own place 2017-09-08 10:48:12 +02:00
Dávid Danyi
985383797e * widget layout implemented 2017-09-08 09:43:50 +02:00
Dávid Danyi
7e82c57b0e * TspInfo module added 2017-09-06 18:53:31 +02:00
24 changed files with 229 additions and 110 deletions

View File

@ -9,7 +9,7 @@
"outDir": "dist", "outDir": "dist",
"assets": [ "assets": [
"assets", "assets",
"favicon.ico" "favicon.png"
], ],
"index": "index.html", "index": "index.html",
"main": "main.ts", "main": "main.ts",

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

@ -1,21 +1,11 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router'; import {Routes, RouterModule} from '@angular/router';
import { KanbanService } from "./kanban/shared"
import { KanbanBoardComponent } from "./kanban/kanban-board/kanban-board.component"
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
redirectTo: '/kanban', redirectTo: '/kanban-fixed',
pathMatch: 'full', pathMatch: 'full',
},{
path: 'kanban',
children: [],
component: KanbanBoardComponent,
resolve: {
kanbanBoard: KanbanService,
},
} }
]; ];

View File

@ -2,14 +2,11 @@ import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subscription} from "rxjs/Subscription"; 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";
import {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;
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -18,22 +15,18 @@ const TIMER_PAGE_SWITCH = 300000;
}) })
export class AppComponent implements OnInit, OnDestroy { export class AppComponent implements OnInit, OnDestroy {
selfUpdateCheckerTimer: Subscription; private selfUpdateCheckerTimer: Subscription;
reloadJiraIssueTimer: Subscription; private reloadKanbanTimer: Subscription;
pageSwitchTimer: Subscription;
constructor( constructor(private selfUpdaterService: SelfUpdaterService,
private selfUpdaterService: SelfUpdaterService, private kanbanService: KanbanService,
private kanbanService: KanbanService,
private router: Router
) {} ) {}
/** /**
* 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 * - reloadKanbanTimer 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);
@ -41,20 +34,13 @@ export class AppComponent implements OnInit, OnDestroy {
this.selfUpdaterService.checkAndReloadIfNecessary(); this.selfUpdaterService.checkAndReloadIfNecessary();
}); });
let timer1 = TimerObservable.create(TIMER_JIRA_REFRESH, TIMER_JIRA_REFRESH); let timer1 = TimerObservable.create(TIMER_JIRA_REFRESH, TIMER_JIRA_REFRESH);
this.reloadJiraIssueTimer = timer1.subscribe(() => { this.reloadKanbanTimer = timer1.subscribe(() => {
this.kanbanService.reload(); 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");
});
} }
public ngOnDestroy() { public ngOnDestroy() {
this.selfUpdateCheckerTimer.unsubscribe(); this.selfUpdateCheckerTimer.unsubscribe();
this.reloadJiraIssueTimer.unsubscribe(); this.reloadKanbanTimer.unsubscribe();
this.pageSwitchTimer.unsubscribe();
} }
} }

View File

@ -12,9 +12,8 @@ import { KanbanModule } from './kanban/kanban.module';
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
KanbanModule KanbanModule,
], ],
providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { } export class AppModule {}

View File

@ -1,4 +1,4 @@
import {Component, HostBinding, HostDecorator, HostListener, OnInit} from '@angular/core'; import {Component, HostBinding, HostListener, OnInit} from '@angular/core';
import {Title} from '@angular/platform-browser'; import {Title} from '@angular/platform-browser';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
@ -33,7 +33,11 @@ export class KanbanBoardComponent implements OnInit {
*/ */
ngOnInit() { ngOnInit() {
this.titleService.setTitle('TaurusXFT : Kanban board'); this.titleService.setTitle('TaurusXFT : Kanban board');
this.route.data.subscribe((data: { kanbanBoard: KanbanBoard }) => this.kanbanBoard = data.kanbanBoard); this.route.data.subscribe((data: {
kanbanBoard: KanbanBoard,
}) => {
this.kanbanBoard = data.kanbanBoard;
});
} }
get kanbanBoard(): KanbanBoard { get kanbanBoard(): KanbanBoard {

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

@ -1,21 +1,33 @@
<h1> <h1>
{{rowHeading}} {{rowHeading}}
<ng-template [ngIf]="wipCount"> <ng-template [ngIf]="wipLimit">
- {{wipCount}}/{{wipLimit}} - {{wipCount}}/{{wipLimit}}
</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">
<span *ngIf="kanbanEntry.epicName"
class="ui mini olive right floated label">{{kanbanEntry.epicName}}</span>
<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|prefixJiraId:kanbanEntry.key"
[title]="kanbanEntry.summary"></a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,11 @@
import {Component, Input} from '@angular/core'; 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";
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://cc-jira.rnd.ki.sw.ericsson.se/browse/';
const labelColors = { const labelColors = {
TSP: 'teal', TSP: 'teal',
@ -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

@ -0,0 +1,35 @@
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {KanbanBoardComponent} from "./kanban-board/kanban-board.component";
import {KanbanService} from "./shared";
const routes: Routes = [
{
path: 'kanban',
children: [],
component: KanbanBoardComponent,
resolve: {
kanbanBoard: KanbanService,
},
data: {
disableAutoSwitch: false
}
}, {
path: 'kanban-fixed',
children: [],
component: KanbanBoardComponent,
resolve: {
kanbanBoard: KanbanService,
},
data: {
disableAutoSwitch: true
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class KanbanRoutingModule {
}

View File

@ -4,17 +4,20 @@ import { HttpModule } from "@angular/http";
import { KanbanBoardComponent } from './kanban-board/kanban-board.component'; import { KanbanBoardComponent } from './kanban-board/kanban-board.component';
import { KanbanService } from './shared/kanban.service'; import { KanbanService } from './shared';
import { KanbanEntryItemComponent } from './kanban-entry-item/kanban-entry-item.component'; import { KanbanEntryItemComponent } from './kanban-entry-item/kanban-entry-item.component';
import { PriorityColorPipe } from './shared/priority-color.pipe'; import { PriorityColorPipe } from './shared/priority-color.pipe';
import { ShortenTextPipe } from './shared/shorten-text.pipe'; import { ShortenTextPipe } from './shared/shorten-text.pipe';
import { SelfUpdaterService } from './shared/self-updater.service'; import { SelfUpdaterService } from './shared/self-updater.service';
import { BlockedDaysPipe } from './shared/blocked-days.pipe'; import { BlockedDaysPipe } from './shared/blocked-days.pipe';
import { KanbanRoutingModule } from "./kanban-routing.module";
import { PrefixJiraIdPipe } from './shared/prefix-jira-id.pipe';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
HttpModule, HttpModule,
KanbanRoutingModule,
], ],
declarations: [ declarations: [
KanbanBoardComponent, KanbanBoardComponent,
@ -22,6 +25,7 @@ import { BlockedDaysPipe } from './shared/blocked-days.pipe';
PriorityColorPipe, PriorityColorPipe,
ShortenTextPipe, ShortenTextPipe,
BlockedDaysPipe, BlockedDaysPipe,
PrefixJiraIdPipe,
], ],
providers: [ providers: [
KanbanService, KanbanService,

View File

@ -7,21 +7,23 @@ export class KanbanEntry {
public key: string; public key: string;
public summary: string; public summary: string;
public issueType: JiraIssueType; public issueType: JiraIssueType;
public epicName: string;
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>;
public prio: number; // public prio: number;
public functionalAreas: Array<string>; // public functionalAreas: Array<string>;
public externalId: string; // public externalId: string;
public externalLink: string; // public externalLink: string;
public project: string; // public project: string;
public mhwebStatus: string; // public mhwebStatus: string;
public mhwebHot: boolean; // public mhwebHot: boolean;
public mhwebExternal: boolean; // public mhwebExternal: boolean;
public team: string; // public team: string;
public answerCode: string; // public answerCode: string;
public isLastOfPriority: boolean; public isLastOfPriority: boolean;
public worklog: number; public worklog: number;
public daysBlocked: number; public daysBlocked: number;

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Http, Headers } from "@angular/http"; import { Http } from "@angular/http";
import 'rxjs/Rx'; import 'rxjs/Rx';
import { Router, Resolve, ActivatedRouteSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
@ -23,7 +23,7 @@ export class KanbanService {
* @returns {Observable<KanbanBoard>} * @returns {Observable<KanbanBoard>}
*/ */
public getList(): Observable<KanbanBoard> { public getList(): Observable<KanbanBoard> {
return this.httpService.get(this.url).map(res => this.preprocessPriorities(<KanbanBoard>res.json())); return this.httpService.get(this.url).map(res => <KanbanBoard>res.json());
} }
/** /**
@ -50,27 +50,4 @@ export class KanbanService {
set kanbanBoard(kanbanBoard: KanbanBoard) { set kanbanBoard(kanbanBoard: KanbanBoard) {
this.cachedKanbanBoard = kanbanBoard; this.cachedKanbanBoard = kanbanBoard;
} }
/**
* Used to preprocess all the entries, mark last item of every priority type for display formatting
*
* @param {KanbanBoard} kanbanBoard
* @returns {KanbanBoard}
*/
private preprocessPriorities(kanbanBoard: KanbanBoard): KanbanBoard {
['inbox','inProgress','verification'].map(progress => {
kanbanBoard[progress].map(entry => entry.isLastOfPriority = false);
['Trivial',
'Minor',
'Major',
'Critical',
'Blocker'].map(prio => {
let prioLastIndex = kanbanBoard[progress].reduce((accumulator, value, idx) => value.issuePriority==prio ? idx : accumulator, -1);
try {
kanbanBoard[progress][prioLastIndex].isLastOfPriority = true;
} catch(e) {}
});
});
return kanbanBoard;
}
} }

View File

@ -0,0 +1,8 @@
import { PrefixJiraIdPipe } from './prefix-jira-id.pipe';
describe('PrefixJiraIdPipe', () => {
it('create an instance', () => {
const pipe = new PrefixJiraIdPipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'prefixJiraId'
})
export class PrefixJiraIdPipe implements PipeTransform {
transform(value: string, jiraId: string): string {
return `[${jiraId}] ${value}`;
}
}

View File

@ -34,7 +34,7 @@ export class PriorityColorPipe implements PipeTransform {
value = value.replace(xlMatch, (fullMatch: string, mhrMatched: string) => { value = value.replace(xlMatch, (fullMatch: string, mhrMatched: string) => {
return `<span class="match-xl">${mhrMatched}</span> `; return `<span class="match-xl">${mhrMatched}</span> `;
}); });
return (prioIcon ? `<img class="prio-icon" src="${prioIcon}"> ` : "") + value; return (prioIcon ? `<img class="prio-icon" width="16" height="16" src="${prioIcon}"> ` : "") + value;
} }
} }

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

Binary file not shown.

Binary file not shown.

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

BIN
src/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -6,7 +6,7 @@
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.png">
</head> </head>
<body> <body>
<app-root> <app-root>

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;
} }