diff --git a/packages/angular-sdk-components/src/lib/_bridge/angular-pconnect.ts b/packages/angular-sdk-components/src/lib/_bridge/angular-pconnect.ts index 0489e9c3..7d557824 100644 --- a/packages/angular-sdk-components/src/lib/_bridge/angular-pconnect.ts +++ b/packages/angular-sdk-components/src/lib/_bridge/angular-pconnect.ts @@ -260,16 +260,31 @@ export class AngularPConnectService { inComp.bridgeComponentID = theCompID; } else { returnObject.compID = theCompID; - returnObject.unsubscribeFn = theUnsub; + returnObject.unsubscribeFn = () => { + this.removeFormField(inComp); + theUnsub(); + }; } // initialize this components entry in the componentPropsArr this.componentPropsArr[theCompID] = {}; + this.addFormField(inComp); + // return object with compID and unsubscribe... return returnObject; } + addFormField(inComp) { + inComp.pConn$?.addFormField(); + } + + removeFormField(inComp) { + if (inComp.pConn$?.removeFormField) { + inComp.pConn$?.removeFormField(); + } + } + // Returns true if the component's entry in ___componentPropsArr___ is // the same as the inCompProps passed in. // As a side effect, update the component's entry in componentPropsArr diff --git a/packages/angular-sdk-components/src/lib/_components/field/auto-complete/auto-complete.component.ts b/packages/angular-sdk-components/src/lib/_components/field/auto-complete/auto-complete.component.ts index 95e27ae7..b824ea48 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/auto-complete/auto-complete.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/auto-complete/auto-complete.component.ts @@ -301,7 +301,9 @@ export class AutoCompleteComponent implements OnInit, OnDestroy { // this.angularPConnect.changeHandler( this, event); this.filterValue = (event.target as HTMLInputElement).value; - this.angularPConnectData.actions?.onChange(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'change', propName, this.filterValue); } optionChanged(event: MatAutocompleteSelectedEvent) { diff --git a/packages/angular-sdk-components/src/lib/_components/field/check-box/check-box.component.ts b/packages/angular-sdk-components/src/lib/_components/field/check-box/check-box.component.ts index c455e196..b8af6fd6 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/check-box/check-box.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/check-box/check-box.component.ts @@ -9,8 +9,8 @@ import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/an import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; -import { deleteInstruction, insertInstruction, updateNewInstructions } from '../../../_helpers/instructions-utils'; import { handleEvent } from '../../../_helpers/event-util'; +import { deleteInstruction, insertInstruction, updateNewInstructions } from '../../../_helpers/instructions-utils'; interface CheckboxProps extends Omit { // If any, enter additional props that only exist on Checkbox here @@ -234,8 +234,9 @@ export class CheckBoxComponent implements OnInit, OnDestroy { if (this.selectionMode === 'multi') { this.pConn$.getValidationApi().validate(this.selectedvalues, this.selectionList); } else { - event.value = event.checked; - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event.checked); } } diff --git a/packages/angular-sdk-components/src/lib/_components/field/currency/currency.component.ts b/packages/angular-sdk-components/src/lib/_components/field/currency/currency.component.ts index 7ec7f1af..324fb569 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/currency/currency.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/currency/currency.component.ts @@ -8,6 +8,7 @@ import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/an import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; import { getCurrencyCharacters } from '../../../_helpers/currency-utils'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface CurrrencyProps extends PConnFieldProps { @@ -175,7 +176,9 @@ export class CurrencyComponent implements OnInit, OnDestroy { fieldOnBlur(event: any) { // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/date-time/date-time.component.ts b/packages/angular-sdk-components/src/lib/_components/field/date-time/date-time.component.ts index 9d02fd39..547faaaa 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/date-time/date-time.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/date-time/date-time.component.ts @@ -202,10 +202,9 @@ export class DateTimeComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - if (event.target.value) event.value = event.target.value; - - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/date/date.component.ts b/packages/angular-sdk-components/src/lib/_components/field/date/date.component.ts index 02c9b4b5..744afe9b 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/date/date.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/date/date.component.ts @@ -13,8 +13,9 @@ import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/an import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; import { dateFormatInfoDefault, getDateFormatInfo } from '../../../_helpers/date-format-utils'; -import { PConnFieldProps } from '../../../_types/PConnProps.interface'; +import { handleEvent } from '../../../_helpers/event-util'; import { format } from '../../../_helpers/formatters'; +import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface DateProps extends PConnFieldProps { // If any, enter additional props that only exist on Date here @@ -222,7 +223,9 @@ export class DateComponent implements OnInit, OnDestroy { // convert date to pega "date" format event.value = event.value?.toISOString(); } - this.angularPConnectData.actions?.onBlur(this, { value: event.value }); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.value); } hasErrors() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/dropdown/dropdown.component.ts b/packages/angular-sdk-components/src/lib/_components/field/dropdown/dropdown.component.ts index ccbf15ba..773f7c35 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/dropdown/dropdown.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/dropdown/dropdown.component.ts @@ -5,12 +5,47 @@ import { MatOptionModule } from '@angular/material/core'; import { MatSelectModule } from '@angular/material/select'; import { MatFormFieldModule } from '@angular/material/form-field'; import { interval } from 'rxjs'; +import isEqual from 'fast-deep-equal'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; +import { DatapageService } from '../../../_services/datapage.service'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; +function flattenParameters(params = {}) { + const flatParams = {}; + Object.keys(params).forEach(key => { + const { name, value: theVal } = params[key]; + flatParams[name] = theVal; + }); + + return flatParams; +} + +function preProcessColumns(columnList) { + return columnList.map(col => { + const tempColObj = { ...col }; + tempColObj.value = col.value && col.value.startsWith('.') ? col.value.substring(1) : col.value; + return tempColObj; + }); +} + +function getDisplayFieldsMetaData(columnList) { + const displayColumns = columnList.filter(col => col.display === 'true'); + const metaDataObj: any = { key: '', primary: '', secondary: [] }; + const keyCol = columnList.filter(col => col.key === 'true'); + metaDataObj.key = keyCol.length > 0 ? keyCol[0].value : 'auto'; + for (let index = 0; index < displayColumns.length; index += 1) { + if (displayColumns[index].primary === 'true') { + metaDataObj.primary = displayColumns[index].value; + } else { + metaDataObj.secondary.push(displayColumns[index].value); + } + } + return metaDataObj; +} + interface IOption { key: string; value: string; @@ -22,6 +57,11 @@ interface DropdownProps extends PConnFieldProps { datasource?: any[]; onRecordChange?: any; fieldMetadata?: any; + listType?: string; + columns?: any[]; + deferDatasource?: boolean; + datasourceMetadata?: any; + parameters?: any; } @Component({ @@ -53,7 +93,7 @@ export class DropdownComponent implements OnInit, OnDestroy { testId = ''; helperText: string; hideLabel: any; - + theDatasource: any[] | null; fieldControl = new FormControl('', null); fieldMetadata: any[]; localeContext = ''; @@ -65,7 +105,8 @@ export class DropdownComponent implements OnInit, OnDestroy { constructor( private angularPConnect: AngularPConnectService, private cdRef: ChangeDetectorRef, - private utils: Utils + private utils: Utils, + private dataPageService: DatapageService ) {} ngOnInit(): void { @@ -76,8 +117,9 @@ export class DropdownComponent implements OnInit, OnDestroy { // Then, continue on with other initialization // call updateSelf when initializing - // this.updateSelf(); this.checkAndUpdate(); + // this should get called afer checkAndUpdate + this.getDatapageData(); if (this.formGroup$) { // add control to formGroup @@ -120,7 +162,6 @@ export class DropdownComponent implements OnInit, OnDestroy { updateSelf(): void { // moved this from ngOnInit() and call this from there instead... this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as DropdownProps; - if (this.configProps$.value != undefined) { this.value$ = this.configProps$.value; } @@ -130,6 +171,7 @@ export class DropdownComponent implements OnInit, OnDestroy { this.label$ = this.configProps$.label; this.helperText = this.configProps$.helperText; this.hideLabel = this.configProps$.hideLabel; + const datasource = this.configProps$.datasource; // timeout and detectChanges to avoid ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { if (this.configProps$.required != null) { @@ -138,6 +180,11 @@ export class DropdownComponent implements OnInit, OnDestroy { this.cdRef.detectChanges(); }); + if (!isEqual(datasource, this.theDatasource)) { + // inbound datasource is different, so update theDatasource + this.theDatasource = datasource || null; + } + if (this.configProps$.visibility != null) { this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility); } @@ -159,14 +206,16 @@ export class DropdownComponent implements OnInit, OnDestroy { this.componentReference = (this.pConn$.getStateProps() as any).value; - // @ts-ignore - parameter “contextName” for getDataObject method should be optional - const optionsList = [...this.utils.getOptionList(this.configProps$, this.pConn$.getDataObject())]; - optionsList?.unshift({ key: 'Select', value: this.pConn$.getLocalizedValue('Select...', '', '') }); - this.options$ = optionsList; if (this.value$ === '' && !this.bReadonly$) { this.value$ = 'Select'; } + if (this.theDatasource) { + const optionsList = [...this.utils.getOptionList(this.configProps$, this.pConn$.getDataObject())]; + optionsList?.unshift({ key: 'Select', value: this.pConn$.getLocalizedValue('Select...', '', '') }); + this.options$ = optionsList; + } + const propName = (this.pConn$.getStateProps() as any).value; const className = this.pConn$.getCaseInfo().getClassName(); const refName = propName?.slice(propName.lastIndexOf('.') + 1); @@ -198,6 +247,70 @@ export class DropdownComponent implements OnInit, OnDestroy { } } + getDatapageData() { + const configProps = this.pConn$.getConfigProps() as DropdownProps; + let { listType, parameters, datasource = [], columns = [] } = configProps; + const { deferDatasource, datasourceMetadata } = configProps; + const context = this.pConn$.getContextName(); + if (deferDatasource && datasourceMetadata?.datasource?.name) { + listType = 'datapage'; + datasource = datasourceMetadata.datasource.name; + const { parameters: dataSourceParameters, propertyForDisplayText, propertyForValue } = datasourceMetadata.datasource; + parameters = flattenParameters(dataSourceParameters); + const displayProp = propertyForDisplayText?.startsWith('@P') ? propertyForDisplayText.substring(3) : propertyForDisplayText; + const valueProp = propertyForValue?.startsWith('@P') ? propertyForValue.substring(3) : propertyForValue; + columns = [ + { + key: 'true', + setProperty: 'Associated property', + value: valueProp + }, + { + display: 'true', + primary: 'true', + useForSearch: true, + value: displayProp + } + ]; + } + + columns = preProcessColumns(columns) || []; + if (!this.displayMode$ && listType !== 'associated' && typeof datasource === 'string') { + this.getData(datasource, parameters, columns, context, listType); + } + } + + getData(dataSource, parameters, columns, context, listType) { + const dataConfig: any = { + columns, + dataSource, + deferDatasource: true, + listType, + parameters, + matchPosition: 'contains', + maxResultsDisplay: '5000', + cacheLifeSpan: 'form' + }; + PCore.getDataApi() + .init(dataConfig, context) + .then((dataApiObj: any) => { + const optionsData: any[] = []; + const displayColumn = getDisplayFieldsMetaData(columns); + dataApiObj.fetchData('').then(response => { + response.data?.forEach(element => { + const val = element[displayColumn.primary]?.toString(); + const obj = { + key: element[displayColumn.key] || element.pyGUID, + value: val + }; + optionsData.push(obj); + }); + optionsData?.unshift({ key: 'Select', value: this.pConn$.getLocalizedValue('Select...', '', '') }); + this.options$ = optionsData; + }); + }); + } + isSelected(buttonValue: string): boolean { return this.value$ === buttonValue; } @@ -214,11 +327,6 @@ export class DropdownComponent implements OnInit, OnDestroy { } } - fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); - } - getLocalizedOptionValue(opt: IOption) { return this.pConn$.getLocalizedValue( opt.value, diff --git a/packages/angular-sdk-components/src/lib/_components/field/email/email.component.ts b/packages/angular-sdk-components/src/lib/_components/field/email/email.component.ts index aa7b9544..03ccd318 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/email/email.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/email/email.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface EmailProps extends PConnFieldProps { @@ -157,8 +158,9 @@ export class EmailComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/integer/integer.component.ts b/packages/angular-sdk-components/src/lib/_components/field/integer/integer.component.ts index 1c78e0cd..1cf96f5e 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/integer/integer.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/integer/integer.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface IntegerProps extends PConnFieldProps { @@ -160,8 +161,9 @@ export class IntegerComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/percentage/percentage.component.ts b/packages/angular-sdk-components/src/lib/_components/field/percentage/percentage.component.ts index d53da03b..a9ed883f 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/percentage/percentage.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/percentage/percentage.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface PercentageProps extends PConnFieldProps { @@ -158,9 +159,9 @@ export class PercentageComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/phone/phone.component.ts b/packages/angular-sdk-components/src/lib/_components/field/phone/phone.component.ts index 56740b8d..043886f0 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/phone/phone.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/phone/phone.component.ts @@ -176,8 +176,9 @@ export class PhoneComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/radio-buttons/radio-buttons.component.ts b/packages/angular-sdk-components/src/lib/_components/field/radio-buttons/radio-buttons.component.ts index 84c8b50c..a5c231a6 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/radio-buttons/radio-buttons.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/radio-buttons/radio-buttons.component.ts @@ -8,6 +8,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface IOption { @@ -205,12 +206,9 @@ export class RadioButtonsComponent implements OnInit, OnDestroy { } fieldOnChange(event: any) { - this.angularPConnectData.actions?.onChange(this, event); - } - - fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.value); } getLocalizedOptionValue(opt: IOption) { diff --git a/packages/angular-sdk-components/src/lib/_components/field/text-area/text-area.component.ts b/packages/angular-sdk-components/src/lib/_components/field/text-area/text-area.component.ts index ad7d97b4..e9871517 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/text-area/text-area.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/text-area/text-area.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface TextAreaProps extends PConnFieldProps { @@ -156,13 +157,9 @@ export class TextAreaComponent implements OnInit, OnDestroy { } fieldOnChange(event: any) { - // PConnect wants to use changeHandler for onChange - this.angularPConnectData.actions?.onChange(this, event); - } - - fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/text-input/text-input.component.ts b/packages/angular-sdk-components/src/lib/_components/field/text-input/text-input.component.ts index 72ec46c5..71d0b5a4 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/text-input/text-input.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/text-input/text-input.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectService, AngularPConnectData } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface TextInputProps extends PConnFieldProps { @@ -160,8 +161,9 @@ export class TextInputComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/time/time.component.ts b/packages/angular-sdk-components/src/lib/_components/field/time/time.component.ts index 85ade831..341b59dc 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/time/time.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/time/time.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface TimeProps extends PConnFieldProps { @@ -157,9 +158,9 @@ export class TimeComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - event.value = event.target.value; - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/url/url.component.ts b/packages/angular-sdk-components/src/lib/_components/field/url/url.component.ts index dcbecdab..1df1e09f 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/url/url.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/url/url.component.ts @@ -7,6 +7,7 @@ import { interval } from 'rxjs'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; interface URLProps extends PConnFieldProps { @@ -158,8 +159,9 @@ export class UrlComponent implements OnInit, OnDestroy { } fieldOnBlur(event: any) { - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onBlur(this, event); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, event?.target?.value); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/field/user-reference/user-reference.component.ts b/packages/angular-sdk-components/src/lib/_components/field/user-reference/user-reference.component.ts index 58a7c9d2..3a560313 100644 --- a/packages/angular-sdk-components/src/lib/_components/field/user-reference/user-reference.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/field/user-reference/user-reference.component.ts @@ -9,6 +9,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { Utils } from '../../../_helpers/utils'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +import { handleEvent } from '../../../_helpers/event-util'; import { PConnFieldProps } from '../../../_types/PConnProps.interface'; const OPERATORS_DP = 'D_pyGetOperatorsForCurrentApplication'; @@ -189,11 +190,9 @@ export class UserReferenceComponent implements OnInit, OnDestroy { key = index > -1 ? (key = this.options$[index].key) : event.target.value; } - const eve = { - value: key - }; - // PConnect wants to use eventHandler for onBlur - this.angularPConnectData.actions?.onChange(this, eve); + const actionsApi = this.pConn$?.getActionsApi(); + const propName = (this.pConn$?.getStateProps() as any).value; + handleEvent(actionsApi, 'changeNblur', propName, key); } getErrorMessage() { diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/flow-container-base.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/flow-container-base.component.ts new file mode 100644 index 00000000..14f8e7b7 --- /dev/null +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/flow-container-base.component.ts @@ -0,0 +1,22 @@ +import { Injector } from '@angular/core'; +import { getPConnectOfActiveContainerItem } from './helper'; +import { AngularPConnectData, AngularPConnectService } from '../../../../_bridge/angular-pconnect'; + +export class FlowContainerBaseComponent { + // For interaction with AngularPConnect + protected angularPConnectData: AngularPConnectData = {}; + protected angularPConnect; + + constructor(injector: Injector) { + this.angularPConnect = injector.get(AngularPConnectService); + } + + getPConnectOfActiveContainerItem(parentPConnect) { + const routingInfo = this.angularPConnect.getComponentProp(this, 'routingInfo'); + const isAssignmentView = this.angularPConnect.getComponentProp(this, 'isAssignmentView'); + return getPConnectOfActiveContainerItem(routingInfo, { + isAssignmentView, + parentPConnect + }); + } +} diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/helper.ts b/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/helper.ts new file mode 100644 index 00000000..064e0310 --- /dev/null +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/base-components/helper.ts @@ -0,0 +1,89 @@ +const processRootViewDetails = (rootView, containerItem, options) => { + const { + config: { context: viewContext, name: viewName } + } = rootView; + const { context: containerContext } = containerItem; + const { parentPConnect } = options; + let resolvedViewName = viewName; + let resolvedViewContext = viewContext; + + const isAnnotedViewName = PCore.getAnnotationUtils().isProperty(viewName); + const isAnnotedViewContext = PCore.getAnnotationUtils().isProperty(viewContext); + + // resolving annoted view context + if (isAnnotedViewContext) { + const viewContextProperty = PCore.getAnnotationUtils().getPropertyName(viewContext); + resolvedViewContext = PCore.getStoreValue( + `.${viewContextProperty}`, + viewContextProperty.startsWith('.') ? parentPConnect.getPageReference() : '', + containerContext + ); + } + + if (!resolvedViewContext) { + resolvedViewContext = parentPConnect.getPageReference(); + } + + // resolving annoted view name + if (isAnnotedViewName) { + const viewNameProperty = PCore.getAnnotationUtils().getPropertyName(viewName); + resolvedViewName = PCore.getStoreValue(`.${viewNameProperty}`, resolvedViewContext, containerContext); + } + + /* Special case where context and viewname are dynamic values + Use case - split for each shape + Ex - (caseInfo.content.SCRequestWorkQueues[1]):context --> .pyViewName:viewName + */ + if (isAnnotedViewName && isAnnotedViewContext && resolvedViewName !== '') { + /* Allow context processor to resolve view and context when both are dynamic */ + resolvedViewName = viewName; + resolvedViewContext = viewContext; + } + + return { + viewName: resolvedViewName, + viewContext: resolvedViewContext + }; +}; + +export const getPConnectOfActiveContainerItem = (containerInfo, options) => { + const { accessedOrder, items } = containerInfo; + const { isAssignmentView = false, parentPConnect } = options; + const containerName = parentPConnect.getContainerName(); + const { CONTAINER_NAMES } = PCore.getContainerUtils(); + const { CREATE_DETAILS_VIEW_NAME } = PCore.getConstants(); + + if (accessedOrder && items) { + const activeContainerItemKey = accessedOrder[accessedOrder.length - 1]; + + if (items[activeContainerItemKey] && items[activeContainerItemKey].view && Object.keys(items[activeContainerItemKey].view).length > 0) { + const activeContainerItem = items[activeContainerItemKey]; + const target = activeContainerItemKey.substring(0, activeContainerItemKey.lastIndexOf('_')); + + const { view: rootView, context } = activeContainerItem; + const { viewName, viewContext } = processRootViewDetails(rootView, activeContainerItem, { parentPConnect }); + + if (!viewName) return null; + + const config = { + meta: rootView, + options: { + context, + pageReference: viewContext || parentPConnect.getPageReference(), + containerName, + containerItemID: activeContainerItemKey, + parentPageReference: parentPConnect.getPageReference(), + hasForm: + isAssignmentView || + containerName === CONTAINER_NAMES.WORKAREA || + containerName === CONTAINER_NAMES.MODAL || + viewName === CREATE_DETAILS_VIEW_NAME, + target + } + }; + + return PCore.createPConnect(config).getPConnect(); + } + } + return null; +}; diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.html b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.html index 6ca29a42..3e2c3372 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.html +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.html @@ -8,21 +8,27 @@

{{ containerName$ }}

- +
diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.ts index c1870028..3981febc 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/flow-container.component.ts @@ -1,14 +1,14 @@ -import { Component, OnInit, Input, ChangeDetectorRef, NgZone, forwardRef, OnDestroy } from '@angular/core'; +import { Component, OnInit, Input, ChangeDetectorRef, NgZone, forwardRef, OnDestroy, Injector } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup } from '@angular/forms'; import { MatCardModule } from '@angular/material/card'; import { publicConstants } from '@pega/pcore-pconnect-typedefs/constants'; -import { AngularPConnectData, AngularPConnectService } from '../../../../_bridge/angular-pconnect'; import { ProgressSpinnerService } from '../../../../_messages/progress-spinner.service'; import { ReferenceComponent } from '../../reference/reference.component'; import { Utils } from '../../../../_helpers/utils'; -import { getToDoAssignments, showBanner } from './helpers'; +import { getToDoAssignments, hasAssignments, showBanner } from './helpers'; import { ComponentMapperComponent } from '../../../../_bridge/component-mapper/component-mapper.component'; +import { FlowContainerBaseComponent } from '../base-components/flow-container-base.component'; /** * WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with @@ -32,11 +32,9 @@ interface FlowContainerProps { standalone: true, imports: [CommonModule, MatCardModule, forwardRef(() => ComponentMapperComponent)] }) -export class FlowContainerComponent implements OnInit, OnDestroy { +export class FlowContainerComponent extends FlowContainerBaseComponent implements OnInit, OnDestroy { @Input() pConn$: typeof PConnect; - // For interaction with AngularPConnect - angularPConnectData: AngularPConnectData = {}; pCoreConstants: typeof publicConstants; configProps$: FlowContainerProps; @@ -72,14 +70,17 @@ export class FlowContainerComponent implements OnInit, OnDestroy { banners: any[]; // itemKey: string = ""; // JA - this is what Nebula/Constellation uses to pass to finishAssignment, navigateToStep + pConnectOfActiveContainerItem; + constructor( - private angularPConnect: AngularPConnectService, + injector: Injector, private cdRef: ChangeDetectorRef, private psService: ProgressSpinnerService, private fb: FormBuilder, private ngZone: NgZone, private utils: Utils ) { + super(injector); // create the formGroup this.formGroup$ = this.fb.group({ hideRequired: false }); } @@ -148,10 +149,14 @@ export class FlowContainerComponent implements OnInit, OnDestroy { // Should always check the bridge to see if the component should update itself (re-render) const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this); + const pConn = this.pConnectOfActiveContainerItem || this.pConn$; + const caseViewModeFromProps = this.angularPConnect.getComponentProp(this, 'caseViewMode'); + const caseViewModeFromRedux = pConn.getValue('context_data.caseViewMode', ''); + // ONLY call updateSelf when the component should update // AND removing the "gate" that was put there since shouldComponentUpdate // should be the real "gate" - if (bUpdateSelf) { + if (bUpdateSelf || caseViewModeFromProps !== caseViewModeFromRedux) { // don't want to redraw the flow container when there are page messages, because // the redraw causes us to loose the errors on the elements const completeProps = this.angularPConnect.getCurrentCompleteProps(this) as FlowContainerProps; @@ -271,36 +276,6 @@ export class FlowContainerComponent implements OnInit, OnDestroy { this.psService.sendMessage(false); } - hasAssignments() { - let hasAssignments = false; - // @ts-ignore - second parameter pageReference for getValue method should be optional - const assignmentsList = this.pConn$.getValue(this.pCoreConstants.CASE_INFO.D_CASE_ASSIGNMENTS_RESULTS); - const thisOperator = PCore.getEnvironmentInfo().getOperatorIdentifier(); - // 8.7 includes assignments in Assignments List that may be assigned to - // a different operator. So, see if there are any assignments for - // the current operator - let bAssignmentsForThisOperator = false; - - // Bail if there is no assignmentsList - if (!assignmentsList) { - return hasAssignments; - } - - for (const assignment of assignmentsList) { - if ((assignment as any).assigneeInfo.ID === thisOperator) { - bAssignmentsForThisOperator = true; - } - } - - const hasChildCaseAssignments = this.hasChildCaseAssignments(); - - if (bAssignmentsForThisOperator || hasChildCaseAssignments || this.isCaseWideLocalAction()) { - hasAssignments = true; - } - - return hasAssignments; - } - isCaseWideLocalAction() { // @ts-ignore - second parameter pageReference for getValue method should be optional const actionID = this.pConn$.getValue(this.pCoreConstants.CASE_INFO.ACTIVE_ACTION_ID); @@ -370,8 +345,10 @@ export class FlowContainerComponent implements OnInit, OnDestroy { // const { getPConnect } = this.arChildren$[0].getPConnect(); const localPConn = this.arChildren$[0].getPConnect(); - // @ts-ignore - second parameter pageReference for getValue method should be optional - const caseViewMode = this.pConn$.getValue('context_data.caseViewMode'); + this.pConnectOfActiveContainerItem = this.getPConnectOfActiveContainerItem(this.pConn$) || this.pConn$; + + const caseViewMode = this.pConnectOfActiveContainerItem.getValue('context_data.caseViewMode'); + this.bShowBanner = showBanner(this.pConn$); if (caseViewMode && caseViewMode == 'review') { @@ -454,19 +431,10 @@ export class FlowContainerComponent implements OnInit, OnDestroy { showCaseMessages() { // @ts-ignore - second parameter pageReference for getValue method should be optional this.caseMessages$ = this.localizedVal(this.pConn$.getValue('caseMessages'), this.localeCategory); - if (this.caseMessages$ || !this.hasAssignments()) { + if (this.caseMessages$ || !hasAssignments(this.pConn$)) { this.bHasCaseMessages$ = true; this.bShowConfirm = true; this.checkSvg$ = this.utils.getImageSrc('check', this.utils.getSDKStaticContentUrl()); - // Temp fix for 8.7 change: confirmationNote no longer coming through in caseMessages$. - // So, if we get here and caseMessages$ is empty, use default value in DX API response - if (!this.caseMessages$) { - this.caseMessages$ = this.localizedVal('Thank you! The next step in this case has been routed appropriately.', this.localeCategory); - } - - // publish this "assignmentFinished" for mashup, need to get approved as a standard - // @ts-ignore - second parameter “payload” for publish method should be optional - PCore.getPubSubUtils().publish('assignmentFinished'); this.psService.sendMessage(false); } else if (this.bHasCaseMessages$) { diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/helpers.ts b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/helpers.ts index 21ef7523..7df377b7 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/helpers.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/flow-container/helpers.ts @@ -28,7 +28,7 @@ function getChildCaseAssignments(pConnect) { return allAssignments; } -function hasAssignments(pConnect) { +export function hasAssignments(pConnect) { const { CASE_INFO } = PCore.getConstants(); const assignments = pConnect.getValue(CASE_INFO.D_CASE_ASSIGNMENTS_RESULTS); const childCasesAssignments = getChildCaseAssignments(pConnect); diff --git a/packages/angular-sdk-components/src/lib/_components/infra/Containers/modal-view-container/modal-view-container.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/Containers/modal-view-container/modal-view-container.component.ts index 11af1535..a29fa126 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/Containers/modal-view-container/modal-view-container.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/Containers/modal-view-container/modal-view-container.component.ts @@ -6,6 +6,7 @@ import { AngularPConnectData, AngularPConnectService } from '../../../../_bridge import { ProgressSpinnerService } from '../../../../_messages/progress-spinner.service'; import { ComponentMapperComponent } from '../../../../_bridge/component-mapper/component-mapper.component'; import { getBanners } from '../../../../_helpers/case-utils'; +import { ReferenceComponent } from '../../reference/reference.component'; /** * WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with @@ -22,7 +23,6 @@ import { getBanners } from '../../../../_helpers/case-utils'; }) export class ModalViewContainerComponent implements OnInit, OnDestroy { @Input() pConn$: typeof PConnect; - @Input() displayOnlyFA$: boolean; // for when non modal @Output() modalVisibleChange = new EventEmitter(); @@ -72,11 +72,6 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy { } ngOnInit(): void { - if (this.displayOnlyFA$) { - // for when non modal - this.bShowAsModal$ = false; - } - // First thing in initialization is registering and subscribing to the AngularPConnect service this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange); @@ -200,7 +195,7 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy { } createView(routingInfo, currentItem, latestItem, key) { - const configObject = this.getConfigObject(currentItem, this.pConn$); + const configObject = this.getConfigObject(currentItem, null, false); const newComp = configObject?.getPConnect(); // const newCompName = newComp.getComponentName(); // @ts-ignore - parameter “contextName” for getDataObject method should be optional @@ -248,8 +243,16 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy { ID, `${theNewCaseInfo?.getClassName()}!CASE!${theNewCaseInfo.getName()}`.toUpperCase() ); - // // update children with new view's children - this.arChildren$ = newComp.getChildren() as any[]; + const bIsRefComponent = this.checkIfRefComponent(newComp); + + if (bIsRefComponent) { + const newPConn = ReferenceComponent.normalizePConn(newComp); + this.arChildren$ = ReferenceComponent.normalizePConnArray(newPConn.getChildren()); + } else { + // update children with new view's children + this.arChildren$ = newComp.getChildren() as any; + } + this.bShowModal$ = true; // for when non modal @@ -280,16 +283,30 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy { }); } - getConfigObject(item, pConnect) { + getConfigObject(item, pConnect, isReverseCoexistence = false) { + let config; + if (isReverseCoexistence) { + config = { + options: { + pageReference: pConnect?.getPageReference(), + hasForm: true, + containerName: pConnect?.getContainerName() || PCore.getConstants().MODAL + } + }; + return PCore.createPConnect(config); + } if (item) { - const { context, view } = item; - const config = { + const { context, view, isBulkAction } = item; + const target = PCore.getContainerUtils().getTargetFromContainerItemID(context); + config = { meta: view, options: { context, pageReference: view.config.context || pConnect.getPageReference(), hasForm: true, - containerName: pConnect?.getContainerName() || PCore.getConstants().MODAL + ...(isBulkAction && { isBulkAction }), + containerName: pConnect?.getContainerName() || PCore.getConstants().MODAL, + target } }; return PCore.createPConnect(config); @@ -297,6 +314,15 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy { return null; } + checkIfRefComponent(thePConn: any): boolean { + let bReturn = false; + if (thePConn && thePConn.getComponentName() == 'reference') { + bReturn = true; + } + + return bReturn; + } + onAlertState(bData: boolean) { this.bAlertState = bData; this.bShowCancelAlert$ = false; diff --git a/packages/angular-sdk-components/src/lib/_components/infra/assignment-card/assignment-card.component.html b/packages/angular-sdk-components/src/lib/_components/infra/assignment-card/assignment-card.component.html index 9463e770..1514318a 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/assignment-card/assignment-card.component.html +++ b/packages/angular-sdk-components/src/lib/_components/infra/assignment-card/assignment-card.component.html @@ -18,6 +18,7 @@
ComponentMapperComponent)] }) -export class AssignmentCardComponent implements OnInit, OnChanges { +export class AssignmentCardComponent implements OnInit, OnChanges, OnDestroy { @Input() pConn$: typeof PConnect; @Input() formGroup$: FormGroup; @Input() arMainButtons$: any[]; @@ -21,19 +23,44 @@ export class AssignmentCardComponent implements OnInit, OnChanges { @Output() actionButtonClick: EventEmitter = new EventEmitter(); + constructor( + private idleService: IdleDetectionService, + private scservice: ServerConfigService + ) {} + ngOnInit(): void { - // Children may contain 'reference' component, so we need to - // normalize them + // Children may contain 'reference' component, so we need to normalize them this.arChildren$ = ReferenceComponent.normalizePConnArray(this.arChildren$); + + this.checkAndEnableAutoSave(); } ngOnChanges() { - // Children may contain 'reference' component, so we need to - // normalize them + // Children may contain 'reference' component, so we need to normalize them this.arChildren$ = ReferenceComponent.normalizePConnArray(this.arChildren$); } + ngOnDestroy() { + this.idleService.stopWatching(); + } + + async checkAndEnableAutoSave() { + const sdkConfig = await this.scservice.getSdkConfig(); + const autoSave = sdkConfig.serverConfig.autoSave; + + if (autoSave) { + this.idleService.startWatching(() => this.autoSave(), autoSave); + } + } + onActionButtonClick(oData: any) { this.actionButtonClick.emit(oData); } + + autoSave() { + const context = this.pConn$.getContextName(); + if (PCore.getFormUtils().isStateModified(context)) { + this.pConn$.getActionsApi().saveAssignment(context); + } + } } diff --git a/packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.ts index d9da6926..8ebef98e 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/assignment/assignment.component.ts @@ -9,6 +9,14 @@ import { ProgressSpinnerService } from '../../../_messages/progress-spinner.serv import { ReferenceComponent } from '../../infra/reference/reference.component'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; +function getRefreshProps(refreshConditions) { + // refreshConditions cuurently supports only "Changes" event + if (!refreshConditions) { + return []; + } + return refreshConditions.filter(item => item.event && item.event === 'Changes').map(item => [item.field, item.field?.substring(1)]) || []; +} + interface AssignmentProps { // If any, enter additional props that only exist on this component template: string; @@ -38,7 +46,6 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges { newPConn$: any; containerName$: string; - bIsRefComponent = false; bInitialized = false; templateName$: string; @@ -127,21 +134,14 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges { } updateChanges() { - this.bIsRefComponent = this.checkIfRefComponent(this.pConn$); + this.registerForRefresh(); - this.ngZone.run(() => { - // pConn$ may be a 'reference' component, so normalize it - // this.pConn$ = ReferenceComponent.normalizePConn(this.pConn$); - this.newPConn$ = ReferenceComponent.normalizePConn(this.pConn$); - - // If 'reference' so we need to get the children of the normalized pConn - if (this.bIsRefComponent) { - // this.arChildren$ = ReferenceComponent.normalizePConnArray(this.pConn$.getChildren()); - this.arChildren$ = ReferenceComponent.normalizePConnArray(this.newPConn$.getChildren()); - } - }); + // pConn$ may be a 'reference' component, so normalize it + this.newPConn$ = ReferenceComponent.normalizePConn(this.pConn$); - this.createButtons(); + if (this.arChildren$) { + this.createButtons(); + } } checkIfRefComponent(thePConn: any): boolean { @@ -154,18 +154,10 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges { } initComponent() { - this.bIsRefComponent = this.checkIfRefComponent(this.pConn$); - // pConn$ may be a 'reference' component, so normalize it // this.pConn$ = ReferenceComponent.normalizePConn(this.pConn$); this.newPConn$ = ReferenceComponent.normalizePConn(this.pConn$); - // If 'reference' so we need to get the children of the normalized pConn - if (this.bIsRefComponent) { - // this.arChildren$ = ReferenceComponent.normalizePConnArray(this.pConn$.getChildren()); - this.arChildren$ = ReferenceComponent.normalizePConnArray(this.newPConn$.getChildren()); - } - // prevent re-intializing with flowContainer update unless an action is taken this.bReInit = false; this.bHasNavigation$ = false; @@ -206,7 +198,9 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges { this.cancelCreateStageAssignment = actionsAPI.cancelCreateStageAssignment.bind(actionsAPI); - this.createButtons(); + if (this.arChildren$) { + this.createButtons(); + } } createButtons() { @@ -460,4 +454,36 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges { control.markAsTouched(); }); } + + registerForRefresh() { + // @ts-ignore - Property 'getActionRefreshConditions' is private and only accessible within class 'CaseInfo' + const refreshConditions = this.pConn$.getCaseInfo()?.getActionRefreshConditions(); + const pageReference = this.pConn$.getPageReference(); + const context = this.pConn$.getContextName(); + + // refresh api de-registration + PCore.getRefreshManager().deRegisterForRefresh(context); + + // refresh api registration + const refreshProps = getRefreshProps(refreshConditions); + const caseKey = this.pConn$.getCaseInfo().getKey(); + const refreshOptions = { + autoDetectRefresh: true, + preserveClientChanges: false + }; + if (refreshProps.length > 0) { + refreshProps.forEach(prop => { + PCore.getRefreshManager().registerForRefresh( + 'PROP_CHANGE', + this.pConn$.getActionsApi().refreshCaseView.bind(this.pConn$.getActionsApi(), caseKey, null, pageReference, { + ...refreshOptions, + refreshFor: prop[0] + }), + `${pageReference}.${prop[1]}`, + `${context}/${pageReference}`, + context + ); + }); + } + } } diff --git a/packages/angular-sdk-components/src/lib/_components/infra/reference/reference.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/reference/reference.component.ts index eb0b79ff..eee384c8 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/reference/reference.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/reference/reference.component.ts @@ -33,13 +33,9 @@ export class ReferenceComponent { static createFullReferencedViewFromRef(inPConn: any) { // BAIL and ERROR if inPConn is NOT a reference! if (inPConn.getComponentName() !== 'reference') { - // debugger; - console.error(`Reference component: createFullReferencedViewFromRef inPConn is NOT a reference! ${inPConn.getComponentName()}`); } - const theResolvedConfigProps = inPConn.resolveConfigProps(inPConn.getConfigProps()); - const referenceConfig = { ...inPConn.getComponentConfig() } || {}; // Since SDK-A implements Reference as static methods and we don't rely on @@ -47,8 +43,8 @@ export class ReferenceComponent { // (and also leaving the others in for now) so the referenced View can act on // the visibility prop. (The following 3 lines were carried over from React SDK) delete referenceConfig?.name; - // delete referenceConfig?.type; - // delete referenceConfig?.visibility; + delete referenceConfig?.type; + delete referenceConfig?.visibility; const viewMetadata = inPConn.getReferencedView(); @@ -67,13 +63,15 @@ export class ReferenceComponent { ...referenceConfig } }; + const theResolvedConfigProps = inPConn.resolveConfigProps(inPConn.getConfigProps()); + const { visibility = true, context, readOnly = false, displayMode = '' } = theResolvedConfigProps; if (ReferenceComponent.bLogging) { - console.log(`Reference: about to call createComponent with pageReference: context: ${theResolvedConfigProps.context}`); + console.log(`Reference: about to call createComponent with pageReference: context: ${inPConn.getContextName()}`); } const viewComponent = inPConn.createComponent(viewObject, null, null, { - pageReference: theResolvedConfigProps.context + pageReference: context && context.startsWith('@CLASS') ? '' : context }); // updating the referencedComponent should trigger a render @@ -81,8 +79,8 @@ export class ReferenceComponent { newCompPConnect.setInheritedConfig({ ...referenceConfig, - readOnly: theResolvedConfigProps.readOnly ? theResolvedConfigProps.readOnly : false, - displayMode: theResolvedConfigProps.displayMode ? theResolvedConfigProps.displayMode : null + readOnly, + displayMode }); if (ReferenceComponent.bLogging) { @@ -93,7 +91,11 @@ export class ReferenceComponent { ); } - return newCompPConnect; + if (visibility !== false) { + return newCompPConnect; + } + + return null; } // STATIC method that other components can call to normalize @@ -130,7 +132,7 @@ export class ReferenceComponent { // debugger; let theRefViewPConn = this.createFullReferencedViewFromRef(inPConn.getPConnect()); // now return its PConnect - theRefViewPConn = theRefViewPConn.getComponent(); + theRefViewPConn = theRefViewPConn?.getComponent(); // const theFullReference = theRefViewPConn.getPConnect().getFullReference(); // console.log(`theFullReference: ${theFullReference}`); diff --git a/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.html b/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.html index f333d3d4..ee22fe01 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.html +++ b/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.html @@ -5,20 +5,11 @@
- - - -
{{ localizedVal('RootContainer Missing: ' + componentName$, localeCategory) }}.
@@ -28,9 +19,5 @@
- +
diff --git a/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.ts index 95aaa9c9..642c123e 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/root-container/root-container.component.ts @@ -128,16 +128,6 @@ export class RootContainerComponent implements OnInit, OnDestroy { } } - modalVisibleChanged(isVisible) { - if (this.displayOnlyFA$) { - if (isVisible) { - this.bShowRoot$ = false; - } else { - this.bShowRoot$ = true; - } - } - } - updateSelf() { // need to call this.getCurrentCompleteProps (not this.thePConn.getConfigProps) // to get full set of props that affect this component in Redux diff --git a/packages/angular-sdk-components/src/lib/_components/infra/view/view.component.ts b/packages/angular-sdk-components/src/lib/_components/infra/view/view.component.ts index c1efa973..1dc3800b 100644 --- a/packages/angular-sdk-components/src/lib/_components/infra/view/view.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/infra/view/view.component.ts @@ -159,7 +159,7 @@ export class ViewComponent implements OnInit, OnDestroy, OnChanges { * The resolution lies in transferring this responsibility to the Reference component, eliminating the need for this code when Reference * component is able to handle it. */ - if (this.pConn$.getPageReference().length > 'caseInfo.content'.length) { + if (!this.configProps$.visibility && this.pConn$.getPageReference().length > 'caseInfo.content'.length) { this.visibility$ = evaluateVisibility(this.pConn$); } diff --git a/packages/angular-sdk-components/src/lib/_components/template/default-form/default-form.component.ts b/packages/angular-sdk-components/src/lib/_components/template/default-form/default-form.component.ts index 811cc760..e6774326 100644 --- a/packages/angular-sdk-components/src/lib/_components/template/default-form/default-form.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/template/default-form/default-form.component.ts @@ -1,10 +1,22 @@ -import { Component, OnInit, Input, forwardRef } from '@angular/core'; +import { Component, OnInit, Input, forwardRef, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormGroup } from '@angular/forms'; import { ReferenceComponent } from '../../infra/reference/reference.component'; +import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect'; import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component'; import { TemplateUtils } from '../../../_helpers/template-utils'; +function areViewsChanged(oldViews: any[], newViews: any[]): boolean { + if (oldViews.length !== newViews.length) { + return true; + } + + return !oldViews.every((oldView, index) => { + const newView = newViews[index]; + return oldView.getPConnect().viewName === newView.getPConnect().viewName; + }); +} + interface DefaultFormProps { // If any, enter additional props that only exist on this component NumCols: string; @@ -19,11 +31,14 @@ interface DefaultFormProps { standalone: true, imports: [CommonModule, forwardRef(() => ComponentMapperComponent)] }) -export class DefaultFormComponent implements OnInit { +export class DefaultFormComponent implements OnInit, OnDestroy { @Input() pConn$: typeof PConnect; @Input() formGroup$: FormGroup; - arChildren$: any[]; + // Used with AngularPConnect + angularPConnectData: AngularPConnectData = {}; + + arChildren$: any[] = []; divClass$: string; template: any; showLabel: any; @@ -41,9 +56,29 @@ export class DefaultFormComponent implements OnInit { 'Confirmation' ]; - constructor(private templateUtils: TemplateUtils) {} + constructor( + private angularPConnect: AngularPConnectService, + private templateUtils: TemplateUtils + ) {} ngOnInit(): void { + // First thing in initialization is registering and subscribing to the AngularPConnect service + this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange); + + this.updateSelf(); + } + + ngOnDestroy() { + if (this.angularPConnectData.unsubscribeFn) { + this.angularPConnectData.unsubscribeFn(); + } + } + + onStateChange() { + this.updateSelf(); + } + + updateSelf() { const configProps = this.pConn$.getConfigProps() as DefaultFormProps; this.template = configProps?.template; const propToUse: any = { ...this.pConn$.getInheritedProps() }; @@ -71,6 +106,12 @@ export class DefaultFormComponent implements OnInit { // repoint children before getting templateArray // Children may contain 'reference' component, so we need to // normalize them - this.arChildren$ = ReferenceComponent.normalizePConnArray(kids[0].getPConnect().getChildren()); + const children = ReferenceComponent.normalizePConnArray(kids[0].getPConnect().getChildren()); + + const visibleChildren = children?.filter(child => child !== undefined) || []; + + if (areViewsChanged(this.arChildren$, visibleChildren)) { + this.arChildren$ = visibleChildren; + } } } diff --git a/packages/angular-sdk-components/src/lib/_components/template/field-group-template/field-group-template.component.ts b/packages/angular-sdk-components/src/lib/_components/template/field-group-template/field-group-template.component.ts index 0779f1de..bbacb625 100644 --- a/packages/angular-sdk-components/src/lib/_components/template/field-group-template/field-group-template.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/template/field-group-template/field-group-template.component.ts @@ -117,7 +117,7 @@ export class FieldGroupTemplateComponent implements OnInit, OnDestroy, OnChanges this.pConn$.setInheritedProp('displayMode', 'LABELS_LEFT'); } this.referenceList = this.configProps$.referenceList; - if (this.prevRefLength != this.referenceList.length) { + if (this.prevRefLength != this.referenceList?.length) { // eslint-disable-next-line sonarjs/no-collapsible-if if (!this.readonlyMode) { if (this.referenceList?.length === 0 && this.allowAddEdit !== false) { diff --git a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/helpers.ts b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/helpers.ts index 708cf536..65842a88 100644 --- a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/helpers.ts +++ b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/helpers.ts @@ -44,18 +44,19 @@ function getFieldWidth(field, label) { export const getContext = thePConn => { const contextName = thePConn.getContextName(); const pageReference = thePConn.getPageReference(); - // 8.7 change = referenceList may now be in top-level of state props, - // not always in config of state props - let { referenceList } = thePConn.getStateProps()?.config || thePConn.getStateProps(); + const { readonlyContextList, referenceList = readonlyContextList } = thePConn.getStateProps()?.config || thePConn.getStateProps(); const pageReferenceForRows = referenceList.startsWith('.') ? `${pageReference}.${referenceList.substring(1)}` : referenceList; + const viewName = thePConn.viewName; // removing "caseInfo.content" prefix to avoid setting it as a target while preparing pageInstructions - referenceList = pageReferenceForRows.replace(PCore.getConstants().CASE_INFO.CASE_INFO_CONTENT, ''); + // skipping the removal as StateMachine itself is removing this case info prefix while preparing pageInstructions + // referenceList = pageReferenceForRows.replace(PCore.getConstants().CASE_INFO.CASE_INFO_CONTENT, ''); return { contextName, referenceListStr: referenceList, - pageReferenceForRows + pageReferenceForRows, + viewName }; }; diff --git a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.scss b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.scss index b23e74e5..a4bd3638 100644 --- a/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.scss +++ b/packages/angular-sdk-components/src/lib/_components/template/simple-table-manual/simple-table-manual.component.scss @@ -4,6 +4,7 @@ width: 100%; margin-top: 0.5rem; margin-bottom: 0.5rem; + overflow-y: auto; } table { 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 a462cadd..67b24cd5 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 @@ -965,9 +965,7 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy { this.rawFields?.forEach(item => { const referenceListData = getReferenceList(this.pConn$); const isDatapage = referenceListData.startsWith('D_'); - const pageReferenceValue = isDatapage - ? `${referenceListData}[${index}]` - : `${this.pConn$.getPageReference()}${referenceListData.substring(referenceListData.lastIndexOf('.'))}[${index}]`; + const pageReferenceValue = isDatapage ? `${referenceListData}[${index}]` : `${this.pConn$.getPageReference()}${referenceListData}[${index}]`; const config = { meta: item, options: { diff --git a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.html b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.html index 94abac6f..0e4d32c8 100644 --- a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.html +++ b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.html @@ -1,10 +1,9 @@
-
-
{{ this.currentUserInitials$ }}
+
+
{{ this.currentUserInitials$ }}
{{ headerText$ }}
-
{{ assignmentCount$ }}
+
{{ assignmentCount$ }}
-

@@ -26,7 +25,7 @@
-
+
diff --git a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.scss b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.scss index 7501300a..a5635580 100644 --- a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.scss +++ b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.scss @@ -1,5 +1,12 @@ +.psdk-todo-assignments > *:last-child { + .psdk-display-divider { + display: none; + } +} + .psdk-display-divider { border-bottom: 0.0625rem solid var(--app-neutral-light-color); + margin-block: 0.5rem; } .psdk-todo { diff --git a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.ts b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.ts index 90b089c4..5e7850eb 100644 --- a/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.ts +++ b/packages/angular-sdk-components/src/lib/_components/widget/todo/todo.component.ts @@ -181,6 +181,10 @@ export class TodoComponent implements OnInit, OnDestroy, OnChanges { return this.type$ === this.CONSTS.TODO ? assignment.name : assignment.stepName; } + get canPerform() { + return this.assignmentsSource$?.[0]?.canPerform === 'true' || this.assignmentsSource$?.[0]?.canPerform === true; + } + initAssignments(): any[] { if (this.assignmentsSource$) { this.assignmentCount$ = this.assignmentsSource$.length; @@ -230,7 +234,7 @@ export class TodoComponent implements OnInit, OnDestroy, OnChanges { const sTarget = this.pConn$.getContainerName(); const sTargetContainerName = sTarget; - const options: any = { containerName: sTargetContainerName }; + const options: any = { containerName: sTargetContainerName, channelName: '' }; if (classname === null || classname === '') { classname = this.pConn$.getCaseInfo().getClassName(); diff --git a/packages/angular-sdk-components/src/lib/_services/idle-detection.service.ts b/packages/angular-sdk-components/src/lib/_services/idle-detection.service.ts new file mode 100644 index 00000000..a1aa2775 --- /dev/null +++ b/packages/angular-sdk-components/src/lib/_services/idle-detection.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { fromEvent, merge, Subscription, timer } from 'rxjs'; +import { switchMap, tap } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class IdleDetectionService { + private activityEvents$ = merge(fromEvent(document, 'mousemove'), fromEvent(document, 'keydown'), fromEvent(document, 'click')); + + private idleSubscription!: Subscription; + + startWatching(idleCallback: () => void, idleTimeout: number = 10000) { + this.idleSubscription = this.activityEvents$.pipe(switchMap(() => timer(idleTimeout).pipe(tap(() => idleCallback())))).subscribe(); + } + + stopWatching() { + if (this.idleSubscription) { + this.idleSubscription.unsubscribe(); + } + } +} diff --git a/packages/angular-sdk-components/src/public-api.ts b/packages/angular-sdk-components/src/public-api.ts index 9bafa77b..d3680ef6 100644 --- a/packages/angular-sdk-components/src/public-api.ts +++ b/packages/angular-sdk-components/src/public-api.ts @@ -50,6 +50,7 @@ export * from './lib/_components/infra/region/region.component'; export * from './lib/_components/infra/root-container/root-container.component'; export * from './lib/_components/infra/stages/stages.component'; export * from './lib/_components/infra/view/view.component'; +export * from './lib/_components/infra/Containers/base-components/flow-container-base.component'; export * from './lib/_components/template/app-shell/app-shell.component'; export * from './lib/_components/template/case-summary/case-summary.component'; @@ -122,6 +123,7 @@ export * from './lib/_directives/thousand-seperator.directive'; export * from './lib/_services/server-config.service'; export * from './lib/_services/case.service'; export * from './lib/_services/datapage.service'; +export * from './lib/_services/idle-detection.service'; export * from './lib/_services/endpoints'; export * from './lib/_helpers/case-utils'; diff --git a/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.html b/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.html index 150ea9cd..43281b5f 100644 --- a/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.html +++ b/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.html @@ -16,7 +16,7 @@
We need to gather a little information about you.
-
+
diff --git a/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.scss b/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.scss index 6b26284d..961621b4 100644 --- a/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.scss +++ b/projects/angular-test-app/src/app/_samples/embedded/main-screen/main-screen.component.scss @@ -59,8 +59,11 @@ color: var(--app-banner-text-color); } +.mc-info-image-container { + padding: 20px; +} + .mc-info-image { - width: 700px; - margin: 20px; + width: 100%; border-radius: 10px; } diff --git a/projects/angular-test-app/src/app/_samples/embedded/mc-nav/mc-nav.component.ts b/projects/angular-test-app/src/app/_samples/embedded/mc-nav/mc-nav.component.ts index 2e6a79c3..0e4bf4e7 100644 --- a/projects/angular-test-app/src/app/_samples/embedded/mc-nav/mc-nav.component.ts +++ b/projects/angular-test-app/src/app/_samples/embedded/mc-nav/mc-nav.component.ts @@ -187,6 +187,12 @@ export class MCNavComponent implements OnInit, OnDestroy { this.pConn$ = props.getPConnect(); + this.pConn$.getContainerManager().initializeContainers({ + type: 'multiple', + name: 'modal', + context: 'app' + }); + this.bHasPConnect$ = true; this.bPConnectLoaded$ = true; @@ -197,7 +203,7 @@ export class MCNavComponent implements OnInit, OnDestroy { showHideProgress(bShow: boolean) { this.isProgress$ = bShow; - this.cdRef.detectChanges(); + // this.cdRef.detectChanges(); } logOff() {