UI: Implement entity version restore

This commit is contained in:
Igor Kulikov 2022-05-26 13:50:50 +03:00
parent 65dc70c51b
commit 46e0c7c908
20 changed files with 347 additions and 66 deletions

View File

@ -190,7 +190,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
SingleEntityVersionLoadRequest versionLoadRequest = (SingleEntityVersionLoadRequest) request; SingleEntityVersionLoadRequest versionLoadRequest = (SingleEntityVersionLoadRequest) request;
VersionLoadConfig config = versionLoadRequest.getConfig(); VersionLoadConfig config = versionLoadRequest.getConfig();
ListenableFuture<EntityExportData> future = gitServiceQueue.getEntity(user.getTenantId(), request.getVersionId(), versionLoadRequest.getExternalEntityId()); ListenableFuture<EntityExportData> future = gitServiceQueue.getEntity(user.getTenantId(), request.getVersionId(), versionLoadRequest.getExternalEntityId());
Futures.transform(future, entityData -> { return Futures.transform(future, entityData -> {
EntityImportResult<?> importResult = transactionTemplate.execute(status -> { EntityImportResult<?> importResult = transactionTemplate.execute(status -> {
try { try {
return exportImportService.importEntity(user, entityData, EntityImportSettings.builder() return exportImportService.importEntity(user, entityData, EntityImportSettings.builder()

View File

@ -18,7 +18,13 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
import { combineLatest, Observable, of } from 'rxjs'; import { combineLatest, Observable, of } from 'rxjs';
import { BranchInfo, EntityVersion, VersionCreateRequest, VersionCreationResult } from '@shared/models/vc.models'; import {
BranchInfo,
EntityVersion,
VersionCreateRequest,
VersionCreationResult,
VersionLoadRequest, VersionLoadResult
} from '@shared/models/vc.models';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
@ -95,4 +101,8 @@ export class EntitiesVersionControlService {
return this.http.get<PageData<EntityVersion>>(`/api/entities/vc/version/${branch}${pageLink.toQuery()}`, return this.http.get<PageData<EntityVersion>>(`/api/entities/vc/version/${branch}${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public loadEntitiesVersion(request: VersionLoadRequest, config?: RequestConfig): Observable<Array<VersionLoadResult>> {
return this.http.post<Array<VersionLoadResult>>('/api/entities/vc/entity', request, defaultHttpOptionsFromConfig(config));
}
} }

View File

@ -157,6 +157,7 @@ import { VersionControlSettingsComponent } from '@home/components/vc/version-con
import { VersionControlComponent } from '@home/components/vc/version-control.component'; import { VersionControlComponent } from '@home/components/vc/version-control.component';
import { EntityVersionsTableComponent } from '@home/components/vc/entity-versions-table.component'; import { EntityVersionsTableComponent } from '@home/components/vc/entity-versions-table.component';
import { EntityVersionExportComponent } from '@home/components/vc/entity-version-export.component'; import { EntityVersionExportComponent } from '@home/components/vc/entity-version-export.component';
import { EntityVersionRestoreComponent } from '@home/components/vc/entity-version-restore.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -284,7 +285,8 @@ import { EntityVersionExportComponent } from '@home/components/vc/entity-version
VersionControlSettingsComponent, VersionControlSettingsComponent,
VersionControlComponent, VersionControlComponent,
EntityVersionsTableComponent, EntityVersionsTableComponent,
EntityVersionExportComponent EntityVersionExportComponent,
EntityVersionRestoreComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -406,7 +408,8 @@ import { EntityVersionExportComponent } from '@home/components/vc/entity-version
VersionControlSettingsComponent, VersionControlSettingsComponent,
VersionControlComponent, VersionControlComponent,
EntityVersionsTableComponent, EntityVersionsTableComponent,
EntityVersionExportComponent EntityVersionExportComponent,
EntityVersionRestoreComponent
], ],
providers: [ providers: [
WidgetComponentService, WidgetComponentService,

View File

@ -16,45 +16,58 @@
--> -->
<section style="min-width: 400px;"> <section style="min-width: 400px;">
<mat-toolbar> <section *ngIf="!resultMessage">
<h2>{{ 'version-control.create-entity-version' | translate }}</h2> <mat-toolbar>
<span fxFlex></span> <h2>{{ 'version-control.create-entity-version' | translate }}</h2>
</mat-toolbar> <span fxFlex></span>
<mat-progress-bar color="warn" style="z-index: 10; width: 100%; margin-bottom: -4px;" mode="indeterminate" </mat-toolbar>
*ngIf="isLoading$ | async"> <mat-progress-bar color="warn" style="z-index: 10; width: 100%; margin-bottom: -4px;" mode="indeterminate"
</mat-progress-bar> *ngIf="isLoading$ | async">
<form [formGroup]="exportFormGroup" style="padding-top: 16px;"> </mat-progress-bar>
<fieldset [disabled]="isLoading$ | async"> <form [formGroup]="exportFormGroup" style="padding-top: 16px;">
<div fxFlex fxLayout="column"> <fieldset [disabled]="isLoading$ | async">
<tb-branch-autocomplete <div fxFlex fxLayout="column">
required <tb-branch-autocomplete
formControlName="branch"> required
</tb-branch-autocomplete> formControlName="branch">
<mat-form-field class="mat-block" fxFlex> </tb-branch-autocomplete>
<mat-label translate>version-control.version-name</mat-label> <mat-form-field class="mat-block" fxFlex>
<input required matInput formControlName="versionName"> <mat-label translate>version-control.version-name</mat-label>
<mat-error *ngIf="exportFormGroup.get('versionName').hasError('required')"> <input required matInput formControlName="versionName">
{{ 'version-control.version-name-required' | translate }} <mat-error *ngIf="exportFormGroup.get('versionName').hasError('required')">
</mat-error> {{ 'version-control.version-name-required' | translate }}
</mat-form-field> </mat-error>
<mat-checkbox formControlName="saveRelations" style="margin-bottom: 16px;"> </mat-form-field>
{{ 'version-control.export-entity-relations' | translate }} <mat-checkbox formControlName="saveRelations" style="margin-bottom: 16px;">
</mat-checkbox> {{ 'version-control.export-entity-relations' | translate }}
</div> </mat-checkbox>
</fieldset> </div>
</form> </fieldset>
<div fxLayoutAlign="end center" fxLayoutGap="8px"> </form>
<button mat-button color="primary" <div fxLayoutAlign="end center" fxLayoutGap="8px">
type="button" <button mat-button color="primary"
[disabled]="(isLoading$ | async)" type="button"
(click)="cancel()" cdkFocusInitial> [disabled]="(isLoading$ | async)"
{{ 'action.cancel' | translate }} (click)="cancel()" cdkFocusInitial>
</button> {{ 'action.cancel' | translate }}
<button mat-raised-button color="primary" </button>
type="button" <button mat-raised-button color="primary"
(click)="export()" type="button"
[disabled]="(isLoading$ | async) || exportFormGroup.invalid || !exportFormGroup.dirty"> (click)="export()"
{{ 'action.create' | translate }} [disabled]="(isLoading$ | async) || exportFormGroup.invalid || !exportFormGroup.dirty">
</button> {{ 'action.create' | translate }}
</div> </button>
</div>
</section>
<section *ngIf="resultMessage">
<div class="mat-title export-result-message">{{ resultMessage }}</div>
<div fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.close' | translate }}
</button>
</div>
</section>
</section> </section>

View File

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.export-result-message {
padding: 48px 8px 8px;
text-align: center;
}
}

View File

@ -26,11 +26,12 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { TranslateService } from '@ngx-translate/core';
@Component({ @Component({
selector: 'tb-entity-version-export', selector: 'tb-entity-version-export',
templateUrl: './entity-version-export.component.html', templateUrl: './entity-version-export.component.html',
styleUrls: [] styleUrls: ['./entity-version-export.component.scss']
}) })
export class EntityVersionExportComponent extends PageComponent implements OnInit { export class EntityVersionExportComponent extends PageComponent implements OnInit {
@ -43,10 +44,16 @@ export class EntityVersionExportComponent extends PageComponent implements OnIni
@Input() @Input()
onClose: (result: VersionCreationResult | null, branch: string | null) => void; onClose: (result: VersionCreationResult | null, branch: string | null) => void;
@Input()
onContentUpdated: () => void;
exportFormGroup: FormGroup; exportFormGroup: FormGroup;
resultMessage: string;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService, private entitiesVersionControlService: EntitiesVersionControlService,
private translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
} }
@ -76,7 +83,12 @@ export class EntityVersionExportComponent extends PageComponent implements OnIni
type: VersionCreateRequestType.SINGLE_ENTITY type: VersionCreateRequestType.SINGLE_ENTITY
}; };
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => { this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
if (this.onClose) { if (!result.added && !result.modified) {
this.resultMessage = this.translate.instant('version-control.nothing-to-commit');
if (this.onContentUpdated) {
this.onContentUpdated();
}
} else if (this.onClose) {
this.onClose(result, request.branch); this.onClose(result, request.branch);
} }
}); });

View File

@ -0,0 +1,49 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section style="min-width: 400px;">
<mat-toolbar>
<h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2>
<span fxFlex></span>
</mat-toolbar>
<mat-progress-bar color="warn" style="z-index: 10; width: 100%; margin-bottom: -4px;" mode="indeterminate"
*ngIf="isLoading$ | async">
</mat-progress-bar>
<form [formGroup]="restoreFormGroup" style="padding-top: 16px;">
<fieldset [disabled]="isLoading$ | async">
<div fxFlex fxLayout="column">
<mat-checkbox formControlName="loadRelations" style="margin-bottom: 16px;">
{{ 'version-control.load-entity-relations' | translate }}
</mat-checkbox>
</div>
</fieldset>
</form>
<div fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary"
type="button"
(click)="restore()"
[disabled]="(isLoading$ | async) || restoreFormGroup.invalid">
{{ 'action.restore' | translate }}
</button>
</div>
</section>

View File

@ -0,0 +1,86 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup } from '@angular/forms';
import { SingleEntityVersionLoadRequest, VersionLoadRequestType, VersionLoadResult } from '@shared/models/vc.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { EntityId } from '@shared/models/id/entity-id';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'tb-entity-version-restore',
templateUrl: './entity-version-restore.component.html',
styleUrls: []
})
export class EntityVersionRestoreComponent extends PageComponent implements OnInit {
@Input()
branch: string;
@Input()
versionName: string;
@Input()
versionId: string;
@Input()
externalEntityId: EntityId;
@Input()
onClose: (result: Array<VersionLoadResult> | null) => void;
restoreFormGroup: FormGroup;
constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService,
private translate: TranslateService,
private fb: FormBuilder) {
super(store);
}
ngOnInit(): void {
this.restoreFormGroup = this.fb.group({
loadRelations: [false, []]
});
}
cancel(): void {
if (this.onClose) {
this.onClose(null);
}
}
restore(): void {
const request: SingleEntityVersionLoadRequest = {
branch: this.branch,
versionId: this.versionId,
externalEntityId: this.externalEntityId,
config: {
loadRelations: this.restoreFormGroup.get('loadRelations').value
},
type: VersionLoadRequestType.SINGLE_ENTITY
};
this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => {
if (this.onClose) {
this.onClose(result);
}
});
}
}

View File

@ -99,6 +99,21 @@
{{ entityVersion.name }} {{ entityVersion.name }}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef style="min-width: 40px; max-width: 40px; width: 40px">
</mat-header-cell>
<mat-cell *matCellDef="let entityVersion">
<div fxFlex fxLayout="row" fxLayoutAlign="end">
<button *ngIf="singleEntityMode" mat-icon-button [disabled]="isLoading$ | async"
matTooltip="{{ 'version-control.restore-version' | translate }}"
matTooltipPosition="above"
#restoreVersionButton
(click)="toggleRestoreEntityVersion($event, restoreVersionButton, entityVersion)">
<mat-icon>restore</mat-icon>
</button>
</div>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [fxShow]="!dataSource.dataLoading" *matRowDef="let entityVersion; columns: displayedColumns;"></mat-row> <mat-row [fxShow]="!dataSource.dataLoading" *matRowDef="let entityVersion; columns: displayedColumns;"></mat-row>
</table> </table>

View File

@ -18,10 +18,10 @@ import {
AfterViewInit, AfterViewInit,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef, ElementRef, EventEmitter,
Input, Input,
OnDestroy, OnDestroy,
OnInit, Renderer2, OnInit, Output, Renderer2,
ViewChild, ViewContainerRef ViewChild, ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -33,7 +33,7 @@ import { BehaviorSubject, fromEvent, merge, Observable, of, ReplaySubject } from
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators';
import { EntityVersion, VersionCreationResult } from '@shared/models/vc.models'; import { EntityVersion, VersionCreationResult, VersionLoadResult } from '@shared/models/vc.models';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
@ -46,6 +46,7 @@ import { TbPopoverService } from '@shared/components/popover.service';
import { EntityVersionExportComponent } from '@home/components/vc/entity-version-export.component'; import { EntityVersionExportComponent } from '@home/components/vc/entity-version-export.component';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { EntityVersionRestoreComponent } from '@home/components/vc/entity-version-restore.component';
@Component({ @Component({
selector: 'tb-entity-versions-table', selector: 'tb-entity-versions-table',
@ -59,7 +60,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
@Input() @Input()
singleEntityMode = false; singleEntityMode = false;
displayedColumns = ['timestamp', 'id', 'name']; displayedColumns = ['timestamp', 'id', 'name', 'actions'];
pageLink: PageLink; pageLink: PageLink;
textSearchMode = false; textSearchMode = false;
dataSource: EntityVersionsDatasource; dataSource: EntityVersionsDatasource;
@ -73,8 +74,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
viewsInited = false; viewsInited = false;
vcExportPopover: TbPopoverComponent;
private componentResize$: ResizeObserver; private componentResize$: ResizeObserver;
@Input() @Input()
@ -104,6 +103,9 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
@Input() @Input()
entityId: EntityId; entityId: EntityId;
@Output()
versionRestored = new EventEmitter<void>();
@ViewChild('searchInput') searchInputField: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef;
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ -182,13 +184,13 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
if (this.popoverService.hasPopover(trigger)) { if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger); this.popoverService.hidePopover(trigger);
} else { } else {
this.vcExportPopover = this.popoverService.displayPopover(trigger, this.renderer, const vcExportPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, EntityVersionExportComponent, 'bottom', true, null, this.viewContainerRef, EntityVersionExportComponent, 'left', true, null,
{ {
branch: this.branch, branch: this.branch,
entityId: this.entityId, entityId: this.entityId,
onClose: (result: VersionCreationResult | null, branch: string | null) => { onClose: (result: VersionCreationResult | null, branch: string | null) => {
this.vcExportPopover.hide(); vcExportPopover.hide();
if (result) { if (result) {
if (this.branch !== branch) { if (this.branch !== branch) {
this.branchChanged(branch); this.branchChanged(branch);
@ -196,13 +198,39 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
this.updateData(); this.updateData();
} }
} }
},
onContentUpdated: () => {
vcExportPopover.updatePosition();
setTimeout(() => {
vcExportPopover.updatePosition();
});
}
}, {}, {}, {}, false);
}
}
toggleRestoreEntityVersion($event: Event, restoreVersionButton: MatButton, entityVersion: EntityVersion) {
if ($event) {
$event.stopPropagation();
}
const trigger = restoreVersionButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, EntityVersionRestoreComponent, 'left', true, null,
{
branch: this.branch,
versionName: entityVersion.name,
versionId: entityVersion.id,
externalEntityId: this.externalEntityIdValue,
onClose: (result: Array<VersionLoadResult> | null) => {
restoreVersionPopover.hide();
if (result && result.length) {
this.versionRestored.emit();
}
} }
}, {}, {}, {}, false); }, {}, {}, {}, false);
this.vcExportPopover.tbVisibleChange.subscribe((visible: boolean) => {
if (!visible) {
this.vcExportPopover = null;
}
});
} }
} }

View File

@ -22,5 +22,6 @@
<tb-entity-versions-table [singleEntityMode]="singleEntityMode" <tb-entity-versions-table [singleEntityMode]="singleEntityMode"
[active]="active" [active]="active"
[entityId]="entityId" [entityId]="entityId"
[externalEntityId]="externalEntityId"></tb-entity-versions-table> [externalEntityId]="externalEntityId"
(versionRestored)="versionRestored.emit()"></tb-entity-versions-table>
</ng-template> </ng-template>

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
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 { selectHasVersionControl } from '@core/auth/auth.selectors'; import { selectHasVersionControl } from '@core/auth/auth.selectors';
@ -47,6 +47,9 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
@Input() @Input()
entityId: EntityId; entityId: EntityId;
@Output()
versionRestored = new EventEmitter<void>();
hasVersionControl$ = this.store.pipe(select(selectHasVersionControl)); hasVersionControl$ = this.store.pipe(select(selectHasVersionControl));
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {

View File

@ -52,5 +52,6 @@
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -52,5 +52,6 @@
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -22,5 +22,6 @@
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -75,8 +75,9 @@
label="{{ 'audit-log.audit-logs' | translate }}"> label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table>
</mat-tab> </mat-tab>
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN && !isEdit"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -52,5 +52,6 @@
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -55,5 +55,6 @@
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab"> label="{{ 'version-control.version-control' | translate }}" #versionControlTab="matTab">
<tb-version-control detailsMode="true" singleEntityMode="true" <tb-version-control detailsMode="true" singleEntityMode="true"
(versionRestored)="entitiesTableConfig.updateData()"
[active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control> [active]="versionControlTab.isActive" [entityId]="entity.id" [externalEntityId]="entity.externalId || entity.id"></tb-version-control>
</mat-tab> </mat-tab>

View File

@ -15,16 +15,26 @@
/// ///
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EntityType } from '@shared/models/entity-type.models';
export interface VersionCreateConfig { export interface VersionCreateConfig {
saveRelations: boolean; saveRelations: boolean;
} }
export interface VersionLoadConfig {
loadRelations: boolean;
}
export enum VersionCreateRequestType { export enum VersionCreateRequestType {
SINGLE_ENTITY = 'SINGLE_ENTITY', SINGLE_ENTITY = 'SINGLE_ENTITY',
COMPLEX = 'COMPLEX' COMPLEX = 'COMPLEX'
} }
export enum VersionLoadRequestType {
SINGLE_ENTITY = 'SINGLE_ENTITY',
ENTITY_TYPE = 'ENTITY_TYPE'
}
export interface VersionCreateRequest { export interface VersionCreateRequest {
versionName: string; versionName: string;
branch: string; branch: string;
@ -37,6 +47,18 @@ export interface SingleEntityVersionCreateRequest extends VersionCreateRequest {
type: VersionCreateRequestType.SINGLE_ENTITY; type: VersionCreateRequestType.SINGLE_ENTITY;
} }
export interface VersionLoadRequest {
branch: string;
versionId: string;
type: VersionLoadRequestType;
}
export interface SingleEntityVersionLoadRequest extends VersionLoadRequest {
externalEntityId: EntityId;
config: VersionLoadConfig;
type: VersionLoadRequestType.SINGLE_ENTITY;
}
export interface BranchInfo { export interface BranchInfo {
name: string; name: string;
default: boolean; default: boolean;
@ -54,3 +76,10 @@ export interface VersionCreationResult {
modified: number; modified: number;
removed: number; removed: number;
} }
export interface VersionLoadResult {
entityType: EntityType;
created: number;
updated: number;
deleted: number;
}

View File

@ -57,7 +57,8 @@
"download": "Download", "download": "Download",
"next-with-label": "Next: {{label}}", "next-with-label": "Next: {{label}}",
"read-more": "Read more", "read-more": "Read more",
"hide": "Hide" "hide": "Hide",
"restore": "Restore"
}, },
"aggregation": { "aggregation": {
"aggregation": "Aggregation", "aggregation": "Aggregation",
@ -3126,7 +3127,11 @@
"no-entity-versions-text": "No entity versions found", "no-entity-versions-text": "No entity versions found",
"no-versions-text": "No versions found", "no-versions-text": "No versions found",
"copy-full-version-id": "Copy full version id", "copy-full-version-id": "Copy full version id",
"create-version": "Create version" "create-version": "Create version",
"nothing-to-commit": "Nothing to commit",
"restore-version": "Restore version",
"restore-entity-from-version": "Restore entity from version '{{versionName}}'",
"load-entity-relations": "Load entity relations"
}, },
"widget": { "widget": {
"widget-library": "Widgets Library", "widget-library": "Widgets Library",