@o3r/rules-engine
Version:
This module provides a rule engine that can be executed on your Otter application to customize your application (translations, placeholders and configs) based on a json file generated by your CMS.
298 lines (290 loc) • 173 kB
JavaScript
import * as i0 from '@angular/core';
import { Pipe, input, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, Input, inject, ChangeDetectorRef, NgModule, InjectionToken, Injectable, DestroyRef } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1$1 from '@angular/common';
import { CommonModule, JsonPipe } from '@angular/common';
import { Subject, of, from, ReplaySubject, Observable, debounceTime, combineLatest, BehaviorSubject, merge, firstValueFrom, fromEvent } from 'rxjs';
import { switchMap, delay, startWith, mergeMap, map, catchError, withLatestFrom, concatMap, tap, share, shareReplay, pairwise, distinctUntilChanged, takeUntil, filter, scan } from 'rxjs/operators';
import * as i1$2 from '@ngrx/store';
import { createAction, props, on, createReducer, StoreModule, createFeatureSelector, createSelector, Store, select } from '@ngrx/store';
import { LoggerService, LoggerModule } from '@o3r/logger';
import { asyncProps, fromApiEffectSwitchMap, asyncStoreItemAdapter, computeItemIdentifier, sendOtterMessage, filterMessageContent } from '@o3r/core';
import * as i2 from '@ngrx/effects';
import { Actions, createEffect, ofType, EffectsModule } from '@ngrx/effects';
import { createEntityAdapter } from '@ngrx/entity';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { JSONPath } from 'jsonpath-plus';
class O3rFallbackToPipe {
transform(value, fallback = 'undefined') {
return value === undefined ? fallback : value;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: O3rFallbackToPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.7", ngImport: i0, type: O3rFallbackToPipe, isStandalone: true, name: "o3rFallbackTo" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: O3rFallbackToPipe, decorators: [{
type: Pipe,
args: [{ name: 'o3rFallbackTo' }]
}] });
class O3rJsonOrStringPipe {
/**
* @inheritDoc
*/
transform(value) {
if (typeof value === 'string') {
return value;
}
return JSON.stringify(value, null, 2);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: O3rJsonOrStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.7", ngImport: i0, type: O3rJsonOrStringPipe, isStandalone: true, name: "o3rJsonOrString" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: O3rJsonOrStringPipe, decorators: [{
type: Pipe,
args: [{ name: 'o3rJsonOrString' }]
}] });
/**
* Compute the status of the execution depending on its execution event type, the output and whether the execution
* is still active
* @param rulesetExecution
* @param isActive
*/
const getStatus = (rulesetExecution, isActive) => {
if (rulesetExecution.type === 'RulesetExecutionError') {
return 'Error';
}
else if (rulesetExecution.outputActions?.length === 0) {
return 'NoEffect';
}
else if (isActive) {
return 'Active';
}
return 'Deactivated';
};
/**
* Transform the output of the debug reports into the model for the ruleset history debug panel
* @param events
* @param rulesetMap
*/
const rulesetReportToHistory = (events, rulesetMap) => {
const availableRulesets = Object.values(rulesetMap);
const lastActiveRulesets = (events.filter((e) => e.type === 'ActiveRulesets').reverse()[0])?.rulesets || [];
return availableRulesets
.filter((ruleset) => !!ruleset)
.reduce((acc, ruleset) => {
const rulesetExecutions = events
.filter((e) => ((e.type === 'RulesetExecutionError' || e.type === 'RulesetExecution') && e.rulesetId === ruleset.id));
if (rulesetExecutions) {
acc.push(...rulesetExecutions);
}
return acc;
}, [])
.sort((execA, execB) => execB.timestamp - execA.timestamp)
.map((rulesetExecution) => {
const rulesetInformation = rulesetMap[rulesetExecution.rulesetId];
const isActive = lastActiveRulesets.find((r) => r.id === rulesetExecution.rulesetId);
return {
...rulesetExecution,
status: getStatus(rulesetExecution, !!isActive),
isActive: !!isActive,
rulesetInformation,
rulesEvaluations: (rulesetExecution.rulesEvaluations || []).sort((evalA, evalB) => (rulesetInformation?.rules.findIndex((r) => r.id === evalA.rule.id) || -1)
- (rulesetInformation?.rules.findIndex((r) => r.id === evalB.rule.id) || -1))
};
});
};
class FactsSnapshotComponent {
constructor() {
/**
* Full list of available facts with their current value
*/
this.facts = input.required(...(ngDevMode ? [{ debugName: "facts" }] : []));
/**
* Search terms
*/
this.search = signal('', ...(ngDevMode ? [{ debugName: "search" }] : []));
/**
* Filtered list of facts using search terms
*/
this.filteredFacts = computed(() => {
const search = this.search();
const facts = this.facts();
if (search) {
const matchString = new RegExp(search.replace(/[\s#$()*+,.?[\\\]^{|}-]/g, '\\$&'), 'i');
return facts.filter(({ factName, value }) => matchString.test(factName)
|| (typeof value === 'object'
? matchString.test(JSON.stringify(value))
: matchString.test(String(value))));
}
else {
return facts;
}
}, ...(ngDevMode ? [{ debugName: "filteredFacts" }] : []));
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FactsSnapshotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FactsSnapshotComponent, isStandalone: true, selector: "o3r-facts-snapshot", inputs: { facts: { classPropertyName: "facts", publicName: "facts", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<section>\n @if (facts().length < 1) {\n <div class=\"m-2\">\n No facts are registered.\n </div>\n } @else {\n <div class=\"input-group\">\n <label class=\"input-group-text\" for=\"search-fact\">\n <span class=\"mx-1 fa-search\" aria-label=\"Search\"></span>\n </label>\n <input class=\"form-control\" [(ngModel)]=\"search\" type=\"text\" id=\"search-fact\" placeholder=\"Search for fact name or value\" />\n </div>\n <div class=\"mt-3\">\n List of <b>{{facts().length}}</b> registered facts\n @if (facts().length > filteredFacts().length) { (<b>{{filteredFacts().length}}</b> matching the search) }\n </div>\n <ul>\n @for (fact of filteredFacts(); track fact.factName) {\n <li>\n <span class=\"fact-name\">{{fact.factName}}: </span>\n <span class=\"fact-value\">{{fact.value | o3rJsonOrString}}</span>\n </li>\n }\n </ul>\n }\n</section>\n", styles: ["o3r-facts-snapshot .fact-name{color:#26c}o3r-facts-snapshot .fact-value{color:#c29}o3r-facts-snapshot .fact-name,o3r-facts-snapshot .fact-value{font-family:monospace}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: O3rJsonOrStringPipe, name: "o3rJsonOrString" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FactsSnapshotComponent, decorators: [{
type: Component,
args: [{ selector: 'o3r-facts-snapshot', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
O3rJsonOrStringPipe,
FormsModule,
ReactiveFormsModule
], encapsulation: ViewEncapsulation.None, template: "<section>\n @if (facts().length < 1) {\n <div class=\"m-2\">\n No facts are registered.\n </div>\n } @else {\n <div class=\"input-group\">\n <label class=\"input-group-text\" for=\"search-fact\">\n <span class=\"mx-1 fa-search\" aria-label=\"Search\"></span>\n </label>\n <input class=\"form-control\" [(ngModel)]=\"search\" type=\"text\" id=\"search-fact\" placeholder=\"Search for fact name or value\" />\n </div>\n <div class=\"mt-3\">\n List of <b>{{facts().length}}</b> registered facts\n @if (facts().length > filteredFacts().length) { (<b>{{filteredFacts().length}}</b> matching the search) }\n </div>\n <ul>\n @for (fact of filteredFacts(); track fact.factName) {\n <li>\n <span class=\"fact-name\">{{fact.factName}}: </span>\n <span class=\"fact-value\">{{fact.value | o3rJsonOrString}}</span>\n </li>\n }\n </ul>\n }\n</section>\n", styles: ["o3r-facts-snapshot .fact-name{color:#26c}o3r-facts-snapshot .fact-value{color:#c29}o3r-facts-snapshot .fact-name,o3r-facts-snapshot .fact-value{font-family:monospace}\n"] }]
}], propDecorators: { facts: [{ type: i0.Input, args: [{ isSignal: true, alias: "facts", required: true }] }] } });
/**
* Duration of the notification for clipboard feature (in milliseconds)
*/
const NOTIFICATION_DURATION = 1750;
/**
* Minimal length required to enable clipboard feature
*/
const CLIPBOARD_FEATURE_LENGTH_THRESHOLD = 80;
class RuleKeyValuePresComponent {
constructor() {
/**
* Type of display:
* - 'state': `key: value`, `key: oldValue -> value` or `oldValue -> value`
* - 'assignment': `key = value`
*/
this.type = 'state';
this.shouldLimitCharactersForValue = true;
this.isClipBoardFeatureAvailableForValue = false;
this.isValuePrimitiveType = false;
this.shouldLimitCharactersForOldValue = true;
this.isClipBoardFeatureAvailableForOldValue = false;
this.isOldValuePrimitiveType = false;
this.triggerNotification = new Subject();
this.showNotification$ = this.triggerNotification.asObservable().pipe(switchMap(() => of(false).pipe(delay(NOTIFICATION_DURATION), startWith(true))));
}
isClipBoardFeatureAvailable(value) {
return !!(navigator.clipboard && value && value.length > CLIPBOARD_FEATURE_LENGTH_THRESHOLD);
}
ngOnChanges({ value, oldValue }) {
if (value) {
this.isValuePrimitiveType = value.currentValue === null || typeof value.currentValue !== 'object';
this.isClipBoardFeatureAvailableForValue = this.isClipBoardFeatureAvailable(this.isValuePrimitiveType ? String(value.currentValue) : JSON.stringify(value.currentValue));
}
if (oldValue) {
this.isOldValuePrimitiveType = oldValue.currentValue === null || typeof oldValue.currentValue !== 'object';
this.isClipBoardFeatureAvailableForOldValue = this.isClipBoardFeatureAvailable(this.isOldValuePrimitiveType ? String(oldValue.currentValue) : JSON.stringify(oldValue.currentValue));
}
}
async copyToClipBoard(content) {
await navigator.clipboard.writeText(content);
this.triggerNotification.next();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleKeyValuePresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: RuleKeyValuePresComponent, isStandalone: true, selector: "o3r-rule-key-value-pres", inputs: { key: "key", value: "value", oldValue: "oldValue", type: "type" }, usesOnChanges: true, ngImport: i0, template: "@if (key) {\n <span class=\"input-key\">{{key}}{{type === 'state' ? ':' : ''}}</span>\n}\n{{type === 'assignment' ? '=' : ''}}\n@if (oldValue) {\n <pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForOldValue\"\n (click)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n (keyup.enter)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n tabindex=\"0\"\n >\n {{isOldValuePrimitiveType ? oldValue : (oldValue | json)}}\n </pre>\n @if (isClipBoardFeatureAvailableForOldValue) {\n <button (click)=\"copyToClipBoard(oldValue)\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n }\n \u2192\n}\n<pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForValue\"\n (click)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n (keyup.enter)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n tabindex=\"0\"\n>\n {{isValuePrimitiveType ? value : (value | json)}}\n</pre>\n@if (isClipBoardFeatureAvailableForValue) {\n <button (click)=\"copyToClipBoard(value)\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n}\n@if (showNotification$ | async) {\n <div role=\"alert\" class=\"notification\">Copied to clipboard</div>\n}\n", styles: ["o3r-rule-key-value-pres .ruleset-panel-title,o3r-rule-key-value-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-key-value-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-key-value-pres .ruleset-expansion-action,o3r-rule-key-value-pres .icon-caret-down,o3r-rule-key-value-pres .icon-caret-up{cursor:pointer}o3r-rule-key-value-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-key-value-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-key-value-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-key-value-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-rule-key-value-pres{position:relative}o3r-rule-key-value-pres .limit-characters{display:inline-block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:38rem;vertical-align:bottom}o3r-rule-key-value-pres .input-value{margin-top:0;margin-bottom:0}o3r-rule-key-value-pres button{background:none;border:0;appearance:none}o3r-rule-key-value-pres .notification{position:absolute;padding:1rem 1.5rem;border-radius:5px;background:#444;color:#eee;right:0;z-index:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleKeyValuePresComponent, decorators: [{
type: Component,
args: [{ selector: 'o3r-rule-key-value-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, JsonPipe], template: "@if (key) {\n <span class=\"input-key\">{{key}}{{type === 'state' ? ':' : ''}}</span>\n}\n{{type === 'assignment' ? '=' : ''}}\n@if (oldValue) {\n <pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForOldValue\"\n (click)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n (keyup.enter)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n tabindex=\"0\"\n >\n {{isOldValuePrimitiveType ? oldValue : (oldValue | json)}}\n </pre>\n @if (isClipBoardFeatureAvailableForOldValue) {\n <button (click)=\"copyToClipBoard(oldValue)\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n }\n \u2192\n}\n<pre class=\"input-value\"\n [class.limit-characters]=\"shouldLimitCharactersForValue\"\n (click)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n (keyup.enter)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n tabindex=\"0\"\n>\n {{isValuePrimitiveType ? value : (value | json)}}\n</pre>\n@if (isClipBoardFeatureAvailableForValue) {\n <button (click)=\"copyToClipBoard(value)\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n}\n@if (showNotification$ | async) {\n <div role=\"alert\" class=\"notification\">Copied to clipboard</div>\n}\n", styles: ["o3r-rule-key-value-pres .ruleset-panel-title,o3r-rule-key-value-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-key-value-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-key-value-pres .ruleset-expansion-action,o3r-rule-key-value-pres .icon-caret-down,o3r-rule-key-value-pres .icon-caret-up{cursor:pointer}o3r-rule-key-value-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-key-value-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-key-value-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-key-value-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-rule-key-value-pres{position:relative}o3r-rule-key-value-pres .limit-characters{display:inline-block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:38rem;vertical-align:bottom}o3r-rule-key-value-pres .input-value{margin-top:0;margin-bottom:0}o3r-rule-key-value-pres button{background:none;border:0;appearance:none}o3r-rule-key-value-pres .notification{position:absolute;padding:1rem 1.5rem;border-radius:5px;background:#444;color:#eee;right:0;z-index:1}\n"] }]
}], propDecorators: { key: [{
type: Input
}], value: [{
type: Input
}], oldValue: [{
type: Input
}], type: [{
type: Input
}] } });
class RuleActionsPresComponent {
constructor() {
/**
* List of all the output actions of a rules or ruleset execution
*/
this.actions = [];
/**
* The list of temporary facts used and/or modified within the rule or the ruleset.
* They are scoped to the ruleset and their value is the one after the rule or ruleset execution.
*/
this.temporaryFacts = {};
/**
* List of temporary facts that will be modified by the ruleset or the rule.
*/
this.runtimeOutputs = [];
}
/**
* Check if a given block is of type ActionBlock
* @param block
*/
isActionBlock(block) {
return !!block.actionType;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleActionsPresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: RuleActionsPresComponent, isStandalone: true, selector: "o3r-rule-actions-pres", inputs: { actions: "actions", temporaryFacts: "temporaryFacts", runtimeOutputs: "runtimeOutputs" }, ngImport: i0, template: "<div class=\"ruleset-panel-category-title\">Output Actions</div>\n@if (actions?.length === 0 && runtimeOutputs.length === 0) {\n <div class=\"ruleset-panel-category-body empty\">\n No action\n </div>\n} @else {\n <ul class=\"ruleset-panel-category-body\">\n @for (action of actions; track $index) {\n <li>\n @if (isActionBlock(action)) {\n @switch (action.actionType) {\n @case ('SET_FACT') {\n <div>\n <div>Set Fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.fact | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_CONFIG') {\n <div>\n <div>Update Config {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.property | o3rFallbackTo: 'Missing \\'property\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_ASSET') {\n <div>\n <div>Update Asset:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.asset | o3rFallbackTo: 'Missing \\'asset\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_LOCALISATION') {\n <div>\n <div>Update localization:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.key | o3rFallbackTo: 'Missing \\'key\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_PLACEHOLDER') {\n <div [class.error]=\"!action.placeholderId\">\n <div>Update placeholder in {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.placeholderId | o3rFallbackTo: 'Missing \\'placeholderId\\''\"\n [value]=\"action.value\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @default {\n <div class=\"error\">\n <div>Unrecognized action</div>\n <div>{{action | json}}</div>\n </div>\n }\n }\n }\n </li>\n }\n @for (runtimeOutput of runtimeOutputs; track $index) {\n <li>\n <div>Set temporary fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"runtimeOutput | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"temporaryFacts[runtimeOutput] | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </li>\n }\n </ul>\n}\n", styles: ["o3r-rule-actions-pres .ruleset-panel-title,o3r-rule-actions-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-actions-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-actions-pres .ruleset-expansion-action,o3r-rule-actions-pres .icon-caret-down,o3r-rule-actions-pres .icon-caret-up{cursor:pointer}o3r-rule-actions-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-actions-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-actions-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-actions-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: RuleKeyValuePresComponent, selector: "o3r-rule-key-value-pres", inputs: ["key", "value", "oldValue", "type"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: O3rFallbackToPipe, name: "o3rFallbackTo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleActionsPresComponent, decorators: [{
type: Component,
args: [{ selector: 'o3r-rule-actions-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, JsonPipe, RuleKeyValuePresComponent, O3rFallbackToPipe], template: "<div class=\"ruleset-panel-category-title\">Output Actions</div>\n@if (actions?.length === 0 && runtimeOutputs.length === 0) {\n <div class=\"ruleset-panel-category-body empty\">\n No action\n </div>\n} @else {\n <ul class=\"ruleset-panel-category-body\">\n @for (action of actions; track $index) {\n <li>\n @if (isActionBlock(action)) {\n @switch (action.actionType) {\n @case ('SET_FACT') {\n <div>\n <div>Set Fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.fact | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_CONFIG') {\n <div>\n <div>Update Config {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"action.property | o3rFallbackTo: 'Missing \\'property\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_ASSET') {\n <div>\n <div>Update Asset:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.asset | o3rFallbackTo: 'Missing \\'asset\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_LOCALISATION') {\n <div>\n <div>Update localization:</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.key | o3rFallbackTo: 'Missing \\'key\\''\"\n [value]=\"action.value | o3rFallbackTo\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @case ('UPDATE_PLACEHOLDER') {\n <div [class.error]=\"!action.placeholderId\">\n <div>Update placeholder in {{action.component}} {{action.library}}</div>\n <div>\n <o3r-rule-key-value-pres\n [oldValue]=\"action.placeholderId | o3rFallbackTo: 'Missing \\'placeholderId\\''\"\n [value]=\"action.value\"\n [type]=\"'state'\"></o3r-rule-key-value-pres>\n </div>\n </div>\n }\n @default {\n <div class=\"error\">\n <div>Unrecognized action</div>\n <div>{{action | json}}</div>\n </div>\n }\n }\n }\n </li>\n }\n @for (runtimeOutput of runtimeOutputs; track $index) {\n <li>\n <div>Set temporary fact</div>\n <div>\n <o3r-rule-key-value-pres\n [key]=\"runtimeOutput | o3rFallbackTo: 'Missing \\'fact\\''\"\n [value]=\"temporaryFacts[runtimeOutput] | o3rFallbackTo\"\n [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n </div>\n </li>\n }\n </ul>\n}\n", styles: ["o3r-rule-actions-pres .ruleset-panel-title,o3r-rule-actions-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-actions-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-actions-pres .ruleset-expansion-action,o3r-rule-actions-pres .icon-caret-down,o3r-rule-actions-pres .icon-caret-up{cursor:pointer}o3r-rule-actions-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-actions-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-actions-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-actions-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}\n"] }]
}], propDecorators: { actions: [{
type: Input
}], temporaryFacts: [{
type: Input
}], runtimeOutputs: [{
type: Input
}] } });
class RuleConditionPresComponent {
constructor() {
/**
* Left hand operator as it will be displayed in the template.
* In the case of a fact with a json path, will resolve the whole fact path, else will only display the value
*/
this.lhs = 'undefined';
}
/**
* Rule condition that will be flattened by the component setter
*/
set condition(condition) {
this._condition = condition;
this.lhs = condition?.lhs ? this.getOperandName(condition.lhs) : 'undefined';
this.rhs = condition?.rhs ? this.getOperandName(condition.rhs) : undefined;
}
get condition() {
return this._condition;
}
getOperandName(operand) {
const value = `${operand.value ?? 'MISSING_VALUE'}`;
return operand.path ? operand.path.replace(/^\$/, value) : value;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleConditionPresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: RuleConditionPresComponent, isStandalone: true, selector: "o3r-rule-condition-pres", inputs: { condition: "condition" }, ngImport: i0, template: "@if (!condition) {\n <span class=\"input-value\">true</span>\n} @else {\n @if (!$any(condition).all && !$any(condition).any && !$any(condition).not) {\n <span class=\"input-key\">{{ lhs }}</span> {{ $any(condition).operator }}\n @if (rhs !== undefined) {\n <span class=\"input-value\">{{ rhs }}</span>\n }\n }\n @if ($any(condition).all || $any(condition).any || $any(condition).not) {\n @if ($any(condition).all) {\n <span>ALL</span>\n }\n @if ($any(condition).any) {\n <span>ANY</span>\n }\n @if ($any(condition).not) {\n <span>NOT</span>\n }\n <span>(\n @for (cond of $any(condition).all || $any(condition).any || [$any(condition).not]; track cond; let last = $last) {\n <o3r-rule-condition-pres [condition]=\"cond\"></o3r-rule-condition-pres>\n @if (!last) {\n <span>, </span>\n }\n }\n )</span>\n }\n}\n", styles: ["o3r-rule-condition-pres{word-break:break-word}\n"], dependencies: [{ kind: "component", type: RuleConditionPresComponent, selector: "o3r-rule-condition-pres", inputs: ["condition"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleConditionPresComponent, decorators: [{
type: Component,
args: [{ selector: 'o3r-rule-condition-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [], template: "@if (!condition) {\n <span class=\"input-value\">true</span>\n} @else {\n @if (!$any(condition).all && !$any(condition).any && !$any(condition).not) {\n <span class=\"input-key\">{{ lhs }}</span> {{ $any(condition).operator }}\n @if (rhs !== undefined) {\n <span class=\"input-value\">{{ rhs }}</span>\n }\n }\n @if ($any(condition).all || $any(condition).any || $any(condition).not) {\n @if ($any(condition).all) {\n <span>ALL</span>\n }\n @if ($any(condition).any) {\n <span>ANY</span>\n }\n @if ($any(condition).not) {\n <span>NOT</span>\n }\n <span>(\n @for (cond of $any(condition).all || $any(condition).any || [$any(condition).not]; track cond; let last = $last) {\n <o3r-rule-condition-pres [condition]=\"cond\"></o3r-rule-condition-pres>\n @if (!last) {\n <span>, </span>\n }\n }\n )</span>\n }\n}\n", styles: ["o3r-rule-condition-pres{word-break:break-word}\n"] }]
}], propDecorators: { condition: [{
type: Input
}] } });
class RuleTreePresComponent {
constructor() {
/**
* Type of the block being resolved.
* A type "IF_ELSE" will display two branches and the success and failure outputs associated
* Else, only the successElements will be shown
*/
this.blockType = '';
/**
* If case output
*/
this.successElements = [];
/**
* Else case output
*/
this.failureElements = [];
/**
* Should the "Else case scenario" actions be displayed
*/
this.failureActionsExpanded = false;
/**
* Should the "If case scenario" actions be displayed
*/
this.successActionsExpanded = false;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleTreePresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: RuleTreePresComponent, isStandalone: true, selector: "o3r-rule-tree-pres", inputs: { name: "name", blockType: "blockType", condition: "condition", successElements: "successElements", failureElements: "failureElements" }, ngImport: i0, template: "@if (name) {\n <span>{{name | titlecase}}:</span>\n}\n<div class=\"rule-wrapper tree\">\n @if (blockType === 'IF_ELSE') {\n @if (!name) {\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n }\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If\n <o3r-rule-condition-pres [condition]=\"condition\"></o3r-rule-condition-pres>\n </div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper tree-node\">\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title success-actions\"\n tabindex=\"0\"\n (click)=\"successActionsExpanded = !successActionsExpanded\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\"></i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title error-actions\"\n tabindex=\"0\"\n (click)=\"failureActionsExpanded = !failureActionsExpanded\"\n (keyup.enter)=\"failureActionsExpanded = !failureActionsExpanded\">\n <i class=\"icon refx-icon-cross\"></i>\n <span>Else</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!failureActionsExpanded\"\n [class.icon-caret-up]=\"failureActionsExpanded\">\n </i>\n </div>\n @if (failureActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"failureElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: failureElements}\"></ng-container>\n </div>\n </div>\n } @else {\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If <span class=\"input-value\">true</span></div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper\">\n <div class=\"rule-actions\">\n <div class=\"rule-action-title success-actions\" tabindex=\"0\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\"\n (click)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\">\n </i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n </div>\n }\n</div>\n\n<ng-template #subTree let-blocks=\"blocks\">\n <div class=\"rule-sub-trees\">\n @for (block of blocks; track block) {\n @if (block.blockType === 'IF_ELSE') {\n <div class=\"tree-branch\">\n <o3r-rule-tree-pres\n [blockType]=\"'IF_ELSE'\"\n [condition]=\"block.condition\"\n [failureElements]=\"block.failureElements\"\n [successElements]=\"block.successElements\"></o3r-rule-tree-pres>\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: ["o3r-rule-tree-pres{display:block;padding-bottom:1rem}o3r-rule-tree-pres .rule-sub-trees{display:flex}o3r-rule-tree-pres .rule-conditions-title,o3r-rule-tree-pres .rule-action-title{text-align:center}o3r-rule-tree-pres .rule-conditions-title{border:1px solid #999999;background:#fff;color:#000;padding:.2rem}o3r-rule-tree-pres .rule-action-title{background:#fff;border-radius:0;border:1px solid #999999;cursor:pointer;display:flex;justify-content:center;align-items:center;gap:.5rem}o3r-rule-tree-pres .rule-tree-actions{display:block;padding:.5rem}o3r-rule-tree-pres .rule-actions:first-child>.rule-action-title{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}o3r-rule-tree-pres .rule-actions:last-child>.rule-action-title{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}o3r-rule-tree-pres .rule-actions-wrapper{display:flex}o3r-rule-tree-pres .rule-actions-wrapper .rule-actions{flex:1 1 100%}o3r-rule-tree-pres .success-actions{border-color:#16aa32}o3r-rule-tree-pres .success-actions.rule-action-title{background:#16aa32;color:#fff}o3r-rule-tree-pres .error-actions{border-color:#c02020}o3r-rule-tree-pres .error-actions.rule-action-title{background:#c02020;color:#fff}o3r-rule-tree-pres .tree .tree-leaf,o3r-rule-tree-pres .tree .tree-root{display:flex;width:100%}o3r-rule-tree-pres .tree .tree-leaf>div,o3r-rule-tree-pres .tree .tree-root>div{height:1rem;width:50%}o3r-rule-tree-pres .tree .tree-root div:first-child{border-right:1px dashed}o3r-rule-tree-pres .tree .tree-root{margin-top:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:last-child>.tree-leaf>div:first-child{border-right:1px dashed;border-top:1px dashed;border-top-right-radius:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:first-child>.tree-leaf>div:last-child{border-left:1px dashed;border-top:1px dashed;border-top-left-radius:.2rem}o3r-rule-tree-pres .icon{cursor:pointer}\n"], dependencies: [{ kind: "component", type: RuleTreePresComponent, selector: "o3r-rule-tree-pres", inputs: ["name", "blockType", "condition", "successElements", "failureElements"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: RuleActionsPresComponent, selector: "o3r-rule-actions-pres", inputs: ["actions", "temporaryFacts", "runtimeOutputs"] }, { kind: "component", type: RuleConditionPresComponent, selector: "o3r-rule-condition-pres", inputs: ["condition"] }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: RuleTreePresComponent, decorators: [{
type: Component,
args: [{ selector: 'o3r-rule-tree-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, RuleActionsPresComponent, RuleConditionPresComponent], template: "@if (name) {\n <span>{{name | titlecase}}:</span>\n}\n<div class=\"rule-wrapper tree\">\n @if (blockType === 'IF_ELSE') {\n @if (!name) {\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n }\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If\n <o3r-rule-condition-pres [condition]=\"condition\"></o3r-rule-condition-pres>\n </div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper tree-node\">\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title success-actions\"\n tabindex=\"0\"\n (click)=\"successActionsExpanded = !successActionsExpanded\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\"></i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n <div class=\"rule-actions tree-branch\">\n <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n <div class=\"rule-action-title error-actions\"\n tabindex=\"0\"\n (click)=\"failureActionsExpanded = !failureActionsExpanded\"\n (keyup.enter)=\"failureActionsExpanded = !failureActionsExpanded\">\n <i class=\"icon refx-icon-cross\"></i>\n <span>Else</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!failureActionsExpanded\"\n [class.icon-caret-up]=\"failureActionsExpanded\">\n </i>\n </div>\n @if (failureActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"failureElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: failureElements}\"></ng-container>\n </div>\n </div>\n } @else {\n <div class=\"rule-conditions\">\n <div class=\"rule-conditions-title\">If <span class=\"input-value\">true</span></div>\n <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n <div></div>\n <div></div>\n </div>\n </div>\n <div class=\"rule-actions-wrapper\">\n <div class=\"rule-actions\">\n <div class=\"rule-action-title success-actions\" tabindex=\"0\"\n (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\"\n (click)=\"successActionsExpanded = !successActionsExpanded\">\n <i class=\"icon refx-icon-validate\">\n </i>\n <span>Then</span>\n <i class=\"icon\"\n [class.icon-caret-down]=\"!successActionsExpanded\"\n [class.icon-caret-up]=\"successActionsExpanded\">\n </i>\n </div>\n @if (successActionsExpanded) {\n <o3r-rule-actions-pres class=\"rule-tree-actions\"\n [actions]=\"successElements\">\n </o3r-rule-actions-pres>\n }\n <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n </div>\n </div>\n }\n</div>\n\n<ng-template #subTree let-blocks=\"blocks\">\n <div class=\"rule-sub-trees\">\n @for (block of blocks; track block) {\n @if (block.blockType === 'IF_ELSE') {\n <div class=\"tree-branch\">\n <o3r-rule-tree-pres\n [blockType]=\"'IF_ELSE'\"\n [condition]=\"block.condition\"\n [failureElements]=\"block.failureElements\"\n [successElements]=\"block.successElements\"></o3r-rule-tree-pres>\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: ["o3r-rule-tree-pres{display:block;padding-bottom:1rem}o3r-rule-tree-pres .rule-sub-trees{display:flex}o3r-rule-tree-pres .rule-conditions-title,o3r-rule-tree-pres .rule-action-title{text-align:center}o3r-rule-tree-pres .rule-conditions-title{border:1px solid #999999;background:#fff;color:#000;padding:.2rem}o3r-rule-tree-pres .rule-action-title{background:#fff;border-radius:0;border:1px solid #999999;cursor:pointer;display:flex;justify-content:center;align-items:center;gap:.5rem}o3r-rule-tree-pres .rule-tree-actions{display:block;padding:.5rem}o3r-rule-tree-pres .rule-actions:first-child>.rule-action-title{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}o3r-rule-tree-pres .rule-actions:last-child>.rule-action-title{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}o3r-rule-tree-pres .rule-actions-wrapper{display:flex}o3r-rule-tree-pres .rule-actions-wrapper .rule-actions{flex:1 1 100%}o3r-rule-tree-pres .success-actions{border-color:#16aa32}o3r-rule-tree-pres .success-actions.rule-action-title{background:#16aa32;color:#fff}o3r-rule-tree-pres .error-actions{border-color:#c02020}o3r-rule-tree-pres .error-actions.rule-action-title{background:#c02020;color:#fff}o3r-rule-tree-pres .tree .tree-leaf,o3r-rule-tree-pres .tree .tree-root{display:flex;width:100%}o3r-rule-tree-pres .tree .tree-leaf>div,o3r-rule-tree-pres .tree .tree-root>div{height:1rem;width:50%}o3r-rule-tree-pres .tree .tree-root div:first-child{border-right:1px dashed}o3r-rule-tree-pres .tree .tree-root{margin-top:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:last-child>.tree-leaf>div:first-child{border-right:1px dashed;border-top:1px dashed;border-top-right-radius:.2rem}o3r-rule-tree-pres .tree .tree-node>.t