* angular2 version uplift
* configurable kanban board added * iframe slide type added * many to many implementation of team-slide connection
This commit is contained in:
parent
96918b50ca
commit
0f535881a4
@ -1,63 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"project": {
|
|
||||||
"name": "mtas-tv-frontend"
|
|
||||||
},
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"root": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"assets": [
|
|
||||||
"assets",
|
|
||||||
"favicon.ico"
|
|
||||||
],
|
|
||||||
"index": "index.html",
|
|
||||||
"main": "main.ts",
|
|
||||||
"polyfills": "polyfills.ts",
|
|
||||||
"test": "test.ts",
|
|
||||||
"tsconfig": "tsconfig.app.json",
|
|
||||||
"testTsconfig": "tsconfig.spec.json",
|
|
||||||
"prefix": "app",
|
|
||||||
"styles": [
|
|
||||||
"../node_modules/semantic-ui-css/semantic.css",
|
|
||||||
"styles.css"
|
|
||||||
],
|
|
||||||
"scripts": [
|
|
||||||
"../node_modules/marked/lib/marked.js"
|
|
||||||
],
|
|
||||||
"environmentSource": "environments/environment.ts",
|
|
||||||
"environments": {
|
|
||||||
"dev": "environments/environment.ts",
|
|
||||||
"prod": "environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"e2e": {
|
|
||||||
"protractor": {
|
|
||||||
"config": "./protractor.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": [
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.app.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.spec.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "e2e/tsconfig.e2e.json",
|
|
||||||
"exclude": "**/node_modules/**"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"test": {
|
|
||||||
"karma": {
|
|
||||||
"config": "./karma.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaults": {
|
|
||||||
"styleExt": "css",
|
|
||||||
"component": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
138
angular.json
Normal file
138
angular.json
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"mtas-tv-frontend": {
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"projectType": "application",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"assets": [
|
||||||
|
"src/assets",
|
||||||
|
"src/favicon.ico"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"node_modules/semantic-ui-css/semantic.css",
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"node_modules/marked/lib/marked.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"extractCss": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "mtas-tv-frontend:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "mtas-tv-frontend:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "mtas-tv-frontend:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"karmaConfig": "./karma.conf.js",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
|
"scripts": [
|
||||||
|
"node_modules/marked/lib/marked.js"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"node_modules/semantic-ui-css/semantic.css",
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"assets": [
|
||||||
|
"src/assets",
|
||||||
|
"src/favicon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"src/tsconfig.app.json",
|
||||||
|
"src/tsconfig.spec.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mtas-tv-frontend-e2e": {
|
||||||
|
"root": "e2e",
|
||||||
|
"sourceRoot": "e2e",
|
||||||
|
"projectType": "application",
|
||||||
|
"architect": {
|
||||||
|
"e2e": {
|
||||||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
|
"options": {
|
||||||
|
"protractorConfig": "./protractor.conf.js",
|
||||||
|
"devServerTarget": "mtas-tv-frontend:serve"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"e2e/tsconfig.e2e.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "mtas-tv-frontend",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"prefix": "app",
|
||||||
|
"styleext": "css"
|
||||||
|
},
|
||||||
|
"@schematics/angular:directive": {
|
||||||
|
"prefix": "app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,24 +4,22 @@
|
|||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
frameworks: ['jasmine', '@angular/cli'],
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
require('karma-chrome-launcher'),
|
require('karma-chrome-launcher'),
|
||||||
require('karma-jasmine-html-reporter'),
|
require('karma-jasmine-html-reporter'),
|
||||||
require('karma-coverage-istanbul-reporter'),
|
require('karma-coverage-istanbul-reporter'),
|
||||||
require('@angular/cli/plugins/karma')
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
],
|
],
|
||||||
client:{
|
client:{
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
},
|
},
|
||||||
coverageIstanbulReporter: {
|
coverageIstanbulReporter: {
|
||||||
reports: [ 'html', 'lcovonly' ],
|
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
||||||
fixWebpackSourcePaths: true
|
fixWebpackSourcePaths: true
|
||||||
},
|
},
|
||||||
angularCli: {
|
|
||||||
environment: 'dev'
|
|
||||||
},
|
|
||||||
reporters: ['progress', 'kjhtml'],
|
reporters: ['progress', 'kjhtml'],
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
||||||
|
|||||||
11146
package-lock.json
generated
11146
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@ -1,52 +1,52 @@
|
|||||||
{
|
{
|
||||||
"name": "mtas-tv-frontend",
|
"name": "mtas-tv-frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"build": "ng build --prod",
|
"build": "ng build",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^5.2.0",
|
"@angular/animations": "^6.1.0",
|
||||||
"@angular/common": "^5.2.0",
|
"@angular/common": "^6.1.0",
|
||||||
"@angular/compiler": "^5.2.0",
|
"@angular/compiler": "^6.1.0",
|
||||||
"@angular/core": "^5.2.0",
|
"@angular/core": "^6.1.0",
|
||||||
"@angular/forms": "^5.2.0",
|
"@angular/forms": "^6.1.0",
|
||||||
"@angular/http": "^5.2.0",
|
"@angular/http": "^6.1.0",
|
||||||
"@angular/platform-browser": "^5.2.0",
|
"@angular/platform-browser": "^6.1.0",
|
||||||
"@angular/platform-browser-dynamic": "^5.2.0",
|
"@angular/platform-browser-dynamic": "^6.1.0",
|
||||||
"@angular/router": "^5.2.0",
|
"@angular/router": "^6.1.0",
|
||||||
"@types/marked": "^0.3.0",
|
"@types/marked": "^0.4.1",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.5.4",
|
||||||
"marked": "^0.3.19",
|
"marked": "^0.5.0",
|
||||||
"ng2-semantic-ui": "^0.9.7",
|
"ng2-semantic-ui": "^0.9.7",
|
||||||
"rxjs": "^5.5.6",
|
"rxjs": "^6.0.0",
|
||||||
"semantic-ui-css": "^2.3.1",
|
"semantic-ui-css": "^2.3.3",
|
||||||
"zone.js": "^0.8.19"
|
"zone.js": "~0.8.26"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/cli": "~1.7.3",
|
"@angular-devkit/build-angular": "~0.7.0",
|
||||||
"@angular/compiler-cli": "^5.2.0",
|
"@angular/cli": "~6.1.5",
|
||||||
"@angular/language-service": "^5.2.0",
|
"@angular/compiler-cli": "^6.1.0",
|
||||||
"@types/jasmine": "~2.8.3",
|
"@angular/language-service": "^6.1.0",
|
||||||
"@types/jasminewd2": "~2.0.2",
|
"@types/jasmine": "~2.8.6",
|
||||||
"@types/node": "~6.0.60",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"codelyzer": "^4.0.1",
|
"@types/node": "~8.9.4",
|
||||||
"jasmine-core": "~2.8.0",
|
"codelyzer": "~4.2.1",
|
||||||
|
"jasmine-core": "~2.99.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~2.0.0",
|
"karma": "~1.7.1",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
"karma-coverage-istanbul-reporter": "~2.0.0",
|
||||||
"karma-jasmine": "~1.1.0",
|
"karma-jasmine": "~1.1.1",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"protractor": "~5.1.2",
|
"protractor": "~5.4.0",
|
||||||
"ts-node": "~4.1.0",
|
"ts-node": "~5.0.1",
|
||||||
"tslint": "~5.9.1",
|
"tslint": "~5.9.1",
|
||||||
"typescript": "~2.5.3"
|
"typescript": "~2.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/app/admin/dashboard/dashboard.component.html
Normal file → Executable file
2
src/app/admin/dashboard/dashboard.component.html
Normal file → Executable file
@ -2,7 +2,7 @@
|
|||||||
<h1 class="ui dividing header">Dashboard</h1>
|
<h1 class="ui dividing header">Dashboard</h1>
|
||||||
|
|
||||||
<div class="ui four cards">
|
<div class="ui four cards">
|
||||||
<a class="ui raised yellow card" [routerLink]="['/commit-tracker']">
|
<a class="ui raised yellow card" [routerLink]="['/kanban']">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="header">Start slideshow</div>
|
<div class="header">Start slideshow</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
|||||||
55
src/app/admin/slide-editor/slide-editor.component.html
Normal file → Executable file
55
src/app/admin/slide-editor/slide-editor.component.html
Normal file → Executable file
@ -2,31 +2,54 @@
|
|||||||
<h1 class="ui dividing header">Slide editor</h1>
|
<h1 class="ui dividing header">Slide editor</h1>
|
||||||
<form class="ui form" #slideEditorForm (ngSubmit)="saveSlide()">
|
<form class="ui form" #slideEditorForm (ngSubmit)="saveSlide()">
|
||||||
<div class="two fields">
|
<div class="two fields">
|
||||||
<div class="eight wide field">
|
<div class="six wide field">
|
||||||
<label for="slide_name">Slide title</label>
|
<label for="slide_name">Slide title</label>
|
||||||
<input id="slide_name" type="text" name="slide_name" [(ngModel)]="slide.title">
|
<input id="slide_name" type="text" name="slide_name" [(ngModel)]="slide.title">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="two wide field">
|
||||||
|
<label>Visibility</label>
|
||||||
|
<div class="field">
|
||||||
|
<sui-radio-button name="slide_visibility" value="public" [(ngModel)]="slide.visibility">Public</sui-radio-button>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<sui-radio-button name="slide_visibility" value="team" [(ngModel)]="slide.visibility">Team</sui-radio-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="eight wide field">
|
<div class="eight wide field">
|
||||||
<label for="team">Visible to this team</label>
|
<label for="teams">Visible to this team</label>
|
||||||
<sui-select class="selection"
|
<sui-multi-select class="selection"
|
||||||
id="team"
|
id="teams"
|
||||||
name="team"
|
name="teams"
|
||||||
[(ngModel)]="slide.team"
|
[(ngModel)]="slide.teams"
|
||||||
|
[isDisabled]="slide.visibility=='public'"
|
||||||
labelField="name"
|
labelField="name"
|
||||||
[isSearchable]="true"
|
[isSearchable]="true"
|
||||||
#select>
|
#select>
|
||||||
<sui-select-option [value]="emptyTeam"></sui-select-option>
|
|
||||||
<sui-select-option *ngFor="let team of teams" [value]="team"></sui-select-option>
|
<sui-select-option *ngFor="let team of teams" [value]="team"></sui-select-option>
|
||||||
</sui-select>
|
</sui-multi-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="inline fields">
|
||||||
<label for="slide_data">Slide data</label>
|
<label>Slide type</label>
|
||||||
<textarea id="slide_data" rows="30" name="slide_data" [(ngModel)]="slide.slideData"></textarea>
|
<div class="field">
|
||||||
|
<sui-radio-button name="slide_type" value="markdown" [(ngModel)]="slide.type">Markdown</sui-radio-button>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<sui-radio-button name="slide_type" value="iframe" [(ngModel)]="slide.type">Iframe</sui-radio-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field" *ngIf="isMarkdown">
|
||||||
|
<label for="slide_data">Slide data</label>
|
||||||
|
<textarea id="slide_data" rows="30" name="slide_data" [(ngModel)]="slide.slideData"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field" *ngIf="isIframe">
|
||||||
|
<label for="slide_url">Slide url</label>
|
||||||
|
<input id="slide_url" type="text" name="slide_data" [(ngModel)]="slide.slideUrl">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="five wide field">
|
<div class="five wide field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@ -43,7 +66,8 @@
|
|||||||
[class.primary]="canPreview"
|
[class.primary]="canPreview"
|
||||||
[class.disabled]="!canPreview"
|
[class.disabled]="!canPreview"
|
||||||
(click)="preview()"><i class="search icon"></i>Preview</button>
|
(click)="preview()"><i class="search icon"></i>Preview</button>
|
||||||
<a class="ui button orange"
|
<a *ngIf="isMarkdown"
|
||||||
|
class="ui button orange"
|
||||||
href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"
|
href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"
|
||||||
target="_blank"><i class="question circle outline icon"></i>MD howto</a>
|
target="_blank"><i class="question circle outline icon"></i>MD howto</a>
|
||||||
<a class="ui button"
|
<a class="ui button"
|
||||||
@ -54,5 +78,10 @@
|
|||||||
[data]="renderedPreview"
|
[data]="renderedPreview"
|
||||||
[preview]="true"
|
[preview]="true"
|
||||||
[(visible)]="previewVisible"
|
[(visible)]="previewVisible"
|
||||||
*ngIf="previewVisible"></app-slide>
|
*ngIf="previewVisible && isMarkdown"></app-slide>
|
||||||
|
<app-slide-iframe class="preview"
|
||||||
|
[data]="slide.slideUrl"
|
||||||
|
[preview]="true"
|
||||||
|
[(visible)]="previewVisible"
|
||||||
|
*ngIf="previewVisible && isIframe"></app-slide-iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
39
src/app/admin/slide-editor/slide-editor.component.ts
Normal file → Executable file
39
src/app/admin/slide-editor/slide-editor.component.ts
Normal file → Executable file
@ -1,11 +1,11 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} 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 * as marked from 'marked';
|
import * as marked from 'marked';
|
||||||
import { Slide } from '../../shared/slide';
|
import {Slide, SlideType, SlideVisibility} from '../../shared/slide';
|
||||||
import { SlideService } from '../../shared/service/slide.service';
|
import {SlideService} from '../../shared/service/slide.service';
|
||||||
import { Team } from '../../shared/team';
|
import {Team} from '../../shared/team';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-slide-editor',
|
selector: 'app-slide-editor',
|
||||||
@ -14,7 +14,6 @@ import { Team } from '../../shared/team';
|
|||||||
})
|
})
|
||||||
export class SlideEditorComponent implements OnInit {
|
export class SlideEditorComponent implements OnInit {
|
||||||
private md;
|
private md;
|
||||||
public emptyTeam: Team = new Team();
|
|
||||||
public slide: Slide;
|
public slide: Slide;
|
||||||
public teams: Array<Team> = [];
|
public teams: Array<Team> = [];
|
||||||
public renderedPreview: String = '';
|
public renderedPreview: String = '';
|
||||||
@ -25,7 +24,6 @@ export class SlideEditorComponent implements OnInit {
|
|||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router) {
|
private router: Router) {
|
||||||
this.md = marked.setOptions({});
|
this.md = marked.setOptions({});
|
||||||
this.emptyTeam.name = 'All teams';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -36,9 +34,11 @@ export class SlideEditorComponent implements OnInit {
|
|||||||
}) => {
|
}) => {
|
||||||
this.teams = data.teams;
|
this.teams = data.teams;
|
||||||
this.slide = data.slide ? data.slide : new Slide;
|
this.slide = data.slide ? data.slide : new Slide;
|
||||||
this.slide.team = this.slide.team === null
|
this.slide.teams = this.teams.filter(
|
||||||
? this.emptyTeam
|
team => this.slide.teams.some(
|
||||||
: this.teams.find(team => team.id === this.slide.team.id);
|
st => team.id === st.id
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,16 +53,27 @@ export class SlideEditorComponent implements OnInit {
|
|||||||
get canSave(): boolean {
|
get canSave(): boolean {
|
||||||
return [
|
return [
|
||||||
this.slide.title,
|
this.slide.title,
|
||||||
this.slide.slideData
|
this.isMarkdown ? this.slide.slideData : this.slide.slideUrl
|
||||||
].every(field => field.trim().length > 0);
|
].every(field => field.trim().length > 0) && (
|
||||||
|
this.slide.visibility === SlideVisibility.Public ||
|
||||||
|
this.slide.visibility === SlideVisibility.Team && this.slide.teams.length > 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canPreview(): boolean {
|
get canPreview(): boolean {
|
||||||
return this.slide.slideData.trim().length > 0;
|
return this.isMarkdown && this.slide.slideData.trim().length > 0 || this.isIframe && this.slide.slideUrl.trim().length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public preview() {
|
public preview() {
|
||||||
this.previewVisible = true;
|
this.previewVisible = true;
|
||||||
this.renderedPreview = this.md.parse(this.slide.slideData);
|
this.renderedPreview = this.md.parse(this.slide.slideData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isMarkdown(): boolean {
|
||||||
|
return this.slide.type === SlideType.MarkDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isIframe(): boolean {
|
||||||
|
return this.slide.type === SlideType.IFrame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/app/admin/slide-list/slide-list.component.html
Normal file → Executable file
6
src/app/admin/slide-list/slide-list.component.html
Normal file → Executable file
@ -19,8 +19,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th class="collapsing"></th>
|
<th class="collapsing"></th>
|
||||||
<th><i class="large address book outline icon"></i>Slide title</th>
|
<th><i class="large address book outline icon"></i>Slide title</th>
|
||||||
<th class="collapsing"><i class="large users icon"></i>Owner team</th>
|
<th class="collapsing"><i class="large users icon"></i>Visible to</th>
|
||||||
<th class="collapsing"><i class="large check square outline icon"></i>Visible</th>
|
<th class="collapsing"><i class="large check square outline icon"></i>Active</th>
|
||||||
<th class="collapsing"><i class="large arrows alternate vertical icon"></i>Order</th>
|
<th class="collapsing"><i class="large arrows alternate vertical icon"></i>Order</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
class="large link red fitted trash alternate outline icon"></i></a>
|
class="large link red fitted trash alternate outline icon"></i></a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{slide.title}}</td>
|
<td>{{slide.title}}</td>
|
||||||
<td class="collapsing">{{slideTeam(slide.team)}}</td>
|
<td class="collapsing">{{slideTeam(slide)}}</td>
|
||||||
<td class="center aligned"><i class="large icon" [ngClass]="visibleClass(slide)"></i></td>
|
<td class="center aligned"><i class="large icon" [ngClass]="visibleClass(slide)"></i></td>
|
||||||
<td class="center aligned">
|
<td class="center aligned">
|
||||||
<a title="Up" (click)="moveUp(slide)"><i
|
<a title="Up" (click)="moveUp(slide)"><i
|
||||||
|
|||||||
27
src/app/admin/slide-list/slide-list.component.ts
Normal file → Executable file
27
src/app/admin/slide-list/slide-list.component.ts
Normal file → Executable file
@ -1,10 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import { Title } from '@angular/platform-browser';
|
import {Title} from '@angular/platform-browser';
|
||||||
|
|
||||||
import { SlideService } from '../../shared/service/slide.service';
|
import {SlideService} from '../../shared/service/slide.service';
|
||||||
import { Slide } from '../../shared/slide';
|
import {Slide, SlideVisibility} from '../../shared/slide';
|
||||||
import { Team } from '../../shared/team';
|
import {Team} from '../../shared/team';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-slide-list',
|
selector: 'app-slide-list',
|
||||||
@ -38,7 +38,7 @@ export class SlideListComponent implements OnInit {
|
|||||||
return this.selectedTeam === this.emptyTeam
|
return this.selectedTeam === this.emptyTeam
|
||||||
? this.slideService.slides
|
? this.slideService.slides
|
||||||
: this.slideService.slides.filter(
|
: this.slideService.slides.filter(
|
||||||
slide => slide.team == null || slide.team.id === this.selectedTeam.id
|
slide => slide.teams == null || slide.teams.some(s => s.id === this.selectedTeam.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,8 +46,10 @@ export class SlideListComponent implements OnInit {
|
|||||||
this.slideService.slides = slides;
|
this.slideService.slides = slides;
|
||||||
}
|
}
|
||||||
|
|
||||||
public slideTeam(team: Team): String {
|
public slideTeam(slide: Slide): string {
|
||||||
return team === null ? 'All teams' : team.name;
|
return slide.visibility === SlideVisibility.Team && slide.teams.length > 0
|
||||||
|
? slide.teams.map(team => team.name).join(', ')
|
||||||
|
: 'All teams';
|
||||||
}
|
}
|
||||||
|
|
||||||
public moveUp(slide: Slide) {
|
public moveUp(slide: Slide) {
|
||||||
@ -106,4 +108,11 @@ export class SlideListComponent implements OnInit {
|
|||||||
'red times': !slide.isVisible
|
'red times': !slide.isVisible
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public visibilityIcon(slide: Slide) {
|
||||||
|
return {
|
||||||
|
'green globe': slide.visibility === SlideVisibility.Public,
|
||||||
|
'green users': slide.visibility === SlideVisibility.Team
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/app/admin/slide-resolver.service.ts
Normal file → Executable file
2
src/app/admin/slide-resolver.service.ts
Normal file → Executable file
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
import { Slide } from '../shared/slide';
|
import { Slide } from '../shared/slide';
|
||||||
|
|||||||
2
src/app/admin/team-resolver.service.ts
Normal file → Executable file
2
src/app/admin/team-resolver.service.ts
Normal file → Executable file
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { Team } from '../shared/team';
|
import { Team } from '../shared/team';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
|||||||
2
src/app/display/commit-tracker/commit-tracker.component.css
Normal file → Executable file
2
src/app/display/commit-tracker/commit-tracker.component.css
Normal file → Executable file
@ -43,6 +43,8 @@
|
|||||||
|
|
||||||
.ui.jira-avatar.image > img {
|
.ui.jira-avatar.image > img {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
max-width: 45px;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
5
src/app/display/commit-tracker/commit-tracker.component.ts
Normal file → Executable file
5
src/app/display/commit-tracker/commit-tracker.component.ts
Normal file → Executable file
@ -1,8 +1,7 @@
|
|||||||
import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
|
import { Component, HostBinding, OnDestroy, 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';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription, timer } from 'rxjs';
|
||||||
import { TimerObservable } from 'rxjs/observable/TimerObservable';
|
|
||||||
|
|
||||||
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
||||||
import { CommitTrackerService } from '../../shared/service/commit-tracker.service';
|
import { CommitTrackerService } from '../../shared/service/commit-tracker.service';
|
||||||
@ -40,7 +39,7 @@ export class CommitTrackerComponent implements OnInit, OnDestroy {
|
|||||||
this.titleService.setTitle('Commit-tracker : MTAStv');
|
this.titleService.setTitle('Commit-tracker : MTAStv');
|
||||||
this.route.data.subscribe((data: { commits: Array<Commit> }) => this.commits = data.commits);
|
this.route.data.subscribe((data: { commits: Array<Commit> }) => this.commits = data.commits);
|
||||||
|
|
||||||
const timerCT = TimerObservable.create(TIMER_COMMITTRACKER_REFRESH, TIMER_COMMITTRACKER_REFRESH);
|
const timerCT = timer(TIMER_COMMITTRACKER_REFRESH, TIMER_COMMITTRACKER_REFRESH);
|
||||||
this.refreshCommitTrackerTimer = timerCT.subscribe(() => {
|
this.refreshCommitTrackerTimer = timerCT.subscribe(() => {
|
||||||
this.commitTrackerService.getTeamCommits(this.settings.team.members.map(member => member.signum))
|
this.commitTrackerService.getTeamCommits(this.settings.team.members.map(member => member.signum))
|
||||||
.subscribe(commits => this.commits = commits);
|
.subscribe(commits => this.commits = commits);
|
||||||
|
|||||||
4
src/app/display/display.module.ts
Normal file → Executable file
4
src/app/display/display.module.ts
Normal file → Executable file
@ -16,6 +16,7 @@ import { PrefixJiraIdPipe } from './shared/prefix-jira-id.pipe';
|
|||||||
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 { KanbanService } from './shared';
|
import { KanbanService } from './shared';
|
||||||
|
import { SlideIframeComponent } from './slide-iframe/slide-iframe.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -24,7 +25,7 @@ import { KanbanService } from './shared';
|
|||||||
SuiModule,
|
SuiModule,
|
||||||
DisplayRoutingModule
|
DisplayRoutingModule
|
||||||
],
|
],
|
||||||
exports: [SlideComponent],
|
exports: [SlideComponent, SlideIframeComponent],
|
||||||
declarations: [
|
declarations: [
|
||||||
CommitTrackerComponent,
|
CommitTrackerComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
@ -37,6 +38,7 @@ import { KanbanService } from './shared';
|
|||||||
PrefixJiraIdPipe,
|
PrefixJiraIdPipe,
|
||||||
PriorityColorPipe,
|
PriorityColorPipe,
|
||||||
ShortenTextPipe,
|
ShortenTextPipe,
|
||||||
|
SlideIframeComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SlideShowService,
|
SlideShowService,
|
||||||
|
|||||||
8
src/app/display/kanban-board/kanban-board.component.html
Normal file → Executable file
8
src/app/display/kanban-board/kanban-board.component.html
Normal file → Executable file
@ -1,18 +1,18 @@
|
|||||||
<div class="ui main fullwide-container">
|
<div class="ui main fullwide-container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div app-kanban-entry-item class="four wide column"
|
<div app-kanban-entry-item class="four wide column"
|
||||||
rowHeading="INBOX"
|
[rowHeading]="backlogLabel"
|
||||||
[kanbanEntries]="kanbanBoard.inbox"></div>
|
[kanbanEntries]="kanbanBoard.inbox"></div>
|
||||||
<div app-kanban-entry-item class="four wide column" [ngClass]="inprogressWipClass"
|
<div app-kanban-entry-item class="four wide column" [ngClass]="inprogressWipClass"
|
||||||
rowHeading="INPROGRESS"
|
[rowHeading]="inProgressLabel"
|
||||||
[wipLimit]="inprogressWipLimit" [wipCount]="inprogressWipCount"
|
[wipLimit]="inprogressWipLimit" [wipCount]="inprogressWipCount"
|
||||||
[kanbanEntries]="kanbanBoard.inProgress"></div>
|
[kanbanEntries]="kanbanBoard.inProgress"></div>
|
||||||
<div app-kanban-entry-item class="four wide column" [ngClass]="verificationWipClass"
|
<div app-kanban-entry-item class="four wide column" [ngClass]="verificationWipClass"
|
||||||
rowHeading="VERIFICATION"
|
[rowHeading]="verificationLabel"
|
||||||
[wipLimit]="verificationWipLimit" [wipCount]="verificationWipCount"
|
[wipLimit]="verificationWipLimit" [wipCount]="verificationWipCount"
|
||||||
[kanbanEntries]="kanbanBoard.verification"></div>
|
[kanbanEntries]="kanbanBoard.verification"></div>
|
||||||
<div app-kanban-entry-item class="four wide column"
|
<div app-kanban-entry-item class="four wide column"
|
||||||
rowHeading="DÖNER"
|
[rowHeading]="doneLabel"
|
||||||
[kanbanEntries]="kanbanBoard.done"></div>
|
[kanbanEntries]="kanbanBoard.done"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
31
src/app/display/kanban-board/kanban-board.component.ts
Normal file → Executable file
31
src/app/display/kanban-board/kanban-board.component.ts
Normal file → Executable file
@ -4,9 +4,7 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
|
|
||||||
import { KanbanBoard, KanbanEntry, KanbanService, } from '../shared';
|
import { KanbanBoard, KanbanEntry, KanbanService, } from '../shared';
|
||||||
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
||||||
|
import { SettingsService } from '../../shared/service/settings.service';
|
||||||
const WIP_LIMIT_INPROGRESS = 12;
|
|
||||||
const WIP_LIMIT_VERIFICATION = 8;
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-kanban-board',
|
selector: 'app-kanban-board',
|
||||||
@ -21,7 +19,8 @@ export class KanbanBoardComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(private titleService: Title,
|
constructor(private titleService: Title,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private kanbanService: KanbanService) {
|
private kanbanService: KanbanService,
|
||||||
|
private settingService: SettingsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +44,7 @@ export class KanbanBoardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get inprogressWipLimit(): number {
|
get inprogressWipLimit(): number {
|
||||||
return WIP_LIMIT_INPROGRESS;
|
return this.settingService.team.inprogressColumn.wipLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
get inprogressWipCount(): number {
|
get inprogressWipCount(): number {
|
||||||
@ -64,12 +63,12 @@ export class KanbanBoardComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
get inprogressWipClass() {
|
get inprogressWipClass() {
|
||||||
return {
|
return {
|
||||||
'over-wip': this.inprogressWipCount > WIP_LIMIT_INPROGRESS,
|
'over-wip': this.inprogressWipCount > this.settingService.team.inprogressColumn.wipLimit,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get verificationWipLimit(): number {
|
get verificationWipLimit(): number {
|
||||||
return WIP_LIMIT_VERIFICATION;
|
return this.settingService.team.verificationColumn.wipLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
get verificationWipCount(): number {
|
get verificationWipCount(): number {
|
||||||
@ -88,7 +87,23 @@ export class KanbanBoardComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
get verificationWipClass() {
|
get verificationWipClass() {
|
||||||
return {
|
return {
|
||||||
'over-wip': this.verificationWipCount > WIP_LIMIT_VERIFICATION,
|
'over-wip': this.verificationWipCount > this.settingService.team.verificationColumn.wipLimit,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get backlogLabel(): string {
|
||||||
|
return this.settingService.team.backlogColumn.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
get inProgressLabel(): string {
|
||||||
|
return this.settingService.team.inprogressColumn.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
get verificationLabel(): string {
|
||||||
|
return this.settingService.team.verificationColumn.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
get doneLabel(): string {
|
||||||
|
return this.settingService.team.doneColumn.label;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/app/display/kanban-entry-item/kanban-entry-item.component.html
Normal file → Executable file
2
src/app/display/kanban-entry-item/kanban-entry-item.component.html
Normal file → Executable file
@ -10,6 +10,8 @@
|
|||||||
[ngClass]="entryClass(kanbanEntry)">
|
[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"
|
<span *ngFor="let label of kanbanEntry.labels"
|
||||||
class="ui mini {{labelClass(label)}} right floated label">{{label|uppercase|blockedDays:kanbanEntry.daysBlocked}}</span>
|
class="ui mini {{labelClass(label)}} right floated label">{{label|uppercase|blockedDays:kanbanEntry.daysBlocked}}</span>
|
||||||
|
|||||||
5
src/app/display/kanban-entry-item/kanban-entry-item.component.ts
Normal file → Executable file
5
src/app/display/kanban-entry-item/kanban-entry-item.component.ts
Normal file → Executable file
@ -4,7 +4,7 @@ import { environment } from '../../../environments/environment';
|
|||||||
import { JiraAssignee, KanbanEntry } from '../shared';
|
import { JiraAssignee, KanbanEntry } 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',
|
||||||
@ -14,6 +14,9 @@ const labelColors = {
|
|||||||
BLOCKED: 'red',
|
BLOCKED: 'red',
|
||||||
SPIKE: 'purple',
|
SPIKE: 'purple',
|
||||||
EXPEDITE: 'pink',
|
EXPEDITE: 'pink',
|
||||||
|
|
||||||
|
'MTAS-GUARDIAN': 'pink',
|
||||||
|
'MTAS-GUARDIANACTIVE': 'yellow',
|
||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
7
src/app/display/shared/kanban-entry.model.ts
Normal file → Executable file
7
src/app/display/shared/kanban-entry.model.ts
Normal file → Executable file
@ -1,12 +1,13 @@
|
|||||||
import {JiraIssueType} from "./jira-issue-type.model";
|
import {JiraIssueType} from './jira-issue-type.model';
|
||||||
import {JiraStatus} from "./jira-status.model";
|
import {JiraStatus} from './jira-status.model';
|
||||||
import {JiraAssignee} from "./jira-assignee.model";
|
import {JiraAssignee} from './jira-assignee.model';
|
||||||
|
|
||||||
export class KanbanEntry {
|
export class KanbanEntry {
|
||||||
public id: number;
|
public id: number;
|
||||||
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 additionalAssignees: Array<JiraAssignee> = [];
|
||||||
|
|||||||
14
src/app/display/shared/kanban.service.ts
Normal file → Executable file
14
src/app/display/shared/kanban.service.ts
Normal file → Executable file
@ -1,10 +1,13 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { KanbanBoard } from './kanban-board.model';
|
import { KanbanBoard } from './kanban-board.model';
|
||||||
|
import { SettingsService } from '../../shared/service/settings.service';
|
||||||
|
import { TeamService } from '../../shared/service/team.service';
|
||||||
|
import { flatMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class KanbanService {
|
export class KanbanService {
|
||||||
@ -12,16 +15,21 @@ export class KanbanService {
|
|||||||
|
|
||||||
private cachedKanbanBoard: KanbanBoard = new KanbanBoard();
|
private cachedKanbanBoard: KanbanBoard = new KanbanBoard();
|
||||||
|
|
||||||
constructor(private httpService: HttpClient) {
|
constructor(private httpService: HttpClient,
|
||||||
|
private teamService: TeamService,
|
||||||
|
private settingService: SettingsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable instance to the kanban board api
|
* Returns an observable instance to the kanban board api
|
||||||
|
* Reloads team data before, to refresh team config
|
||||||
*
|
*
|
||||||
* @returns {Observable<KanbanBoard>}
|
* @returns {Observable<KanbanBoard>}
|
||||||
*/
|
*/
|
||||||
public getList(): Observable<KanbanBoard> {
|
public getList(): Observable<KanbanBoard> {
|
||||||
return this.httpService.get<KanbanBoard>(this.url);
|
return this.teamService.get(this.settingService.team.id).pipe(
|
||||||
|
flatMap(() => this.httpService.get<KanbanBoard>(`${this.url}/${this.settingService.team.id}`))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
20
src/app/display/slide-iframe/slide-iframe.component.css
Executable file
20
src/app/display/slide-iframe/slide-iframe.component.css
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 3px;
|
||||||
|
background: #222;
|
||||||
|
}
|
||||||
|
:host.preview {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
1
src/app/display/slide-iframe/slide-iframe.component.html
Executable file
1
src/app/display/slide-iframe/slide-iframe.component.html
Executable file
@ -0,0 +1 @@
|
|||||||
|
<iframe [src]="sanitizedUrl"></iframe>
|
||||||
25
src/app/display/slide-iframe/slide-iframe.component.spec.ts
Normal file
25
src/app/display/slide-iframe/slide-iframe.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SlideIframeComponent } from './slide-iframe.component';
|
||||||
|
|
||||||
|
describe('SlideIframeComponent', () => {
|
||||||
|
let component: SlideIframeComponent;
|
||||||
|
let fixture: ComponentFixture<SlideIframeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SlideIframeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SlideIframeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
29
src/app/display/slide-iframe/slide-iframe.component.ts
Executable file
29
src/app/display/slide-iframe/slide-iframe.component.ts
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
import {Component, EventEmitter, HostListener, Input, OnInit, Output} from '@angular/core';
|
||||||
|
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-slide-iframe',
|
||||||
|
templateUrl: './slide-iframe.component.html',
|
||||||
|
styleUrls: ['./slide-iframe.component.css']
|
||||||
|
})
|
||||||
|
export class SlideIframeComponent implements OnInit {
|
||||||
|
@Input() data = '';
|
||||||
|
@Input() preview = false;
|
||||||
|
|
||||||
|
@Input() visible = true;
|
||||||
|
@Output() visibleChange = new EventEmitter();
|
||||||
|
|
||||||
|
constructor(private sanitizer: DomSanitizer) {}
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
public hide() {
|
||||||
|
if (this.preview) {
|
||||||
|
this.visibleChange.emit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get sanitizedUrl(): SafeResourceUrl {
|
||||||
|
return this.sanitizer.bypassSecurityTrustResourceUrl(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/display/slide-show.service.ts
Normal file → Executable file
8
src/app/display/slide-show.service.ts
Normal file → Executable file
@ -19,9 +19,9 @@ export class SlideShowService {
|
|||||||
|
|
||||||
public nextSlide() {
|
public nextSlide() {
|
||||||
if (this.currentSlideIndex === this.slides.length - 1) {
|
if (this.currentSlideIndex === this.slides.length - 1) {
|
||||||
// this.currentSlideIndex++;
|
this.currentSlideIndex++;
|
||||||
// this.router.navigate(['/kanban']);
|
this.router.navigate(['/kanban']);
|
||||||
// } else if (this.currentSlideIndex === this.slides.length) {
|
} else if (this.currentSlideIndex === this.slides.length) {
|
||||||
this.currentSlideIndex = -1;
|
this.currentSlideIndex = -1;
|
||||||
this.reloadSlides();
|
this.reloadSlides();
|
||||||
this.router.navigate(['/commit-tracker']);
|
this.router.navigate(['/commit-tracker']);
|
||||||
@ -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.team === null || slide.team.id === team.id
|
slide => slide.teams === null || slide.teams.some(s => s.id === team.id) && slide.isVisible
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/app/display/slide-show/slide-show.component.html
Normal file → Executable file
3
src/app/display/slide-show/slide-show.component.html
Normal file → Executable file
@ -1 +1,2 @@
|
|||||||
<app-slide [data]="renderedSlide"></app-slide>
|
<app-slide *ngIf="isMarkdown" [data]="renderedSlide"></app-slide>
|
||||||
|
<app-slide-iframe *ngIf="isIframe" [data]="slide.slideUrl"></app-slide-iframe>
|
||||||
|
|||||||
18
src/app/display/slide-show/slide-show.component.ts
Normal file → Executable file
18
src/app/display/slide-show/slide-show.component.ts
Normal file → Executable file
@ -1,10 +1,10 @@
|
|||||||
import { Component, HostBinding, OnInit } from '@angular/core';
|
import {Component, HostBinding, OnInit} from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import { Title } from '@angular/platform-browser';
|
import {Title} from '@angular/platform-browser';
|
||||||
|
|
||||||
import * as marked from 'marked';
|
import * as marked from 'marked';
|
||||||
import { slideInOutAnimation } from '../../shared/slide-in-out-animation';
|
import {slideInOutAnimation} from '../../shared/slide-in-out-animation';
|
||||||
import { Slide } from '../../shared/slide';
|
import {Slide, SlideType} from '../../shared/slide';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-slide-show',
|
selector: 'app-slide-show',
|
||||||
@ -34,4 +34,12 @@ export class SlideShowComponent implements OnInit {
|
|||||||
get renderedSlide(): String {
|
get renderedSlide(): String {
|
||||||
return this.md.parse(this.slide.slideData);
|
return this.md.parse(this.slide.slideData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isMarkdown(): boolean {
|
||||||
|
return this.slide.type === SlideType.MarkDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isIframe(): boolean {
|
||||||
|
return this.slide.type === SlideType.IFrame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/app/display/slide/slide.component.ts
Normal file → Executable file
1
src/app/display/slide/slide.component.ts
Normal file → Executable file
@ -12,7 +12,6 @@ export class SlideComponent implements OnInit {
|
|||||||
@Input() visible = true;
|
@Input() visible = true;
|
||||||
@Output() visibleChange = new EventEmitter();
|
@Output() visibleChange = new EventEmitter();
|
||||||
|
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
|
|||||||
2
src/app/shared/service/commit-tracker.service.ts
Normal file → Executable file
2
src/app/shared/service/commit-tracker.service.ts
Normal file → Executable file
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { Commit } from '../commit';
|
import { Commit } from '../commit';
|
||||||
|
|||||||
2
src/app/shared/service/self-updater.service.ts
Normal file → Executable file
2
src/app/shared/service/self-updater.service.ts
Normal file → Executable file
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
3
src/app/shared/service/settings.service.ts
Normal file → Executable file
3
src/app/shared/service/settings.service.ts
Normal file → Executable file
@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { Subject } from 'rxjs/Subject';
|
|
||||||
import { Team } from '../team';
|
import { Team } from '../team';
|
||||||
|
|
||||||
const DEFAULT_SLIDE_INTERVAL = 30000;
|
const DEFAULT_SLIDE_INTERVAL = 30000;
|
||||||
|
|||||||
6
src/app/shared/service/slide.service.ts
Normal file → Executable file
6
src/app/shared/service/slide.service.ts
Normal file → Executable file
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { Slide } from '../slide';
|
import { Slide } from '../slide';
|
||||||
@ -18,9 +18,7 @@ export class SlideService implements Resolve<Array<Slide>>{
|
|||||||
private static prepareSlideData(slide: Slide) {
|
private static prepareSlideData(slide: Slide) {
|
||||||
const slideToSave = <any>Object.assign({}, slide);
|
const slideToSave = <any>Object.assign({}, slide);
|
||||||
try {
|
try {
|
||||||
slideToSave.team = slideToSave.team.id === null
|
slideToSave.teams = slide.teams.map(team => team.id);
|
||||||
? null
|
|
||||||
: slideToSave.team.id;
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return slideToSave;
|
return slideToSave;
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/app/shared/service/team.service.ts
Normal file → Executable file
20
src/app/shared/service/team.service.ts
Normal file → Executable file
@ -1,10 +1,12 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { Team } from '../team';
|
import { Team } from '../team';
|
||||||
|
import {SettingsService} from './settings.service';
|
||||||
|
import {map} from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TeamService implements Resolve<Array<Team>> {
|
export class TeamService implements Resolve<Array<Team>> {
|
||||||
@ -12,7 +14,8 @@ export class TeamService implements Resolve<Array<Team>> {
|
|||||||
private apiEndPoint = environment.apiUrl + '/api/team';
|
private apiEndPoint = environment.apiUrl + '/api/team';
|
||||||
private cachedTeams: Array<Team> = [];
|
private cachedTeams: Array<Team> = [];
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) { }
|
constructor(private httpClient: HttpClient,
|
||||||
|
private settingsService: SettingsService) { }
|
||||||
|
|
||||||
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Array<Team>> {
|
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Array<Team>> {
|
||||||
return this.list().toPromise();
|
return this.list().toPromise();
|
||||||
@ -22,6 +25,12 @@ export class TeamService implements Resolve<Array<Team>> {
|
|||||||
return this.httpClient.get<Array<Team>>(this.apiEndPoint);
|
return this.httpClient.get<Array<Team>>(this.apiEndPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get(id: number): Observable<Team> {
|
||||||
|
return this.httpClient.get<Team>(`${this.apiEndPoint}/${id}`).pipe(
|
||||||
|
map(team => this.updateSettingsWhenSelected(team))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public persist(team: Team): Observable<Team> {
|
public persist(team: Team): Observable<Team> {
|
||||||
return team.id === null
|
return team.id === null
|
||||||
? this.create(team)
|
? this.create(team)
|
||||||
@ -47,4 +56,11 @@ export class TeamService implements Resolve<Array<Team>> {
|
|||||||
set teams(teams: Array<Team>) {
|
set teams(teams: Array<Team>) {
|
||||||
this.cachedTeams = teams;
|
this.cachedTeams = teams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateSettingsWhenSelected(team: Team): Team {
|
||||||
|
if (this.settingsService.team.id === team.id) {
|
||||||
|
this.settingsService.team = team;
|
||||||
|
}
|
||||||
|
return team;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/app/shared/service/timer.service.ts
Normal file → Executable file
14
src/app/shared/service/timer.service.ts
Normal file → Executable file
@ -1,13 +1,10 @@
|
|||||||
import { Injectable, OnDestroy } from '@angular/core';
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
import { Subject } from 'rxjs/Subject';
|
|
||||||
import { SettingsService } from './settings.service';
|
import { SettingsService } from './settings.service';
|
||||||
import { SelfUpdaterService } from './self-updater.service';
|
import { SelfUpdaterService } from './self-updater.service';
|
||||||
import { TimerObservable } from 'rxjs/observable/TimerObservable';
|
import { Subject, timer, Subscription } from 'rxjs';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
|
||||||
import { ActivationStart, Router } from '@angular/router';
|
import { ActivationStart, Router } from '@angular/router';
|
||||||
import { SlideShowService } from '../../display/slide-show.service';
|
import { SlideShowService } from '../../display/slide-show.service';
|
||||||
import 'rxjs/add/operator/filter';
|
import { filter, switchMap } from 'rxjs/operators';
|
||||||
import 'rxjs/add/operator/switchMap';
|
|
||||||
|
|
||||||
const TIMER_UPDATE_POLL_INTERVAL = 30000;
|
const TIMER_UPDATE_POLL_INTERVAL = 30000;
|
||||||
|
|
||||||
@ -24,18 +21,19 @@ export class TimerService implements OnDestroy {
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private slideShowService: SlideShowService) {
|
private slideShowService: SlideShowService) {
|
||||||
|
|
||||||
const timerSUC = TimerObservable.create(TIMER_UPDATE_POLL_INTERVAL, TIMER_UPDATE_POLL_INTERVAL);
|
const timerSUC = timer(TIMER_UPDATE_POLL_INTERVAL, TIMER_UPDATE_POLL_INTERVAL);
|
||||||
this.selfUpdateCheckerTimer = timerSUC.subscribe(() => {
|
this.selfUpdateCheckerTimer = timerSUC.subscribe(() => {
|
||||||
this.selfUpdaterService.checkAndReloadIfNecessary();
|
this.selfUpdaterService.checkAndReloadIfNecessary();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.slideShowTimer = this.slideTimerSubject.switchMap((period: number) => TimerObservable.create(period))
|
this.slideShowTimer = this.slideTimerSubject
|
||||||
|
.pipe(switchMap((period: number) => timer(period)))
|
||||||
.subscribe(() => this.changeSlide());
|
.subscribe(() => this.changeSlide());
|
||||||
this.setSlideTimer(this.settings.slideInterval);
|
this.setSlideTimer(this.settings.slideInterval);
|
||||||
|
|
||||||
this.autoSwitch = false;
|
this.autoSwitch = false;
|
||||||
this.router.events
|
this.router.events
|
||||||
.filter(event => event instanceof ActivationStart)
|
.pipe(filter(event => event instanceof ActivationStart))
|
||||||
.subscribe((event: ActivationStart) => this.autoSwitch = !!event.snapshot.data.autoSwitchable);
|
.subscribe((event: ActivationStart) => this.autoSwitch = !!event.snapshot.data.autoSwitchable);
|
||||||
|
|
||||||
this.slideIntervalSubscription = this.settings.slideIntervalChanged.subscribe(
|
this.slideIntervalSubscription = this.settings.slideIntervalChanged.subscribe(
|
||||||
|
|||||||
23
src/app/shared/slide.ts
Normal file → Executable file
23
src/app/shared/slide.ts
Normal file → Executable file
@ -2,11 +2,24 @@ import { Team } from './team';
|
|||||||
|
|
||||||
export class Slide {
|
export class Slide {
|
||||||
id: number = null;
|
id: number = null;
|
||||||
title: String = '';
|
title = '';
|
||||||
team: Team = null;
|
type: SlideType = SlideType.MarkDown;
|
||||||
slideData: String = '';
|
visibility: SlideVisibility = SlideVisibility.Public;
|
||||||
|
teams: Array<Team> = [];
|
||||||
|
slideData = '';
|
||||||
|
slideUrl = '';
|
||||||
isVisible = true;
|
isVisible = true;
|
||||||
createdAt: String = null;
|
createdAt: string = null;
|
||||||
updatedAt: String = null;
|
updatedAt: string = null;
|
||||||
position = 0;
|
position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum SlideType {
|
||||||
|
MarkDown = 'markdown',
|
||||||
|
IFrame = 'iframe',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SlideVisibility {
|
||||||
|
Public = 'public',
|
||||||
|
Team = 'team',
|
||||||
|
}
|
||||||
|
|||||||
@ -10,7 +10,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"test.ts"
|
"test.ts",
|
||||||
|
"polyfills.ts"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.spec.ts",
|
"**/*.spec.ts",
|
||||||
|
|||||||
@ -14,6 +14,8 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"es2017",
|
"es2017",
|
||||||
"dom"
|
"dom"
|
||||||
]
|
],
|
||||||
|
"module": "es2015",
|
||||||
|
"baseUrl": "./"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18,7 +18,6 @@
|
|||||||
"forin": true,
|
"forin": true,
|
||||||
"import-blacklist": [
|
"import-blacklist": [
|
||||||
true,
|
true,
|
||||||
"rxjs",
|
|
||||||
"rxjs/Rx"
|
"rxjs/Rx"
|
||||||
],
|
],
|
||||||
"import-spacing": true,
|
"import-spacing": true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user