TSK-998: Removing linting errors with minor code changes
Mainly renaming and restructuring to forEach-loops.
This commit is contained in:
parent
96f0b8bb15
commit
b4ddeb4055
|
@ -17,13 +17,16 @@ module.exports = {
|
|||
"rules": {
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
"@typescript-eslint/indent": ['error', 2],
|
||||
"max-len": ["off", { "code": 140, "ignorePattern": "import *" }], // smaller than 140?
|
||||
"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": ["off", { "allow": ["_links", "__karma__"] }],
|
||||
"no-param-reassign": ["off", { "props": false }],
|
||||
"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",
|
||||
|
@ -35,10 +38,5 @@ module.exports = {
|
|||
|
||||
// all following rules MUST be removed (mostly autofix)
|
||||
"linebreak-style": ["off", "unix"], // own PR
|
||||
"no-restricted-syntax": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
}
|
||||
};
|
||||
|
|
|
@ -55,9 +55,9 @@ export class AccessItemsManagementComponent implements OnInit, OnDestroy {
|
|||
const AccessItemsFormGroups = accessItems.map(accessItem => this.formBuilder.group(accessItem));
|
||||
AccessItemsFormGroups.forEach(accessItemGroup => {
|
||||
accessItemGroup.controls.accessId.setValidators(Validators.required);
|
||||
for (const key of Object.keys(accessItemGroup.controls)) {
|
||||
Object.keys(accessItemGroup.controls).forEach(key => {
|
||||
accessItemGroup.controls[key].disable();
|
||||
}
|
||||
});
|
||||
});
|
||||
const AccessItemsFormArray = this.formBuilder.array(AccessItemsFormGroups);
|
||||
if (!this.AccessItemsForm) { this.AccessItemsForm = this.formBuilder.group({}); }
|
||||
|
|
|
@ -59,9 +59,8 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
|
|||
this.performRequest();
|
||||
});
|
||||
|
||||
this.categoriesSubscription = this.categoryService.getCategories(this.classificationTypeSelected).subscribe((categories: Array<string>) => {
|
||||
this.categories = categories;
|
||||
});
|
||||
this.categoriesSubscription = this.categoryService.getCategories(this.classificationTypeSelected)
|
||||
.subscribe((categories: Array<string>) => { this.categories = categories; });
|
||||
this.importingExportingSubscription = this.importExportService.getImportingFinished().subscribe((value: Boolean) => {
|
||||
this.performRequest(true);
|
||||
});
|
||||
|
|
|
@ -12,8 +12,8 @@ export class ClassificationDefinitionService {
|
|||
|
||||
// GET
|
||||
async exportClassifications(domain: string) {
|
||||
domain = (domain === '' ? '' : `?domain=${domain}`);
|
||||
const classificationDefinitions = await this.httpClient.get<ClassificationDefinition[]>(this.url + domain).toPromise();
|
||||
const domainRequest = (domain ? '' : `?domain=${domain}`);
|
||||
const classificationDefinitions = await this.httpClient.get<ClassificationDefinition[]>(this.url + domainRequest).toPromise();
|
||||
BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ export class WorkbasketDefinitionService {
|
|||
|
||||
// GET
|
||||
async exportWorkbaskets(domain: string) {
|
||||
domain = (domain === '' ? domain : `?domain=${domain}`);
|
||||
const workbasketDefinitions = await this.httpClient.get<WorkbasketDefinition[]>(this.url + domain).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`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,11 +164,11 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
checkAll(row: number, value: any) {
|
||||
const checkAll = value.target.checked;
|
||||
const workbasketAccessItemsObj = new WorkbasketAccessItems();
|
||||
for (const property in workbasketAccessItemsObj) {
|
||||
Object.keys(workbasketAccessItemsObj).forEach(property => {
|
||||
if (property !== 'accessId' && property !== '_links' && property !== 'workbasketId' && property !== 'accessItemId') {
|
||||
this.accessItemsGroups.controls[row].get(property).setValue(checkAll);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
accessItemSelected(accessItem: AccessIdDefinition, row: number) {
|
||||
|
|
|
@ -104,8 +104,8 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
}
|
||||
} else {
|
||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight);
|
||||
this.distributionTargetsSelected = this.removeSeletedItems(this.distributionTargetsSelected, itemsSelected);
|
||||
this.distributionTargetsRight = this.removeSeletedItems(this.distributionTargetsRight, itemsSelected);
|
||||
this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected);
|
||||
this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected);
|
||||
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
|
||||
}
|
||||
|
||||
|
@ -143,17 +143,16 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
performFilter(dualListFilter: any) {
|
||||
dualListFilter.side === Side.RIGHT ? delete this.distributionTargetsRight : delete this.distributionTargetsLeft;
|
||||
this.fillDistributionTargets(dualListFilter.side, undefined);
|
||||
this.onRequest(false, dualListFilter.side);
|
||||
this.workbasketFilterSubscription = this.workbasketService.getWorkBasketsSummary(true, '', '', '',
|
||||
dualListFilter.filterBy.filterParams.name, dualListFilter.filterBy.filterParams.description, '',
|
||||
dualListFilter.filterBy.filterParams.owner, dualListFilter.filterBy.filterParams.type, '',
|
||||
dualListFilter.filterBy.filterParams.key, '', true).subscribe(resultList => {
|
||||
(dualListFilter.side === Side.RIGHT)
|
||||
? this.distributionTargetsRight = (resultList.workbaskets)
|
||||
: this.distributionTargetsLeft = (resultList.workbaskets);
|
||||
this.onRequest(true, dualListFilter.side);
|
||||
});
|
||||
dualListFilter.filterBy.filterParams.key, '', true)
|
||||
.subscribe(resultList => {
|
||||
this.fillDistributionTargets(dualListFilter.side, (resultList.workbaskets));
|
||||
this.onRequest(true, dualListFilter.side);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -203,6 +202,11 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
private fillDistributionTargets(side: Side, workbaskets: WorkbasketSummary[]) {
|
||||
this.distributionTargetsLeft = side === Side.LEFT ? workbaskets : this.distributionTargetsLeft;
|
||||
this.distributionTargetsRight = side === Side.RIGHT ? workbaskets : this.distributionTargetsRight;
|
||||
}
|
||||
|
||||
private getNextPage(side: Side) {
|
||||
TaskanaQueryParameters.page += 1;
|
||||
this.getWorkbaskets(side);
|
||||
|
@ -251,7 +255,7 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
return originList.filter((item: any) => (item.selected === true));
|
||||
}
|
||||
|
||||
private removeSeletedItems(originList: any, selectedItemList) {
|
||||
private removeSelectedItems(originList: any, selectedItemList) {
|
||||
for (let index = originList.length - 1; index >= 0; index--) {
|
||||
if (selectedItemList.some(itemToRemove => (originList[index].workbasketId === itemToRemove.workbasketId))) {
|
||||
originList.splice(index, 1);
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import { Component,
|
||||
OnInit,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
@ -18,8 +12,7 @@ import { TaskanaDate } from 'app/shared/util/taskana.date';
|
|||
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { GeneralModalService } from 'app/services/general-modal/general-modal.service';
|
||||
import { SavingWorkbasketService,
|
||||
SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
|
|
|
@ -36,13 +36,13 @@ class DummyDetailComponent {
|
|||
template: 'dummydetail'
|
||||
})
|
||||
class PaginationComponent {
|
||||
@Input()
|
||||
@Input()
|
||||
page: Page;
|
||||
|
||||
@Output()
|
||||
workbasketsResourceChange = new EventEmitter<any>();
|
||||
@Output()
|
||||
workbasketsResourceChange = new EventEmitter<any>();
|
||||
|
||||
@Output() changePage = new EventEmitter<any>();
|
||||
@Output() changePage = new EventEmitter<any>();
|
||||
}
|
||||
|
||||
const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSummaryResource(
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
import { getTestBed,
|
||||
TestBed } from '@angular/core/testing';
|
||||
import { getTestBed, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service';
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-useless-constructor */
|
||||
import { WorkbasketAccessItems } from './workbasket-access-items';
|
||||
import { Workbasket } from './workbasket';
|
||||
|
||||
export class WorkbasketDefinition {
|
||||
constructor(distributionTargets: string[],
|
||||
workbasketAccessItems: WorkbasketAccessItems[],
|
||||
workbasket: Workbasket) {
|
||||
constructor(public distributionTargets: string[],
|
||||
public workbasketAccessItems: WorkbasketAccessItems[],
|
||||
public workbasket: Workbasket) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,21 +15,18 @@ export class ReportComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
toggleFold(index: number, sumRow: boolean = false) {
|
||||
const rows = sumRow ? this.reportData.sumRow : this.reportData.rows;
|
||||
const toggleRow = rows[index++];
|
||||
toggleFold(indexNumber: number, sumRow: boolean = false) {
|
||||
let rows = sumRow ? this.reportData.sumRow : this.reportData.rows;
|
||||
let index = indexNumber;
|
||||
const toggleRow = rows[index += 1];
|
||||
if (toggleRow.depth < this.reportData.meta.rowDesc.length - 1) {
|
||||
const firstChildRow = rows[index++];
|
||||
const firstChildRow = rows[index += 1];
|
||||
firstChildRow.display = !firstChildRow.display;
|
||||
|
||||
let end = false;
|
||||
for (let i = index; i < rows.length && !end; i++) {
|
||||
const row = rows[i];
|
||||
end = row.depth <= toggleRow.depth;
|
||||
if (!end) {
|
||||
row.display = firstChildRow.display && row.depth === firstChildRow.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; });
|
||||
|
||||
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)
|
||||
|
|
|
@ -65,17 +65,13 @@ export class CustomFieldsService {
|
|||
private mergeKeys(defaultObject: Object, newObject: Object) {
|
||||
const value = {};
|
||||
|
||||
for (const item of Object.keys(defaultObject)) {
|
||||
if (!value[item]) {
|
||||
value[item] = defaultObject[item];
|
||||
}
|
||||
}
|
||||
Object.keys(defaultObject).forEach(item => {
|
||||
value[item] = value[item] ? value[item] : defaultObject[item];
|
||||
});
|
||||
|
||||
for (const item of Object.keys(newObject)) {
|
||||
if (!value[item]) {
|
||||
value[item] = newObject[item];
|
||||
}
|
||||
}
|
||||
Object.keys(newObject).forEach(item => {
|
||||
value[item] = value[item] ? value[item] : newObject[item];
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -27,11 +27,6 @@ export class SelectedRouteService {
|
|||
}
|
||||
|
||||
private checkUrl(url: string): string {
|
||||
for (const routeDetail of this.detailRoutes) {
|
||||
if (url.indexOf(routeDetail) !== -1) {
|
||||
return routeDetail;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return this.detailRoutes.find(routeDetail => url.indexOf(routeDetail) !== -1) || '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ export class StartupService {
|
|||
).then(
|
||||
() => this.taskanaEngineService.getUserInformation()
|
||||
).catch(error => {
|
||||
console.log(error);
|
||||
// this.window.nativeWindow.location.href = environment.taskanaRestUrl + '/login';
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,9 +43,7 @@ export class FilterComponent implements OnInit {
|
|||
}
|
||||
|
||||
clear() {
|
||||
for (const key of Object.keys(this.filterParams)) {
|
||||
this.filterParams[key] = '';
|
||||
}
|
||||
Object.keys(this.filterParams).forEach(key => { this.filterParams[key] = ''; });
|
||||
this.initializeFilterModel();
|
||||
}
|
||||
|
||||
|
@ -70,12 +68,6 @@ export class FilterComponent implements OnInit {
|
|||
* @returns {string[]}
|
||||
*/
|
||||
getUnusedKeys(): string[] {
|
||||
const unusedKeys = [];
|
||||
for (const key of this.filterParamKeys) {
|
||||
if (['name', 'key', 'type'].indexOf(key) < 0) {
|
||||
unusedKeys.push(key);
|
||||
}
|
||||
}
|
||||
return unusedKeys;
|
||||
return Object.keys(this.filterParamKeys).filter(key => ['name', 'key', 'type'].indexOf(key) < 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,7 @@ export class MasterAndDetailComponent implements OnInit {
|
|||
|
||||
private checkUrl(url: string): boolean {
|
||||
this.checkRoute(url);
|
||||
for (const routeDetail of this.detailRoutes) {
|
||||
if (url.indexOf(routeDetail) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this.detailRoutes.some(routeDetail => url.indexOf(routeDetail) !== -1);
|
||||
}
|
||||
|
||||
private checkRoute(url: string) {
|
||||
|
|
|
@ -56,10 +56,10 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {
|
|||
}
|
||||
|
||||
increase() {
|
||||
this.value++;
|
||||
this.value += 1;
|
||||
}
|
||||
|
||||
decrease() {
|
||||
this.value--;
|
||||
this.value -= 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import { Component,
|
||||
OnInit,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
OnChanges,
|
||||
SimpleChanges } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Page } from 'app/models/page';
|
||||
|
||||
@Component({
|
||||
|
@ -40,7 +34,8 @@ export class PaginationComponent implements OnChanges {
|
|||
this.hasItems = this.numberOfItems > 0;
|
||||
}
|
||||
|
||||
changeToPage(page) {
|
||||
changeToPage(p) {
|
||||
let page = p;
|
||||
if (page < 1) {
|
||||
this.pageSelected = 1;
|
||||
page = this.pageSelected;
|
||||
|
|
|
@ -6,11 +6,9 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
export class MapToIterable implements PipeTransform {
|
||||
transform(dict: Object) {
|
||||
const result = [];
|
||||
for (const key in dict) {
|
||||
if (dict.hasOwnProperty(key)) {
|
||||
result.push({ key, val: dict[key] });
|
||||
}
|
||||
}
|
||||
Object.keys(dict).forEach(key => {
|
||||
result.push({ key, val: dict[key] });
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ export class SpreadNumberPipe implements PipeTransform {
|
|||
transform(maxPageNumber: number, currentIndex: number, maxArrayElements: number): number[] {
|
||||
const returnArray = [];
|
||||
if (maxPageNumber <= 5) {
|
||||
for (let i = 0; i < maxPageNumber; i++) {
|
||||
for (let i = 0; i < maxPageNumber; i += 1) {
|
||||
returnArray.push(i);
|
||||
}
|
||||
return returnArray;
|
||||
|
@ -25,7 +25,7 @@ export class SpreadNumberPipe implements PipeTransform {
|
|||
const minIndex = (minArrayValue - rightDifference) <= 0 ? 0 : minArrayValue - rightDifference;
|
||||
const maxIndex = (maxArrayValue + leftDifference) > maxPageNumber ? maxPageNumber : maxArrayValue + leftDifference;
|
||||
|
||||
for (let i = minIndex; i < maxIndex; i++) {
|
||||
for (let i = minIndex; i < maxIndex; i += 1) {
|
||||
returnArray.push(i);
|
||||
}
|
||||
return returnArray;
|
||||
|
|
|
@ -35,17 +35,16 @@ export class AccessIdsService {
|
|||
sortModel: SortingModel = new SortingModel('workbasket-key'),
|
||||
forceRequest: boolean = false
|
||||
): Observable<AccessItemsWorkbasketResource> {
|
||||
if (this.accessItemsRef && !forceRequest) {
|
||||
return this.accessItemsRef;
|
||||
if (forceRequest || !this.accessItemsRef) {
|
||||
this.accessItemsRef = this.httpClient.get<AccessItemsWorkbasketResource>(encodeURI(
|
||||
`${environment.taskanaRestUrl}/v1/workbasket-access-items/${TaskanaQueryParameters.getQueryParameters(
|
||||
this.accessIdsParameters(sortModel,
|
||||
accessIds,
|
||||
accessIdLike,
|
||||
workbasketKeyLike)
|
||||
)}`
|
||||
));
|
||||
}
|
||||
|
||||
this.accessItemsRef = this.httpClient.get<AccessItemsWorkbasketResource>(encodeURI(
|
||||
`${environment.taskanaRestUrl}/v1/workbasket-access-items/${TaskanaQueryParameters.getQueryParameters(
|
||||
this.accessIdsParameters(sortModel,
|
||||
accessIds,
|
||||
accessIdLike, workbasketKeyLike)
|
||||
)}`
|
||||
));
|
||||
return this.accessItemsRef;
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,9 @@ export class ClassificationsService {
|
|||
classificationTypes]
|
||||
).pipe(
|
||||
map(
|
||||
(classification: any[]) => (classification[0].classifications ? this.buildHierarchy(classification[0].classifications, classification[1]) : [])
|
||||
(classification: any[]) => (
|
||||
classification[0].classifications ? this.buildHierarchy(classification[0].classifications, classification[1]) : []
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -123,27 +125,21 @@ export class ClassificationsService {
|
|||
const roots = [];
|
||||
const children = [];
|
||||
|
||||
for (let index = 0, len = classifications.length; index < len; ++index) {
|
||||
const item = classifications[index];
|
||||
classifications.forEach(item => {
|
||||
if (item.type === type) {
|
||||
const parent = item.parentId;
|
||||
const target = !parent ? roots : (children[parent] || (children[parent] = []));
|
||||
|
||||
target.push(item);
|
||||
}
|
||||
}
|
||||
for (let index = 0, len = roots.length; index < len; ++index) {
|
||||
this.findChildren(roots[index], children);
|
||||
}
|
||||
});
|
||||
roots.forEach(parent => this.findChildren(parent, children));
|
||||
return roots;
|
||||
}
|
||||
|
||||
private findChildren(parent: any, children: Array<any>) {
|
||||
if (children[parent.classificationId]) {
|
||||
parent.children = children[parent.classificationId];
|
||||
for (let index = 0, len = parent.children.length; index < len; ++index) {
|
||||
this.findChildren(parent.children[index], children);
|
||||
}
|
||||
parent.children.forEach(child => this.findChildren(child, children));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,13 @@ export class FormsValidatorService {
|
|||
return false;
|
||||
}
|
||||
const forFieldsPromise = new Promise((resolve, reject) => {
|
||||
for (const control in form.form.controls) {
|
||||
Object.keys(form.form.controls).forEach(control => {
|
||||
if (control.indexOf('owner') === -1 && form.form.controls[control].invalid) {
|
||||
const validationState = toogleValidationMap.get(control);
|
||||
validationState ? toogleValidationMap.set(control, !validationState) : toogleValidationMap.set(control, true);
|
||||
toogleValidationMap.set(this.workbasketOwner, !validationState);
|
||||
validSync = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
resolve(validSync);
|
||||
});
|
||||
|
||||
|
@ -36,16 +36,13 @@ export class FormsValidatorService {
|
|||
if (form.form.controls[this.workbasketOwner]) {
|
||||
this.accessIdsService.getAccessItemsInformation(form.form.controls[this.workbasketOwner].value).subscribe(items => {
|
||||
const validationState = toogleValidationMap.get(this.workbasketOwner);
|
||||
validationState ? toogleValidationMap.set(this.workbasketOwner, !validationState)
|
||||
: toogleValidationMap.set(this.workbasketOwner, true);
|
||||
items.find(item => item.accessId === form.form.controls[this.workbasketOwner].value)
|
||||
? resolve(new ResponseOwner({ valid: true, field: ownerString }))
|
||||
: resolve(new ResponseOwner({ valid: false, field: ownerString }));
|
||||
toogleValidationMap.set(this.workbasketOwner, !validationState);
|
||||
const valid = items.find(item => item.accessId === form.form.controls[this.workbasketOwner].value);
|
||||
resolve(new ResponseOwner({ valid, field: ownerString }));
|
||||
});
|
||||
} else {
|
||||
const validationState = toogleValidationMap.get(form.form.controls[this.workbasketOwner]);
|
||||
validationState ? toogleValidationMap.set(this.workbasketOwner, !validationState)
|
||||
: toogleValidationMap.set(this.workbasketOwner, true);
|
||||
toogleValidationMap.set(this.workbasketOwner, !validationState);
|
||||
resolve(new ResponseOwner({ valid: true, field: ownerString }));
|
||||
}
|
||||
});
|
||||
|
@ -68,12 +65,9 @@ export class FormsValidatorService {
|
|||
for (let i = 0; i < form.length; i++) {
|
||||
ownerPromise.push(new Promise((resolve, reject) => {
|
||||
const validationState = toogleValidationAccessIdMap.get(i);
|
||||
validationState ? toogleValidationAccessIdMap.set(i, !validationState)
|
||||
: toogleValidationAccessIdMap.set(i, true);
|
||||
toogleValidationAccessIdMap.set(i, !validationState);
|
||||
this.accessIdsService.getAccessItemsInformation(form.controls[i].value.accessId).subscribe(items => {
|
||||
items.length > 0
|
||||
? resolve(new ResponseOwner({ valid: true, field: 'access id' }))
|
||||
: resolve(new ResponseOwner({ valid: false, field: 'access id' }));
|
||||
resolve(new ResponseOwner({ valid: items.length > 0, field: 'access id' }));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
@ -81,10 +75,10 @@ export class FormsValidatorService {
|
|||
let result = true;
|
||||
const values = await Promise.all(ownerPromise);
|
||||
let responseOwner;
|
||||
for (let i_1 = 0; i_1 < values.length; i_1++) {
|
||||
responseOwner = new ResponseOwner(values[i_1]);
|
||||
values.forEach(owner => {
|
||||
responseOwner = new ResponseOwner(owner);
|
||||
result = result && responseOwner.valid;
|
||||
}
|
||||
});
|
||||
if (!result) {
|
||||
this.alertService.triggerAlert(new AlertModel(AlertType.WARNING, `The ${responseOwner.field} introduced is not valid.`));
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ export class HttpClientInterceptor implements HttpInterceptor {
|
|||
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
req = req.clone({ headers: req.headers.set('Content-Type', 'application/hal+json') });
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
let req = request.clone({ headers: request.headers.set('Content-Type', 'application/hal+json') });
|
||||
if (!environment.production) {
|
||||
req = req.clone({ headers: req.headers.set('Authorization', 'Basic YWRtaW46YWRtaW4=') });
|
||||
}
|
||||
|
|
|
@ -47,14 +47,19 @@ export class WorkbasketService {
|
|||
}
|
||||
|
||||
|
||||
return this.domainService.getSelectedDomain().pipe(mergeMap(domain => this.workbasketSummaryRef = this.httpClient.get<WorkbasketSummaryResource>(
|
||||
`${environment.taskanaRestUrl}/v1/workbaskets/${TaskanaQueryParameters
|
||||
.getQueryParameters(this.workbasketParameters(sortBy, order, name, nameLike, descLike, owner, ownerLike,
|
||||
type, key, keyLike, requiredPermission, allPages, domain))}`
|
||||
)
|
||||
.pipe(tap((workbaskets => workbaskets)))), tap(() => {
|
||||
this.domainService.domainChangedComplete();
|
||||
}));
|
||||
return this.domainService.getSelectedDomain()
|
||||
.pipe(mergeMap(domain => {
|
||||
this.workbasketSummaryRef = this.httpClient.get<WorkbasketSummaryResource>(
|
||||
`${environment.taskanaRestUrl}/v1/workbaskets/${TaskanaQueryParameters
|
||||
.getQueryParameters(this.workbasketParameters(sortBy, order, name, nameLike, descLike, owner, ownerLike,
|
||||
type, key, keyLike, requiredPermission, allPages, domain))}`
|
||||
);
|
||||
this.workbasketSummaryRef.pipe(tap((workbaskets => workbaskets)));
|
||||
return this.workbasketSummaryRef;
|
||||
}),
|
||||
tap(() => {
|
||||
this.domainService.domainChangedComplete();
|
||||
}));
|
||||
}
|
||||
|
||||
// GET
|
||||
|
@ -151,7 +156,6 @@ export class WorkbasketService {
|
|||
} else {
|
||||
errMsg = error.message ? error.message : error.toString();
|
||||
}
|
||||
console.error(errMsg);
|
||||
return observableThrowError(errMsg);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,71 +24,71 @@ export class SpinnerComponent implements OnDestroy {
|
|||
|
||||
showSpinner: boolean;
|
||||
|
||||
@Input()
|
||||
@Input()
|
||||
delay = 250;
|
||||
|
||||
@Input()
|
||||
set isRunning(value: boolean) {
|
||||
if (!value) {
|
||||
this.cancelTimeout();
|
||||
if (this.isModal) { this.closeModal(); }
|
||||
this.isDelayedRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentTimeout) {
|
||||
return;
|
||||
}
|
||||
this.runSpinner(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
isModal = false;
|
||||
|
||||
@Input()
|
||||
positionClass: string;
|
||||
|
||||
@Output()
|
||||
spinnerIsRunning = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild('spinnerModal', { static: true })
|
||||
private modal;
|
||||
|
||||
constructor(private generalModalService: GeneralModalService) {
|
||||
|
||||
}
|
||||
|
||||
private runSpinner(value) {
|
||||
this.currentTimeout = setTimeout(() => {
|
||||
if (this.isModal) { $(this.modal.nativeElement).modal('show'); }
|
||||
this.isDelayedRunning = value;
|
||||
this.cancelTimeout();
|
||||
this.requestTimeout = setTimeout(() => {
|
||||
this.generalModalService.triggerMessage(
|
||||
new MessageModal('There was an error with your request, please make sure you have internet connection',
|
||||
'Request time execeed')
|
||||
);
|
||||
this.cancelTimeout();
|
||||
this.isRunning = false;
|
||||
}, this.maxRequestTimeout);
|
||||
}, this.delay);
|
||||
}
|
||||
|
||||
private closeModal() {
|
||||
if (this.showSpinner) {
|
||||
$(this.modal.nativeElement).modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private cancelTimeout(): void {
|
||||
clearTimeout(this.currentTimeout);
|
||||
clearTimeout(this.requestTimeout);
|
||||
delete this.currentTimeout; // do we need this?
|
||||
delete this.requestTimeout;
|
||||
}
|
||||
|
||||
ngOnDestroy(): any {
|
||||
@Input()
|
||||
set isRunning(value: boolean) {
|
||||
if (!value) {
|
||||
this.cancelTimeout();
|
||||
if (this.isModal) { this.closeModal(); }
|
||||
this.isDelayedRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentTimeout) {
|
||||
return;
|
||||
}
|
||||
this.runSpinner(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
isModal = false;
|
||||
|
||||
@Input()
|
||||
positionClass: string;
|
||||
|
||||
@Output()
|
||||
spinnerIsRunning = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild('spinnerModal', { static: true })
|
||||
private modal;
|
||||
|
||||
constructor(private generalModalService: GeneralModalService) {
|
||||
|
||||
}
|
||||
|
||||
private runSpinner(value) {
|
||||
this.currentTimeout = setTimeout(() => {
|
||||
if (this.isModal) { $(this.modal.nativeElement).modal('show'); }
|
||||
this.isDelayedRunning = value;
|
||||
this.cancelTimeout();
|
||||
this.requestTimeout = setTimeout(() => {
|
||||
this.generalModalService.triggerMessage(
|
||||
new MessageModal('There was an error with your request, please make sure you have internet connection',
|
||||
'Request time execeed')
|
||||
);
|
||||
this.cancelTimeout();
|
||||
this.isRunning = false;
|
||||
}, this.maxRequestTimeout);
|
||||
}, this.delay);
|
||||
}
|
||||
|
||||
private closeModal() {
|
||||
if (this.showSpinner) {
|
||||
$(this.modal.nativeElement).modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private cancelTimeout(): void {
|
||||
clearTimeout(this.currentTimeout);
|
||||
clearTimeout(this.requestTimeout);
|
||||
delete this.currentTimeout; // do we need this?
|
||||
delete this.requestTimeout;
|
||||
}
|
||||
|
||||
ngOnDestroy(): any {
|
||||
this.cancelTimeout();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,25 +13,25 @@ import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
|
|||
]
|
||||
})
|
||||
export class TaskanaTypeAheadMockComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
@Input()
|
||||
validationValue;
|
||||
@Input()
|
||||
validationValue;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ export class TaskanaQueryParameters {
|
|||
|
||||
private static removeLastChar(query: string): string {
|
||||
if (query.lastIndexOf('&') === query.length - 1) {
|
||||
query = query.slice(0, query.lastIndexOf('&'));
|
||||
return query.slice(0, query.lastIndexOf('&'));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ import { environment } from 'environments/environment';
|
|||
@Injectable()
|
||||
export class CustomHttpClientInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
let request = req;
|
||||
if (!environment.production) {
|
||||
req = req.clone({ headers: req.headers.set('Authorization', 'Basic YWRtaW46YWRtaW4=') });
|
||||
request = req.clone({ headers: req.headers.set('Authorization', 'Basic YWRtaW46YWRtaW4=') });
|
||||
}
|
||||
return next.handle(req);
|
||||
return next.handle(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,8 +76,8 @@ export class TaskService {
|
|||
}
|
||||
|
||||
updateTask(task: Task): Observable<Task> {
|
||||
task = this.convertTasksDatesToGMT(task);
|
||||
return this.httpClient.put<Task>(`${this.url}/${task.taskId}`, task);
|
||||
const taskConv = this.convertTasksDatesToGMT(task);
|
||||
return this.httpClient.put<Task>(`${this.url}/${task.taskId}`, taskConv);
|
||||
}
|
||||
|
||||
deleteTask(task: Task): Observable<Task> {
|
||||
|
|
|
@ -57,12 +57,7 @@ export class TaskComponent implements OnInit, OnDestroy {
|
|||
this.requestInProgress = false;
|
||||
this.workbaskets = workbaskets.workbaskets;
|
||||
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.workbaskets.length; i++) {
|
||||
if (this.workbaskets[i].name === this.task.workbasketSummaryResource.name) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
const index = this.workbaskets.findIndex(workbasket => workbasket.name === this.task.workbasketSummaryResource.name);
|
||||
if (index !== -1) {
|
||||
this.workbaskets.splice(index, 1);
|
||||
}
|
||||
|
@ -100,15 +95,16 @@ export class TaskComponent implements OnInit, OnDestroy {
|
|||
const me = this;
|
||||
const extractedExpressions = url.match(this.regex);
|
||||
if (!extractedExpressions) { return url; }
|
||||
let extractedUrl = url;
|
||||
extractedExpressions.forEach(expression => {
|
||||
const parameter = expression.substring(2, expression.length - 1);
|
||||
let objectValue: any = me;
|
||||
parameter.split('.').forEach(property => {
|
||||
objectValue = this.getReflectiveProperty(objectValue, property);
|
||||
});
|
||||
url = url.replace(expression, objectValue);
|
||||
extractedUrl = extractedUrl.replace(expression, objectValue);
|
||||
});
|
||||
return url;
|
||||
return extractedUrl;
|
||||
}
|
||||
|
||||
private getReflectiveProperty(scope: any, property: string) {
|
||||
|
|
|
@ -13,79 +13,79 @@ import { Classification } from '../../../models/classification';
|
|||
styleUrls: ['./general-fields.component.scss']
|
||||
})
|
||||
export class TaskdetailsGeneralFieldsComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
@Input()
|
||||
task: Task;
|
||||
|
||||
@Output() taskChange: EventEmitter<Task> = new EventEmitter<Task>();
|
||||
@Output() taskChange: EventEmitter<Task> = new EventEmitter<Task>();
|
||||
|
||||
@Input()
|
||||
saveToggleTriggered: boolean;
|
||||
@Input()
|
||||
saveToggleTriggered: boolean;
|
||||
|
||||
@Output() formValid: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
@Output() formValid: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild('TaskForm', { static: false })
|
||||
taskForm: NgForm;
|
||||
@ViewChild('TaskForm', { static: false })
|
||||
taskForm: NgForm;
|
||||
|
||||
toogleValidationMap = new Map<string, boolean>();
|
||||
requestInProgress = false;
|
||||
classifications: Classification[];
|
||||
toogleValidationMap = new Map<string, boolean>();
|
||||
requestInProgress = false;
|
||||
classifications: Classification[];
|
||||
|
||||
ownerField = this.customFieldsService.getCustomField(
|
||||
'Owner',
|
||||
'tasks.information.owner'
|
||||
);
|
||||
ownerField = this.customFieldsService.getCustomField(
|
||||
'Owner',
|
||||
'tasks.information.owner'
|
||||
);
|
||||
|
||||
constructor(
|
||||
private classificationService: ClassificationsService,
|
||||
private customFieldsService: CustomFieldsService,
|
||||
private formsValidatorService: FormsValidatorService,
|
||||
private domainService: DomainService
|
||||
) {
|
||||
constructor(
|
||||
private classificationService: ClassificationsService,
|
||||
private customFieldsService: CustomFieldsService,
|
||||
private formsValidatorService: FormsValidatorService,
|
||||
private domainService: DomainService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getClassificationByDomain();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.saveToggleTriggered && changes.saveToggleTriggered.currentValue !== changes.saveToggleTriggered.previousValue) {
|
||||
this.validate();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getClassificationByDomain();
|
||||
}
|
||||
selectClassification(classification: Classification) {
|
||||
this.task.classificationSummaryResource = classification;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.saveToggleTriggered && changes.saveToggleTriggered.currentValue !== changes.saveToggleTriggered.previousValue) {
|
||||
this.validate();
|
||||
}
|
||||
}
|
||||
isFieldValid(field: string): boolean {
|
||||
return this.formsValidatorService.isFieldValid(this.taskForm, field);
|
||||
}
|
||||
|
||||
selectClassification(classification: Classification) {
|
||||
this.task.classificationSummaryResource = classification;
|
||||
updateDate($event) {
|
||||
if (new Date(this.task.due).toISOString() !== $event) {
|
||||
this.task.due = $event;
|
||||
}
|
||||
}
|
||||
|
||||
isFieldValid(field: string): boolean {
|
||||
return this.formsValidatorService.isFieldValid(this.taskForm, field);
|
||||
}
|
||||
private validate() {
|
||||
this.formsValidatorService.formSubmitAttempt = true;
|
||||
this.formsValidatorService
|
||||
.validateFormInformation(this.taskForm, this.toogleValidationMap)
|
||||
.then(value => {
|
||||
if (value) {
|
||||
this.formValid.emit(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateDate($event) {
|
||||
if (new Date(this.task.due).toISOString() !== $event) {
|
||||
this.task.due = $event;
|
||||
}
|
||||
}
|
||||
private changedClassification(itemSelected: any) {
|
||||
this.task.classificationSummaryResource = itemSelected;
|
||||
}
|
||||
|
||||
private validate() {
|
||||
this.formsValidatorService.formSubmitAttempt = true;
|
||||
this.formsValidatorService
|
||||
.validateFormInformation(this.taskForm, this.toogleValidationMap)
|
||||
.then(value => {
|
||||
if (value) {
|
||||
this.formValid.emit(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private changedClassification(itemSelected: any) {
|
||||
this.task.classificationSummaryResource = itemSelected;
|
||||
}
|
||||
|
||||
private async getClassificationByDomain() {
|
||||
this.requestInProgress = true;
|
||||
this.classifications = (await this.classificationService.getClassificationsByDomain(this.domainService.getSelectedDomainValue()))
|
||||
.classifications;
|
||||
this.requestInProgress = false;
|
||||
}
|
||||
private async getClassificationByDomain() {
|
||||
this.requestInProgress = true;
|
||||
this.classifications = (await this.classificationService.getClassificationsByDomain(this.domainService.getSelectedDomainValue()))
|
||||
.classifications;
|
||||
this.requestInProgress = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Component, OnInit, Input, ChangeDetectionStrategy, Output,
|
||||
EventEmitter, SimpleChanges, OnChanges, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, OnInit, Input, ChangeDetectionStrategy, Output, EventEmitter, SimpleChanges, OnChanges, ChangeDetectorRef } from '@angular/core';
|
||||
import { Task } from 'app/workplace/models/task';
|
||||
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
||||
import { WorkplaceService } from 'app/workplace/services/workplace.service';
|
||||
|
|
|
@ -128,7 +128,7 @@ export class TaskMasterComponent implements OnInit, OnDestroy {
|
|||
const totalHeight = window.innerHeight;
|
||||
const cards = Math.round((totalHeight - (unusedHeight + toolbarSize)) / cardHeight);
|
||||
TaskanaQueryParameters.page = TaskanaQueryParameters.page ? TaskanaQueryParameters.page : 1;
|
||||
cards > 0 ? TaskanaQueryParameters.pageSize = cards : TaskanaQueryParameters.pageSize = 1;
|
||||
TaskanaQueryParameters.pageSize = cards > 0 ? cards : 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@ export class OrderTasksByPipe implements PipeTransform {
|
|||
if (value === null) { return null; }
|
||||
value.sort((a, b) => {
|
||||
if (typeof a[column] === 'string') {
|
||||
return _compareString(a[column], b[column]);
|
||||
return compareString(a[column], b[column]);
|
||||
}
|
||||
return _compareNumber(a[column], b[column]);
|
||||
return compareNumber(a[column], b[column]);
|
||||
});
|
||||
return value;
|
||||
|
||||
function _compareString(a: string, b: string): number {
|
||||
function compareString(a: string, b: string): number {
|
||||
if (a.toLowerCase() < b.toLowerCase()) {
|
||||
return -1;
|
||||
} if (a.toLowerCase() > b.toLowerCase()) {
|
||||
|
@ -25,8 +25,8 @@ export class OrderTasksByPipe implements PipeTransform {
|
|||
return 0;
|
||||
}
|
||||
|
||||
function _compareNumber(a: number, b: number): number {
|
||||
return _compareString(a.toString(), b.toString());
|
||||
function compareNumber(a: number, b: number): number {
|
||||
return compareString(a.toString(), b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,12 @@ import 'zone.js/dist/jasmine-patch';
|
|||
import 'zone.js/dist/async-test';
|
||||
import 'zone.js/dist/fake-async-test';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
import 'rxjs';
|
||||
|
||||
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
|
||||
declare var __karma__: any;
|
||||
declare var require: any;
|
||||
declare let __karma__: any;
|
||||
declare let require: any;
|
||||
|
||||
// Prevent Karma from running prematurely.
|
||||
__karma__.loaded = function () {};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* SystemJS module definition */
|
||||
declare var module: NodeModule;
|
||||
interface NodeModule {
|
||||
id: string;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue