Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e346f84eeb | ||
|
|
5f2639061b | ||
|
|
41c057c5e5 | ||
|
|
04aa27bd2e | ||
|
|
82184678b9 | ||
|
|
cc8a3ddd03 | ||
|
|
f4ce7ec951 | ||
|
|
9f1d5d4d9a | ||
|
|
0d628456c4 | ||
|
|
6494c5c75c | ||
|
|
d37a6f68c6 | ||
|
|
97d946a324 | ||
|
|
59b463c3b2 | ||
|
|
985383797e | ||
|
|
7e82c57b0e |
@ -9,7 +9,7 @@
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico"
|
||||
"favicon.png"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -1,21 +1,11 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {Routes, RouterModule} from '@angular/router';
|
||||
|
||||
import { KanbanService } from "./kanban/shared"
|
||||
import { KanbanBoardComponent } from "./kanban/kanban-board/kanban-board.component"
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/kanban',
|
||||
redirectTo: '/kanban-fixed',
|
||||
pathMatch: 'full',
|
||||
},{
|
||||
path: 'kanban',
|
||||
children: [],
|
||||
component: KanbanBoardComponent,
|
||||
resolve: {
|
||||
kanbanBoard: KanbanService,
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -2,14 +2,11 @@ import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Subscription} from "rxjs/Subscription";
|
||||
import {TimerObservable} from "rxjs/observable/TimerObservable";
|
||||
|
||||
import {KanbanService} from "./kanban/shared/kanban.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {KanbanService} from "./kanban/shared";
|
||||
import {SelfUpdaterService} from "./kanban/shared/self-updater.service";
|
||||
|
||||
const TIMER_DEPLOY_REFRESH = 30000;
|
||||
const TIMER_JIRA_REFRESH = 60000;
|
||||
const TIMER_PAGE_SWITCH = 300000;
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -18,22 +15,18 @@ const TIMER_PAGE_SWITCH = 300000;
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
selfUpdateCheckerTimer: Subscription;
|
||||
reloadJiraIssueTimer: Subscription;
|
||||
pageSwitchTimer: Subscription;
|
||||
private selfUpdateCheckerTimer: Subscription;
|
||||
private reloadKanbanTimer: Subscription;
|
||||
|
||||
constructor(
|
||||
private selfUpdaterService: SelfUpdaterService,
|
||||
constructor(private selfUpdaterService: SelfUpdaterService,
|
||||
private kanbanService: KanbanService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* - reloadKanbanTimer 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);
|
||||
@ -41,20 +34,13 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.selfUpdaterService.checkAndReloadIfNecessary();
|
||||
});
|
||||
let timer1 = TimerObservable.create(TIMER_JIRA_REFRESH, TIMER_JIRA_REFRESH);
|
||||
this.reloadJiraIssueTimer = timer1.subscribe(() => {
|
||||
this.reloadKanbanTimer = 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");
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.selfUpdateCheckerTimer.unsubscribe();
|
||||
this.reloadJiraIssueTimer.unsubscribe();
|
||||
this.pageSwitchTimer.unsubscribe();
|
||||
this.reloadKanbanTimer.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,9 +12,8 @@ import { KanbanModule } from './kanban/kanban.module';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
KanbanModule
|
||||
KanbanModule,
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@ -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 {ActivatedRoute} from '@angular/router';
|
||||
|
||||
@ -33,7 +33,11 @@ export class KanbanBoardComponent implements OnInit {
|
||||
*/
|
||||
ngOnInit() {
|
||||
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 {
|
||||
|
||||
@ -57,3 +57,43 @@ a:hover {
|
||||
/*.ui.divided.items > .item.trivial.bottom-separator {*/
|
||||
/*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;
|
||||
}
|
||||
|
||||
@ -1,21 +1,33 @@
|
||||
<h1>
|
||||
{{rowHeading}}
|
||||
<ng-template [ngIf]="wipCount">
|
||||
<ng-template [ngIf]="wipLimit">
|
||||
- {{wipCount}}/{{wipLimit}}
|
||||
</ng-template>
|
||||
</h1>
|
||||
<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="task-description">
|
||||
<span *ngIf="kanbanEntry.epicName"
|
||||
class="ui mini olive right floated label">{{kanbanEntry.epicName}}</span>
|
||||
<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>
|
||||
<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">
|
||||
</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>
|
||||
|
||||
@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core';
|
||||
|
||||
import { environment } from "../../../environments/environment";
|
||||
import { KanbanEntry } from "../shared";
|
||||
import { JiraAssignee } from "../shared";
|
||||
|
||||
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 = {
|
||||
TSP: 'teal',
|
||||
@ -97,4 +98,22 @@ export class KanbanEntryItemComponent {
|
||||
return kanbanEntry.daysBlocked > 0
|
||||
&& 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);
|
||||
}
|
||||
}
|
||||
|
||||
35
src/app/kanban/kanban-routing.module.ts
Normal file
35
src/app/kanban/kanban-routing.module.ts
Normal 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 {
|
||||
}
|
||||
@ -4,17 +4,20 @@ import { HttpModule } from "@angular/http";
|
||||
|
||||
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 { PriorityColorPipe } from './shared/priority-color.pipe';
|
||||
import { ShortenTextPipe } from './shared/shorten-text.pipe';
|
||||
import { SelfUpdaterService } from './shared/self-updater.service';
|
||||
import { BlockedDaysPipe } from './shared/blocked-days.pipe';
|
||||
import { KanbanRoutingModule } from "./kanban-routing.module";
|
||||
import { PrefixJiraIdPipe } from './shared/prefix-jira-id.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpModule,
|
||||
KanbanRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
KanbanBoardComponent,
|
||||
@ -22,6 +25,7 @@ import { BlockedDaysPipe } from './shared/blocked-days.pipe';
|
||||
PriorityColorPipe,
|
||||
ShortenTextPipe,
|
||||
BlockedDaysPipe,
|
||||
PrefixJiraIdPipe,
|
||||
],
|
||||
providers: [
|
||||
KanbanService,
|
||||
|
||||
@ -7,21 +7,23 @@ export class KanbanEntry {
|
||||
public key: string;
|
||||
public summary: string;
|
||||
public issueType: JiraIssueType;
|
||||
public epicName: string;
|
||||
public status: JiraStatus;
|
||||
public assignee: JiraAssignee;
|
||||
public additionalAssignees: Array<JiraAssignee> = [];
|
||||
public issuePriority: string;
|
||||
public issuePriorityIcon: string;
|
||||
public labels: Array<string>;
|
||||
public prio: number;
|
||||
public functionalAreas: Array<string>;
|
||||
public externalId: string;
|
||||
public externalLink: string;
|
||||
public project: string;
|
||||
public mhwebStatus: string;
|
||||
public mhwebHot: boolean;
|
||||
public mhwebExternal: boolean;
|
||||
public team: string;
|
||||
public answerCode: string;
|
||||
// public prio: number;
|
||||
// public functionalAreas: Array<string>;
|
||||
// public externalId: string;
|
||||
// public externalLink: string;
|
||||
// public project: string;
|
||||
// public mhwebStatus: string;
|
||||
// public mhwebHot: boolean;
|
||||
// public mhwebExternal: boolean;
|
||||
// public team: string;
|
||||
// public answerCode: string;
|
||||
public isLastOfPriority: boolean;
|
||||
public worklog: number;
|
||||
public daysBlocked: number;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, Headers } from "@angular/http";
|
||||
import { Http } from "@angular/http";
|
||||
import 'rxjs/Rx';
|
||||
import { Router, Resolve, ActivatedRouteSnapshot } from '@angular/router';
|
||||
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { environment } from '../../../environments/environment';
|
||||
@ -23,7 +23,7 @@ export class KanbanService {
|
||||
* @returns {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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
8
src/app/kanban/shared/prefix-jira-id.pipe.spec.ts
Normal file
8
src/app/kanban/shared/prefix-jira-id.pipe.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
12
src/app/kanban/shared/prefix-jira-id.pipe.ts
Normal file
12
src/app/kanban/shared/prefix-jira-id.pipe.ts
Normal 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}`;
|
||||
}
|
||||
|
||||
}
|
||||
@ -34,7 +34,7 @@ export class PriorityColorPipe implements PipeTransform {
|
||||
value = value.replace(xlMatch, (fullMatch: string, mhrMatched: string) => {
|
||||
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
BIN
src/assets/font/impact.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/utm-ericsson-capital.ttf
Normal file
BIN
src/assets/font/utm-ericsson-capital.ttf
Normal file
Binary file not shown.
@ -5,5 +5,5 @@
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUri: "http://localhost:8080",
|
||||
apiUri: "http://localhost:8888",
|
||||
};
|
||||
|
||||
BIN
src/favicon.ico
BIN
src/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB |
BIN
src/favicon.png
Normal file
BIN
src/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
@ -6,7 +6,7 @@
|
||||
<base href="/">
|
||||
|
||||
<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>
|
||||
<body>
|
||||
<app-root>
|
||||
|
||||
@ -1,4 +1,20 @@
|
||||
/* 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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user