Upgrade to Angular 8 - CI build will fail

This commit is contained in:
Matthew Ross 2019-12-29 08:35:31 -05:00
parent 43abb1773e
commit 3dac31c7d3
84 changed files with 8868 additions and 5822 deletions

View File

@ -14,7 +14,7 @@
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"tsConfig": "tsconfig.app.json",
"showCircularDependencies": false,
"polyfills": "src/polyfills.ts",
"assets": [
@ -80,7 +80,7 @@
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"tsConfig": "tsconfig.spec.json",
"scripts": [],
"styles": [
"src/scss/main.scss"
@ -106,8 +106,8 @@
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
"tsconfig.app.json",
"tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
@ -115,29 +115,6 @@
}
}
}
},
"TaskBoard-e2e": {
"root": "",
"sourceRoot": "",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "TaskBoard:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "TaskBoard",
@ -148,11 +125,11 @@
},
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"prefix": "tb",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "app"
"prefix": "tb"
}
}
}

View File

@ -3,6 +3,8 @@
const path = require('path');
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function (config) {
config.set({
basePath: '',
@ -10,7 +12,6 @@ module.exports = function (config) {
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-phantomjs-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
@ -20,17 +21,16 @@ module.exports = function (config) {
},
coverageIstanbulReporter: {
dir: path.join(__dirname, 'coverage/app'),
reports: ['html'],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'], // ['Chrome'],
singleRun: false
browsers: ['ChromeHeadless'],
singleRun: false,
restartOnFileChange: true
});
};

11385
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -40,53 +40,56 @@
"postinstall": "cd src/api/ && composer update && composer install --optimize-autoloader && cd ../../"
},
"dependencies": {
"@angular/animations": "^6.1.8",
"@angular/common": "^6.1.8",
"@angular/compiler": "^6.1.8",
"@angular/core": "^6.1.8",
"@angular/forms": "^6.1.8",
"@angular/http": "^6.1.8",
"@angular/platform-browser": "^6.1.8",
"@angular/platform-browser-dynamic": "^6.1.8",
"@angular/router": "^6.1.8",
"chartist": "^0.11.0",
"@angular/animations": "^8.2.14",
"@angular/common": "^8.2.14",
"@angular/compiler": "^8.2.14",
"@angular/core": "^8.2.14",
"@angular/forms": "^8.2.14",
"@angular/platform-browser": "^8.2.14",
"@angular/platform-browser-dynamic": "^8.2.14",
"@angular/router": "^8.2.14",
"ajv": "^6.10.2",
"chartist": "^0.11.4",
"chartist-plugin-tooltips": "^0.0.17",
"classlist.js": "^1.1.20150312",
"core-js": "^2.5.7",
"core-js": "^3.6.1",
"dragula": "^3.7.2",
"highlight.js": "^9.12.0",
"marked": "^0.5.1",
"ng2-dragula": "^2.1.0",
"node-sass": "^4.9.3",
"rxjs": "^6.3.3",
"highlight.js": "^9.17.1",
"marked": "^0.8.0",
"ng2-dragula": "^2.1.1",
"node-sass": "^4.13.0",
"rxjs": "^6.5.4",
"scss-base": "^1.4.0",
"zone.js": "^0.8.26"
"zone.js": "^0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.8.3",
"@angular/cli": "^6.2.3",
"@angular/compiler-cli": "^6.1.8",
"@angular/language-service": "^6.1.8",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~10.11.0",
"bourbon": "5.1.0",
"bourbon-neat": "1.9.1",
"codelyzer": "^4.4.4",
"jasmine": "^3.2.0",
"jasmine-core": "^3.2.1",
"@angular-devkit/build-angular": "^0.803.21",
"@angular/cli": "^8.3.21",
"@angular/compiler-cli": "^8.2.14",
"@angular/language-service": "^8.2.14",
"@types/chartist": "^0.9.46",
"@types/highlight.js": "^9.12.3",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.8",
"@types/marked": "^0.7.2",
"@types/node": "^12.12.22",
"bourbon": "6.0.0",
"bourbon-neat": "4.0.0",
"codelyzer": "^5.2.1",
"jasmine": "^3.5.0",
"jasmine-core": "^3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^2.0.4",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^1.3.1",
"karma-phantomjs-launcher": "^1.0.4",
"nodemon": "^1.18.4",
"npm-run-all": "^4.1.3",
"protractor": "~5.4.1",
"ts-node": "~7.0.1",
"tslint": "~5.11.0",
"typescript": "^2.9.2"
"karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.1",
"nodemon": "^2.0.2",
"npm-run-all": "^4.1.5",
"protractor": "~5.4.2",
"puppeteer": "^2.0.0",
"ts-node": "^8.5.4",
"tslint": "~5.20.1",
"typescript": "^3.5.3"
}
}

403
src/api/composer.lock generated
View File

@ -6,60 +6,31 @@
],
"content-hash": "cdb39721e83209f8f5487c1b6b7fafc9",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "doctrine/instantiator",
"version": "1.1.0",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda"
"reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
"reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
"reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"doctrine/coding-standard": "^6.0",
"ext-pdo": "*",
"ext-phar": "*",
"phpunit/phpunit": "^6.2.3",
"squizlabs/php_codesniffer": "^3.0.2"
"phpbench/phpbench": "^0.13",
"phpstan/phpstan-phpunit": "^0.11",
"phpstan/phpstan-shim": "^0.11",
"phpunit/phpunit": "^7.0"
},
"type": "library",
"extra": {
@ -84,12 +55,12 @@
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://github.com/doctrine/instantiator",
"homepage": "https://www.doctrine-project.org/projects/instantiator.html",
"keywords": [
"constructor",
"instantiate"
],
"time": "2017-07-22T11:58:36+00:00"
"time": "2019-10-21T16:45:58+00:00"
},
{
"name": "firebase/php-jwt",
@ -139,16 +110,16 @@
},
{
"name": "gabordemooij/redbean",
"version": "v5.1",
"version": "v5.4.1",
"source": {
"type": "git",
"url": "https://github.com/gabordemooij/redbean.git",
"reference": "a02e58ad5519149f572559a8293b5dffea385956"
"reference": "cf1c4e63e64d7d34f88e7ebe77cb0d10f68fb9be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/gabordemooij/redbean/zipball/a02e58ad5519149f572559a8293b5dffea385956",
"reference": "a02e58ad5519149f572559a8293b5dffea385956",
"url": "https://api.github.com/repos/gabordemooij/redbean/zipball/cf1c4e63e64d7d34f88e7ebe77cb0d10f68fb9be",
"reference": "cf1c4e63e64d7d34f88e7ebe77cb0d10f68fb9be",
"shasum": ""
},
"require": {
@ -162,7 +133,7 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"New BSD and GPLv2"
"BSD-3-Clause"
],
"authors": [
{
@ -176,20 +147,20 @@
"keywords": [
"orm"
],
"time": "2018-04-01T11:51:37+00:00"
"time": "2019-11-30T16:55:12+00:00"
},
{
"name": "monolog/monolog",
"version": "1.23.0",
"version": "1.25.2",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
"reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287",
"reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287",
"shasum": ""
},
"require": {
@ -254,20 +225,20 @@
"logging",
"psr-3"
],
"time": "2017-06-19T01:22:40+00:00"
"time": "2019-11-13T10:00:05+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.8.1",
"version": "1.9.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
"reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7",
"reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7",
"shasum": ""
},
"require": {
@ -302,24 +273,25 @@
"object",
"object graph"
],
"time": "2018-06-11T23:09:50+00:00"
"time": "2019-12-15T19:12:40+00:00"
},
{
"name": "myclabs/php-enum",
"version": "1.6.1",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "8c5649e4ed99acd53a40eada270154dcac089f7e"
"reference": "45f01adf6922df6082bcda36619deb466e826acf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/8c5649e4ed99acd53a40eada270154dcac089f7e",
"reference": "8c5649e4ed99acd53a40eada270154dcac089f7e",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/45f01adf6922df6082bcda36619deb466e826acf",
"reference": "45f01adf6922df6082bcda36619deb466e826acf",
"shasum": ""
},
"require": {
"php": ">=5.4"
"ext-json": "*",
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|^5.7|^6.0",
@ -346,7 +318,7 @@
"keywords": [
"enum"
],
"time": "2018-05-09T08:09:15+00:00"
"time": "2019-08-19T13:53:00+00:00"
},
{
"name": "nikic/fast-route",
@ -498,35 +470,33 @@
},
{
"name": "phpdocumentor/reflection-common",
"version": "1.0.1",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
"shasum": ""
},
"require": {
"php": ">=5.5"
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^4.6"
"phpunit/phpunit": "~6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src"
]
"phpDocumentor\\Reflection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -548,30 +518,30 @@
"reflection",
"static analysis"
],
"time": "2017-09-11T18:02:19+00:00"
"time": "2018-08-07T13:53:10+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "4.3.0",
"version": "4.3.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08"
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"shasum": ""
},
"require": {
"php": "^7.0",
"phpdocumentor/reflection-common": "^1.0.0",
"phpdocumentor/type-resolver": "^0.4.0",
"phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
"phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
"doctrine/instantiator": "~1.0.5",
"doctrine/instantiator": "^1.0.5",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^6.4"
},
@ -599,41 +569,40 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2017-11-30T07:14:17+00:00"
"time": "2019-09-12T14:27:41+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "0.4.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
"reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
"reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
"shasum": ""
},
"require": {
"php": "^5.5 || ^7.0",
"phpdocumentor/reflection-common": "^1.0"
"php": "^7.1",
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.2||^4.8.24"
"ext-tokenizer": "^7.1",
"mockery/mockery": "~1",
"phpunit/phpunit": "^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/"
]
"phpDocumentor\\Reflection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -646,20 +615,21 @@
"email": "me@mikevanriel.com"
}
],
"time": "2017-07-14T14:27:02+00:00"
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"time": "2019-08-22T18:11:29+00:00"
},
{
"name": "phpmailer/phpmailer",
"version": "v6.0.5",
"version": "v6.1.4",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32"
"reference": "c5e61d0729507049cec9673aa1a679f9adefd683"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/cb3ea134d4d3729e7857737d5f320cce9caf4d32",
"reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c5e61d0729507049cec9673aa1a679f9adefd683",
"reference": "c5e61d0729507049cec9673aa1a679f9adefd683",
"shasum": ""
},
"require": {
@ -668,13 +638,9 @@
"php": ">=5.5.0"
},
"require-dev": {
"doctrine/annotations": "1.2.*",
"doctrine/annotations": "^1.2",
"friendsofphp/php-cs-fixer": "^2.2",
"phpdocumentor/phpdocumentor": "2.*",
"phpunit/phpunit": "^4.8 || ^5.7",
"zendframework/zend-eventmanager": "3.0.*",
"zendframework/zend-i18n": "2.7.3",
"zendframework/zend-serializer": "2.7.*"
"phpunit/phpunit": "^4.8 || ^5.7"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset",
@ -692,17 +658,17 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
"LGPL-2.1-only"
],
"authors": [
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
@ -712,42 +678,42 @@
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2018-03-27T13:49:45+00:00"
"time": "2019-12-10T11:17:38+00:00"
},
{
"name": "phpspec/prophecy",
"version": "1.7.6",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712"
"reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712",
"reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d638ebbb58daba25a6a0dc7969e1358a0e3c6682",
"reference": "d638ebbb58daba25a6a0dc7969e1358a0e3c6682",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
"sebastian/comparator": "^1.1|^2.0|^3.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
"sebastian/comparator": "^1.2.3|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
"phpspec/phpspec": "^2.5 || ^3.2",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7.x-dev"
"dev-master": "1.10.x-dev"
}
},
"autoload": {
"psr-0": {
"Prophecy\\": "src/"
"psr-4": {
"Prophecy\\": "src/Prophecy"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -775,7 +741,7 @@
"spy",
"stub"
],
"time": "2018-04-18T13:57:24+00:00"
"time": "2019-12-17T16:54:23+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1028,16 +994,16 @@
},
{
"name": "phpunit/phpunit",
"version": "6.5.9",
"version": "6.5.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f"
"reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f",
"reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7",
"reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7",
"shasum": ""
},
"require": {
@ -1055,7 +1021,7 @@
"phpunit/php-file-iterator": "^1.4.3",
"phpunit/php-text-template": "^1.2.1",
"phpunit/php-timer": "^1.0.9",
"phpunit/phpunit-mock-objects": "^5.0.5",
"phpunit/phpunit-mock-objects": "^5.0.9",
"sebastian/comparator": "^2.1",
"sebastian/diff": "^2.0",
"sebastian/environment": "^3.1",
@ -1108,20 +1074,20 @@
"testing",
"xunit"
],
"time": "2018-07-03T06:40:40+00:00"
"time": "2019-02-01T05:22:47+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "5.0.7",
"version": "5.0.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce"
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3eaf040f20154d27d6da59ca2c6e28ac8fd56dce",
"reference": "3eaf040f20154d27d6da59ca2c6e28ac8fd56dce",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
"reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
"shasum": ""
},
"require": {
@ -1134,7 +1100,7 @@
"phpunit/phpunit": "<6.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5"
"phpunit/phpunit": "^6.5.11"
},
"suggest": {
"ext-soap": "*"
@ -1167,7 +1133,8 @@
"mock",
"xunit"
],
"time": "2018-05-29T13:50:43+00:00"
"abandoned": true,
"time": "2018-08-09T05:50:03+00:00"
},
{
"name": "pimple/pimple",
@ -1320,16 +1287,16 @@
},
{
"name": "psr/log",
"version": "1.0.2",
"version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
"shasum": ""
},
"require": {
@ -1338,7 +1305,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.1.x-dev"
}
},
"autoload": {
@ -1363,7 +1330,7 @@
"psr",
"psr-3"
],
"time": "2016-10-10T12:19:37+00:00"
"time": "2019-11-01T11:05:21+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -1578,16 +1545,16 @@
},
{
"name": "sebastian/exporter",
"version": "3.1.0",
"version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
"reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
"shasum": ""
},
"require": {
@ -1614,6 +1581,10 @@
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
@ -1622,17 +1593,13 @@
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
@ -1641,7 +1608,7 @@
"export",
"exporter"
],
"time": "2017-04-03T13:19:02+00:00"
"time": "2019-09-14T09:02:43+00:00"
},
{
"name": "sebastian/global-state",
@ -1926,20 +1893,22 @@
},
{
"name": "slim/slim",
"version": "3.10.0",
"version": "3.12.3",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748"
"reference": "1c9318a84ffb890900901136d620b4f03a59da38"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
"reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/1c9318a84ffb890900901136d620b4f03a59da38",
"reference": "1c9318a84ffb890900901136d620b4f03a59da38",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"ext-json": "*",
"ext-libxml": "*",
"ext-simplexml": "*",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
@ -1964,25 +1933,25 @@
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
},
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
@ -1993,20 +1962,78 @@
"micro",
"router"
],
"time": "2018-04-19T19:29:08+00:00"
"time": "2019-11-28T17:40:33+00:00"
},
{
"name": "theseer/tokenizer",
"version": "1.1.0",
"name": "symfony/polyfill-ctype",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b"
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b",
"reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "theseer/tokenizer",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
"reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
"shasum": ""
},
"require": {
@ -2033,35 +2060,33 @@
}
],
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"time": "2017-04-07T12:08:54+00:00"
"time": "2019-06-13T22:48:21+00:00"
},
{
"name": "webmozart/assert",
"version": "1.3.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "0df1908962e7a3071564e857d86874dad1ef204a"
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
"reference": "0df1908962e7a3071564e857d86874dad1ef204a",
"url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925",
"reference": "573381c0a64f155a0d9a23f4b0c797194805b925",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0"
"php": "^5.3.3 || ^7.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"vimeo/psalm": "<3.6.0"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
"sebastian/version": "^1.0.1"
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
@ -2083,7 +2108,7 @@
"check",
"validate"
],
"time": "2018-01-29T19:49:41+00:00"
"time": "2019-11-24T13:36:37+00:00"
}
],
"packages-dev": [],

View File

@ -98,6 +98,15 @@ class Boards extends BaseController {
}
$data = json_decode($request->getBody());
if (!property_exists($args, 'id')) {
$this->logger->addError('Update Board: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$board = R::load('board', (int)$args['id']);
if (!$this->checkBoardAccess($board->id, $request)) {

View File

@ -1,13 +1,11 @@
import { Injectable } from '@angular/core';
import {
HttpErrorResponse,
HttpHeaderResponse,
HttpResponseBase,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse
} from '@angular/common/http';
import { Router } from '@angular/router';
@ -29,6 +27,7 @@ export class ApiInterceptor implements HttpInterceptor {
const token = sessionStorage.getItem(this.JWT_KEY);
if (token !== null) {
// tslint:disable-next-line
headers['Authorization'] = token;
}

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-component',
selector: 'tb-app-component',
templateUrl: './app.component.html'
})
export class AppComponent {

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { BrowserModule, Title } from '@angular/platform-browser';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';
@ -10,7 +10,7 @@ import { ROUTES } from './app.routes';
import { AppComponent } from './app.component';
import { ApiInterceptor } from './app.api-http';
import { Login } from './login/login.component';
import { LoginComponent } from './login/login.component';
import { BoardModule } from './board/board.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { SettingsModule } from './settings/settings.module';
@ -28,18 +28,19 @@ import { SharedModule } from './shared/shared.module';
SharedModule,
RouterModule.forRoot(ROUTES)
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,
multi: true
}
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,
multi: true
}],
declarations: [
AppComponent,
Login
LoginComponent
],
bootstrap: [ AppComponent ]
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,34 +1,34 @@
import { Routes } from '@angular/router';
import { BoardDisplay } from './board/board.component';
import { Login } from './login/login.component';
import { Settings } from './settings/settings.component';
import { Dashboard } from './dashboard/dashboard.component';
import { BoardDisplayComponent } from './board/board.component';
import { LoginComponent } from './login/login.component';
import { SettingsComponent } from './settings/settings.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AuthGuard } from './shared/auth/auth.guard';
export const ROUTES: Routes = [
{
path: '',
component: Login
component: LoginComponent
},
{
path: 'boards',
component: BoardDisplay,
component: BoardDisplayComponent,
canActivate: [ AuthGuard ]
},
{
path: 'boards/:id',
component: BoardDisplay,
component: BoardDisplayComponent,
canActivate: [ AuthGuard ]
},
{
path: 'settings',
component: Settings,
component: SettingsComponent,
canActivate: [ AuthGuard ]
},
{
path: 'dashboard',
component: Dashboard,
component: DashboardComponent,
canActivate: [ AuthGuard ]
}
];

View File

@ -23,7 +23,7 @@
<label>
{{ strings['boards_userFilter'] }}:
<select [(ngModel)]="userFilter"
(change)="filterTasks('user')">
(change)="filterTasks()">
<option [ngValue]="null">
{{ strings['boards_filterByAny'] }}
</option>
@ -39,7 +39,7 @@
<label>
{{ strings['boards_categoryFilter'] }}:
<select [(ngModel)]="categoryFilter"
(change)="filterTasks('cat')">
(change)="filterTasks()">
<option [ngValue]="null">
{{ strings['boards_filterByAny'] }}
</option>

View File

@ -8,9 +8,7 @@ import {
ApiResponse,
Board,
Column,
Task,
User,
Notification,
} from '../shared/models';
import {
AuthService,
@ -24,12 +22,8 @@ import { BoardService } from './board.service';
selector: 'tb-board',
templateUrl: './board.component.html'
})
export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
private strings: any;
private noBoardsMessage: string;
private subs: Array<any>;
private hideFiltered: boolean;
export class BoardDisplayComponent implements OnInit, OnDestroy, AfterContentInit {
private subs: any[];
private everyOtherDrop: boolean;
public categoryFilter: number;
@ -38,19 +32,24 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
public activeUser: User;
public activeBoard: Board;
public boards: Array<Board>;
public boards: Board[];
public pageName: string;
public noBoardsMessage: string;
public strings: any;
public loading: boolean;
public hideFiltered: boolean;
constructor(public title: Title,
private router: Router,
private active: ActivatedRoute,
public active: ActivatedRoute,
public auth: AuthService,
public boardService: BoardService,
private menuService: ContextMenuService,
private notes: NotificationsService,
private stringsService: StringsService,
public menuService: ContextMenuService,
public notes: NotificationsService,
public stringsService: StringsService,
public dragula: DragulaService) {
title.setTitle('TaskBoard - Kanban App');
@ -97,7 +96,7 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
this.subs.push(sub);
sub = active.params.subscribe(params => {
let id = +params.id;
const id = +params.id;
this.boardNavId = id ? id : null;
this.updateActiveBoard();
@ -121,22 +120,22 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
}
ngAfterContentInit() {
let bag = this.dragula.find('tasks-bag');
const bag = this.dragula.find('tasks-bag');
if (bag) {
this.dragula.destroy('tasks-bag');
}
this.dragula.createGroup('tasks-bag', <any>{
moves: (el: any, container: any, handle: any) => {
this.dragula.createGroup('tasks-bag', {
moves: (_: any, __: any, handle: any) => {
return handle.classList.contains('drag-handle');
}
});
this.dragula.dropModel('tasks-bag').subscribe((value: any) => {
let taskId = +value[1].id,
toColumnId = +value[2].parentNode.id,
fromColumnId = +value[3].parentNode.id;
const taskId = +value[1].id;
const toColumnId = +value[2].parentNode.id;
const fromColumnId = +value[3].parentNode.id;
if (toColumnId !== fromColumnId) {
this.changeTaskColumn(taskId, toColumnId);
@ -145,8 +144,8 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
this.everyOtherDrop = !this.everyOtherDrop;
if (this.everyOtherDrop) {
for (var i = 0, len = this.activeBoard.columns.length; i < len; ++i) {
let column = this.activeBoard.columns[i];
for (let i = 0, len = this.activeBoard.columns.length; i < len; ++i) {
const column = this.activeBoard.columns[i];
if (column.id === toColumnId) {
let pos = 0;
@ -224,7 +223,7 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
});
}
private updateBoards(): void {
updateBoards(): void {
this.boardService.getBoards().subscribe((response: ApiResponse) => {
this.boards = [];
if (response.data.length > 1) {
@ -235,11 +234,11 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
}
private updateBoardsList(boards: Array<any>): void {
let activeBoards: Array<Board> = [];
const activeBoards: Array<Board> = [];
if (boards) {
boards.forEach((board: any) => {
let currentBoard = new Board(+board.id, board.name,
const currentBoard = new Board(+board.id, board.name,
board.is_active === '1',
board.ownColumn,
board.ownCategory,
@ -300,9 +299,9 @@ export class BoardDisplay implements OnInit, OnDestroy, AfterContentInit {
}
private changeTaskColumn(taskId: number, toColumnId: number) {
let column = this.activeBoard.columns
const column = this.activeBoard.columns
.find(col => col.id === toColumnId);
let task = column.tasks.find(ta => ta.id === taskId);
const task = column.tasks.find(ta => ta.id === taskId);
if (task) {
task.column_id = toColumnId;

View File

@ -6,29 +6,29 @@ import { RouterModule } from '@angular/router';
import { DragulaModule } from 'ng2-dragula/dist';
import { SharedModule } from '../shared/shared.module';
import { BoardDisplay } from './board.component';
import { ColumnDisplay } from './column/column.component';
import { TaskDisplay } from './task/task.component';
import { BoardDisplayComponent } from './board.component';
import { ColumnDisplayComponent } from './column/column.component';
import { TaskDisplayComponent } from './task/task.component';
import { BoardService } from './board.service';
@NgModule({
imports: [
CommonModule,
DragulaModule,
DragulaModule.forRoot(),
FormsModule,
RouterModule,
SharedModule
],
declarations: [
BoardDisplay,
ColumnDisplay,
TaskDisplay
BoardDisplayComponent,
ColumnDisplayComponent,
TaskDisplayComponent
],
providers: [ BoardService ],
exports: [
BoardDisplay,
ColumnDisplay,
TaskDisplay
BoardDisplayComponent,
ColumnDisplayComponent,
TaskDisplayComponent,
]
})
export class BoardModule { }

View File

@ -13,7 +13,6 @@ import {
Attachment,
Comment,
Task,
User
} from '../shared/models';
interface MarkedReturn {
@ -26,12 +25,8 @@ export class BoardService {
private checkCounts = {
total: 0,
complete: 0
}
private activeBoard = new BehaviorSubject<Board>(null);
private defaultCallback = (err: any, text: string) => {
console.log('default', err, text);
return text;
};
private activeBoard = new BehaviorSubject<Board>(null);
public activeBoardChanged = this.activeBoard.asObservable();
@ -43,7 +38,7 @@ export class BoardService {
this.checkCounts.total = 0;
this.checkCounts.complete = 0;
let retVal: MarkedReturn = { html: '', counts: {} };
const retVal: MarkedReturn = { html: '', counts: {} };
retVal.html = marked(markdown, callback);
@ -55,99 +50,99 @@ export class BoardService {
}
updateActiveBoard(board: Board): void {
let newBoard = this.convertBoardData(board);
const newBoard = this.convertBoardData(board);
this.activeBoard.next(newBoard);
}
getBoards(): Observable<ApiResponse> {
return this.http.get('api/boards')
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
toggleCollapsed(userId: number, columnId: number): Observable<ApiResponse> {
return this.http.post('api/users/' + userId + '/cols', { id: columnId })
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
updateBoard(board: Board): Observable<ApiResponse> {
return this.http.post('api/boards/' + board.id, board)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
updateColumn(column: Column): Observable<ApiResponse> {
return this.http.post('api/columns/' + column.id, column)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
addTask(task: Task): Observable<ApiResponse> {
return this.http.post('api/tasks', task)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
updateTask(task: Task): Observable<ApiResponse> {
return this.http.post('api/tasks/' + task.id, task)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
removeTask(taskId: number): Observable<ApiResponse> {
return this.http.delete('api/tasks/' + taskId)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
getTaskActivity(taskId: number): Observable<ApiResponse> {
return this.http.get('api/activity/task/' + taskId)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
updateComment(comment: Comment): Observable<ApiResponse> {
return this.http.post('api/comments/' + comment.id, comment)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
removeComment(commentId: number): Observable<ApiResponse> {
return this.http.delete('api/comments/' + commentId)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
uploadAttachment(attachment: Attachment, data: FormData): Observable<ApiResponse> {
let headers = new HttpHeaders();
let options = { headers: headers, params: new HttpParams() };
const headers = new HttpHeaders();
const options = { headers, params: new HttpParams() };
options.params.set('attachment', JSON.stringify(attachment));
return this.http.post('api/attachments', data, options)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
@ -155,6 +150,11 @@ export class BoardService {
this.http.post('api/refresh', {}).subscribe();
}
private defaultCallback = (err: any, text: string) => {
console.log('default', err, text);
return text;
}
private convertBoardData(boardData: any): Board {
if (boardData instanceof Board) {
return boardData;
@ -170,10 +170,10 @@ export class BoardService {
}
private initMarked(): void {
let renderer = new marked.Renderer();
const renderer = new marked.Renderer();
renderer.checkbox = isChecked => {
let text = '<i class="icon icon-check' + (isChecked ? '' : '-empty') + '"></i>';
const text = '<i class="icon icon-check' + (isChecked ? '' : '-empty') + '"></i>';
this.checkCounts.total += 1;

View File

@ -158,7 +158,7 @@
<div>
<h3>{{ strings['boards_taskAddAttachment'] }}</h3>
<input type="file" #fileupload (change)="fileChange(fileupload.files[0])">
<button (click)="uploadFile()">
<button (click)="uploadFile()" [disabled]="!fileupload.files[0]">
<i class="icon icon-upload"></i>
{{ strings['boards_taskUpload'] }}
</button>
@ -220,7 +220,7 @@
*ngIf="showActivity">
<div class="title">
<h2>
Task Activity
{{ strings['boards_taskActivity'] }}
<span class="right">
<i class="icon icon-angle-double-down"
*ngIf="!collapseActivity"

View File

@ -7,7 +7,7 @@ import {
OnDestroy,
Output
} from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { DomSanitizer, } from '@angular/platform-browser';
import {
ApiResponse,
@ -34,28 +34,30 @@ import { BoardService } from '../board.service';
selector: 'tb-column',
templateUrl: './column.component.html'
})
export class ColumnDisplay implements OnInit, OnDestroy {
private tasks: Array<Task>;
private viewTaskActivities: Array<ActivitySimple>;
private MODAL_ID: string;
private MODAL_VIEW_ID: string;
export class ColumnDisplayComponent implements OnInit, OnDestroy {
private fileUpload: any;
private subs = [];
public tasks: Task[];
public viewTaskActivities: ActivitySimple[];
public showActivity: boolean;
public collapseActivity: boolean;
public isOverdue: boolean;
public isNearlyDue: boolean;
public showLimitEditor: boolean;
public collapseTasks: boolean;
public saving: boolean;
public taskLimit: number;
public taskToRemove: number;
public newComment: string;
public sortOption: string;
public templateElement: any;
public strings: any;
public collapseTasks: boolean;
public sortOption: string;
public saving: boolean;
public taskToRemove: number;
public commentEdit: Comment;
public commentToRemove: Comment;
@ -66,20 +68,25 @@ export class ColumnDisplay implements OnInit, OnDestroy {
public activeBoard: Board;
public quickAdd: Task;
public MODAL_ID: string;
public MODAL_VIEW_ID: string;
public MODAL_CONFIRM_ID: string;
public MODAL_CONFIRM_COMMENT_ID: string;
// tslint:disable-next-line
@Input('column') columnData: Column;
// tslint:disable-next-line
@Input('boards') boards: Array<Board>;
// tslint:disable-next-line
@Output('on-update-boards')
onUpdateBoards: EventEmitter<any> = new EventEmitter<any>();
constructor(private elRef: ElementRef,
constructor(public elRef: ElementRef,
private auth: AuthService,
private notes: NotificationsService,
public modal: ModalService,
private stringsService: StringsService,
public stringsService: StringsService,
public boardService: BoardService,
private sanitizer: DomSanitizer) {
this.templateElement = elRef.nativeElement;
@ -220,7 +227,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
? this.columnData.id + ''
: ''));
let boardData = response.data[2][0];
const boardData = response.data[2][0];
boardData.ownColumn.forEach((column: any) => {
if (!column.ownTask) {
column.ownTask = [];
@ -284,10 +291,10 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let formData = new FormData();
const formData = new FormData();
formData.append('file', this.fileUpload);
let attachment = new Attachment();
const attachment = new Attachment();
attachment.filename = this.fileUpload.name;
attachment.name = attachment.filename.split('.')[0];
attachment.type = this.fileUpload.type;
@ -317,7 +324,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let updatedTask = response.data[1][0];
const updatedTask = response.data[1][0];
this.replaceUpdatedTask(updatedTask);
this.viewModalProps = this.convertToTask(updatedTask);
@ -340,7 +347,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let updatedTask = response.data[1][0];
const updatedTask = response.data[1][0];
this.replaceUpdatedTask(updatedTask);
this.viewModalProps = this.convertToTask(updatedTask);
@ -358,7 +365,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
.subscribe((response: ApiResponse) => {
response.alerts.forEach(note => this.notes.add(note));
let updatedTask = response.data[1][0];
const updatedTask = response.data[1][0];
this.replaceUpdatedTask(updatedTask);
});
}
@ -381,7 +388,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
}
saveLimitChanges() {
let originalLimit = this.columnData.task_limit;
const originalLimit = this.columnData.task_limit;
this.columnData.task_limit = this.taskLimit;
@ -394,7 +401,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let colData = response.data[1][0];
const colData = response.data[1][0];
this.columnData = new Column(colData.id,
colData.name,
colData.position,
@ -408,10 +415,10 @@ export class ColumnDisplay implements OnInit, OnDestroy {
// Expects a color in full HEX with leading #, e.g. #ffffe0
getTextColor(color: string): string {
let r = parseInt(color.substr(1, 2), 16),
g = parseInt(color.substr(3, 2), 16),
b = parseInt(color.substr(5, 2), 16),
yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
const r = parseInt(color.substr(1, 2), 16);
const g = parseInt(color.substr(3, 2), 16);
const b = parseInt(color.substr(5, 2), 16);
const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return yiq >= 140 ? '#333333' : '#efefef';
}
@ -435,16 +442,16 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let dueDate = new Date(this.viewModalProps.due_date);
const dueDate = new Date(this.viewModalProps.due_date);
if (isNaN(dueDate.valueOf())) {
return;
}
let millisecondsPerDay = (1000 * 3600 * 24),
today = new Date(),
timeDiff = today.getTime() - dueDate.getTime(),
daysDiff = Math.ceil(timeDiff / millisecondsPerDay);
const millisecondsPerDay = (1000 * 3600 * 24);
const today = new Date();
const timeDiff = today.getTime() - dueDate.getTime();
const daysDiff = Math.ceil(timeDiff / millisecondsPerDay);
if (daysDiff > 0) {
// past due date
@ -456,18 +463,18 @@ export class ColumnDisplay implements OnInit, OnDestroy {
}
}
getRemoveTaskFunction(taskId: number): Function {
getRemoveTaskFunction(taskId: number): () => void {
return () => {
this.taskToRemove = taskId;
this.modal.open(this.MODAL_CONFIRM_ID + this.columnData.id);
};
}
getShowModalFunction(taskId: number = 0): Function {
getShowModalFunction(taskId: number = 0): () => void {
return () => { this.showModal(taskId); };
}
getShowViewModalFunction(taskId: number): Function {
getShowViewModalFunction(taskId: number): () => void {
return () => { this.showViewModal(taskId); };
}
@ -480,7 +487,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return;
}
let editTask = this.columnData.tasks.find(task => task.id === taskId);
const editTask = this.columnData.tasks.find(task => task.id === taskId);
this.modalProps = new Task(editTask.id, editTask.title,
editTask.description, editTask.color,
@ -508,8 +515,53 @@ export class ColumnDisplay implements OnInit, OnDestroy {
this.modal.open(this.MODAL_ID + this.columnData.id);
}
getComment(text: string) {
const data = this.boardService.convertMarkdown(text, this.markedCallback);
return this.sanitizer.bypassSecurityTrustHtml(data.html);
}
getUserName(userId: number) {
const user = this.activeBoard.users.find((test: User) => test.id === userId);
return user.username;
}
callBoardUpdate() {
this.onUpdateBoards.emit();
}
showViewModal(taskId: number) {
const viewTask = this.columnData.tasks.find(task => task.id === taskId);
this.viewTaskActivities = [];
this.boardService.getTaskActivity(viewTask.id)
.subscribe(response => {
response.data[1].forEach((item: any) => {
this.viewTaskActivities.push(
new ActivitySimple(item.text, item.timestamp));
});
});
this.newComment = '';
this.viewModalProps = this.convertToTask(viewTask);
this.checkDueDate();
if (this.showActivity) {
this.showActivity = false;
setTimeout(() => (this.showActivity = true), 500);
}
this.modal.open(this.MODAL_VIEW_ID + this.columnData.id);
}
preventEnter(event: any) {
if (event && event.stopPropagation) {
event.stopPropagation();
}
}
private convertToTask(updatedTask: any) {
let task = new Task(updatedTask.id,
const task = new Task(updatedTask.id,
updatedTask.title,
updatedTask.description,
updatedTask.color,
@ -522,7 +574,7 @@ export class ColumnDisplay implements OnInit, OnDestroy {
updatedTask.sharedUser,
updatedTask.sharedCategory);
const data = this.boardService.convertMarkdown(task.description,
(_, text) => { return text; }, true);
(_, text) => text, true);
task.html = data.html;
@ -557,14 +609,14 @@ export class ColumnDisplay implements OnInit, OnDestroy {
}
// Needs anonymous function for proper `this` context.
private markedCallback = (error: any, text: string) => {
private markedCallback = (_: any, text: string) => {
this.activeBoard.issue_trackers.forEach(tracker => {
let re = new RegExp(tracker.regex, 'ig');
let replacements = new Array<any>();
const re = new RegExp(tracker.regex, 'ig');
const replacements = new Array<any>();
let result = re.exec(text);
while (result !== null) {
let link = '<a href="' +
const link = '<a href="' +
tracker.url.replace(/%BUGID%/g, result[1]) +
'" target="tb_external" rel="noreferrer">' +
result[0] + '</a>';
@ -585,17 +637,6 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return text;
}
private getComment(text: string) {
let data = this.boardService.convertMarkdown(text, this.markedCallback);
return this.sanitizer.bypassSecurityTrustHtml(data.html);
}
private getUserName(userId: number) {
let user = this.activeBoard.users.find((test: User) => test.id === userId);
return user.username;
}
private validateTask(task: Task) {
if (task.title === '') {
this.notes.add(
@ -605,39 +646,5 @@ export class ColumnDisplay implements OnInit, OnDestroy {
return true;
}
private callBoardUpdate() {
this.onUpdateBoards.emit();
}
private showViewModal(taskId: number) {
let viewTask = this.columnData.tasks.find(task => task.id === taskId);
this.viewTaskActivities = [];
this.boardService.getTaskActivity(viewTask.id)
.subscribe(response => {
response.data[1].forEach((item: any) => {
this.viewTaskActivities.push(
new ActivitySimple(item.text, item.timestamp));
});
});
this.newComment = '';
this.viewModalProps = this.convertToTask(viewTask);
this.checkDueDate();
if (this.showActivity) {
this.showActivity = false;
setTimeout(() => (this.showActivity = true), 500);
}
this.modal.open(this.MODAL_VIEW_ID + this.columnData.id);
}
private preventEnter(event: any) {
if (event && event.stopPropagation) {
event.stopPropagation();
}
}
}

View File

@ -5,12 +5,11 @@ import {
OnInit,
Output
} from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { DomSanitizer } from '@angular/platform-browser';
import {
ApiResponse,
Board,
Column,
Notification,
Task,
UserOptions
@ -27,23 +26,29 @@ import { BoardService } from '../board.service';
selector: 'tb-task',
templateUrl: './task.component.html'
})
export class TaskDisplay implements OnInit {
private isOverdue: boolean;
private isNearlyDue: boolean;
export class TaskDisplayComponent implements OnInit {
public isOverdue: boolean;
public isNearlyDue: boolean;
public strings: any;
public percentComplete: number;
public activeBoard: Board;
public userOptions: UserOptions;
public boardsList: Array<Board>;
// tslint:disable-next-line
@Input('task') taskData: Task;
// tslint:disable-next-line
@Input('add-task') addTask: Function;
// tslint:disable-next-line
@Input('edit-task') editTask: Function;
// tslint:disable-next-line
@Input('view-task') viewTask: Function;
// tslint:disable-next-line
@Input('remove-task') removeTask: Function;
// tslint:disable-next-line
@Input('collapse') isCollapsed: boolean;
// tslint:disable-next-line
@Output('on-update-boards') onUpdateBoards: EventEmitter<any>;
@Input('boards')
@ -51,12 +56,12 @@ export class TaskDisplay implements OnInit {
this.boardsList = boards;
}
constructor(private auth: AuthService,
constructor(public auth: AuthService,
private sanitizer: DomSanitizer,
public boardService: BoardService,
private modal: ModalService,
public modal: ModalService,
private notes: NotificationsService,
private stringsService: StringsService) {
public stringsService: StringsService) {
this.onUpdateBoards = new EventEmitter<any>();
this.percentComplete = 0;
@ -83,7 +88,7 @@ export class TaskDisplay implements OnInit {
return;
}
let data = this.boardService.convertMarkdown(
const data = this.boardService.convertMarkdown(
this.taskData.description, this.markedCallback, true
);
@ -109,10 +114,10 @@ export class TaskDisplay implements OnInit {
// Expects a color in full HEX with leading #, e.g. #ffffe0
getTextColor(color: string): string {
let r = parseInt(color.substr(1, 2), 16),
g = parseInt(color.substr(3, 2), 16),
b = parseInt(color.substr(5, 2), 16),
yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
const r = parseInt(color.substr(1, 2), 16);
const g = parseInt(color.substr(3, 2), 16);
const b = parseInt(color.substr(5, 2), 16);
const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return yiq >= 140 ? '#333333' : '#efefef';
}
@ -123,8 +128,9 @@ export class TaskDisplay implements OnInit {
}
event.target.parentElement.parentElement.click();
let select = document.getElementById('columnsList' + this.taskData.id) as HTMLSelectElement,
id = +select[select.selectedIndex].value;
const select = document.getElementById('columnsList' + this.taskData.id) as
HTMLSelectElement;
const id = +(select[select.selectedIndex] as HTMLOptionElement).value;
if (id === 0) {
return;
@ -148,11 +154,11 @@ export class TaskDisplay implements OnInit {
}
event.target.parentElement.parentElement.click();
let select = document.getElementById('boardsList' + this.taskData.id +
const select = document.getElementById('boardsList' + this.taskData.id +
this.strings.boards_copyTaskTo.split(' ')[0]) as HTMLSelectElement;
let newBoardId = +select[select.selectedIndex].value;
let taskData = { ...this.taskData };
const newBoardId = +(select[select.selectedIndex] as HTMLOptionElement).value;
const taskData = { ...this.taskData };
let boardData: Board;
this.boardsList.forEach(board => {
@ -186,10 +192,10 @@ export class TaskDisplay implements OnInit {
}
event.target.parentElement.parentElement.click();
let select = document.getElementById('boardsList' + this.taskData.id +
const select = document.getElementById('boardsList' + this.taskData.id +
this.strings.boards_moveTaskTo.split(' ')[0]) as HTMLSelectElement;
let newBoardId = +select[select.selectedIndex].value;
const newBoardId = +(select[select.selectedIndex] as HTMLOptionElement).value;
let boardData: Board;
this.boardsList.forEach(board => {
@ -222,16 +228,16 @@ export class TaskDisplay implements OnInit {
return;
}
let dueDate = new Date(this.taskData.due_date);
const dueDate = new Date(this.taskData.due_date);
if (isNaN(dueDate.valueOf())) {
return;
}
let millisecondsPerDay = (1000 * 3600 * 24),
today = new Date(),
timeDiff = today.getTime() - dueDate.getTime(),
daysDiff = Math.ceil(timeDiff / millisecondsPerDay);
const millisecondsPerDay = (1000 * 3600 * 24);
const today = new Date();
const timeDiff = today.getTime() - dueDate.getTime();
const daysDiff = Math.ceil(timeDiff / millisecondsPerDay);
if (daysDiff > 0) {
// past due date
@ -244,18 +250,18 @@ export class TaskDisplay implements OnInit {
}
// Needs anonymous function for proper `this` context.
private markedCallback = (error: any, text: string) => {
private markedCallback = (_: any, text: string) => {
if (!this.activeBoard.issue_trackers) {
return;
}
this.activeBoard.issue_trackers.forEach(tracker => {
let re = new RegExp(tracker.regex, 'ig');
let replacements = new Array<any>();
const re = new RegExp(tracker.regex, 'ig');
const replacements = new Array<any>();
let result = re.exec(text);
while (result !== null) {
let link = '<a href="' +
const link = '<a href="' +
tracker.url.replace(/%BUGID%/g, result[1]) +
'" target="tb_external" rel="noreferrer">' +
result[0] + '</a>';

View File

@ -4,9 +4,9 @@ import { Component, Input } from '@angular/core';
selector: 'tb-calendar',
templateUrl: './calendar.component.html'
})
export class Calendar {
private today: Date;
private tasks: Array<any>; // TODO: Use Task model when created
export class CalendarComponent {
public today: Date;
public tasks: any[]; // TODO: Use Task model when created
public dayLabels = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
public monthLabels = [
@ -17,8 +17,9 @@ export class Calendar {
public month: number;
public year: number;
public calendarDays: Array<Array<string>>;
public calendarDays: string[][];
// tslint:disable-next-line
@Input('board-id') boardId: number;
constructor() {
@ -49,7 +50,7 @@ export class Calendar {
this.generateCalendar();
}
public previousMonth(): void {
previousMonth(): void {
this.month -= 1;
// Months are zero-index in JS
@ -61,7 +62,7 @@ export class Calendar {
this.generateCalendar();
}
public nextMonth(): void {
nextMonth(): void {
this.month += 1;
if (this.month > 11) {
@ -72,21 +73,21 @@ export class Calendar {
this.generateCalendar();
}
private getColor(task: any) { // TODO: Use Task model
getColor(task: any) { // TODO: Use Task model
return task.color;
}
private generateCalendar(): void {
let firstDate = new Date(this.year, this.month, 1),
startingDay = firstDate.getDay(),
monthLength = new Date(this.year, this.month + 1, 0).getDate(),
rows = Math.ceil((monthLength + startingDay) / 7),
day = 1;
const firstDate = new Date(this.year, this.month, 1);
const startingDay = firstDate.getDay();
const monthLength = new Date(this.year, this.month + 1, 0).getDate();
const rows = Math.ceil((monthLength + startingDay) / 7);
let day = 1;
this.calendarDays = [];
for (let i = 0; i < rows; ++i) {
let weekDays: Array<string> = [];
const weekDays: Array<string> = [];
for (let j = 0; j < 7; ++j) {
if (day <= monthLength && (j >= startingDay || i > 0)) {

View File

@ -5,15 +5,20 @@ import * as Chartist from 'chartist';
selector: 'tb-charts',
templateUrl: './charts.component.html'
})
export class Charts implements AfterViewInit {
export class ChartsComponent implements AfterViewInit {
public percentages: Array<number>;
public data: Array<number>;
public words: Array<string>;
// tslint:disable-next-line
@Input('chart-name') chartName = '';
// tslint:disable-next-line
@Input('chart-type') chartType = 'pie';
// tslint:disable-next-line
@Input('series') series = '';
// tslint:disable-next-line
@Input('labels') labels = '';
// tslint:disable-next-line
@Input('table-head') tableHead = '';
constructor() {
@ -34,50 +39,54 @@ export class Charts implements AfterViewInit {
}
private createPieChart() {
let data = {
const data = {
series: this.data,
labels: this.words
},
options = {
donut: true,
donutWidth: 100,
labelInterpolationFnc: (label: string, index: number) => {
let value = this.data[index],
percent = Math.round(value /
this.data.reduce(Chartist.sum) * 100);
};
const options = {
donut: true,
donutWidth: 100,
labelInterpolationFnc: (label: string, index: number) => {
const value = this.data[index];
const percent = Math.round(value /
this.data.reduce(Chartist.sum) * 100);
if (this.percentages.length < this.data.length) {
this.percentages.push(percent);
}
return label + ' ' + percent + '%';
if (this.percentages.length < this.data.length) {
this.percentages.push(percent);
}
},
pie = new Chartist.Pie('#' + this.chartName, data, options);
return label + ' ' + percent + '%';
}
};
// tslint:disable-next-line
new Chartist.Pie('#' + this.chartName, data, options);
}
private createLineChart() {
let data = {
const data = {
series: [this.data],
labels: this.words
},
options = {
axisY: {
onlyInteger: true
},
low: 0,
height: '300px'// ,
// plugins: [ Chartist.plugins.tooltip({
// transformTooltipTextFnc: (value) => {
// return value + ' points remaining'
// }
// }) ]
};
const options = {
axisY: {
onlyInteger: true
},
line = new Chartist.Line('#' + this.chartName, data, options);
low: 0,
height: '300px'// ,
// plugins: [ Chartist.plugins.tooltip({
// transformTooltipTextFnc: (value) => {
// return value + ' points remaining'
// }
// }) ]
};
// tslint:disable-next-line
new Chartist.Line('#' + this.chartName, data, options);
}
private convertToNumberArray(arr: Array<string>): Array<number> {
let nums: Array<number> = [];
const nums: Array<number> = [];
for (let i = 0, len = arr.length; i < len; ++i) {
nums.push(Number(arr[i]));

View File

@ -1,15 +1,15 @@
import { Component } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Charts } from './charts/charts.component';
import { Calendar } from './calendar/calendar.component';
// import { Charts } from './charts/charts.component';
// import { Calendar } from './calendar/calendar.component';
@Component({
selector: 'tb-dashboard',
templateUrl: './dashboard.component.html'
})
export class Dashboard {
constructor(private title: Title) {
export class DashboardComponent {
constructor(public title: Title) {
title.setTitle('TaskBoard - Dashboard');
}
}

View File

@ -3,9 +3,9 @@ import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
import { Dashboard } from './dashboard.component';
import { Calendar } from './calendar/calendar.component';
import { Charts } from './charts/charts.component';
import { DashboardComponent } from './dashboard.component';
import { CalendarComponent } from './calendar/calendar.component';
import { ChartsComponent } from './charts/charts.component';
@NgModule({
imports: [
@ -13,14 +13,14 @@ import { Charts } from './charts/charts.component';
SharedModule
],
declarations: [
Dashboard,
Calendar,
Charts
DashboardComponent,
CalendarComponent,
ChartsComponent
],
exports: [
Dashboard,
Calendar,
Charts
DashboardComponent,
CalendarComponent,
ChartsComponent
]
})
export class DashboardModule { }

View File

@ -15,14 +15,14 @@ import {
selector: 'tb-login',
templateUrl: './login.component.html'
})
export class Login implements OnInit {
export class LoginComponent implements OnInit {
version: string;
username = '';
password = '';
remember = false;
isSubmitted = false;
constructor(private constants: Constants, public authService: AuthService,
constructor(public constants: Constants, public authService: AuthService,
private router: Router, private notes: NotificationsService) {
this.version = constants.VERSION;
}

View File

@ -8,7 +8,6 @@ import {
ActionType,
User,
Board,
Notification
} from '../../shared/models';
import {
AuthService,
@ -24,28 +23,26 @@ import { AutoActionsService } from './auto-actions.service';
templateUrl: './auto-actions.component.html',
providers: [ AutoActionsService ]
})
export class AutoActions {
private noActionsMessage: string;
export class AutoActionsComponent implements OnDestroy {
private actionToRemove: AutoAction;
private autoActions: Array<AutoAction>;
private triggers: Array<Array<any>>;
private subs: Array<any>;
public autoActions: AutoAction[];
private subs: any[];
private firstRun = true;
private isAddDisabled = true;
public isAddDisabled = true;
public boards: Array<Board>;
public triggerSources: Array<Array<any>>;
public actionSources: Array<Array<any>>;
public types: Array<Array<any>>;
public typesList: Array<Array<any>>;
public triggers: any[][];
public boards: Board[];
public triggerSources: any[][];
public actionSources: any[][];
public types: any[][];
public typesList: any[][];
public newAction: AutoAction;
public activeUser: User;
public noActionsMessage: string;
public strings: any;
public MODAL_CONFIRM_ID: string;
@ -53,12 +50,12 @@ export class AutoActions {
public loading = true;
public hasInactiveBoards = false;
constructor(private auth: AuthService,
constructor(public auth: AuthService,
public modal: ModalService,
private settings: SettingsService,
public actions: AutoActionsService,
private notes: NotificationsService,
private stringsService: StringsService,
public stringsService: StringsService,
private sanitizer: DomSanitizer) {
this.newAction = new AutoAction();
this.activeUser = new User();
@ -74,14 +71,14 @@ export class AutoActions {
});
this.subs.push(sub);
sub = settings.boardsChanged.subscribe((boards: Array<Board>) => {
sub = settings.boardsChanged.subscribe((boards: Board[]) => {
this.boards = boards;
this.updateHasInactiveBoards();
});
this.subs.push(sub);
sub = settings.actionsChanged
.subscribe((actionsList: Array<AutoAction>) => {
.subscribe((actionsList: AutoAction[]) => {
this.updateActions(actionsList);
});
this.subs.push(sub);
@ -96,13 +93,13 @@ export class AutoActions {
this.subs.forEach(sub => sub.unsubscribe());
}
updateActions(actionList: Array<AutoAction>) {
updateActions(actionList: AutoAction[]) {
this.autoActions = actionList;
this.updateHasInactiveBoards();
this.autoActions.sort((a, b) => {
let nameA = this.getBoardName(a.board_id),
nameB = this.getBoardName(b.board_id);
const nameA = this.getBoardName(a.board_id);
const nameB = this.getBoardName(b.board_id);
return nameA.localeCompare(nameB);
});
@ -257,10 +254,10 @@ export class AutoActions {
}
getBoardName(id: number): string {
let board = this.getBoard(+id);
const board = this.getBoard(+id);
if (board) {
let note = +board.is_active ? '' : '*';
const note = +board.is_active ? '' : '*';
return board.name + note;
}
@ -269,8 +266,8 @@ export class AutoActions {
}
getTriggerDescription(action: AutoAction): string {
let desc = '',
board = this.getBoard(action.board_id);
let desc = '';
const board = this.getBoard(action.board_id);
if (!board) {
return;
@ -303,8 +300,8 @@ export class AutoActions {
}
getTypeDescription(action: AutoAction): SafeHtml {
let desc = '',
board = this.getBoard(action.board_id);
let desc = '';
const board = this.getBoard(action.board_id);
if (!board) {
return;
@ -361,6 +358,11 @@ export class AutoActions {
});
}
showConfirmModal(action: AutoAction): void {
this.actionToRemove = action;
this.modal.open(this.MODAL_CONFIRM_ID);
}
private updateHasInactiveBoards(): void {
this.hasInactiveBoards = false;
@ -382,39 +384,38 @@ export class AutoActions {
}
private buildSourcesArray(sourceArray: string,
name: string,
arrayName: string,
prop: string = 'name'): void {
this[sourceArray] =
[ [ null, this.strings['settings_select' + name ] ] ]; // tslint:disable-line
name: string,
arrayName: string,
prop: string = 'name'): void {
this[sourceArray] = [[null, this.strings['settings_select' + name]]];
for (let i = 0; i < this.boards.length; ++i) {
if (this.boards[i].id !== this.newAction.board_id) {
for (const board of this.boards) {
if (board.id !== this.newAction.board_id) {
continue;
}
this.boards[i][arrayName].forEach((item: any) => {
board[arrayName].forEach((item: any) => {
this[sourceArray].push([ item.id, item[prop] ]);
});
}
}
private getBoard(id: number): Board {
let board: Board = null;
let foundBoard: Board = null;
for (let i = 0; i < this.boards.length; ++i) {
if (+this.boards[i].id === +id) {
board = this.boards[i];
for (const board of this.boards) {
if (+board.id === +id) {
foundBoard = board;
break;
}
}
return board;
return foundBoard;
}
private getNameFromArray(boardArray: Array<any>,
arrayItemId: number,
prop: string = 'name') {
arrayItemId: number,
prop: string = 'name') {
let name = '';
boardArray.forEach(item => {
@ -448,9 +449,5 @@ export class AutoActions {
}
}
private showConfirmModal(action: AutoAction): void {
this.actionToRemove = action;
this.modal.open(this.MODAL_CONFIRM_ID);
}
}

View File

@ -17,16 +17,16 @@ export class AutoActionsService {
addAction(action: AutoAction): Observable<ApiResponse> {
return this.http.post('api/autoactions', action)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
removeAction(action: AutoAction): Observable<ApiResponse> {
return this.http.delete('api/autoactions/' + action.id)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
}

View File

@ -163,8 +163,8 @@
[dragula]="'columns-bag'" [(dragulaModel)]="modalProps.columns">
<li *ngFor="let column of modalProps.columns; let i = index">
<i class="icon icon-resize-vertical"></i>
<inline-edit [text]="getPropertyValue('columns', 'name', i)"
(edit)="onPropertyEdit('columns', 'name', i, $event)"></inline-edit>
<tb-inline-edit [text]="getPropertyValue('columns', 'name', i)"
(edit)="onPropertyEdit('columns', 'name', i, $event)"></tb-inline-edit>
<span class="actions">
<i class="icon icon-trash-empty color-secondary"
title="{{ strings['settings_removeColumn'] }}"
@ -195,8 +195,8 @@
<input type="color" [title]="strings['settings_defaultTaskColor']"
[ngModel]="getColor(category)"
(ngModelChange)="setCategoryColor($event, i)">
<inline-edit [text]="getPropertyValue('categories', 'name', i)"
(edit)="onPropertyEdit('categories', 'name', i, $event)"></inline-edit>
<tb-inline-edit [text]="getPropertyValue('categories', 'name', i)"
(edit)="onPropertyEdit('categories', 'name', i, $event)"></tb-inline-edit>
<span class="actions">
<i class="icon icon-trash-empty color-secondary"
title="{{ strings['settings_removeCategory'] }}"
@ -234,14 +234,14 @@
<ul *ngIf="modalProps.issue_trackers.length" class="modal-list">
<li class="double-edit"
*ngFor="let tracker of modalProps.issue_trackers; let i = index">
<inline-edit class="first"
<tb-inline-edit class="first"
[text]="getPropertyValue('issue_trackers', 'url', i)"
(edit)="onPropertyEdit('issue_trackers', 'url', i, $event)">
</inline-edit>
<inline-edit class="last"
</tb-inline-edit>
<tb-inline-edit class="last"
[text]="getPropertyValue('issue_trackers', 'regex', i)"
(edit)="onPropertyEdit('issue_trackers', 'regex', i, $event)">
</inline-edit>
</tb-inline-edit>
<span class="actions">
<i class="icon icon-trash-empty color-secondary"
(click)="modalProps.removeIssueTracker(tracker)"></i>

View File

@ -1,6 +1,6 @@
import { Component, OnDestroy } from '@angular/core';
import { Component, OnDestroy, AfterContentInit } from '@angular/core';
import { DragulaService } from 'ng2-dragula/dist';
import { DragulaService } from 'ng2-dragula';
import {
ApiResponse,
@ -26,17 +26,15 @@ class SelectableUser extends User {
@Component({
selector: 'tb-board-admin',
templateUrl: './board-admin.component.html',
providers: [ BoardAdminService ]
providers: [ DragulaService, BoardAdminService ]
})
export class BoardAdmin implements OnDestroy {
private noBoardsMessage: string;
export class BoardAdminComponent implements OnDestroy, AfterContentInit {
private firstRun = true;
private subs: Array<any>;
private subs: any[];
public displayBoards: Array<Board>;
public users: Array<User>;
public boards: Array<Board>;
public displayBoards: Board[];
public users: SelectableUser[];
public boards: Board[];
public activeUser: User;
public modalProps: BoardData;
public boardToRemove: Board;
@ -49,16 +47,17 @@ export class BoardAdmin implements OnDestroy {
public userFilter: string;
public statusFilter: string;
public sortFilter: string;
public noBoardsMessage: string;
public MODAL_ID: string;
public MODAL_CONFIRM_ID: string;
constructor(private auth: AuthService,
constructor(public auth: AuthService,
public modal: ModalService,
public settings: SettingsService,
public boardService: BoardAdminService,
private notes: NotificationsService,
private stringsService: StringsService,
public stringsService: StringsService,
public dragula: DragulaService) {
this.MODAL_ID = 'board-addedit-form';
this.MODAL_CONFIRM_ID = 'board-remove-confirm';
@ -80,12 +79,12 @@ export class BoardAdmin implements OnDestroy {
});
this.subs.push(sub);
sub = settings.usersChanged.subscribe((users: Array<User>) => {
sub = settings.usersChanged.subscribe((users: User[]) => {
this.updateUsersList(users);
});
this.subs.push(sub);
sub = settings.boardsChanged.subscribe((boards: Array<Board>) => {
sub = settings.boardsChanged.subscribe((boards: Board[]) => {
this.updateBoardsList(boards);
});
this.subs.push(sub);
@ -102,15 +101,15 @@ export class BoardAdmin implements OnDestroy {
}
ngAfterContentInit() {
let ul = document.getElementsByClassName('modal-list')[0];
let bag = this.dragula.find('columns-bag');
const ul = document.getElementsByClassName('modal-list')[0];
const bag = this.dragula.find('columns-bag');
if (bag !== undefined) {
this.dragula.destroy('columns-bag');
}
this.dragula.createGroup('columns-bag', <any>{
moves: (el: any, container: any, handle: any) => {
this.dragula.createGroup('columns-bag', {
moves(_: any, __: any, handle: any) {
return handle.classList.contains('icon-resize-vertical');
},
mirrorContainer: ul
@ -128,7 +127,7 @@ export class BoardAdmin implements OnDestroy {
return;
}
let isAdd = this.modalProps.title === 'Add';
const isAdd = this.modalProps.title === 'Add';
this.saving = true;
this.setBoardUsers();
@ -167,7 +166,7 @@ export class BoardAdmin implements OnDestroy {
}
toggleBoardStatus(board: Board): void {
let boardData = new BoardData('', board.id, board.name,
const boardData = new BoardData('', board.id, board.name,
!board.is_active, board.columns,
board.categories, board.issue_trackers,
board.users);
@ -179,14 +178,14 @@ export class BoardAdmin implements OnDestroy {
}
filterBoards(): void {
let userBoards = this.filterBoardsByUser(),
statusBoards = this.filterBoardsByStatus();
const userBoards = this.filterBoardsByUser();
const statusBoards = this.filterBoardsByStatus();
this.displayBoards = [];
this.boards.forEach((board: Board) => {
let foundInUserBoards = false,
foundInStatusBoards = false;
let foundInUserBoards = false;
let foundInStatusBoards = false;
userBoards.forEach((userBoard: Board) => {
if (userBoard.id === board.id) {
@ -243,12 +242,12 @@ export class BoardAdmin implements OnDestroy {
}
}
private filterBoardsByUser(): Array<Board> {
private filterBoardsByUser(): Board[] {
if (+this.userFilter === -1) {
return this.deepCopy(this.boards);
}
let filteredBoards: Array<Board> = [];
const filteredBoards: Board[] = [];
this.boards.forEach((board: Board) => {
let userFound = false;
@ -267,12 +266,12 @@ export class BoardAdmin implements OnDestroy {
return filteredBoards;
}
private filterBoardsByStatus(): Array<Board> {
private filterBoardsByStatus(): Board[] {
if (+this.statusFilter === -1) {
return this.deepCopy(this.boards);
}
let filteredBoards: Array<Board> = [];
const filteredBoards: Board[] = [];
this.boards.forEach((board: Board) => {
if ((board.is_active && +this.statusFilter === 1) ||
@ -308,7 +307,7 @@ export class BoardAdmin implements OnDestroy {
this.modal.close(this.MODAL_ID);
this.modal.close(this.MODAL_CONFIRM_ID);
let boards = Array<Board>();
const boards: Board[] = [];
response.data[1].forEach((board: any) => {
boards.push(new Board(+board.id, board.name,
board.is_active === '1', board.ownColumn,
@ -324,7 +323,7 @@ export class BoardAdmin implements OnDestroy {
private setBoardUsers(): void {
this.modalProps.users = [];
this.users.forEach((user: SelectableUser) => {
this.users.forEach(user => {
if (user.selected) {
this.modalProps.users.push(user);
}
@ -356,7 +355,7 @@ export class BoardAdmin implements OnDestroy {
}
}
private updateUsersList(users: Array<any>): void {
private updateUsersList(users: any[]): void {
this.users = [];
this.hasBAUsers = false;
@ -372,7 +371,7 @@ export class BoardAdmin implements OnDestroy {
});
}
private updateBoardsList(boards: Array<Board>): void {
private updateBoardsList(boards: Board[]): void {
this.boards = boards;
this.boards.forEach(board => {
@ -381,27 +380,21 @@ export class BoardAdmin implements OnDestroy {
});
});
this.displayBoards = this.deepCopy(this.boards);
this.filterBoards();
if (this.firstRun) {
this.firstRun = false;
return;
}
this.loading = false;
}
private getPropertyValue(obj: string, prop: string, i: number): string {
public getPropertyValue(obj: string, prop: string, i: number): string {
return this.modalProps[obj][i][prop];
}
private onPropertyEdit(obj: string, prop: string,
i: number, value: any): void {
public onPropertyEdit(obj: string, prop: string,
i: number, value: any): void {
this.modalProps[obj][i][prop] = value;
}
private getColor(category: any): string {
public getColor(category: any): string {
if (category.default_task_color) {
return category.default_task_color;
}
@ -409,16 +402,17 @@ export class BoardAdmin implements OnDestroy {
return category.defaultColor;
}
private setCategoryColor(color: any, index: number): void {
public setCategoryColor(color: any, index: number): void {
this.modalProps.categories[index].default_task_color = color;
}
private deepCopy(source: any): any {
let output: any, value: any, key: any;
let output: any;
let value: any;
output = Array.isArray(source) ? [] : {};
for (key in source) {
for (const key in source) {
if (source.hasOwnProperty(key)) {
value = source[key];
output[key] = (typeof value === 'object') ?
@ -429,8 +423,8 @@ export class BoardAdmin implements OnDestroy {
return output;
}
private showModal(title: string, board?: Board): void {
let isAdd = (title === 'Add');
public showModal(title: string, board?: Board): void {
const isAdd = (title === 'Add');
this.modalProps = new BoardData(title);
@ -446,7 +440,7 @@ export class BoardAdmin implements OnDestroy {
this.modalProps.issue_trackers = this.deepCopy(board.issue_trackers);
this.users.forEach((user: SelectableUser) => {
let filtered = board.users.filter(u => +u.id === user.id);
const filtered = board.users.filter(u => +u.id === user.id);
user.selected = filtered.length > 0;
});
@ -455,7 +449,7 @@ export class BoardAdmin implements OnDestroy {
this.modal.open(this.MODAL_ID);
}
private showConfirmModal(board: Board): void {
public showConfirmModal(board: Board): void {
this.boardToRemove = board;
this.modal.open(this.MODAL_CONFIRM_ID);
}

View File

@ -4,14 +4,7 @@ import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import {
ApiResponse,
Board,
Column,
Category,
IssueTracker,
User
} from '../../shared/models';
import { ApiResponse } from '../../shared/models';
import { BoardData } from './board-data.model';
@Injectable()
@ -22,24 +15,24 @@ export class BoardAdminService {
addBoard(board: BoardData): Observable<ApiResponse> {
return this.http.post('api/boards', board)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
editBoard(board: BoardData): Observable<ApiResponse> {
return this.http.post('api/boards/' + board.id, board)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
removeBoard(boardId: number): Observable<ApiResponse> {
return this.http.delete('api/boards/' + boardId)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}

View File

@ -28,7 +28,7 @@ export class BoardData {
}
removeColumn(column: any): void {
let index = this.columns.indexOf(column);
const index = this.columns.indexOf(column);
if (index === -1) {
return;
@ -50,7 +50,7 @@ export class BoardData {
}
removeCategory(category: any): void {
let index = this.categories.indexOf(category);
const index = this.categories.indexOf(category);
if (index === -1) {
return;
@ -73,7 +73,7 @@ export class BoardData {
}
removeIssueTracker(tracker: any): void {
let index = this.issue_trackers.indexOf(tracker);
const index = this.issue_trackers.indexOf(tracker);
if (index === -1) {
return;

View File

@ -2,20 +2,22 @@ import { Component, OnDestroy } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { StringsService } from '../shared/services';
import { SettingsService } from './settings.service';
@Component({
selector: 'tb-settings',
templateUrl: './settings.component.html'
})
export class Settings implements OnDestroy {
export class SettingsComponent implements OnDestroy {
public strings: any;
private subs: Array<any>;
constructor(private stringsService: StringsService,
private title: Title) {
constructor(public stringsService: StringsService,
public settings: SettingsService,
public title: Title) {
this.subs = [];
let sub = stringsService.stringsChanged.subscribe(newStrings => {
const sub = stringsService.stringsChanged.subscribe(newStrings => {
this.strings = newStrings;
title.setTitle('TaskBoard - ' + this.strings['settings']); // tslint:disable-line
});

View File

@ -6,33 +6,35 @@ import { RouterModule } from '@angular/router';
import { DragulaModule } from 'ng2-dragula/dist';
import { SharedModule } from '../shared/shared.module';
import { AutoActions } from './auto-actions/auto-actions.component';
import { BoardAdmin } from './board-admin/board-admin.component';
import { Settings } from './settings.component';
import { UserAdmin } from './user-admin/user-admin.component';
import { UserSettings } from './user-settings/user-settings.component';
import { SettingsService } from 'src/app/settings/settings.service';
import { AutoActionsService } from 'src/app/settings/auto-actions/auto-actions.service';
import { BoardAdminService } from 'src/app/settings/board-admin/board-admin.service';
import { UserAdminService } from 'src/app/settings/user-admin/user-admin.service';
import { UserSettingsService } from 'src/app/settings/user-settings/user-settings.service';
import { AutoActionsService } from './auto-actions/auto-actions.service';
import { BoardAdminService } from './board-admin/board-admin.service';
import { SettingsService } from './settings.service';
import { UserAdminService } from './user-admin/user-admin.service';
import { UserSettingsService } from './user-settings/user-settings.service';
import { AutoActionsComponent } from './auto-actions/auto-actions.component';
import { BoardAdminComponent } from './board-admin/board-admin.component';
import { SettingsComponent } from './settings.component';
import { UserAdminComponent } from './user-admin/user-admin.component';
import { UserSettingsComponent } from './user-settings/user-settings.component';
const declarationsAndExports = [
AutoActionsComponent,
BoardAdminComponent,
SettingsComponent,
UserAdminComponent,
UserSettingsComponent
];
@NgModule({
imports: [
CommonModule,
DragulaModule,
DragulaModule.forRoot(),
FormsModule,
RouterModule,
SharedModule
],
declarations: [
AutoActions,
BoardAdmin,
Settings,
UserAdmin,
UserSettings
],
providers: [
AutoActionsService,
BoardAdminService,
@ -40,12 +42,9 @@ import { UserSettingsService } from './user-settings/user-settings.service';
UserAdminService,
UserSettingsService
],
exports: [
AutoActions,
BoardAdmin,
Settings,
UserAdmin,
UserSettings
]
declarations: declarationsAndExports,
exports: declarationsAndExports
})
export class SettingsModule { }

View File

@ -11,7 +11,9 @@ import {
AutoAction
} from '../shared/models';
@Injectable()
@Injectable({
providedIn: 'root'
})
export class SettingsService {
private users = new BehaviorSubject<Array<User>>([]);
private boards = new BehaviorSubject<Array<Board>>([]);
@ -31,8 +33,8 @@ export class SettingsService {
getUsers(): Observable<ApiResponse> {
return this.http.get('api/users')
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
@ -46,8 +48,8 @@ export class SettingsService {
getBoards(): Observable<ApiResponse> {
return this.http.get('api/boards')
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
@ -58,8 +60,8 @@ export class SettingsService {
getActions(): Observable<ApiResponse> {
return this.http.get('api/autoactions')
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
}

View File

@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { UserAdminService } from './user-admin.service';
import { SettingsService } from '../settings.service';
import {
ApiResponse,
Board,
@ -14,6 +15,7 @@ import {
NotificationsService,
StringsService
} from '../../shared/services';
import {
UserDisplay,
ModalUser,
@ -25,12 +27,12 @@ import {
templateUrl: './user-admin.component.html',
providers: [ UserAdminService ]
})
export class UserAdmin {
private users: Array<UserDisplay>;
export class UserAdminComponent {
public users: UserDisplay[];
public userToRemove: UserDisplay;
public boards: Array<Board>;
public boards: Board[];
public activeUser: User;
public modalProps: ModalProperties;
public strings: any;
@ -43,10 +45,10 @@ export class UserAdmin {
constructor(public userService: UserAdminService,
private notes: NotificationsService,
private auth: AuthService,
public auth: AuthService,
private settings: SettingsService,
public modal: ModalService,
private stringsService: StringsService) {
public stringsService: StringsService) {
this.MODAL_ID = 'user-addEdit-form';
this.MODAL_CONFIRM_ID = 'user-remove-confirm';
@ -73,6 +75,7 @@ export class UserAdmin {
stringsService.stringsChanged.subscribe(newStrings => {
this.strings = newStrings;
this.updateUserList();
});
settings.boardsChanged
@ -97,7 +100,7 @@ export class UserAdmin {
return;
}
let isAdd = this.modalProps.prefix;
const isAdd = this.modalProps.prefix;
this.saving = true;
if (!this.validateModalUser()) {
@ -139,6 +142,20 @@ export class UserAdmin {
});
}
showModal(isAdd: boolean = true, user?: UserDisplay): void {
this.modalProps = {
prefix: isAdd,
user: isAdd ? new ModalUser(new User()) : new ModalUser(user)
};
this.modal.open(this.MODAL_ID);
}
showConfirmModal(user: UserDisplay): void {
this.userToRemove = user;
this.modal.open(this.MODAL_CONFIRM_ID);
}
private closeModal(status: string): void {
if (status === 'success') {
this.modal.close(this.MODAL_ID);
@ -151,7 +168,7 @@ export class UserAdmin {
private getBoards(): void {
this.settings.getBoards()
.subscribe((response: ApiResponse) => {
let boards = response.data[1];
const boards = response.data[1];
this.boards = [];
if (boards) {
@ -212,7 +229,7 @@ export class UserAdmin {
}
private validateModalUser(): boolean {
let user = this.modalProps.user;
const user = this.modalProps.user;
if (user.username === '') {
this.notes.add(
@ -232,8 +249,8 @@ export class UserAdmin {
return false;
}
let emailRegex = /.+@.+\..+/i;
let match = user.email.match(emailRegex);
const emailRegex = /.+@.+\..+/i;
const match = user.email.match(emailRegex);
if (!match && user.email !== '') {
this.notes.add(
@ -244,22 +261,8 @@ export class UserAdmin {
return true;
}
private showModal(isAdd: boolean = true, user?: UserDisplay): void {
this.modalProps = {
prefix: isAdd,
user: isAdd ? new ModalUser(new User()) : new ModalUser(user)
};
this.modal.open(this.MODAL_ID);
}
private showConfirmModal(user: UserDisplay): void {
this.userToRemove = user;
this.modal.open(this.MODAL_CONFIRM_ID);
}
private getDefaultBoardName(user: UserDisplay): string {
let filtered = this.boards
const filtered = this.boards
.filter(board => board.id === user.default_board_id);
if (filtered.length) {
@ -285,7 +288,7 @@ export class UserAdmin {
}
});
this.settings.updateUsers(<Array<User>> this.users);
this.settings.updateUsers(this.users as User[]);
}
}

View File

@ -1,4 +1,4 @@
import { User } from '../../shared/models';
import { User } from '../../shared/models';
export class UserDisplay extends User {
/* tslint:disable:variable-name */
@ -9,7 +9,7 @@ export class UserDisplay extends User {
}
export class ModalUser extends UserDisplay {
public password: string = '';
public password = '';
public password_verify: string = ''; // tslint:disable-line
public boardAccess: Array<string> = [];

View File

@ -15,24 +15,24 @@ export class UserAdminService {
addUser(user: ModalUser): Observable<ApiResponse> {
return this.http.post('api/users', user)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
editUser(user: ModalUser): Observable<ApiResponse> {
return this.http.post('api/users/' + user.id, user)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
removeUser(userId: number): Observable<ApiResponse> {
return this.http.delete('api/users/' + userId)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
}

View File

@ -21,7 +21,7 @@ import {
templateUrl: './user-settings.component.html',
providers: [ UserSettingsService ]
})
export class UserSettings implements OnInit {
export class UserSettingsComponent implements OnInit {
public boards: Array<Board>;
public user: User;
public userOptions: UserOptions;
@ -34,7 +34,7 @@ export class UserSettings implements OnInit {
private notes: NotificationsService,
private settings: SettingsService,
public users: UserSettingsService,
private stringsService: StringsService) {
public stringsService: StringsService) {
this.user = new User();
this.changeEmail = new EmailForm();
@ -131,6 +131,7 @@ export class UserSettings implements OnInit {
if (this.changeUsername.newName === '') {
this.notes.add(new Notification('error',
// tslint:disable-next-line
this.strings['settings_usernameRequired']));
this.changeUsername.submitted = false;
@ -145,8 +146,8 @@ export class UserSettings implements OnInit {
this.settings.getBoards()
.subscribe((res: ApiResponse) => {
let boardData = res.data[1],
boards: Array<Board> = [];
const boardData = res.data[1];
const boards: Array<Board> = [];
if (boardData) {
boardData.forEach((board: any) => {
@ -169,8 +170,8 @@ export class UserSettings implements OnInit {
this.changeEmail.submitted = true;
// https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
let emailRegex = /.+@.+\..+/i;
let match = this.changeEmail.newEmail.match(emailRegex);
const emailRegex = /.+@.+\..+/i;
const match = this.changeEmail.newEmail.match(emailRegex);
if (!match && this.changeEmail.newEmail !== '') {
this.notes.add(new Notification('error', 'Invalid email address.'));

View File

@ -25,34 +25,34 @@ export class UserSettingsService {
}
changeDefaultBoard(user: User): Observable<ApiResponse> {
let json = JSON.stringify(user);
const json = JSON.stringify(user);
return this.http.post('api/users/' + this.activeUser.id, json)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
changePassword(oldPass: string, newPass: string): Observable<ApiResponse> {
let updateUser: UpdateUser = this.activeUser;
const updateUser: UpdateUser = this.activeUser;
updateUser.new_password = newPass;
updateUser.old_password = oldPass;
let json = JSON.stringify(updateUser);
const json = JSON.stringify(updateUser);
return this.http.post('api/users/' + this.activeUser.id, json)
.pipe(
map((response: ApiResponse) => { return response; }),
catchError((err) => { return of(<ApiResponse>err.error); })
map((response: ApiResponse) => response),
catchError((err) => of(err.error as ApiResponse))
);
}
changeUsername(newName: string): Observable<ApiResponse> {
let updateUser = this.activeUser;
const updateUser = this.activeUser;
updateUser.username = newName;
let json = JSON.stringify(updateUser);
const json = JSON.stringify(updateUser);
return this.http.post('api/users/' + this.activeUser.id, json)
.pipe(
@ -60,15 +60,15 @@ export class UserSettingsService {
this.auth.updateUser(JSON.parse(response.data[1]));
return response;
}),
catchError((err) => { return of(<ApiResponse>err.error); })
catchError((err) => of(err.error as ApiResponse))
);
}
changeEmail(newEmail: string): Observable<ApiResponse> {
let updateUser = this.activeUser;
const updateUser = this.activeUser;
updateUser.email = newEmail;
let json = JSON.stringify(updateUser);
const json = JSON.stringify(updateUser);
return this.http.post('api/users/' + this.activeUser.id, json)
.pipe(
@ -76,12 +76,12 @@ export class UserSettingsService {
this.auth.updateUser(JSON.parse(response.data[1]));
return response;
}),
catchError((err) => { return of(<ApiResponse>err.error); })
catchError((err) => of(err.error as ApiResponse))
);
}
changeUserOptions(newOptions: UserOptions): Observable<ApiResponse> {
let json = JSON.stringify(newOptions);
const json = JSON.stringify(newOptions);
return this.http.post('api/users/' + this.activeUser.id + '/opts', json)
.pipe(
@ -90,7 +90,7 @@ export class UserSettingsService {
JSON.parse(response.data[1]));
return response;
}),
catchError((err) => { return of(<ApiResponse>err.error); })
catchError((err) => of(err.error as ApiResponse))
);
}
}

View File

@ -20,8 +20,8 @@ export class AuthService {
public userOptions: UserOptions = null;
public userChanged = this.activeUser.asObservable();
constructor(constants: Constants, private http: HttpClient,
private router: Router, private strings: StringsService) {
constructor(public constants: Constants, private http: HttpClient,
public router: Router, private strings: StringsService) {
}
updateUser(user: User, userOpts?: UserOptions): void {
@ -40,13 +40,13 @@ export class AuthService {
this.updateUser(response.data[1], response.data[2]);
return true;
}),
catchError((err, caught) => { return of(false); })
catchError((_, __) => of(false))
);
}
login(username: string, password: string,
remember: boolean): Observable<ApiResponse> {
let json = JSON.stringify({
const json = JSON.stringify({
username,
password,
remember
@ -58,9 +58,9 @@ export class AuthService {
this.updateUser(response.data[1], response.data[2]);
return response;
}),
catchError((err, caught) => {
catchError((err, _) => {
this.updateUser(null, null);
return of(<ApiResponse>err.error);
return of(err.error as ApiResponse);
})
);
}
@ -75,12 +75,12 @@ export class AuthService {
}
private convertOpts(opts: any): UserOptions {
let converted = new UserOptions(+opts.id,
opts.new_tasks_at_bottom === '1',
opts.show_animations === '1',
opts.show_assignee === '1',
opts.multiple_tasks_per_row === '1',
opts.language);
const converted = new UserOptions(+opts.id,
opts.new_tasks_at_bottom === '1',
opts.show_animations === '1',
opts.show_assignee === '1',
opts.multiple_tasks_per_row === '1',
opts.language);
return converted;
}
}

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, EventEmitter, Input } from '@angular/core';
import { Component, ElementRef, Input } from '@angular/core';
@Component({
selector: 'tb-context-menu-item',
@ -14,7 +14,7 @@ import { Component, ElementRef, EventEmitter, Input } from '@angular/core';
}
}`]
})
export class ContextMenuItem {
export class ContextMenuItemComponent {
@Input()
isSeparator: boolean;
@ -24,19 +24,19 @@ export class ContextMenuItem {
constructor(public el: ElementRef) {
const elem = el.nativeElement;
elem.onclick = (event) => {
elem.onclick = (event: any) => {
if (this.isSeparator || this.isCustomEvent) {
this.killEvent(event);
return;
}
};
elem.oncontextmenu = (event) => {
elem.oncontextmenu = (event: any) => {
this.killEvent(event);
}
};
}
private killEvent(event) {
private killEvent(event: any) {
event.preventDefault();
event.stopPropagation();
}

View File

@ -6,7 +6,7 @@ import { ContextMenuService } from './context-menu.service';
selector: 'tb-context-menu',
templateUrl: './context-menu.component.html'
})
export class ContextMenu {
export class ContextMenuComponent {
public isOpen = false;
constructor(public el: ElementRef,

View File

@ -1,16 +1,16 @@
import { Injectable } from '@angular/core';
import { ContextMenu } from './context-menu.component';
import { ContextMenuComponent } from './context-menu.component';
@Injectable()
export class ContextMenuService {
private menus: Array<ContextMenu> = [];
private menus: Array<ContextMenuComponent> = [];
constructor() {
document.addEventListener('click', event => { this.closeAllMenus(); });
document.addEventListener('click', _ => { this.closeAllMenus(); });
}
registerMenu(newMenu: ContextMenu) {
registerMenu(newMenu: ContextMenuComponent) {
const index = this.menus.indexOf(newMenu);
if (index === -1) {

View File

@ -6,10 +6,10 @@ import {
} from '@angular/core';
@Component({
selector: 'inline-edit',
selector: 'tb-inline-edit',
templateUrl: './inline-edit.component.html'
})
export class InlineEdit {
export class InlineEditComponent {
public isDisplay = true;
@Input() text: string;

View File

@ -10,19 +10,22 @@ import { ModalService } from './modal.service';
@Component({
selector: 'tb-modal',
templateUrl: './modal.component.html',
// tslint:disable-next-line
host: {
'(document:keyup.enter)': 'keyup($event)',
'(document:keyup.escape)': 'keyup($event)'
}
})
export class Modal implements OnInit {
export class ModalComponent implements OnInit {
// tslint:disable-next-line
@Input('modal-id') modalId = '';
// tslint:disable-next-line
@Input('modal-title') modalTitle = '';
@Input() blocking = false;
@Input() wide = false;
@ContentChild('focusMe') focusElement: any;
@ContentChild('defaultAction') defaultActionElement: any;
@ContentChild('focusMe', { static: false }) focusElement: any;
@ContentChild('defaultAction', { static: false }) defaultActionElement: any;
isOpen = false;
animate = true;
@ -39,6 +42,7 @@ export class Modal implements OnInit {
}
filterClick(event: Event): void {
// tslint:disable-next-line
event = event || window.event;
// Prevent click from propagating to modal container
@ -47,11 +51,13 @@ export class Modal implements OnInit {
}
}
private keyup(event: KeyboardEvent): void {
keyup(event: KeyboardEvent): void {
// tslint:disable-next-line
if (event.keyCode === 27) {
this.modalService.close(this.modalId, true);
}
// tslint:disable-next-line
if (event.keyCode === 13) {
this.clickDefaultAction();
}

View File

@ -2,20 +2,20 @@ import { Injectable } from '@angular/core';
import { AuthService } from '../auth/auth.service';
import { UserOptions } from '../models';
import { Modal } from './modal.component';
import { ModalComponent } from './modal.component';
@Injectable()
export class ModalService {
private modals: Array<Modal>;
private modals: Array<ModalComponent>;
private userOptions: UserOptions;
constructor(private auth: AuthService) {
constructor(public auth: AuthService) {
this.modals = [];
this.userOptions = auth.userOptions;
}
registerModal(newModal: Modal): void {
let modal = this.modals.find(modal => modal.modalId === newModal.modalId);
registerModal(newModal: ModalComponent): void {
const modal = this.modals.find(modall => modall.modalId === newModal.modalId);
// Delete existing to replace the modal
if (modal) {
@ -26,7 +26,7 @@ export class ModalService {
}
isOpen(modalId: string): boolean {
let modal = this.modals.find(modal => modal.modalId === modalId);
const modal = this.modals.find(modall => modall.modalId === modalId);
if (modal) {
return modal.isOpen;
@ -36,7 +36,7 @@ export class ModalService {
}
open(modalId: string): void {
let modal = this.modals.find(modal => modal.modalId === modalId);
const modal = this.modals.find(modall => modall.modalId === modalId);
if (modal) {
modal.animate = (this.userOptions.show_animations);
@ -52,7 +52,7 @@ export class ModalService {
}
close(modalId: string, checkBlocking = false): void {
let modal = this.modals.find(modal => modal.modalId === modalId);
const modal = this.modals.find(modall => modall.modalId === modalId);
if (modal) {
if (checkBlocking && modal.blocking) {

View File

@ -1,16 +1,16 @@
export class Activity {
constructor(public userId: number,
public text: string,
public before: any,
public after: any,
public itemType: string,
public itemId: number,
public timestamp: number) {
public text: string,
public before: any,
public after: any,
public itemType: string,
public itemId: number,
public timestamp: number) {
}
}
export class ActivitySimple {
constructor(public text: string, public timestamp: number){
constructor(public text: string, public timestamp: number) {
}
}

View File

@ -70,7 +70,7 @@ export class Board {
}
addColumn(name: string): void {
let column = new Column(0, name, this.columns.length);
const column = new Column(0, name, this.columns.length);
this.columns.push(column);
}

View File

@ -7,7 +7,7 @@ import { NotificationsService } from './notifications.service';
selector: 'tb-notifications',
templateUrl: './notifications.component.html'
})
export class Notifications {
export class NotificationsComponent {
public notes: Array<Notification>;
constructor(public notifications: NotificationsService) {
@ -20,8 +20,8 @@ export class Notifications {
});
}
private hide(note: Notification): void {
let index = this.notes.indexOf(note);
hide(note: Notification): void {
const index = this.notes.indexOf(note);
if (index >= 0) {
note.type += ' clicked';

View File

@ -3,29 +3,16 @@ import { CommonModule } from '@angular/common';
import { AuthGuard } from './auth/auth.guard';
import { AuthService } from './auth/auth.service';
import { ContextMenu } from './context-menu/context-menu.component';
import { ContextMenuItem } from './context-menu/context-menu-item.component';
import { ContextMenuComponent } from './context-menu/context-menu.component';
import { ContextMenuItemComponent } from './context-menu/context-menu-item.component';
import { ContextMenuService } from './context-menu/context-menu.service';
import { InlineEdit } from './inline-edit/inline-edit.component';
import { Modal } from './modal/modal.component';
import { InlineEditComponent } from './inline-edit/inline-edit.component';
import { ModalComponent } from './modal/modal.component';
import { ModalService } from './modal/modal.service';
import { Activity, ActivitySimple } from './models/activity.model';
import { ApiResponse } from './models/api-response.model';
import { Attachment } from './models/attachment.model';
import { AutoAction, ActionType, ActionTrigger } from './models/auto-actions.model';
import { Board } from './models/board.model';
import { Category } from './models/category.model';
import { Column } from './models/column.model';
import { Comment } from './models/comment.model';
import { IssueTracker } from './models/issue-tracker.model';
import { Notification } from './models/notification.model';
import { Task } from './models/task.model';
import { User } from './models/user.model';
import { UserOptions } from './models/user-options.model';
import { Notifications } from './notifications/notifications.component';
import { NotificationsComponent } from './notifications/notifications.component';
import { NotificationsService } from './notifications/notifications.service';
import { StringsService } from './strings/strings.service';
import { TopNav } from './top-nav/top-nav.component';
import { TopNavComponent } from './top-nav/top-nav.component';
import { Constants } from './constants';
@NgModule({
@ -33,12 +20,12 @@ import { Constants } from './constants';
CommonModule
],
declarations: [
ContextMenu,
ContextMenuItem,
InlineEdit,
Modal,
Notifications,
TopNav
ContextMenuComponent,
ContextMenuItemComponent,
InlineEditComponent,
ModalComponent,
NotificationsComponent,
TopNavComponent
],
providers: [
AuthGuard,
@ -50,12 +37,12 @@ import { Constants } from './constants';
StringsService
],
exports: [
ContextMenu,
ContextMenuItem,
InlineEdit,
Modal,
Notifications,
TopNav
ContextMenuComponent,
ContextMenuItemComponent,
InlineEditComponent,
ModalComponent,
NotificationsComponent,
TopNavComponent
]
})
export class SharedModule {}

View File

@ -13,18 +13,19 @@ import { StringsService } from '../strings/strings.service';
selector: 'tb-top-nav',
templateUrl: './top-nav.component.html'
})
export class TopNav {
export class TopNavComponent {
public strings: any;
// tslint:disable-next-line
@Input('page-name') pageName: string = '';
version: string = '';
username: string = '';
version = '';
username = '';
constructor(public constants: Constants, private router: Router,
public authService: AuthService,
private notes: NotificationsService,
private stringsService: StringsService) {
public stringsService: StringsService) {
this.version = constants.VERSION;
authService.userChanged
@ -37,7 +38,7 @@ export class TopNav {
logout(): void {
this.authService.logout().subscribe(res => {
let alert = res.alerts[0];
const alert = res.alerts[0];
this.notes.add(new Notification(alert.type, alert.text));
this.router.navigate(['']);

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>TaskBoard - Kanban App</title>
<base href="./">
<base href="/TaskBoard/dist/">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@ -15,8 +15,8 @@
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-component>
<tb-app-component>
<div class="loading">TaskBoard is Loading...</div>
</app-component>
</tb-app-component>
</body>
</html>

View File

@ -169,6 +169,7 @@
"boards_taskDescription": "Description",
"boards_taskDescriptionPlaceholder": "What needs to get done?",
"boards_taskAssignees": "Assignees",
"boards_taskActivity": "Task Activity",
"boards_taskCategories": "Categories",
"boards_taskColumn": "Column",
"boards_taskColor": "Color",

View File

@ -169,6 +169,7 @@
"boards_taskDescription": "Descripción",
"boards_taskDescriptionPlaceholder": "¿Qué se necesita hacer?",
"boards_taskAssignees": "Cesionarios",
"boards_taskActivity": "Actividad de Tarea",
"boards_taskCategories": "Categorías",
"boards_taskColumn": "Columna",
"boards_taskColor": "Color",

View File

@ -11,67 +11,54 @@
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/weak-map';
import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js/dist/zone-patch-rxjs';
(window as any).global = window;
/***************************************************************************************************

View File

@ -145,7 +145,7 @@ button {
}
.row {
@include row();
@include grid-container();
padding: 7px;
}

View File

@ -8,7 +8,7 @@
}
.half-page {
@include span-columns(9 of 18);
@include grid-column(9 of 18);
}
.calendar {

View File

@ -8,7 +8,7 @@
}
.half-page {
@include span-columns(9 of 18);
@include grid-column(9 of 18);
}
.half-modal {
@ -275,7 +275,7 @@
}
.half {
@include span-columns(4.5 of 9);
@include grid-column(4.5 of 9);
padding: 7px;
input,

View File

@ -1,7 +1,7 @@
// Bourbon and Neat
@import '../../node_modules/bourbon/core/bourbon';
@import 'neat-settings';
@import '../../node_modules/bourbon-neat/app/assets/stylesheets/neat';
@import '../../node_modules/bourbon-neat/core/neat';
// scss-base
@import 'scss-base-settings';

View File

@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}

View File

@ -14,9 +14,7 @@ import { AuthService } from '../../src/app/shared/auth/auth.service';
import { ApiInterceptor } from '../../src/app/app.api-http';
describe('ApiInterceptor', () => {
const mockAuthService = {
};
const mockAuthService = { };
beforeEach(() => {
TestBed.configureTestingModule({
@ -49,9 +47,9 @@ describe('ApiInterceptor', () => {
expect(response).toBeTruthy();
});
const req = httpMock.expectOne(req =>
req.headers.has('Content-Type') &&
req.headers.get('Content-Type') === 'application/json'
const req = httpMock.expectOne(r =>
r.headers.has('Content-Type') &&
r.headers.get('Content-Type') === 'application/json'
);
expect(req.request.method).toEqual('GET');
@ -69,9 +67,9 @@ describe('ApiInterceptor', () => {
expect(response).toBeTruthy();
});
const req = httpMock.expectOne(req =>
req.headers.has('Authorization') &&
req.headers.get('Authorization') === 'fake'
const req = httpMock.expectOne(r =>
r.headers.has('Authorization') &&
r.headers.get('Authorization') === 'fake'
);
expect(req.request.method).toEqual('POST');
@ -79,7 +77,7 @@ describe('ApiInterceptor', () => {
expect(sessionStorage.getItem('taskboard.jwt')).toEqual('newToken');
}
)
)
);
it('handles errors and clears the JWT',
inject([HttpClient, HttpTestingController],
@ -88,13 +86,13 @@ describe('ApiInterceptor', () => {
http.get('').subscribe(response => {
expect(response).toBeTruthy();
}, error => {
expect(error).toBeTruthy();
}, err => {
expect(err).toBeTruthy();
});
const req = httpMock.expectOne(req =>
req.headers.has('Content-Type') &&
req.headers.get('Content-Type') === 'application/json'
const req = httpMock.expectOne(r =>
r.headers.has('Content-Type') &&
r.headers.get('Content-Type') === 'application/json'
);
expect(req.request.method).toEqual('GET');
@ -103,7 +101,7 @@ describe('ApiInterceptor', () => {
expect(sessionStorage.getItem('Authorization')).toEqual(null);
}
)
)
);
});

View File

@ -2,8 +2,8 @@ import { AppModule } from '../../src/app/app.module';
describe('Module', () => {
it('provides an AppModule class', () => {
let appModule = new AppModule();
const appModule = new AppModule();
expect(appModule).toEqual(jasmine.any(Object));
});
})
});

View File

@ -1,4 +1,4 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
@ -8,8 +8,7 @@ import { Location } from '@angular/common';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { DragulaService } from 'ng2-dragula/ng2-dragula';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { DragulaService, DragulaModule } from 'ng2-dragula';
import {
AuthService,
@ -18,19 +17,19 @@ import {
ContextMenuService,
NotificationsService
} from '../../../src/app/shared/services';
import { Login } from '../../../src/app/login/login.component';
import { LoginComponent } from '../../../src/app/login/login.component';
import { SettingsModule } from '../../../src/app/settings/settings.module';
import { SharedModule } from '../../../src/app/shared/shared.module';
import { DashboardModule } from '../../../src/app/dashboard/dashboard.module';
import { BoardDisplay } from '../../../src/app/board/board.component';
import { BoardDisplayComponent } from '../../../src/app/board/board.component';
import { BoardService } from '../../../src/app/board/board.service';
import { ColumnDisplay } from '../../../src/app/board/column/column.component';
import { TaskDisplay } from '../../../src/app/board/task/task.component';
import { ColumnDisplayComponent } from '../../../src/app/board/column/column.component';
import { TaskDisplayComponent } from '../../../src/app/board/task/task.component';
import { RouterMock, BoardServiceMock, DragulaMock } from '../mocks';
describe('BoardDisplay', () => {
let component: BoardDisplay,
fixture: ComponentFixture<BoardDisplay>;
let component: BoardDisplayComponent;
let fixture: ComponentFixture<BoardDisplayComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -44,10 +43,10 @@ describe('BoardDisplay', () => {
DashboardModule
],
declarations: [
Login,
BoardDisplay,
ColumnDisplay,
TaskDisplay
LoginComponent,
BoardDisplayComponent,
ColumnDisplayComponent,
TaskDisplayComponent
],
providers: [
Title,
@ -71,7 +70,7 @@ describe('BoardDisplay', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(BoardDisplay);
fixture = TestBed.createComponent(BoardDisplayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -85,7 +84,7 @@ describe('BoardDisplay', () => {
component.ngOnInit();
expect(component.boardNavId).toEqual(0);
component.activeUser = <any>{ default_board_id: 2 };
component.activeUser = { default_board_id: 2 } as any;
component.ngOnInit();
const location = TestBed.get(Location);
@ -94,14 +93,14 @@ describe('BoardDisplay', () => {
});
it('sets up drag and drop during ngAfterContentInit', () => {
component.activeBoard = <any>{ columns: [
component.activeBoard = { columns: [
{ id: 1, tasks: [{}] }
] };
] } as any;
component.ngAfterContentInit();
expect((<any>component.dragula).opts.moves).toEqual(jasmine.any(Function));
expect((component.dragula as any).opts.moves).toEqual(jasmine.any(Function));
const test = (<any>component.dragula).opts.moves(null, null, {
const test = (component.dragula as any).opts.moves(null, null, {
classList: { contains: () => false }
});
expect(test).toEqual(false);
@ -119,9 +118,9 @@ describe('BoardDisplay', () => {
});
it('has a function to toggle filtered tasks', () => {
component.activeBoard = <any>{ columns: [
component.activeBoard = { columns: [
{ tasks: [{ hidefiltered: true }] }
] };
] } as any;
component.toggleFiltered();
expect(component.activeBoard.columns[0].tasks[0].hideFiltered).toEqual(false);
@ -130,20 +129,20 @@ describe('BoardDisplay', () => {
it('has a function to filter tasks', () => {
component.userFilter = -1;
component.activeBoard = <any>{
component.activeBoard = {
columns: [{
tasks: [{
assignees: []
}]
}]
};
} as any;
const task = component.activeBoard.columns[0].tasks[0];
component.filterTasks();
expect(task.filtered).toEqual(false);
task.assignees = <any>[{ id: 1 }];
task.assignees = [{ id: 1 }] as any;
component.filterTasks();
expect(task.filtered).toEqual(true);
@ -158,7 +157,7 @@ describe('BoardDisplay', () => {
component.filterTasks();
expect(task.filtered).toEqual(false);
task.categories = <any>[{ id: 1 }];
task.categories = [{ id: 1 }] as any;
component.filterTasks();
expect(task.filtered).toEqual(true);
@ -170,13 +169,13 @@ describe('BoardDisplay', () => {
it('updates the active board from a service', () => {
component.boardService.updateActiveBoard(null);
component.boardService.updateActiveBoard(<any>{});
component.boardService.updateActiveBoard({} as any);
expect(component.activeBoard).toEqual(jasmine.any(Object));
});
it('updates the active user from a service', () => {
component.auth.updateUser(<any>{ security_level: 1 });
component.auth.updateUser({ security_level: 1 } as any);
expect(component.activeUser).toEqual(jasmine.any(Object));
});

View File

@ -11,6 +11,17 @@ describe('BoardService', () => {
let service: BoardService;
let httpMock: HttpTestingController;
const testCall = (url: string, method: string, isError = false) => {
const req = httpMock.expectOne(url);
expect(req.request.method).toEqual(method);
if (isError) {
req.flush({ alerts: [{}] }, { status: 500, statusText: '' });
} else {
req.flush({ data: [] });
}
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
@ -24,197 +35,186 @@ describe('BoardService', () => {
afterEach(() => {
httpMock.verify();
})
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('lets the active board get updated', () => {
let changed = false;
service.activeBoardChanged.subscribe(() => (changed = true));
service.updateActiveBoard(<any>{});
expect(changed).toEqual(true);
});
it('gets all boards', () => {
service.getBoards().subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/boards', 'GET');
});
it('handles errors when getting all boards', () => {
service.getBoards().subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/boards', 'GET', true);
});
it('toggles the collapsed state of a column', () => {
service.toggleCollapsed(1, 1).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/users/1/cols', 'POST');
});
it('handles error when toggling collapse', () => {
service.toggleCollapsed(1, 1).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/users/1/cols', 'POST', true);
});
it('updates a board', () => {
service.updateBoard(<any>{ id: 1 }).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/boards/1', 'POST');
});
it('handles errors on board update', () => {
service.updateBoard(<any>{ id: 1 }).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/boards/1', 'POST', true);
});
it('updates a column', () => {
service.updateColumn(<any>{ id: 1 }).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/columns/1', 'POST');
});
it('handles errors on column update', () => {
service.updateColumn(<any>{ id: 1 }).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/columns/1', 'POST', true);
});
it('adds a task', () => {
service.addTask(<any>{}).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/tasks', 'POST');
});
it('handles errors on task add', () => {
service.addTask(<any>{}).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/tasks', 'POST', true);
});
it('updates a task', () => {
service.updateTask(<any>{ id: 1 }).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/tasks/1', 'POST');
});
it('handles errors on task update', () => {
service.updateTask(<any>{ id: 1 }).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/tasks/1', 'POST', true);
});
it('removes a task', () => {
service.removeTask(1).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/tasks/1', 'DELETE');
});
it('handles errors on task removal', () => {
service.removeTask(1).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/tasks/1', 'DELETE', true);
});
it('gets task activity', () => {
service.getTaskActivity(1).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/activity/task/1', 'GET');
});
it('handles errors on get task activity', () => {
service.getTaskActivity(1).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/activity/task/1', 'GET', true);
});
it('updates a comment', () => {
service.updateComment(<any>{ id: 1 }).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/comments/1', 'POST');
});
it('handles errors on comment update', () => {
service.updateComment(<any>{ id: 1 }).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/comments/1', 'POST', true);
});
it('removes a comment', () => {
service.removeComment(1).subscribe(response => {
expect(response.data.length).toEqual(0);
});
testCall('api/comments/1', 'DELETE');
});
it('handles errors on comment removal', () => {
service.removeComment(1).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/comments/1', 'DELETE', true);
});
it('refreshes the API token', () => {
service.refreshToken();
testCall('api/refresh', 'POST');
});
const testCall = (url, method, isError = false) => {
const req = httpMock.expectOne(url);
expect(req.request.method).toEqual(method);
if (isError) {
req.flush({ alerts: [{}] }, { status: 500, statusText: '' });
} else {
req.flush({ data: [] });
}
};
// it('lets the active board get updated', () => {
// let changed = false;
//
// service.activeBoardChanged.subscribe(() => (changed = true));
//
// service.updateActiveBoard(<any>{});
//
// expect(changed).toEqual(true);
// });
//
// it('gets all boards', () => {
// service.getBoards().subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/boards', 'GET');
// });
//
// it('handles errors when getting all boards', () => {
// service.getBoards().subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/boards', 'GET', true);
// });
//
// it('toggles the collapsed state of a column', () => {
// service.toggleCollapsed(1, 1).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/users/1/cols', 'POST');
// });
//
// it('handles error when toggling collapse', () => {
// service.toggleCollapsed(1, 1).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/users/1/cols', 'POST', true);
// });
//
// it('updates a board', () => {
// service.updateBoard(<any>{ id: 1 }).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/boards/1', 'POST');
// });
//
// it('handles errors on board update', () => {
// service.updateBoard(<any>{ id: 1 }).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/boards/1', 'POST', true);
// });
//
// it('updates a column', () => {
// service.updateColumn(<any>{ id: 1 }).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/columns/1', 'POST');
// });
//
// it('handles errors on column update', () => {
// service.updateColumn(<any>{ id: 1 }).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/columns/1', 'POST', true);
// });
//
// it('adds a task', () => {
// service.addTask(<any>{}).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/tasks', 'POST');
// });
//
// it('handles errors on task add', () => {
// service.addTask(<any>{}).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/tasks', 'POST', true);
// });
//
// it('updates a task', () => {
// service.updateTask(<any>{ id: 1 }).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/tasks/1', 'POST');
// });
//
// it('handles errors on task update', () => {
// service.updateTask(<any>{ id: 1 }).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/tasks/1', 'POST', true);
// });
//
// it('removes a task', () => {
// service.removeTask(1).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/tasks/1', 'DELETE');
// });
//
// it('handles errors on task removal', () => {
// service.removeTask(1).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/tasks/1', 'DELETE', true);
// });
//
// it('gets task activity', () => {
// service.getTaskActivity(1).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/activity/task/1', 'GET');
// });
//
// it('handles errors on get task activity', () => {
// service.getTaskActivity(1).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/activity/task/1', 'GET', true);
// });
//
// it('updates a comment', () => {
// service.updateComment(<any>{ id: 1 }).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/comments/1', 'POST');
// });
//
// it('handles errors on comment update', () => {
// service.updateComment(<any>{ id: 1 }).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/comments/1', 'POST', true);
// });
//
// it('removes a comment', () => {
// service.removeComment(1).subscribe(response => {
// expect(response.data.length).toEqual(0);
// });
//
// testCall('api/comments/1', 'DELETE');
// });
//
// it('handles errors on comment removal', () => {
// service.removeComment(1).subscribe(() => {}, response => {
// expect(response.alerts.length).toEqual(1);
// });
//
// testCall('api/comments/1', 'DELETE', true);
// });
//
// it('refreshes the API token', () => {
// service.refreshToken();
// testCall('api/refresh', 'POST');
// });
});

View File

@ -1,20 +1,15 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ElementRef } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { DragulaService } from 'ng2-dragula/ng2-dragula';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { DragulaService, DragulaModule } from 'ng2-dragula';
import { ColumnDisplay } from '../../../../src/app/board/column/column.component';
import { TaskDisplay } from '../../../../src/app/board/task/task.component';
import { ColumnDisplayComponent } from '../../../../src/app/board/column/column.component';
import { TaskDisplayComponent } from '../../../../src/app/board/task/task.component';
import {
AuthService,
StringsService,
Constants,
ContextMenuService,
ModalService,
NotificationsService
} from '../../../../src/app/shared/services';
@ -23,8 +18,8 @@ import { SharedModule } from '../../../../src/app/shared/shared.module';
import { User } from '../../../../src/app/shared/models';
describe('ColumnDisplay', () => {
let component: ColumnDisplay,
fixture: ComponentFixture<ColumnDisplay>;
let component: ColumnDisplayComponent;
let fixture: ComponentFixture<ColumnDisplayComponent>;
const mockTask = {
id: 1, title: 'test', description: '', color: '#ffffe0', due: Date.now,
@ -47,8 +42,8 @@ describe('ColumnDisplay', () => {
SharedModule
],
declarations: [
ColumnDisplay,
TaskDisplay
ColumnDisplayComponent,
TaskDisplayComponent
],
providers: [
DragulaService,
@ -62,7 +57,7 @@ describe('ColumnDisplay', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(ColumnDisplay);
fixture = TestBed.createComponent(ColumnDisplayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -72,9 +67,9 @@ describe('ColumnDisplay', () => {
});
it('implements OnInit', () => {
component.userOptions = <any>{ multiple_tasks_per_row: true };
component.columnData = <any>{ id: 1, task_limit: 3, tasks: [] };
component.activeUser = <any>{ collapsed: [1] };
component.userOptions = { multiple_tasks_per_row: true } as any;
component.columnData = { id: 1, task_limit: 3, tasks: [] } as any;
component.activeUser = { collapsed: [1] } as any;
component.ngOnInit();
@ -82,10 +77,10 @@ describe('ColumnDisplay', () => {
});
it('sorts tasks', () => {
component.columnData = <any>{ tasks:[
component.columnData = { tasks: [
{ position: 2, due_date: '1/1/2018', points: 1 },
{ position: 1, due_date: '1/1/2019', points: 3 }
] };
] } as any;
component.sortOption = 'pos';
component.sortTasks();
@ -101,11 +96,11 @@ describe('ColumnDisplay', () => {
});
it('calls a service to toggle collapsed state', () => {
(<any>component.boardService.toggleCollapsed) = () => {
return { subscribe: fn => fn(<any>{ data: [{}, [1]] }) };
(component.boardService.toggleCollapsed as any) = () => {
return { subscribe: (fn: any) => fn({ data: [{}, [1]] } as any) };
};
component.activeUser = <any>{ id: 1, collapsed: [] };
component.columnData = <any>{ id: 1 };
component.activeUser = { id: 1, collapsed: [] } as any;
component.columnData = { id: 1 } as any;
component.toggleCollapsed();
@ -122,7 +117,7 @@ describe('ColumnDisplay', () => {
});
it('updates task color by category', () => {
const mock = [<any>{ default_task_color: 'red' }];
const mock = [{ default_task_color: 'red' } as any];
component.updateTaskColorByCategory(mock);
expect(component.modalProps.categories).toEqual(mock);
@ -133,20 +128,20 @@ describe('ColumnDisplay', () => {
component.addTask();
expect(component.saving).toEqual(false);
(<any>component.boardService.addTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'error', alerts: [{}] }) };
(component.boardService.addTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'error', alerts: [{}] } as any) };
};
component.modalProps = <any>{ title: 'Testing' };
component.modalProps = { title: 'Testing' } as any;
component.addTask();
expect(component.saving).toEqual(false);
(<any>component.boardService.addTask) = () => {
return { subscribe: fn => fn(<any>{
(component.boardService.addTask as any) = () => {
return { subscribe: (fn: any) => fn({
status: 'success',
alerts: [],
data: [{}, {}, [{ ownColumn: [{}] }]]
}) };
} as any) };
};
component.addTask();
@ -157,20 +152,20 @@ describe('ColumnDisplay', () => {
component.updateTask();
expect(component.saving).toEqual(false);
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'error', alerts: [{}] }) };
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'error', alerts: [{}] } as any) };
};
component.modalProps = <any>{ title: 'Testing' };
component.modalProps = { title: 'Testing' } as any;
component.updateTask();
expect(component.saving).toEqual(false);
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({
status: 'success',
alerts: [],
data: [{}, {}, [{ ownColumn: [{}] }]]
}) };
} as any) };
};
component.updateTask();
@ -181,24 +176,24 @@ describe('ColumnDisplay', () => {
let called = false;
component.taskToRemove = 1;
(<any>component.boardService.removeTask) = () => {
return { subscribe: fn => {
(component.boardService.removeTask as any) = () => {
return { subscribe: (fn: any) => {
called = true;
fn(<any>{ status: 'error', alerts: [{}] });
fn({ status: 'error', alerts: [{}] } as any);
} };
};
component.removeTask();
expect(called).toEqual(true);
(<any>component.boardService.removeTask) = () => {
return { subscribe: fn => {
(component.boardService.removeTask as any) = () => {
return { subscribe: (fn: any) => {
called = true;
fn(<any>{
fn({
status: 'success',
alerts: [{}],
data: [{}, [{}]]
});
} as any);
} };
};
@ -217,51 +212,51 @@ describe('ColumnDisplay', () => {
expect(component.newComment).toEqual('');
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'error' }) };
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'error' } as any) };
};
component.addComment();
expect(component.newComment).toEqual('');
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'success', data:[{}, [
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'success', data: [{}, [
mockTask, { id: 2 }
]] }) };
]] } as any) };
};
component.activeBoard = <any>{
component.activeBoard = {
columns: [{ id: 1, tasks: [{ id: 1 }, { id: 2 }] }, { id: 2 }]
};
} as any;
component.addComment();
expect(component.viewModalProps.id).toEqual(1);
});
it('loads a comment into the editor', () => {
component.beginEditComment(<any>{ id: 1, text: 'Testing' });
component.beginEditComment({ id: 1, text: 'Testing' } as any);
expect(component.commentEdit.text).toEqual('Testing');
});
it('calls a service to edit a comment', () => {
component.activeUser = <any>{ id: 1 };
component.commentEdit = <any>{ is_edited: false, user_id: 0 };
component.activeUser = { id: 1 } as any;
component.commentEdit = { is_edited: false, user_id: 0 } as any;
(<any>component.boardService.updateComment) = () => {
return { subscribe: fn => fn(<any>{ status: 'error', alerts: [{}] }) };
(component.boardService.updateComment as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'error', alerts: [{}] } as any) };
};
component.editComment();
expect(component.commentEdit.is_edited).toEqual(true);
component.activeBoard = <any>{
component.activeBoard = {
columns: [{ id: 1, tasks: [{ id: 1 }, { id: 2 }] }, { id: 2 }]
};
} as any;
(<any>component.boardService.updateComment) = () => {
return { subscribe: fn => fn(<any>{ status: 'success', alerts: [{}],
(component.boardService.updateComment as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'success', alerts: [{}],
data: [{}, [mockTask]]
}) };
} as any) };
};
component.editComment();
@ -269,40 +264,40 @@ describe('ColumnDisplay', () => {
});
it('calls aservice to remove a comment', () => {
component.viewModalProps = <any>{ comments: [{ id: 1 }] };
component.commentToRemove = <any>{ id: 1 };
component.viewModalProps = { comments: [{ id: 1 }] } as any;
component.commentToRemove = { id: 1 } as any;
component.activeBoard = <any>{
component.activeBoard = {
columns: [
{ id: 1, tasks: [{ id: 1, text: 'test' }, { id: 2 }] },
{ id: 2 }
]
};
} as any;
(<any>component.boardService.removeComment) = () => {
return { subscribe: fn => fn(<any>{ alerts: [{}],
(component.boardService.removeComment as any) = () => {
return { subscribe: (fn: any) => fn({ alerts: [{}],
data: [{}, [mockTask]]
}) };
} as any) };
};
component.removeComment();
expect((<any>component.activeBoard.columns[0].tasks[0]).text).toEqual('test');
expect((component.activeBoard.columns[0].tasks[0] as any).text).toEqual('test');
});
it('has a function to check user comment admin', () => {
component.activeUser = <any>{
component.activeUser = {
id: 2,
isAnyAdmin: () => false
}
} as any;
expect(component.canAdminComment(<any>{ user_id: 1 })).toEqual(false);
expect(component.canAdminComment({ user_id: 1 } as any)).toEqual(false);
component.activeUser.id = 1;
expect(component.canAdminComment(<any>{ user_id: 1 })).toEqual(true);
expect(component.canAdminComment({ user_id: 1 } as any)).toEqual(true);
});
it('has a function to show the editor for column task limit', () => {
component.columnData = <any>{ task_limit: 3 };
component.columnData = { task_limit: 3 } as any;
component.beginLimitEdit();
expect(component.taskLimit).toEqual(3);
@ -315,23 +310,23 @@ describe('ColumnDisplay', () => {
});
it('calls a service to save column task limit changes', () => {
component.columnData = <any>{ task_limit: 3 };
component.columnData = { task_limit: 3 } as any;
component.taskLimit = 2;
(<any>component.boardService.updateColumn) = () => {
return { subscribe: fn => fn(<any>{ status: 'error', alerts: [{}] }) };
(component.boardService.updateColumn as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'error', alerts: [{}] } as any) };
};
component.saveLimitChanges();
expect(component.columnData.task_limit).toEqual(3);
(<any>component.boardService.updateColumn) = () => {
return { subscribe: fn => fn(<any>{ status: 'success', alerts: [], data: [
(component.boardService.updateColumn as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'success', alerts: [], data: [
{}, [{
id: 1, name: 'test', position: 1,
board_id: 1, task_limit: 2, ownTask: []
}]
] }) };
] } as any) };
};
component.saveLimitChanges();
@ -352,39 +347,39 @@ describe('ColumnDisplay', () => {
it('can add a task with only a title', () => {
let called = false;
component.quickAdd = <any>{ title: 'test' };
component.columnData = <any> { id: 1 };
component.addTask = () => { called = true };
component.quickAdd = { title: 'test' } as any;
component.columnData = { id: 1 } as any;
component.addTask = () => { called = true; };
component.quickAddClicked({ preventEnter: () => {} });
expect(called).toEqual(true);
});
it('opens a model to add a task', () => {
component.quickAdd = <any>{ title: '' };
component.columnData = <any>{ id: 1 };
component.quickAdd = { title: '' } as any;
component.columnData = { id: 1 } as any;
component.quickAddClicked({ stopPropagation: () => {} });
expect(component.modalProps.column_id).toEqual(1);
})
});
it('checks a due date against the current date', () => {
component.checkDueDate();
component.viewModalProps = <any>{ due_date: 'asdf' };
component.viewModalProps = { due_date: 'asdf' } as any;
component.checkDueDate();
const today = new Date(),
yesterday = new Date(
today.getFullYear(), today.getMonth(), today.getDay() - 1
);
const today = new Date();
const yesterday = new Date(
today.getFullYear(), today.getMonth(), today.getDay() - 1
);
component.viewModalProps = <any>{ due_date: today };
component.viewModalProps = { due_date: today } as any;
component.checkDueDate();
expect(component.isNearlyDue).toEqual(true);
component.viewModalProps = <any>{ due_date: yesterday };
component.viewModalProps = { due_date: yesterday } as any;
component.checkDueDate();
expect(component.isOverdue).toEqual(true);
@ -395,7 +390,7 @@ describe('ColumnDisplay', () => {
expect(remove).toEqual(jasmine.any(Function));
component.columnData = <any>{ id: 1 };
component.columnData = { id: 1 } as any;
remove();
expect(component.taskToRemove).toEqual(3);
@ -406,12 +401,12 @@ describe('ColumnDisplay', () => {
expect(show).toEqual(jasmine.any(Function));
component.columnData = <any>{ id: 1, tasks: [{
component.columnData = { id: 1, tasks: [{
id: 3, title: 'test', description: '', color: '#ffffe0', points: 1,
position: 1, column_id: 1, comments: [], attachments: [],
assignees: [{}], categories: [{}]
}] };
component.activeBoard = <any>{ users: [{}], categories: [{}] };
}] } as any;
component.activeBoard = { users: [{}], categories: [{}] } as any;
show();
expect(component.modalProps.column_id).toEqual(1);
@ -423,15 +418,16 @@ describe('ColumnDisplay', () => {
expect(view).toEqual(jasmine.any(Function));
component.showActivity = true;
component.columnData = <any>{ id: 1, tasks: [{
component.columnData = { id: 1, tasks: [{
id: 3, title: 'test', description: '', color: '#ffffe0', points: 1,
position: 1, column_id: 1, comments: [], attachments: [],
assignees: [{}], categories: [{}]
}] };
(<any>component.boardService.getTaskActivity) = () => {
return { subscribe: fn => fn(<any>{ data: [
}] } as any;
(component.boardService.getTaskActivity as any) = () => {
return { subscribe: (fn: any) => fn({ data: [
{}, [{ text: '', timestamp: '' }]
] }) };
] } as any) };
};
view();

View File

@ -1,15 +1,12 @@
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ElementRef } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { TaskDisplay } from '../../../../src/app/board/task/task.component';
import { TaskDisplayComponent } from '../../../../src/app/board/task/task.component';
import {
AuthService,
StringsService,
Constants,
ModalService,
NotificationsService
} from '../../../../src/app/shared/services';
@ -17,8 +14,8 @@ import { BoardService } from '../../../../src/app/board/board.service';
import { SharedModule } from '../../../../src/app/shared/shared.module';
describe('TaskDisplay', () => {
let component: TaskDisplay,
fixture: ComponentFixture<TaskDisplay>;
let component: TaskDisplayComponent;
let fixture: ComponentFixture<TaskDisplayComponent>;
const eventMock = {
target: {
@ -38,7 +35,7 @@ describe('TaskDisplay', () => {
SharedModule
],
declarations: [
TaskDisplay
TaskDisplayComponent
],
providers: [
AuthService,
@ -51,7 +48,7 @@ describe('TaskDisplay', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(TaskDisplay);
fixture = TestBed.createComponent(TaskDisplayComponent);
component = fixture.componentInstance;
});
@ -60,22 +57,22 @@ describe('TaskDisplay', () => {
});
it('implements OnInit', () => {
component.taskData = <any>{ id: 1, description: '' };
component.activeBoard = <any>{ id: 1, columns: [{ id: 1, name: 'test' }] };
component.taskData = { id: 1, description: '' } as any;
component.activeBoard = { id: 1, columns: [{ id: 1, name: 'test' }] } as any;
component.ngOnInit();
component.taskData = <any>{ id: 1, description: '', due_date: '' };
component.taskData = { id: 1, description: '', due_date: '' } as any;
component.ngOnInit();
component.taskData = <any>{ id: 1, description: '', due_date: '1/1/2018' };
component.taskData = { id: 1, description: '', due_date: '1/1/2018' } as any;
component.ngOnInit();
const today = new Date();
component.taskData = <any>{
component.taskData = {
id: 1, description: '',
due_date: (today.getMonth() + 1) + '/' +
(today.getDate() + 1) + '/' + today.getFullYear()
};
} as any;
component.ngOnInit();
expect(component.taskData.id).toEqual(1);
@ -84,11 +81,12 @@ describe('TaskDisplay', () => {
it('parses task description markdown into text', () => {
setupBoard();
component.taskData.description = '# Make this HTML';
component.activeBoard.issue_trackers = <any>[
component.activeBoard.issue_trackers = [
{ regex: 'test', url: '%BUGID%' }
];
] as any;
component.ngOnInit();
// tslint:disable-next-line
expect(component.taskData.html['changingThisBreaksApplicationSecurity'])
.toEqual('<h1 id="make-this-html">Make this HTML</h1>\n');
});
@ -98,6 +96,7 @@ describe('TaskDisplay', () => {
component.taskData.description = ' - [x] One\n - [ ] Two';
component.ngOnInit();
// tslint:disable-next-line
expect(component.taskData.html['changingThisBreaksApplicationSecurity'])
.toEqual('<ul>\n<li><i class="icon icon-check"></i>One</li>\n' +
'<li><i class="icon icon-check-empty"></i>Two</li>\n</ul>\n');
@ -108,6 +107,7 @@ describe('TaskDisplay', () => {
component.taskData.description = '[link](google.com)';
component.ngOnInit();
// tslint:disable-next-line
expect(component.taskData.html['changingThisBreaksApplicationSecurity'])
.toContain('target="tb_external" rel="noreferrer"');
});
@ -117,7 +117,8 @@ describe('TaskDisplay', () => {
const actual = component.getPercentStyle();
expect((<any>actual)['changingThisBreaksApplicationSecurity'])
// tslint:disable-next-line
expect((actual as any)['changingThisBreaksApplicationSecurity'])
.toContain('width: 50%;');
});
@ -144,8 +145,8 @@ describe('TaskDisplay', () => {
});
it('calls a service to change a task\'s column', () => {
const select = document.createElement('select'),
option = document.createElement('option');
const select = document.createElement('select');
const option = document.createElement('option');
select.id = 'columnsList1';
select.selectedIndex = 0;
@ -154,7 +155,7 @@ describe('TaskDisplay', () => {
select.appendChild(option);
document.body.appendChild(select);
component.taskData = <any>{ id: 1 };
component.taskData = { id: 1 } as any;
component.changeTaskColumn(eventMock);
const secondOpt = document.createElement('option');
@ -163,12 +164,12 @@ describe('TaskDisplay', () => {
select.appendChild(secondOpt);
select.selectedIndex = 1;
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({
status: 'success',
alerts: [{}],
data: [{}, {}, [{}]]
}) };
} as any) };
};
component.changeTaskColumn(eventMock);
@ -176,8 +177,8 @@ describe('TaskDisplay', () => {
});
it('calls a service to copy a task to another board', () => {
const sel = document.createElement('select'),
opt = document.createElement('option');
const sel = document.createElement('select');
const opt = document.createElement('option');
sel.id = 'boardsList1Copy';
sel.selectedIndex = 0;
@ -186,25 +187,25 @@ describe('TaskDisplay', () => {
sel.appendChild(opt);
document.body.appendChild(sel);
component.strings = <any>{ boards_copyTaskTo: 'Copy To' };
component.taskData = <any>{ id: 1 };
component.boardsList = <any>[
component.strings = { boards_copyTaskTo: 'Copy To' } as any;
component.taskData = { id: 1 } as any;
component.boardsList = [
{ id: 1, name: 'one', columns: [{ id: 1 }] },
{ id: 2, name: 'test' }
];
] as any;
let emitted = false;
component.onUpdateBoards.subscribe(() => emitted = true);
(<any>component.boardService.addTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'success' }) };
(component.boardService.addTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'success' } as any) };
};
component.copyTaskToBoard(eventMock);
(<any>component.boardService.addTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'asdf', alerts: [{}] }) };
(component.boardService.addTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'asdf', alerts: [{}] } as any) };
};
component.copyTaskToBoard(eventMock);
@ -213,8 +214,8 @@ describe('TaskDisplay', () => {
});
it('calls a service to move a task to another board', () => {
const sel = document.createElement('select'),
opt = document.createElement('option');
const sel = document.createElement('select');
const opt = document.createElement('option');
sel.id = 'boardsList1Move';
sel.selectedIndex = 0;
@ -223,25 +224,25 @@ describe('TaskDisplay', () => {
sel.appendChild(opt);
document.body.appendChild(sel);
component.strings = <any>{ boards_moveTaskTo: 'Move To' };
component.taskData = <any>{ id: 1 };
component.boardsList = <any>[
component.strings = { boards_moveTaskTo: 'Move To' } as any;
component.taskData = { id: 1 } as any;
component.boardsList = [
{ id: 1, name: 'one', columns: [{ id: 1 }] },
{ id: 2, name: 'test' }
];
] as any;
let emitted = false;
component.onUpdateBoards.subscribe(() => emitted = true);
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'success' }) };
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'success' } as any) };
};
component.moveTaskToBoard(eventMock);
(<any>component.boardService.updateTask) = () => {
return { subscribe: fn => fn(<any>{ status: 'asdf', alerts: [{}] }) };
(component.boardService.updateTask as any) = () => {
return { subscribe: (fn: any) => fn({ status: 'asdf', alerts: [{}] } as any) };
};
component.moveTaskToBoard(eventMock);
@ -252,16 +253,17 @@ describe('TaskDisplay', () => {
function setupBoard() {
const today = new Date();
component.activeBoard = <any>{
component.activeBoard = {
id: 1,
columns: [{ id: 1, name: 'test' }],
issue_trackers: []
};
component.taskData = <any>{
} as any;
component.taskData = {
id: 1, description: '',
due_date: (today.getMonth() + 1) + '/' +
(today.getDate() + 1) + '/' + today.getFullYear()
};
} as any;
}
});

View File

@ -1,17 +1,17 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Calendar } from '../../../../src/app/dashboard/calendar/calendar.component';
import { CalendarComponent } from '../../../../src/app/dashboard/calendar/calendar.component';
describe('Calendar', () => {
let component: Calendar,
fixture: ComponentFixture<Calendar>;
let component: CalendarComponent;
let fixture: ComponentFixture<CalendarComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
],
declarations: [
Calendar
CalendarComponent
],
providers: [
]
@ -19,7 +19,7 @@ describe('Calendar', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Calendar);
fixture = TestBed.createComponent(CalendarComponent);
component = fixture.componentInstance;
});

View File

@ -1,14 +1,14 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { Title } from '@angular/platform-browser';
import { Charts } from '../../../../src/app/dashboard/charts/charts.component';
import { ChartsComponent } from '../../../../src/app/dashboard/charts/charts.component';
import { DashboardModule } from '../../../../src/app/dashboard/dashboard.module';
describe('Charts', () => {
let component: Charts,
fixture: ComponentFixture<Charts>;
let component: ChartsComponent;
let fixture: ComponentFixture<ChartsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -24,7 +24,7 @@ describe('Charts', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Charts);
fixture = TestBed.createComponent(ChartsComponent);
component = fixture.componentInstance;
});

View File

@ -1,14 +1,14 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { Title } from '@angular/platform-browser';
import { Dashboard } from '../../../src/app/dashboard/dashboard.component';
import { DashboardComponent } from '../../../src/app/dashboard/dashboard.component';
import { DashboardModule } from '../../../src/app/dashboard/dashboard.module';
describe('DashBoard', () => {
let component: Dashboard,
fixture: ComponentFixture<Dashboard>;
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -24,7 +24,7 @@ describe('DashBoard', () => {
});
it('can be constructed', () => {
fixture = TestBed.createComponent(Dashboard);
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
expect(component).toBeTruthy();

View File

@ -1,10 +1,10 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { Login } from '../../../src/app/login/login.component';
import { SharedModule } from '../../../src/app/shared/shared.module'
import { LoginComponent } from '../../../src/app/login/login.component';
import { SharedModule } from '../../../src/app/shared/shared.module';
import {
Constants,
AuthService,
@ -12,8 +12,8 @@ import {
} from '../../../src/app/shared/services';
describe('Login', () => {
let component: Login,
fixture: ComponentFixture<Login>;
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -24,7 +24,7 @@ describe('Login', () => {
SharedModule
],
declarations: [
Login
LoginComponent
],
providers: [
RouterTestingModule,
@ -36,7 +36,7 @@ describe('Login', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Login);
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
});
@ -45,11 +45,11 @@ describe('Login', () => {
});
it('implements OnInit', () => {
(<any>component.authService).authenticate = () => {
return { subscribe: fn => { fn(true); } };
(component.authService as any).authenticate = () => {
return { subscribe: (fn: any) => { fn(true); } };
};
let spy = spyOn((<any>component).router, 'navigate');
const spy = spyOn((component as any).router, 'navigate');
component.ngOnInit();
expect(spy).toHaveBeenCalledWith(['/boards']);
@ -58,7 +58,7 @@ describe('Login', () => {
it('requires a username and password to log in', () => {
let called = false;
(<any>component).notes = { add: () => { called = true; } };
(component as any).notes = { add: () => { called = true; } };
component.login();
expect(called).toEqual(true);
@ -74,11 +74,11 @@ describe('Login', () => {
component.username = 'name';
component.password = 'pass';
(<any>component.authService).login = () => {
return { subscribe: fn => fn({ status: 'success', alerts:[{}] }) };
(component.authService as any).login = () => {
return { subscribe: (fn: any) => fn({ status: 'success', alerts: [{}] }) };
};
let spy = spyOn((<any>component).router, 'navigate');
const spy = spyOn((component as any).router, 'navigate');
component.login();
expect(spy).toHaveBeenCalledWith(['/boards']);

View File

@ -1,4 +1,4 @@
import { TestBed } from '@angular/core/testing'
import { TestBed } from '@angular/core/testing';
import { Location } from '@angular/common';
import { BehaviorSubject } from 'rxjs';
@ -7,35 +7,46 @@ import { User, Board } from '../../src/app/shared/models';
export class RouterMock {
public url = {
indexOf: str => TestBed.get(Location).path().indexOf(str)
}
indexOf: (str: string) => TestBed.get(Location).path().indexOf(str)
};
navigate(arr) {
navigate(arr: any[]) {
TestBed.get(Location).go(arr[0]);
}
}
export class DragulaMock {
public opts;
public dropModel = new BehaviorSubject([
{},
{ id: '1' },
{ parentNode: { id: '1' } },
{ parentNode: { id: '1' } }
]);
public opts: any;
public dragend = {
subscribe: (fn) => { fn(); }
subscribe: (fn: any) => { fn(); }
};
find () {
find() {
return { drake: {
containers: []
} };
}
destroy () {}
dropModel() {
return {
subscribe: (fn: any) => {
fn([
{},
{ id: '1' },
{ parentNode: { id: '1' } },
{ parentNode: { id: '1' } }
]);
}
};
}
setOptions (name, opts) {
destroy() {}
createGroup(_: string, opts: any) {
this.opts = opts;
}
setOptions(_: any, opts: any) {
this.opts = opts;
}
}
@ -44,17 +55,17 @@ export class BoardServiceMock {
public activeBoardChanged =
new BehaviorSubject({ id: 0, name: 'Test', columns: [] });
getBoards () {
getBoards() {
return new BehaviorSubject({
data: [{}, [{ id: 1, name: 'Test', is_active: '1' }]]
});
}
updateActiveBoard (board) {
updateActiveBoard(board: any) {
this.activeBoardChanged.next(board);
}
updateColumn (col) {
updateColumn(_: any) {
return new BehaviorSubject({});
}
}
@ -66,21 +77,21 @@ export class SettingsServiceMock {
{ columns: [{ position: 3 }, { position: 2 }] }
]);
updateBoards () { }
updateBoards() { }
updateActions () { }
updateActions() { }
updateUsers () { }
updateUsers() { }
getUsers () {
getUsers() {
return new BehaviorSubject({ data: [{}, [new User()]] });
}
getBoards () {
getBoards() {
return new BehaviorSubject({ data: [{}, [new Board()]] });
}
getActions () {
getActions() {
return new BehaviorSubject({ data: [{}, []] });
}
}
@ -96,14 +107,14 @@ export class AuthServiceMock {
language: 'en'
};
authenticate () { return new BehaviorSubject(true); }
updateUser () { }
authenticate() { return new BehaviorSubject(true); }
updateUser() { }
}
export class NotificationsServiceMock {
public noteAdded = new BehaviorSubject({});
addNote (note) {
addNote(note: any) {
this.noteAdded.next(note);
}
}

View File

@ -1,8 +1,7 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { SharedModule } from '../../../../src/app/shared/shared.module';
@ -21,12 +20,12 @@ import {
AutoActionsService
} from '../../../../src/app/settings/auto-actions/auto-actions.service';
import {
AutoActions
AutoActionsComponent
} from '../../../../src/app/settings/auto-actions/auto-actions.component';
describe('AutoActions', () => {
let component: AutoActions,
fixture: ComponentFixture<AutoActions>;
let component: AutoActionsComponent;
let fixture: ComponentFixture<AutoActionsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -37,7 +36,7 @@ describe('AutoActions', () => {
SharedModule
],
declarations: [
AutoActions
AutoActionsComponent
],
providers: [
AuthService,
@ -51,7 +50,7 @@ describe('AutoActions', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(AutoActions);
fixture = TestBed.createComponent(AutoActionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -61,18 +60,18 @@ describe('AutoActions', () => {
});
it('updates the list of automatic actions', () => {
component.boards = <any>[
component.boards = [
{ is_active: false, id: 1, name: 'First' },
{ is_active: true, id: 2, name: 'Second' }
];
component.updateActions(<any>[{ board_id: 1 }, { board_id: 2 }]);
] as any;
component.updateActions([{ board_id: 1 }, { board_id: 2 }] as any);
expect(component.loading).toEqual(false);
});
it('calls a service to add a new action', () => {
(<any>component.actions).addAction = () => {
return { subscribe: fn => { fn({ alerts: [{}], data: [{}, []] }); } };
(component.actions as any).addAction = () => {
return { subscribe: (fn: any) => { fn({ alerts: [{}], data: [{}, []] }); } };
};
component.addNewAction();
@ -81,46 +80,46 @@ describe('AutoActions', () => {
});
it('updates trigger sources', () => {
component.boards = <any>[{
component.boards = [{
id: 1,
columns: [{ id: 1, name: 'TestCol' }],
users: [{ id: 1, username: 'tester' }],
categories: [{ id: 1, name: 'TestCat' }]
}, { id: 2 }];
}, { id: 2 }] as any;
component.newAction = <any>{
component.newAction = {
trigger: ActionTrigger.MovedToColumn,
board_id: 1
};
} as any;
component.updateTriggerSources();
expect(component.triggerSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
trigger: ActionTrigger.AssignedToUser,
board_id: 1
};
} as any;
component.updateTriggerSources();
expect(component.triggerSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
trigger: ActionTrigger.AddedToCategory,
board_id: 1
};
} as any;
component.updateTriggerSources();
expect(component.triggerSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
trigger: ActionTrigger.PointsChanged,
board_id: 1
};
} as any;
component.updateTriggerSources();
expect(component.types.length).toEqual(1);
component.newAction = <any>{ trigger: -1 };
component.newAction = { trigger: -1 } as any;
component.typesList = null;
component.updateTriggerSources();
@ -128,53 +127,53 @@ describe('AutoActions', () => {
});
it('updates action sources', () => {
component.boards = <any>[{
component.boards = [{
id: 1,
columns: [{ id: 1, name: 'TestCol' }],
users: [{ id: 1, username: 'tester' }],
categories: [{ id: 1, name: 'TestCat' }]
}, { id: 2 }];
}, { id: 2 }] as any;
component.newAction = <any>{
component.newAction = {
type: ActionType.SetCategory,
board_id: 1
};
} as any;
component.updateActionSources();
expect(component.actionSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
type: ActionType.AddCategory,
board_id: 1
};
} as any;
component.updateActionSources();
expect(component.actionSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
type: ActionType.SetAssignee,
board_id: 1
};
} as any;
component.updateActionSources();
expect(component.actionSources.length).toEqual(2);
component.newAction = <any>{
component.newAction = {
type: ActionType.AddAssignee,
board_id: 1
};
} as any;
component.updateActionSources();
expect(component.actionSources.length).toEqual(2);
component.newAction = <any>{ type: ActionType.SetColor };
component.newAction = { type: ActionType.SetColor } as any;
component.updateActionSources();
expect(component.newAction.change_to).toEqual('#000000');
});
it('provides the name of a board by its ID', () => {
component.boards = <any>[{ id: 1, name: 'Test' }];
component.boards = [{ id: 1, name: 'Test' }] as any;
let actual = component.getBoardName(1);
expect(actual).toEqual('Test*');
@ -190,18 +189,18 @@ describe('AutoActions', () => {
settings_triggerAddedToCategory: 'Added To Category',
settings_triggerPointsChanged: 'Points Changed'
};
component.boards = <any>[{
component.boards = [{
id: 1,
columns: [{ id: 1, name: 'Test' }],
users: [{ id: 1, username: 'tester' }],
categories: [{ id: 1, name: 'Test' }]
}];
}] as any;
const action = <any>{
const action = {
source_id: 1,
board_id: 0,
trigger: ActionTrigger.MovedToColumn
};
} as any;
let actual = component.getTriggerDescription(action);
expect(actual).toEqual(undefined);
@ -228,20 +227,20 @@ describe('AutoActions', () => {
});
it('provides HTML for the description of an action\'s type', () => {
component.boards = <any>[{
component.boards = [{
id: 1,
columns: [{ id: 1, name: 'Test' }],
users: [{ id: 1, username: 'tester' }],
categories: [{ id: 1, name: 'Test' }]
}];
}] as any;
const action = <any>{
const action = {
change_to: 'red',
board_id: 0,
type: ActionType.SetColor
},
safeValuePre = 'SafeValue must use [property]=binding: undefined ',
safeValuePost = ' (see http://g.co/ng/security#xss)';
} as any;
const safeValuePre = 'SafeValue must use [property]=binding: undefined ';
const safeValuePost = ' (see http://g.co/ng/security#xss)';
let actual = component.getTypeDescription(action);
expect(actual).toEqual(undefined);
@ -298,8 +297,8 @@ describe('AutoActions', () => {
});
it('calls aservice to remove an automatic action', () => {
(<any>component.actions).removeAction = () => {
return { subscribe: fn => { fn({ alerts: [], data: [{}, []] }); } };
(component.actions as any).removeAction = () => {
return { subscribe: (fn: any) => { fn({ alerts: [], data: [{}, []] }); } };
};
component.saving = true;

View File

@ -1,4 +1,4 @@
import { TestBed, getTestBed } from '@angular/core/testing'
import { TestBed, getTestBed } from '@angular/core/testing';
import {
HttpClientTestingModule,
HttpTestingController
@ -26,14 +26,14 @@ describe('AutoActionsService', () => {
afterEach(() => {
httpMock.verify();
})
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('adds an action', () => {
service.addAction(<any>{}).subscribe(response => {
service.addAction({} as any).subscribe(response => {
expect(response.data.length).toEqual(0);
});
@ -41,7 +41,7 @@ describe('AutoActionsService', () => {
});
it('handles errors when adding an action', () => {
service.addAction(<any>{}).subscribe(() => {}, response => {
service.addAction({} as any).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
@ -49,7 +49,7 @@ describe('AutoActionsService', () => {
});
it('removes an action', () => {
service.removeAction(<any>{ id: 1 }).subscribe(response => {
service.removeAction({ id: 1 } as any).subscribe(response => {
expect(response.data.length).toEqual(0);
});
@ -57,14 +57,14 @@ describe('AutoActionsService', () => {
});
it('handles errors when removing an action', () => {
service.removeAction(<any>{ id: 1 }).subscribe(() => {}, response => {
service.removeAction({ id: 1 } as any).subscribe(() => {}, response => {
expect(response.alerts.length).toEqual(1);
});
testCall('api/autoactions/1', 'DELETE', true);
});
const testCall = (url, method, isError = false) => {
const testCall = (url: string, method: string, isError = false) => {
const req = httpMock.expectOne(url);
expect(req.request.method).toEqual(method);

View File

@ -1,10 +1,9 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { DragulaService } from 'ng2-dragula/ng2-dragula';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { DragulaService, DragulaModule } from 'ng2-dragula';
import { SharedModule } from '../../../../src/app/shared/shared.module';
@ -22,14 +21,14 @@ import { Board } from '../../../../src/app/shared/models';
import { DragulaMock, SettingsServiceMock, AuthServiceMock } from '../../mocks';
import {
BoardAdmin
BoardAdminComponent
} from '../../../../src/app/settings/board-admin/board-admin.component';
describe('BoardAdmin', () => {
let component: BoardAdmin,
fixture: ComponentFixture<BoardAdmin>;
let component: BoardAdminComponent;
let fixture: ComponentFixture<BoardAdminComponent>;
const getPrivateFunction = name => component[name].bind(component);
const getPrivateFunction = (name: any) => component[name].bind(component);
beforeEach(() => {
TestBed.configureTestingModule({
@ -41,7 +40,7 @@ describe('BoardAdmin', () => {
DragulaModule
],
declarations: [
BoardAdmin
BoardAdminComponent
],
providers: [
AuthService,
@ -57,7 +56,7 @@ describe('BoardAdmin', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(BoardAdmin);
fixture = TestBed.createComponent(BoardAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -67,38 +66,32 @@ describe('BoardAdmin', () => {
});
it('sets up drag and drop during ngAfterContentInit', () => {
component.modalProps = <any>{ columns: [{ position: '' }] };
component.modalProps = { columns: [{ position: '' }] } as any;
component.ngAfterContentInit();
expect((<any>component.dragula).opts.moves).toEqual(jasmine.any(Function));
expect(component.modalProps.columns[0].position).toEqual('0');
const test = (<any>component.dragula).opts.moves(null, null, {
classList: { contains: () => false }
});
expect(test).toEqual(false);
expect(component.modalProps.columns[0].position).toEqual('');
});
it('validates a board before saving', () => {
component.modalProps = <any>{ columns: [] };
component.modalProps = { columns: [] } as any;
component.addEditBoard();
expect(component.saving).toEqual(false);
});
it('calls a service to add a board', () => {
component.modalProps = <any>{
component.modalProps = {
title: 'Add',
name: 'Test',
columns: [{}]
};
component.users = <any>[{ selected: true }];
} as any;
component.users = [{ selected: true }] as any;
let called = false;
(<any>component.modal).isOpen = () => true;
(<any>component.boardService).addBoard = () => {
return { subscribe: fn => {
(component.modal as any).isOpen = () => true;
(component.boardService as any).addBoard = () => {
return { subscribe: (fn: any) => {
const board = new Board();
fn({ status: 'success', alerts: [{}], data: [{}, [board]] });
called = true;
@ -110,18 +103,18 @@ describe('BoardAdmin', () => {
});
it('calls a service to edit a board', () => {
component.modalProps = <any>{
component.modalProps = {
title: '',
name: '',
columns: [{}]
};
component.users = <any>[{}];
} as any;
component.users = [{}] as any;
let called = false;
(<any>component.modal).isOpen = () => true;
(<any>component.boardService).editBoard = () => {
return { subscribe: fn => {
(component.modal as any).isOpen = () => true;
(component.boardService as any).editBoard = () => {
return { subscribe: (fn: any) => {
const board = new Board();
fn({ status: 'success', alerts: [{}], data: [{}, [board]] });
called = true;
@ -139,17 +132,17 @@ describe('BoardAdmin', () => {
it('calls a service to remove a board', () => {
let called = false;
(<any>component.boardService).removeBoard = () => {
return { subscribe: fn => fn({ alerts: [], data: [{}, []] }) };
(component.boardService as any).removeBoard = () => {
return { subscribe: (fn: any) => fn({ alerts: [], data: [{}, []] }) };
};
(<any>component.settings).getActions = () => {
return { subscribe: fn => {
(component.settings as any).getActions = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [], data: [{}, []] });
called = true;
} };
};
component.boardToRemove = <any>{ id: 1 };
component.boardToRemove = { id: 1 } as any;
component.removeBoard();
expect(called).toEqual(true);
@ -158,26 +151,26 @@ describe('BoardAdmin', () => {
it('calls a service to toggle a board\'s status', () => {
let called = false;
(<any>component.boardService).editBoard = () => {
return { subscribe: fn => {
(component.boardService as any).editBoard = () => {
return { subscribe: (fn: any) => {
const board = new Board();
fn({ status: 'success', alerts: [{}], data: [{}, [board]] });
called = true;
} };
};
component.toggleBoardStatus(<any>{
component.toggleBoardStatus({
id: 1, name: 'Name', is_active: true, columns: [],
categories: [], issue_trackers: [], users: []
});
} as any);
expect(called).toEqual(true);
});
it('can filter the list of boards by user', () => {
component.boards = <any>[
component.boards = [
{ users: [{ id: 1 }] }
];
] as any;
component.filterBoards();
expect(component.displayBoards.length).toEqual(1);
@ -194,9 +187,9 @@ describe('BoardAdmin', () => {
});
it('can filter the list of boards by status', () => {
component.boards = <any>[
component.boards = [
{ is_active: true, users: [] }
];
] as any;
component.filterBoards();
expect(component.displayBoards.length).toEqual(1);
@ -213,10 +206,10 @@ describe('BoardAdmin', () => {
});
it('sorts the list of boards after filtering', () => {
component.boards = <any>[
component.boards = [
{ id: 1, name: 'last' },
{ id: 2, name: 'first' }
];
] as any;
component.sortFilter = 'name-asc';
component.filterBoards();
@ -248,7 +241,7 @@ describe('BoardAdmin', () => {
it('can get a property value for the modal', () => {
const getPropertyValue = getPrivateFunction('getPropertyValue');
component.modalProps = <any>{ columns: [{ name: 'test' }] };
component.modalProps = { columns: [{ name: 'test' }] } as any;
const actual = getPropertyValue('columns', 'name', 0);
expect(actual).toEqual('test');
@ -256,7 +249,7 @@ describe('BoardAdmin', () => {
it('handles a property change', () => {
const onPropertyEdit = getPrivateFunction('onPropertyEdit');
component.modalProps = <any>{ columns: [{ name: 'test' }] };
component.modalProps = { columns: [{ name: 'test' }] } as any;
onPropertyEdit('columns', 'name', 0, 'changed');
expect(component.modalProps.columns[0].name).toEqual('changed');
@ -274,7 +267,7 @@ describe('BoardAdmin', () => {
it('can set a category color', () => {
const setCategoryColor = getPrivateFunction('setCategoryColor');
component.modalProps = <any>{ categories: [{}] };
component.modalProps = { categories: [{}] } as any;
setCategoryColor('purple', 0);
const actual = component.modalProps.categories[0].default_task_color;
@ -284,10 +277,10 @@ describe('BoardAdmin', () => {
it('can show a modal', () => {
const showModal = getPrivateFunction('showModal');
component.users = <any>[{ selected: true }];
component.users = [{ selected: true }] as any;
showModal('Add');
expect((<any>component.users[0]).selected).toEqual(false);
expect((component.users[0] as any).selected).toEqual(false);
showModal('Edit', new Board());
});
@ -295,9 +288,9 @@ describe('BoardAdmin', () => {
it('can show a confirmation modal', () => {
const showConfirmModal = getPrivateFunction('showConfirmModal');
showConfirmModal(<any>{ works: true });
showConfirmModal({ works: true } as any);
expect((<any>component.boardToRemove).works).toEqual(true);
expect((component.boardToRemove as any).works).toEqual(true);
});
});

View File

@ -1,17 +1,17 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { Title } from '@angular/platform-browser';
import { Settings } from '../../../src/app/settings/settings.component';
import { SettingsComponent } from '../../../src/app/settings/settings.component';
import { SettingsModule } from '../../../src/app/settings/settings.module';
import { SettingsService } from '../../../src/app/settings/settings.service';
import { StringsService } from '../../../src/app/shared/services';
describe('Settings', () => {
let component: Settings,
fixture: ComponentFixture<Settings>;
let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -28,7 +28,7 @@ describe('Settings', () => {
provide: StringsService,
useValue: {
stringsChanged: {
subscribe: fn => {
subscribe: (fn: any) => {
fn({ settings: 'Settings' });
return { unsubscribe: () => {} };
}
@ -40,13 +40,13 @@ describe('Settings', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Settings);
fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('sets the title when constructed', () => {
expect((<any>component).title.getTitle()).toEqual('TaskBoard - Settings');
expect((component as any).title.getTitle()).toEqual('TaskBoard - Settings');
});
});

View File

@ -1,4 +1,4 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
@ -19,17 +19,15 @@ import { User } from '../../../../src/app/shared/models';
import { SettingsServiceMock, AuthServiceMock } from '../../mocks';
import {
UserDisplay, ModalUser
UserDisplay
} from '../../../../src/app/settings/user-admin/user-admin.models';
import {
UserAdmin
UserAdminComponent
} from '../../../../src/app/settings/user-admin/user-admin.component';
describe('UserAdmin', () => {
let component: UserAdmin,
fixture: ComponentFixture<UserAdmin>;
const getPrivateFunction = name => component[name].bind(component);
let component: UserAdminComponent;
let fixture: ComponentFixture<UserAdminComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -40,7 +38,7 @@ describe('UserAdmin', () => {
SharedModule
],
declarations: [
UserAdmin
UserAdminComponent
],
providers: [
ModalService,
@ -54,7 +52,7 @@ describe('UserAdmin', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(UserAdmin);
fixture = TestBed.createComponent(UserAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -66,24 +64,24 @@ describe('UserAdmin', () => {
it('calls a service to add a user', () => {
let called = false;
(<any>component.modal).isOpen = () => true;
(<any>component.userService).addUser = () => {
return { subscribe: fn => {
(component.modal as any).isOpen = () => true;
(component.userService as any).addUser = () => {
return { subscribe: (fn: any) => {
const user = new User();
fn({ status: 'success', alerts: [{}], data: [{}, [user]] });
called = true;
} };
};
component.modalProps = <any>{
component.modalProps = {
prefix: true,
user: <any>{
user: {
username: 'test',
password: 'pass',
password_verify: 'pass',
email: 'you@me.net'
}
};
} as any
} as any;
component.addEditUser();
expect(called).toEqual(true);
@ -92,12 +90,11 @@ describe('UserAdmin', () => {
it('calls a service to edit a user', () => {
let called = false;
(<any>component.modal).isOpen = () => true;
(<any>component.userService).editUser = () => {
return { subscribe: fn => {
(component.modal as any).isOpen = () => true;
(component.userService as any).editUser = () => {
return { subscribe: (fn: any) => {
const user = new User();
user.board_access = [1];
const mUser = new ModalUser(user);
fn({ status: 'success',
alerts: [{}],
@ -106,15 +103,15 @@ describe('UserAdmin', () => {
} };
};
component.modalProps = <any>{
component.modalProps = {
prefix: false,
user: <any>{
user: {
username: 'test',
password: 'pass',
password_verify: 'pass',
email: 'you@me.net'
}
};
} as any
} as any;
component.addEditUser();
expect(called).toEqual(true);
@ -123,8 +120,8 @@ describe('UserAdmin', () => {
it('calls a service to remove a user', () => {
let called = false;
(<any>component.userService).removeUser = () => {
return { subscribe: fn => {
(component.userService as any).removeUser = () => {
return { subscribe: (fn: any) => {
fn({ status: 'success', alerts: [{}], data: [{}, []] });
called = true;
} };
@ -138,21 +135,21 @@ describe('UserAdmin', () => {
it('validates modal user data', () => {
let called = false;
(<any>component.userService).addUser = () => {
(component.userService as any).addUser = () => {
return { subscribe: fn => {
const user = new User();
fn({ status: 'success', alerts: [{}], data: [{}, [user]] });
called = true;
} };
};
(<any>component.modal).isOpen = () => true;
(component.modal as any).isOpen = () => true;
component.modalProps = <any>{
component.modalProps = {
prefix: true,
user: {
username: ''
}
};
} as any;
component.addEditUser();
expect(called).toEqual(false);

View File

@ -1,4 +1,4 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
@ -7,7 +7,6 @@ import { SharedModule } from '../../../../src/app/shared/shared.module';
import {
AuthService,
ModalService,
NotificationsService,
StringsService
} from '../../../../src/app/shared/services';
@ -15,18 +14,15 @@ import { SettingsService } from '../../../../src/app/settings/settings.service';
import {
UserSettingsService
} from '../../../../src/app/settings/user-settings/user-settings.service';
import { User } from '../../../../src/app/shared/models';
import { SettingsServiceMock, AuthServiceMock } from '../../mocks';
import {
UserSettings
UserSettingsComponent
} from '../../../../src/app/settings/user-settings/user-settings.component';
describe('UserSettings', () => {
let component: UserSettings,
fixture: ComponentFixture<UserSettings>;
const getPrivateFunction = name => component[name].bind(component);
let component: UserSettingsComponent;
let fixture: ComponentFixture<UserSettingsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -37,7 +33,7 @@ describe('UserSettings', () => {
SharedModule
],
declarations: [
UserSettings
UserSettingsComponent
],
providers: [
NotificationsService,
@ -50,7 +46,7 @@ describe('UserSettings', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(UserSettings);
fixture = TestBed.createComponent(UserSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -62,8 +58,8 @@ describe('UserSettings', () => {
it('updates user options and calls a service', () => {
let called = false;
(<any>component.users).changeUserOptions = () => {
return { subscribe: fn => {
(component.users as any).changeUserOptions = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [{}] });
called = true;
} };
@ -102,8 +98,8 @@ describe('UserSettings', () => {
it('calls a service to update a user\'s default board', () => {
let called = false;
(<any>component.users).changeDefaultBoard = () => {
return { subscribe: fn => {
(component.users as any).changeDefaultBoard = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [], data: [{}, '{}'] });
called = true;
} };
@ -118,8 +114,8 @@ describe('UserSettings', () => {
it('calls a service to update a user\'s password', () => {
let called = false;
(<any>component.users).changePassword = () => {
return { subscribe: fn => {
(component.users as any).changePassword = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [], data: [{}, '{}'] });
called = true;
} };
@ -128,11 +124,11 @@ describe('UserSettings', () => {
component.updatePassword();
expect(called).toEqual(false);
component.changePassword = <any>{ current: 'test', newPass: 'tester', verPass: 'test' };
component.changePassword = { current: 'test', newPass: 'tester', verPass: 'test' } as any;
component.updatePassword();
expect(called).toEqual(false);
component.changePassword = <any>{ current: 'test', newPass: 'tester', verPass: 'tester' };
component.changePassword = { current: 'test', newPass: 'tester', verPass: 'tester' } as any;
component.updatePassword();
expect(called).toEqual(true);
@ -141,8 +137,8 @@ describe('UserSettings', () => {
it('calls a service to update a user\'s username', () => {
let called = false;
(<any>component.users).changeUsername = () => {
return { subscribe: fn => {
(component.users as any).changeUsername = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [], data: [{}, '{}'] });
called = true;
} };
@ -151,7 +147,7 @@ describe('UserSettings', () => {
component.updateUsername();
expect(called).toEqual(false);
component.changeUsername = <any>{ newName: 'test' };
component.changeUsername = { newName: 'test' } as any;
component.updateUsername();
expect(called).toEqual(true);
@ -160,18 +156,18 @@ describe('UserSettings', () => {
it('calls a service to update a user\'s email', () => {
let called = false;
(<any>component.users).changeEmail = () => {
return { subscribe: fn => {
(component.users as any).changeEmail = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [], data: [{}, '{}'] });
called = true;
} };
};
component.changeEmail = <any>{ newEmail: 'test' };
component.changeEmail = { newEmail: 'test' } as any;
component.updateEmail();
expect(called).toEqual(false);
component.changeEmail = <any>{ newEmail: 'test@test.net' };
component.changeEmail = { newEmail: 'test@test.net' } as any;
component.updateEmail();
expect(called).toEqual(true);

View File

@ -2,25 +2,25 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, ViewChild } from '@angular/core';
import {
ContextMenuItem
ContextMenuItemComponent
} from '../../../../src/app/shared/context-menu/context-menu-item.component';
@Component({
selector: 'host-component',
selector: 'tb-host-component',
template: `<tb-context-menu-item [isSeparator]="true"></tb-context-menu-item>`
})
class TestHostComponent {
@ViewChild(ContextMenuItem)
public menuItem: ContextMenuItem;
@ViewChild(ContextMenuItemComponent, { static: false })
public menuItem: ContextMenuItemComponent;
}
describe('ContextMenuItem', () => {
let hostComponent: TestHostComponent,
hostFixture: ComponentFixture<TestHostComponent>;
let hostComponent: TestHostComponent;
let hostFixture: ComponentFixture<TestHostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ ContextMenuItem, TestHostComponent ]
declarations: [ ContextMenuItemComponent, TestHostComponent ]
}).compileComponents();
});
@ -35,8 +35,8 @@ describe('ContextMenuItem', () => {
});
it('handles clicks on the element', () => {
let pdCalled = false,
spCalled = false;
let pdCalled = false;
let spCalled = false;
const evt = {
preventDefault: () => {
@ -54,8 +54,8 @@ describe('ContextMenuItem', () => {
});
it('handles context menu clicks on the element', () => {
let pdCalled = false,
spCalled = false;
let pdCalled = false;
let spCalled = false;
const evt = {
preventDefault: () => {

View File

@ -1,6 +1,5 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ElementRef } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { SharedModule } from '../../../../src/app/shared/shared.module';
@ -8,7 +7,7 @@ import {
ContextMenuService
} from '../../../../src/app/shared/context-menu/context-menu.service';
import {
ContextMenu
ContextMenuComponent
} from '../../../../src/app/shared/context-menu/context-menu.component';
class ElementRefMock {
@ -16,9 +15,9 @@ class ElementRefMock {
}
describe('ContextMenu', () => {
let component: ContextMenu,
fixture: ComponentFixture<ContextMenu>,
elementRef: ElementRefMock;
let component: ContextMenuComponent;
let fixture: ComponentFixture<ContextMenuComponent>;
let elementRef: ElementRefMock;
beforeEach(() => {
elementRef = new ElementRefMock();
@ -33,7 +32,7 @@ describe('ContextMenu', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(ContextMenu);
fixture = TestBed.createComponent(ContextMenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -43,7 +42,7 @@ describe('ContextMenu', () => {
});
it('captures parent oncontextmenu events', () => {
let parentElement = component.el.nativeElement.parentElement;
const parentElement = component.el.nativeElement.parentElement;
expect(parentElement.oncontextmenu).toEqual(jasmine.any(Function));

View File

@ -1,14 +1,14 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { SharedModule } from '../../../../src/app/shared/shared.module';
import {
InlineEdit
InlineEditComponent
} from '../../../../src/app/shared/inline-edit/inline-edit.component';
describe('InlineEdit', () => {
let component: InlineEdit,
fixture: ComponentFixture<InlineEdit>;
let component: InlineEditComponent;
let fixture: ComponentFixture<InlineEditComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -17,7 +17,7 @@ describe('InlineEdit', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(InlineEdit);
fixture = TestBed.createComponent(InlineEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -32,7 +32,7 @@ describe('InlineEdit', () => {
let called = false;
const el = { focus: () => called = true };
component.beginEdit(<any>el);
component.beginEdit(el as any);
expect(component.isDisplay).toEqual(false);
setTimeout(() => {
@ -46,7 +46,7 @@ describe('InlineEdit', () => {
const event = { stopPropagation: () => called = true };
component.editDone('test');
component.editDone('test', <any>event);
component.editDone('test', event as any);
expect(called).toEqual(true);
expect(component.isDisplay).toEqual(true);

View File

@ -6,11 +6,11 @@ import { FormsModule } from '@angular/forms';
import { SharedModule } from '../../../../src/app/shared/shared.module';
import { ModalService } from '../../../../src/app/shared/services';
import { Modal } from '../../../../src/app/shared/modal/modal.component';
import { ModalComponent } from '../../../../src/app/shared/modal/modal.component';
describe('Modal', () => {
let component: Modal,
fixture: ComponentFixture<Modal>;
let component: ModalComponent;
let fixture: ComponentFixture<ModalComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -25,7 +25,7 @@ describe('Modal', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Modal);
fixture = TestBed.createComponent(ModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -37,43 +37,48 @@ describe('Modal', () => {
it('calls the modal service to close', () => {
component.modalId = 'MODAL';
(<any>component.modalService).close = (id, checkBlocking) => {
expect(id).toEqual('MODAL');
expect(checkBlocking).toEqual(false);
};
(component.modalService as any).close =
(id: string, checkBlocking: boolean) => {
expect(id).toEqual('MODAL');
expect(checkBlocking).toEqual(false);
};
component.close();
(<any>component.modalService).close = (id, checkBlocking) => {
expect(id).toEqual('MODAL');
expect(checkBlocking).toEqual(true);
};
(component.modalService as any).close =
(id: string, checkBlocking: boolean) => {
expect(id).toEqual('MODAL');
expect(checkBlocking).toEqual(true);
};
component.close(true);
});
it('filters click events', () => {
window.event = <any>{};
(window as any).event = {} as any;
component.filterClick(null);
let called = false;
const event = { stopPropagation: () => called = true };
component.filterClick(<any>event);
component.filterClick(event as any);
expect(called).toEqual(true);
});
it('handles the Escape key', () => {
// tslint:disable-next-line
const keyUp = component['keyup'].bind(component);
(<any>component.modalService).close = (id, checkBlocking) => {
expect(checkBlocking).toEqual(true);
};
(component.modalService as any).close =
(_: any, checkBlocking: boolean) => {
expect(checkBlocking).toEqual(true);
};
keyUp(<any>{ keyCode: 27 });
keyUp({ keyCode: 27 } as any);
});
it('handles the Enter key', () => {
// tslint:disable-next-line
const keyUp = component['keyup'].bind(component);
let called = false;
@ -81,7 +86,7 @@ describe('Modal', () => {
click: () => called = true
} };
keyUp(<any>{ keyCode: 13 });
keyUp({ keyCode: 13 } as any);
expect(called).toEqual(true);
});

View File

@ -7,12 +7,12 @@ import {
NotificationsService
} from '../../../../src/app/shared/notifications/notifications.service';
import {
Notifications
NotificationsComponent
} from '../../../../src/app/shared/notifications/notifications.component';
describe('Notifications', () => {
let component: Notifications,
fixture: ComponentFixture<Notifications>;
let component: NotificationsComponent;
let fixture: ComponentFixture<NotificationsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -26,7 +26,7 @@ describe('Notifications', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(Notifications);
fixture = TestBed.createComponent(NotificationsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@ -35,19 +35,20 @@ describe('Notifications', () => {
expect(component).toBeTruthy();
});
it('subscribes to note add events', fakeAsync(done => {
it('subscribes to note add events', fakeAsync(() => {
const note = { type: 'info' };
(<any>component.notifications).addNote(note);
(component.notifications as any).addNote(note);
tick(4000);
expect(note.type).toEqual('info clicked');
}));
it('hides notes', fakeAsync(() => {
const hide = component['hide'].bind(component),
note = { type: 'test' };
// tslint:disable-next-line
const hide = component['hide'].bind(component);
const note = { type: 'test' };
component.notes = <any>[note];
component.notes = [note] as any;
hide({});
tick(4000);

View File

@ -1,9 +1,9 @@
import { TestBed, ComponentFixture } from '@angular/core/testing'
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { TopNav } from '../../../../src/app/shared/top-nav/top-nav.component';
import { TopNavComponent } from '../../../../src/app/shared/top-nav/top-nav.component';
import {
Constants,
AuthService,
@ -12,8 +12,8 @@ import {
} from '../../../../src/app/shared/services';
describe('TopNav', () => {
let component: TopNav,
fixture: ComponentFixture<TopNav>;
let component: TopNavComponent;
let fixture: ComponentFixture<TopNavComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
@ -22,7 +22,7 @@ describe('TopNav', () => {
RouterTestingModule.withRoutes([]),
FormsModule
],
declarations: [ TopNav ],
declarations: [ TopNavComponent ],
providers: [
RouterTestingModule,
Constants,
@ -34,7 +34,7 @@ describe('TopNav', () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(TopNav);
fixture = TestBed.createComponent(TopNavComponent);
component = fixture.componentInstance;
});
@ -45,8 +45,8 @@ describe('TopNav', () => {
it('can log out a user', () => {
let called = false;
(<any>component.authService).logout = () => {
return { subscribe: fn => {
(component.authService as any).logout = () => {
return { subscribe: (fn: any) => {
fn({ alerts: [{}] });
called = true;
} };
@ -63,7 +63,7 @@ describe('TopNav', () => {
});
it('can navigate to a target route', () => {
let spy = spyOn((<any>component).router, 'navigate');
const spy = spyOn((component as any).router, 'navigate');
component.navigateTo('test');
expect(spy).toHaveBeenCalledWith(['/test']);

18
tsconfig.app.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/app/**/*.ts"
],
"exclude": [
"./src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -1,13 +1,16 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"downlevelIteration": true,
"experimentalDecorators": true,
"target": "es5",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
@ -15,8 +18,12 @@
"dragula": ["../node_modules/dragula/dist/dragula.min.js"]
},
"lib": [
"es2017",
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}

View File

@ -1,21 +1,19 @@
{
"extends": "../tsconfig.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"**/*.spec.ts",
"../test/app/**/*.spec.ts",
"test/app/**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -1,32 +1,32 @@
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"extends": "tslint:recommended",
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
"severity": "warning"
},
"eofline": true,
"forin": true,
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"tb",
"camelCase"
],
"component-selector": [
true,
"element",
"tb",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
@ -43,8 +43,7 @@
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
@ -53,90 +52,40 @@
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
"as-needed"
],
"prefer-const": true,
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}