File: //home/cafsindia/lead_cafsinfotech.com/core/app/core/src/lib/fields/base/base-enum.component.ts
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
import {BaseFieldComponent} from './base-field.component';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Subscription} from 'rxjs';
import {Field, FieldDefinition, Option} from '../../common/record/field.model';
import {isVoid, isEmptyString} from '../../common/utils/value-utils';
import {DataTypeFormatter} from '../../services/formatters/data-type.formatter.service';
import {
LanguageListStringMap,
LanguageStore,
LanguageStringMap,
LanguageStrings
} from '../../store/language/language.store';
import {FieldLogicManager} from '../field-logic/field-logic.manager';
import {FieldLogicDisplayManager} from '../field-logic-display/field-logic-display.manager';
import {isNull, isObject} from "lodash-es";
@Component({template: ''})
export class BaseEnumComponent extends BaseFieldComponent implements OnInit, OnDestroy {
selectedValues: Option[] = [];
valueLabel = '';
optionsMap: LanguageStringMap;
options: Option[] = [];
labels: LanguageStringMap;
protected subs: Subscription[] = [];
protected mappedOptions: { [key: string]: Option[] };
protected isDynamicEnum = false;
constructor(
protected languages: LanguageStore,
protected typeFormatter: DataTypeFormatter,
protected logic: FieldLogicManager,
protected logicDisplay: FieldLogicDisplayManager
) {
super(typeFormatter, logic, logicDisplay);
}
ngOnInit(): void {
super.ngOnInit();
const options$ = this?.field?.metadata?.options$ ?? null;
if (options$) {
this.subs.push(this.field.metadata.options$.subscribe((options: Option[]) => {
this.buildProvidedOptions(options);
this.initValue();
}));
return;
}
const options = this?.field?.definition?.options ?? null;
if (options) {
this.subs.push(this.languages.vm$.subscribe((strings: LanguageStrings) => {
this.buildAppStringListOptions(strings.appListStrings);
this.initValue();
}));
}
if (!options && !options$) {
this.initValue();
}
}
ngOnDestroy(): void {
this.isDynamicEnum = false;
this.subs.forEach(sub => sub.unsubscribe());
this.options = [];
this.optionsMap = {};
this.selectedValues = [];
}
getInvalidClass(): string {
if (this.validateOnlyOnSubmit ? this.isInvalid() : (this.field.formControl.invalid && this.field.formControl.touched)) {
return 'is-invalid';
}
return '';
}
protected buildProvidedOptions(options: Option[]): void {
this.options = options;
this.optionsMap = {};
options.forEach(option => {
this.optionsMap[option.value] = option.label;
});
}
protected buildAppStringListOptions(appStrings: LanguageListStringMap): void {
this.optionsMap = {} as LanguageStringMap;
this.addExtraOptions();
if (appStrings && this.field.definition.options && appStrings[this.field.definition.options]) {
const options = appStrings[this.field.definition.options] as LanguageStringMap;
if (this.options && Object.keys(this.options)) {
this.optionsMap = {...this.optionsMap, ...options};
}
}
this.buildOptionsArray(appStrings);
}
protected addExtraOptions(): void {
const extraOptions = (this.field.metadata && this.field.metadata.extraOptions) || [];
extraOptions.forEach((item: Option) => {
if (isVoid(item.value)) {
return;
}
let label = item.label || '';
if (item.labelKey) {
label = this.languages.getFieldLabel(item.labelKey);
}
this.optionsMap[item.value] = label;
});
}
protected buildOptionsArray(appStrings: LanguageListStringMap): void {
this.options = [];
Object.keys(this.optionsMap).forEach(key => {
const isOptionEmpty = isEmptyString(this.optionsMap[key]);
if (isOptionEmpty && this.isSkipEmptyMode()) {
return;
}
if (isOptionEmpty && !this.addEmptyStringOption()){
return;
}
this.options.push({
value: key,
label: this.optionsMap[key]
} as Option);
});
if (this.isDynamicEnum) {
this.buildDynamicEnumOptions(appStrings);
}
}
protected addEmptyStringOption(): boolean {
return this.field.type !== 'multienum';
}
protected isSkipEmptyMode(): boolean {
return this.mode === 'massupdate' || this.mode === 'filter';
}
protected initValue(): void {
this.selectedValues = [];
if (this.field.criteria) {
this.initValueLabel();
return;
}
if (typeof this.field.value !== 'string') {
return;
}
if (!this.optionsMap) {
return;
}
if (typeof this.optionsMap[this.field.value] !== 'string') {
return;
}
this.initValueLabel();
}
protected initValueLabel() {
const fieldValue = this.field.value || this.field.criteria?.target || undefined;
if (fieldValue !== undefined) {
this.valueLabel = this.optionsMap[fieldValue];
this.selectedValues.push({
value: fieldValue,
label: this.valueLabel
} as Option);
}
}
/**
* Initialize the default value for the enum
*
* @returns {void}
* @description set default enum value, if defined in vardefs
* */
protected initEnumDefault(): void {
if (!isEmptyString(this.record?.id)) {
this.field?.formControl.setValue('');
return;
}
let defaultVal = this?.field?.default ?? this?.field?.definition?.default ?? null;
if (typeof defaultVal === 'string') {
defaultVal = defaultVal.trim();
}
if (!defaultVal) {
this.field.formControl.setValue('');
return;
}
this.selectedValues.push({
value: defaultVal,
label: this.optionsMap[defaultVal]
} as Option);
this.initEnumDefaultFieldValues(defaultVal);
}
protected initEnumDefaultFieldValues(defaultVal: string): void {
if (this.field.type === 'multienum') {
const defaultValues = this.selectedValues.map(option => option.value);
this.field.valueList = defaultValues;
this.field.formControl.setValue(defaultValues);
} else {
this.field.value = defaultVal;
this.field.formControl.setValue(defaultVal);
}
this.field.formControl.markAsDirty();
}
protected checkAndInitAsDynamicEnum(): void {
const definition = (this.field && this.field.definition) || {} as FieldDefinition;
const dynamic = (definition && definition.dynamic) || false;
const parentEnumKey = (definition && definition.parentenum) || '';
const fields = (this.record && this.record.fields) || null;
if (dynamic && parentEnumKey && fields) {
this.isDynamicEnum = true;
const parentEnum: Field = fields[parentEnumKey];
if (parentEnum) {
this.subscribeToParentValueChanges(parentEnum);
}
}
}
protected buildDynamicEnumOptions(appStrings: LanguageListStringMap): void {
const parentEnum = this.record.fields[this.field.definition.parentenum];
if (parentEnum) {
const parentOptionMap: LanguageStringMap = appStrings[parentEnum.definition.options] as LanguageStringMap;
if (parentOptionMap && Object.keys(parentOptionMap).length !== 0) {
this.mappedOptions = this.createParentChildOptionsMap(parentOptionMap, this.options);
let parentValues: string[] = [];
if (parentEnum.definition.type === 'multienum') {
parentValues = parentEnum.valueList;
} else {
parentValues.push(parentEnum.value);
}
this.options = this.filterMatchingOptions(parentValues);
if (parentValues && parentValues.length) {
this.setValueToAvailableOption();
}
}
}
}
protected filterMatchingOptions(values: string[]): Option[] {
let filteredOptions: Option[] = [];
if (!values || !values.length) {
return [];
}
values.forEach(value => {
if (!this.mappedOptions[value]) {
return;
}
filteredOptions = filteredOptions.concat([...this.mappedOptions[value]]);
});
return filteredOptions;
}
protected createParentChildOptionsMap(parentOptions: LanguageStringMap, childOptions: Option[]): { [key: string]: Option[] } {
const mappedOptions: { [key: string]: Option[] } = {};
Object.keys(parentOptions).forEach(key => {
mappedOptions[key] = childOptions.filter(
option => String(option.value).startsWith(key)
);
});
return mappedOptions;
}
protected subscribeToParentValueChanges(parentEnum: Field): void {
if (parentEnum.formControl) {
this.subs.push(parentEnum.formControl.valueChanges.subscribe(values => {
if (typeof values === 'string') {
values = [values];
}
// Rebuild available enum options
this.options = this.filterMatchingOptions(values);
this.setValueToAvailableOption();
this.initValue();
}));
}
}
protected setValueToAvailableOption(): void {
if (!this?.options?.length) {
this.field.value = '';
this.field.formControl.setValue('');
return;
}
if (!this.options.some(option => option.value === this.field.value)) {
this.field.value = this.options[0].value;
this.field.formControl.setValue(this.options[0].value);
}
}
protected buildOptionFromValue(value: string): Option {
const option: Option = {value: '', label: ''};
if (isNull(value)) {
return option;
}
option.value = (typeof value !== 'string' ? JSON.stringify(value) : value).trim();
option.label = option.value;
const valueLabel = this.optionsMap[option.value] ?? option.label;
if (isObject(valueLabel)) {
return option;
}
option.label = (typeof valueLabel !== 'string' ? JSON.stringify(valueLabel) : valueLabel).trim();
return option;
}
}