UI: Improvement VC in mobile mode, fixed catch error message and add validation space in commit message

This commit is contained in:
Vladyslav_Prykhodko 2022-06-27 14:29:57 +03:00
parent a83f99cfd9
commit ced8ff5bd6
20 changed files with 253 additions and 248 deletions

View File

@ -38,11 +38,10 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { selectIsUserLoaded } from '@core/auth/auth.selectors'; import { selectIsUserLoaded } from '@core/auth/auth.selectors';
import { catchError, finalize, map, switchMap, takeWhile, tap } from 'rxjs/operators'; import { catchError, finalize, switchMap, takeWhile, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { ActionLoadFinish, ActionLoadStart } from '@core/interceptors/load.actions'; import { ActionLoadFinish, ActionLoadStart } from '@core/interceptors/load.actions';
import { NULL_UUID } from '@shared/models/id/has-uuid';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -184,6 +183,9 @@ export class EntitiesVersionControlService {
(this.translate.instant(entityTypeTranslations.get(entityLoadError.target.entityType).type) as string).toLowerCase(); (this.translate.instant(entityTypeTranslations.get(entityLoadError.target.entityType).type) as string).toLowerCase();
messageArgs.targetEntityId = entityLoadError.target.id; messageArgs.targetEntityId = entityLoadError.target.id;
break; break;
case EntityLoadErrorType.RUNTIME:
messageArgs.message = entityLoadError.message.toLowerCase();
break;
} }
return this.sanitizer.bypassSecurityTrustHtml(this.translate.instant(messageId, messageArgs)); return this.sanitizer.bypassSecurityTrustHtml(this.translate.instant(messageId, messageArgs));
} }

View File

@ -32,64 +32,63 @@
<fieldset class="fields-group" [disabled]="isLoading$ | async"> <fieldset class="fields-group" [disabled]="isLoading$ | async">
<legend class="group-title" translate>admin.auto-commit-entities</legend> <legend class="group-title" translate>admin.auto-commit-entities</legend>
<div fxLayout="column"> <div fxLayout="column">
<div class="tb-control-list"> <div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType;
<div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType; let $index = index; last as isLast;"
let $index = index; last as isLast;" fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> <mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)">
<mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)"> <mat-expansion-panel-header>
<mat-expansion-panel-header> <div fxFlex fxLayout="row" fxLayoutAlign="start center">
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> <mat-panel-title>
<mat-panel-title> <div fxLayout="row" fxFlex fxLayoutAlign="start center">
<div fxLayout="row" fxFlex fxLayoutAlign="start center"> <div [innerHTML]="entityTypeText(entityTypeFormGroup)"></div>
<div [innerHTML]="entityTypeText(entityTypeFormGroup)"></div> </div>
</div> </mat-panel-title>
</mat-panel-title> <span fxFlex></span>
<span fxFlex></span> <button mat-icon-button style="min-width: 40px;"
<button mat-icon-button style="min-width: 40px;" type="button"
type="button" (click)="removeEntityType($index)"
(click)="removeEntityType($index)" matTooltip="{{ 'action.remove' | translate }}"
matTooltip="{{ 'action.remove' | translate }}" matTooltipPosition="above">
matTooltipPosition="above"> <mat-icon>delete</mat-icon>
<mat-icon>delete</mat-icon> </button>
</button> </div>
</div> </mat-expansion-panel-header>
</mat-expansion-panel-header> <ng-template matExpansionPanelContent>
<ng-template matExpansionPanelContent> <div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em">
<div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em"> <mat-divider></mat-divider>
<mat-divider></mat-divider> <div fxLayout="column" fxLayout.gt-lg="row" fxLayoutGap.gt-lg="16px">
<div fxLayout="column" fxLayout.gt-lg="row" fxLayoutGap.gt-lg="16px"> <div fxLayout="row" fxLayoutGap="16px" fxLayout.xs="column" fxLayoutGap.xs="0px">
<div fxLayout="row" fxLayoutGap="16px"> <tb-entity-type-select
<tb-entity-type-select showLabel
showLabel formControlName="entityType"
formControlName="entityType" required
required [filterAllowedEntityTypes]="false"
[filterAllowedEntityTypes]="false" [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> </tb-entity-type-select>
</tb-entity-type-select> <div formGroupName="config">
<div formGroupName="config"> <tb-branch-autocomplete
<tb-branch-autocomplete emptyPlaceholder="{{ 'version-control.default' | translate }}"
emptyPlaceholder="{{ 'version-control.default' | translate }}" [selectDefaultBranch]="false"
[selectDefaultBranch]="false" formControlName="branch">
formControlName="branch"> </tb-branch-autocomplete>
</tb-branch-autocomplete>
</div>
</div>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px" formGroupName="config">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials">
{{ 'version-control.export-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes">
{{ 'version-control.export-attributes' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveRelations">
{{ 'version-control.export-relations' | translate }}
</mat-checkbox>
</div> </div>
</div> </div>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"
fxLayout.xs="column" fxLayoutAlign.xs="start start" fxLayoutGap.xs="0px" formGroupName="config">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials">
{{ 'version-control.export-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes">
{{ 'version-control.export-attributes' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveRelations">
{{ 'version-control.export-relations' | translate }}
</mat-checkbox>
</div>
</div> </div>
</ng-template> </div>
</mat-expansion-panel> </ng-template>
</div> </mat-expansion-panel>
</div> </div>
<div *ngIf="!entityTypesFormGroupArray().length"> <div *ngIf="!entityTypesFormGroupArray().length">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"

View File

@ -37,11 +37,6 @@
} }
} }
.tb-control-list {
overflow-y: auto;
max-height: 600px;
}
.tb-prompt { .tb-prompt {
margin: 30px 0; margin: 30px 0;
} }

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<section style="min-width: 800px;"> <section style="min-width: 600px;">
<section *ngIf="!versionCreateResult$"> <section *ngIf="!versionCreateResult$">
<mat-toolbar> <mat-toolbar>
<h2>{{ 'version-control.create-entities-version' | translate }}</h2> <h2>{{ 'version-control.create-entities-version' | translate }}</h2>
@ -35,7 +35,8 @@
<mat-form-field class="mat-block" fxFlex> <mat-form-field class="mat-block" fxFlex>
<mat-label translate>version-control.version-name</mat-label> <mat-label translate>version-control.version-name</mat-label>
<input required matInput formControlName="versionName"> <input required matInput formControlName="versionName">
<mat-error *ngIf="createVersionFormGroup.get('versionName').hasError('required')"> <mat-error *ngIf="createVersionFormGroup.get('versionName').hasError('required') ||
createVersionFormGroup.get('versionName').hasError('pattern')">
{{ 'version-control.version-name-required' | translate }} {{ 'version-control.version-name-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>

View File

@ -20,7 +20,9 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { import {
ComplexVersionCreateRequest, ComplexVersionCreateRequest,
createDefaultEntityTypesVersionCreate, createDefaultEntityTypesVersionCreate,
SyncStrategy, syncStrategyHintMap, syncStrategyTranslationMap, SyncStrategy,
syncStrategyHintMap,
syncStrategyTranslationMap,
VersionCreateRequestType, VersionCreateRequestType,
VersionCreationResult VersionCreationResult
} from '@shared/models/vc.models'; } from '@shared/models/vc.models';
@ -82,7 +84,7 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
ngOnInit(): void { ngOnInit(): void {
this.createVersionFormGroup = this.fb.group({ this.createVersionFormGroup = this.fb.group({
branch: [this.branch, [Validators.required]], branch: [this.branch, [Validators.required]],
versionName: [null, [Validators.required]], versionName: [null, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],
syncStrategy: [SyncStrategy.MERGE, Validators.required], syncStrategy: [SyncStrategy.MERGE, Validators.required],
entityTypes: [createDefaultEntityTypesVersionCreate(), []], entityTypes: [createDefaultEntityTypesVersionCreate(), []],
}); });

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<section [ngStyle]="versionLoadResult$ ? {minWidth: '500px'} : {minWidth: '800px'}"> <section [ngStyle]="versionLoadResult$ ? {minWidth: '500px'} : {minWidth: '600px'}">
<section *ngIf="!versionLoadResult$"> <section *ngIf="!versionLoadResult$">
<mat-toolbar> <mat-toolbar>
<h2>{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}</h2> <h2>{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}</h2>

View File

@ -19,81 +19,79 @@
<fieldset class="fields-group"> <fieldset class="fields-group">
<legend class="group-title" translate>version-control.entities-to-export</legend> <legend class="group-title" translate>version-control.entities-to-export</legend>
<div fxLayout="column"> <div fxLayout="column">
<div class="tb-control-list"> <div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType;
<div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType; let $index = index; last as isLast;"
let $index = index; last as isLast;" fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> <mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)">
<mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)"> <mat-expansion-panel-header>
<mat-expansion-panel-header> <div fxFlex fxLayout="row" fxLayoutAlign="start center">
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> <mat-panel-title>
<mat-panel-title> <div fxLayout="row" fxFlex fxLayoutAlign="start center">
<div fxLayout="row" fxFlex fxLayoutAlign="start center"> <div>{{ entityTypeText(entityTypeFormGroup) }}</div>
<div>{{ entityTypeText(entityTypeFormGroup) }}</div> </div>
</div> </mat-panel-title>
</mat-panel-title> <span fxFlex></span>
<span fxFlex></span> <button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;" type="button"
type="button" (click)="removeEntityType($index)"
(click)="removeEntityType($index)" matTooltip="{{ 'action.remove' | translate }}"
matTooltip="{{ 'action.remove' | translate }}" matTooltipPosition="above">
matTooltipPosition="above"> <mat-icon>delete</mat-icon>
<mat-icon>delete</mat-icon> </button>
</button> </div>
</div> </mat-expansion-panel-header>
</mat-expansion-panel-header> <ng-template matExpansionPanelContent>
<ng-template matExpansionPanelContent> <div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em">
<div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em"> <mat-divider></mat-divider>
<mat-divider></mat-divider> <div fxLayout="row" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutGap="16px"> <tb-entity-type-select
<tb-entity-type-select showLabel
showLabel formControlName="entityType"
formControlName="entityType" required
required [filterAllowedEntityTypes]="false"
[filterAllowedEntityTypes]="false" [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> </tb-entity-type-select>
</tb-entity-type-select> <div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config">
<div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config"> <mat-form-field fxFlex class="mat-block">
<mat-form-field fxFlex class="mat-block"> <mat-label translate>version-control.sync-strategy</mat-label>
<mat-label translate>version-control.sync-strategy</mat-label> <mat-select formControlName="syncStrategy">
<mat-select formControlName="syncStrategy"> <mat-option [value]="'default'">
<mat-option [value]="'default'"> {{ 'version-control.default' | translate }}
{{ 'version-control.default' | translate }} </mat-option>
</mat-option> <mat-option *ngFor="let strategy of syncStrategies" [value]="strategy">
<mat-option *ngFor="let strategy of syncStrategies" [value]="strategy"> {{syncStrategyTranslations.get(strategy) | translate}}
{{syncStrategyTranslations.get(strategy) | translate}} </mat-option>
</mat-option> </mat-select>
</mat-select> </mat-form-field>
</mat-form-field> <div fxFlex fxLayout="column" fxLayoutGap="8px">
<div fxFlex fxLayout="column" fxLayoutGap="8px"> <mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials"> {{ 'version-control.export-credentials' | translate }}
{{ 'version-control.export-credentials' | translate }} </mat-checkbox>
</mat-checkbox> <mat-checkbox formControlName="saveAttributes">
<mat-checkbox formControlName="saveAttributes"> {{ 'version-control.export-attributes' | translate }}
{{ 'version-control.export-attributes' | translate }} </mat-checkbox>
</mat-checkbox> <mat-checkbox formControlName="saveRelations">
<mat-checkbox formControlName="saveRelations"> {{ 'version-control.export-relations' | translate }}
{{ 'version-control.export-relations' | translate }} </mat-checkbox>
</mat-checkbox>
</div>
</div> </div>
</div> </div>
<div fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center"
formGroupName="config" style="min-height: 76px;">
<mat-slide-toggle formControlName="allEntities">
{{ 'version-control.all-entities' | translate }}
</mat-slide-toggle>
<tb-entity-list
fxFlex
[fxShow]="!entityTypeFormGroup.get('config').get('allEntities').value"
[entityType]="entityTypeFormGroup.get('entityType').value"
required
formControlName="entityIds">
</tb-entity-list>
</div>
</div> </div>
</ng-template> <div fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center"
</mat-expansion-panel> formGroupName="config" style="min-height: 76px;">
</div> <mat-slide-toggle formControlName="allEntities">
{{ 'version-control.all-entities' | translate }}
</mat-slide-toggle>
<tb-entity-list
fxFlex
[fxShow]="!entityTypeFormGroup.get('config').get('allEntities').value"
[entityType]="entityTypeFormGroup.get('entityType').value"
required
formControlName="entityIds">
</tb-entity-list>
</div>
</div>
</ng-template>
</mat-expansion-panel>
</div> </div>
<div *ngIf="!entityTypesFormGroupArray().length"> <div *ngIf="!entityTypesFormGroupArray().length">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"

View File

@ -32,11 +32,6 @@
} }
} }
.tb-control-list {
overflow-y: auto;
max-height: 600px;
}
.tb-prompt { .tb-prompt {
margin: 30px 0; margin: 30px 0;
} }

View File

@ -19,67 +19,65 @@
<fieldset class="fields-group"> <fieldset class="fields-group">
<legend class="group-title" translate>version-control.entities-to-restore</legend> <legend class="group-title" translate>version-control.entities-to-restore</legend>
<div fxLayout="column"> <div fxLayout="column">
<div class="tb-control-list"> <div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType;
<div *ngFor="let entityTypeFormGroup of entityTypesFormGroupArray(); trackBy: trackByEntityType; let $index = index; last as isLast;"
let $index = index; last as isLast;" fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
fxLayout="row" fxLayoutAlign="start center" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> <mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)">
<mat-expansion-panel class="entity-type-config" fxFlex [formGroup]="entityTypeFormGroup" [expanded]="entityTypesFormGroupExpanded(entityTypeFormGroup)"> <mat-expansion-panel-header>
<mat-expansion-panel-header> <div fxFlex fxLayout="row" fxLayoutAlign="start center">
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> <mat-panel-title>
<mat-panel-title> <div fxLayout="row" fxFlex fxLayoutAlign="start center">
<div fxLayout="row" fxFlex fxLayoutAlign="start center"> <div>{{ entityTypeText(entityTypeFormGroup) }}</div>
<div>{{ entityTypeText(entityTypeFormGroup) }}</div> </div>
</mat-panel-title>
<span fxFlex></span>
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
type="button"
(click)="removeEntityType($index)"
matTooltip="{{ 'action.remove' | translate }}"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider>
<div fxLayout="row" fxLayoutGap="32px">
<tb-entity-type-select
showLabel
formControlName="entityType"
required
[filterAllowedEntityTypes]="false"
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config">
<div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox #removeOtherEntitiesCheckbox
formControlName="removeOtherEntities"
(click)="onRemoveOtherEntities(removeOtherEntitiesCheckbox, entityTypeFormGroup, $event)">
{{ 'version-control.remove-other-entities' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="findExistingEntityByName">
{{ 'version-control.find-existing-entity-by-name' | translate }}
</mat-checkbox>
</div> </div>
</mat-panel-title> <div fxFlex fxLayout="column" fxLayoutGap="8px">
<span fxFlex></span> <mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="loadCredentials">
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;" {{ 'version-control.load-credentials' | translate }}
type="button" </mat-checkbox>
(click)="removeEntityType($index)" <mat-checkbox formControlName="loadAttributes">
matTooltip="{{ 'action.remove' | translate }}" {{ 'version-control.load-attributes' | translate }}
matTooltipPosition="above"> </mat-checkbox>
<mat-icon>delete</mat-icon> <mat-checkbox formControlName="loadRelations">
</button> {{ 'version-control.load-relations' | translate }}
</div> </mat-checkbox>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="entity-type-config-content" fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider>
<div fxLayout="row" fxLayoutGap="32px">
<tb-entity-type-select
showLabel
formControlName="entityType"
required
[filterAllowedEntityTypes]="false"
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config">
<div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox #removeOtherEntitiesCheckbox
formControlName="removeOtherEntities"
(click)="onRemoveOtherEntities(removeOtherEntitiesCheckbox, entityTypeFormGroup, $event)">
{{ 'version-control.remove-other-entities' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="findExistingEntityByName">
{{ 'version-control.find-existing-entity-by-name' | translate }}
</mat-checkbox>
</div>
<div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="loadCredentials">
{{ 'version-control.load-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="loadAttributes">
{{ 'version-control.load-attributes' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="loadRelations">
{{ 'version-control.load-relations' | translate }}
</mat-checkbox>
</div>
</div> </div>
</div> </div>
</div> </div>
</ng-template> </div>
</mat-expansion-panel> </ng-template>
</div> </mat-expansion-panel>
</div> </div>
<div *ngIf="!entityTypesFormGroupArray().length"> <div *ngIf="!entityTypesFormGroupArray().length">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"

View File

@ -32,11 +32,6 @@
} }
} }
.tb-control-list {
overflow-y: auto;
max-height: 600px;
}
.tb-prompt { .tb-prompt {
margin: 30px 0; margin: 30px 0;
} }

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<section style="min-width: 400px;"> <section style="min-width: 300px;">
<section *ngIf="!versionCreateResult$"> <section *ngIf="!versionCreateResult$">
<mat-toolbar> <mat-toolbar>
<h2>{{ 'version-control.create-entity-version' | translate }}</h2> <h2>{{ 'version-control.create-entity-version' | translate }}</h2>
@ -34,7 +34,8 @@
<mat-form-field class="mat-block" fxFlex> <mat-form-field class="mat-block" fxFlex>
<mat-label translate>version-control.version-name</mat-label> <mat-label translate>version-control.version-name</mat-label>
<input required matInput formControlName="versionName"> <input required matInput formControlName="versionName">
<mat-error *ngIf="createVersionFormGroup.get('versionName').hasError('required')"> <mat-error *ngIf="createVersionFormGroup.get('versionName').hasError('required') ||
createVersionFormGroup.get('versionName').hasError('pattern')">
{{ 'version-control.version-name-required' | translate }} {{ 'version-control.version-name-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>

View File

@ -80,7 +80,7 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
this.createVersionFormGroup = this.fb.group({ this.createVersionFormGroup = this.fb.group({
branch: [this.branch, [Validators.required]], branch: [this.branch, [Validators.required]],
versionName: [this.translate.instant('version-control.default-create-entity-version-name', versionName: [this.translate.instant('version-control.default-create-entity-version-name',
{entityName: this.entityName}), [Validators.required]], {entityName: this.entityName}), [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],
saveRelations: [false, []], saveRelations: [false, []],
saveAttributes: [true, []], saveAttributes: [true, []],
saveCredentials: [true, []] saveCredentials: [true, []]

View File

@ -19,6 +19,7 @@
.entity-version-diff-view { .entity-version-diff-view {
position: relative; position: relative;
&:not(.tb-fullscreen) { &:not(.tb-fullscreen) {
max-height: 95vh;
&.content-ready { &.content-ready {
width: 600px; width: 600px;
@media #{$mat-gt-sm} { @media #{$mat-gt-sm} {

View File

@ -161,7 +161,7 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit,
setTimeout(() => { setTimeout(() => {
this.diffCount = this.differ.getNumDiffs(); this.diffCount = this.differ.getNumDiffs();
this.updateHasNextAndPrevious(); this.updateHasNextAndPrevious();
}); }, 1);
}); });
}); });
}); });

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<section [ngStyle]="entityDataInfo ? {minWidth: '400px'} : {}"> <section [ngStyle]="entityDataInfo ? {minWidth: '300px'} : {}">
<section *ngIf="!versionLoadResult$"> <section *ngIf="!versionLoadResult$">
<mat-toolbar *ngIf="entityDataInfo"> <mat-toolbar *ngIf="entityDataInfo">
<h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2> <h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2>

View File

@ -31,32 +31,36 @@
</tb-branch-autocomplete> </tb-branch-autocomplete>
</div> </div>
<span fxFlex></span> <span fxFlex></span>
<button *ngIf="singleEntityMode" mat-stroked-button color="primary" <div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center end">
#createVersionButton <button *ngIf="singleEntityMode" mat-stroked-button color="primary"
[disabled]="(isLoading$ | async)" #createVersionButton
(click)="toggleCreateVersion($event, createVersionButton)"> [disabled]="(isLoading$ | async)"
<mat-icon>update</mat-icon> (click)="toggleCreateVersion($event, createVersionButton)">
{{'version-control.create-version' | translate }} <mat-icon>update</mat-icon>
</button> {{'version-control.create-version' | translate }}
<button *ngIf="!singleEntityMode" mat-stroked-button color="primary" </button>
#complexCreateVersionButton <button *ngIf="!singleEntityMode" mat-stroked-button color="primary"
[disabled]="(isLoading$ | async)" #complexCreateVersionButton
(click)="toggleComplexCreateVersion($event, complexCreateVersionButton)"> [disabled]="(isLoading$ | async)"
<mat-icon>update</mat-icon> (click)="toggleComplexCreateVersion($event, complexCreateVersionButton)">
{{'version-control.create-entities-version' | translate }} <mat-icon>update</mat-icon>
</button> {{'version-control.create-entities-version' | translate }}
<button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()" </button>
matTooltip="{{ 'action.refresh' | translate }}" <div fxLayout="row">
matTooltipPosition="above"> <button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()"
<mat-icon>refresh</mat-icon> matTooltip="{{ 'action.refresh' | translate }}"
</button> matTooltipPosition="above">
<button mat-icon-button <mat-icon>refresh</mat-icon>
[disabled]="isLoading$ | async" </button>
(click)="enterFilterMode()" <button mat-icon-button
matTooltip="{{ 'action.search' | translate }}" [disabled]="isLoading$ | async"
matTooltipPosition="above"> (click)="enterFilterMode()"
<mat-icon>search</mat-icon> matTooltip="{{ 'action.search' | translate }}"
</button> matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
</div>
</div>
</div> </div>
</mat-toolbar> </mat-toolbar>
<mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode"> <mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
@ -70,7 +74,7 @@
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [(ngModel)]="pageLink.textSearch"
placeholder="{{ 'common.enter-search' | translate }}"/> placeholder="{{ 'version-control.search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"
matTooltip="{{ 'action.close' | translate }}" matTooltip="{{ 'action.close' | translate }}"

View File

@ -18,11 +18,15 @@ import {
AfterViewInit, AfterViewInit,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef, EventEmitter, ElementRef,
EventEmitter,
Input, Input,
OnDestroy, OnDestroy,
OnInit, Output, Renderer2, OnInit,
ViewChild, ViewContainerRef Output,
Renderer2,
ViewChild,
ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -213,7 +217,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
} }
} }
} }
}, {}, {}, {}, false); }, {maxHeight: '100vh', height: '100%', padding: '10px'}, {}, {}, false);
createVersionPopover.tbComponentRef.instance.popoverComponent = createVersionPopover; createVersionPopover.tbComponentRef.instance.popoverComponent = createVersionPopover;
} }
} }
@ -240,7 +244,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
} }
} }
} }
}, {}, {}, {}, false); }, {maxHeight: '100vh', height: '100%', padding: '10px'}, {}, {}, false);
complexCreateVersionPopover.tbComponentRef.instance.popoverComponent = complexCreateVersionPopover; complexCreateVersionPopover.tbComponentRef.instance.popoverComponent = complexCreateVersionPopover;
} }
} }
@ -290,7 +294,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
this.versionRestored.emit(); this.versionRestored.emit();
} }
} }
}, {}, {}, {}, false); }, {maxHeight: '100vh', height: '100%', padding: '10px'}, {}, {}, false);
restoreVersionPopover.tbComponentRef.instance.popoverComponent = restoreVersionPopover; restoreVersionPopover.tbComponentRef.instance.popoverComponent = restoreVersionPopover;
} }
} }
@ -312,7 +316,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
onClose: (result: VersionLoadResult | null) => { onClose: (result: VersionLoadResult | null) => {
restoreEntitiesVersionPopover.hide(); restoreEntitiesVersionPopover.hide();
} }
}, {}, {}, {}, false); }, {maxHeight: '100vh', height: '100%', padding: '10px'}, {}, {}, false);
restoreEntitiesVersionPopover.tbComponentRef.instance.popoverComponent = restoreEntitiesVersionPopover; restoreEntitiesVersionPopover.tbComponentRef.instance.popoverComponent = restoreEntitiesVersionPopover;
} }
} }

View File

@ -34,3 +34,8 @@
} }
} }
} }
:host ::ng-deep {
.mat-checkbox-layout {
white-space: normal !important;
}
}

View File

@ -178,13 +178,15 @@ export interface EntityTypeLoadResult {
export enum EntityLoadErrorType { export enum EntityLoadErrorType {
DEVICE_CREDENTIALS_CONFLICT = 'DEVICE_CREDENTIALS_CONFLICT', DEVICE_CREDENTIALS_CONFLICT = 'DEVICE_CREDENTIALS_CONFLICT',
MISSING_REFERENCED_ENTITY = 'MISSING_REFERENCED_ENTITY' MISSING_REFERENCED_ENTITY = 'MISSING_REFERENCED_ENTITY',
RUNTIME = 'RUNTIME'
} }
export const entityLoadErrorTranslationMap = new Map<EntityLoadErrorType, string>( export const entityLoadErrorTranslationMap = new Map<EntityLoadErrorType, string>(
[ [
[EntityLoadErrorType.DEVICE_CREDENTIALS_CONFLICT, 'version-control.device-credentials-conflict'], [EntityLoadErrorType.DEVICE_CREDENTIALS_CONFLICT, 'version-control.device-credentials-conflict'],
[EntityLoadErrorType.MISSING_REFERENCED_ENTITY, 'version-control.missing-referenced-entity'] [EntityLoadErrorType.MISSING_REFERENCED_ENTITY, 'version-control.missing-referenced-entity'],
[EntityLoadErrorType.RUNTIME, 'version-control.runtime-failed']
] ]
); );
@ -192,6 +194,7 @@ export interface EntityLoadError {
type: EntityLoadErrorType; type: EntityLoadErrorType;
source: EntityId; source: EntityId;
target: EntityId; target: EntityId;
message?: string;
} }
export interface VersionLoadResult { export interface VersionLoadResult {

View File

@ -3318,6 +3318,7 @@
"version-control": { "version-control": {
"version-control": "Version control", "version-control": "Version control",
"management": "Version control management", "management": "Version control management",
"search": "Search versions",
"branch": "Branch", "branch": "Branch",
"default": "Default", "default": "Default",
"select-branch": "Select branch", "select-branch": "Select branch",
@ -3378,7 +3379,8 @@
"sync-strategy-merge-hint": "Creates or updates selected entities in the repository. All other repository entities are <b>not modified</b>.", "sync-strategy-merge-hint": "Creates or updates selected entities in the repository. All other repository entities are <b>not modified</b>.",
"sync-strategy-overwrite-hint": "Creates or updates selected entities in the repository. All other repository entities are <b>deleted</b>.", "sync-strategy-overwrite-hint": "Creates or updates selected entities in the repository. All other repository entities are <b>deleted</b>.",
"device-credentials-conflict": "Failed to load the device with external id <b>{{entityId}}</b><br/>due to the same credentials are already present in the database for another device.<br/>Please consider disabling the <b>load credentials</b> setting in the restore form.", "device-credentials-conflict": "Failed to load the device with external id <b>{{entityId}}</b><br/>due to the same credentials are already present in the database for another device.<br/>Please consider disabling the <b>load credentials</b> setting in the restore form.",
"missing-referenced-entity": "Failed to load the <b>{{sourceEntityTypeName}}</b> with external id <b>{{sourceEntityId}}</b><br/>because it references missing <b>{{targetEntityTypeName}}</b> with id <b>{{targetEntityId}}</b>." "missing-referenced-entity": "Failed to load the <b>{{sourceEntityTypeName}}</b> with external id <b>{{sourceEntityId}}</b><br/>because it references missing <b>{{targetEntityTypeName}}</b> with id <b>{{targetEntityId}}</b>.",
"runtime-failed": "<b>Failed:</b> {{message}}"
}, },
"widget": { "widget": {
"widget-library": "Widgets Library", "widget-library": "Widgets Library",