Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync repo from v92 to v92 20240812072532 #169

Merged
merged 4 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a389dcd952875866288789039304ce0360b6d655
d7376dc1bc7c9b8080948907c5bb14befb21039c
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

## Change log

### August 12, 2024
- 459770: Fixed an issue where the request parameter validation result was not working on first input.
- 462048: Fixed an issue with delegation search caching.
- 461659: Fixed an issue regarding delegation or ESet requests on the Request History page.
- 460151: Fixed an issue with the QueryWhereClause evaluation during the value check on updated parameter.
- 460511: Fixed an issue that causes the side navigation on New Request to show search results twice.
- 461414: Fixed the scrollability of the Operation Support Portal's Process view
- 462785: Fixed an UI issue regarding the alignment for the column "User account is disabled" and its data.


### July 30, 2024

The v92 branch has been updated with fixes for the following issues.
Expand Down
92 changes: 35 additions & 57 deletions imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.html
Original file line number Diff line number Diff line change
@@ -1,85 +1,63 @@
<mat-form-field appearance="outline" *ngIf="hasCandidatesOrIsLoading && columnContainer?.canEdit; else noCandidatesAvailOrReadonly">
<mat-form-field appearance="outline"
*ngIf="hasCandidatesOrIsLoading && columnContainer?.canEdit; else noCandidatesAvailOrReadonly">
<mat-label>
{{ columnContainer?.display | translate }}
<ng-container *ngIf="selectedTable && columnContainer?.fkRelations?.length > 1 && !isHierarchical">
({{ '#LDS#Table: {0}' | translate | ldsReplace : metadataProvider.tables[selectedTable.TableName]?.DisplaySingular || selectedTable.TableName }})
({{ '#LDS#Table: {0}' | translate | ldsReplace : metadataProvider.tables[selectedTable.TableName]?.DisplaySingular
|| selectedTable.TableName }})
</ng-container>
</mat-label>
<eui-icon icon="search" *ngIf="columnContainer?.canEdit && auto.isOpen" matPrefix></eui-icon>

<input
#inputControl
matInput
type="search"
[formControl]="control"
[matAutocomplete]="auto"
[readonly]="!columnContainer?.canEdit || isHierarchical"
(keydown.esc)="close($event); inputControl.blur()"
(focusout)="close($event)"
(focus)="inputFocus()"
<input #inputControl matInput type="search" [formControl]="control" [matAutocomplete]="auto"
[readonly]="!columnContainer?.canEdit || isHierarchical" (keydown.esc)="close($event); inputControl.blur()"
(focusout)="close($event)" (focus)="inputFocus()"
[attr.data-imx-identifier]="'cdr-edit-fk-input-search-candidates-' + columnContainer?.name"
[required]="columnContainer.isValueRequired"
/>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="getDisplay" (optionSelected)="optionSelected($event); inputControl.blur()" (opened)="onOpened()" #autocomplete>
<mat-option
*ngFor="let candidate of candidates; index as i; trackBy: candidateTrackByFn"
[value]="candidate"
class="imx-candidate-option"
[attr.data-imx-identifier]="'cdr-edit-fk-mat-option-assign-candidate-' + columnContainer?.name"
>
<div class="imx-candidate-item">
<div class="imx-candidate-content">
<div class="imx-candidate-display">{{ getDisplay(candidate) }}</div>
<div *ngIf="getDisplay(candidate) !== candidate.displayLong" class="imx-candidate-longdisplay">
{{ candidate.displayLong }}
[required]="columnContainer.isValueRequired" />
<mat-autocomplete #auto="matAutocomplete" [displayWith]="getDisplay"
(optionSelected)="optionSelected($event); inputControl.blur()" (opened)="onOpened()" #autocomplete>
<ng-container *ngIf="!loading">
<mat-option *ngFor="let candidate of candidates; index as i; trackBy: candidateTrackByFn" [value]="candidate"
class="imx-candidate-option"
[attr.data-imx-identifier]="'cdr-edit-fk-mat-option-assign-candidate-' + columnContainer?.name">
<div class="imx-candidate-item">
<div class="imx-candidate-content">
<div class="imx-candidate-display">{{ getDisplay(candidate) }}</div>
<div *ngIf="getDisplay(candidate) !== candidate.displayLong" class="imx-candidate-longdisplay">
{{ candidate.displayLong }}
</div>
</div>
</div>
</div>
</mat-option>
<mat-option *ngIf="loading" disabled>{{ '#LDS#Loading...' | translate }}</mat-option>
</mat-option>
</ng-container>
<mat-option *ngIf="loading" disabled>{{ '#LDS#Loading...' | translate }}</mat-option>
</mat-autocomplete>
<div matSuffix class="imx-suffix-container">
<mat-spinner diameter="30" *ngIf="loading"></mat-spinner>
<eui-icon
*ngIf="columnContainer?.canEdit && control.value && !loading"
icon="close"
class="imx-icon-delete"
<eui-icon *ngIf="columnContainer?.canEdit && control.value && !loading" icon="close" class="imx-icon-delete"
(click)="removeAssignment($event)"
[attr.data-imx-identifier]="'cdr-edit-fk-button-remove-assignment-' + columnContainer?.name"
></eui-icon>
[attr.data-imx-identifier]="'cdr-edit-fk-button-remove-assignment-' + columnContainer?.name"></eui-icon>
<div class="imx-spacer"></div>
<button
*ngIf="columnContainer?.canEdit && (isHierarchical || columnContainer?.fkRelations?.length > 1)"
mat-button
color="primary"
type="button"
(click)="editAssignment($event)"
[attr.data-imx-identifier]="'cdr-edit-fk-button-open-picker-' + columnContainer?.name"
>
<button *ngIf="columnContainer?.canEdit && (isHierarchical || columnContainer?.fkRelations?.length > 1)" mat-button
color="primary" type="button" (click)="editAssignment($event)"
[attr.data-imx-identifier]="'cdr-edit-fk-button-open-picker-' + columnContainer?.name">
{{ columnContainer?.value?.length ? ('#LDS#Change' | translate) : ('#LDS#Select' | translate) }}
</button>
</div>
<mat-error *ngIf="control.errors?.checkAutocomplete">
{{ '#LDS#The value entered in the {0} box could not be found. Please select a value from the list.' | translate | ldsReplace : (columnContainer?.display | translate) }}
{{ '#LDS#The value entered in the {0} box could not be found. Please select a value from the list.' | translate |
ldsReplace : (columnContainer?.display | translate) }}
</mat-error>
<mat-error *ngIf="control.errors?.['required']">
{{ '#LDS#This field is mandatory.' | translate }}
</mat-error>
</mat-form-field>

<ng-template #noCandidatesAvailOrReadonly>
<imx-view-property
*ngIf="columnContainer?.canEdit"
[columnContainer]="columnContainer"
defaultValue="#LDS#No data"
[attr.data-imx-identifier]="'cdr-edit-fk-no-candidates-' + columnContainer?.name"
>
<imx-view-property *ngIf="columnContainer?.canEdit" [columnContainer]="columnContainer" defaultValue="#LDS#No data"
[attr.data-imx-identifier]="'cdr-edit-fk-no-candidates-' + columnContainer?.name">
</imx-view-property>
<imx-view-property
*ngIf="!columnContainer?.canEdit"
[columnContainer]="columnContainer"
defaultValue="#LDS#Not set"
[attr.data-imx-identifier]="'cdr-edit-fk-candidates-readonly-' + columnContainer?.name"
>
<imx-view-property *ngIf="!columnContainer?.canEdit" [columnContainer]="columnContainer" defaultValue="#LDS#Not set"
[attr.data-imx-identifier]="'cdr-edit-fk-candidates-readonly-' + columnContainer?.name">
</imx-view-property>
</ng-template>
</ng-template>
30 changes: 20 additions & 10 deletions imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,15 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
/**
* A list of possible candidates, that can be selected.
*/
public candidates: Candidate[];
private _candidates: Candidate[];

public get candidates(): Candidate[] {
return this._candidates;
}

public set candidates(value: Candidate[]) {
this._candidates = value;
}

/**
* Indicator that the component is loading data from the server.
Expand Down Expand Up @@ -211,7 +219,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
public async onOpened(): Promise<void> {
// Use the stashed values if we already have a selected value
this.parameters = this.savedParameters ?? { PageSize: this.pageSize, StartIndex: 0 };
if ((this.savedCandidates?.length ?? 0) > 0) {
if (!!this.savedCandidates?.length) {
this.candidates = this.savedCandidates;
} else if (this.parameters.search || this.parameters.filter || this.control.value == null) {
await this.updateCandidates({ search: undefined, filter: undefined, StartIndex: 0 }, false);
Expand Down Expand Up @@ -485,10 +493,10 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
const multipleFkRelations = this.columnContainer.fkRelations && this.columnContainer.fkRelations.length > 1;
const identityRelatedTable = this.selectedTable.TableName === 'Person';

const newCandidates = candidateCollection.Entities.map((entityData) => {
let key: string = null;
let detailValue: string = entityData.LongDisplay;
const defaultEmailColumn = entityData.Columns['DefaultEmailAddress'];
const newCandidates = candidateCollection.Entities?.map((entityData) => {
let key: string = '';
let detailValue: string = entityData.LongDisplay ?? '';
const defaultEmailColumn = entityData.Columns?.['DefaultEmailAddress'];
/**
* If the candidates data relate to identities (fkRelation Person table)
* then we want to use the email address for the detail line (displayLong)
Expand All @@ -498,7 +506,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
}
if (multipleFkRelations) {
this.logger.trace(this, 'dynamic foreign key');
const xObjectKeyColumn = entityData.Columns['XObjectKey'];
const xObjectKeyColumn = entityData.Columns?.['XObjectKey'];
key = xObjectKeyColumn ? xObjectKeyColumn.Value : undefined;
} else {
this.logger.trace(this, 'foreign key');
Expand All @@ -510,7 +518,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
} else {
this.logger.trace(this, 'Use the primary key');
const keys = entityData.Keys;
key = keys && keys.length ? keys[0] : undefined;
key = keys && keys.length ? keys[0] : '';
}
}
return {
Expand All @@ -520,9 +528,11 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI
};
});
if (concatCandidates) {
this.candidates.push(...newCandidates);
this.candidates.push(...(newCandidates || []));
this.savedCandidates = this.candidates;
} else {
this.candidates = newCandidates;
this.candidates = newCandidates || [];
this.savedCandidates = this.candidates;
}
} finally {
this.loading = false;
Expand Down
45 changes: 29 additions & 16 deletions imxweb/projects/qbm/src/lib/cdr/editor-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*
*/
import { OnDestroy, Component, EventEmitter, ErrorHandler } from '@angular/core';
import { AbstractControl, Validators } from '@angular/forms';
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
import { Subject, Subscription } from 'rxjs';

import { CdrEditor, ValueHasChangedEventArg } from './cdr-editor.interface';
Expand Down Expand Up @@ -73,7 +73,7 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
* @ignore
* Used for the template and displays the last server error, that occured while loading content.
*/
public lastError: ServerError;
public lastError: ServerError | undefined;

/**
* The maximal length a string could have.
Expand All @@ -99,21 +99,20 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
* If an error occured, it returns its message
*/
public get validationErrorMessage(): string {
if (this.control.errors?.['generalError']) {
return this.lastError.toString();
}
return undefined;
return this.lastError?.toString() || '';
}

/**
* Binds a column dependent reference to the component, by setting the control value and subscribing to the events,
* the CDR or the ColumnContainer emits
* the CDR or the ColumnContainer emits
* @param cdref a column dependent reference
*/
public bind(cdref: ColumnDependentReference): void {
if (cdref && cdref.column) {
this.columnContainer.init(cdref);

this.control.addValidators(EditorBase.hasServerError(this));

this.setControlValue();

this.subscribers.push(this.control.valueChanges.subscribe(async (value) => this.writeValue(value)));
Expand All @@ -133,7 +132,7 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
return;
}

if (this.control.value !== this.columnContainer.value) {
if (!this.control.hasError('generalError') && this.control.value !== this.columnContainer.value) {
this.logger.trace(
this,
`Control (${this.columnContainer.name}) set to new value:`,
Expand All @@ -150,8 +149,16 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
this.updateRequested.subscribe(() => {
setTimeout(() => {
try {
this.setControlValue();
this.control.updateValueAndValidity({ onlySelf: true, emitEvent: false });
if (!this.control.hasError('generalError') && this.control.value !== this.columnContainer.value) {
this.logger.trace(
this,
`Control (${this.columnContainer.name}) set to new value:`,
this.columnContainer.value,
this.control.value
);
this.setControlValue();
this.control.updateValueAndValidity({ onlySelf: true, emitEvent: false });
}
} finally {
}
this.valueHasChanged.emit({ value: this.control.value });
Expand All @@ -176,9 +183,9 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
this.columnContainer.type !== ValType.Bool // because bool is always valid
) {
this.logger.debug(this, `A value for column "${this.columnContainer.name}" is required`);
this.control.setValidators(Validators.required);
this.control.setValidators([Validators.required, EditorBase.hasServerError(this)]);
} else {
this.control.setValidators(null);
this.control.setValidators(EditorBase.hasServerError(this));
}
}

Expand All @@ -187,11 +194,10 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
* @param value the new value
*/
private async writeValue(value: any): Promise<void> {
if (this.control.errors) {
this.logger.debug(this, 'writeValue - validation failed');
if (this.control.errors && Object.keys(this.control.errors).some((elem) => elem !== 'generalError')) {
this.logger.debug(this, 'writeValue - client validation failed');
return;
}

this.logger.debug(this, 'writeValue called with value', value);

if (!this.columnContainer.canEdit || this.columnContainer.value === value) {
Expand All @@ -203,10 +209,11 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {
try {
this.logger.debug(this, 'writeValue - PutValue...');
await this.columnContainer.updateValue(value);
this.lastError = undefined;
} catch (e) {
this.lastError = e;
this.logger.error(this, e);
this.control.setErrors({ generalError: true });
this.control.updateValueAndValidity({ emitEvent: true });
} finally {
this.isBusy = false;
this.isWriting = false;
Expand All @@ -218,4 +225,10 @@ export abstract class EditorBase<T = any> implements CdrEditor, OnDestroy {

this.valueHasChanged.emit({ value, forceEmit: true });
}

private static hasServerError(base: any): ValidatorFn {
return (_: AbstractControl): { [key: string]: boolean } | null => {
return !base.lastError ? null : { generalError: true };
};
}
}
11 changes: 7 additions & 4 deletions imxweb/projects/qbm/src/lib/cdr/entity-column-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
*
*/

import { IForeignKeyInfo, IValueMetadata, ValType, ValueConstraint, ValueStruct } from 'imx-qbm-dbts';
import { Subscription } from 'rxjs';
import { ValueWrapper } from '../value-wrapper/value-wrapper';
import { ColumnDependentReference } from './column-dependent-reference.interface';
import { ValueStruct, IForeignKeyInfo, ValType, ValueConstraint, IValueMetadata } from 'imx-qbm-dbts';
import { LimitedValuesContainer } from './limited-values-container';
import { ValueWrapper } from '../value-wrapper/value-wrapper';
import { Subscription } from 'rxjs';

/**
* An implementation of the {@link ValueWrapper} interface.
Expand Down Expand Up @@ -113,6 +113,10 @@ export class EntityColumnContainer<T = any> implements ValueWrapper<T> {
return this.cdr?.hint;
}

public get showDisplayValue(): boolean {
return !!this.metaData?.CanSee();
}

/**
* An container, that wraps limited value functionality
*/
Expand Down Expand Up @@ -141,7 +145,6 @@ export class EntityColumnContainer<T = any> implements ValueWrapper<T> {
return new Subscription(() => subscription?.unsubscribe());
}


/**
* Updates the value and puts it into the column
* @param value The new value for the column
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="imx-readonly-view" readonly>
<span class="column-display">{{ (columnContainer?.display | translate) + (columnContainer.isValueRequired ? '*' :'') }}</span>
<span class="column-value">{{ displayedValue | translate }}</span>
<span class="column-display">{{ (columnContainer?.display | translate) + (columnContainer.isValueRequired ? '*' : '') }}</span>
<span class="column-value">{{ columnContainer.showDisplayValue ? (displayedValue | translate) : '' }}</span>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -1313,18 +1313,18 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy
this.columnOptions?.resetView();

this.searchTerms = [];
if (this.settings.navigationState.search) {
if (this.settings.navigationState?.search) {
this.searchControl.reset(null);
delete this.settings.navigationState.search;
}
if (this.settings.navigationState.OrderBy) {
if (this.settings.navigationState?.OrderBy) {
delete this.settings.navigationState.OrderBy;
this.clearSort(false);
}
if (this.settings?.groupData?.currentGrouping) {
this.clearGroupedBy(false);
}
delete this.settings.navigationState.filter;
delete this.settings.navigationState?.filter;
if (this.filtersCurrentlyApplied) {
this.clearFilters(false);
}
Expand Down
Loading
Loading