TSK-601 Make classification categories customizables via icon and text

This commit is contained in:
Martin Rojas Miguel Angel 2018-06-27 11:28:47 +02:00 committed by Holger Hagen
parent 7e3439c00f
commit 68779cf68a
28 changed files with 178 additions and 82 deletions

View File

@ -50,6 +50,12 @@
"field": "",
"visible": false
}
},
"categories": {
"EXTERNAL": "assets/icons/categories/external.svg",
"MANUAL": "assets/icons/categories/manual.svg",
"AUTOMATIC": "assets/icons/categories/automatic.svg",
"PROCESS": "assets/icons/categories/process.svg"
}
}
}

View File

@ -50,10 +50,7 @@
<div class="dropdown clearfix btn-group">
<button class="btn btn-default" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span class="text-top">
<svg-icon class="blue small fa-fw" src="./assets/icons/{{classification.category === 'EXTERNAL'? 'external':
classification.category === 'AUTOMATIC'? 'automatic':
classification.category === 'MANUAL'? 'manual':
'closed'}}.svg"></svg-icon>
<svg-icon class="blue fa-fw" src="{{getCategoryIcon(classification.category).name}}" data-toggle="tooltip" [title]="getCategoryIcon(classification.category).text"></svg-icon>
</span>
{{classification.category}}
<span class="caret"></span>
@ -62,10 +59,7 @@
<li>
<a *ngFor="let category of categories" (click)="selectCategory(category)">
<span class="text-top">
<svg-icon class="blue small fa-fw" src="./assets/icons/{{category === 'EXTERNAL'? 'external':
category === 'AUTOMATIC'? 'automatic':
category === 'MANUAL'? 'manual':
'closed'}}.svg"></svg-icon>
<svg-icon class="blue fa-fw" src="{{getCategoryIcon(category).name}}" data-toggle="tooltip" [title]="getCategoryIcon(category).text"></svg-icon>
</span>
{{category}}
</a>

View File

@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { Routes } from '@angular/router';
@ -6,7 +6,6 @@ import { RouterTestingModule } from '@angular/router/testing';
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularSvgIconModule } from 'angular-svg-icon';
import { AppModule } from 'app/app.module'
import { ClassificationDetailsComponent } from './classification-details.component';
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
@ -26,6 +25,7 @@ import { ClassificationCategoriesService } from 'app/administration/services/cla
// tslint:enable:max-line-length
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
import { configureTests } from 'app/app.test.configuration';
import { Pair } from 'app/models/pair';
@Component({
selector: 'taskana-dummy-detail',
@ -67,6 +67,7 @@ describe('ClassificationDetailsComponent', () => {
classificationsTypesSpy = spyOn(classificationTypesService, 'getClassificationTypes').and.returnValue(Observable.of([]));
spyOn(classificationCategoriesService, 'getCategories').and.returnValue(Observable.of(['firstCategory', 'secondCategory']));
spyOn(classificationsService, 'deleteClassification').and.returnValue(Observable.of(true));
spyOn(classificationCategoriesService, 'getCategoryIcon').and.returnValue(new Pair('assets/icons/categories/external.svg'));
component.classification = new ClassificationDefinition('id1', undefined, undefined, undefined, undefined, undefined, undefined,
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
undefined, undefined, undefined, new LinksClassification({ 'self': '' }));

View File

@ -22,6 +22,7 @@ import { ClassificationCategoriesService } from 'app/administration/services/cla
// tslint:enable:max-line-length
import { DomainService } from 'app/services/domain/domain.service';
import { CustomFieldsService } from '../../../services/custom-fields/custom-fields.service';
import { Pair } from 'app/models/pair';
@Component({
selector: 'taskana-classification-details',
@ -186,6 +187,10 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
this.classification.category = category;
}
getCategoryIcon(category: string): Pair {
return this.categoryService.getCategoryIcon(category);
}
private afterRequest() {
this.requestInProgressService.setRequestInProgress(false);
this.classificationsService.triggerClassificationSaved();
@ -237,6 +242,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
});
}
ngOnDestroy(): void {
if (this.masterAndDetailSubscription) { this.masterAndDetailSubscription.unsubscribe(); }

View File

@ -14,13 +14,21 @@
<div class="col-xs-2 category-filter">
<button class="btn btn-default" data-toggle="dropdown" type="button" id="dropdown-classification-filter" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
<taskana-icon-type [type]="selectedCategory" class="vertical-align"></taskana-icon-type>
<svg-icon *ngIf="selectedCategory else category_unselected" class="blue" [src]="getCategoryIcon(selectedCategory).name" data-toggle="tooltip"
[title]="getCategoryIcon(category).text"></svg-icon>
<ng-template #category_unselected>
<svg-icon class="blue vertical-align" src="./assets/icons/asterisk.svg"></svg-icon>
</ng-template>
</button>
<ul class="dropdown-menu dropdown-menu-classification" role="menu">
<li>
<a *ngFor="let categories of allCategories | mapValues" type="button" (click)="selectCategory(categories.key);" data-toggle="tooltip"
[title]="categories.value">
<taskana-icon-type class="vertical-align" [type]='categories.key' [text]="categories.value"></taskana-icon-type>
<a type="button" (click)="selectCategory('');" data-toggle="tooltip" title="All">
<svg-icon class="blue vertical-align" src="./assets/icons/asterisk.svg"></svg-icon>
All
</a>
<a *ngFor="let category of categories" type="button" (click)="selectCategory(category);" data-toggle="tooltip" [title]="category">
<svg-icon class="blue" [src]="getCategoryIcon(category).name" data-toggle="tooltip" [title]="getCategoryIcon(category).text"></svg-icon>
{{category}}
</a>
</li>
</ul>

View File

@ -17,7 +17,6 @@ import { ClassificationTypesSelectorComponent } from 'app/shared/classification-
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
import { MapValuesPipe } from 'app/shared/pipes/mapValues/map-values.pipe';
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition/workbasket-definition.service';
import { AlertService } from 'app/services/alert/alert.service';
import { ClassificationsService } from 'app/administration/services/classifications/classifications.service';
@ -27,6 +26,10 @@ import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { configureTests } from 'app/app.test.configuration';
import {
ClassificationCategoriesService
} from 'app/administration/services/classification-categories-service/classification-categories.service';
import { Pair } from 'app/models/pair';
@Component({
selector: 'taskana-tree',
@ -57,7 +60,7 @@ describe('ClassificationListComponent', () => {
let fixture: ComponentFixture<ClassificationListComponent>;
const treeNodes: Array<TreeNodeModel> = new Array(new TreeNodeModel());
const classificationTypes: Array<string> = new Array<string>('type1', 'type2');
let classificationsService, classificationTypesService;
let classificationsService, classificationTypesService, classificationCategoriesService;
beforeEach(done => {
const configure = (testBed: TestBed) => {
@ -67,19 +70,22 @@ describe('ClassificationListComponent', () => {
imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpModule],
providers: [
HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService,
ErrorModalService, ClassificationTypesService, RequestInProgressService
ErrorModalService, ClassificationTypesService, RequestInProgressService, ClassificationCategoriesService
]
})
};
configureTests(configure).then(testBed => {
fixture = TestBed.createComponent(ClassificationListComponent);
fixture = testBed.createComponent(ClassificationListComponent);
component = fixture.componentInstance;
classificationsService = TestBed.get(ClassificationsService);
classificationTypesService = TestBed.get(ClassificationTypesService);
classificationsService = testBed.get(ClassificationsService);
classificationTypesService = testBed.get(ClassificationTypesService);
classificationCategoriesService = testBed.get(ClassificationCategoriesService);
spyOn(classificationsService, 'getClassifications').and.returnValue(Observable.of(treeNodes));
spyOn(classificationTypesService, 'getClassificationTypes')
.and.returnValue(Observable.of(classificationTypes));
spyOn(classificationCategoriesService, 'getCategories').and.returnValue(Observable.of(new Array<string>('cat1', 'cat2')));
spyOn(classificationCategoriesService, 'getCategoryIcon').and.returnValue(new Pair('assets/icons/categories/external.svg'));
fixture.detectChanges();
done();
});

View File

@ -8,6 +8,10 @@ import { TreeNodeModel } from 'app/models/tree-node';
import { ClassificationsService } from 'app/administration/services/classifications/classifications.service';
import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service';
import {
ClassificationCategoriesService
} from 'app/administration/services/classification-categories-service/classification-categories.service';
import { Pair } from 'app/models/pair';
@Component({
selector: 'taskana-classification-list',
@ -23,10 +27,7 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
requestInProgress = false;
initialized = false;
inputValue: string;
allCategories: Map<string, string> = new Map([['ALL', 'All'], ['EXTERNAL', 'External'],
['AUTOMATIC', 'Automatic'], ['MANUAL', 'manual'], ['CLOSED', 'closed']]);
categories = new Array<string>();
classifications: Array<Classification> = [];
classificationsTypes: Array<string> = [];
classificationTypeSelected: string;
@ -35,12 +36,14 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
classificationSelectedSubscription: Subscription;
classificationSavedSubscription: Subscription;
selectedClassificationSubscription: Subscription;
categoriesSubscription: Subscription;
constructor(
private classificationService: ClassificationsService,
private router: Router,
private route: ActivatedRoute,
private classificationTypeService: ClassificationTypesService) {
private classificationTypeService: ClassificationTypesService,
private categoryService: ClassificationCategoriesService) {
}
ngOnInit() {
@ -53,6 +56,10 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
this.classificationTypeSelected = value;
this.performRequest();
})
this.categoriesSubscription = this.categoryService.getCategories().subscribe((categories: Array<string>) => {
this.categories = categories;
});
}
selectClassificationType(classificationTypeSelected: string) {
@ -83,6 +90,10 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
this.selectedCategory = category;
}
getCategoryIcon(category: string): Pair {
return this.categoryService.getCategoryIcon(category);
}
private performRequest(forceRequest = false) {
if (this.initialized && !forceRequest) {
return;

View File

@ -35,11 +35,7 @@ export class IconTypeComponent implements OnInit {
return type === 'PERSONAL' ? 'user.svg' :
type === 'GROUP' ? 'users.svg' :
type === 'TOPIC' ? 'topic.svg' :
type === 'CLEARANCE' ? 'clearance.svg' :
type === 'EXTERNAL' ? 'external.svg' :
type === 'AUTOMATIC' ? 'automatic.svg' :
type === 'MANUAL' ? 'manual.svg' :
type === 'CLOSED' ? 'closed.svg' : 'asterisk.svg';
type === 'CLEARANCE' ? 'clearance.svg' : 'asterisk.svg';
}
}

View File

@ -1,18 +0,0 @@
import { TestBed, inject } from '@angular/core/testing';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ClassificationCategoriesService } from './classification-categories.service';
describe('CategoryService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [HttpClient, ClassificationCategoriesService]
});
});
it('should be created', inject([ClassificationCategoriesService], (service: ClassificationCategoriesService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -1,23 +1,28 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
import { Pair } from 'app/models/pair';
@Injectable()
export class ClassificationCategoriesService {
private url = environment.taskanaRestUrl + '/v1/classification-categories';
private dataObs$ = new ReplaySubject<Array<string>>(1);
private categoriesObject = new Object();
private missingIcon = 'assets/icons/categories/missing-icon.svg';
constructor(
private httpClient: HttpClient) { }
private httpClient: HttpClient,
private customFieldsService: CustomFieldsService) { }
getCategories(forceRefresh = false): Observable<Array<string>> {
if (!this.dataObs$.observers.length || forceRefresh) {
this.httpClient.get<Array<string>>(this.url).subscribe(
data => this.dataObs$.next(data),
data => { this.dataObs$.next(data); this.categoriesObject = this.getCustomCategoriesObject(data) },
error => {
this.dataObs$.error(error);
this.dataObs$ = new ReplaySubject(1);
@ -27,4 +32,28 @@ export class ClassificationCategoriesService {
return this.dataObs$;
};
getCategoryIcon(category: string): Pair {
let categoryIcon = this.categoriesObject[category];
let text = '';
if (!categoryIcon) {
categoryIcon = this.missingIcon;
text = 'Category does not match with the configuration'
}
return new Pair(categoryIcon, text);
}
private getCustomCategoriesObject(categories: Array<string>): Object {
return this.customFieldsService.getCustomObject(
this.getDefaultCategoryMap(categories), 'classifications.categories');
}
private getDefaultCategoryMap(categoryList: Array<string>): Object {
const defaultCategoryMap = new Object();
categoryList.forEach(element => {
defaultCategoryMap[element] = `assets/icons/categories/${element.toLowerCase()}.svg`;
});
return defaultCategoryMap;
}
}

View File

@ -133,7 +133,7 @@
</form>
<button id="button-add-access-item" type="button" (click)="addAccessItem()" class="btn btn-default">
<span>
<svg-icon class="green-blue small" src="./assets/icons/wb-add.svg"></svg-icon>
<svg-icon class="green-blue" src="./assets/icons/wb-add.svg"></svg-icon>
</span>
Add new access
</button>

View File

@ -5,7 +5,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AlertModule } from 'ngx-bootstrap';
import { AngularSvgIconModule } from 'angular-svg-icon';

View File

@ -1,7 +1,6 @@
import {
getTestBed,
ComponentFixtureAutoDetect,
TestBed,
} from '@angular/core/testing';
@ -13,6 +12,8 @@ import { TaskanaEngineServiceMock } from './services/taskana-engine/taskana-engi
import { TaskanaEngineService } from './services/taskana-engine/taskana-engine.service';
import { DomainService } from './services/domain/domain.service';
import { DomainServiceMock } from './services/domain/domain.service.mock';
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
export const configureTests = (configure: (testBed: TestBed) => void) => {
const testBed = getTestBed();
@ -26,7 +27,7 @@ export const configureTests = (configure: (testBed: TestBed) => void) => {
configure(testBed);
testBed.configureTestingModule({
providers: [{ provide: TaskanaEngineService, useClass: TaskanaEngineServiceMock },
{ provide: DomainService, useClass: DomainServiceMock }]
{ provide: DomainService, useClass: DomainServiceMock }, CustomFieldsService]
});
return testBed.compileComponents().then(() => testBed);

View File

@ -0,0 +1,6 @@
export class Pair {
constructor(
public name: string = undefined,
public text: string = undefined,
) { }
}

View File

@ -17,6 +17,13 @@ export class CustomFieldsService {
return this.jsonPath(customPath, fallbacktext);
}
getCustomObject(fallbackObject: Object, customPath: string = undefined): Object {
if (!customPath) {
return fallbackObject;
}
return this.jsonPathObject(customPath, fallbackObject);
}
private jsonPath(path: string, fallbacktext: string): CustomField {
if (!this.customizedFields) {
return new CustomField(true, fallbacktext);
@ -34,4 +41,24 @@ export class CustomFieldsService {
return value;
}
private jsonPathObject(path: string, fallbackObject: Object): Object {
if (!this.customizedFields) {
return fallbackObject;
};
const paths = path.split('.');
let value = this.customizedFields;
paths.every(element => {
value = value[element];
if (!value) {
value = fallbackObject
return false;
}
return true;
});
return value;
}
}

View File

@ -46,7 +46,7 @@ export class StartupService {
}
geCustomizedFieldsFilePromise() {
return this.httpClient.get<any>('environments/data-sources/customized-fields.json').map(jsonFile => {
return this.httpClient.get<any>('environments/data-sources/taskana-customization.json').map(jsonFile => {
if (jsonFile) {
this.customFieldsService.initCustomFields('EN', jsonFile);
}

View File

@ -1,14 +1,12 @@
<tree-root #tree [nodes]="treeNodes" [options]="options" (activate)="onActivate($event)" (deactivate)="onDeactivate($event)">
<ng-template #treeNodeTemplate let-node let-index="index">
<span class="text-top">
<svg-icon class="blue small fa-fw" src="./assets/icons/{{node.data.category === 'EXTERNAL'? 'external':
node.data.category === 'AUTOMATIC'? 'automatic':
node.data.category === 'MANUAL'? 'manual':
'closed'}}.svg"></svg-icon>
<svg-icon *ngIf="node.data.category" class="blue fa-fw" [src]="getCategoryIcon(node.data.category).name" data-toggle="tooltip"
[title]="getCategoryIcon(node.data.category).text"></svg-icon>
</span>
<span>
<strong>{{ node.data.key }}</strong>
</span>
<span> - {{ node.data.name }}</span>
</ng-template>
</tree-root>
</tree-root>

View File

@ -1,5 +1,5 @@
import { Input, Component } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AngularSvgIconModule } from 'angular-svg-icon';
import { HttpClientModule } from '@angular/common/http';
import { HttpModule } from '@angular/http';
@ -7,6 +7,11 @@ import { HttpModule } from '@angular/http';
import { TaskanaTreeComponent } from './tree.component';
import { TreeService } from 'app/services/tree/tree.service';
import {
ClassificationCategoriesService
} from 'app/administration/services/classification-categories-service/classification-categories.service';
import { configureTests } from 'app/app.test.configuration';
import { Pair } from 'app/models/pair';
// tslint:disable:component-selector
@Component({
@ -26,20 +31,26 @@ class TreeVendorComponent {
describe('TaskanaTreeComponent', () => {
let component: TaskanaTreeComponent;
let fixture: ComponentFixture<TaskanaTreeComponent>;
let classificationCategoriesService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule, HttpModule],
declarations: [TaskanaTreeComponent, TreeVendorComponent],
providers: [TreeService]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TaskanaTreeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
beforeEach(done => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule, HttpModule],
declarations: [TaskanaTreeComponent, TreeVendorComponent],
providers: [TreeService, ClassificationCategoriesService]
})
};
configureTests(configure).then(testBed => {
fixture = testBed.createComponent(TaskanaTreeComponent);
classificationCategoriesService = testBed.get(ClassificationCategoriesService);
spyOn(classificationCategoriesService, 'getCategoryIcon').and.returnValue(new Pair('assets/icons/categories/external.svg'));
component = fixture.componentInstance;
fixture.detectChanges();
done();
});
});
it('should create', () => {

View File

@ -1,8 +1,12 @@
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, AfterViewChecked, OnChanges, SimpleChanges } from '@angular/core';
import { TreeNodeModel } from 'app/models/tree-node';
import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions, ITreeState, TreeComponent, TreeNode } from 'angular-tree-component';
import { KEYS, ITreeOptions, TreeComponent, TreeNode } from 'angular-tree-component';
import { TreeService } from '../../services/tree/tree.service';
import {
ClassificationCategoriesService
} from 'app/administration/services/classification-categories-service/classification-categories.service';
import { Pair } from 'app/models/pair';
@Component({
selector: 'taskana-tree',
@ -25,7 +29,6 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked {
private filterTextOld: string
private filterIconOld: string
private beforeFilteringState: ITreeState;
options: ITreeOptions = {
displayField: 'name',
@ -42,10 +45,9 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked {
levelPadding: 20
}
constructor(private treeService: TreeService) { }
constructor(private treeService: TreeService, private categoryService: ClassificationCategoriesService) { }
ngOnInit() {
this.selectNode(this.selectNodeId);
this.treeService.getRemovedNodeId().subscribe(value => {
const removedNode = this.getNode(value);
if (removedNode.parent) {
@ -83,6 +85,11 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked {
this.selectNodeIdChanged.emit(undefined);
}
getCategoryIcon(category: string): Pair {
return this.categoryService.getCategoryIcon(category);
}
private selectNode(nodeId: string) {
if (nodeId) {
const selectedNode = this.getNode(nodeId)

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M6.108 15.89c-.088-.086-.15-.406-.193-.984l-.062-.856-.664-.327a9.303 9.303 0 0 1-1.013-.576l-.348-.248-.769.325c-.923.39-1.128.404-1.349.091-.442-.627-1.679-2.822-1.679-2.98 0-.128.207-.323.72-.678l.72-.498.048-1.144.049-1.143-.223-.193a8.167 8.167 0 0 0-.72-.51c-.273-.175-.52-.372-.548-.437-.047-.112 1.473-2.84 1.77-3.177.187-.212.379-.187 1.044.138.901.44.91.441 1.395.102.235-.164.684-.416.997-.56l.57-.259.062-.823c.034-.453.1-.892.145-.976.077-.139.245-.152 1.946-.152 1.39 0 1.885.027 1.953.107.05.059.12.482.154.94l.063.834.663.327c.365.18.82.439 1.012.575l.348.249.769-.325c.923-.39 1.128-.405 1.349-.092.443.628 1.679 2.823 1.679 2.981 0 .128-.207.323-.72.678l-.72.499-.024 1.16-.024 1.16.658.483c.38.28.68.567.709.681.038.148-.153.548-.757 1.584-.899 1.543-1 1.683-1.225 1.683-.086 0-.504-.147-.928-.326l-.771-.326-.39.295a6.852 6.852 0 0 1-1.01.586l-.621.29-.062.847c-.041.571-.104.889-.191.974-.193.188-3.62.188-3.812 0zM9.19 11.79c3.02-.797 3.91-4.723 1.544-6.797-.764-.67-1.612-.985-2.664-.99C5.713 3.995 3.945 5.7 3.95 7.979c.004 2.735 2.486 4.541 5.241 3.813zm-2.18-1.65c-.895-.395-1.395-1.172-1.394-2.163.003-1.388.976-2.338 2.397-2.338 1.42 0 2.393.95 2.396 2.338.002 1.384-.973 2.335-2.396 2.335-.428 0-.729-.052-1.004-.173z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M6.108 15.89c-.088-.086-.15-.406-.193-.984l-.062-.856-.664-.327a9.303 9.303 0 0 1-1.013-.576l-.348-.248-.769.325c-.923.39-1.128.404-1.349.091-.442-.627-1.679-2.822-1.679-2.98 0-.128.207-.323.72-.678l.72-.498.048-1.144.049-1.143-.223-.193a8.167 8.167 0 0 0-.72-.51c-.273-.175-.52-.372-.548-.437-.047-.112 1.473-2.84 1.77-3.177.187-.212.379-.187 1.044.138.901.44.91.441 1.395.102.235-.164.684-.416.997-.56l.57-.259.062-.823c.034-.453.1-.892.145-.976.077-.139.245-.152 1.946-.152 1.39 0 1.885.027 1.953.107.05.059.12.482.154.94l.063.834.663.327c.365.18.82.439 1.012.575l.348.249.769-.325c.923-.39 1.128-.405 1.349-.092.443.628 1.679 2.823 1.679 2.981 0 .128-.207.323-.72.678l-.72.499-.024 1.16-.024 1.16.658.483c.38.28.68.567.709.681.038.148-.153.548-.757 1.584-.899 1.543-1 1.683-1.225 1.683-.086 0-.504-.147-.928-.326l-.771-.326-.39.295a6.852 6.852 0 0 1-1.01.586l-.621.29-.062.847c-.041.571-.104.889-.191.974-.193.188-3.62.188-3.812 0zm3.082-4.1c3.02-.797 3.91-4.723 1.544-6.797-.764-.67-1.612-.985-2.664-.99C5.713 3.995 3.945 5.7 3.95 7.979c.004 2.735 2.486 4.541 5.241 3.813zm-2.18-1.65c-.895-.395-1.395-1.172-1.394-2.163.003-1.388.976-2.338 2.397-2.338 1.42 0 2.393.95 2.396 2.338.002 1.384-.973 2.335-2.396 2.335-.428 0-.729-.052-1.004-.173z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M8.11 9.71h.004c.12 0 .239-.04.335-.111l.026-.02.018-.012.303-.284 7.248-6.771a.083.083 0 0 0-.067-.04H.204a.076.076 0 0 0-.052.022L7.735 9.57a.55.55 0 0 0 .376.14zM.12 3.432v10.037L5.552 8.5zm10.515 5.102l5.426 4.937V3.465zm-1.726 1.612c-.216.2-.499.311-.796.311-.298 0-.581-.11-.797-.31L6.072 8.986.12 14.428v.017c0 .052.04.096.085.096h15.772c.045 0 .084-.044.084-.096v-.016l-5.947-5.41z"/></svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M.167 15.869c-.08-.078-.144-.203-.144-.277 0-.129.875-1.237 1.857-2.352.258-.293 1.24-1.308 2.184-2.257L5.78 9.26 4.405 7.89c-.756-.753-1.378-1.416-1.383-1.472-.028-.342.223-.628.748-.852.505-.215 1.252-.172 1.718.099.179.104.341.189.36.189.02 0 1.078-.887 2.35-1.972l2.315-1.97-.098-.391c-.08-.316-.082-.452-.012-.707.096-.345.483-.785.691-.785.148 0 4.802 4.574 4.87 4.785.053.167-.162.462-.464.638-.243.141-.828.185-1.201.09-.224-.058-.23-.052-2.152 2.17a157.651 157.651 0 0 0-1.977 2.308c-.028.046.038.252.146.46.261.499.28 1.356.041 1.802-.241.451-.488.639-.806.613-.057-.005-.693-.59-1.414-1.302l-1.31-1.294-1.666 1.634c-.915.899-1.939 1.876-2.273 2.171-1.013.894-2.314 1.908-2.45 1.908-.07 0-.193-.064-.271-.142z"/></svg>

After

Width:  |  Height:  |  Size: 800 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M12.456 15.847a.98.98 0 0 1-.3-.288c-.133-.192-.151-.323-.151-1.054v-.836l-.972.002-.972.001-.907-1.07c-1.193-1.407-1.417-1.716-1.344-1.859.033-.062.394-.52.803-1.015l.744-.902.811 1.007.812 1.006.512.005.513.004v-.892c0-.88.003-.896.206-1.128.164-.187.27-.236.509-.236.298 0 .32.02 1.737 1.629 1.507 1.71 1.626 1.903 1.435 2.332-.117.261-2.605 3.114-2.841 3.257-.213.129-.373.139-.595.037zM.2 13.508c-.128-.146-.141-.268-.14-1.27 0-.609.03-1.162.067-1.228.055-.099.33-.128 1.5-.161l1.432-.04 3.2-3.91c1.759-2.149 3.34-4.062 3.514-4.25l.316-.343H12.005v-.84c0-.783.012-.856.18-1.085.225-.303.57-.394.854-.223.244.148 2.732 2.995 2.853 3.265.19.428.072.621-1.435 2.332-1.417 1.608-1.439 1.628-1.737 1.628-.24 0-.345-.048-.509-.235-.203-.232-.206-.249-.206-1.132V5.12l-.515.024-.515.023L7.532 9.37c-1.894 2.311-3.495 4.224-3.556 4.251-.062.027-.905.05-1.874.05-1.668 0-1.77-.01-1.903-.162zm3.65-7.374l-.833-1.007H1.648C.498 5.127.262 5.107.168 5c-.09-.103-.11-.336-.11-1.249 0-1.567-.18-1.444 2.103-1.446l1.82-.001.766.908c.42.499.94 1.133 1.155 1.409l.39.501-.132.191c-.072.105-.434.56-.805 1.01l-.673.818z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M.517 15.83c-.144-.104-.292-.32-.377-.549-.133-.357-.14-.55-.12-3.638.019-3.142.025-3.27.167-3.495.311-.494.488-.582 1.233-.611l.691-.027 2.592-3.068 2.592-3.068-.043-.31c-.05-.356.09-.75.34-.957.208-.173.694-.17.905.006.224.187.38.655.33.99-.04.262.04.37 2.55 3.339L13.97 7.51l.692.027c.745.03.922.117 1.233.61.142.226.148.355.168 3.471.024 3.663.01 3.784-.473 4.174l-.26.21H8.042c-7.19 0-7.291-.003-7.525-.172zm14.885-.63l.176-.208v-6.38l-.209-.249-.209-.248H.921l-.208.248-.209.248v6.38l.175.21.176.207h14.372zM1.72 13.564a1.386 1.386 0 0 1-.41-.437c-.147-.26-.163-.388-.163-1.315 0-1.237.09-1.51.593-1.818.265-.162.383-.188.634-.138.412.083.866.6.832.947-.036.367-.242.348-.553-.051-.293-.377-.407-.412-.715-.223-.27.166-.29.255-.29 1.286 0 .829.01.901.174 1.095.111.132.257.209.397.209.212 0 .571-.312.571-.496 0-.132.226-.193.33-.09.233.23-.002.801-.429 1.042a.933.933 0 0 1-.971-.011zm1.975.092c-.09-.108-.055-3.633.038-3.743.113-.134.168-.128.302.032.094.11.113.384.113 1.65v1.517l.617.025c.647.025.817.129.718.436-.038.118-.174.14-.893.14-.466 0-.869-.026-.895-.057zm2.468-.085c-.459-.268-.548-.537-.574-1.724-.022-.994-.013-1.079.143-1.35.263-.458.512-.633.9-.633.38 0 .689.179.909.527.122.194.146.384.166 1.328.021 1.039.013 1.12-.145 1.4-.308.546-.907.74-1.399.452zm.881-.66c.165-.196.175-.263.173-1.124-.002-.959-.04-1.098-.344-1.29-.208-.132-.462-.062-.652.18-.127.161-.145.297-.145 1.105 0 .864.011.933.176 1.128.11.132.257.209.396.209.14 0 .286-.077.396-.209zm1.526.62c-.263-.18-.493-.546-.493-.783 0-.073.063-.173.14-.222.113-.071.172-.054.287.082.08.094.144.203.144.243 0 .107.337.268.562.268.233 0 .51-.278.51-.512 0-.255-.256-.506-.518-.508-.353-.002-.818-.29-.98-.608-.594-1.155.837-2.214 1.718-1.273.252.27.31.665.117.811-.108.082-.164.045-.366-.24-.207-.291-.28-.338-.528-.338-.496 0-.767.497-.453.834.073.078.312.177.531.22.646.124.979.502.979 1.109 0 .854-.964 1.39-1.65.917zm2.057.127c-.092-.109-.058-3.633.036-3.745.056-.066.35-.102.848-.102.803 0 .964.07.91.4-.02.13-.115.155-.684.176l-.66.024V11.5l.518.025c.485.023.52.038.542.227.033.276-.09.346-.613.348h-.447v1.018l.59.001c.666.002.79.06.756.348-.024.199-.039.203-.885.226-.474.013-.884-.003-.911-.035zm2.208-.08c-.024-.074-.043-.909-.043-1.855 0-2.003-.012-1.967.613-1.88.935.131 1.532.888 1.528 1.937-.004 1.094-.727 1.932-1.667 1.932-.288 0-.399-.034-.431-.134zm1.019-.635c.382-.258.534-.55.566-1.093.023-.387-.002-.521-.14-.762a1.391 1.391 0 0 0-.792-.644l-.196-.057v1.366c0 1.334.003 1.366.15 1.366.084 0 .269-.08.412-.176zM10.845 4.66l-2.41-2.862h-.788L5.237 4.66l-2.41 2.862h10.428zM8.303 1.037c.099-.257-.02-.469-.262-.469a.345.345 0 0 0-.252.13c-.14.2.02.549.252.549.124 0 .207-.067.262-.21z"/></svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M.056 8.794V1.611h10.202L8.68 3.294 7.105 4.978H2.933v8.082h8.19V9.113l1.402-1.675c.771-.92 1.419-1.674 1.44-1.674.02 0 .036 2.298.036 5.107v5.107H.056zm9.96-2.862l-.936-.955 1.63-1.656 1.63-1.657-.8-.812-.802-.813h5.255v5.326l-.83-.837-.831-.837-1.578 1.598c-.868.878-1.628 1.597-1.69 1.597-.061 0-.533-.43-1.048-.954z"/></svg>

Before

Width:  |  Height:  |  Size: 400 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M5.622 15.86c-.279-.151-.432-.49-.482-1.069l-.044-.504-1.554-2.058C1.9 10.052 1.777 9.835 1.88 9.27c.102-.56.59-1.024 1.161-1.103.403-.055.773.072 1.117.383l.274.249.002-2.41c.001-2.717.012-2.783.533-3.214.442-.365 1.218-.435 1.576-.142.066.055.089.029.089-.103 0-.224.245-.587.575-.854.582-.47 1.5-.297 1.916.36.113.178.205.386.205.46 0 .11.033.128.16.089.622-.19.884-.19 1.266.003.417.21.772.807.773 1.3 0 .132.027.157.125.117.365-.15.599-.18.857-.11.42.113.74.363.916.713.156.309.16.374.16 2.598 0 2.41-.085 3.537-.35 4.634-.154.637-.452 1.473-.613 1.72-.058.088-.14.425-.185.748-.095.7-.202.938-.505 1.123-.216.132-.374.14-3.172.137-2.466-.002-2.976-.02-3.139-.109zm5.932-.624c.068-.052.136-.307.185-.688.05-.388.142-.725.256-.942.273-.515.517-1.29.678-2.154.124-.667.15-1.167.182-3.389.027-1.936.014-2.646-.048-2.764-.047-.087-.194-.204-.327-.26-.21-.088-.275-.088-.485 0-.134.056-.278.167-.32.246-.046.087-.076.64-.076 1.414 0 1.034-.02 1.299-.11 1.428-.125.177-.464.215-.588.065-.05-.06-.084-.819-.098-2.169l-.02-2.077-.186-.186c-.333-.333-.966-.206-1.13.227-.037.099-.068 1.073-.068 2.166v1.986l-.177.117c-.165.108-.19.108-.355 0l-.178-.117V5.73c0-1.324-.02-2.508-.044-2.63-.108-.542-.727-.763-1.14-.407l-.2.171-.02 2.618c-.012 1.603-.047 2.65-.09 2.703-.142.171-.36.168-.536-.007l-.17-.17V6.006c0-1.297-.026-2.051-.076-2.145-.1-.188-.476-.341-.722-.294-.108.02-.276.132-.373.248l-.176.21v2.811c0 1.801-.027 2.86-.074 2.948-.143.269-.428.174-.886-.294-.667-.681-.982-.784-1.376-.451-.18.151-.219.237-.219.48 0 .24.058.37.305.688.167.215.886 1.161 1.596 2.103l1.292 1.712.046.554c.029.334.088.594.15.657.088.088.522.103 2.787.095 1.916-.007 2.714-.033 2.791-.092zM.118 4.356c-.12-.12-.106-.774.027-1.292C.59 1.34 2.276-.042 3.936-.042c.427 0 .567.08.567.325 0 .233-.157.321-.695.39C2.164.878.896 2.137.763 3.693c-.02.235-.082.5-.137.587-.108.174-.37.213-.508.075zm1.475-.023c-.134-.162-.045-.881.167-1.345.365-.8 1.25-1.44 2.1-1.516.479-.044.643.035.643.308 0 .216-.226.355-.668.411-.854.109-1.602.947-1.602 1.795 0 .128-.05.282-.112.344-.142.142-.412.144-.528.003zm12.354.043c-.055-.035-.124-.244-.154-.464-.126-.93-.762-1.614-1.597-1.72-.442-.057-.668-.196-.668-.412 0-.273.164-.352.643-.308.85.077 1.696.685 2.076 1.491.225.477.327 1.206.191 1.37-.1.12-.337.14-.491.043zm1.458-.095c-.055-.088-.119-.356-.142-.597-.15-1.57-1.397-2.805-3.04-3.012-.538-.068-.695-.156-.695-.389 0-.245.14-.325.567-.325.793 0 1.694.353 2.429.954.481.393 1.044 1.214 1.256 1.831.194.567.275 1.436.148 1.59-.133.16-.408.134-.523-.052z"/></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -50,6 +50,12 @@
"field": "",
"visible": false
}
},
"categories": {
"EXTERNAL": "assets/icons/categories/external.svg",
"MANUAL": "assets/icons/categories/manual.svg",
"AUTOMATIC": "assets/icons/categories/automatic.svg",
"PROCESS": "assets/icons/categories/process.svg"
}
}
}