diff --git a/core/app/common/src/lib/metadata/metadata.model.ts b/core/app/common/src/lib/metadata/metadata.model.ts index ba1134bd33..633c18366c 100644 --- a/core/app/common/src/lib/metadata/metadata.model.ts +++ b/core/app/common/src/lib/metadata/metadata.model.ts @@ -30,6 +30,7 @@ import {BehaviorSubject, Observable} from 'rxjs'; export interface ViewFieldDefinition { name?: string; + vardefBased?: boolean; label?: string; labelKey?: string; dynamicLabelKey?: string; @@ -63,7 +64,7 @@ export interface PanelCell extends ViewFieldDefinition { } export interface ViewFieldDefinitionMap { - [key: string]: ViewFieldDefinition + [key: string]: ViewFieldDefinition; } export interface TabDefinitions { @@ -86,12 +87,12 @@ export interface LogicDefinition { modes: Array; params: { activeOnFields?: { - [key:string]: LogicRuleValues[]; - } + [key: string]: LogicRuleValues[]; + }; displayState?: boolean; fieldDependencies: Array; asyncProcessHandler?: string; - } + }; } export interface LogicRuleValues{ diff --git a/core/app/core/src/lib/services/record/field/field.manager.ts b/core/app/core/src/lib/services/record/field/field.manager.ts index db9abe363e..16d85768dc 100644 --- a/core/app/core/src/lib/services/record/field/field.manager.ts +++ b/core/app/core/src/lib/services/record/field/field.manager.ts @@ -148,17 +148,18 @@ export class FieldManager { /** * Build line item and add to record - * @param {object} itemDefinition - * @param {object }item - * @param {object} parentRecord - * @param {object} parentField + * + * @param {FieldDefinition} itemDefinition Item Definition + * @param {Record} parentRecord Parent Record + * @param {Field} parentField Parent Field + * @param {Record | null} item Item */ public addLineItem( itemDefinition: FieldDefinition, parentRecord: Record, parentField: Field, item: Record = null - ) { + ): void { if (!item) { item = { id: '', @@ -183,10 +184,11 @@ export class FieldManager { /** * Remove line item - * @param {object} parentField - * @param index + * + * @param {Field} parentField Parent Field + * @param {number} index Index */ - public removeLineItem(parentField: Field, index: number) { + public removeLineItem(parentField: Field, index: number): void { const item = parentField.items[index]; if (!item) { @@ -204,13 +206,13 @@ export class FieldManager { parentField.itemFormArray.clear(); - parentField.items.forEach(item => { - const deleted = item && item.attributes && item.attributes.deleted; - if (!item || deleted) { + parentField.items.forEach(parentItem => { + const deleted = parentItem && parentItem.attributes && parentItem.attributes.deleted; + if (!parentItem || deleted) { return; } - parentField.itemFormArray.push(item.formGroup); + parentField.itemFormArray.push(parentItem.formGroup); }); parentField.itemFormArray.updateValueAndValidity(); @@ -246,6 +248,45 @@ export class FieldManager { } + /** + * Build and add vardef only field to record + * + * @param {object} record Record + * @param {object} viewField ViewFieldDefinition + * @param {object} language LanguageStore + * @returns {object}Field + */ + public addVardefOnlyField(record: Record, viewField: ViewFieldDefinition, language: LanguageStore = null): Field { + + const field = this.fieldBuilder.buildField(record, viewField, language); + + this.addVardefOnlyFieldToRecord(record, viewField.name, field); + + return field; + } + + + /** + * Add field to record + * + * @param {object} record Record + * @param {string} name string + * @param {object} field Field + */ + public addVardefOnlyFieldToRecord(record: Record, name: string, field: Field): void { + + if (!record || !name || !field) { + return; + } + + if (!record.fields) { + record.fields = {}; + } + + record.fields[name] = field; + } + + /** * Is field initialized in record * diff --git a/core/app/core/src/lib/services/record/record.manager.ts b/core/app/core/src/lib/services/record/record.manager.ts index 8da3e17ad7..04ed3af99b 100644 --- a/core/app/core/src/lib/services/record/record.manager.ts +++ b/core/app/core/src/lib/services/record/record.manager.ts @@ -81,6 +81,18 @@ export class RecordManager { if (!viewField || !viewField.name) { return; } + + if(record.fields[viewField.name]) { + return; + } + + const isVardefBased = viewField?.vardefBased ?? false; + + if (isVardefBased) { + this.fieldManager.addVardefOnlyField(record, viewField, this.language); + return; + } + this.fieldManager.addField(record, viewField, this.language); }); diff --git a/core/app/core/src/lib/views/record/store/record-view/record-view.store.ts b/core/app/core/src/lib/views/record/store/record-view/record-view.store.ts index 69fd742fca..f749eaac57 100644 --- a/core/app/core/src/lib/views/record/store/record-view/record-view.store.ts +++ b/core/app/core/src/lib/views/record/store/record-view/record-view.store.ts @@ -24,12 +24,18 @@ * the words "Supercharged by SuiteCRM". */ +import { isEmpty } from 'lodash-es'; +import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; +import { catchError, distinctUntilChanged, finalize, map, take, tap } from 'rxjs/operators'; import {Injectable} from '@angular/core'; -import {BehaviorSubject, combineLatest, Observable, of, Subscription} from 'rxjs'; +import { Params } from '@angular/router'; import { BooleanMap, deepClone, + Field, FieldDefinitionMap, + FieldLogicMap, + FieldMetadata, isVoid, Record, StatisticsMap, @@ -37,10 +43,10 @@ import { SubPanelMeta, ViewContext, ViewFieldDefinition, - ViewMode + ViewFieldDefinitionMap, + ViewMode, } from 'common'; -import {catchError, distinctUntilChanged, finalize, map, take, tap} from 'rxjs/operators'; -import {RecordViewData, RecordViewModel, RecordViewState} from './record-view.store.model'; +import { RecordViewData, RecordViewModel, RecordViewState } from './record-view.store.model'; import {NavigationStore} from '../../../../store/navigation/navigation.store'; import {StateStore} from '../../../../store/state'; import {RecordSaveGQL} from '../../../../store/record/graphql/api.record.save'; @@ -61,7 +67,6 @@ import {LocalStorageService} from '../../../../services/local-storage/local-stor import {SubpanelStoreFactory} from '../../../../containers/subpanel/store/subpanel/subpanel.store.factory'; import {ViewStore} from '../../../../store/view/view.store'; import {RecordFetchGQL} from '../../../../store/record/graphql/api.record.get'; -import {Params} from '@angular/router'; import {StatisticsBatch} from '../../../../store/statistics/statistics-batch.service'; import {RecordStoreFactory} from '../../../../store/record/record.store.factory'; import {UserPreferenceStore} from '../../../../store/user-preference/user-preference.store'; @@ -170,9 +175,7 @@ export class RecordViewStore extends ViewStore implements StateStore { this.subpanels$ = this.subpanelsState.asObservable(); - this.viewContext$ = this.record$.pipe(map(() => { - return this.getViewContext(); - })); + this.viewContext$ = this.record$.pipe(map(() => this.getViewContext())); } get widgets(): boolean { @@ -394,6 +397,58 @@ export class RecordViewStore extends ViewStore implements StateStore { return templates[this.getMode()] || ''; } + initValidators(record: Record): void { + if(!record || !Object.keys(record?.fields).length) { + return; + } + + Object.keys(record.fields).forEach(fieldName => { + const field = record.fields[fieldName]; + const formControl = field?.formControl ?? null; + if (!formControl) { + return; + } + + this.resetValidators(field); + + const validators = field?.validators ?? []; + const asyncValidators = field?.asyncValidators ?? []; + + if (field?.formControl && validators.length) { + field.formControl.setValidators(validators); + } + if (field?.formControl && asyncValidators.length) { + field.formControl.setAsyncValidators(asyncValidators); + } + }); + + } + + resetValidators(field: Field): void { + if (!field?.formControl) { + return; + } + + field.formControl.clearValidators(); + field.formControl.clearAsyncValidators(); + } + + resetValidatorsForAllFields(record: Record): void { + if(!record || !record?.fields?.length) { + return ; + } + Object.keys(record.fields).forEach(fieldName => { + const field = record.fields[fieldName]; + const formControl = field?.formControl ?? null; + + if (!formControl) { + return; + } + + this.resetValidators(field); + }); + } + /** * Parse query params * @@ -587,23 +642,49 @@ export class RecordViewStore extends ViewStore implements StateStore { */ protected getViewFieldsObservable(): Observable { return this.metadataStore.recordViewMetadata$.pipe(map((recordMetadata: RecordViewMetadata) => { - const fields: ViewFieldDefinition[] = []; + const fieldsMap: ViewFieldDefinitionMap = {}; recordMetadata.panels.forEach(panel => { panel.rows.forEach(row => { row.cols.forEach(col => { - fields.push(col); + const fieldName = col.name ?? col.fieldDefinition.name ?? ''; + fieldsMap[fieldName] = col; }); }); }); - return fields; + Object.keys(recordMetadata.vardefs).forEach(fieldKey => { + const vardef = recordMetadata.vardefs[fieldKey] ?? null; + if (!vardef || isEmpty(vardef)) { + return; + } + + // already defined. skip + if (fieldsMap[fieldKey]) { + return; + } + + fieldsMap[fieldKey] = { + name: fieldKey, + vardefBased: true, + label: vardef.vname ?? '', + type: vardef.type ?? '', + display: vardef.display ?? '', + fieldDefinition: vardef, + metadata: vardef.metadata ?? {} as FieldMetadata, + logic: vardef.logic ?? {} as FieldLogicMap + } as ViewFieldDefinition; + }); + + return Object.values(fieldsMap); })); } /** * Build ui user preference key - * @param storageKey + * + * @param {string} storageKey Storage Key * @protected + * @returns {string} Preference Key */ protected getPreferenceKey(storageKey: string): string { return 'recordview-' + storageKey; @@ -611,9 +692,10 @@ export class RecordViewStore extends ViewStore implements StateStore { /** * Save ui user preference - * @param module - * @param storageKey - * @param value + * + * @param {string} module Module + * @param {string} storageKey Storage Key + * @param {any} value Value * @protected */ protected savePreference(module: string, storageKey: string, value: any): void { @@ -622,60 +704,14 @@ export class RecordViewStore extends ViewStore implements StateStore { /** * Load ui user preference - * @param module - * @param storageKey + * + * @param {string} module Module + * @param {string} storageKey Storage Key * @protected + * @returns {any} User Preference */ protected loadPreference(module: string, storageKey: string): any { return this.preferences.getUi(module, this.getPreferenceKey(storageKey)); } - initValidators(record: Record): void { - if(!record || !Object.keys(record?.fields).length) { - return; - } - - Object.keys(record.fields).forEach(fieldName => { - const field = record.fields[fieldName]; - const formControl = field?.formControl ?? null; - if (!formControl) { - return; - } - - this.resetValidators(field); - - const validators = field?.validators ?? []; - const asyncValidators = field?.asyncValidators ?? []; - - if (validators.length) { - field?.formControl?.setValidators(validators); - } - if (asyncValidators.length) { - field?.formControl?.setAsyncValidators(asyncValidators); - } - }); - - } - - resetValidators(field) { - field?.formControl?.clearValidators(); - field?.formControl?.clearAsyncValidators(); - } - - resetValidatorsForAllFields(record) { - if(!record || !record?.fields?.length) { - return ; - } - Object.keys(record.fields).forEach(fieldName => { - const field = record.fields[fieldName]; - const formControl = field?.formControl ?? null; - - if (!formControl) { - return; - } - - this.resetValidators(field); - }); - } - }