TSK-1750: configuration schema now respects order of individual fields

This commit is contained in:
Mustapha Zorgati 2021-10-08 13:00:53 +02:00
parent 3398e81a7c
commit 331bf2998b
6 changed files with 115 additions and 107 deletions

View File

@ -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
}
}
]
}
}
]
}

View File

@ -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>

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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 {

View File

@ -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
}
}
]
}
}
]
}
};