import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { BaseApplicationForLogic, FormDefinitionComponent, ValueLogicResult } from '@features/configure-forms/form.typing'; import { FormBuilderService } from '@features/formio/form-builder/services/form-builder/form-builder.service'; import { FormHelperService } from '@features/formio/services/form-helper/form-helper.service'; import { FormLogicService } from '@features/formio/services/form-logic/form-logic.service'; import { LogicBuilderService } from '@features/logic-builder/logic-builder.service'; import { EvaluationType, GlobalValueLogicGroup, LogicColumnDisplay, LogicValueFormatType } from '@features/logic-builder/logic-builder.typing'; import { ValueLogicBuilderModalComponent } from '@features/logic-builder/value-logic-builder-modal/value-logic-builder-modal.component'; import { SelectOption, TypeaheadSelectOption } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { ModalFactory } from '@yourcause/common/modals'; @Component({ selector: 'gc-conditional-value-form-builder-settings', templateUrl: './conditional-value-form-builder-settings.component.html', styleUrls: ['./conditional-value-form-builder-settings.component.scss'] }) export class ConditionalValueFormBuilderSettingsComponent implements OnInit { @Input() logicGroups: GlobalValueLogicGroup>[]; @Input() component: FormDefinitionComponent; @Output() onModalOpenOrClose = new EventEmitter(); @Output() onChange = new EventEmitter>[] >(); availableColumns: LogicColumnDisplay[]; noRulesMessage = this.i18n.translate( 'common:textNoValueLogicRulesHelp', {}, `Click "Add new rule" below to create conditional logic to assign this component a value.` ); newFieldMessage = this.i18n.translate( 'common:textSaveBeforeSetValueLogic', {}, 'You must save this component before set value logic can be added.' ); options: (TypeaheadSelectOption|SelectOption)[] = []; logicValueFormatType: LogicValueFormatType; ready = false; isNewField = false; logicEnabled: boolean; EvaluationType = EvaluationType; sourceColumn: LogicColumnDisplay; constructor ( private i18n: I18nService, private modalFactory: ModalFactory, private formLogicService: FormLogicService, private logicBuilderService: LogicBuilderService, private formHelperService: FormHelperService, private formBuilderService: FormBuilderService ) { } get rules () { return this.logicGroups || []; } async ngOnInit () { const hasSetValueRules = (this.component.conditionalValue?.length ?? 0) > 0; const hasCalculatedValue = !!this.component?.formula?.step; this.logicEnabled = !hasCalculatedValue || // this is an edge case, but if both are somehow set (bug 1872663), allow them to change this one (hasSetValueRules && hasCalculatedValue); if (!this.logicGroups) { this.logicGroups = []; this.onChange.emit(this.logicGroups); } const { availableColumns, sourceColumn } = this.formLogicService.getAvailableLogicColumnsForComponent( this.component, true, this.formBuilderService.currentFormBuilderDefinition, this.formBuilderService.currentFormBuilderIndex, this.formBuilderService.currentFormBuilderFormAudience, false ); this.sourceColumn = sourceColumn; this.availableColumns = availableColumns; this.setIsNewField(); await this.getOptionsAndFormatValueType(); this.ready = true; } /** * Will be new field if editComponent.type includes the prefix "referenceFieldsType-". * Not sure if there's another way to tell **/ setIsNewField () { this.isNewField = this.component?.type?.startsWith('referenceFieldsType-'); } async getOptionsAndFormatValueType () { const { options, logicValueFormatType } = await this.formHelperService.getOptionsAndFormatValueType( this.component ); this.options = options; this.logicValueFormatType = logicValueFormatType; } moveUpOrDown ( index: number, isUp: boolean ) { const otherIndex = isUp ? index - 1 : index + 1; const temp1 = this.rules[index]; const temp2 = this.rules[otherIndex]; this.logicGroups[index] = temp2; this.logicGroups[otherIndex] = temp1; this.emitValueLogic(); } async openLogicBuilderModal ( rule?: GlobalValueLogicGroup>, index?: number ) { this.onModalOpenOrClose.emit(true); const isNewLogic = !rule; if (isNewLogic) { rule = this.logicBuilderService.getDefaultConditionalValueLogic< BaseApplicationForLogic, ValueLogicResult >(this.logicValueFormatType); } const alwaysTrueAvailable = this.logicGroups.every((condition) => { return condition.evaluationType !== EvaluationType.AlwaysTrue; }) || rule.evaluationType === EvaluationType.AlwaysTrue; const response = await this.modalFactory.open< ValueLogicBuilderModalComponent >( ValueLogicBuilderModalComponent, { builderName: this.i18n.translate( 'FORMS:hdrManageConditionalLogic', {}, 'Manage Conditional Logic' ), formId: this.formBuilderService.currentFormBuilderFormId, component: this.component, options: this.options, logicValueFormatType: this.logicValueFormatType, currentColumnName: this.component.label, availableColumns: this.availableColumns, logic: rule, alwaysTrueAvailable, sourceColumn: this.sourceColumn } ); this.onModalOpenOrClose.emit(false); if (isNewLogic && response?.delete) { return; // this is essentially cancel } if (response) { if (response.delete) { this.handleDeleteRule(index); } else { let newData: GlobalValueLogicGroup>[] = []; if (isNewLogic) { // if new we can add to the bottom of the list newData = [ ...this.logicGroups, { ...response } ]; } else { // otherwise, update the list with new data newData = [ ...this.logicGroups.slice(0, index), { ...response }, ...this.logicGroups.slice(index + 1) ]; } const alwaysTrueRuleArray: GlobalValueLogicGroup>[] = []; // remove the always true rule and push it to it's own array const conditionalRules = newData.filter((r) => { if (r.evaluationType === EvaluationType.AlwaysTrue) { alwaysTrueRuleArray.push(r); return false; } else { return true; } }); // spread arays so always true rules are on the bottom (evaluated last) this.logicGroups = [ ...conditionalRules, ...alwaysTrueRuleArray ]; } this.emitValueLogic(); } } handleDeleteRule (index: number) { this.logicGroups = [ ...this.logicGroups.slice(0, index), ...this.logicGroups.slice(index + 1) ]; } emitValueLogic () { this.onChange.emit(this.logicGroups); } }