Browse Source

Merge remote-tracking branch 'upstream/lts-4.2' into lts-4.3

# Conflicts:
#	ui-ngx/src/app/shared/components/string-items-list.component.html
#	ui-ngx/src/app/shared/components/string-items-list.component.ts
pull/15308/head
Vladyslav Prykhodko 6 months ago
parent
commit
697a31af7a
  1. 4
      ui-ngx/src/app/core/interceptors/global-http-interceptor.ts
  2. 18
      ui-ngx/src/app/core/utils.ts
  3. 9
      ui-ngx/src/app/shared/components/string-items-list.component.html
  4. 45
      ui-ngx/src/app/shared/components/string-items-list.component.ts
  5. 6
      ui-ngx/src/app/shared/models/constants.ts
  6. 6
      ui-ngx/yarn.lock

4
ui-ngx/src/app/core/interceptors/global-http-interceptor.ts

@ -129,10 +129,6 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
this.showError(req.method + ': ' + req.url + '<br/>' + this.showError(req.method + ': ' + req.url + '<br/>' +
errorResponse.status + ': ' + errorResponse.statusText); errorResponse.status + ': ' + errorResponse.statusText);
} }
} else if (errorResponse.status === 504) {
if (!ignoreErrors) {
this.showError('Request timeout');
}
} else { } else {
unhandled = true; unhandled = true;
} }

18
ui-ngx/src/app/core/utils.ts

@ -23,7 +23,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid';
import { baseDetailsPageByEntityType, EntityType } from '@shared/models/entity-type.models'; import { baseDetailsPageByEntityType, EntityType } from '@shared/models/entity-type.models';
import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { serverErrorCodesTranslations } from '@shared/models/constants'; import { httpStatusMessageMap, serverErrorCodesTranslations } from '@shared/models/constants';
import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models';
import { import {
CompiledTbFunction, CompiledTbFunction,
@ -910,11 +910,13 @@ export function parseHttpErrorMessage(errorResponse: HttpErrorResponse,
} else { } else {
error = errorResponse.error; error = errorResponse.error;
} }
if (error && !error.message) { if (error && error.message) {
errorMessage = prepareMessageFromData(error);
} else if (error && error.message) {
errorMessage = error.message; errorMessage = error.message;
timeout = error.timeout ? error.timeout : 0; timeout = error.timeout ? error.timeout : 0;
} else if (isProxyError(errorResponse)) {
errorMessage = httpStatusMessageMap.get(errorResponse.status);
} else if (error) {
errorMessage = prepareMessageFromData(error);
} else { } else {
errorMessage = `Unhandled error code ${error ? error.status : '\'Unknown\''}`; errorMessage = `Unhandled error code ${error ? error.status : '\'Unknown\''}`;
} }
@ -951,6 +953,14 @@ function prepareMessageFromData(data): string {
} }
} }
function isProxyError(errorResponse: HttpErrorResponse): boolean {
if (!httpStatusMessageMap.has(errorResponse.status)) {
return false;
}
const error = errorResponse.error;
return !error || typeof error === 'string' || (typeof error === 'object' && !error.message);
}
export const genNextLabel = (name: string, datasources: Datasource[]): string => { export const genNextLabel = (name: string, datasources: Datasource[]): string => {
let label = name; let label = name;
let i = 1; let i = 1;

9
ui-ngx/src/app/shared/components/string-items-list.component.html

@ -31,7 +31,6 @@
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon> <mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip-row> </mat-chip-row>
<input matInput type="text" <input matInput type="text"
(blur)="addOnBlur($event)"
placeholder="{{ placeholder }}" placeholder="{{ placeholder }}"
style="max-width: 300px;min-width: 250px" style="max-width: 300px;min-width: 250px"
#stringItemInput #stringItemInput
@ -40,20 +39,22 @@
#origin="matAutocompleteOrigin" #origin="matAutocompleteOrigin"
[matChipInputFor]="itemsChipList" [matChipInputFor]="itemsChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
matAutocompleteOrigin matAutocompleteOrigin
(matChipInputTokenEnd)="addOnEnd($event)" (matChipInputTokenEnd)="addOnEnd($event)"
[matAutocompleteConnectedTo]="origin" [matAutocompleteConnectedTo]="origin"
[matAutocomplete]="stringItemAutocomplete" [matAutocomplete]="stringItemAutocomplete"
[matAutocompleteDisabled]="!predefinedValues?.length"> [matAutocompleteDisabled]="!predefinedValues?.length && !fetchOptionsFn">
</mat-chip-grid> </mat-chip-grid>
<mat-autocomplete #stringItemAutocomplete="matAutocomplete" <mat-autocomplete #stringItemAutocomplete="matAutocomplete"
[displayWith]="displayValueFn" [displayWith]="displayValueFn"
(optionSelected)="onOptionSelected($event)"
class="tb-autocomplete"> class="tb-autocomplete">
@for (value of filteredValues | async; track value.value) { @for (value of filteredValues | async; track value.value) {
<mat-option [value]="value"><span [innerHTML]="value.name | highlight:searchText"></span></mat-option> <mat-option [value]="value" (mousedown)="$event.preventDefault()"><span [innerHTML]="value.name | highlight:searchText:true"></span></mat-option>
} @empty { } @empty {
@if (!allowUserValue) { @if (!allowUserValue) {
<mat-option [value]="null">{{ 'common.not-found' | translate }}</mat-option> <mat-option [value]="null" (mousedown)="$event.preventDefault()">{{ 'common.not-found' | translate }}</mat-option>
} }
} }
</mat-autocomplete> </mat-autocomplete>

45
ui-ngx/src/app/shared/components/string-items-list.component.ts

@ -29,7 +29,7 @@ import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angula
import { coerceArray, coerceBoolean } from '@shared/decorators/coercion'; import { coerceArray, coerceBoolean } from '@shared/decorators/coercion';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { filter, mergeMap, share, tap } from 'rxjs/operators'; import { filter, mergeMap, share, tap } from 'rxjs/operators';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { isDefined, isUndefined } from '@core/utils'; import { isDefined, isUndefined } from '@core/utils';
export interface StringItemsOption { export interface StringItemsOption {
@ -37,18 +37,18 @@ export interface StringItemsOption {
value: any; value: any;
} }
@Component({ @Component({
selector: 'tb-string-items-list', selector: 'tb-string-items-list',
templateUrl: './string-items-list.component.html', templateUrl: './string-items-list.component.html',
styleUrls: [], styleUrls: [],
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => StringItemsListComponent), useExisting: forwardRef(() => StringItemsListComponent),
multi: true multi: true
} }
], ],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: false standalone: false
}) })
export class StringItemsListComponent implements ControlValueAccessor, OnInit { export class StringItemsListComponent implements ControlValueAccessor, OnInit {
@ -149,9 +149,7 @@ export class StringItemsListComponent implements ControlValueAccessor, OnInit {
this.filteredValues = this.itemControl.valueChanges this.filteredValues = this.itemControl.valueChanges
.pipe( .pipe(
tap((value) => { tap((value) => {
if (value && typeof value !== 'string') { if (value === null) {
this.add(value);
} else if (value === null) {
this.clear(); this.clear();
} }
}), }),
@ -209,16 +207,17 @@ export class StringItemsListComponent implements ControlValueAccessor, OnInit {
this.dirty = true; this.dirty = true;
} }
addOnBlur(event: FocusEvent) { onOptionSelected(event: MatAutocompleteSelectedEvent): void {
const target: HTMLElement = event.relatedTarget as HTMLElement; if (event.option.value != null) {
if ((target && target.tagName !== 'MAT-OPTION') || this.allowUserValue) { this.add(event.option.value);
this.addItem(this.stringItemInput.nativeElement.value ?? ''); } else {
this.clear();
} }
this.onTouched();
} }
addOnEnd(event: MatChipInputEvent): void { addOnEnd(event: MatChipInputEvent): void {
this.addItem(event.value ?? '') this.addItem(event.value ?? '');
this.onTouched();
} }
removeItems(item: StringItemsOption) { removeItems(item: StringItemsOption) {
@ -295,6 +294,8 @@ export class StringItemsListComponent implements ControlValueAccessor, OnInit {
this.stringItemInput.nativeElement.value = value; this.stringItemInput.nativeElement.value = value;
this.itemControl.patchValue(value, {emitEvent: true}); this.itemControl.patchValue(value, {emitEvent: true});
setTimeout(() => { setTimeout(() => {
this.stringItemInput.nativeElement.value = value;
this.itemControl.patchValue(null, {emitEvent: false});
this.stringItemInput.nativeElement.blur(); this.stringItemInput.nativeElement.blur();
this.stringItemInput.nativeElement.focus(); this.stringItemInput.nativeElement.focus();
}, 0); }, 0);

6
ui-ngx/src/app/shared/models/constants.ts

@ -56,6 +56,12 @@ export const serverErrorCodesTranslations = new Map<number, string>([
[Constants.serverErrorCode.entitiesLimitExceeded, 'server-error.entities-limit-exceeded'], [Constants.serverErrorCode.entitiesLimitExceeded, 'server-error.entities-limit-exceeded'],
]); ]);
export const httpStatusMessageMap = new Map<number, string>([
[502, 'Server is temporarily unavailable (Bad Gateway)'],
[503, 'Server is temporarily unavailable'],
[504, 'Server did not respond in time (Gateway Timeout)'],
]);
export const MediaBreakpoints = { export const MediaBreakpoints = {
xs: 'screen and (max-width: 599px)', xs: 'screen and (max-width: 599px)',
sm: 'screen and (min-width: 600px) and (max-width: 959px)', sm: 'screen and (min-width: 600px) and (max-width: 959px)',

6
ui-ngx/yarn.lock

@ -6033,9 +6033,9 @@ flat@^5.0.2:
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
flatted@^3.2.9: flatted@^3.2.9:
version "3.4.1" version "3.4.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.1.tgz#84ccd9579e76e9cc0d246c11d8be0beb019143e6" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
integrity sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ== integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
"flot.curvedlines@https://github.com/MichaelZinsmaier/CurvedLines.git#master": "flot.curvedlines@https://github.com/MichaelZinsmaier/CurvedLines.git#master":
version "1.1.1" version "1.1.1"

Loading…
Cancel
Save