TSK-981: Provide frontend formatter for common IDEs (#1187)

* TSK-981: tested adding prettier

* TSK-981: Refined prettier and eslint configs

* TSK-981: Update eslint rules

* TSK-981: Updated VS Code settings and ignore folders

* TSK-981: Added editor config

* TSK-981: added html to prettierignore

* TSK-981: tested adding prettier

* TSK-981: Refined prettier and eslint configs

* TSK-981: Update eslint rules

* TSK-981: Updated VS Code settings and ignore folders

* TSK-981: Added editor config

* TSK-981: added html to prettierignore

* TSK-981: reformatted code using prettier/new eslint configs
This commit is contained in:
Chi Nguyen 2020-07-22 10:44:33 +02:00 committed by GitHub
parent 59b45f626a
commit 29e468e6aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
230 changed files with 3479 additions and 2983 deletions

14
web/.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -1,42 +1,17 @@
module.exports = {
"extends": [
"airbnb-typescript/base"
],
"env": {
"browser": true,
"node": true
extends: ['prettier/@typescript-eslint', 'plugin:prettier/recommended'],
env: {
browser: true,
node: true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
errorOnUnknownASTType: true,
errorOnTypeScriptSyntacticAndSemanticIssues: true
},
"plugins": [
"@typescript-eslint",
"@typescript-eslint/tslint"
],
"rules": {
"arrow-parens": ["error", "as-needed"],
"@typescript-eslint/indent": ['error', 2],
"max-len": ["error", { "code": 140, "ignorePattern": "import *" }], // smaller than 140?
"object-curly-newline": ["error", { "ImportDeclaration": "never" }],
"quote-props": ["error", "as-needed"],
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
"comma-dangle": ["error", "only-multiline"],
"no-underscore-dangle": ["error", { "allow": ["_links", "__karma__"] }],
"no-param-reassign": ["error", { "props": false }],
"no-plusplus" : ["error", { "allowForLoopAfterthoughts": true }],
"@typescript-eslint/no-use-before-define": ["error", { "functions": false, "classes": false }],
"@typescript-eslint/no-unused-expressions": ["error", { "allowTernary": true }],
// all following rules SHOULD be removed
"class-methods-use-this": "off",
"import/extensions": "off",
"import/no-unresolved": "off",
"import/prefer-default-export": "off",
"max-classes-per-file": "off",
"@typescript-eslint/no-unused-vars": "off",
// all following rules MUST be removed (mostly autofix)
"linebreak-style": ["off", "unix"], // own PR
}
plugins: ['@typescript-eslint', '@typescript-eslint/tslint'],
rules: {}
};

12
web/.prettierignore Normal file
View File

@ -0,0 +1,12 @@
*.lint.ts
**/node_modules/**
.github/**
.idea/**
.cache/**
.vscode/**
dist*/**
build/**
coverage/**
**/dist/**
out-tsc/**
**/*.html

8
web/.prettierrc.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
tabWidth: 2,
semi: true,
singleQuote: true,
bracketSpacing: true,
printWidth: 120,
trailingComma: 'none'
};

15
web/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"eslint.validate": [ "javascript", "typescript", "html"],
"eslint.options": {
"extensions": [".js", ".ts", "html"]
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
},
"[html]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "vscode.html-language-features"
}
}

View File

@ -16,14 +16,8 @@
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/environments/data-sources"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/theme/_main.scss"
],
"assets": ["src/assets", "src/environments/data-sources"],
"styles": ["./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/theme/_main.scss"],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/popper.js/dist/umd/popper.min.js",
@ -80,23 +74,14 @@
"node_modules/popper.js/dist/umd/popper.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/theme/_main.scss"
],
"assets": [
"src/assets",
"src/environments/data-sources"
]
"styles": ["./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/theme/_main.scss"],
"assets": ["src/assets", "src/environments/data-sources"]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"],
"exclude": []
}
}

View File

@ -1,13 +1,13 @@
var SeedReporter = function(baseReporterDecorator) {
var SeedReporter = function (baseReporterDecorator) {
baseReporterDecorator(this);
this.onBrowserComplete = function(browser, result) {
this.onBrowserComplete = function (browser, result) {
if (result.order && result.order.random && result.order.seed) {
this.write("%s: Randomized with seed %s\n", browser, result.order.seed);
this.write('%s: Randomized with seed %s\n', browser, result.order.seed);
}
};
};
module.exports = {
"reporter:jasmine-seed": ["type", SeedReporter]
'reporter:jasmine-seed': ['type', SeedReporter]
};

View File

@ -21,23 +21,21 @@ module.exports = function (config) {
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
files: [
],
preprocessors: {
},
files: [],
preprocessors: {},
mime: {
'text/x-typescript': ['ts', 'tsx']
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly'],
dir: require('path').join(__dirname, 'coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: config.angularCli && config.angularCli.codeCoverage
? ['progress', 'coverage-istanbul', 'jasmine-seed']
: ['progress', 'kjhtml', 'jasmine-seed'],
reporters:
config.angularCli && config.angularCli.codeCoverage
? ['progress', 'coverage-istanbul', 'jasmine-seed']
: ['progress', 'kjhtml', 'jasmine-seed'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,

67
web/package-lock.json generated
View File

@ -665,6 +665,7 @@
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
@ -3166,6 +3167,16 @@
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
@ -5397,6 +5408,23 @@
"eslint-config-airbnb-base": "^14.0.0"
}
},
"eslint-config-prettier": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
"integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==",
"dev": true,
"requires": {
"get-stdin": "^6.0.0"
},
"dependencies": {
"get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
"dev": true
}
}
},
"eslint-import-resolver-node": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
@ -5654,6 +5682,15 @@
}
}
},
"eslint-plugin-prettier": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
"integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
@ -6044,6 +6081,12 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
@ -6132,6 +6175,13 @@
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz",
"integrity": "sha1-zdTETTqiZOrC9o7BZbx5HDSvEjI="
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"fileset": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
@ -10116,6 +10166,21 @@
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
"prettier": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
"integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -13278,6 +13343,7 @@
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
@ -13855,6 +13921,7 @@
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},

View File

@ -11,7 +11,9 @@
"test": "ng test --karma-config karma.conf.js --watch=false --browsers Firefox",
"test:watch": "ng test --karma-config karma.conf.js --browsers Chrome",
"lint": "eslint --ext .ts src",
"lint:fix": "eslint --ext .ts src --fix"
"lint:fix": "eslint --ext .ts src --fix",
"format": "prettier --write \"**/*.{js,ts,css,scss,md,json,yml}\"",
"format:html": "prettier --write \"**/*.{html,}\""
},
"private": true,
"dependencies": {
@ -62,6 +64,8 @@
"eslint-config-airbnb-typescript": "6.3.1",
"eslint-plugin-import": "2.22.0",
"@hapi/hoek": "9.0.4",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "5.1.0",
@ -73,6 +77,7 @@
"karma-jasmine-html-reporter": "1.4.0",
"moment": "2.22.1",
"ng2-mock-component": "0.1.1",
"prettier": "^2.0.5",
"protractor": "7.0.0",
"ts-mockito": "2.3.0",
"ts-node": "4.1.0",

View File

@ -75,5 +75,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdministrationRoutingModule {
}
export class AdministrationRoutingModule {}

View File

@ -44,7 +44,7 @@ const MODULES = [
SharedModule,
AdministrationRoutingModule,
TypeaheadModule,
InfiniteScrollModule,
InfiniteScrollModule
];
const DECLARATIONS = [
@ -66,17 +66,13 @@ const DECLARATIONS = [
@NgModule({
declarations: DECLARATIONS,
imports: [
MODULES,
MatRadioModule
],
imports: [MODULES, MatRadioModule],
providers: [
ClassificationDefinitionService,
WorkbasketDefinitionService,
SavingWorkbasketService,
ClassificationCategoriesService,
ImportExportService,
ImportExportService
]
})
export class AdministrationModule {
}
export class AdministrationModule {}

View File

@ -1,36 +1,37 @@
@import '../../../../theme/colors';
.margin {
margin-top: 10px;
margin-bottom: 20px;
margin-top: 10px;
margin-bottom: 20px;
}
.required-header:after {
content:" *";
color: $invalid;
content: ' *';
color: $invalid;
}
td {
&.has-changes {
border-bottom: 1px solid $brown;
}
&.has-changes {
border-bottom: 1px solid $brown;
}
}
.table > thead > tr > th {
max-width: 150px;
border-bottom: none;
max-width: 150px;
border-bottom: none;
}
.table > thead > tr:last-child > th {
max-width: 150px;
border-bottom: 2px solid $grey;
max-width: 150px;
border-bottom: 2px solid $grey;
}
.wrap{
max-width: 150px;
word-wrap:break-word;
.wrap {
max-width: 150px;
word-wrap: break-word;
}
.min-width {
min-width: 135px;
}
.min-width{ min-width: 135px;}
.modal-title {
font-weight: bold;

View File

@ -22,8 +22,8 @@ describe('AccessItemsManagementComponent', () => {
});
};
beforeEach(done => {
configureTests(configure).then(testBed => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
fixture = testBed.createComponent(AccessItemsManagementComponent);
component = fixture.componentInstance;
accessIdsService = testBed.get(AccessIdsService);

View File

@ -16,9 +16,7 @@ import { AccessIdsService } from '../../../shared/services/access-ids/access-ids
import { AccessIdDefinition } from '../../../shared/models/access-id';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { AccessItemsCustomisation,
CustomField,
getCustomFields } from '../../../shared/models/customisation';
import { AccessItemsCustomisation, CustomField, getCustomFields } from '../../../shared/models/customisation';
import { customFieldCount } from '../../../shared/models/workbasket-access-items';
@Component({
@ -34,22 +32,28 @@ export class AccessItemsManagementComponent implements OnInit {
toggleValidationAccessIdMap = new Map<number, boolean>();
accessId: AccessIdDefinition;
groups: AccessIdDefinition[];
sortingFields = new Map([['access-id', 'Access id'], ['workbasket-key', 'Workbasket Key']]);
sortingFields = new Map([
['access-id', 'Access id'],
['workbasket-key', 'Workbasket Key']
]);
sortModel: Sorting = new Sorting('access-id', Direction.DESC);
isGroup: boolean = false;
@Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<AccessItemsCustomisation>;
@Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<
AccessItemsCustomisation
>;
customFields$: Observable<CustomField[]>;
constructor(private formBuilder: FormBuilder,
constructor(
private formBuilder: FormBuilder,
private accessIdsService: AccessIdsService,
private formsValidatorService: FormsValidatorService,
private requestInProgressService: RequestInProgressService,
private notificationService: NotificationService) {
}
private notificationService: NotificationService
) {}
get accessItemsGroups(): FormArray {
return this.accessItemsForm ? this.accessItemsForm.get('accessItemsGroups') as FormArray : null;
return this.accessItemsForm ? (this.accessItemsForm.get('accessItemsGroups') as FormArray) : null;
}
ngOnInit() {
@ -57,10 +61,10 @@ export class AccessItemsManagementComponent implements OnInit {
}
setAccessItemsGroups(accessItems: Array<AccessItemWorkbasket>) {
const AccessItemsFormGroups = accessItems.map(accessItem => this.formBuilder.group(accessItem));
AccessItemsFormGroups.forEach(accessItemGroup => {
const AccessItemsFormGroups = accessItems.map((accessItem) => this.formBuilder.group(accessItem));
AccessItemsFormGroups.forEach((accessItemGroup) => {
accessItemGroup.controls.accessId.setValidators(Validators.required);
Object.keys(accessItemGroup.controls).forEach(key => {
Object.keys(accessItemGroup.controls).forEach((key) => {
accessItemGroup.controls[key].disable();
});
});
@ -86,16 +90,20 @@ export class AccessItemsManagementComponent implements OnInit {
if (this.accessIdPrevious !== selected.accessId) {
this.accessIdPrevious = selected.accessId;
this.accessIdsService.getGroupsByAccessId(selected.accessId)
.pipe(take(1)).subscribe((groups: AccessIdDefinition[]) => {
this.accessId = selected;
this.groups = groups;
this.searchForAccessItemsWorkbaskets();
},
error => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.triggerError(NOTIFICATION_TYPES.FETCH_ERR, error);
});
this.accessIdsService
.getGroupsByAccessId(selected.accessId)
.pipe(take(1))
.subscribe(
(groups: AccessIdDefinition[]) => {
this.accessId = selected;
this.groups = groups;
this.searchForAccessItemsWorkbaskets();
},
(error) => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.triggerError(NOTIFICATION_TYPES.FETCH_ERR, error);
}
);
}
}
@ -110,41 +118,48 @@ export class AccessItemsManagementComponent implements OnInit {
searchForAccessItemsWorkbaskets() {
this.requestInProgressService.setRequestInProgress(true);
this.accessIdsService.getAccessItems(
[this.accessId, ...this.groups],
this.accessItemsForm ? this.accessItemsForm.value.accessIdFilter : undefined,
this.accessItemsForm ? this.accessItemsForm.value.workbasketKeyFilter : undefined,
this.sortModel
).pipe(take(1)).subscribe((accessItemsResource: AccessItemWorkbasketResource) => {
this.setAccessItemsGroups(accessItemsResource ? accessItemsResource.accessItems : []);
this.requestInProgressService.setRequestInProgress(false);
}, error => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.triggerError(NOTIFICATION_TYPES.FETCH_ERR_2, error);
});
this.accessIdsService
.getAccessItems(
[this.accessId, ...this.groups],
this.accessItemsForm ? this.accessItemsForm.value.accessIdFilter : undefined,
this.accessItemsForm ? this.accessItemsForm.value.workbasketKeyFilter : undefined,
this.sortModel
)
.pipe(take(1))
.subscribe(
(accessItemsResource: AccessItemWorkbasketResource) => {
this.setAccessItemsGroups(accessItemsResource ? accessItemsResource.accessItems : []);
this.requestInProgressService.setRequestInProgress(false);
},
(error) => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.triggerError(NOTIFICATION_TYPES.FETCH_ERR_2, error);
}
);
}
revokeAccess() {
this.notificationService.showDialog(
`You are going to delete all access related: ${
this.accessIdSelected
}. Can you confirm this action?`,
`You are going to delete all access related: ${this.accessIdSelected}. Can you confirm this action?`,
this.onRemoveConfirmed.bind(this)
);
}
private onRemoveConfirmed() {
this.requestInProgressService.setRequestInProgress(true);
this.accessIdsService.removeAccessItemsPermissions(this.accessIdSelected)
.pipe(take(1)).subscribe(
this.accessIdsService
.removeAccessItemsPermissions(this.accessIdSelected)
.pipe(take(1))
.subscribe(
() => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT, new Map<string, string>([['accessId', this.accessIdSelected]])
NOTIFICATION_TYPES.SUCCESS_ALERT,
new Map<string, string>([['accessId', this.accessIdSelected]])
);
this.searchForAccessItemsWorkbaskets();
},
error => {
(error) => {
this.requestInProgressService.setRequestInProgress(false);
this.notificationService.triggerError(NOTIFICATION_TYPES.DELETE_ERR, error);
}

View File

@ -1,5 +1,5 @@
.custom-field-row {
display:flex;
display: flex;
flex-wrap: wrap;
flex-direction: column;
height: 40vh;
@ -9,7 +9,7 @@
.custom-field-wrapper {
height: 70px;
padding: 0 15px
padding: 0 15px;
}
.dropdown-menu > li {
cursor: pointer;

View File

@ -22,8 +22,7 @@ import { NotificationService } from '../../../shared/services/notifications/noti
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
class DummyDetailComponent {
}
class DummyDetailComponent {}
describe('ClassificationDetailsComponent', () => {
let component: ClassificationDetailsComponent;
@ -38,17 +37,22 @@ describe('ClassificationDetailsComponent', () => {
testBed.configureTestingModule({
imports: [FormsModule, HttpClientModule, AngularSvgIconModule, NgxsModule.forRoot()],
declarations: [ClassificationDetailsComponent, DummyDetailComponent],
providers: [MasterAndDetailService, RequestInProgressService, ClassificationsService,
HttpClient, NotificationService,
providers: [
MasterAndDetailService,
RequestInProgressService,
ClassificationsService,
HttpClient,
NotificationService,
ImportExportService,
{ provide: Location, useValue: locationSpy },
{ provide: Store, useValue: storeSpy }]
{ provide: Store, useValue: storeSpy }
]
});
};
beforeEach(done => {
configureTests(configure).then(testBed => {
storeSpy.select.and.callFake(selector => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
storeSpy.select.and.callFake((selector) => {
switch (selector) {
case EngineConfigurationSelectors.classificationsCustomisation:
return of({ information: {} });
@ -99,7 +103,7 @@ describe('ClassificationDetailsComponent', () => {
expect(component).toBeTruthy();
});
it('should enable editing of key on create and on copy', async done => {
it('should enable editing of key on create and on copy', async (done) => {
component.isCreatingNewClassification = true;
await fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('#classification-key').disabled).toEqual(false);

View File

@ -17,19 +17,19 @@ import { ClassificationSelectors } from 'app/shared/store/classification-store/c
import { Location } from '@angular/common';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { ClassificationCategoryImages,
CustomField,
getCustomFields } from '../../../shared/models/customisation';
import { ClassificationCategoryImages, CustomField, getCustomFields } from '../../../shared/models/customisation';
import { Classification } from '../../../shared/models/classification';
import { customFieldCount } from '../../../shared/models/classification-summary';
import { CategoriesResponse } from '../../../shared/services/classification-categories/classification-categories.service';
import { SaveCreatedClassification,
import {
SaveCreatedClassification,
RemoveSelectedClassification,
RestoreSelectedClassification,
SaveModifiedClassification,
SelectClassification,
CopyClassification } from '../../../shared/store/classification-store/classification.actions';
CopyClassification
} from '../../../shared/store/classification-store/classification.actions';
@Component({
selector: 'taskana-administration-classification-details',
@ -63,29 +63,32 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
private notificationsService: NotificationService,
private importExportService: ImportExportService,
private store: Store
) {
}
) {}
ngOnInit() {
this.customFields$ = this.store.select(EngineConfigurationSelectors.classificationsCustomisation).pipe(
map(customisation => customisation.information),
map((customisation) => customisation.information),
getCustomFields(customFieldCount)
);
this.selectedClassification$.pipe(takeUntil(this.destroy$))
.subscribe(classification => {
this.classification = { ...classification };
this.isCreatingNewClassification = typeof this.classification.classificationId === 'undefined';
});
this.importExportService.getImportingFinished().pipe(takeUntil(this.destroy$)).subscribe(() => {
this.store.dispatch(new SelectClassification(this.classification.classificationId));
this.selectedClassification$.pipe(takeUntil(this.destroy$)).subscribe((classification) => {
this.classification = { ...classification };
this.isCreatingNewClassification = typeof this.classification.classificationId === 'undefined';
});
this.importExportService
.getImportingFinished()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.store.dispatch(new SelectClassification(this.classification.classificationId));
});
}
removeClassification() {
this.notificationsService.showDialog(`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`,
this.removeClassificationConfirmation.bind(this));
this.notificationsService.showDialog(
`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`,
this.removeClassificationConfirmation.bind(this)
);
}
isFieldValid(field: string): boolean {
@ -94,20 +97,23 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
onSubmit() {
this.formsValidatorService.formSubmitAttempt = true;
this.formsValidatorService.validateFormInformation(this.classificationForm, this.toogleValidationMap).then(value => {
if (value) {
this.onSave();
}
});
this.formsValidatorService
.validateFormInformation(this.classificationForm, this.toogleValidationMap)
.then((value) => {
if (value) {
this.onSave();
}
});
}
onRestore() {
this.formsValidatorService.formSubmitAttempt = false;
this.store.dispatch(
new RestoreSelectedClassification(this.classification.classificationId)
).pipe(take(1)).subscribe(() => {
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
});
this.store
.dispatch(new RestoreSelectedClassification(this.classification.classificationId))
.pipe(take(1))
.subscribe(() => {
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
});
}
onCopy() {
@ -123,11 +129,13 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
}
getCategoryIcon(category: string): Observable<Pair> {
return this.categoryIcons$.pipe(map(
iconMap => (iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration'))
));
return this.categoryIcons$.pipe(
map((iconMap) =>
iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration')
)
);
}
spinnerRunning(value) {
@ -147,7 +155,10 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
}
getAvailableCategories(type: string): Observable<string[]> {
return this.classificationTypes$.pipe(take(1), map(classTypes => classTypes[type]));
return this.classificationTypes$.pipe(
take(1),
map((classTypes) => classTypes[type])
);
}
ngOnDestroy(): void {
@ -158,26 +169,36 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
private async onSave() {
this.requestInProgressService.setRequestInProgress(true);
if (typeof this.classification.classificationId === 'undefined') {
this.store.dispatch(
new SaveCreatedClassification(this.classification)
).pipe(take(1)).subscribe(store => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_2,
new Map<string, string>([['classificationKey', store.classification.selectedClassification.key]])
this.store
.dispatch(new SaveCreatedClassification(this.classification))
.pipe(take(1))
.subscribe(
(store) => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_2,
new Map<string, string>([['classificationKey', store.classification.selectedClassification.key]])
);
this.location.go(
this.location
.path()
.replace(
/(classifications).*/g,
`classifications/(detail:${store.classification.selectedClassification.classificationId})`
)
);
this.afterRequest();
},
(error) => {
this.notificationsService.triggerError(NOTIFICATION_TYPES.CREATE_ERR, error);
this.afterRequest();
}
);
this.location.go(this.location.path().replace(
/(classifications).*/g,
`classifications/(detail:${store.classification.selectedClassification.classificationId})`
));
this.afterRequest();
}, error => {
this.notificationsService.triggerError(NOTIFICATION_TYPES.CREATE_ERR, error);
this.afterRequest();
});
} else {
try {
this.store.dispatch(new SaveModifiedClassification(this.classification))
.pipe(take(1)).subscribe(() => {
this.store
.dispatch(new SaveModifiedClassification(this.classification))
.pipe(take(1))
.subscribe(() => {
this.afterRequest();
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_3,
@ -202,11 +223,16 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
}
this.requestInProgressService.setRequestInProgress(true);
this.store.dispatch(new RemoveSelectedClassification()).pipe(take(1)).subscribe(() => {
this.notificationsService.showToast(NOTIFICATION_TYPES.SUCCESS_ALERT_4,
new Map<string, string>([['classificationKey', this.classification.key]]));
this.afterRequest();
});
this.store
.dispatch(new RemoveSelectedClassification())
.pipe(take(1))
.subscribe(() => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_4,
new Map<string, string>([['classificationKey', this.classification.key]])
);
this.afterRequest();
});
this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications'));
}
}

View File

@ -7,12 +7,12 @@
border: none;
}
.tab-align{
.tab-align {
margin-bottom: 0px;
border-bottom: 1px dotted #ddd;
padding: 8px 12px 8px 4px;
&>div{
margin: 6px 0px;
& > div {
margin: 6px 0px;
}
}
@ -25,10 +25,10 @@ input.filter-input {
}
.category-filter {
margin: 7px 2px;
margin: 7px 2px;
}
.dropdown-menu-classification{
.dropdown-menu-classification {
margin-left: 15px;
min-width: 0px;
}

View File

@ -28,12 +28,9 @@ import { NotificationService } from '../../../shared/services/notifications/noti
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
class DummyDetailComponent {
}
class DummyDetailComponent {}
const routes: Routes = [
{ path: ':id', component: DummyDetailComponent }
];
const routes: Routes = [{ path: ':id', component: DummyDetailComponent }];
describe('ClassificationListComponent', () => {
let component: ClassificationListComponent;
@ -43,19 +40,35 @@ describe('ClassificationListComponent', () => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [ClassificationListComponent, ImportExportComponent, ClassificationTypesSelectorComponent,
DummyDetailComponent],
imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, NgxsModule.forRoot([]), MatRadioModule],
declarations: [
ClassificationListComponent,
ImportExportComponent,
ClassificationTypesSelectorComponent,
DummyDetailComponent
],
imports: [
HttpClientModule,
RouterTestingModule.withRoutes(routes),
FormsModule,
AngularSvgIconModule,
NgxsModule.forRoot([]),
MatRadioModule
],
providers: [
HttpClient, WorkbasketDefinitionService, NotificationService,
ClassificationsService, DomainService, ClassificationDefinitionService,
RequestInProgressService, ImportExportService
HttpClient,
WorkbasketDefinitionService,
NotificationService,
ClassificationsService,
DomainService,
ClassificationDefinitionService,
RequestInProgressService,
ImportExportService
]
});
};
beforeEach(done => {
configureTests(configure).then(testBed => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
fixture = testBed.createComponent(ClassificationListComponent);
component = fixture.componentInstance;

View File

@ -12,8 +12,10 @@ import { ClassificationSelectors } from 'app/shared/store/classification-store/c
import { Location } from '@angular/common';
import { ClassificationCategoryImages } from '../../../shared/models/customisation';
import { GetClassifications,
CreateClassification } from '../../../shared/store/classification-store/classification.actions';
import {
GetClassifications,
CreateClassification
} from '../../../shared/store/classification-store/classification.actions';
import { DomainService } from '../../../shared/services/domain/domain.service';
import { ClassificationSummary } from '../../../shared/models/classification-summary';
@ -44,40 +46,38 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
private ngxsActions$: Actions,
private domainService: DomainService
) {
this.ngxsActions$.pipe(ofActionDispatched(GetClassifications),
takeUntil(this.destroy$))
.subscribe(() => {
this.requestInProgress = true;
});
this.ngxsActions$.pipe(ofActionCompleted(GetClassifications),
takeUntil(this.destroy$))
.subscribe(() => {
this.requestInProgress = false;
});
this.ngxsActions$.pipe(ofActionDispatched(GetClassifications), takeUntil(this.destroy$)).subscribe(() => {
this.requestInProgress = true;
});
this.ngxsActions$.pipe(ofActionCompleted(GetClassifications), takeUntil(this.destroy$)).subscribe(() => {
this.requestInProgress = false;
});
}
ngOnInit() {
this.classifications$.pipe(takeUntil(this.destroy$)).subscribe(classifications => {
this.classifications$.pipe(takeUntil(this.destroy$)).subscribe((classifications) => {
this.classifications = classifications;
});
this.classificationTypeSelected$
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.store.dispatch(new GetClassifications());
this.selectedCategory = '';
});
this.classificationTypeSelected$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.store.dispatch(new GetClassifications());
this.selectedCategory = '';
});
this.importExportService.getImportingFinished()
this.importExportService
.getImportingFinished()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.store.dispatch(new GetClassifications());
});
// needed, so that the list updates, when domain gets changed (could be placed anywhere and should be removed, when domain is in store)
this.domainService.getSelectedDomain().pipe(takeUntil(this.destroy$)).subscribe(domain => {
this.store.dispatch(GetClassifications);
});
this.domainService
.getSelectedDomain()
.pipe(takeUntil(this.destroy$))
.subscribe((domain) => {
this.store.dispatch(GetClassifications);
});
}
addClassification() {
@ -91,10 +91,10 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
getCategoryIcon(category: string): Observable<Pair> {
return this.categoryIcons$.pipe(
map(
iconMap => (iconMap[category]
map((iconMap) =>
iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration'))
: new Pair(iconMap.missing, 'Category does not match with the configuration')
)
);
}

View File

@ -10,8 +10,7 @@ import { ClassificationOverviewComponent } from './classification-overview.compo
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
describe('ClassificationOverviewComponent', () => {
let component: ClassificationOverviewComponent;
@ -20,21 +19,13 @@ describe('ClassificationOverviewComponent', () => {
let debugElement;
const locationSpy: jasmine.SpyObj<Location> = jasmine.createSpyObj('Location', ['go']);
beforeEach((() => {
const routes: Routes = [
{ path: ':id', component: DummyDetailComponent }
];
beforeEach(() => {
const routes: Routes = [{ path: ':id', component: DummyDetailComponent }];
TestBed.configureTestingModule({
declarations: [
ClassificationOverviewComponent,
DummyDetailComponent],
imports: [
RouterTestingModule.withRoutes(routes),
NgxsModule.forRoot()],
providers: [
{ provide: Location, useValue: locationSpy },
],
declarations: [ClassificationOverviewComponent, DummyDetailComponent],
imports: [RouterTestingModule.withRoutes(routes), NgxsModule.forRoot()],
providers: [{ provide: Location, useValue: locationSpy }],
schemas: [NO_ERRORS_SCHEMA]
});
@ -44,7 +35,7 @@ describe('ClassificationOverviewComponent', () => {
router = TestBed.get(Router);
fixture.detectChanges();
}));
});
it('should create', () => {
expect(component).toBeTruthy();

View File

@ -4,7 +4,11 @@ import { Observable, Subject } from 'rxjs';
import { Select, Store } from '@ngxs/store';
import { takeUntil } from 'rxjs/operators';
import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors';
import { GetClassifications, SelectClassification, CreateClassification } from '../../../shared/store/classification-store/classification.actions';
import {
GetClassifications,
SelectClassification,
CreateClassification
} from '../../../shared/store/classification-store/classification.actions';
import { Classification } from '../../../shared/models/classification';
@Component({
@ -18,35 +22,28 @@ export class ClassificationOverviewComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
routerParams: any;
constructor(
private route: ActivatedRoute,
private store: Store
) {
}
constructor(private route: ActivatedRoute, private store: Store) {}
ngOnInit() {
if (this.route.firstChild) {
this.route.firstChild.params
.pipe(takeUntil(this.destroy$))
.subscribe(params => {
this.routerParams = params;
this.route.firstChild.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
this.routerParams = params;
if (this.routerParams.id) {
this.showDetail = true;
this.store.dispatch(new SelectClassification(this.routerParams.id))
.subscribe(() => this.store.dispatch(new GetClassifications()));
}
if (this.routerParams.id && this.routerParams.id.indexOf('new-classification') !== -1) {
this.store.dispatch(new CreateClassification());
}
});
if (this.routerParams.id) {
this.showDetail = true;
this.store
.dispatch(new SelectClassification(this.routerParams.id))
.subscribe(() => this.store.dispatch(new GetClassifications()));
}
if (this.routerParams.id && this.routerParams.id.indexOf('new-classification') !== -1) {
this.store.dispatch(new CreateClassification());
}
});
}
this.selectedClassification$
.pipe(takeUntil(this.destroy$))
.subscribe(selectedClassification => {
this.showDetail = !!selectedClassification;
});
this.selectedClassification$.pipe(takeUntil(this.destroy$)).subscribe((selectedClassification) => {
this.showDetail = !!selectedClassification;
});
}
ngOnDestroy() {

View File

@ -14,9 +14,7 @@ describe('ClassificationTypesSelectorComponent', () => {
TestBed.configureTestingModule({
imports: [NgxsModule.forRoot(), MatRadioModule],
declarations: [ClassificationTypesSelectorComponent],
providers: [
{ provide: Location, useValue: locationSpy },
]
providers: [{ provide: Location, useValue: locationSpy }]
}).compileComponents();
}));

View File

@ -15,16 +15,21 @@ describe('ImportExportComponent', () => {
let fixture: ComponentFixture<ImportExportComponent>;
let debugElement;
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [ImportExportComponent],
imports: [HttpClientModule, AngularSvgIconModule],
providers: [WorkbasketService, ClassificationDefinitionService, WorkbasketDefinitionService, NotificationService,
ImportExportService]
providers: [
WorkbasketService,
ClassificationDefinitionService,
WorkbasketDefinitionService,
NotificationService,
ImportExportService
]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(ImportExportComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement.nativeElement;

View File

@ -32,15 +32,12 @@ export class ImportExportComponent implements OnInit {
public uploadservice: UploadService,
private errorsService: NotificationService,
private importExportService: ImportExportService
) {
}
) {}
ngOnInit() {
this.domainService.getDomains().subscribe(
data => {
this.domains = data;
}
);
this.domainService.getDomains().subscribe((data) => {
this.domains = data;
});
}
export(domain = '') {

View File

@ -1,5 +1,5 @@
.text-top{
vertical-align: text-top;
.text-top {
vertical-align: text-top;
}
svg-icon.blue.fa-fw {

View File

@ -37,16 +37,18 @@ describe('TaskanaTreeComponent', () => {
testBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule, NgxsModule.forRoot()],
declarations: [TreeVendorComponent],
providers: [ClassificationsService,
providers: [
ClassificationsService,
{ provide: Location, useValue: locationSpy },
{ provide: Store, useValue: storeSpy }]
{ provide: Store, useValue: storeSpy }
]
});
};
beforeEach(done => {
configureTests(configure).then(testBed => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
locationSpy.path.and.callFake(() => '');
storeSpy.select.and.callFake(selector => {
storeSpy.select.and.callFake((selector) => {
switch (selector) {
case ClassificationSelectors.selectedClassificationId:
return of('id4');
@ -96,7 +98,7 @@ describe('TaskanaTreeComponent', () => {
name: 'classification4',
description: 'description',
priority: 1,
serviceLevel: 'level',
serviceLevel: 'level'
};
// using parameter 'any' since getClassification is a private method

View File

@ -1,4 +1,5 @@
import { AfterViewChecked,
import {
AfterViewChecked,
Component,
ElementRef,
EventEmitter,
@ -7,7 +8,8 @@ import { AfterViewChecked,
OnDestroy,
OnInit,
Output,
ViewChild } from '@angular/core';
ViewChild
} from '@angular/core';
import { TreeNodeModel } from 'app/administration/models/tree-node';
import { ITreeOptions, KEYS, TREE_ACTIONS, TreeComponent } from 'angular-tree-component';
@ -24,15 +26,17 @@ import { Classification } from '../../../shared/models/classification';
import { ClassificationsService } from '../../../shared/services/classifications/classifications.service';
import { ClassificationCategoryImages } from '../../../shared/models/customisation';
import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors';
import { DeselectClassification,
import {
DeselectClassification,
SelectClassification,
UpdateClassification } from '../../../shared/store/classification-store/classification.actions';
UpdateClassification
} from '../../../shared/store/classification-store/classification.actions';
import { ClassificationTreeService } from '../../services/classification-tree.service';
@Component({
selector: 'taskana-administration-tree',
templateUrl: './tree.component.html',
styleUrls: ['./tree.component.scss'],
styleUrls: ['./tree.component.scss']
})
export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy {
treeNodes: TreeNodeModel[];
@ -75,8 +79,7 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
private store: Store,
private notificationsService: NotificationService,
private classificationTreeService: ClassificationTreeService
) {
}
) {}
@HostListener('document:click', ['$event'])
onDocumentClick(event) {
@ -87,11 +90,12 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
ngOnInit() {
const computedTreeNodes$: Observable<TreeNodeModel[]> = this.classifications$.pipe(
filter(classifications => typeof (classifications) !== 'undefined'),
map(classifications => this.classificationTreeService.transformToTreeNode(classifications))
filter((classifications) => typeof classifications !== 'undefined'),
map((classifications) => this.classificationTreeService.transformToTreeNode(classifications))
);
combineLatest([this.selectedClassificationId$, computedTreeNodes$]).pipe(takeUntil(this.destroy$))
combineLatest([this.selectedClassificationId$, computedTreeNodes$])
.pipe(takeUntil(this.destroy$))
.subscribe(([selectedClassificationId, treeNodes]) => {
this.treeNodes = treeNodes;
this.selectNodeId = typeof selectedClassificationId !== 'undefined' ? selectedClassificationId : undefined;
@ -122,8 +126,7 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
}
}
if (this.filterTextOld !== this.filterText
|| this.filterIconOld !== this.filterIcon) {
if (this.filterTextOld !== this.filterText || this.filterIconOld !== this.filterIcon) {
this.filterIconOld = this.filterIcon;
this.filterTextOld = this.filterText;
this.filterNodes(this.filterText ? this.filterText : '', this.filterIcon);
@ -166,11 +169,13 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
}
getCategoryIcon(category: string): Observable<Pair> {
return this.categoryIcons$.pipe(map(
iconMap => (iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration'))
));
return this.categoryIcons$.pipe(
map((iconMap) =>
iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration')
)
);
}
switchTaskanaSpinner(active: boolean) {
@ -203,18 +208,20 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
}
private filterNodes(text, iconText) {
this.tree.treeModel.filterNodes(node => TaskanaTreeComponent.checkNameAndKey(node, text)
&& TaskanaTreeComponent.checkIcon(node, iconText));
this.tree.treeModel.filterNodes(
(node) => TaskanaTreeComponent.checkNameAndKey(node, text) && TaskanaTreeComponent.checkIcon(node, iconText)
);
}
private static checkNameAndKey(node: any, text: string): boolean {
return (node.data.name.toUpperCase().includes(text.toUpperCase())
|| node.data.key.toUpperCase().includes(text.toUpperCase()));
return (
node.data.name.toUpperCase().includes(text.toUpperCase()) ||
node.data.key.toUpperCase().includes(text.toUpperCase())
);
}
private static checkIcon(node: any, iconText: string): boolean {
return (node.data.category.toUpperCase() === iconText.toUpperCase()
|| iconText === '');
return node.data.category.toUpperCase() === iconText.toUpperCase() || iconText === '';
}
private manageTreeState() {
@ -225,10 +232,10 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
}
private checkValidElements(event): boolean {
return (this.elementRef.nativeElement.contains(event.target)
|| this.elementRef.nativeElement === event.target)
&& (event.target.localName === 'tree-viewport'
|| event.target.localName === 'taskana-tree');
return (
(this.elementRef.nativeElement.contains(event.target) || this.elementRef.nativeElement === event.target) &&
(event.target.localName === 'tree-viewport' || event.target.localName === 'taskana-tree')
);
}
private getClassification(classificationId: string): Promise<Classification> {
@ -236,14 +243,13 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
}
private updateClassification(classification: Classification) {
this.store.dispatch(new UpdateClassification(classification))
.subscribe(() => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_5,
new Map<string, string>([['classificationKey', classification.key]])
);
this.switchTaskanaSpinner(false);
});
this.store.dispatch(new UpdateClassification(classification)).subscribe(() => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_5,
new Map<string, string>([['classificationKey', classification.key]])
);
this.switchTaskanaSpinner(false);
});
}
private collapseParentNodeIfItIsTheLastChild(node: any) {

View File

@ -9,13 +9,13 @@ describe('IconTypeComponent', () => {
let fixture: ComponentFixture<IconTypeComponent>;
let debugElement;
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(IconTypeComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement.nativeElement;

View File

@ -20,20 +20,28 @@ export class IconTypeComponent implements OnInit {
text: string;
public static get allTypes(): Map<string, string> {
return new Map([['PERSONAL', 'Personal'], ['GROUP', 'Group'], ['CLEARANCE', 'Clearance'], ['TOPIC', 'Topic']]);
return new Map([
['PERSONAL', 'Personal'],
['GROUP', 'Group'],
['CLEARANCE', 'Clearance'],
['TOPIC', 'Topic']
]);
}
ngOnInit() {
}
ngOnInit() {}
getIconPath(type: string) {
switch (type) {
case 'PERSONAL': return 'user.svg';
case 'GROUP': return 'users.svg';
case 'TOPIC': return 'topic.svg';
case 'CLEARANCE': return 'clearance.svg';
default: return 'asterisk.svg';
case 'PERSONAL':
return 'user.svg';
case 'GROUP':
return 'users.svg';
case 'TOPIC':
return 'topic.svg';
case 'CLEARANCE':
return 'clearance.svg';
default:
return 'asterisk.svg';
}
}
}

View File

@ -1,34 +1,33 @@
@import '../../../../theme/colors';
td > input[type="checkbox"] {
margin-top: 0px;
td > input[type='checkbox'] {
margin-top: 0px;
}
.panel-body {
overflow-x: auto;
overflow-x: auto;
}
.text-width{
width: 100%;
min-width: 180px;
.text-width {
width: 100%;
min-width: 180px;
}
.required-header {
width: 200px;
width: 200px;
}
.required-header:after {
content:" *";
color: red;
content: ' *';
color: red;
}
td {
vertical-align: bottom !important;
&.has-changes {
border-bottom: 1px solid #f0ad4e;;
}
vertical-align: bottom !important;
&.has-changes {
border-bottom: 1px solid #f0ad4e;
}
}
.table > thead > tr > th {
max-width: 150px;
border-bottom: none;
max-width: 150px;
border-bottom: none;
}
taskana-shared-type-ahead {
top: 0;
top: 0;
}

View File

@ -34,18 +34,27 @@ describe('WorkbasketAccessItemsComponent', () => {
testBed.configureTestingModule({
declarations: [WorkbasketAccessItemsComponent],
imports: [
FormsModule, AngularSvgIconModule,
HttpClientModule, ReactiveFormsModule,
NgxsModule.forRoot([WorkbasketState, EngineConfigurationState])],
providers: [WorkbasketService, NotificationService, SavingWorkbasketService, RequestInProgressService,
AccessIdsService, FormsValidatorService, ClassificationCategoriesService,
{ provide: Location, useValue: locationSpy },
FormsModule,
AngularSvgIconModule,
HttpClientModule,
ReactiveFormsModule,
NgxsModule.forRoot([WorkbasketState, EngineConfigurationState])
],
providers: [
WorkbasketService,
NotificationService,
SavingWorkbasketService,
RequestInProgressService,
AccessIdsService,
FormsValidatorService,
ClassificationCategoriesService,
{ provide: Location, useValue: locationSpy }
]
});
};
beforeEach(done => {
configureTests(configure).then(testBed => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
const store: Store = testBed.get(Store);
store.reset([WorkbasketState, EngineConfigurationState]);
@ -59,31 +68,33 @@ describe('WorkbasketAccessItemsComponent', () => {
workbasketService = testBed.get(WorkbasketService);
notificationsService = testBed.get(NotificationService);
const workbasketAccessItemsRepresentation: WorkbasketAccessItemsRepresentation = {
accessItems: [{
accessId: 'accessID1',
workbasketId: 'id1',
workbasketKey: '1',
accessItemId: '',
accessName: '',
permRead: false,
permOpen: false,
permAppend: false,
permTransfer: false,
permDistribute: false,
permCustom1: false,
permCustom2: false,
permCustom3: false,
permCustom4: false,
permCustom5: false,
permCustom6: false,
permCustom7: false,
permCustom8: false,
permCustom9: false,
permCustom10: false,
permCustom11: false,
permCustom12: false,
_links: {},
}],
accessItems: [
{
accessId: 'accessID1',
workbasketId: 'id1',
workbasketKey: '1',
accessItemId: '',
accessName: '',
permRead: false,
permOpen: false,
permAppend: false,
permTransfer: false,
permDistribute: false,
permCustom1: false,
permCustom2: false,
permCustom3: false,
permCustom4: false,
permCustom5: false,
permCustom6: false,
permCustom7: false,
permCustom8: false,
permCustom9: false,
permCustom10: false,
permCustom11: false,
permCustom12: false,
_links: {}
}
],
_links: { self: { href: 'someurl' } }
};
debugElement = fixture.debugElement.nativeElement;

View File

@ -1,4 +1,5 @@
import { Component,
import {
Component,
ElementRef,
Input,
OnChanges,
@ -6,7 +7,8 @@ import { Component,
OnInit,
QueryList,
SimpleChanges,
ViewChildren } from '@angular/core';
ViewChildren
} from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Select, Store } from '@ngxs/store';
import { FormArray, FormBuilder, Validators } from '@angular/forms';
@ -27,8 +29,10 @@ import { takeUntil } from 'rxjs/operators';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { AccessItemsCustomisation, CustomField, getCustomFields } from '../../../shared/models/customisation';
import { GetWorkbasketAccessItems,
UpdateWorkbasketAccessItems } from '../../../shared/store/workbasket-store/workbasket.actions';
import {
GetWorkbasketAccessItems,
UpdateWorkbasketAccessItems
} from '../../../shared/store/workbasket-store/workbasket.actions';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
@Component({
@ -81,8 +85,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
private formsValidatorService: FormsValidatorService,
private notificationsService: NotificationService,
private store: Store
) {
}
) {}
get accessItemsGroups(): FormArray {
return this.AccessItemsForm.get('accessItemsGroups') as FormArray;
@ -90,7 +93,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
ngOnInit() {
this.customFields$ = this.accessItemsCustomization$.pipe(getCustomFields(customFieldCount));
this.accessItemsRepresentation$.subscribe(accessItemsRepresentation => {
this.accessItemsRepresentation$.subscribe((accessItemsRepresentation) => {
if (typeof accessItemsRepresentation !== 'undefined') {
this.accessItemsRepresentation = accessItemsRepresentation;
this.setAccessItemsGroups(accessItemsRepresentation.accessItems);
@ -101,7 +104,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
}
ngAfterViewInit() {
this.inputs.changes.subscribe(next => {
this.inputs.changes.subscribe((next) => {
if (typeof next.last !== 'undefined') {
if (this.added) next.last.nativeElement.focus();
}
@ -131,7 +134,8 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
this.requestInProgress = false;
});
this.savingWorkbaskets.triggeredAccessItemsSaving()
this.savingWorkbaskets
.triggeredAccessItemsSaving()
.pipe(takeUntil(this.destroy$))
.subscribe((savingInformation: SavingInformation) => {
if (this.action === ACTION.COPY) {
@ -144,8 +148,8 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
}
setAccessItemsGroups(accessItems: Array<WorkbasketAccessItems>) {
const AccessItemsFormGroups = accessItems.map(accessItem => this.formBuilder.group(accessItem));
AccessItemsFormGroups.forEach(accessItemGroup => {
const AccessItemsFormGroups = accessItems.map((accessItem) => this.formBuilder.group(accessItem));
AccessItemsFormGroups.forEach((accessItemGroup) => {
accessItemGroup.controls.accessId.setValidators(Validators.required);
});
const AccessItemsFormArray = this.formBuilder.array(AccessItemsFormGroups);
@ -176,7 +180,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
permCustom10: false,
permCustom11: false,
permCustom12: false,
_links: {},
_links: {}
};
}
@ -210,18 +214,25 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
onSubmit() {
this.formsValidatorService.formSubmitAttempt = true;
this.formsValidatorService.validateFormAccess(this.accessItemsGroups, this.toggleValidationAccessIdMap).then(value => {
if (value) {
this.onSave();
}
});
this.formsValidatorService
.validateFormAccess(this.accessItemsGroups, this.toggleValidationAccessIdMap)
.then((value) => {
if (value) {
this.onSave();
}
});
}
checkAll(row: number, value: any) {
const checkAll = value.target.checked;
const workbasketAccessItemsObj: WorkbasketAccessItems = this.createWorkbasketAccessItems();
Object.keys(workbasketAccessItemsObj).forEach(property => {
if (property !== 'accessId' && property !== '_links' && property !== 'workbasketId' && property !== 'accessItemId') {
Object.keys(workbasketAccessItemsObj).forEach((property) => {
if (
property !== 'accessId' &&
property !== '_links' &&
property !== 'workbasketId' &&
property !== 'accessItemId'
) {
this.accessItemsGroups.controls[row].get(property).setValue(checkAll);
}
});
@ -234,12 +245,16 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
private onSave() {
this.requestInProgressService.setRequestInProgress(true);
this.store.dispatch(new UpdateWorkbasketAccessItems(
this.accessItemsRepresentation._links.self.href,
this.AccessItemsForm.value.accessItemsGroups
)).subscribe(() => {
this.requestInProgressService.setRequestInProgress(false);
});
this.store
.dispatch(
new UpdateWorkbasketAccessItems(
this.accessItemsRepresentation._links.self.href,
this.AccessItemsForm.value.accessItemsGroups
)
)
.subscribe(() => {
this.requestInProgressService.setRequestInProgress(false);
});
}
private setBadge() {
@ -249,13 +264,13 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
}
private cloneAccessItems(inputaccessItem): Array<WorkbasketAccessItems> {
return this.AccessItemsForm.value.accessItemsGroups.map(
(accessItems: WorkbasketAccessItems) => ({ ...accessItems })
);
return this.AccessItemsForm.value.accessItemsGroups.map((accessItems: WorkbasketAccessItems) => ({
...accessItems
}));
}
private setWorkbasketIdForCopy(workbasketId: string) {
this.accessItemsGroups.value.forEach(element => {
this.accessItemsGroups.value.forEach((element) => {
delete element.accessItemId;
element.workbasketId = workbasketId;
});

View File

@ -33,12 +33,29 @@ import { NotificationService } from '../../../shared/services/notifications/noti
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
function createWorkbasket(workbasketId?, created?, key?, domain?, type?, modified?, name?, description?,
owner?, custom1?, custom2?, custom3?, custom4?, orgLevel1?, orgLevel2?, orgLevel3?, orgLevel4?,
_links?: Links, markedForDeletion?: boolean) {
function createWorkbasket(
workbasketId?,
created?,
key?,
domain?,
type?,
modified?,
name?,
description?,
owner?,
custom1?,
custom2?,
custom3?,
custom4?,
orgLevel1?,
orgLevel2?,
orgLevel3?,
orgLevel4?,
_links?: Links,
markedForDeletion?: boolean
) {
const workbasket: Workbasket = {
workbasketId,
created,
@ -70,29 +87,63 @@ describe('WorkbasketDetailsComponent', () => {
let masterAndDetailService;
let workbasketService;
let router;
const workbasket = createWorkbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '',
{});
const workbasket = createWorkbasket(
'1',
'',
'',
'',
ICONTYPES.TOPIC,
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
{}
);
const workbasketSummaryRepresentation: WorkbasketSummaryRepresentation = { workbaskets: [], _links: {}, page: {} };
const workbasketAccessItemsRepresentation: WorkbasketAccessItemsRepresentation = { accessItems: [], _links: {} };
const routes: Routes = [
{ path: '*', component: DummyDetailComponent }
];
const routes: Routes = [{ path: '*', component: DummyDetailComponent }];
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, ReactiveFormsModule,
InfiniteScrollModule, NgxsModule.forRoot()],
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent,
imports: [
RouterTestingModule.withRoutes(routes),
FormsModule,
AngularSvgIconModule,
HttpClientModule,
ReactiveFormsModule,
InfiniteScrollModule,
NgxsModule.forRoot()
],
declarations: [
WorkbasketDetailsComponent,
WorkbasketInformationComponent,
WorkbasketAccessItemsComponent,
WorkbasketDistributionTargetsComponent, WorkbasketDualListComponent, DummyDetailComponent],
providers: [WorkbasketService, MasterAndDetailService, RequestInProgressService,
NotificationService, SavingWorkbasketService, ImportExportService]
WorkbasketDistributionTargetsComponent,
WorkbasketDualListComponent,
DummyDetailComponent
],
providers: [
WorkbasketService,
MasterAndDetailService,
RequestInProgressService,
NotificationService,
SavingWorkbasketService,
ImportExportService
]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(WorkbasketDetailsComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement.nativeElement;
@ -106,7 +157,9 @@ describe('WorkbasketDetailsComponent', () => {
spyOn(workbasketService, 'getWorkBasket').and.callFake(() => of(workbasket));
spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => of(workbasketAccessItemsRepresentation));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(workbasketSummaryRepresentation));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() =>
of(workbasketSummaryRepresentation)
);
done();
});
});

View File

@ -40,34 +40,34 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
destroy$ = new Subject<void>();
constructor(private service: WorkbasketService,
constructor(
private service: WorkbasketService,
private route: ActivatedRoute,
private router: Router,
private domainService: DomainService,
private importExportService: ImportExportService,
private store: Store) {
}
private store: Store
) {}
ngOnInit() {
this.selectedWorkbasketAndAction$
.pipe(takeUntil(this.destroy$))
.subscribe(selectedWorkbasketAndAction => {
this.action = selectedWorkbasketAndAction.action;
if (this.action === ACTION.CREATE) {
this.tabSelected = 'information';
this.selectedId = undefined;
this.initWorkbasket();
} else if (this.action === ACTION.COPY) {
// delete this.workbasket.key;
this.workbasketCopy = this.workbasket;
this.getWorkbasketInformation();
} else if (typeof selectedWorkbasketAndAction.selectedWorkbasket !== 'undefined') {
this.workbasket = { ...selectedWorkbasketAndAction.selectedWorkbasket };
this.getWorkbasketInformation(this.workbasket);
}
});
this.selectedWorkbasketAndAction$.pipe(takeUntil(this.destroy$)).subscribe((selectedWorkbasketAndAction) => {
this.action = selectedWorkbasketAndAction.action;
if (this.action === ACTION.CREATE) {
this.tabSelected = 'information';
this.selectedId = undefined;
this.initWorkbasket();
} else if (this.action === ACTION.COPY) {
// delete this.workbasket.key;
this.workbasketCopy = this.workbasket;
this.getWorkbasketInformation();
} else if (typeof selectedWorkbasketAndAction.selectedWorkbasket !== 'undefined') {
this.workbasket = { ...selectedWorkbasketAndAction.selectedWorkbasket };
this.getWorkbasketInformation(this.workbasket);
}
});
this.importExportService.getImportingFinished()
this.importExportService
.getImportingFinished()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
if (this.workbasket) {
@ -105,15 +105,18 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
workbasketIdSelected = selectedWorkbasket.workbasketId;
}
this.requestInProgress = true;
if (!workbasketIdSelected && this.action === ACTION.CREATE) { // CREATE
if (!workbasketIdSelected && this.action === ACTION.CREATE) {
// CREATE
this.workbasket = {};
this.domainService.getSelectedDomain()
this.domainService
.getSelectedDomain()
.pipe(takeUntil(this.destroy$))
.subscribe(domain => {
.subscribe((domain) => {
this.workbasket.domain = domain;
});
this.requestInProgress = false;
} else if (!workbasketIdSelected && this.action === ACTION.COPY) { // COPY
} else if (!workbasketIdSelected && this.action === ACTION.COPY) {
// COPY
this.workbasket = { ...this.workbasketCopy };
delete this.workbasket.workbasketId;
this.requestInProgress = false;
@ -126,9 +129,10 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
}
private checkDomainAndRedirect() {
this.domainService.getSelectedDomain()
this.domainService
.getSelectedDomain()
.pipe(takeUntil(this.destroy$))
.subscribe(domain => {
.subscribe((domain) => {
if (domain !== '' && this.workbasket && this.workbasket.domain !== domain) {
this.backClicked();
}

View File

@ -1,15 +1,13 @@
.button-margin-top {
margin-top: 100px
margin-top: 100px;
}
.list-arrows > button{
margin: 10px 0px;
.list-arrows > button {
margin: 10px 0px;
}
.col-md-5-6 {
@media (min-width: 992px){
width: 45.82%;
}
@media (min-width: 992px) {
width: 45.82%;
}
}

View File

@ -21,18 +21,55 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { NgxsModule, Store } from '@ngxs/store';
import { WorkbasketDistributionTargetsComponent, Side } from './workbasket-distribution-targets.component';
import { WorkbasketDualListComponent } from '../workbasket-dual-list/workbasket-dual-list.component';
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
describe('WorkbasketDistributionTargetsComponent', () => {
let component: WorkbasketDistributionTargetsComponent;
let fixture: ComponentFixture<WorkbasketDistributionTargetsComponent>;
let workbasketService;
const workbasket = createWorkbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '',
{});
function createWorkbasket(workbasketId?, created?, key?, domain?, type?, modified?, name?, description?,
owner?, custom1?, custom2?, custom3?, custom4?, orgLevel1?, orgLevel2?, orgLevel3?, orgLevel4?,
_links?: Links, markedForDeletion?: boolean): Workbasket {
const workbasket = createWorkbasket(
'1',
'',
'',
'',
ICONTYPES.TOPIC,
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
{}
);
function createWorkbasket(
workbasketId?,
created?,
key?,
domain?,
type?,
modified?,
name?,
description?,
owner?,
custom1?,
custom2?,
custom3?,
custom4?,
orgLevel1?,
orgLevel2?,
orgLevel3?,
orgLevel4?,
_links?: Links,
markedForDeletion?: boolean
): Workbasket {
return {
workbasketId,
created,
@ -56,7 +93,19 @@ describe('WorkbasketDistributionTargetsComponent', () => {
};
}
function createWorkbasketSummary(workbasketId, key, name, domain, type, description, owner, custom1, custom2, custom3, custom4) {
function createWorkbasketSummary(
workbasketId,
key,
name,
domain,
type,
description,
owner,
custom1,
custom2,
custom3,
custom4
) {
const workbasketSummary: WorkbasketSummary = {
workbasketId,
key,
@ -74,10 +123,8 @@ describe('WorkbasketDistributionTargetsComponent', () => {
}
const workbasketSummaryResource: WorkbasketSummaryRepresentation = {
workbaskets: [
createWorkbasketSummary('1', 'key1', 'NAME1', '', 'PERSONAL',
'description 1', 'owner1', '', '', '', ''),
createWorkbasketSummary('2', 'key2', 'NAME2', '', 'PERSONAL',
'description 2', 'owner2', '', '', '', ''),
createWorkbasketSummary('1', 'key1', 'NAME1', '', 'PERSONAL', 'description 1', 'owner1', '', '', '', ''),
createWorkbasketSummary('2', 'key2', 'NAME2', '', 'PERSONAL', 'description 2', 'owner2', '', '', '', '')
],
_links: new LinksWorkbasketSummary({ href: 'url' }),
page: {}
@ -90,17 +137,22 @@ describe('WorkbasketDistributionTargetsComponent', () => {
const storeSpy: jasmine.SpyObj<Store> = jasmine.createSpyObj('Store', ['select', 'dispatch']);
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule, NgxsModule.forRoot()],
declarations: [WorkbasketDistributionTargetsComponent, WorkbasketDualListComponent],
providers: [WorkbasketService, NotificationService, SavingWorkbasketService, RequestInProgressService,
{ provide: Store, useValue: storeSpy }]
providers: [
WorkbasketService,
NotificationService,
SavingWorkbasketService,
RequestInProgressService,
{ provide: Store, useValue: storeSpy }
]
});
};
configureTests(configure).then(testBed => {
storeSpy.select.and.callFake(selector => {
configureTests(configure).then((testBed) => {
storeSpy.select.and.callFake((selector) => {
switch (selector) {
case WorkbasketSelectors.workbasketDistributionTargets:
return of(['distributionTargets', '_links']);
@ -113,7 +165,9 @@ describe('WorkbasketDistributionTargetsComponent', () => {
component.workbasket = workbasket;
workbasketService = TestBed.get(WorkbasketService);
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(workbasketSummaryResource));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(workbasketDistributionTargets));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() =>
of(workbasketDistributionTargets)
);
component.ngOnChanges({
active: new SimpleChange(undefined, 'distributionTargets', true)
});
@ -139,21 +193,19 @@ describe('WorkbasketDistributionTargetsComponent', () => {
let repeteadElemens = false;
expect(component.distributionTargetsLeft.length).toBe(2);
expect(component.distributionTargetsRight.length).toBe(2);
component.distributionTargetsLeft.forEach(leftElement => {
component.distributionTargetsRight.forEach(rightElement => {
if (leftElement.workbasketId === rightElement.workbasketId) { repeteadElemens = true; }
component.distributionTargetsLeft.forEach((leftElement) => {
component.distributionTargetsRight.forEach((rightElement) => {
if (leftElement.workbasketId === rightElement.workbasketId) {
repeteadElemens = true;
}
});
});
expect(repeteadElemens).toBeTruthy();
});
it('should reset distribution target and distribution target selected on reset', () => {
component.distributionTargetsLeft.push(
createWorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '')
);
component.distributionTargetsRight.push(
createWorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '')
);
component.distributionTargetsLeft.push(createWorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', ''));
component.distributionTargetsRight.push(createWorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', ''));
expect(component.distributionTargetsLeft.length).toBe(3);
expect(component.distributionTargetsRight.length).toBe(3);

View File

@ -1,10 +1,4 @@
import { Component,
ElementRef,
Input,
OnChanges,
OnDestroy, OnInit,
SimpleChanges,
ViewChild } from '@angular/core';
import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Workbasket } from 'app/shared/models/workbasket';
@ -24,8 +18,11 @@ import { Select, Store } from '@ngxs/store';
import { take, takeUntil } from 'rxjs/operators';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { GetWorkbasketDistributionTargets,
GetWorkbasketsSummary, UpdateWorkbasketDistributionTargets } from '../../../shared/store/workbasket-store/workbasket.actions';
import {
GetWorkbasketDistributionTargets,
GetWorkbasketsSummary,
UpdateWorkbasketDistributionTargets
} from '../../../shared/store/workbasket-store/workbasket.actions';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
import { WorkbasketStateModel } from '../../../shared/store/workbasket-store/workbasket.state';
@ -82,10 +79,10 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
private orientationService: OrientationService,
private notificationsService: NotificationService,
private store: Store
) { }
) {}
ngOnInit() {
this.workbasketDistributionTargets$.subscribe(workbasketDistributionTargets => {
this.workbasketDistributionTargets$.subscribe((workbasketDistributionTargets) => {
if (typeof workbasketDistributionTargets !== 'undefined') {
this.distributionTargetsSelectedResource = { ...workbasketDistributionTargets };
this.distributionTargetsSelected = this.distributionTargetsSelectedResource.distributionTargets;
@ -120,7 +117,8 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
}
this.store.dispatch(new GetWorkbasketDistributionTargets(this.workbasket._links.distributionTargets.href));
this.savingWorkbaskets.triggeredDistributionTargetsSaving()
this.savingWorkbaskets
.triggeredDistributionTargetsSaving()
.pipe(takeUntil(this.destroy$))
.subscribe((savingInformation: SavingInformation) => {
if (this.action === ACTION.COPY) {
@ -129,7 +127,8 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
}
});
this.orientationService.getOrientation()
this.orientationService
.getOrientation()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.calculateNumberItemsList();
@ -144,52 +143,73 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
}
// TODO: Implement this into NGXS
this.workbasketService.getWorkBasketsSummary(true)
this.workbasketService
.getWorkBasketsSummary(true)
.pipe(takeUntil(this.destroy$))
.subscribe(
(distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
if (TaskanaQueryParameters.page === 1) {
this.distributionTargetsLeft = [];
this.page = distributionTargetsAvailable.page;
}
if (side === this.side.LEFT) {
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
} else if (side === this.side.RIGHT) {
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
} else {
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable.workbaskets);
}
this.onRequest(true);
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
if (TaskanaQueryParameters.page === 1) {
this.distributionTargetsLeft = [];
this.page = distributionTargetsAvailable.page;
}
);
if (side === this.side.LEFT) {
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
} else if (side === this.side.RIGHT) {
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
} else {
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable.workbaskets);
}
this.onRequest(true);
});
}
performFilter(dualListFilter: any) {
this.fillDistributionTargets(dualListFilter.side, undefined);
this.onRequest(false, dualListFilter.side);
this.store.dispatch(new GetWorkbasketsSummary(true, '', '', '',
dualListFilter.filterBy.filterParams.name, dualListFilter.filterBy.filterParams.description, '',
dualListFilter.filterBy.filterParams.owner, dualListFilter.filterBy.filterParams.type, '',
dualListFilter.filterBy.filterParams.key, '', true)).subscribe((state: WorkbasketStateModel) => {
this.fillDistributionTargets(dualListFilter.side, state.paginatedWorkbasketsSummary.workbaskets);
this.onRequest(true, dualListFilter.side);
});
this.store
.dispatch(
new GetWorkbasketsSummary(
true,
'',
'',
'',
dualListFilter.filterBy.filterParams.name,
dualListFilter.filterBy.filterParams.description,
'',
dualListFilter.filterBy.filterParams.owner,
dualListFilter.filterBy.filterParams.type,
'',
dualListFilter.filterBy.filterParams.key,
'',
true
)
)
.subscribe((state: WorkbasketStateModel) => {
this.fillDistributionTargets(dualListFilter.side, state.paginatedWorkbasketsSummary.workbaskets);
this.onRequest(true, dualListFilter.side);
});
}
onSave() {
this.requestInProgressService.setRequestInProgress(true);
this.store.dispatch(new UpdateWorkbasketDistributionTargets(
this.distributionTargetsSelectedResource._links.self.href,
this.getSeletedIds()
)).subscribe(() => {
this.requestInProgressService.setRequestInProgress(false);
return true;
}, error => {
this.requestInProgressService.setRequestInProgress(false);
return false;
});
this.store
.dispatch(
new UpdateWorkbasketDistributionTargets(
this.distributionTargetsSelectedResource._links.self.href,
this.getSeletedIds()
)
)
.subscribe(
() => {
this.requestInProgressService.setRequestInProgress(false);
return true;
},
(error) => {
this.requestInProgressService.setRequestInProgress(false);
return false;
}
);
/* TODO: OLD IMPLEMENTATION, KEPT HERE FOR REFERENCE
this.workbasketService.updateWorkBasketsDistributionTargets(
this.distributionTargetsSelectedResource._links.self.href, this.getSeletedIds()
@ -220,7 +240,10 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft);
this.distributionTargetsSelected = [...this.distributionTargetsSelected, ...itemsSelected];
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
if (((itemsLeft - itemsSelected.length) <= TaskanaQueryParameters.pageSize) && ((itemsLeft + itemsRight) < this.page.totalElements)) {
if (
itemsLeft - itemsSelected.length <= TaskanaQueryParameters.pageSize &&
itemsLeft + itemsRight < this.page.totalElements
) {
this.getNextPage(side);
}
this.unselectItems(this.distributionTargetsSelected);
@ -244,9 +267,13 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
if (this.panelBody) {
const cardHeight = 72;
const unusedHeight = 100;
this.cards = this.orientationService.calculateNumberItemsList(
this.panelBody.nativeElement.offsetHeight, cardHeight, unusedHeight, true
) + 1; // TODO: warum +1
this.cards =
this.orientationService.calculateNumberItemsList(
this.panelBody.nativeElement.offsetHeight,
cardHeight,
unusedHeight,
true
) + 1; // TODO: warum +1
}
}
@ -267,7 +294,7 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
}
getSelectedItems(originList: any): Array<any> {
return originList.filter((item: any) => (item.selected === true));
return originList.filter((item: any) => item.selected === true);
}
unselectItems(originList: any): Array<any> {
@ -282,7 +309,7 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
removeSelectedItems(originList: any, selectedItemList) {
for (let index = originList.length - 1; index >= 0; index--) {
if (selectedItemList.some(itemToRemove => (originList[index].workbasketId === itemToRemove.workbasketId))) {
if (selectedItemList.some((itemToRemove) => originList[index].workbasketId === itemToRemove.workbasketId)) {
originList.splice(index, 1);
}
}
@ -293,9 +320,11 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
this.loadingItems = false;
const inProgress = !finished;
switch (side) {
case Side.LEFT: this.requestInProgressLeft = inProgress;
case Side.LEFT:
this.requestInProgressLeft = inProgress;
break;
case Side.RIGHT: this.requestInProgressRight = inProgress;
case Side.RIGHT:
this.requestInProgressRight = inProgress;
break;
default:
this.requestInProgressLeft = inProgress;
@ -305,15 +334,19 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
getSeletedIds(): Array<string> {
const distributionTargetsSelelected: Array<string> = [];
this.distributionTargetsSelected.forEach(item => {
this.distributionTargetsSelected.forEach((item) => {
distributionTargetsSelelected.push(item.workbasketId);
});
return distributionTargetsSelelected;
}
private uncheckSelectAll(side: number) {
if (side === Side.LEFT && this.selectAllLeft) { this.selectAllLeft = false; }
if (side === Side.RIGHT && this.selectAllRight) { this.selectAllRight = false; }
if (side === Side.LEFT && this.selectAllLeft) {
this.selectAllLeft = false;
}
if (side === Side.RIGHT && this.selectAllRight) {
this.selectAllRight = false;
}
}
ngOnDestroy() {

View File

@ -1,49 +1,48 @@
$selected-item: #e3f3f5;
.dual-list {
min-height: 250px;
padding: 0px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
& .row {
padding: 0px 0px 0px 3px;
}
& .row:first {
border-top: 1px solid #ddd;
}
min-height: 250px;
padding: 0px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
& .row {
padding: 0px 0px 0px 3px;
}
& .row:first {
border-top: 1px solid #ddd;
}
& div.pull-right {
margin-right: 17px;
}
& div.pull-right {
margin-right: 17px;
}
& >.list-group {
margin-bottom: 0px;
margin-top: 0px;
& > .list-group {
margin-bottom: 0px;
margin-top: 0px;
}
& > .list-group > li {
border-left: none;
border-right: none;
}
}
& > .list-group > li {
border-left: none;
border-right: none;
}
overflow-x: hidden;
overflow-y: hidden;
overflow-x: hidden;
overflow-y: hidden;
@media screen and (max-width: 991px){
height: calc((100vh - 241px) / 2);
min-height: 120px;
margin-bottom: 0;
}
max-height: calc(100vh - 194px);
margin-bottom: 2px;
@media screen and (max-width: 991px) {
height: calc((100vh - 241px) / 2);
min-height: 120px;
margin-bottom: 0;
}
max-height: calc(100vh - 194px);
margin-bottom: 2px;
}
.infinite-scroll {
overflow-y: scroll;
height: calc(100vh - 233px);
@media screen and (max-width: 991px){
@media screen and (max-width: 991px) {
height: calc((100vh - 315px) / 2);
min-height: 83px;
}
@ -54,60 +53,60 @@ $selected-item: #e3f3f5;
}
.list-group {
margin-top: 8px;
margin-top: 8px;
}
ul>li {
&:first-child.list-group-item.selected {
ul > li {
&:first-child.list-group-item.selected {
border-color: #ddd;
}
&.list-group-item.selected {
background-color: $selected-item;
}
}
&.list-group-item.selected {
background-color: $selected-item;
}
}
.list-left li {
cursor: pointer;
cursor: pointer;
}
button.no-style{
background: none;
border:none;
button.no-style {
background: none;
border: none;
}
.row.list-group {
margin-left: 2px;
margin-left: 2px;
}
.list-group > li {
border-left: none;
border-right: none;
border-left: none;
border-right: none;
}
a > label{
height: 2em;
width: 100%;
a > label {
height: 2em;
width: 100%;
}
dd, dt {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
dd,
dt {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
dt > i {
font-weight: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-weight: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
li > div.row > dl {
margin-bottom: 0px;
margin-bottom: 0px;
}
li.list-group-item:hover, {
color: #555;
text-decoration: none;
background-color: #f5f5f5;
li.list-group-item:hover {
color: #555;
text-decoration: none;
background-color: #f5f5f5;
}

View File

@ -13,9 +13,9 @@ import { Side } from '../workbasket-distribution-targets/workbasket-distribution
export class WorkbasketDualListComponent implements OnInit {
@Input() distributionTargets: WorkbasketSummary[];
@Input() distributionTargetsSelected: WorkbasketSummary[];
@Output() performDualListFilter = new EventEmitter<{ filterBy: Filter, side: Side }>();
@Output() performDualListFilter = new EventEmitter<{ filterBy: Filter; side: Side }>();
@Input() requestInProgress = false;
@Input() loadingItems ? = false;
@Input() loadingItems? = false;
@Input() side: Side;
@Input() header: string;
@Output() scrolling = new EventEmitter<Side>();

View File

@ -1,3 +1,3 @@
.dropdown-menu {
min-width: auto;
min-width: auto;
}

View File

@ -23,8 +23,7 @@ import { NotificationService } from '../../../shared/services/notifications/noti
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
const routes: Routes = [
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' },
@ -44,17 +43,44 @@ describe('WorkbasketInformationComponent', () => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [WorkbasketInformationComponent, DummyDetailComponent],
imports: [FormsModule, AngularSvgIconModule, HttpClientModule, RouterTestingModule.withRoutes(routes),
NgxsModule.forRoot()],
providers: [WorkbasketService, NotificationService, SavingWorkbasketService,
RequestInProgressService, FormsValidatorService]
imports: [
FormsModule,
AngularSvgIconModule,
HttpClientModule,
RouterTestingModule.withRoutes(routes),
NgxsModule.forRoot()
],
providers: [
WorkbasketService,
NotificationService,
SavingWorkbasketService,
RequestInProgressService,
FormsValidatorService
]
});
};
function createWorkbasket(workbasketId?, created?, key?, domain?, type?, modified?, name?, description?,
owner?, custom1?, custom2?, custom3?, custom4?, orgLevel1?, orgLevel2?, orgLevel3?, orgLevel4?,
_links?: Links, markedForDeletion?: boolean) {
function createWorkbasket(
workbasketId?,
created?,
key?,
domain?,
type?,
modified?,
name?,
description?,
owner?,
custom1?,
custom2?,
custom3?,
custom4?,
orgLevel1?,
orgLevel2?,
orgLevel3?,
orgLevel4?,
_links?: Links,
markedForDeletion?: boolean
) {
if (!type) {
// eslint-disable-next-line no-param-reassign
type = 'PERSONAL';
@ -83,8 +109,8 @@ describe('WorkbasketInformationComponent', () => {
return workbasket;
}
beforeEach(done => {
configureTests(configure).then(testBed => {
beforeEach((done) => {
configureTests(configure).then((testBed) => {
fixture = testBed.createComponent(WorkbasketInformationComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement.nativeElement;
@ -119,11 +145,26 @@ describe('WorkbasketInformationComponent', () => {
it('should create a copy of workbasket when workbasket is selected', () => {
expect(component.workbasketClone).toBeUndefined();
component.workbasket = createWorkbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'orgLevel3', 'orgLevel4');
component.ngOnChanges(
undefined
component.workbasket = createWorkbasket(
'id',
'created',
'keyModified',
'domain',
ICONTYPES.TOPIC,
'modified',
'name',
'description',
'owner',
'custom1',
'custom2',
'custom3',
'custom4',
'orgLevel1',
'orgLevel2',
'orgLevel3',
'orgLevel4'
);
component.ngOnChanges(undefined);
fixture.detectChanges();
expect(component.workbasket.workbasketId).toEqual(component.workbasketClone.workbasketId);
});

View File

@ -1,10 +1,4 @@
import { Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
ViewChild } from '@angular/core';
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subject, Subscription } from 'rxjs';
import { NgForm } from '@angular/forms';
@ -23,20 +17,21 @@ import { map, takeUntil } from 'rxjs/operators';
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { CustomField,
getCustomFields,
WorkbasketsCustomisation } from '../../../shared/models/customisation';
import { CopyWorkbasket, MarkWorkbasketForDeletion,
RemoveDistributionTarget, SaveNewWorkbasket,
UpdateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
import { CustomField, getCustomFields, WorkbasketsCustomisation } from '../../../shared/models/customisation';
import {
CopyWorkbasket,
MarkWorkbasketForDeletion,
RemoveDistributionTarget,
SaveNewWorkbasket,
UpdateWorkbasket
} from '../../../shared/store/workbasket-store/workbasket.actions';
@Component({
selector: 'taskana-administration-workbasket-information',
templateUrl: './workbasket-information.component.html',
styleUrls: ['./workbasket-information.component.scss']
})
export class WorkbasketInformationComponent
implements OnInit, OnChanges, OnDestroy {
export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDestroy {
@Input()
workbasket: Workbasket;
@ -68,8 +63,7 @@ implements OnInit, OnChanges, OnDestroy {
private formsValidatorService: FormsValidatorService,
private notificationService: NotificationService,
private store: Store
) {
}
) {}
ngOnInit() {
this.allTypes = new Map([
@ -79,16 +73,14 @@ implements OnInit, OnChanges, OnDestroy {
['TOPIC', 'Topic']
]);
this.customFields$ = this.workbasketsCustomisation$.pipe(
map(customisation => customisation.information),
map((customisation) => customisation.information),
getCustomFields(customFieldCount)
);
this.workbasketsCustomisation$
.pipe(takeUntil(this.destroy$))
.subscribe(workbasketsCustomization => {
if (workbasketsCustomization.information.owner) {
this.lookupField = workbasketsCustomization.information.owner.lookupField;
}
});
this.workbasketsCustomisation$.pipe(takeUntil(this.destroy$)).subscribe((workbasketsCustomization) => {
if (workbasketsCustomization.information.owner) {
this.lookupField = workbasketsCustomization.information.owner.lookupField;
}
});
}
ngOnChanges(changes: SimpleChanges) {
@ -106,13 +98,11 @@ implements OnInit, OnChanges, OnDestroy {
onSubmit() {
this.formsValidatorService.formSubmitAttempt = true;
this.formsValidatorService
.validateFormInformation(this.workbasketForm, this.toogleValidationMap)
.then(value => {
if (value) {
this.onSave();
}
});
this.formsValidatorService.validateFormInformation(this.workbasketForm, this.toogleValidationMap).then((value) => {
if (value) {
this.onSave();
}
});
}
isFieldValid(field: string): boolean {
@ -146,11 +136,10 @@ implements OnInit, OnChanges, OnDestroy {
this.postNewWorkbasket();
return;
}
this.store.dispatch(new UpdateWorkbasket(this.workbasket._links.self.href, this.workbasket))
.subscribe(state => {
this.requestInProgressService.setRequestInProgress(false);
this.workbasketClone = { ...this.workbasket };
});
this.store.dispatch(new UpdateWorkbasket(this.workbasket._links.self.href, this.workbasket)).subscribe((state) => {
this.requestInProgressService.setRequestInProgress(false);
this.workbasketClone = { ...this.workbasket };
});
}
private beforeRequest() {
@ -164,25 +153,17 @@ implements OnInit, OnChanges, OnDestroy {
private postNewWorkbasket() {
this.addDateToWorkbasket();
this.store.dispatch(new SaveNewWorkbasket(this.workbasket)).subscribe(
() => {
this.afterRequest();
if (this.action === ACTION.COPY) {
this.savingWorkbasket.triggerDistributionTargetSaving(
new SavingInformation(
this.workbasket._links.distributionTargets.href,
this.workbasket.workbasketId
)
);
this.savingWorkbasket.triggerAccessItemsSaving(
new SavingInformation(
this.workbasket._links.accessItems.href,
this.workbasket.workbasketId
)
);
}
this.store.dispatch(new SaveNewWorkbasket(this.workbasket)).subscribe(() => {
this.afterRequest();
if (this.action === ACTION.COPY) {
this.savingWorkbasket.triggerDistributionTargetSaving(
new SavingInformation(this.workbasket._links.distributionTargets.href, this.workbasket.workbasketId)
);
this.savingWorkbasket.triggerAccessItemsSaving(
new SavingInformation(this.workbasket._links.accessItems.href, this.workbasket.workbasketId)
);
}
);
});
}
private addDateToWorkbasket() {

View File

@ -1,13 +1,13 @@
.list-group-item {
padding: 5px 0px 2px 1px;
border: none;
padding: 5px 0px 2px 1px;
border: none;
}
.tab-align{
padding: 8px 12px 8px 4px;
margin-bottom: 0px;
.tab-align {
padding: 8px 12px 8px 4px;
margin-bottom: 0px;
&>div{
& > div {
margin: 6px 0px;
}
}
}
}

View File

@ -27,9 +27,7 @@ import { ICONTYPES } from '../../../shared/models/icon-types';
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
describe('WorkbasketListToolbarComponent', () => {
let component: WorkbasketListToolbarComponent;
@ -38,15 +36,20 @@ describe('WorkbasketListToolbarComponent', () => {
let workbasketService;
let router;
const routes: Routes = [
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' }
];
const routes: Routes = [{ path: ':id', component: DummyDetailComponent, outlet: 'detail' }];
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, AngularSvgIconModule,
HttpClientModule, RouterTestingModule.withRoutes(routes), SharedModule, AppModule],
imports: [
FormsModule,
ReactiveFormsModule,
AngularSvgIconModule,
HttpClientModule,
RouterTestingModule.withRoutes(routes),
SharedModule,
AppModule
],
declarations: [WorkbasketListToolbarComponent, DummyDetailComponent, ImportExportComponent],
providers: [
WorkbasketService,
@ -56,7 +59,7 @@ describe('WorkbasketListToolbarComponent', () => {
]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(WorkbasketListToolbarComponent);
workbasketService = TestBed.get(WorkbasketService);
router = TestBed.get(Router);
@ -65,12 +68,16 @@ describe('WorkbasketListToolbarComponent', () => {
debugElement = fixture.debugElement.nativeElement;
component = fixture.componentInstance;
component.workbaskets = [{ workbasketId: '1',
key: 'key1',
name: 'NAME1',
description: 'description 1',
owner: 'owner 1',
type: ICONTYPES.PERSONAL }];
component.workbaskets = [
{
workbasketId: '1',
key: 'key1',
name: 'NAME1',
description: 'description 1',
owner: 'owner 1',
type: ICONTYPES.PERSONAL
}
];
component.workbaskets[0].markedForDeletion = false;
fixture.detectChanges();
@ -90,7 +97,9 @@ describe('WorkbasketListToolbarComponent', () => {
let sort: Sorting;
const compareSort = new Sorting();
component.performSorting.subscribe(value => { sort = value; });
component.performSorting.subscribe((value) => {
sort = value;
});
component.sorting(compareSort);
expect(sort).toBe(compareSort);
});
@ -99,7 +108,9 @@ describe('WorkbasketListToolbarComponent', () => {
let filter: Filter;
const compareFilter = new Filter();
component.performFilter.subscribe(value => { filter = value; });
component.performFilter.subscribe((value) => {
filter = value;
});
component.filtering(compareFilter);
expect(filter).toBe(compareFilter);
});

View File

@ -29,9 +29,20 @@ export class WorkbasketListToolbarComponent implements OnInit {
@Output() performFilter = new EventEmitter<Filter>();
selectionToImport = TaskanaType.WORKBASKETS;
sortingFields = new Map([['name', 'Name'], ['key', 'Key'], ['description', 'Description'], ['owner', 'Owner'], ['type', 'Type']]);
filteringTypes = new Map([['ALL', 'All'], ['PERSONAL', 'Personal'], ['GROUP', 'Group'],
['CLEARANCE', 'Clearance'], ['TOPIC', 'Topic']]);
sortingFields = new Map([
['name', 'Name'],
['key', 'Key'],
['description', 'Description'],
['owner', 'Owner'],
['type', 'Type']
]);
filteringTypes = new Map([
['ALL', 'All'],
['PERSONAL', 'Personal'],
['GROUP', 'Group'],
['CLEARANCE', 'Clearance'],
['TOPIC', 'Topic']
]);
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
toolbarState = false;
@ -49,15 +60,12 @@ export class WorkbasketListToolbarComponent implements OnInit {
private router: Router,
private store: Store,
private location: Location
) {
}
) {}
ngOnInit() {
this.workbasketActiveAction$
.pipe(takeUntil(this.destroy$))
.subscribe(action => {
this.action = action;
});
this.workbasketActiveAction$.pipe(takeUntil(this.destroy$)).subscribe((action) => {
this.action = action;
});
}
sorting(sort: Sorting) {

View File

@ -1,37 +1,38 @@
.row.list-group {
margin-left: 2px;
margin-left: 2px;
}
.list-group > li {
border-left: none;
border-right: none;
border-left: none;
border-right: none;
}
a > label{
height: 2em;
width: 100%;
a > label {
height: 2em;
width: 100%;
}
dd, dt {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
dd,
dt {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
dt > i {
font-weight: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-weight: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
li > div.row > dl {
margin-bottom: 0px;
margin-bottom: 0px;
}
li > div.row > dl:first-child {
margin-left: 15px;
margin-left: 15px;
}
.no-space {
border-top: none;
padding: 0px
border-top: none;
padding: 0px;
}

View File

@ -28,8 +28,7 @@ import { WorkbasketListComponent } from './workbasket-list.component';
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
class DummyDetailComponent {
}
class DummyDetailComponent {}
@Component({
selector: 'taskana-pagination',
@ -45,7 +44,19 @@ class PaginationComponent {
@Output() changePage = new EventEmitter<any>();
}
function createWorkbasketSummary(workbasketId, key, name, domain, type, description, owner, custom1, custom2, custom3, custom4) {
function createWorkbasketSummary(
workbasketId,
key,
name,
domain,
type,
description,
owner,
custom1,
custom2,
custom3,
custom4
) {
const workbasketSummary: WorkbasketSummary = {
workbasketId,
key,
@ -63,10 +74,8 @@ function createWorkbasketSummary(workbasketId, key, name, domain, type, descript
}
const workbasketSummaryResource: WorkbasketSummaryRepresentation = {
workbaskets: [
createWorkbasketSummary('1', 'key1', 'NAME1', '', 'PERSONAL',
'description 1', 'owner1', '', '', '', ''),
createWorkbasketSummary('2', 'key2', 'NAME2', '', 'PERSONAL',
'description 2', 'owner2', '', '', '', ''),
createWorkbasketSummary('1', 'key1', 'NAME1', '', 'PERSONAL', 'description 1', 'owner1', '', '', '', ''),
createWorkbasketSummary('2', 'key2', 'NAME2', '', 'PERSONAL', 'description 2', 'owner2', '', '', '', '')
],
_links: new LinksWorkbasketSummary({ href: 'url' }),
page: {}
@ -84,21 +93,16 @@ describe('WorkbasketListComponent', () => {
{ path: 'workbaskets', component: DummyDetailComponent }
];
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [
WorkbasketListComponent,
DummyDetailComponent,
WorkbasketListToolbarComponent,
ImportExportComponent,
],
imports: [
AngularSvgIconModule,
HttpClientModule,
RouterTestingModule.withRoutes(routes),
NgxsModule.forRoot()
ImportExportComponent
],
imports: [AngularSvgIconModule, HttpClientModule, RouterTestingModule.withRoutes(routes), NgxsModule.forRoot()],
providers: [
WorkbasketService,
WorkbasketDefinitionService,
@ -108,7 +112,7 @@ describe('WorkbasketListComponent', () => {
]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(WorkbasketListComponent);
component = fixture.componentInstance;
Object.defineProperty(component, 'workbasketsSummaryRepresentation$', { writable: true });
@ -121,7 +125,9 @@ describe('WorkbasketListComponent', () => {
debugElement = fixture.debugElement.nativeElement;
workbasketService = TestBed.get(WorkbasketService);
const orientationService = TestBed.get(OrientationService);
workbasketSummarySpy = spyOn(workbasketService, 'getWorkBasketsSummary').and.returnValue(of(workbasketSummaryResource));
workbasketSummarySpy = spyOn(workbasketService, 'getWorkBasketsSummary').and.returnValue(
of(workbasketSummaryResource)
);
spyOn(workbasketService, 'getSelectedWorkBasket').and.returnValue(of('2'));
spyOn(orientationService, 'getOrientation').and.returnValue(of(undefined));
@ -139,15 +145,18 @@ describe('WorkbasketListComponent', () => {
expect(component).toBeTruthy();
});
it('should have wb-action-toolbar, wb-search-bar, wb-list-container, wb-pagination,'
+ ' collapsedMenufilterWb and taskana-filter created in the html', () => {
expect(debugElement.querySelector('#wb-action-toolbar')).toBeDefined();
expect(debugElement.querySelector('#wb-search-bar')).toBeDefined();
expect(debugElement.querySelector('#wb-pagination')).toBeDefined();
expect(debugElement.querySelector('#wb-list-container')).toBeDefined();
expect(debugElement.querySelector('#collapsedMenufilterWb')).toBeDefined();
expect(debugElement.querySelector('taskana-filter')).toBeDefined();
});
it(
'should have wb-action-toolbar, wb-search-bar, wb-list-container, wb-pagination,' +
' collapsedMenufilterWb and taskana-filter created in the html',
() => {
expect(debugElement.querySelector('#wb-action-toolbar')).toBeDefined();
expect(debugElement.querySelector('#wb-search-bar')).toBeDefined();
expect(debugElement.querySelector('#wb-pagination')).toBeDefined();
expect(debugElement.querySelector('#wb-list-container')).toBeDefined();
expect(debugElement.querySelector('#collapsedMenufilterWb')).toBeDefined();
expect(debugElement.querySelector('taskana-filter')).toBeDefined();
}
);
it('should have rendered sort by: name, id, description, owner and type', () => {
expect(debugElement.querySelector('#sort-by-name')).toBeDefined();

View File

@ -13,8 +13,11 @@ import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
import { ImportExportService } from 'app/administration/services/import-export.service';
import { Actions, ofActionCompleted, ofActionDispatched, Select, Store } from '@ngxs/store';
import { takeUntil } from 'rxjs/operators';
import { DeselectWorkbasket, GetWorkbasketsSummary,
SelectWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
import {
DeselectWorkbasket,
GetWorkbasketsSummary,
SelectWorkbasket
} from '../../../shared/store/workbasket-store/workbasket.actions';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
import { Workbasket } from '../../../shared/models/workbasket';
@ -53,47 +56,45 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
private workbasketService: WorkbasketService,
private orientationService: OrientationService,
private importExportService: ImportExportService,
private ngxsActions$: Actions,
private ngxsActions$: Actions
) {
this.ngxsActions$.pipe(ofActionDispatched(GetWorkbasketsSummary),
takeUntil(this.destroy$))
.subscribe(() => {
this.requestInProgress = true;
});
this.ngxsActions$.pipe(ofActionCompleted(GetWorkbasketsSummary),
takeUntil(this.destroy$))
.subscribe(() => {
this.requestInProgress = false;
});
this.ngxsActions$.pipe(ofActionDispatched(GetWorkbasketsSummary), takeUntil(this.destroy$)).subscribe(() => {
this.requestInProgress = true;
});
this.ngxsActions$.pipe(ofActionCompleted(GetWorkbasketsSummary), takeUntil(this.destroy$)).subscribe(() => {
this.requestInProgress = false;
});
}
ngOnInit() {
this.requestInProgress = true;
this.selectedWorkbasket$.pipe(takeUntil(this.destroy$))
.subscribe(selectedWorkbasket => {
if (typeof selectedWorkbasket !== 'undefined') {
this.selectedId = selectedWorkbasket.workbasketId;
} else {
this.selectedId = undefined;
}
});
this.selectedWorkbasket$.pipe(takeUntil(this.destroy$)).subscribe((selectedWorkbasket) => {
if (typeof selectedWorkbasket !== 'undefined') {
this.selectedId = selectedWorkbasket.workbasketId;
} else {
this.selectedId = undefined;
}
});
TaskanaQueryParameters.page = this.pageSelected;
TaskanaQueryParameters.pageSize = this.pageSize;
this.workbasketService.workbasketSavedTriggered()
this.workbasketService
.workbasketSavedTriggered()
.pipe(takeUntil(this.destroy$))
.subscribe(value => {
.subscribe((value) => {
this.performRequest();
});
this.orientationService.getOrientation()
this.orientationService
.getOrientation()
.pipe(takeUntil(this.destroy$))
.subscribe((orientation: Orientation) => {
this.refreshWorkbasketList();
});
this.importExportService.getImportingFinished()
this.importExportService
.getImportingFinished()
.pipe(takeUntil(this.destroy$))
.subscribe((value: Boolean) => {
this.refreshWorkbasketList();
@ -125,15 +126,31 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
refreshWorkbasketList() {
this.cards = this.orientationService.calculateNumberItemsList(
window.innerHeight, 72, 170 + this.toolbarElement.nativeElement.offsetHeight, false
window.innerHeight,
72,
170 + this.toolbarElement.nativeElement.offsetHeight,
false
);
this.performRequest();
}
private performRequest(): void {
this.store.dispatch(new GetWorkbasketsSummary(true, this.sort.sortBy, this.sort.sortDirection, '',
this.filterBy.filterParams.name, this.filterBy.filterParams.description, '', this.filterBy.filterParams.owner,
this.filterBy.filterParams.type, '', this.filterBy.filterParams.key, ''));
this.store.dispatch(
new GetWorkbasketsSummary(
true,
this.sort.sortBy,
this.sort.sortDirection,
'',
this.filterBy.filterParams.name,
this.filterBy.filterParams.description,
'',
this.filterBy.filterParams.owner,
this.filterBy.filterParams.type,
'',
this.filterBy.filterParams.key,
''
)
);
TaskanaQueryParameters.pageSize = this.cards;
}

View File

@ -12,39 +12,26 @@ import { WorkbasketOverviewComponent } from './workbasket-overview.component';
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
@Component({
selector: 'taskana-workbasket-list',
template: 'dummylist'
})
export class DummyListComponent {
}
export class DummyListComponent {}
describe('WorkbasketOverviewComponent', () => {
let debugElement: any;
let component: WorkbasketOverviewComponent;
let fixture: ComponentFixture<WorkbasketOverviewComponent>;
const locationSpy: jasmine.SpyObj<Location> = jasmine.createSpyObj('Location', ['go']);
const routes: Routes = [
{ path: ':id', component: DummyDetailComponent }
];
const routes: Routes = [{ path: ':id', component: DummyDetailComponent }];
beforeEach(async(() => {
TestBed.configureTestingModule(
{ imports: [
RouterTestingModule.withRoutes(routes),
NgxsModule.forRoot()
],
declarations: [
WorkbasketOverviewComponent,
DummyDetailComponent,
DummyListComponent],
providers: [
{ provide: Location, useValue: locationSpy },
],
schemas: [NO_ERRORS_SCHEMA] }
)
.compileComponents();
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes(routes), NgxsModule.forRoot()],
declarations: [WorkbasketOverviewComponent, DummyDetailComponent, DummyListComponent],
providers: [{ provide: Location, useValue: locationSpy }],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
afterEach(() => {

View File

@ -5,9 +5,11 @@ import { ActivatedRoute } from '@angular/router';
import { takeUntil } from 'rxjs/operators';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
import { CreateWorkbasket,
import {
CreateWorkbasket,
SelectWorkbasket,
SetActiveAction } from '../../../shared/store/workbasket-store/workbasket.actions';
SetActiveAction
} from '../../../shared/store/workbasket-store/workbasket.actions';
@Component({
selector: 'app-workbasket-overview',
@ -20,34 +22,25 @@ export class WorkbasketOverviewComponent implements OnInit {
destroy$ = new Subject<void>();
routerParams: any;
constructor(
private route: ActivatedRoute,
private store: Store
) {
}
constructor(private route: ActivatedRoute, private store: Store) {}
ngOnInit() {
if (this.route.firstChild) {
this.route.firstChild.params
.pipe(takeUntil(this.destroy$))
.subscribe(params => {
this.routerParams = params;
if (this.routerParams.id) {
this.showDetail = true;
if (this.routerParams.id === 'new-workbasket') {
this.store.dispatch(new CreateWorkbasket());
} else {
this.store.dispatch(new SelectWorkbasket(this.routerParams.id));
}
this.route.firstChild.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
this.routerParams = params;
if (this.routerParams.id) {
this.showDetail = true;
if (this.routerParams.id === 'new-workbasket') {
this.store.dispatch(new CreateWorkbasket());
} else {
this.store.dispatch(new SelectWorkbasket(this.routerParams.id));
}
});
}
this.selectedWorkbasketAndAction$
.pipe(takeUntil(this.destroy$))
.subscribe(state => {
this.showDetail = !!state.selectedWorkbasket || state.action === 1;
}
});
}
this.selectedWorkbasketAndAction$.pipe(takeUntil(this.destroy$)).subscribe((state) => {
this.showDetail = !!state.selectedWorkbasket || state.action === 1;
});
}
ngOnDestroy() {

View File

@ -1,5 +1,5 @@
import { Classification } from 'app/shared/models/classification';
export interface TreeNodeModel extends Classification {
children: TreeNodeModel[]
children: TreeNodeModel[];
}

View File

@ -8,11 +8,11 @@ import { Classification } from '../../shared/models/classification';
@Injectable()
export class ClassificationDefinitionService {
url = `${environment.taskanaRestUrl}/v1/classification-definitions`;
constructor(private httpClient: HttpClient) { }
constructor(private httpClient: HttpClient) {}
// GET
async exportClassifications(domain: string) {
const domainRequest = (domain ? '' : `?domain=${domain}`);
const domainRequest = domain ? '' : `?domain=${domain}`;
const classificationDefinitions = await this.httpClient.get<Classification[]>(this.url + domainRequest).toPromise();
BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`);
}

View File

@ -7,25 +7,27 @@ import { Classification } from '../../shared/models/classification';
})
export class ClassificationTreeService {
transformToTreeNode(classifications: Classification[]): TreeNodeModel[] {
const classificationsAsTree: TreeNodeModel[] = classifications.map(c => ({
...c,
children: []
})).sort((a: TreeNodeModel, b: TreeNodeModel) => a.key.localeCompare(b.key));
const classificationsAsTree: TreeNodeModel[] = classifications
.map((c) => ({
...c,
children: []
}))
.sort((a: TreeNodeModel, b: TreeNodeModel) => a.key.localeCompare(b.key));
const roots: TreeNodeModel[] = [];
const children: TreeNodeModel[] = [];
classificationsAsTree.forEach(item => {
classificationsAsTree.forEach((item) => {
const parent = item.parentId;
const target = !parent ? roots : (children[parent] || (children[parent] = []));
const target = !parent ? roots : children[parent] || (children[parent] = []);
target.push(item);
});
roots.forEach(parent => this.findChildren(parent, children));
roots.forEach((parent) => this.findChildren(parent, children));
return roots;
}
private findChildren(parent: TreeNodeModel, children: TreeNodeModel[]) {
if (children[parent.classificationId]) {
parent.children = children[parent.classificationId];
parent.children.forEach(child => this.findChildren(child, children));
parent.children.forEach((child) => this.findChildren(child, children));
}
}
}

View File

@ -2,9 +2,7 @@ import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
export class SavingInformation {
constructor(public url: string,
public workbasketId: string) {
}
constructor(public url: string, public workbasketId: string) {}
}
@Injectable()

View File

@ -9,12 +9,14 @@ import { BlobGenerator } from 'app/shared/util/blob-generator';
export class WorkbasketDefinitionService {
url: string = `${environment.taskanaRestUrl}/v1/workbasket-definitions`;
constructor(private httpClient: HttpClient) { }
constructor(private httpClient: HttpClient) {}
// GET
async exportWorkbaskets(domain: string) {
const domainRequest = (domain === '' ? domain : `?domain=${domain}`);
const workbasketDefinitions = await this.httpClient.get<WorkbasketDefinition[]>(this.url + domainRequest).toPromise();
const domainRequest = domain === '' ? domain : `?domain=${domain}`;
const workbasketDefinitions = await this.httpClient
.get<WorkbasketDefinition[]>(this.url + domainRequest)
.toPromise();
BlobGenerator.saveFile(workbasketDefinitions, `Workbaskets_${TaskanaDate.getDate()}.json`);
}
}

View File

@ -14,22 +14,22 @@ const appRoutes: Routes = [
{
canActivate: [BusinessAdminGuard],
path: 'administration',
loadChildren: () => import('./administration/administration.module').then(m => m.AdministrationModule),
loadChildren: () => import('./administration/administration.module').then((m) => m.AdministrationModule)
},
{
canActivate: [MonitorGuard],
path: 'monitor',
loadChildren: () => import('./monitor/monitor.module').then(m => m.MonitorModule),
loadChildren: () => import('./monitor/monitor.module').then((m) => m.MonitorModule)
},
{
canActivate: [UserGuard],
path: 'workplace',
loadChildren: () => import('./workplace/workplace.module').then(m => m.WorkplaceModule)
loadChildren: () => import('./workplace/workplace.module').then((m) => m.WorkplaceModule)
},
{
canActivate: [HistoryGuard],
path: 'history',
loadChildren: () => import('./history/history.module').then(m => m.HistoryModule)
loadChildren: () => import('./history/history.module').then((m) => m.HistoryModule)
},
{
path: 'no-role',
@ -37,13 +37,13 @@ const appRoutes: Routes = [
},
{
path: 'administration',
redirectTo: 'administration/workbaskets',
redirectTo: 'administration/workbaskets'
},
{
path: '**',
redirectTo: 'workplace'
},
],
}
]
},
{
path: 'no-role',
@ -52,16 +52,10 @@ const appRoutes: Routes = [
{
path: '**',
redirectTo: 'taskana/workplace'
},
}
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes, { useHash: true }
)
],
exports: [
RouterModule
]
imports: [RouterModule.forRoot(appRoutes, { useHash: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
export class AppRoutingModule {}

View File

@ -13,21 +13,12 @@ describe('AppComponent', () => {
let fixture;
let debugElement;
const routes: Routes = [
{ path: 'classifications', component: AppComponent }
];
const routes: Routes = [{ path: 'classifications', component: AppComponent }];
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent, NavBarComponent
],
imports: [
AngularSvgIconModule,
RouterTestingModule.withRoutes(routes),
HttpClientModule,
SharedModule
]
declarations: [AppComponent, NavBarComponent],
imports: [AngularSvgIconModule, RouterTestingModule.withRoutes(routes), HttpClientModule, SharedModule]
}).compileComponents();
fixture = TestBed.createComponent(AppComponent);
@ -39,19 +30,22 @@ describe('AppComponent', () => {
document.body.removeChild(debugElement);
}));
it('should create the app', (() => {
it('should create the app', () => {
expect(app).toBeTruthy();
}));
});
it('should render title in a <a> tag', (() => {
it('should render title in a <a> tag', () => {
fixture.detectChanges();
expect(debugElement.querySelector('ul p a').textContent).toContain('Taskana administration');
}));
});
it('should call Router.navigateByUrl("classifications") and workbasketRoute should be false', (inject([Router], (router: Router) => {
expect(app.workbasketsRoute).toBe(true);
fixture.detectChanges();
router.navigateByUrl('/classifications');
expect(app.workbasketsRoute).toBe(false);
})));
it('should call Router.navigateByUrl("classifications") and workbasketRoute should be false', inject(
[Router],
(router: Router) => {
expect(app.workbasketsRoute).toBe(true);
fixture.detectChanges();
router.navigateByUrl('/classifications');
expect(app.workbasketsRoute).toBe(false);
}
));
});

View File

@ -38,9 +38,7 @@ export class AppComponent implements OnInit, OnDestroy {
private formsValidatorService: FormsValidatorService,
private errorService: NotificationService,
public uploadService: UploadService
) {
}
) {}
@HostListener('window:resize', ['$event'])
onResize() {
@ -48,16 +46,18 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.routerSubscription = this.router.events.subscribe(event => {
this.routerSubscription = this.router.events.subscribe((event) => {
if (event instanceof NavigationStart) {
this.selectedRouteService.selectRoute(event);
this.formsValidatorService.formSubmitAttempt = false;
}
});
this.requestInProgressSubscription = this.requestInProgressService.getRequestInProgress().subscribe((value: boolean) => {
this.requestInProgress = value;
});
this.requestInProgressSubscription = this.requestInProgressService
.getRequestInProgress()
.subscribe((value: boolean) => {
this.requestInProgress = value;
});
this.selectedRouteSubscription = this.selectedRouteService.getSelectedRoute().subscribe((value: string) => {
if (value.indexOf('classifications') !== -1) {
@ -65,7 +65,7 @@ export class AppComponent implements OnInit, OnDestroy {
}
this.selectedRoute = value;
});
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe(value => {
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe((value) => {
this.currentProgressValue = value;
});
}

View File

@ -66,12 +66,7 @@ const MODULES = [
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production, maxAge: 25 })
];
const DECLARATIONS = [
AppComponent,
NavBarComponent,
UserInformationComponent,
NoAccessComponent,
];
const DECLARATIONS = [AppComponent, NavBarComponent, UserInformationComponent, NoAccessComponent];
export function startupServiceFactory(startupService: StartupService): () => Promise<any> {
return (): Promise<any> => startupService.load();
@ -102,9 +97,8 @@ export function startupServiceFactory(startupService: StartupService): () => Pro
FormsValidatorService,
UploadService,
NotificationService,
ClassificationCategoriesService,
ClassificationCategoriesService
],
bootstrap: [AppComponent]
})
export class AppModule {
}
export class AppModule {}

View File

@ -21,18 +21,28 @@ export const configureTests = (configure: (testBed: TestBed) => void) => {
const testBed = getTestBed();
if (testBed.platform == null) {
testBed.initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
testBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
}
configure(testBed);
testBed.configureTestingModule({
imports: [BrowserAnimationsModule, SharedModule, FormsModule, ReactiveFormsModule, HttpClientModule, AngularSvgIconModule],
providers: [{ provide: TaskanaEngineService, useClass: TaskanaEngineServiceMock },
{ provide: DomainService, useClass: DomainServiceMock }, NotificationService,
RequestInProgressService, OrientationService, SelectedRouteService, FormsValidatorService]
imports: [
BrowserAnimationsModule,
SharedModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
AngularSvgIconModule
],
providers: [
{ provide: TaskanaEngineService, useClass: TaskanaEngineServiceMock },
{ provide: DomainService, useClass: DomainServiceMock },
NotificationService,
RequestInProgressService,
OrientationService,
SelectedRouteService,
FormsValidatorService
]
});
return testBed.compileComponents().then(() => testBed);

View File

@ -1,17 +1,13 @@
import { NgModule } from '@angular/core';
import { TaskanaTypeAheadMockComponent } from 'app/shared/components/type-ahead/type-ahead.mock.component';
const MODULES = [
];
const MODULES = [];
const DECLARATIONS = [
TaskanaTypeAheadMockComponent
];
const DECLARATIONS = [TaskanaTypeAheadMockComponent];
@NgModule({
declarations: DECLARATIONS,
imports: MODULES,
providers: []
})
export class AppTestModule {
}
export class AppTestModule {}

View File

@ -10,10 +10,11 @@ const routes: Routes = [
{
path: '**',
redirectTo: ''
}];
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HistoryRoutingModule { }
export class HistoryRoutingModule {}

View File

@ -7,13 +7,7 @@ import { HistoryRoutingModule } from './history-routing.module';
import { TaskQueryComponent } from './task-query/task-query.component';
@NgModule({
imports: [
CommonModule,
HistoryRoutingModule,
SharedModule,
FormsModule,
ReactiveFormsModule
],
imports: [CommonModule, HistoryRoutingModule, SharedModule, FormsModule, ReactiveFormsModule],
declarations: [TaskQueryComponent]
})
export class HistoryModule { }
export class HistoryModule {}

View File

@ -14,7 +14,7 @@ import { environment } from 'environments/environment';
export class TaskQueryService {
private url = environment.taskanaRestUrl;
constructor(private httpClient: HttpClient) { }
constructor(private httpClient: HttpClient) {}
queryTask(
orderBy: string = 'created',
@ -22,31 +22,33 @@ export class TaskQueryService {
searchForValues: TaskHistoryEventData,
allPages: boolean = false
): Observable<TaskHistoryEventResourceData> {
return this.httpClient.get<TaskHistoryEventResourceData>(`${this.url}/v1/task-history-event${this.getQueryParameters(
orderBy,
sortDirection,
searchForValues.taskId,
searchForValues.parentBusinessProcessId,
searchForValues.businessProcessId,
searchForValues.eventType,
searchForValues.userId,
searchForValues.domain,
searchForValues.workbasketKey,
searchForValues.porCompany,
searchForValues.porSystem,
searchForValues.porInstance,
searchForValues.porType,
searchForValues.porValue,
searchForValues.taskClassificationKey,
searchForValues.taskClassificationCategory,
searchForValues.attachmentClassificationKey,
searchForValues.custom1,
searchForValues.custom2,
searchForValues.custom3,
searchForValues.custom4,
searchForValues.created,
allPages
)}`);
return this.httpClient.get<TaskHistoryEventResourceData>(
`${this.url}/v1/task-history-event${this.getQueryParameters(
orderBy,
sortDirection,
searchForValues.taskId,
searchForValues.parentBusinessProcessId,
searchForValues.businessProcessId,
searchForValues.eventType,
searchForValues.userId,
searchForValues.domain,
searchForValues.workbasketKey,
searchForValues.porCompany,
searchForValues.porSystem,
searchForValues.porInstance,
searchForValues.porType,
searchForValues.porValue,
searchForValues.taskClassificationKey,
searchForValues.taskClassificationCategory,
searchForValues.attachmentClassificationKey,
searchForValues.custom1,
searchForValues.custom2,
searchForValues.custom3,
searchForValues.custom4,
searchForValues.created,
allPages
)}`
);
}
private getQueryParameters(
@ -98,7 +100,10 @@ export class TaskQueryService {
parameters.CUSTOM_4_LIKE = custom4;
parameters.CREATED = created;
if (allPages) { delete TaskanaQueryParameters.page; delete TaskanaQueryParameters.pageSize; }
if (allPages) {
delete TaskanaQueryParameters.page;
delete TaskanaQueryParameters.pageSize;
}
return TaskanaQueryParameters.getQueryParameters(parameters);
}

View File

@ -1,41 +1,41 @@
@import '../../../theme/colors';
.table {
max-height: calc(100vh - 152px);
padding-right: 15px;
max-height: calc(100vh - 152px);
padding-right: 15px;
}
.table-header {
background-color: $light-grey;
cursor: pointer;
background-color: $light-grey;
cursor: pointer;
}
.table-footer {
display: block;
display: block;
}
.table-cell {
height: 35px;
max-height: 35px;
min-height: 35px;
height: 35px;
max-height: 35px;
min-height: 35px;
}
.divDate {
padding-right: 20px;
padding-right: 20px;
}
.btn-xs{
padding: 0px;
.btn-xs {
padding: 0px;
}
.table .btn {
border: none;
border: none;
}
.icon-space {
margin-right: 20px;
margin-right: 20px;
}
.divTablePagination {
margin: 0 auto;
margin: 0 auto;
}

View File

@ -9,8 +9,7 @@ describe('TaskQueryComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TaskQueryComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -31,8 +31,7 @@ export class TaskQueryComponent implements OnInit {
private orientationService: OrientationService,
private requestInProgressService: RequestInProgressService,
private errorsService: NotificationService
) {
}
) {}
ngOnInit() {
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
@ -99,12 +98,18 @@ export class TaskQueryComponent implements OnInit {
}
isDate(fieldName: string): boolean {
return (fieldName === 'created');
return fieldName === 'created';
}
filterFieldsToAllowQuerying(fieldName: string): boolean {
if (!fieldName || fieldName === 'oldData' || fieldName === 'newData' || fieldName === 'comment'
|| fieldName === 'oldValue' || fieldName === 'newValue') {
if (
!fieldName ||
fieldName === 'oldData' ||
fieldName === 'newData' ||
fieldName === 'comment' ||
fieldName === 'oldValue' ||
fieldName === 'newValue'
) {
return false;
}
@ -119,9 +124,17 @@ export class TaskQueryComponent implements OnInit {
}
filterExpandGroup(fieldName: string): boolean {
if (fieldName === 'custom1' || fieldName === 'custom2' || fieldName === 'custom3' || fieldName === 'custom4'
|| fieldName === 'oldData' || fieldName === 'newData' || fieldName === 'comment'
|| fieldName === 'oldValue' || fieldName === 'newValue') {
if (
fieldName === 'custom1' ||
fieldName === 'custom2' ||
fieldName === 'custom3' ||
fieldName === 'custom4' ||
fieldName === 'oldData' ||
fieldName === 'newData' ||
fieldName === 'comment' ||
fieldName === 'oldValue' ||
fieldName === 'newValue'
) {
return true;
}
return false;
@ -179,21 +192,23 @@ export class TaskQueryComponent implements OnInit {
private performRequest() {
this.requestInProgressService.setRequestInProgress(true);
this.calculateQueryPages();
this.taskQuerySubscription = this.taskQueryService.queryTask(
this.orderBy.sortBy.replace(/([A-Z])|([0-9])/g, g => `-${g[0].toLowerCase()}`),
this.orderBy.sortDirection,
new TaskHistoryEventData(this.taskQueryForm.value),
false
).subscribe(taskQueryResource => {
this.requestInProgressService.setRequestInProgress(false);
this.taskQueryResource = taskQueryResource.taskHistoryEvents ? taskQueryResource : null;
this.taskQuery = taskQueryResource.taskHistoryEvents ? taskQueryResource.taskHistoryEvents : null;
});
this.taskQuerySubscription = this.taskQueryService
.queryTask(
this.orderBy.sortBy.replace(/([A-Z])|([0-9])/g, (g) => `-${g[0].toLowerCase()}`),
this.orderBy.sortDirection,
new TaskHistoryEventData(this.taskQueryForm.value),
false
)
.subscribe((taskQueryResource) => {
this.requestInProgressService.setRequestInProgress(false);
this.taskQueryResource = taskQueryResource.taskHistoryEvents ? taskQueryResource : null;
this.taskQuery = taskQueryResource.taskHistoryEvents ? taskQueryResource.taskHistoryEvents : null;
});
}
private initTaskQueryForm() {
const me = this;
Object.keys(new TaskHistoryEventData()).forEach(key => {
Object.keys(new TaskHistoryEventData()).forEach((key) => {
me.taskQueryForm.addControl(key, new FormControl());
});
}
@ -202,7 +217,7 @@ export class TaskQueryComponent implements OnInit {
const rowHeight = 34;
const unusedHeight = 300;
const totalHeight = window.innerHeight;
const cards = Math.round((totalHeight - (unusedHeight)) / rowHeight);
const cards = Math.round((totalHeight - unusedHeight) / rowHeight);
TaskanaQueryParameters.page = TaskanaQueryParameters.page ? TaskanaQueryParameters.page : 1;
TaskanaQueryParameters.pageSize = cards > 0 ? cards : 1;
}

View File

@ -9,8 +9,7 @@ describe('ClassificationReportComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ClassificationReportComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -26,8 +26,7 @@ export class ClassificationReportComponent implements OnInit {
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService
) {
}
) {}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);

View File

@ -8,11 +8,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
export class MonitorComponent implements OnInit, OnDestroy {
tabSelected = 'tasks';
ngOnInit(): void {
}
ngOnInit(): void {}
ngOnDestroy(): void {
}
ngOnDestroy(): void {}
selectTab(tab) {
this.tabSelected = tab;

View File

@ -1,20 +1,20 @@
.report {
margin-top: 20px;
margin-top: 20px;
}
.table-cell--clickable {
cursor: pointer;
cursor: pointer;
}
.table-row--highlight,
.table-row--hover:hover {
background-color: #f9f9f9;
background-color: #f9f9f9;
}
.table-row--highlight > .table-cell {
border-bottom: 2px solid #ddd;
border-bottom: 2px solid #ddd;
}
.table-row--white {
background-color: white !important;
}
background-color: white !important;
}

View File

@ -12,24 +12,25 @@ export class ReportTableComponent implements OnInit {
@Input()
reportData: ReportData;
ngOnInit(): void {
}
ngOnInit(): void {}
toggleFold(indexNumber: number, sumRow: boolean = false) {
let rows = sumRow ? this.reportData.sumRow : this.reportData.rows;
let index = indexNumber;
const toggleRow = rows[index += 1];
const toggleRow = rows[(index += 1)];
if (toggleRow.depth < this.reportData.meta.rowDesc.length - 1) {
const firstChildRow = rows[index += 1];
const firstChildRow = rows[(index += 1)];
firstChildRow.display = !firstChildRow.display;
const endIndex = rows.findIndex(row => row.depth <= toggleRow.depth);
const endIndex = rows.findIndex((row) => row.depth <= toggleRow.depth);
rows = endIndex >= 0 ? rows.slice(0, endIndex) : rows;
rows.forEach(row => { row.display = firstChildRow.display && row.depth === firstChildRow.depth; });
rows.forEach((row) => {
row.display = firstChildRow.display && row.depth === firstChildRow.depth;
});
this.currentExpHeaders = Math.max(
...this.reportData.rows.filter(r => r.display).map(r => r.depth),
...this.reportData.sumRow.filter(r => r.display).map(r => r.depth)
...this.reportData.rows.filter((r) => r.display).map((r) => r.depth),
...this.reportData.sumRow.filter((r) => r.display).map((r) => r.depth)
);
}
}

View File

@ -9,8 +9,7 @@ describe('TaskReportComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TaskReportComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -6,7 +6,7 @@ import { RequestInProgressService } from '../../../shared/services/request-in-pr
@Component({
selector: 'taskana-monitor-task-report',
templateUrl: './task-report.component.html',
styleUrls: ['./task-report.component.scss'],
styleUrls: ['./task-report.component.scss']
})
export class TaskReportComponent implements OnInit {
pieChartLabels: string[];
@ -17,14 +17,13 @@ export class TaskReportComponent implements OnInit {
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService
) {
}
) {}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);
this.reportData = await this.restConnectorService.getTaskStatusReport().toPromise();
this.pieChartLabels = this.reportData.meta.header;
this.reportData.sumRow[0].cells.forEach(c => {
this.reportData.sumRow[0].cells.forEach((c) => {
this.pieChartData.push(c);
});
this.requestInProgressService.setRequestInProgress(false);

View File

@ -10,8 +10,7 @@ import { RestConnectorService } from '../../services/rest-connector.service';
export class TimestampReportComponent implements OnInit {
reportData: ReportData;
constructor(private restConnectorService: RestConnectorService) {
}
constructor(private restConnectorService: RestConnectorService) {}
ngOnInit() {
this.restConnectorService.getDailyEntryExitReport().subscribe((data: ReportData) => {

View File

@ -9,8 +9,7 @@ describe('WorkbasketReportDueDateComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [WorkbasketReportDueDateComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -30,8 +30,7 @@ export class WorkbasketReportDueDateComponent implements OnInit {
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService
) {
}
) {}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);

View File

@ -9,8 +9,7 @@ describe('WorkbasketReportPlannedDateComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [WorkbasketReportPlannedDateComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -23,7 +23,7 @@ export class WorkbasketReportPlannedDateComponent implements OnInit {
lineChartData: Array<ChartData>;
lineChartOptions: any = {
responsive: true,
scales: { xAxes: [{}], yAxes: [{}] },
scales: { xAxes: [{}], yAxes: [{}] }
};
lineChartColors = ChartColorsDefinition.getColors();
@ -31,8 +31,7 @@ export class WorkbasketReportPlannedDateComponent implements OnInit {
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService
) {
}
) {}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);

View File

@ -9,8 +9,7 @@ describe('WorkbasketReportQuerySwitcherComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [WorkbasketReportQuerySwitcherComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -9,8 +9,7 @@ describe('WorkbasketReportComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [WorkbasketReportComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -12,8 +12,7 @@ export class WorkbasketReportComponent implements OnInit {
showMonitorQueryPlannedDate: Boolean;
showMonitorQueryDueDate: Boolean;
ngOnInit() {
}
ngOnInit() {}
getMetaInformation(metaInformation: MetaInfoData) {
this.metaInformation = metaInformation;

View File

@ -1,7 +1,8 @@
export class ChartColorsDefinition {
public static getColors(): Array<any> {
return [
{ // red
{
// red
backgroundColor: 'rgba(215, 40, 40, 0.2)',
borderColor: 'rgba(215, 40, 40, 1)',
pointBackgroundColor: 'rgba(215, 40, 40, 1)',
@ -9,7 +10,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(215, 40, 40, 0.8)'
},
{ // yellow
{
// yellow
backgroundColor: 'rgba(208, 205, 44, 0.2)',
borderColor: 'rgba(208, 205, 44, 1)',
pointBackgroundColor: 'rgba(208, 205, 44, 1)',
@ -17,7 +19,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(208, 205, 44, 0.8)'
},
{ // green
{
// green
backgroundColor: 'rgba(45, 205, 44, 0.2)',
borderColor: 'rgba(45, 205, 44, 1)',
pointBackgroundColor: 'rgba(45, 205, 44, 1)',
@ -25,7 +28,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(45, 205, 44, 0.8)'
},
{ // blue
{
// blue
backgroundColor: 'rgba(45, 45, 201, 0.2)',
borderColor: 'rgba(45, 45, 201, 1)',
pointBackgroundColor: 'rgba(45, 45, 201, 1)',
@ -33,7 +37,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(45, 45, 201, 0.8)'
},
{ // cyan
{
// cyan
backgroundColor: 'rgba(45, 190, 201, 0.2)',
borderColor: 'rgba(45, 190, 201, 1)',
pointBackgroundColor: 'rgba(45, 190, 201, 1)',
@ -41,7 +46,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(45, 190, 201, 0.8)'
},
{ // black-grey
{
// black-grey
backgroundColor: 'rgba(37, 35, 38, 0.2)',
borderColor: 'rgba(37, 35, 38, 1)',
pointBackgroundColor: 'rgba(37, 35, 38, 1)',
@ -49,7 +55,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(37, 35, 38, 0.8)'
},
{ // orange
{
// orange
backgroundColor: 'rgba(221, 125, 0, 0.2)',
borderColor: 'rgba(221, 125, 0, 1)',
pointBackgroundColor: 'rgba(221, 125, 0, 1)',
@ -57,7 +64,8 @@ export class ChartColorsDefinition {
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(221, 125, 0, 0.8)'
},
{ // lile
{
// lile
backgroundColor: 'rgba(180, 137, 255, 0.2)',
borderColor: 'rgba(180, 137, 255, 1)',
pointBackgroundColor: 'rgba(180, 137, 255, 1)',

View File

@ -43,7 +43,7 @@ const DECLARATIONS = [
WorkbasketReportDueDateComponent,
WorkbasketReportQuerySwitcherComponent,
TaskReportComponent,
ClassificationReportComponent,
ClassificationReportComponent
];
@NgModule({
@ -51,5 +51,4 @@ const DECLARATIONS = [
imports: MODULES,
providers: [RestConnectorService, MapToIterable]
})
export class MonitorModule {
}
export class MonitorModule {}

View File

@ -9,36 +9,36 @@ const monitorUrl = '/v1/monitor/';
@Injectable()
export class RestConnectorService {
constructor(private httpClient: HttpClient) {
}
constructor(private httpClient: HttpClient) {}
getTaskStatusReport(): Observable<ReportData> {
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl + monitorUrl
}tasks-status-report?states=READY,CLAIMED,COMPLETED`);
return this.httpClient.get<ReportData>(
`${environment.taskanaRestUrl + monitorUrl}tasks-status-report?states=READY,CLAIMED,COMPLETED`
);
}
getWorkbasketStatisticsQueryingByDueDate(): Observable<ReportData> {
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl
+ monitorUrl}tasks-workbasket-report?states=READY,CLAIMED,COMPLETED`);
return this.httpClient.get<ReportData>(
`${environment.taskanaRestUrl + monitorUrl}tasks-workbasket-report?states=READY,CLAIMED,COMPLETED`
);
}
getWorkbasketStatisticsQueryingByPlannedDate(): Observable<ReportData> {
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl
}/v1/monitor/tasks-workbasket-planned-date-report?daysInPast=7&states=READY,CLAIMED,COMPLETED`);
return this.httpClient.get<ReportData>(
`${environment.taskanaRestUrl}/v1/monitor/tasks-workbasket-planned-date-report?daysInPast=7&states=READY,CLAIMED,COMPLETED`
);
}
getClassificationTasksReport(): Observable<ReportData> {
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl
+ monitorUrl}tasks-classification-report`);
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl + monitorUrl}tasks-classification-report`);
}
getDailyEntryExitReport(): Observable<ReportData> {
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl
+ monitorUrl}timestamp-report`);
return this.httpClient.get<ReportData>(`${environment.taskanaRestUrl + monitorUrl}timestamp-report`);
}
getChartData(source: ReportData): Array<ChartData> {
return source.rows.map(row => {
return source.rows.map((row) => {
const rowData = new ChartData();
[rowData.label] = row.desc;
rowData.data = row.cells;

View File

@ -1,6 +1,12 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BsDatepickerModule, BsDatepickerConfig, ComponentLoaderFactory, PositioningService, BsLocaleService } from 'ngx-bootstrap';
import {
BsDatepickerModule,
BsDatepickerConfig,
ComponentLoaderFactory,
PositioningService,
BsLocaleService
} from 'ngx-bootstrap';
import { DatePickerComponent } from './date-picker.component';
describe('DatePickerComponent', () => {
@ -11,10 +17,8 @@ describe('DatePickerComponent', () => {
TestBed.configureTestingModule({
imports: [BsDatepickerModule],
declarations: [DatePickerComponent],
providers: [BsDatepickerConfig, ComponentLoaderFactory, PositioningService,
BsLocaleService]
})
.compileComponents();
providers: [BsDatepickerConfig, ComponentLoaderFactory, PositioningService, BsLocaleService]
}).compileComponents();
}));
beforeEach(() => {

View File

@ -9,8 +9,7 @@ describe('DropdownComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DropdownComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -10,8 +10,7 @@ export class DropdownComponent implements OnInit {
@Input() list: any[];
@Output() performClassification = new EventEmitter<any>();
ngOnInit(): void {
}
ngOnInit(): void {}
selectItem(item: any) {
this.itemSelected = item;

View File

@ -9,8 +9,7 @@ describe('FieldErrorDisplayComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FieldErrorDisplayComponent]
})
.compileComponents();
}).compileComponents();
}));
beforeEach(() => {

View File

@ -17,6 +17,5 @@ export class FieldErrorDisplayComponent implements OnInit {
@Input()
validationTrigger: boolean;
ngOnInit() {
}
ngOnInit() {}
}

View File

@ -1,19 +1,19 @@
.dropdown-menu-users {
&>li{
margin-bottom: 5px;
}
margin-left: 15px;
& > li {
margin-bottom: 5px;
}
margin-left: 15px;
}
.list-group-search {
padding: 0px 15px;
border-top: 1px solid #ddd;
padding: 0px 15px;
border-top: 1px solid #ddd;
}
.list-group-search {
border-top: none;
border-top: none;
}
row.padding {
padding: 1px 0px;
padding: 1px 0px;
}
.filter-list {

View File

@ -13,14 +13,14 @@ describe('FilterComponent', () => {
let fixture: ComponentFixture<FilterComponent>;
let debugElement: any;
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
providers: [MatSnackBar, Overlay],
imports: [AngularSvgIconModule, FormsModule, HttpClientModule]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(FilterComponent);
component = fixture.componentInstance;
component.filterParams = {

View File

@ -9,11 +9,20 @@ import { TaskanaType } from 'app/shared/models/taskana-type';
styleUrls: ['./filter.component.scss']
})
export class FilterComponent implements OnInit {
@Input() allTypes: Map<string, string> = new Map([['ALL', 'All'], ['PERSONAL', 'Personal'], ['GROUP', 'Group'],
['CLEARANCE', 'Clearance'], ['TOPIC', 'Topic']]);
@Input() allTypes: Map<string, string> = new Map([
['ALL', 'All'],
['PERSONAL', 'Personal'],
['GROUP', 'Group'],
['CLEARANCE', 'Clearance'],
['TOPIC', 'Topic']
]);
@Input() allStates: Map<string, string> = new Map([['ALL', 'All'], ['READY', 'Ready'], ['CLAIMED', 'Claimed'],
['COMPLETED', 'Completed']]);
@Input() allStates: Map<string, string> = new Map([
['ALL', 'All'],
['READY', 'Ready'],
['CLAIMED', 'Claimed'],
['COMPLETED', 'Completed']
]);
@Input() filterParams = { name: '', key: '', type: '', description: '', owner: '' };
@ -35,15 +44,17 @@ export class FilterComponent implements OnInit {
}
selectType(type: ICONTYPES) {
this.filter.filterParams.type = (type === ICONTYPES.ALL) ? '' : type;
this.filter.filterParams.type = type === ICONTYPES.ALL ? '' : type;
}
selectState(state: ICONTYPES) {
this.filter.filterParams.state = (state === 'ALL') ? '' : state;
this.filter.filterParams.state = state === 'ALL' ? '' : state;
}
clear() {
Object.keys(this.filterParams).forEach(key => { this.filterParams[key] = ''; });
Object.keys(this.filterParams).forEach((key) => {
this.filterParams[key] = '';
});
this.initializeFilterModel();
}
@ -68,6 +79,6 @@ export class FilterComponent implements OnInit {
* @returns {string[]}
*/
getUnusedKeys(): string[] {
return Object.keys(this.filterParamKeys).filter(key => ['name', 'key', 'type'].indexOf(key) < 0);
return Object.keys(this.filterParamKeys).filter((key) => ['name', 'key', 'type'].indexOf(key) < 0);
}
}

View File

@ -1,4 +1,3 @@
.center-block.no-detail {
text-align: center;
text-align: center;
}

View File

@ -12,17 +12,13 @@ import { MasterAndDetailComponent } from './master-and-detail.component';
selector: 'taskana-dummy-master',
template: 'dummymaster'
})
export class DummyMasterComponent {
}
export class DummyMasterComponent {}
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
export class DummyDetailComponent {}
describe('MasterAndDetailComponent ', () => {
let component;
@ -51,18 +47,10 @@ describe('MasterAndDetailComponent ', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
MasterAndDetailComponent,
DummyMasterComponent,
DummyDetailComponent],
imports: [
RouterTestingModule.withRoutes(routes),
AngularSvgIconModule,
HttpClientModule
],
declarations: [MasterAndDetailComponent, DummyMasterComponent, DummyDetailComponent],
imports: [RouterTestingModule.withRoutes(routes), AngularSvgIconModule, HttpClientModule],
providers: [MasterAndDetailService]
})
.compileComponents();
}).compileComponents();
fixture = TestBed.createComponent(MasterAndDetailComponent);
component = fixture.debugElement.componentInstance;

View File

@ -5,8 +5,7 @@ import { MasterAndDetailService } from 'app/shared/services/master-and-detail/ma
@Component({
selector: 'taskana-shared-master-and-detail',
templateUrl: './master-and-detail.component.html',
styleUrls: ['./master-and-detail.component.scss'],
styleUrls: ['./master-and-detail.component.scss']
})
export class MasterAndDetailComponent implements OnInit {
private classifications = 'classifications';
@ -16,13 +15,16 @@ export class MasterAndDetailComponent implements OnInit {
showDetail = false;
currentRoute = '';
constructor(private route: ActivatedRoute, private router: Router, private masterAndDetailService: MasterAndDetailService) {
}
constructor(
private route: ActivatedRoute,
private router: Router,
private masterAndDetailService: MasterAndDetailService
) {}
ngOnInit(): void {
this.showDetail = this.showDetails();
this.masterAndDetailService.setShowDetail(this.showDetail);
this.router.events.subscribe(event => {
this.router.events.subscribe((event) => {
if (event instanceof NavigationStart) {
this.showDetail = this.showDetails(event);
this.masterAndDetailService.setShowDetail(this.showDetail);
@ -43,7 +45,7 @@ export class MasterAndDetailComponent implements OnInit {
private checkUrl(url: string): boolean {
this.checkRoute(url);
return this.detailRoutes.some(routeDetail => url.indexOf(routeDetail) !== -1);
return this.detailRoutes.some((routeDetail) => url.indexOf(routeDetail) !== -1);
}
private checkRoute(url: string) {

View File

@ -1,144 +1,144 @@
@import '../../../../theme/variables';
.navbar.main:before {
@include degraded-bar(right, 100%, 3px)
.navbar.main:before {
@include degraded-bar(right, 100%, 3px);
}
.navbar-inverse {
border:none;
background-color: $dark-green;
box-shadow: 0px 1px 5px -1px black;
border: none;
background-color: $dark-green;
box-shadow: 0px 1px 5px -1px black;
}
.navbar-toggle{
margin: 3px 0px;
.navbar-toggle {
margin: 3px 0px;
font-size: 20px;
&.logout {
font-size: 20px;
&.logout{
font-size: 20px;
}
}
}
button.navbar-toggle:hover > span{
color:$aquamarine;
button.navbar-toggle:hover > span {
color: $aquamarine;
}
ul.nav > p {
white-space: nowrap;
text-overflow: ellipsis;
padding-right: 0px;
white-space: nowrap;
text-overflow: ellipsis;
padding-right: 0px;
}
.navbar-inverse .navbar-toggle, .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus {
border: none;
background-color: transparent;
.navbar-inverse .navbar-toggle,
.navbar-toggle:hover,
.navbar-inverse .navbar-toggle:focus {
border: none;
background-color: transparent;
}
svg-icon.logo {
float: left;
width: 150px;
height: 50px;
padding: 5px;
position: initial;
float: left;
width: 150px;
height: 50px;
padding: 5px;
position: initial;
}
h2.navbar-brand{
vertical-align: middle;
text-decoration: none;
color: white;
padding: 15px 0px 0px 0px;
font-size: 20px;
h2.navbar-brand {
vertical-align: middle;
text-decoration: none;
color: white;
padding: 15px 0px 0px 0px;
font-size: 20px;
}
.domain-form {
margin: 13px;
color: white;
font-size: 18px;
> div{
cursor: pointer;
> button {
color: white;
background-color: $dark-green;
border: none;
font-size: 16px;
border-bottom: 1px solid $dark-green;
margin-left:5px;
}
margin: 13px;
color: white;
font-size: 18px;
> div {
cursor: pointer;
> button {
color: white;
background-color: $dark-green;
border: none;
font-size: 16px;
border-bottom: 1px solid $dark-green;
margin-left: 5px;
}
}
}
.nav-content{
margin-top: 5px;
.nav-content {
margin-top: 5px;
}
.nav-version {
color: $grey;
position: absolute;
bottom: 5px;
font-size: 12px;
color: $grey;
position: absolute;
bottom: 5px;
font-size: 12px;
}
/*
* All side bar links styling.
*/
.nav-sidebar > li > a {
padding-right: 20px;
padding-left: 20px;
padding-right: 20px;
padding-left: 20px;
}
.menu > span:hover,
.submenu > span:hover
{
color: white;
cursor: pointer;
.submenu > span:hover {
color: white;
cursor: pointer;
}
.sidenav {
position:fixed;
z-index:999;
box-shadow: none;
height: 100%;
background-color: $dark-green;
box-shadow: 3px 0px 10px -1px $dark-green;
position: fixed;
z-index: 999;
box-shadow: none;
height: 100%;
background-color: $dark-green;
box-shadow: 3px 0px 10px -1px $dark-green;
}
.navbar {
height: 52px;
margin-bottom: 0px;
height: 52px;
margin-bottom: 0px;
}
.menu,.submenu > span {
margin-top: 15px;
font-size: 20px;
width: 100%;
font-family: inherit;
font-weight: 500;
line-height: 1.1;
.menu,
.submenu > span {
margin-top: 15px;
font-size: 20px;
width: 100%;
font-family: inherit;
font-weight: 500;
line-height: 1.1;
}
.menu,.submenu > span {
padding-left: 12px;
color: $grey;
outline: none;
.menu,
.submenu > span {
padding-left: 12px;
color: $grey;
outline: none;
}
.menu.selected,.submenu.selected {
background-color: transparent;
&> span{
padding-left: 10px;
border-left: $pallete-green 5px solid;
color: white;
}
.menu.selected,
.submenu.selected {
background-color: transparent;
& > span {
padding-left: 10px;
border-left: $pallete-green 5px solid;
color: white;
}
}
.menu > .submenu{
margin-left: 30px;
.menu > .submenu {
margin-left: 30px;
}
a {
color: $grey;
&:hover{
color:white;
}
text-decoration: none
color: $grey;
&:hover {
color: white;
}
text-decoration: none;
}

View File

@ -24,20 +24,13 @@ describe('NavBarComponent', () => {
let debugElement;
let navBar;
const routes: Routes = [
{ path: 'classifications', component: NavBarComponent }
];
const routes: Routes = [{ path: 'classifications', component: NavBarComponent }];
beforeEach(done => {
beforeEach((done) => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [NavBarComponent, UserInformationComponent],
imports: [
AngularSvgIconModule,
HttpClientModule,
RouterTestingModule.withRoutes(routes),
SharedModule
],
imports: [AngularSvgIconModule, HttpClientModule, RouterTestingModule.withRoutes(routes), SharedModule],
providers: [
SelectedRouteService,
BusinessAdminGuard,
@ -45,10 +38,11 @@ describe('NavBarComponent', () => {
WindowRefService,
RequestInProgressService,
MatSnackBar,
Overlay]
Overlay
]
});
};
configureTests(configure).then(testBed => {
configureTests(configure).then((testBed) => {
fixture = TestBed.createComponent(NavBarComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement.nativeElement;
@ -67,7 +61,7 @@ describe('NavBarComponent', () => {
expect(component).toBeTruthy();
});
it('should have as title \'\'', (() => {
it("should have as title ''", () => {
expect(navBar.title).toEqual('');
}));
});
});

Some files were not shown because too many files have changed in this diff Show More