+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/angular-sdk-components/src/lib/_components/field/object-reference/object-reference.component.ts b/packages/angular-sdk-components/src/lib/_components/field/object-reference/object-reference.component.ts
index d3d594be..cca848d5 100644
--- a/packages/angular-sdk-components/src/lib/_components/field/object-reference/object-reference.component.ts
+++ b/packages/angular-sdk-components/src/lib/_components/field/object-reference/object-reference.component.ts
@@ -4,7 +4,19 @@ import { FormGroup } from '@angular/forms';
import { ComponentMetadataConfig } from '@pega/pcore-pconnect-typedefs/interpreter/types';
import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect';
import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component';
-import { generateColumns, getDataRelationshipContextFromKey } from '../../../_helpers/objectReference-utils';
+import {
+ SELECTION_MODE,
+ SIMPLE_TABLE_MANUAL_READONLY,
+ generateColumns,
+ addCompositeKeysToConfig,
+ getDataRelationshipContextFromKey,
+ generateDetailsDisplay,
+ createNewRecord,
+ getAdditionalInfo,
+ camelCase
+} from '../../../_helpers/objectReference-utils';
+import { componentCachePersistUtils, getMappedKey } from '../../template/advanced-search/search-group/persist-utils';
+import { DataReferenceAdvancedSearchService } from '../../../_services/data-reference-advanced-search.service';
import { PConnFieldProps } from '../../../_types/PConnProps.interface';
interface ObjectReferenceProps extends PConnFieldProps {
@@ -14,8 +26,17 @@ interface ObjectReferenceProps extends PConnFieldProps {
mode: string;
targetObjectType: any;
allowAndPersistChangesInReviewMode: boolean;
+ allowCreatingRecords?: boolean;
+ linkReference?: boolean;
+ matchPosition?: string;
+ additionalFields?: any;
}
+/**
+ * Rendering modes for the template
+ */
+type RenderMode = 'singleReferenceReadonly' | 'multiReferenceReadonly' | 'semanticLink' | 'searchAndSelect' | 'dynamicComponent';
+
@Component({
selector: 'app-object-reference',
imports: [CommonModule, forwardRef(() => ComponentMapperComponent)],
@@ -28,9 +49,7 @@ export class ObjectReferenceComponent implements OnInit, OnDestroy {
angularPConnectData: AngularPConnectData = {};
configProps: ObjectReferenceProps;
- value: { [key: string]: any };
readOnly: boolean;
- isForm: boolean;
type: string;
isDisplayModeEnabled: boolean;
canBeChangedInReviewMode: boolean;
@@ -38,7 +57,25 @@ export class ObjectReferenceComponent implements OnInit, OnDestroy {
newPconn: typeof PConnect;
rawViewMetadata: ComponentMetadataConfig | undefined;
- constructor(private angularPConnect: AngularPConnectService) {}
+ // Rendering mode
+ renderMode: RenderMode = 'dynamicComponent';
+
+ // Whether the dynamic component supports @Output() onRecordChange binding
+ useOutputEvents = false;
+
+ // Visibility flag — prevents rendering when platform sets visibility to false
+ bVisible$ = true;
+
+ // For SearchAndSelect
+ searchSelectCacheKey: string;
+
+ // For parameterized CheckboxGroup datasource
+ parameterizedDataSource: any[] = [];
+
+ constructor(
+ private angularPConnect: AngularPConnectService,
+ private dataRefAdvancedSearchService: DataReferenceAdvancedSearchService
+ ) {}
ngOnInit() {
this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
@@ -63,85 +100,262 @@ export class ObjectReferenceComponent implements OnInit, OnDestroy {
}
updateSelf() {
+ this.useOutputEvents = false;
this.configProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as ObjectReferenceProps;
+
+ const { visibility = true } = this.configProps;
+ this.bVisible$ = visibility !== false;
const {
- displayMode,
allowAndPersistChangesInReviewMode: editableInReview = false,
+ allowCreatingRecords,
targetObjectType,
- mode,
+ mode = '',
parameters,
- hideLabel,
- inline,
- showPromotedFilters
+ hideLabel = false,
+ inline = false,
+ showPromotedFilters = false,
+ linkReference,
+ matchPosition = 'contains'
} = this.configProps;
- const referenceType: string = targetObjectType === 'case' ? 'Case' : 'Data';
+ let displayMode = this.configProps.displayMode ?? '';
+
+ const referenceType: string = targetObjectType?.toLowerCase() === 'case' ? 'Case' : 'Data';
this.rawViewMetadata = this.pConn$.getRawMetadata();
- const refFieldMetadata = this.pConn$.getFieldMetadata(this.rawViewMetadata?.config?.value?.split('.', 2)[1] ?? '');
- const propsToUse = { ...this.pConn$.getInheritedProps(), ...this.configProps };
+ const rawConfig = this.rawViewMetadata?.config as any;
+
+ const refFieldMetadata = this.pConn$.getFieldMetadata(
+ rawConfig?.mode === SELECTION_MODE.SINGLE ? rawConfig?.value?.split('.', 2)[1] : rawConfig?.pagelistValue?.substring(4)
+ );
+
+ const propsToUse: any = { ...this.pConn$.getInheritedProps(), ...this.configProps };
+ if (!propsToUse.label) {
+ propsToUse.label = this.configProps.label;
+ }
+
+ this.normalizeComboboxType(rawConfig, mode);
+
+ if (rawConfig?.parameters && ['CheckboxGroup'].includes(rawConfig.componentType)) {
+ this.loadParameterizedDataSource(rawConfig, parameters ?? {});
+ }
+
+ displayMode = this.resolveDisplayMode(rawConfig, displayMode, mode);
this.isDisplayModeEnabled = displayMode === 'DISPLAY_ONLY';
- this.type = this.getComponentType();
- this.canBeChangedInReviewMode = editableInReview && ['Autocomplete', 'Dropdown'].includes(this.type);
-
- if (this.type === 'SemanticLink' && !this.canBeChangedInReviewMode) {
- const config: any = {
- ...this.rawViewMetadata?.config,
- primaryField: (this.rawViewMetadata?.config as any).displayField,
- caseClass: (this.rawViewMetadata?.config as any).targetObjectClass,
- text: (this.rawViewMetadata?.config as any).displayField,
- caseID: (this.rawViewMetadata?.config as any).value,
- contextPage: `@P .${(this.rawViewMetadata?.config as any).displayField ? getDataRelationshipContextFromKey((this.rawViewMetadata?.config as any).displayField) : null}`,
- resourceParams: { workID: (this.rawViewMetadata?.config as any).value },
- resourcePayload: { caseClassName: (this.rawViewMetadata?.config as any).targetObjectClass }
- };
- this.createSemanticLinkPConnect(config, displayMode ?? '', referenceType, hideLabel);
+ this.type = rawConfig?.componentType;
+ this.canBeChangedInReviewMode = editableInReview && ['AutoComplete', 'Dropdown'].includes(this.type);
+
+ if (this.handleReadOnlyMode(rawConfig, mode)) {
return;
}
- if (this.type !== 'SemanticLink' && !this.isDisplayModeEnabled) {
- const config: any = { ...this.rawViewMetadata?.config };
- generateColumns(config, this.pConn$, referenceType);
- config.deferDatasource = true;
- config.listType = 'datapage';
- if (['Dropdown', 'AutoComplete'].includes(this.type) && !config.placeholder) {
- config.placeholder = '@L Select...';
- }
- config.showPromotedFilters = showPromotedFilters;
- if (!this.canBeChangedInReviewMode) {
- config.displayMode = displayMode;
+ this.routeToEditableComponent(
+ rawConfig,
+ mode,
+ refFieldMetadata,
+ propsToUse,
+ referenceType,
+ displayMode,
+ parameters,
+ hideLabel,
+ inline,
+ showPromotedFilters,
+ matchPosition,
+ linkReference,
+ targetObjectType,
+ allowCreatingRecords
+ );
+ }
+
+ private normalizeComboboxType(rawConfig: any, mode: string): void {
+ if (rawConfig?.componentType === 'Combobox') {
+ rawConfig.componentType = mode === SELECTION_MODE.MULTI ? 'Multiselect' : 'AutoComplete';
+ }
+ }
+
+ private resolveDisplayMode(rawConfig: any, displayMode: string, mode: string): string {
+ if (displayMode === 'DISPLAY_ONLY' || !this.configProps.readOnly) {
+ return displayMode;
+ }
+ if (rawConfig?.componentType === 'Multiselect') {
+ rawConfig.componentType = 'SemanticLink';
+ return displayMode;
+ }
+ if (mode === 'readonly-single') {
+ return this.configProps.displayMode ?? displayMode;
+ }
+ return 'DISPLAY_ONLY';
+ }
+
+ private handleReadOnlyMode(rawConfig: any, mode: string): boolean {
+ const isReadOnlyOrSemanticLink = (this.configProps.readOnly || this.type === 'SemanticLink') && !this.canBeChangedInReviewMode;
+ if (isReadOnlyOrSemanticLink) {
+ if (mode !== 'readonly-multi') {
+ this.buildSingleReferenceReadonly(rawConfig);
+ } else {
+ this.buildMultiReferenceReadonly();
}
- config.parameters = parameters;
+ return true;
+ }
+ if (this.isDisplayModeEnabled && !this.canBeChangedInReviewMode) {
+ this.buildSingleReferenceReadonly(rawConfig);
+ return true;
+ }
+ return false;
+ }
- this.createOtherComponentPConnect(config, propsToUse, mode, refFieldMetadata, referenceType, hideLabel, inline);
+ private resolveCreatePermissions(rawConfig: any, allowCreatingRecords: boolean | undefined) {
+ const { disableStartingFieldsForReference = false } = (PCore as any).getEnvironmentInfo().environmentInfoObject?.features?.form || {};
+ const contextClass = rawConfig.targetObjectClass;
+ const formFeaturesAvailable = (PCore as any).getEnvironmentInfo().environmentInfoObject?.features?.form;
+ const createAuthoringEnabled = allowCreatingRecords ?? formFeaturesAvailable?.isCreateNewReferenceEnabled;
+ const userHasCreateAccess = formFeaturesAvailable
+ ? formFeaturesAvailable.isCreateNewReferenceEnabled && PCore.getAccessPrivilege().hasCreateAccess(contextClass)
+ : PCore.getAccessPrivilege().hasCreateAccess(contextClass);
+ return {
+ isCreateNewReferenceEnabled: createAuthoringEnabled && userHasCreateAccess,
+ disableStartingFieldsForReference,
+ contextClass
+ };
+ }
+
+ private routeToEditableComponent(
+ rawConfig: any,
+ mode: string,
+ refFieldMetadata: any,
+ propsToUse: any,
+ referenceType: string,
+ displayMode: string,
+ parameters: any,
+ hideLabel: boolean,
+ inline: boolean,
+ showPromotedFilters: boolean,
+ matchPosition: string,
+ linkReference: any,
+ targetObjectType: any,
+ allowCreatingRecords: boolean | undefined
+ ): void {
+ if (this.type === 'EmbeddedInsightTable') {
+ this.buildEmbeddedInsightTableChild(rawConfig, referenceType, propsToUse);
+ this.renderMode = 'dynamicComponent';
+ return;
+ }
+ if (this.type === 'Cards') {
+ this.buildCardsChild(rawConfig);
+ this.renderMode = 'dynamicComponent';
+ return;
}
+ if (this.type === 'Map') {
+ this.buildMapChild(rawConfig, targetObjectType);
+ this.renderMode = 'dynamicComponent';
+ return;
+ }
+ if (this.type === 'CheckboxGroup') {
+ this.buildCheckboxGroupChild(rawConfig, mode, refFieldMetadata, propsToUse, hideLabel);
+ this.renderMode = 'dynamicComponent';
+ return;
+ }
+ if (this.type === 'Table' || this.type === 'SimpleTable') {
+ this.buildTableChild(this.type, rawConfig, mode, referenceType, propsToUse);
+ this.renderMode = 'dynamicComponent';
+ return;
+ }
+
+ generateColumns(rawConfig, this.pConn$, referenceType);
+ addCompositeKeysToConfig(rawConfig, this.pConn$);
+ rawConfig.deferDatasource = true;
+ rawConfig.listType = 'datapage';
+ if (['Dropdown', 'AutoComplete'].includes(this.type) && !rawConfig.placeholder) {
+ rawConfig.placeholder = '@L Select...';
+ }
+ rawConfig.showPromotedFilters = showPromotedFilters;
+ if (!this.canBeChangedInReviewMode) {
+ rawConfig.displayMode = displayMode;
+ }
+
+ const fieldMetaData = this.buildFieldMetaData(rawConfig, parameters);
+ const { isCreateNewReferenceEnabled, disableStartingFieldsForReference, contextClass } = this.resolveCreatePermissions(
+ rawConfig,
+ allowCreatingRecords
+ );
+
+ if (this.type === 'SearchAndSelect') {
+ this.buildSearchAndSelectChild(
+ rawConfig,
+ refFieldMetadata,
+ propsToUse,
+ referenceType,
+ hideLabel,
+ inline,
+ matchPosition,
+ isCreateNewReferenceEnabled,
+ disableStartingFieldsForReference
+ );
+ this.renderMode = 'searchAndSelect';
+ return;
+ }
+ if (this.type === 'Multiselect') {
+ this.buildMultiselectChild(
+ rawConfig,
+ mode,
+ refFieldMetadata,
+ fieldMetaData,
+ propsToUse,
+ referenceType,
+ hideLabel,
+ inline,
+ isCreateNewReferenceEnabled,
+ disableStartingFieldsForReference,
+ contextClass
+ );
+ this.renderMode = 'dynamicComponent';
+ return;
+ }
+ this.buildDefaultChild(
+ rawConfig,
+ mode,
+ refFieldMetadata,
+ fieldMetaData,
+ propsToUse,
+ referenceType,
+ hideLabel,
+ inline,
+ isCreateNewReferenceEnabled,
+ disableStartingFieldsForReference,
+ contextClass,
+ linkReference
+ );
+ this.renderMode = 'dynamicComponent';
}
- onRecordChange(value) {
- const caseKey = this.pConn$.getCaseInfo().getKey() ?? '';
- const refreshOptions = { autoDetectRefresh: true, propertyName: '' };
- refreshOptions.propertyName = this.rawViewMetadata?.config?.value ?? '';
+ onRecordChange = event => {
+ const pConn = this.pConn$;
+ const caseKey = pConn.getCaseInfo().getKey() ?? '';
+ const refreshOptions: any = { autoDetectRefresh: true, propertyName: '' };
+ refreshOptions.propertyName = (this.rawViewMetadata?.config as any)?.value ?? '';
+
+ const { allowImplicitRefresh } = (PCore as any).getFieldDefaultUtils().fieldDefaults.DataReference || {};
- if (!this.canBeChangedInReviewMode || !this.pConn$.getValue('__currentPageTabViewName')) {
- const pgRef = this.pConn$.getPageReference().replace('caseInfo.content', '') ?? '';
+ if (!this.canBeChangedInReviewMode || !pConn.getValue('__currentPageTabViewName') || allowImplicitRefresh) {
+ const pgRef = pConn.getPageReference().replace('caseInfo.content', '') ?? '';
const viewName = this.rawViewMetadata?.name;
if (viewName && viewName.length > 0) {
- getPConnect().getActionsApi().refreshCaseView(caseKey, viewName, pgRef, refreshOptions);
+ pConn.getActionsApi().refreshCaseView(caseKey, viewName, pgRef, refreshOptions);
}
}
- const propValue = value;
+ const propValue = event?.id || event?.target?.value || event;
const propName =
- this.rawViewMetadata?.type === 'SimpleTableSelect' && this.configProps.mode === 'multi'
- ? PCore.getAnnotationUtils().getPropertyName(this.rawViewMetadata?.config?.selectionList ?? '')
- : PCore.getAnnotationUtils().getPropertyName(this.rawViewMetadata?.config?.value ?? '');
+ this.rawViewMetadata?.type === 'SimpleTableSelect' && this.configProps.mode === SELECTION_MODE.MULTI
+ ? PCore.getAnnotationUtils().getPropertyName((this.rawViewMetadata?.config as any)?.selectionList ?? '')
+ : PCore.getAnnotationUtils().getPropertyName((this.rawViewMetadata?.config as any)?.value ?? '');
if (propValue && this.canBeChangedInReviewMode && this.isDisplayModeEnabled) {
PCore.getCaseUtils()
.getCaseEditLock(caseKey, '')
.then(caseResponse => {
- const pageTokens = this.pConn$.getPageReference().replace('caseInfo.content', '').split('.');
- let curr = {};
+ const pageTokens = pConn.getPageReference().replace('caseInfo.content', '').split('.');
+ let curr: any = {};
const commitData = curr;
pageTokens?.forEach(el => {
@@ -151,7 +365,6 @@ export class ObjectReferenceComponent implements OnInit, OnDestroy {
}
});
- // expecting format like {Customer: {pyID:"C-100"}}
const propArr = propName.split('.');
propArr.forEach((element, idx) => {
if (idx + 1 === propArr.length) {
@@ -163,75 +376,584 @@ export class ObjectReferenceComponent implements OnInit, OnDestroy {
});
PCore.getCaseUtils()
- .updateCaseEditFieldsData(caseKey, { [caseKey]: commitData }, caseResponse.headers.etag, this.pConn$?.getContextName() ?? '')
+ .updateCaseEditFieldsData(caseKey, { [caseKey]: commitData }, caseResponse.headers.etag, pConn.getContextName() ?? '')
.then(response => {
- PCore.getContainerUtils().updateParentLastUpdateTime(this.pConn$.getContextName() ?? '', response.data.data.caseInfo.lastUpdateTime);
- PCore.getContainerUtils().updateRelatedContextEtag(this.pConn$.getContextName() ?? '', response.headers.etag);
+ PCore.getContainerUtils().updateParentLastUpdateTime(pConn.getContextName() ?? '', (response.data as any).data.caseInfo.lastUpdateTime);
+ PCore.getContainerUtils().updateRelatedContextEtag(pConn.getContextName() ?? '', response.headers.etag);
});
});
}
+ };
+
+ private loadParameterizedDataSource(rawConfig: any, parameters: any) {
+ const { value, key, text } = {
+ key: `@P ${rawConfig.selectionKey}`,
+ text: `@P ${rawConfig.displayField}`,
+ value: `@P ${rawConfig.selectionKey}`
+ };
+ const refList = rawConfig.referenceList;
+ (PCore as any)
+ .getDataApiUtils()
+ .getData(refList, { dataViewParameters: parameters })
+ .then((res: any) => {
+ if (res.data.data !== null) {
+ this.parameterizedDataSource = res.data.data
+ .map((listItem: any) => ({
+ key: listItem[key.split(' .', 2)[1]],
+ text: listItem[text.split(' .', 2)[1]],
+ value: listItem[value.split(' .', 2)[1]]
+ }))
+ .filter((item: any) => item.key);
+ } else {
+ this.parameterizedDataSource = [];
+ }
+ })
+ .catch(() => {
+ this.parameterizedDataSource = [];
+ });
+ }
+
+ private buildFieldMetaData(rawConfig: any, parameters: any) {
+ const fieldMetaData: any = {
+ datasourceMetadata: { datasource: { parameters: {} } }
+ };
+ if (rawConfig.parameters) {
+ fieldMetaData.datasourceMetadata.datasource.parameters = parameters;
+ }
+ fieldMetaData.datasourceMetadata.datasource.propertyForDisplayText = rawConfig.datasource?.fields?.text?.startsWith('@P')
+ ? rawConfig.datasource.fields.text.substring(3)
+ : rawConfig.datasource?.fields?.text;
+ fieldMetaData.datasourceMetadata.datasource.propertyForValue = rawConfig.datasource?.fields?.value?.startsWith('@P')
+ ? rawConfig.datasource.fields.value.substring(3)
+ : rawConfig.datasource?.fields?.value;
+ fieldMetaData.datasourceMetadata.datasource.name = rawConfig.referenceList;
+ return fieldMetaData;
}
- private getComponentType(): string {
- // componentType is not defined in ComponentMetadataConfig type so using any
- return (this.rawViewMetadata?.config as any)?.componentType;
+ private buildSingleReferenceReadonly(rawConfig: any) {
+ // SingleReferenceReadonly is rendered via component-mapper with pConn$ which internally resolves config
+ // Setting properties needed by the child component on config
+ rawConfig.primaryField = rawConfig.displayField;
+ rawConfig.text = rawConfig.displayField;
+ rawConfig.caseClass = rawConfig.targetObjectClass;
+ rawConfig.caseID = rawConfig.value;
+ rawConfig.contextPage = `@P .${rawConfig.displayField ? getDataRelationshipContextFromKey(rawConfig.displayField) : null}`;
+ this.renderMode = 'singleReferenceReadonly';
}
- private createSemanticLinkPConnect(config: any, displayMode: string, referenceType: string, hideLabel: boolean) {
- const semanticLinkConfig = {
- ...config,
- displayMode,
- referenceType,
- hideLabel,
- dataRelationshipContext: config.displayField ? getDataRelationshipContextFromKey(config.displayField) : null
+ private buildMultiReferenceReadonly() {
+ this.renderMode = 'multiReferenceReadonly';
+ }
+
+ private buildCardsChild(rawConfig: any) {
+ const selectionMode = rawConfig.mode;
+ const datasourceKeyField =
+ rawConfig.selectionKey || (rawConfig.isCalculated ? '.pzInsKey' : `.${rawConfig.value.trim().split('.').pop()?.trim()}`);
+
+ const componentMeta = {
+ type: selectionMode === 'single' ? 'RadioButtons' : 'Checkbox',
+ config: {
+ ...rawConfig,
+ label: rawConfig.label,
+ value: selectionMode === 'single' ? rawConfig.value : undefined,
+ referenceList: rawConfig.referenceList,
+ contextClass: rawConfig.targetObjectClass,
+ referenceType: rawConfig.targetObjectType === 'case' ? 'Case' : 'Data',
+ readonlyContextList: selectionMode.includes('multi') ? rawConfig.pagelistValue : undefined,
+ ...(selectionMode.includes('multi')
+ ? {
+ selectionList: rawConfig.pagelistValue.substring(3),
+ selectionKey: rawConfig.selectionKey || (rawConfig.isCalculated ? '.pzInsKey' : undefined)
+ }
+ : { selectionList: rawConfig.contextPage?.substring(3) }),
+ selectionMode: selectionMode.includes('multi') ? 'multi' : undefined,
+ displayMode: rawConfig.isCalculated ? 'DISPLAY_ONLY' : undefined,
+ renderMode: !rawConfig.isCalculated && rawConfig.mode === 'readonly-multi' ? 'ReadOnly' : undefined,
+ variant: 'card',
+ inlineDisplay: rawConfig.inlineDisplay ?? true,
+ hideFieldLabels: rawConfig.hideFieldLabels,
+ presets: [
+ {
+ children: [
+ {
+ children: rawConfig.secondaryFields,
+ name: 'AdditionalDetails',
+ type: 'Region'
+ }
+ ],
+ config: {},
+ id: 'P_',
+ label: '',
+ name: 'presets',
+ template: 'Cards'
+ }
+ ],
+ datasource: {
+ fields: {
+ key: `@P ${datasourceKeyField}`,
+ text:
+ typeof rawConfig.displayField === 'string' && rawConfig.displayField.trim()
+ ? `@P .${rawConfig.displayField.trim().split('.').pop()?.trim()}`
+ : `@P ${datasourceKeyField}`,
+ value: `@P ${datasourceKeyField}`
+ },
+ filterDownloadedFields: true,
+ source: `@DATASOURCE ${rawConfig.referenceList}.pxResults`
+ },
+ displayAs: 'cards',
+ hideLabel: rawConfig.hideLabel,
+ imagePosition: rawConfig.imagePosition,
+ image: rawConfig.image,
+ imageSize: rawConfig.imageSize,
+ showImageDescription: rawConfig.showImageDescription,
+ imageDescription: rawConfig.imageDescription,
+ required: rawConfig.required,
+ readOnly: !!rawConfig.isCalculated,
+ disabled: rawConfig.disabled,
+ labelOption: rawConfig.labelOption,
+ primaryField: rawConfig.displayField || datasourceKeyField,
+ dataRelationshipContext: rawConfig.displayField ? getDataRelationshipContextFromKey(rawConfig.displayField) : null
+ }
};
- const component = this.pConn$.createComponent({ type: 'SemanticLink', config: semanticLinkConfig }, '', 0, {});
+ const component = this.pConn$.createComponent(componentMeta as any, '', 0, {});
+ this.newComponentName = component?.getPConnect().getComponentName();
this.newPconn = component?.getPConnect();
+
+ // For multi-mode, pre-set the reference list on the child PConnect so that
+ // getListActions() resolves the correct path even if the child's updateSelf()
+ // doesn't re-run after a setInput update.
+ if (selectionMode.includes('multi') && this.newPconn) {
+ const selectionList = rawConfig.pagelistValue.substring(3);
+ this.newPconn.setReferenceList(selectionList);
+ }
}
- private createOtherComponentPConnect(
- config: any,
- propsToUse: any,
- mode: string,
+ private buildMapChild(rawConfig: any, targetObjectType: any) {
+ const displayField = getDataRelationshipContextFromKey(rawConfig.displayField);
+ const displayFieldMetadata = this.pConn$.getFieldMetadata(displayField);
+
+ const componentMeta = {
+ type: 'MapView',
+ config: {
+ contextClass: rawConfig.targetObjectClass,
+ displayAs: 'map',
+ hideLabel: false,
+ label: rawConfig.label,
+ referenceType: targetObjectType?.toLowerCase() === 'case' ? 'Case' : 'Data',
+ localeReference: rawConfig.localeReference,
+ classId: SIMPLE_TABLE_MANUAL_READONLY,
+ detailsDisplay: generateDetailsDisplay({
+ isCaseType: targetObjectType?.toLowerCase() === 'case',
+ fieldForDisplay: displayField,
+ fieldNameForKey: rawConfig.selectionKey,
+ displayFieldMetadata
+ }),
+ presets: [
+ {
+ children: [
+ {
+ children: [],
+ name: 'Columns',
+ type: 'Region'
+ },
+ {
+ children: rawConfig.secondaryFields,
+ name: 'AdditionalDetails',
+ type: 'Region'
+ }
+ ],
+ config: {},
+ id: 'P_',
+ label: '',
+ name: 'presets',
+ locationDetails: rawConfig.locationDetails,
+ template: 'Map'
+ }
+ ],
+ readonlyContextList: rawConfig.pagelistValue,
+ referenceList: rawConfig.pagelistValue,
+ renderMode: 'ReadOnly',
+ selectionMode: 'multi',
+ required: rawConfig.required,
+ readOnly: false,
+ disabled: rawConfig.disabled,
+ visibility: rawConfig.visibility
+ }
+ };
+
+ const component = this.pConn$.createComponent(componentMeta as any, '', 0, {});
+ this.newComponentName = component?.getPConnect().getComponentName();
+ this.newPconn = component?.getPConnect();
+ }
+
+ private buildCheckboxGroupChild(rawConfig: any, mode: string, refFieldMetadata: any, propsToUse: any, hideLabel: boolean) {
+ const displayField = rawConfig.displayField;
+ const primaryField = displayField?.startsWith('@P') ? displayField?.substring(3) : displayField;
+ const readOnly = !(mode === 'multi' || mode === 'single');
+ const pageListValueFromConfig = rawConfig.pagelistValue;
+ const selectionList = pageListValueFromConfig?.startsWith('@P') ? pageListValueFromConfig?.substring(3) : pageListValueFromConfig;
+
+ const component = this.pConn$.createComponent(
+ {
+ type: 'Checkbox',
+ config: {
+ ...rawConfig,
+ contextClass: rawConfig.targetObjectClass,
+ datasource: {
+ fields: {
+ key: `@P ${rawConfig.selectionKey}`,
+ text: `@P ${rawConfig.displayField}`,
+ value: `@P ${rawConfig.selectionKey}`
+ },
+ source: !rawConfig.parameters ? `@DATASOURCE ${rawConfig.referenceList}.pxResults` : this.parameterizedDataSource
+ },
+ descriptors: mode === SELECTION_MODE.SINGLE ? refFieldMetadata?.descriptors : null,
+ displayAs: 'checkboxgroup',
+ hideLabel,
+ inline: rawConfig.inline ?? false,
+ label: propsToUse.label,
+ primaryField,
+ readOnly,
+ readonlyContextList: pageListValueFromConfig,
+ referenceType: rawConfig.targetObjectType,
+ selectionKey: rawConfig.selectionKey,
+ selectionList,
+ selectionMode: mode,
+ disabled: propsToUse.disabled,
+ required: propsToUse.required,
+ visibility: propsToUse.visibility,
+ additionalInfo: refFieldMetadata?.additionalInformation ? { content: refFieldMetadata.additionalInformation } : undefined
+ }
+ } as any,
+ '',
+ 0,
+ {}
+ );
+ this.newComponentName = component?.getPConnect().getComponentName();
+ this.newPconn = component?.getPConnect();
+
+ // For multi-mode, pre-set the reference list on the child PConnect so that
+ // getListActions() resolves the correct path on every re-render.
+ if (mode === 'multi' && this.newPconn && selectionList) {
+ this.newPconn.setReferenceList(selectionList);
+ }
+ }
+
+ private buildTableChild(type: string, rawConfig: any, mode: string, referenceType: string, propsToUse: any) {
+ const presets = [
+ {
+ children: [{ children: rawConfig.columns, name: 'Columns', type: 'Region' }],
+ config: { filterExpression: rawConfig.filterExpression },
+ id: 'P_',
+ label: '',
+ name: 'presets',
+ template: 'Table'
+ }
+ ];
+ const tableDisplayAs = camelCase(type);
+
+ let componentConfig: any;
+
+ if (mode === 'readonly-multi') {
+ componentConfig = {
+ type: 'SimpleTableSelect',
+ config: {
+ contextClass: rawConfig.targetObjectClass,
+ defaultRowHeight: rawConfig.defaultRowHeight,
+ displayAs: tableDisplayAs,
+ hideLabel: false,
+ label: propsToUse.label,
+ localeReference: rawConfig.localeReference,
+ presets,
+ readOnly: true,
+ readonlyContextList: rawConfig.pagelistValue,
+ referenceList: rawConfig.pagelistValue,
+ referenceType,
+ renderMode: 'ReadOnly',
+ rowHeader: rawConfig.rowHeader,
+ selectionMode: 'multi',
+ required: propsToUse.required,
+ visibility: propsToUse.visibility,
+ disabled: propsToUse.disabled,
+ toggleFieldVisibility: rawConfig.toggleFieldVisibility
+ }
+ };
+ } else if (mode === 'single' || mode === 'multi') {
+ const contextPageFromConfig = mode === 'single' ? rawConfig.contextPage : rawConfig.pagelistValue;
+ const contextPageValue = contextPageFromConfig?.startsWith('@P') ? contextPageFromConfig?.substring(3) : contextPageFromConfig;
+
+ componentConfig = {
+ type: 'SimpleTableSelect',
+ config: {
+ ...(mode === 'single' ? { selectionKey: rawConfig.value, value: rawConfig.value } : {}),
+ dataRelationshipContext: contextPageValue,
+ defaultRowHeight: rawConfig.defaultRowHeight,
+ displayAs: tableDisplayAs,
+ hideLabel: false,
+ inline: false,
+ label: propsToUse.label,
+ localeReference: rawConfig.localeReference,
+ presets,
+ readOnly: false,
+ referenceList: rawConfig.referenceList,
+ referenceType,
+ rowHeader: rawConfig.rowHeader,
+ parameters: rawConfig.parameters,
+ selectionList: contextPageValue,
+ selectionMode: mode,
+ showPromotedFilters: false,
+ required: propsToUse.required,
+ visibility: propsToUse.visibility,
+ disabled: propsToUse.disabled,
+ toggleFieldVisibility: rawConfig.toggleFieldVisibility
+ }
+ };
+ }
+
+ if (componentConfig) {
+ const component = this.pConn$.createComponent(componentConfig, '', 0, {});
+ this.newComponentName = component?.getPConnect().getComponentName();
+ this.newPconn = component?.getPConnect();
+ }
+ }
+
+ private buildSearchAndSelectChild(
+ rawConfig: any,
refFieldMetadata: any,
+ propsToUse: any,
referenceType: string,
hideLabel: boolean,
- inline: boolean
+ inline: boolean,
+ matchPosition: string,
+ isCreateNewReferenceEnabled: boolean,
+ disableStartingFieldsForReference: boolean
) {
- const fieldMetaData = {
- datasourceMetadata: {
- datasource: {
- parameters: config.parameters ?? {},
- propertyForDisplayText: config.datasource?.fields?.text?.substring(3) ?? config.datasource?.fields?.text,
- propertyForValue: config.datasource?.fields?.value?.substring(3) ?? config.datasource?.fields?.value,
- name: config.referenceList ?? ''
- }
- }
- };
+ const selectionMode = rawConfig.mode;
+ const firstChildMeta = structuredClone((this.rawViewMetadata as any)?.children?.[0]);
+ const pyID = getMappedKey('pyID');
+ const additionalInfo = refFieldMetadata?.additionalInformation ? { content: refFieldMetadata.additionalInformation } : undefined;
+ const pageListValueFromConfig = rawConfig.pagelistValue;
+ const selectionList = pageListValueFromConfig?.startsWith('@P') ? pageListValueFromConfig?.substring(3) : pageListValueFromConfig;
+ const contextPageFromConfig = rawConfig.contextPage;
+ const unannotatedContextPageFromConfig = contextPageFromConfig?.startsWith('@P') ? contextPageFromConfig?.substring(3) : contextPageFromConfig;
+ const name = (selectionList ?? unannotatedContextPageFromConfig)?.replace(/^\./, '');
- const componentConfig = {
- ...config,
- descriptors: mode === 'single' ? refFieldMetadata?.descriptors : null,
- datasourceMetadata: fieldMetaData.datasourceMetadata,
+ const dataReferenceConfigToChild: any = {
+ selectionMode,
+ additionalInfo,
+ descriptors: selectionMode === SELECTION_MODE.SINGLE ? refFieldMetadata?.descriptors : null,
required: propsToUse.required,
visibility: propsToUse.visibility,
disabled: propsToUse.disabled,
label: propsToUse.label,
+ displayAs: 'advancedSearch',
readOnly: false,
- ...(mode === 'single' && { referenceType }),
- contextClass: config.targetObjectClass,
- primaryField: config.displayField,
- dataRelationshipContext: config.displayField ? getDataRelationshipContextFromKey(config.displayField) : null,
+ matchPosition,
+ ...(selectionMode === SELECTION_MODE.SINGLE && { referenceType }),
+ ...(selectionMode === SELECTION_MODE.SINGLE && {
+ value: rawConfig.value,
+ contextPage: contextPageFromConfig
+ }),
+ ...(selectionMode === SELECTION_MODE.MULTI && {
+ selectionList,
+ readonlyContextList: pageListValueFromConfig,
+ referenceType: referenceType || firstChildMeta?.config?.referenceType
+ }),
+ dataRelationshipContext: rawConfig.targetObjectClass && name ? name : null,
hideLabel,
+ onRecordChange: this.onRecordChange,
+ getAdditionalInfo: getAdditionalInfo.bind(this, this.pConn$, rawConfig?.authorContext),
+ createNewRecord: isCreateNewReferenceEnabled ? createNewRecord : undefined,
inline
};
- const component = this.pConn$.createComponent({ type: this.type, config: componentConfig }, '', 0, {});
+ this.searchSelectCacheKey = componentCachePersistUtils.getComponentStateKey(this.pConn$, name);
+
+ // Set advanced search context via service
+ this.dataRefAdvancedSearchService.setConfig({
+ dataReferenceConfigToChild,
+ isCreateNewReferenceEnabled,
+ disableStartingFieldsForReference,
+ pyID,
+ searchSelectKey: this.searchSelectCacheKey
+ });
+ }
+
+ private buildMultiselectChild(
+ rawConfig: any,
+ mode: string,
+ refFieldMetadata: any,
+ fieldMetaData: any,
+ propsToUse: any,
+ referenceType: string,
+ hideLabel: boolean,
+ inline: boolean,
+ isCreateNewReferenceEnabled: boolean,
+ disableStartingFieldsForReference: boolean,
+ contextClass: string
+ ) {
+ const component = this.pConn$.createComponent(
+ {
+ type: 'Multiselect',
+ config: {
+ ...rawConfig,
+ descriptors: refFieldMetadata?.descriptors,
+ datasourceMetadata: fieldMetaData?.datasourceMetadata,
+ selectionList: rawConfig.pagelistValue?.substring(3),
+ readonlyContextList: rawConfig.pagelistValue,
+ selectionKey: rawConfig.selectionKey,
+ selectionMode: SELECTION_MODE.MULTI,
+ required: propsToUse.required,
+ visibility: propsToUse.visibility,
+ disabled: propsToUse.disabled,
+ label: propsToUse.label,
+ parameters: rawConfig.parameters,
+ readOnly: false,
+ localeReference: rawConfig.localeReference,
+ ...(mode === SELECTION_MODE.MULTI ? { referenceType } : {}),
+ contextClass: rawConfig.targetObjectClass,
+ primaryField: rawConfig.displayField?.startsWith('@P') ? rawConfig.displayField.slice(3) : rawConfig.displayField,
+ dataRelationshipContext: rawConfig.pagelistValue?.substring(4),
+ hideLabel: hideLabel ?? false,
+ onRecordChange: this.onRecordChange,
+ createNewRecord: isCreateNewReferenceEnabled
+ ? () =>
+ createNewRecord({
+ referenceType: rawConfig.targetObjectType === 'case' ? 'Case' : 'Data',
+ pConn: this.pConn$,
+ getPConnect: () => this.pConn$,
+ disableStartingFieldsForReference,
+ startingFields: {},
+ contextClass
+ })
+ : undefined,
+ inline,
+ columnsFormatter: rawConfig.secondaryFields,
+ additionalInfo: refFieldMetadata?.additionalInformation ? { content: refFieldMetadata.additionalInformation } : undefined
+ }
+ } as any,
+ '',
+ 0,
+ {}
+ );
this.newComponentName = component?.getPConnect().getComponentName();
this.newPconn = component?.getPConnect();
- if (this.rawViewMetadata?.config) {
- this.rawViewMetadata.config = { ...config };
+
+ // Pre-set the reference list on the child PConnect so that
+ // getListActions() resolves the correct path on every re-render.
+ const selectionList = rawConfig.pagelistValue?.substring(3);
+ if (this.newPconn && selectionList) {
+ this.newPconn.setReferenceList(selectionList);
}
}
+
+ private buildEmbeddedInsightTableChild(rawConfig: any, referenceType: string, propsToUse: any) {
+ // Extract columns from insightModel.query.columns and convert to presets format
+ const insightModel = propsToUse.insightModel;
+ const insightColumns: any[] = insightModel?.query?.columns ?? [];
+
+ const columnChildren = insightColumns
+ .filter((col: any) => col.type === 'column' && col.field)
+ .map((col: any) => ({
+ type: 'TextInput',
+ config: {
+ value: `@P .${col.field.fieldID}`,
+ label: col.field.name || col.field.fieldID
+ }
+ }));
+
+ const presets = [
+ {
+ children: [{ children: columnChildren, name: 'Columns', type: 'Region' }],
+ config: {},
+ id: 'P_',
+ label: '',
+ name: 'presets',
+ template: 'Table'
+ }
+ ];
+
+ const componentConfig = {
+ type: 'SimpleTableManual',
+ config: {
+ contextClass: rawConfig.targetObjectClass,
+ presets,
+ label: propsToUse.label,
+ readonlyContextList: rawConfig.pagelistValue,
+ referenceList: rawConfig.pagelistValue,
+ renderMode: 'ReadOnly',
+ dataPageName: rawConfig.referenceList,
+ fieldMetadata: {
+ datasource: {
+ parameters: this.configProps.parameters ?? {}
+ }
+ }
+ }
+ };
+
+ const component = this.pConn$.createComponent(componentConfig as any, '', 0, {});
+ this.newComponentName = component?.getPConnect().getComponentName();
+ this.newPconn = component?.getPConnect();
+ }
+
+ private buildDefaultChild(
+ rawConfig: any,
+ mode: string,
+ refFieldMetadata: any,
+ fieldMetaData: any,
+ propsToUse: any,
+ referenceType: string,
+ hideLabel: boolean,
+ inline: boolean,
+ isCreateNewReferenceEnabled: boolean,
+ disableStartingFieldsForReference: boolean,
+ contextClass: string,
+ linkReference: any
+ ) {
+ this.useOutputEvents = true;
+ const component = this.pConn$.createComponent(
+ {
+ type: this.type,
+ config: {
+ ...rawConfig,
+ descriptors: mode === SELECTION_MODE.SINGLE ? refFieldMetadata?.descriptors : null,
+ datasourceMetadata: fieldMetaData?.datasourceMetadata,
+ required: propsToUse.required,
+ visibility: propsToUse.visibility,
+ disabled: propsToUse.disabled,
+ label: propsToUse.label,
+ parameters: rawConfig.parameters,
+ readOnly: false,
+ localeReference: rawConfig.localeReference,
+ ...(mode === SELECTION_MODE.SINGLE ? { referenceType } : {}),
+ contextClass: rawConfig.targetObjectClass,
+ primaryField: rawConfig.displayField,
+ dataRelationshipContext: rawConfig.displayField ? getDataRelationshipContextFromKey(rawConfig.displayField) : null,
+ hideLabel,
+ onRecordChange: this.onRecordChange,
+ createNewRecord: isCreateNewReferenceEnabled
+ ? () =>
+ createNewRecord({
+ referenceType: rawConfig.targetObjectType === 'case' ? 'Case' : 'Data',
+ pConn: this.pConn$,
+ getPConnect: () => this.pConn$,
+ disableStartingFieldsForReference,
+ startingFields: {},
+ contextClass
+ })
+ : undefined,
+ inline,
+ columnsFormatter: rawConfig.secondaryFields,
+ getAdditionalInfo: getAdditionalInfo.bind(this, this.pConn$, rawConfig.displayField),
+ linkReference: this.type === 'AutoComplete' ? linkReference : undefined
+ }
+ } as any,
+ '',
+ 0,
+ {}
+ );
+ this.newComponentName = component?.getPConnect().getComponentName();
+ this.newPconn = component?.getPConnect();
+ }
}
diff --git a/packages/angular-sdk-components/src/lib/_components/field/selectable-card/selectable-card.component.html b/packages/angular-sdk-components/src/lib/_components/field/selectable-card/selectable-card.component.html
index 345ba763..71910278 100644
--- a/packages/angular-sdk-components/src/lib/_components/field/selectable-card/selectable-card.component.html
+++ b/packages/angular-sdk-components/src/lib/_components/field/selectable-card/selectable-card.component.html
@@ -17,6 +17,7 @@
[checked]="cardContent.commonCardProps.selected"
[disabled]="disabled || readOnly"
[attr.data-test-id]="testId + ':' + cardContent.commonCardProps.label"
+ (click)="$event.stopPropagation()"
(change)="handleChangeMultiMode($event, cardContent.commonCardProps)"
(blur)="fieldOnBlur()"
>{{ cardContent.commonCardProps.label }} this.pConn$ };
+ } else {
+ this.deferLoadedTabs = this.pConn$.getChildren()[2];
+ }
const cache: any = PCore.getNavigationUtils().getComponentCache(this.searchSelectCacheKey) ?? {};
const { selectedCategory } = cache;
const firstTabId = getFirstVisibleTabId(this.deferLoadedTabs, selectedCategory);
diff --git a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.ts b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.ts
index 2aaa4d74..7b945a66 100644
--- a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.ts
+++ b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.ts
@@ -159,6 +159,9 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
referenceListStr: any;
bUseSeparateViewForEdit: any;
editView: any;
+ editType: any;
+ defaultActionId: any;
+ editActionId: any;
settingsSvgIcon$: string;
isInitialized = false;
@@ -308,6 +311,10 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
this.defaultView = editModeConfig ? editModeConfig.defaultView : viewForAddAndEditModal;
this.bUseSeparateViewForEdit = editModeConfig ? editModeConfig.useSeparateViewForEdit : useSeparateViewForEdit;
this.editView = editModeConfig ? editModeConfig.editView : viewForEditModal;
+ this.editType = editModeConfig?.editType;
+ this.defaultActionId = this.editType === 'action' ? editModeConfig?.defaultAction : undefined;
+ this.editActionId =
+ this.editType === 'action' && editModeConfig?.useSeparateActionForEdit ? editModeConfig?.editAction : editModeConfig?.defaultAction;
const primaryFieldsViewIndex = resolvedFields.findIndex(field => field.config.value === 'pyPrimaryFields');
// const showDeleteButton = !this.readOnlyMode && !hideDeleteRow;
@@ -384,17 +391,15 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
}
initializeDefaultPageInstructions() {
- if (this.isInitialized) {
+ if (this.allowEditingInModal) {
+ this.pConn$.getListActions().initDefaultPageInstructions(
+ this.pConn$.getReferenceList(),
+ this.fieldDefs.filter(item => item.name).map(item => item.name)
+ );
+ } else if (this.isInitialized) {
this.isInitialized = false;
- if (this.allowEditingInModal) {
- this.pConn$.getListActions().initDefaultPageInstructions(
- this.pConn$.getReferenceList(),
- this.fieldDefs.filter(item => item.name).map(item => item.name)
- );
- } else {
- // @ts-ignore - An argument for 'propertyNames' was not provided.
- this.pConn$.getListActions().initDefaultPageInstructions(this.pConn$.getReferenceList());
- }
+ // @ts-ignore - An argument for 'propertyNames' was not provided.
+ this.pConn$.getListActions().initDefaultPageInstructions(this.pConn$.getReferenceList());
}
}
@@ -958,17 +963,18 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
}
addRecord() {
- if (this.allowEditingInModal && this.defaultView) {
+ if (this.allowEditingInModal && (this.defaultView || this.defaultActionId)) {
this.pConn$
.getActionsApi()
- // @ts-expect-error
.openEmbeddedDataModal(
this.defaultView,
this.pConn$ as any,
this.referenceListStr,
this.referenceList.length,
PCore.getConstants().RESOURCE_STATUS.CREATE,
- this.targetClassLabel
+ this.targetClassLabel,
+ this.editType,
+ this.defaultActionId
);
} else {
this.pConn$.getListActions().insert({ classID: this.contextClass }, this.referenceList.length);
@@ -981,16 +987,18 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
editRecord(data, index) {
if (data) {
+ const viewForEdit = this.bUseSeparateViewForEdit ? this.editView : this.defaultView;
this.pConn$
.getActionsApi()
- // @ts-expect-error
.openEmbeddedDataModal(
- this.bUseSeparateViewForEdit ? this.editView : this.defaultView,
+ viewForEdit,
this.pConn$ as any,
this.referenceListStr,
index,
PCore.getConstants().RESOURCE_STATUS.UPDATE,
- this.targetClassLabel
+ this.targetClassLabel,
+ this.editType,
+ this.editActionId
);
}
}
diff --git a/packages/angular-sdk-components/src/lib/_components/template/simple-table/simple-table.component.ts b/packages/angular-sdk-components/src/lib/_components/template/simple-table/simple-table.component.ts
index 1ad6f2ff..005771e4 100644
--- a/packages/angular-sdk-components/src/lib/_components/template/simple-table/simple-table.component.ts
+++ b/packages/angular-sdk-components/src/lib/_components/template/simple-table/simple-table.component.ts
@@ -82,6 +82,11 @@ export class SimpleTableComponent implements OnInit, OnDestroy {
// moved this from ngOnInit() and call this from there instead...
this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as SimpleTableProps;
+ // Reset rendering state on every update
+ this.fieldGroupProps = null;
+ this.listViewProps = null;
+ this.refToPConnect = null;
+
if (this.configProps$.visibility != null) {
this.bVisible$ = this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
}
@@ -95,6 +100,7 @@ export class SimpleTableComponent implements OnInit, OnDestroy {
}
if (multiRecordDisplayAs === 'fieldGroup') {
this.fieldGroupProps = { ...this.configProps$, contextClass };
+ return;
}
const {
diff --git a/packages/angular-sdk-components/src/lib/_helpers/objectReference-utils.ts b/packages/angular-sdk-components/src/lib/_helpers/objectReference-utils.ts
index 10cb611b..75fa6b5d 100644
--- a/packages/angular-sdk-components/src/lib/_helpers/objectReference-utils.ts
+++ b/packages/angular-sdk-components/src/lib/_helpers/objectReference-utils.ts
@@ -1,12 +1,17 @@
+export const SELECTION_MODE = { SINGLE: 'single', MULTI: 'multi' };
+
+export const SIMPLE_TABLE_MANUAL_READONLY = 'SimpleTableManualReadOnly';
+
const PERIOD = '.';
const AT = '@';
const SQUARE_BRACKET_START = '[';
const SQUARE_BRACKET_END = ']';
function getMappedKey(key) {
- const mappedKey = PCore.getEnvironmentInfo().getKeyMapping(key);
+ const qualifiedKey = (PCore as any).getNameSpaceUtils().getDefaultQualifiedName(key);
+ const mappedKey = PCore.getEnvironmentInfo().getKeyMapping(qualifiedKey);
if (!mappedKey) {
- return key;
+ return qualifiedKey;
}
return mappedKey;
}
@@ -16,7 +21,7 @@ function updatePageListPropertyValue(value) {
return value;
}
-function getPropertyValue(value) {
+export function getPropertyValue(value) {
if (value.startsWith(AT)) {
value = value.substring(value.indexOf(' ') + 1);
if (value.startsWith(PERIOD)) value = value.substring(1);
@@ -28,30 +33,45 @@ function getPropertyValue(value) {
}
function getLeafNameFromPropertyName(property): string {
- return property?.substr(property.lastIndexOf('.'));
+ return property?.substr(property.lastIndexOf('.') + 1);
+}
+
+export function isSelfReferencedProperty(param, referenceProp): boolean {
+ const [, parentPropName] = param.split('.');
+ const referencePropParent = referenceProp?.split('.').pop();
+ return parentPropName === referencePropParent;
}
-function isSelfReferencedProperty(param, referenceProp): boolean {
- return param === referenceProp?.split('.', 2)[1];
+function getReferenceProp(config): string {
+ if (config.mode === SELECTION_MODE.MULTI) {
+ return config?.pagelistValue?.substring(4) ?? '';
+ }
+ const property = config.value;
+ const arr = property?.split('.') ?? [];
+ if (arr.length > 1) {
+ arr.pop();
+ return arr.slice(1).join('.');
+ }
+ return '';
}
function getCompositeKeys(c11nEnv, property): any {
const { datasource: { parameters = {} } = {} } = c11nEnv.getFieldMetadata(property) || {};
return Object.values(parameters).reduce((compositeKeys: any, param: any) => {
- if (isSelfReferencedProperty(property, param)) {
+ if (isSelfReferencedProperty(param, property)) {
let propName = getPropertyValue(param);
- propName = propName.substring(propName.indexOf('.'));
+ propName = propName.substring(propName.indexOf('.') + 1);
compositeKeys.push(propName);
}
return compositeKeys;
}, []);
}
-function generateColumns(config, pConn, referenceType) {
+export function generateColumns(config, pConn, referenceType) {
const displayField = getLeafNameFromPropertyName(config.displayField);
- const referenceProp = config.value.split('.', 2)[1];
+ const referenceProp = getReferenceProp(config);
const compositeKeys = getCompositeKeys(pConn, referenceProp);
- let value = getLeafNameFromPropertyName(config.value);
+ let value = getLeafNameFromPropertyName(config.mode === SELECTION_MODE.MULTI ? config.selectionKey : config.value);
const columns: any[] = [];
if (displayField) {
@@ -63,6 +83,9 @@ function generateColumns(config, pConn, referenceType) {
});
}
if (value && compositeKeys.indexOf(value) !== -1) {
+ if (!config.value) {
+ config.value = `@P .${referenceProp}.${value}`;
+ }
columns.push({
value,
setProperty: 'Associated property',
@@ -70,7 +93,7 @@ function generateColumns(config, pConn, referenceType) {
});
} else {
const actualValue = compositeKeys.length > 0 ? compositeKeys[0] : value;
- config.value = `@P .${referenceProp}${actualValue}`;
+ config.value = `@P .${referenceProp}.${actualValue}`;
value = actualValue;
columns.push({
value: actualValue,
@@ -81,9 +104,9 @@ function generateColumns(config, pConn, referenceType) {
config.datasource = {
fields: {
- key: getLeafNameFromPropertyName(config.value),
- text: getLeafNameFromPropertyName(config.displayField),
- value: getLeafNameFromPropertyName(config.value)
+ key: `.${getLeafNameFromPropertyName(config.value)}`,
+ text: `.${getLeafNameFromPropertyName(config.displayField)}`,
+ value: `.${getLeafNameFromPropertyName(config.value)}`
}
};
@@ -97,21 +120,105 @@ function generateColumns(config, pConn, referenceType) {
}
compositeKeys.forEach(key => {
+ const descriptorsFieldName = `.${key}`;
if (value !== key)
columns.push({
- value: key,
+ value: descriptorsFieldName,
display: 'false',
secondary: 'true',
useForSearch: false,
- setProperty: `.${referenceProp}${key}`
+ setProperty: `.${referenceProp}.${key}`
});
});
config.columns = columns;
}
-function getDataRelationshipContextFromKey(key) {
- return key.split('.', 2)[1];
+export function addCompositeKeysToConfig(config, pConn) {
+ const referenceProp = getReferenceProp(config);
+ const fieldMetadata = pConn.getFieldMetadata(referenceProp) || {};
+ const { datasource: { parameters: fieldParameters = {} } = {} } = fieldMetadata;
+ const compositeKeys: string[] = [];
+ Object.values(fieldParameters).forEach((param: any) => {
+ if (isSelfReferencedProperty(param, referenceProp)) {
+ compositeKeys.push(param);
+ }
+ });
+ config.compositeKeys = compositeKeys;
+}
+
+export function getDataRelationshipContextFromKey(key) {
+ const firstIndexOfDot = key.indexOf('.');
+ if (firstIndexOfDot > -1) {
+ const lastIndexOfDot = key.lastIndexOf('.');
+ if (lastIndexOfDot > -1) {
+ return key.substring(firstIndexOfDot + 1, lastIndexOfDot);
+ }
+ }
+ return '';
+}
+
+export function createNewRecord({ referenceType, disableStartingFieldsForReference, pConn, contextClass, startingFields, getPConnect }) {
+ if (referenceType === 'Case') {
+ if (!disableStartingFieldsForReference) {
+ startingFields[(PCore as any).getNameSpaceUtils().getDefaultQualifiedName('pyAddCaseContextPage')] = {
+ pyID: pConn.getCaseInfo().getKey()?.split(' ')?.pop()
+ };
+ }
+ return pConn.getActionsApi().createWork(contextClass, {
+ openCaseViewAfterCreate: false,
+ startingFields
+ });
+ }
+ if (referenceType === 'Data') {
+ return getPConnect().getActionsApi().showDataObjectCreateView(contextClass);
+ }
+}
+
+export function generateDetailsDisplay({ isCaseType, fieldForDisplay, fieldNameForKey, displayFieldMetadata }) {
+ const displayDetails: any[] = [
+ {
+ config: {
+ label: `@L ${fieldForDisplay}`,
+ value: `@P .${fieldForDisplay}`,
+ ...(isCaseType && {
+ additionalDetails: {
+ type: 'DISPLAY_LINK',
+ params: {}
+ }
+ })
+ },
+ type: displayFieldMetadata?.type || 'TextInput'
+ }
+ ];
+ if (isCaseType) {
+ displayDetails.push({
+ config: {
+ additionalDetails: {
+ params: {},
+ type: 'DISPLAY_LINK'
+ },
+ label: '@L Case ID',
+ previewKey: '@P .pzInsKey',
+ value: `@P ${fieldNameForKey || 'pyID'}`
+ },
+ type: 'TextInput'
+ });
+ }
+ return displayDetails;
+}
+
+export function getAdditionalInfo(pConn, propertyName) {
+ const parentFieldMetadata = pConn.getFieldMetadata(getDataRelationshipContextFromKey(propertyName));
+ return parentFieldMetadata?.additionalInformation
+ ? {
+ content: parentFieldMetadata.additionalInformation
+ }
+ : undefined;
+}
+
+export function camelCase(str: string): string {
+ return str.replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : '')).replace(/^[A-Z]/, char => char.toLowerCase());
}
-export { getLeafNameFromPropertyName, isSelfReferencedProperty, getCompositeKeys, generateColumns, getDataRelationshipContextFromKey };
+export { getLeafNameFromPropertyName, getCompositeKeys };
diff --git a/projects/angular-test-app/tests/e2e/DigV2/NewComplexFields/CaseReference.spec.js b/projects/angular-test-app/tests/e2e/DigV2/NewComplexFields/CaseReference.spec.js
new file mode 100644
index 00000000..68618686
--- /dev/null
+++ b/projects/angular-test-app/tests/e2e/DigV2/NewComplexFields/CaseReference.spec.js
@@ -0,0 +1,179 @@
+const { test, expect } = require('@playwright/test');
+const config = require('../../../config');
+const common = require('../../../common');
+
+test.beforeEach(async ({ page }) => {
+ await page.setViewportSize({ width: 1920, height: 1080 });
+ await page.goto(config.config.baseUrl, { waitUntil: 'networkidle' });
+});
+
+const selectComplexFieldType = async (type, page) => {
+ await page.locator('mat-select[data-test-id="d1b79d3f-1b8c-4347-bdd8-f86a5967bebf"]').click();
+ await page.getByRole('option', { name: type, exact: true }).click();
+};
+
+const selectMode = async (mode, page) => {
+ const modeDropdown = page.locator('mat-select[data-test-id="5131692e-09b6-45f1-83fa-3c280566f0fa"]');
+ await modeDropdown.waitFor({ state: 'visible' });
+ await expect(async () => {
+ await modeDropdown.click();
+ await expect(page.locator('.cdk-overlay-pane mat-option').first()).toBeVisible({ timeout: 2000 });
+ }).toPass({ timeout: 30000 });
+ await page.locator(`mat-option > span:has-text("${mode}")`).click();
+};
+
+const selectDisplayAs = async (displayAs, page) => {
+ const displayDropdown = page.locator('mat-select[data-test-id="a97b483b-34e0-48c5-8f48-a09cee7d74a3"]');
+ await displayDropdown.waitFor({ state: 'visible' });
+ await displayDropdown.click();
+ await page.locator(`mat-option > span:has-text("${displayAs}")`).click();
+};
+
+test.describe('Single select mode of Case Reference', () => {
+ test('Verify Single select mode of Case Reference displayed as Cards', async ({ page }) => {
+ await common.login(config.config.apps.digv2.user.username, config.config.apps.digv2.user.password, page);
+
+ await common.verifyHomePage(page);
+
+ // Creating New complex case type
+ await common.createCase('New Complex Fields', page);
+
+ // Get the case ID of the newly created case
+ await expect(page.locator('div[id="caseId"]')).not.toBeEmpty();
+ const caseID = await page.locator('div[id="caseId"]').textContent();
+
+ // Select Case Reference from the category dropdown
+ await selectComplexFieldType('CaseReference', page);
+ await page.locator('button:has-text("submit")').click();
+
+ await page.locator('h2:has-text("CaseReference")').waitFor({ state: 'visible' });
+ await page.waitForLoadState('networkidle');
+
+ // Select Single Select mode and Cards as display option for the Case Reference field
+ await selectMode('Single Select', page);
+ await selectDisplayAs('Cards', page);
+
+ // Select current case reference field from the list of cards
+ await page.locator(`mat-card:has(label:has-text("${caseID}")) mat-radio-button`).click();
+ await page.locator('button:has-text("submit")').click();
+
+ // Verify the selected case reference displays the correct case ID
+ await expect(page.locator('app-semantic-link a.psdk-value')).toBeVisible();
+ await expect(page.locator('app-semantic-link a.psdk-value')).toHaveText(caseID);
+ }, 10000);
+
+ test('Verify Single select mode of Case Reference displayed as Combobox', async ({ page }) => {
+ await common.login(config.config.apps.digv2.user.username, config.config.apps.digv2.user.password, page);
+
+ await common.verifyHomePage(page);
+
+ // Creating New complex case type
+ await common.createCase('New Complex Fields', page);
+
+ // Get the case ID of the newly created case
+ await expect(page.locator('div[id="caseId"]')).not.toBeEmpty();
+ const caseID = await page.locator('div[id="caseId"]').textContent();
+
+ // Select Case Reference from the category dropdown
+ await selectComplexFieldType('CaseReference', page);
+ await page.locator('button:has-text("submit")').click();
+
+ await page.locator('h2:has-text("CaseReference")').waitFor({ state: 'visible' });
+ await page.waitForLoadState('networkidle');
+
+ // Select Single Select mode and Cards as display option for the Case Reference field
+ await selectMode('Single Select', page);
+ await selectDisplayAs('Combobox', page);
+
+ // Select current case reference field from the list of combobox options
+ const combobox = page.locator('input[role="combobox"]');
+ await combobox.click();
+ await combobox.fill(caseID);
+ await page.locator(`mat-option:has-text("${caseID}")`).click();
+ await page.locator('button:has-text("submit")').click();
+
+ // Verify the selected case reference displays the correct case ID
+ await expect(page.locator('app-semantic-link a.psdk-value')).toBeVisible();
+ await expect(page.locator('app-semantic-link a.psdk-value')).toHaveText(caseID);
+ }, 10000);
+
+ test('Verify Single select mode of Case Reference displayed as Dropdown', async ({ page }) => {
+ await common.login(config.config.apps.digv2.user.username, config.config.apps.digv2.user.password, page);
+
+ await common.verifyHomePage(page);
+
+ // Creating New complex case type
+ await common.createCase('New Complex Fields', page);
+
+ // Get the case ID of the newly created case
+ await expect(page.locator('div[id="caseId"]')).not.toBeEmpty();
+ const caseID = await page.locator('div[id="caseId"]').textContent();
+
+ // Select Case Reference from the category dropdown
+ await selectComplexFieldType('CaseReference', page);
+ await page.locator('button:has-text("submit")').click();
+
+ await page.locator('h2:has-text("CaseReference")').waitFor({ state: 'visible' });
+ await page.waitForLoadState('networkidle');
+
+ // Select Single Select mode and Cards as display option for the Case Reference field
+ await selectMode('Single Select', page);
+ await selectDisplayAs('Dropdown', page);
+
+ // Select current case reference field from the list of dropdown options
+ const dropdown = page.locator('mat-select[role="combobox"]').last();
+ await dropdown.click();
+ await page.locator(`mat-option:has-text("${caseID}")`).click();
+ await page.locator('button:has-text("submit")').click();
+
+ // Verify the selected case reference displays the correct case ID
+ await expect(page.locator('app-semantic-link a.psdk-value')).toBeVisible();
+ await expect(page.locator('app-semantic-link a.psdk-value')).toHaveText(caseID);
+ }, 10000);
+
+ test('Verify Single select mode of Case Reference displayed as Search and select', async ({ page }) => {
+ await common.login(config.config.apps.digv2.user.username, config.config.apps.digv2.user.password, page);
+
+ await common.verifyHomePage(page);
+
+ // Creating New complex case type
+ await common.createCase('New Complex Fields', page);
+
+ // Get the case ID of the newly created case
+ await expect(page.locator('div[id="caseId"]')).not.toBeEmpty();
+ const caseID = await page.locator('div[id="caseId"]').textContent();
+
+ // Select Case Reference from the category dropdown
+ await selectComplexFieldType('CaseReference', page);
+ await page.locator('button:has-text("submit")').click();
+
+ await page.locator('h2:has-text("CaseReference")').waitFor({ state: 'visible' });
+ await page.waitForLoadState('networkidle');
+
+ // Select Single Select mode and Search and select as display option for the Case Reference field
+ await selectMode('Single Select', page);
+ await selectDisplayAs('Search and select', page);
+
+ // Select current case reference field from the Search and select options
+ const targetCaseID = caseID.trim();
+ const searchInput = page.getByLabel('Case ID');
+ await searchInput.click();
+ await searchInput.fill(targetCaseID);
+ await page.locator('button:has(span:has-text("Search"))').click();
+
+ // Wait for search results to load and select the correct case reference
+ const searchResultRow = page.locator('tr[role="row"]', { hasText: targetCaseID });
+ await searchResultRow.waitFor({ state: 'visible' });
+ await searchResultRow.locator('mat-radio-button').click();
+
+ await page.locator('button:has-text("submit")').click();
+
+ // Verify the selected case reference displays the correct case ID
+ await expect(page.locator('app-semantic-link a.psdk-value')).toBeVisible();
+ await expect(page.locator('app-semantic-link a.psdk-value')).toHaveText(caseID);
+ }, 10000);
+});
+
+test.afterEach(async ({ page }) => {
+ await page.close();
+});