summaryrefslogtreecommitdiffstats
path: root/public/src/app/rule-engine/action-list
diff options
context:
space:
mode:
Diffstat (limited to 'public/src/app/rule-engine/action-list')
-rw-r--r--public/src/app/rule-engine/action-list/action-list.component.html100
-rw-r--r--public/src/app/rule-engine/action-list/action-list.component.scss77
-rw-r--r--public/src/app/rule-engine/action-list/action-list.component.ts290
3 files changed, 467 insertions, 0 deletions
diff --git a/public/src/app/rule-engine/action-list/action-list.component.html b/public/src/app/rule-engine/action-list/action-list.component.html
new file mode 100644
index 0000000..e7879b7
--- /dev/null
+++ b/public/src/app/rule-engine/action-list/action-list.component.html
@@ -0,0 +1,100 @@
+<form #actionListFrm="ngForm" class="wrapper" data-tests-id="popupRuleEditor">
+ <div class="header">
+ <div style="display: flex; justify-content: flex-end; align-items: center;">
+ <a (click)="closeDialog()" data-tests-id="btnBackRule" style="cursor: pointer;text-decoration: none; color: #009fdb;">
+ <mat-icon fontSet="fontawesome" fontIcon="fa-angle-left" style="height: 22px; width: 22px; font-size: 22px; padding-right: 20px;"></mat-icon>
+ </a>
+ <span style="font-size: 18px;">New Rule Editor</span>
+ </div>
+
+ <div style="display: flex; justify-content: flex-end; align-items: center; padding: 10px;">
+
+ <button mat-icon-button [disabled]="actions.length === 0" (click)="saveRole()" data-tests-id="btnSave">
+ <span style="width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;" [innerHTML]="'save' | feather:22"></span>
+ </button>
+
+ <button mat-raised-button [disabled]="actions.length === 0" style="height: 35px; margin-left: 20px;" color="primary" data-tests-id="btnDone"
+ (click)="saveAndDone()">
+ Done
+ </button>
+ </div>
+ </div>
+ <!-- error container -->
+ <div *ngIf="error" data-tests-id="errorList" class="error-list">
+ <div *ngFor="let item of error">
+ {{ item }}
+ </div>
+ </div>
+
+ <div class="main-content">
+ <div>
+ <div class="required" style="padding-right: 1rem; width: 100%; padding-bottom: 0.5rem;">Description</div>
+ <input type="text" [(ngModel)]="description" ngModel required name="descInput" style="padding: 5px; width: 100%;" data-tests-id="inputDescription">
+ </div>
+
+ <div style="margin: 1.5rem 0;">
+ <div class="pretty p-svg" style="margin: 1rem 0rem;">
+ <input type="checkbox" name="isCondition" data-tests-id="isCondition" [checked]="ifStatement" (change)="ifStatement = !ifStatement"
+ />
+ <div class="state">
+ <!-- svg path -->
+ <svg class="svg svg-icon" viewBox="0 0 20 20">
+ <path d="M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z"
+ style="stroke: #009fdb; fill:#009fdb;"></path>
+ </svg>
+ <label>Conditional Action</label>
+ </div>
+ </div>
+
+ <div *ngIf="ifStatement">
+ <app-condition #condition (removeConditionCheck)="removeConditionCheck($event)" (onConditionChange)="updateCondition($event)"></app-condition>
+ </div>
+ </div>
+
+ <div>
+ <div class="required" style="padding-bottom: 0.5rem">
+ Action
+ </div>
+ <div style="display: flex;">
+ <select [(ngModel)]="selectedAction" name="selectedAction" style="height: 2rem; width: 150px; margin-right: 1rem;" data-tests-id="selectAction">
+ <option [ngValue]="null" disabled>Select Action</option>
+ <option value="copy">Copy</option>
+ <option value="concat">Concat</option>
+ <option value="map">Map</option>
+ <option value="date formatter">Date Formatter</option>
+ </select>
+
+ <div style="display: flex; align-items: center;">
+ <button mat-mini-fab color="primary" style="height: 24px; width: 24px; display:flex; justify-content: center;" (click)="addAction2list(selectedAction)"
+ data-tests-id="btnAddAction">
+ <span style="display: flex; justify-content: center; align-items: center" [innerHTML]="'plus' | feather:16"></span>
+ </button>
+ <span style="color: #009FDB; display: flex; justify-content: center; padding-left: 10px">Add Action</span>
+ </div>
+
+ </div>
+
+ <div>
+ <ul>
+ <li *ngFor="let action of actions; let index = index" style="list-style: none; margin: 1rem 0;" (mouseleave)="hoveredIndex=-1"
+ (mouseover)="hoveredIndex=index" data-tests-id="action">
+ <div style="display:flex;">
+ <app-action #actions style="width: 100%;" [action]="action"></app-action>
+
+ <div style="height: 45px; display: flex; align-items: center;">
+ <button mat-icon-button class='button-remove' (click)="removeAction(action)" data-tests-id="deleteAction">
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ </div>
+ </div>
+</form>
diff --git a/public/src/app/rule-engine/action-list/action-list.component.scss b/public/src/app/rule-engine/action-list/action-list.component.scss
new file mode 100644
index 0000000..39b9dce
--- /dev/null
+++ b/public/src/app/rule-engine/action-list/action-list.component.scss
@@ -0,0 +1,77 @@
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: #191919;
+ border-bottom: 2px solid #d2d2d2;
+ // padding: 0.4rem 1rem;
+ }
+
+ .main-content {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ flex-grow: 1;
+ // overflow-y: auto;
+ padding: 24px 10px;
+ width: 100%;
+ // height: calc(100vh - 54px);
+ }
+}
+
+.mat-fab,
+.mat-mini-fab,
+.mat-raised-button {
+ box-shadow: none;
+}
+
+.button-remove {
+ display: flex;
+ justify-content: center;
+ padding-top: 5px;
+ color: #a7a7a7;
+ &:hover {
+ color: #009fdb;
+ }
+}
+
+:host {
+ @mixin md-icon-size($size: 24px) {
+ // font-size: $size;
+ height: $size;
+ width: $size;
+ }
+
+ .material-icons.mat-icon {
+ @include md-icon-size(24px);
+ }
+ /deep/ .mat-button-wrapper {
+ padding: 0;
+ }
+ .mat-icon {
+ width: 18px;
+ height: 18px;
+ }
+}
+
+.black {
+ color: black;
+}
+.highlight {
+ color: #009fdb;
+}
+
+.error-list {
+ margin: 10px;
+ color: white;
+ background: red;
+ padding: 1rem;
+ border-radius: 5px;
+ font-weight: bold;
+}
diff --git a/public/src/app/rule-engine/action-list/action-list.component.ts b/public/src/app/rule-engine/action-list/action-list.component.ts
new file mode 100644
index 0000000..40ff46d
--- /dev/null
+++ b/public/src/app/rule-engine/action-list/action-list.component.ts
@@ -0,0 +1,290 @@
+import {
+ Component,
+ Inject,
+ ViewChildren,
+ QueryList,
+ AfterViewInit,
+ ViewChild,
+ Input
+} from '@angular/core';
+import { RuleEngineApiService } from '../api/rule-engine-api.service';
+import { Subject } from 'rxjs/Subject';
+import { v1 as uuid } from 'uuid';
+import { environment } from '../../../environments/environment';
+import { ActionComponent } from '../action/action.component';
+import { cloneDeep } from 'lodash';
+import { Store } from '../../store/store';
+import { NgForm } from '@angular/forms';
+
+@Component({
+ selector: 'app-action-list',
+ templateUrl: './action-list.component.html',
+ styleUrls: ['./action-list.component.scss']
+})
+export class ActionListComponent implements AfterViewInit {
+ error: Array<string>;
+ condition: any;
+ eventType: string;
+ version: string;
+ params;
+ selectedAction;
+ targetSource;
+ description = '';
+ actions = new Array();
+ ifStatement = false;
+ uid = '';
+ backupActionForCancel = new Array();
+ @ViewChild('actionListFrm') actionListFrm: NgForm;
+ @ViewChild('condition') conditionRef;
+ @ViewChildren('actions') actionsRef: QueryList<ActionComponent>;
+
+ constructor(private _ruleApi: RuleEngineApiService, public store: Store) {
+ this._ruleApi.editorData.subscribe(data => {
+ this.params = data.params;
+ console.log('update.. params', data.params);
+ this.targetSource = data.targetSource;
+ this.version = data.version;
+ this.eventType = data.eventType;
+ if (data.item) {
+ // edit mode set values to attributes
+ console.log('actions %o', data.item.actions);
+ this.actions = this.convertActionDataFromServer(data.item.actions);
+ this.backupActionForCancel = cloneDeep(this.actions);
+ this.condition = data.item.condition;
+ this.uid = data.item.uid;
+ this.description = data.item.description;
+ this.ifStatement = this.condition == null ? false : true;
+ } else {
+ this.actions = new Array();
+ this.backupActionForCancel = new Array();
+ this.condition = null;
+ this.uid = '';
+ this.description = '';
+ this.ifStatement = false;
+ }
+ this.selectedAction = null;
+ });
+ }
+
+ convertActionDataFromServer(actions) {
+ return actions.map(item => {
+ if (!item.hasOwnProperty('nodes')) {
+ return Object.assign({}, item, { nodes: this.targetSource });
+ }
+ });
+ }
+
+ ngAfterViewInit() {
+ // console.log(this.actionsRef.toArray());
+ if (this.condition) {
+ if (this.condition.name === 'condition') {
+ this.conditionRef.updateMode(true, this.condition);
+ } else {
+ const convertedCondition = this.convertConditionFromServer(
+ this.condition
+ );
+ this.conditionRef.updateMode(false, convertedCondition);
+ }
+ }
+ }
+
+ addAction2list(selectedAction) {
+ if (selectedAction !== null) {
+ this.actions.push({
+ id: uuid(),
+ nodes: this.targetSource,
+ from: {
+ value: '',
+ regex: '',
+ state: 'closed',
+ values: [{ value: '' }, { value: '' }]
+ },
+ actionType: this.selectedAction,
+ target: '',
+ map: {
+ values: [{ key: '', value: '' }],
+ haveDefault: false,
+ default: ''
+ },
+ dateFormatter: {
+ fromFormat: '',
+ toFormat: '',
+ fromTimezone: '',
+ toTimezone: ''
+ }
+ });
+ }
+ }
+
+ removeConditionCheck(flag) {
+ this.ifStatement = flag;
+ }
+
+ removeAction(action) {
+ this.actions = this.actions.filter(item => {
+ return item.id !== action.id;
+ });
+ }
+
+ updateCondition(data) {
+ this.condition = data;
+ }
+
+ changeRightToArrayOrString(data, toArray) {
+ data.forEach(element => {
+ if (element.name === 'operator') {
+ this.changeRightToArrayOrString(element.children, toArray);
+ }
+ if (element.name === 'condition') {
+ if (toArray) {
+ element.right = element.right.split(',');
+ } else {
+ element.right = element.right.join(',');
+ }
+ }
+ });
+ console.log(data);
+ return data;
+ }
+
+ prepareDataToSaveRule() {
+ // action array
+ console.log(this.actions);
+ const actionSetData = this.actions.map(item => {
+ return {
+ id: item.id,
+ actionType: item.actionType,
+ from: item.from,
+ target:
+ typeof item.selectedNode === 'string'
+ ? item.selectedNode
+ : typeof item.selectedNode === 'undefined'
+ ? item.target
+ : item.selectedNode.id,
+ map: item.map,
+ dateFormatter: item.dateFormatter
+ };
+ });
+ let conditionData2server = null;
+ if (this.ifStatement) {
+ if (this.conditionRef.conditionTree) {
+ // change condition right to array
+ conditionData2server = this.convertConditionToServer(
+ this.conditionRef.conditionTree
+ );
+ }
+ }
+ // data structure
+ return {
+ version: this.version,
+ eventType: this.eventType,
+ uid: this.uid,
+ description: this.description,
+ actions: actionSetData,
+ condition: this.ifStatement ? conditionData2server : null
+ };
+ }
+
+ errorHandler(error) {
+ this.store.loader = false;
+ console.log(error);
+ this.error = [];
+ if (typeof error === 'string') {
+ this.error.push(error);
+ } else {
+ console.log(error.notes);
+ const errorFromServer = Object.values(error)[0] as any;
+ if (Object.keys(error)[0] === 'serviceExceptions') {
+ this.error = errorFromServer.map(x => x.formattedErrorMessage);
+ } else {
+ this.error.push(errorFromServer.formattedErrorMessage);
+ }
+ }
+ }
+
+ saveAndDone() {
+ const data = this.prepareDataToSaveRule();
+ this.store.loader = true;
+ this._ruleApi.modifyRule(data).subscribe(
+ response => {
+ this.store.loader = false;
+ this.store.updateRuleInList(response);
+ this._ruleApi.callUpdateVersionLock();
+ this.store.isLeftVisible = true;
+ },
+ error => {
+ this.errorHandler(error);
+ },
+ () => {
+ this.store.loader = false;
+ }
+ );
+ }
+
+ saveRole() {
+ const actionComp = this.actionsRef.toArray();
+ const filterInvalidActions = actionComp.filter(comp => {
+ return (
+ comp.fromInstance.fromFrm.invalid ||
+ comp.targetInstance.targetFrm.invalid ||
+ comp.actionFrm.invalid
+ );
+ });
+ if (this.actionListFrm.valid && filterInvalidActions.length === 0) {
+ const data = this.prepareDataToSaveRule();
+ this.store.loader = true;
+ this._ruleApi.modifyRule(data).subscribe(
+ response => {
+ this.store.loader = false;
+ this.store.updateRuleInList(response);
+ this._ruleApi.callUpdateVersionLock();
+ this.uid = response.uid;
+ // add toast notification
+ },
+ error => {
+ this.errorHandler(error);
+ },
+ () => {
+ this.store.loader = false;
+ }
+ );
+ } else {
+ // scroll to first invalid element
+ const elId = filterInvalidActions[0].action.id;
+ const el = document.getElementById(elId as string);
+ const label = el.children.item(0) as HTMLElement;
+ el.scrollIntoView();
+ }
+ }
+
+ public convertConditionFromServer(condition) {
+ const temp = new Array();
+ temp.push(condition);
+ const cloneCondition = cloneDeep(temp);
+ const conditionSetData = this.changeRightToArrayOrString(
+ cloneCondition,
+ false
+ );
+ console.log('condition to server:', conditionSetData);
+ return conditionSetData;
+ }
+
+ public convertConditionToServer(tree) {
+ const cloneCondition = cloneDeep(tree);
+ const conditionSetData = this.changeRightToArrayOrString(
+ cloneCondition,
+ true
+ );
+ let simpleCondition = null;
+ if (conditionSetData[0].children.length === 1) {
+ simpleCondition = conditionSetData[0].children;
+ }
+ console.log('condition to server:', conditionSetData);
+ return simpleCondition !== null ? simpleCondition[0] : conditionSetData[0];
+ }
+
+ closeDialog(): void {
+ this.actions = this.backupActionForCancel;
+ this.store.isLeftVisible = true;
+ }
+}