TSK-1750: configuration schema now respects order of individual fields
This commit is contained in:
parent
3398e81a7c
commit
331bf2998b
|
@ -18,65 +18,75 @@
|
|||
"colorLowPriority": "#5FAD00",
|
||||
"colorMediumPriority": "#FFD700",
|
||||
"filter": "{ \"Tasks with state READY\": { \"state\": [\"READY\"]}, \"Tasks with state CLAIMED\": {\"state\": [\"CLAIMED\"] }}",
|
||||
"schema": {
|
||||
"Monitor Workbasket-Priority-Report": {
|
||||
"schema": [
|
||||
{
|
||||
"displayName": "Priority Report",
|
||||
"members": {
|
||||
"nameHighPriority": {
|
||||
"members": [
|
||||
{
|
||||
"key": "nameHighPriority",
|
||||
"displayName": "High Priority Name",
|
||||
"type": "text",
|
||||
"max": 32
|
||||
},
|
||||
"nameMediumPriority": {
|
||||
{
|
||||
"key": "nameMediumPriority",
|
||||
"displayName": "Medium Priority Name",
|
||||
"type": "text",
|
||||
"min": 0,
|
||||
"max": 32
|
||||
},
|
||||
"nameLowPriority": {
|
||||
{
|
||||
"key": "nameLowPriority",
|
||||
"displayName": "Low Priority Name",
|
||||
"type": "text",
|
||||
"min": 0,
|
||||
"max": 32
|
||||
},
|
||||
"intervalHighPriority": {
|
||||
{
|
||||
"key": "intervalHighPriority",
|
||||
"displayName": "High Priority Interval",
|
||||
"type": "interval",
|
||||
"min": 0
|
||||
},
|
||||
"intervalMediumPriority": {
|
||||
{
|
||||
"key": "intervalMediumPriority",
|
||||
"displayName": "Medium Priority Interval",
|
||||
"type": "interval",
|
||||
"min": 0
|
||||
},
|
||||
"intervalLowPriority": {
|
||||
{
|
||||
"key": "intervalLowPriority",
|
||||
"displayName": "Low Priority Interval",
|
||||
"type": "interval",
|
||||
"min": 0
|
||||
},
|
||||
"colorHighPriority": {
|
||||
{
|
||||
"key": "colorHighPriority",
|
||||
"displayName": "High Priority Color",
|
||||
"type": "color"
|
||||
},
|
||||
"colorMediumPriority": {
|
||||
{
|
||||
"key": "colorMediumPriority",
|
||||
"displayName": "Medium Priority Color",
|
||||
"type": "color"
|
||||
},
|
||||
"colorLowPriority": {
|
||||
{
|
||||
"key": "colorLowPriority",
|
||||
"displayName": "Low Priority Color",
|
||||
"type": "color"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Monitor Workbasket-Priority-Report-Filter": {
|
||||
{
|
||||
"displayName": "Filter for Task-Priority-Report",
|
||||
"members": {
|
||||
"filter": {
|
||||
"members": [
|
||||
{
|
||||
"key": "filter",
|
||||
"displayName": "Filter values",
|
||||
"type": "json",
|
||||
"min": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
<!-- BUTTONS -->
|
||||
<div class="settings__buttons">
|
||||
<button mat-button class="settings__button--primary" matTooltip="Save settings" (click)="onSave()">
|
||||
<button (click)="onSave()" class="settings__button--primary" mat-button
|
||||
matTooltip="Save settings">
|
||||
Save
|
||||
<mat-icon class="md-20">save</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-stroked-button class="settings__button--secondary" matTooltip="Revert changes" (click)="onReset()">
|
||||
<button (click)="onReset()" class="settings__button--secondary" mat-stroked-button
|
||||
matTooltip="Revert changes">
|
||||
Undo changes
|
||||
<mat-icon class="settings__icon md-20">restore</mat-icon>
|
||||
</button>
|
||||
|
@ -15,54 +17,63 @@
|
|||
|
||||
<div class="settings__content">
|
||||
|
||||
<div *ngFor="let group of groups; let outerIndex = index">
|
||||
<h4 class="settings__domain-name"> {{settings.schema[group].displayName}} </h4>
|
||||
<div *ngFor="let member of members[outerIndex]; let innerIndex = index">
|
||||
<div *ngFor="let group of settings.schema; let outerIndex = index">
|
||||
<h4 class="settings__domain-name"> {{group.displayName}} </h4>
|
||||
<div *ngFor="let member of group.members">
|
||||
|
||||
<!-- STRING -->
|
||||
<div *ngIf="getMember(group,member).type == settingTypes.TEXT" class="settings__grid">
|
||||
<span> {{getMember(group,member).displayName}} </span>
|
||||
<div *ngIf="member.type == settingTypes.TEXT" class="settings__grid">
|
||||
<span> {{member.displayName}} </span>
|
||||
<mat-form-field appearance="outline" class="settings__grid--two-columns">
|
||||
<mat-label class="{{member}}">{{getMember(group,member).displayName}}</mat-label>
|
||||
<input matInput type="text" placeholder="{{getMember(group,member).displayName}}" [(ngModel)]="settings[member]"
|
||||
minlength="{{settings.schema[group].members[member].min}}" maxlength="{{settings.schema[group].members[member].max}}">
|
||||
<mat-label class="{{member.key}}">{{member.displayName}}</mat-label>
|
||||
<input [(ngModel)]="settings[member.key]" matInput maxlength="{{member.max}}"
|
||||
minlength="{{member.min}}"
|
||||
placeholder="{{member.displayName}}"
|
||||
type="text">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- INTERVAL -->
|
||||
<div *ngIf="getMember(group,member).type == settingTypes.INTERVAL" class="settings__grid">
|
||||
<span>{{getMember(group,member).displayName}}</span>
|
||||
<div *ngIf="member.type == settingTypes.INTERVAL" class="settings__grid">
|
||||
<span>{{member.displayName}}</span>
|
||||
|
||||
<mat-form-field appearance="outline" >
|
||||
<mat-label class="{{member}}">Lower boundary</mat-label>
|
||||
<input matInput type="number" placeholder="Lower boundary" [(ngModel)]="settings[member][0]"
|
||||
min="{{getMember(group,member).min}}" max="{{getMember(group,member).max}}">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label class="{{member.key}}">Lower boundary</mat-label>
|
||||
<input [(ngModel)]="settings[member.key][0]" matInput max="{{member.max}}"
|
||||
min="{{member.min}}"
|
||||
placeholder="Lower boundary" type="number">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label class="{{member}}">Upper boundary</mat-label>
|
||||
<input matInput type="number" placeholder="Upper boundary" [(ngModel)]="settings[member][1]"
|
||||
min="{{getMember(group,member).min}}" max="{{getMember(group,member).max}}">
|
||||
<input [(ngModel)]="settings[member.key][1]" matInput max="{{member.max}}"
|
||||
min="{{member.min}}"
|
||||
placeholder="Upper boundary" type="number">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- COLOR -->
|
||||
<div *ngIf="getMember(group,member).type == settingTypes.COLOR" class="settings__grid settings__color">
|
||||
<span>{{getMember(group,member).displayName}}</span>
|
||||
<input matInput value="{{settings[member]}}" type="color" id="{{member}}"
|
||||
(change)="onColorChange(member)" class="settings__colors--input">
|
||||
<div *ngIf="member.type == settingTypes.COLOR"
|
||||
class="settings__grid settings__color">
|
||||
<span>{{member.displayName}}</span>
|
||||
<input (change)="onColorChange(member.key)" class="settings__colors--input"
|
||||
id="{{member.key}}" matInput
|
||||
type="color" value="{{settings[member.key]}}">
|
||||
</div>
|
||||
|
||||
<!-- JSON -->
|
||||
<div *ngIf="getMember(group,member).type == settingTypes.JSON" class="settings__grid">
|
||||
<span>{{getMember(group,member).displayName}}</span>
|
||||
<div *ngIf="member.type == settingTypes.JSON" class="settings__grid">
|
||||
<span>{{member.displayName}}</span>
|
||||
<mat-form-field appearance="outline" class="settings__grid--two-columns">
|
||||
<mat-label class="{{member}}">
|
||||
{{getMember(group,member).displayName}}
|
||||
<mat-label class="{{member.key}}">
|
||||
{{member.displayName}}
|
||||
</mat-label>
|
||||
<textarea matInput cdkTextareaAutosize cdkAutosizeMinRows="1" cdkAutosizeMaxRows="10"
|
||||
placeholder="{{getMember(group,member).displayName}}" [(ngModel)]="settings[member]"></textarea>
|
||||
<textarea [(ngModel)]="settings[member.key]" cdkAutosizeMaxRows="10"
|
||||
cdkAutosizeMinRows="1"
|
||||
cdkTextareaAutosize
|
||||
matInput
|
||||
placeholder="{{member.displayName}}"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Settings, SettingsMember, SettingTypes } from '../../models/settings';
|
||||
import { Settings, SettingTypes } from '../../models/settings';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
import { SetSettings } from '../../../shared/store/settings-store/settings.actions';
|
||||
import { SettingsSelectors } from '../../../shared/store/settings-store/settings.selectors';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { validateForm } from './settings.validators';
|
||||
import { validateSettings } from './settings.validators';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-settings',
|
||||
|
@ -17,8 +17,6 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||
settingTypes = SettingTypes;
|
||||
settings: Settings;
|
||||
oldSettings: Settings;
|
||||
groups: string[];
|
||||
members: string[][] = [];
|
||||
invalidMembers: string[] = [];
|
||||
destroy$ = new Subject<void>();
|
||||
|
||||
|
@ -30,7 +28,6 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||
this.settings$.pipe(takeUntil(this.destroy$)).subscribe((settings) => {
|
||||
this.settings = this.deepCopy(settings);
|
||||
this.oldSettings = this.deepCopy(settings);
|
||||
this.getKeysOfSettings();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -38,22 +35,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||
return JSON.parse(JSON.stringify(settings));
|
||||
}
|
||||
|
||||
getKeysOfSettings() {
|
||||
this.groups = Object.keys(this.settings.schema);
|
||||
this.groups.forEach((group) => {
|
||||
let groupMembers = Object.keys(this.settings.schema[group].members);
|
||||
this.members.push(groupMembers);
|
||||
groupMembers.forEach((member) => {
|
||||
if (!(member in this.settings)) {
|
||||
this.notificationService.showWarning('SETTINGS_INVALID_DATA', { setting: member });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSave() {
|
||||
this.changeLabelColor('grey');
|
||||
this.invalidMembers = validateForm(this.members, this.settings, this.groups);
|
||||
this.invalidMembers = validateSettings(this.settings);
|
||||
if (this.invalidMembers.length === 0) {
|
||||
this.store.dispatch(new SetSettings(this.settings)).subscribe(() => {
|
||||
this.notificationService.showSuccess('SETTINGS_SAVE');
|
||||
|
@ -78,12 +62,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||
this.settings = this.deepCopy(this.oldSettings);
|
||||
}
|
||||
|
||||
getMember(group: string, member: string): SettingsMember {
|
||||
return this.settings.schema[group].members[member];
|
||||
}
|
||||
|
||||
onColorChange(member: string) {
|
||||
this.settings[member] = (document.getElementById(member) as HTMLInputElement).value;
|
||||
onColorChange(key: string) {
|
||||
this.settings[key] = (document.getElementById(key) as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { Settings, SettingTypes } from '../../models/settings';
|
||||
|
||||
export const validateForm = (members: string[][], settings: Settings, groups: string[]): string[] => {
|
||||
export const validateSettings = (settings: Settings): string[] => {
|
||||
const invalidMembers = [];
|
||||
|
||||
for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
||||
for (let memberIndex = 0; memberIndex < members[groupIndex].length; memberIndex++) {
|
||||
const memberKey = members[groupIndex][memberIndex];
|
||||
const member = settings.schema[groups[groupIndex]].members[memberKey];
|
||||
const value = settings[memberKey];
|
||||
for (let group of settings.schema) {
|
||||
for (let member of group.members) {
|
||||
const value = settings[member.key];
|
||||
|
||||
if (member.type == SettingTypes.TEXT || member.type == SettingTypes.INTERVAL) {
|
||||
let compareWithMin;
|
||||
|
@ -33,11 +31,11 @@ export const validateForm = (members: string[][], settings: Settings, groups: st
|
|||
}
|
||||
|
||||
if (!isValid) {
|
||||
invalidMembers.push(memberKey);
|
||||
invalidMembers.push(member.key);
|
||||
}
|
||||
|
||||
if (member.type == SettingTypes.INTERVAL && compareWithMin > compareWithMax) {
|
||||
invalidMembers.push(memberKey);
|
||||
invalidMembers.push(member.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +43,7 @@ export const validateForm = (members: string[][], settings: Settings, groups: st
|
|||
try {
|
||||
JSON.parse(value);
|
||||
} catch {
|
||||
invalidMembers.push(memberKey);
|
||||
invalidMembers.push(member.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
export interface SettingsMember {
|
||||
displayName: string;
|
||||
key: string;
|
||||
type: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export interface GroupSetting {
|
||||
displayName: string;
|
||||
members: SettingsMember[];
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
schema: GroupSetting[];
|
||||
|
||||
[setting: string]: any;
|
||||
schema: {
|
||||
[parameterGroup: string]: {
|
||||
displayName: string;
|
||||
members: {
|
||||
[memberName: string]: SettingsMember;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export enum SettingTypes {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Workbasket } from '../../models/workbasket';
|
|||
import { WorkbasketType } from '../../models/workbasket-type';
|
||||
import { ACTION } from '../../models/action';
|
||||
import { WorkbasketAccessItemsRepresentation } from '../../models/workbasket-access-items-representation';
|
||||
import { Settings } from '../../../settings/models/settings';
|
||||
|
||||
export const classificationStateMock = {
|
||||
classifications: [],
|
||||
|
@ -529,66 +528,76 @@ export const settingsStateMock = {
|
|||
colorLowPriority: '#5FAD00',
|
||||
colorMediumPriority: '#FFD700',
|
||||
filter: '{ "Tasks with state READY": { "state": ["READY"]}, "Tasks with state CLAIMED": {"state": ["CLAIMED"] }}',
|
||||
schema: {
|
||||
'Monitor Workbasket-Priority-Report': {
|
||||
schema: [
|
||||
{
|
||||
displayName: 'Priority Report',
|
||||
members: {
|
||||
nameHighPriority: {
|
||||
members: [
|
||||
{
|
||||
key: 'nameHighPriority',
|
||||
displayName: 'High Priority Name',
|
||||
type: 'text',
|
||||
max: 32
|
||||
},
|
||||
nameMediumPriority: {
|
||||
{
|
||||
key: 'nameMediumPriority',
|
||||
displayName: 'Medium Priority Name',
|
||||
type: 'text',
|
||||
min: 0,
|
||||
max: 32
|
||||
},
|
||||
nameLowPriority: {
|
||||
{
|
||||
key: 'nameLowPriority',
|
||||
displayName: 'Low Priority Name',
|
||||
type: 'text',
|
||||
min: 0,
|
||||
max: 32
|
||||
},
|
||||
intervalHighPriority: {
|
||||
{
|
||||
key: 'intervalHighPriority',
|
||||
displayName: 'High Priority Interval',
|
||||
type: 'interval',
|
||||
min: 0
|
||||
},
|
||||
intervalMediumPriority: {
|
||||
{
|
||||
key: 'intervalMediumPriority',
|
||||
displayName: 'Medium Priority Interval',
|
||||
type: 'interval',
|
||||
min: 0
|
||||
},
|
||||
intervalLowPriority: {
|
||||
{
|
||||
key: 'intervalLowPriority',
|
||||
displayName: 'Low Priority Interval',
|
||||
type: 'interval',
|
||||
min: 0
|
||||
},
|
||||
colorHighPriority: {
|
||||
{
|
||||
key: 'colorHighPriority',
|
||||
displayName: 'High Priority Color',
|
||||
type: 'color'
|
||||
},
|
||||
colorMediumPriority: {
|
||||
{
|
||||
key: 'colorMediumPriority',
|
||||
displayName: 'Medium Priority Color',
|
||||
type: 'color'
|
||||
},
|
||||
colorLowPriority: {
|
||||
{
|
||||
key: 'colorLowPriority',
|
||||
displayName: 'Low Priority Color',
|
||||
type: 'color'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'Monitor Workbasket-Priority-Report-Filter': {
|
||||
{
|
||||
displayName: 'Filter for Task-Priority-Report',
|
||||
members: {
|
||||
filter: {
|
||||
members: [
|
||||
{
|
||||
key: 'filter',
|
||||
displayName: 'Filter values',
|
||||
type: 'json',
|
||||
min: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue