UI: Implement rule chain page version control.

This commit is contained in:
Igor Kulikov 2022-06-01 18:02:52 +03:00
parent 8382850d1a
commit 14023e4cc9
9 changed files with 148 additions and 27 deletions

View File

@ -27,6 +27,7 @@ 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';
import { Observable, of } from 'rxjs';
@Component({
selector: 'tb-entity-version-create',
@ -50,6 +51,9 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
@Input()
onContentUpdated: () => void;
@Input()
onBeforeCreateVersion: () => Observable<any>;
createVersionFormGroup: FormGroup;
resultMessage: string;
@ -78,25 +82,29 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
}
export(): void {
const request: SingleEntityVersionCreateRequest = {
entityId: this.entityId,
branch: this.createVersionFormGroup.get('branch').value,
versionName: this.createVersionFormGroup.get('versionName').value,
config: {
saveRelations: this.createVersionFormGroup.get('saveRelations').value,
saveAttributes: this.createVersionFormGroup.get('saveAttributes').value,
},
type: VersionCreateRequestType.SINGLE_ENTITY
};
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
if (!result.added && !result.modified) {
this.resultMessage = this.translate.instant('version-control.nothing-to-commit');
if (this.onContentUpdated) {
this.onContentUpdated();
const before = this.onBeforeCreateVersion ? this.onBeforeCreateVersion() : of(null);
before.subscribe(() => {
const request: SingleEntityVersionCreateRequest = {
entityId: this.entityId,
branch: this.createVersionFormGroup.get('branch').value,
versionName: this.createVersionFormGroup.get('versionName').value,
config: {
saveRelations: this.createVersionFormGroup.get('saveRelations').value,
saveAttributes: this.createVersionFormGroup.get('saveAttributes').value,
},
type: VersionCreateRequestType.SINGLE_ENTITY
};
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
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);
}
} else if (this.onClose) {
this.onClose(result, request.branch);
}
});
});
}
}

View File

@ -16,7 +16,7 @@
-->
<div class="mat-padding tb-entity-table tb-absolute-fill">
<div fxFlex fxLayout="column" class="mat-elevation-z1 tb-entity-table-content">
<div fxFlex fxLayout="column" class="tb-entity-table-content" [ngClass]="{'mat-elevation-z1': !popoverMode}">
<mat-toolbar class="mat-table-toolbar" [fxShow]="!textSearchMode">
<div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
@ -45,6 +45,11 @@
<mat-icon>update</mat-icon>
{{'version-control.create-entities-version' | translate }}
</button>
<button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()"
matTooltip="{{ 'action.refresh' | translate }}"
matTooltipPosition="above">
<mat-icon>refresh</mat-icon>
</button>
<button mat-icon-button
[disabled]="isLoading$ | async"
(click)="enterFilterMode()"

View File

@ -62,6 +62,12 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
@Input()
singleEntityMode = false;
@Input()
popoverMode = false;
@Input()
onBeforeCreateVersion: () => Observable<any>;
displayedColumns = ['timestamp', 'id', 'name', 'author', 'actions'];
pageLink: PageLink;
textSearchMode = false;
@ -195,6 +201,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
branch: this.branch,
entityId: this.entityId,
entityName: this.entityName,
onBeforeCreateVersion: this.onBeforeCreateVersion,
onClose: (result: VersionCreationResult | null, branch: string | null) => {
createVersionPopover.hide();
if (result) {
@ -353,7 +360,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
}
}
private updateData() {
updateData() {
this.pageLink.page = this.paginator.pageIndex;
this.pageLink.pageSize = this.paginator.pageSize;
this.pageLink.sortOrder.property = this.sort.active;

View File

@ -20,6 +20,8 @@
</tb-repository-settings>
<ng-template #versionsTable>
<tb-entity-versions-table [singleEntityMode]="singleEntityMode"
[popoverMode]="popoverMode"
[onBeforeCreateVersion]="onBeforeCreateVersion"
[active]="active"
[entityId]="entityId"
[entityName]="entityName"

View File

@ -22,6 +22,7 @@ import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
import { RepositorySettingsComponent } from '@home/components/vc/repository-settings.component';
import { FormGroup } from '@angular/forms';
import { EntityId } from '@shared/models/id/entity-id';
import { Observable } from 'rxjs';
@Component({
selector: 'tb-version-control',
@ -35,6 +36,9 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
@Input()
detailsMode = false;
@Input()
popoverMode = false;
@Input()
active = true;
@ -50,6 +54,9 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
@Input()
entityName: string;
@Input()
onBeforeCreateVersion: () => Observable<any>;
@Output()
versionRestored = new EventEmitter<void>();

View File

@ -164,6 +164,17 @@
</tb-details-panel>
</mat-drawer>
<mat-drawer-content class="tb-rulechain-graph-content">
<button #versionControlButton
*ngIf="!isImport"
mat-button
color="primary"
type="button"
mat-icon-button class="mat-fab version-control-button"
(click)="toggleVersionControl($event, versionControlButton)"
matTooltip="{{'version-control.version-control' | translate}}"
matTooltipPosition="above">
<mat-icon>history</mat-icon>
</button>
<button mat-button
type="button"
mat-icon-button class="tb-fullscreen-button"

View File

@ -44,6 +44,21 @@
margin: 0 6px;
z-index: 2;
}
button.mat-button.mat-icon-button.version-control-button {
position: absolute;
top: 10px;
right: 60px;
opacity: .85;
margin: 0 6px;
z-index: 2;
&.mat-fab {
.mat-button-wrapper {
padding: 0;
}
}
}
section.tb-header-buttons.tb-library-open {
position: absolute;
top: 0;

View File

@ -17,15 +17,15 @@
import {
AfterViewInit,
Component,
ElementRef,
ElementRef, EventEmitter,
HostBinding,
Inject,
OnDestroy,
OnInit,
QueryList,
QueryList, Renderer2,
SkipSelf,
ViewChild,
ViewChildren,
ViewChildren, ViewContainerRef,
ViewEncapsulation
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
@ -63,7 +63,7 @@ import {
} from '@shared/models/rule-node.models';
import { FcRuleNodeModel, FcRuleNodeTypeModel, RuleChainMenuContextInfo } from './rulechain-page.models';
import { RuleChainService } from '@core/http/rule-chain.service';
import { fromEvent, NEVER, Observable, of, Subscription } from 'rxjs';
import { fromEvent, NEVER, Observable, of, ReplaySubject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, mergeMap, tap } from 'rxjs/operators';
import { ISearchableComponent } from '../../models/searchable-component.models';
import { deepClone } from '@core/utils';
@ -75,6 +75,11 @@ import { ItemBufferService, RuleNodeConnection } from '@core/services/item-buffe
import { Hotkey } from 'angular2-hotkeys';
import { DebugEventType, EventType } from '@shared/models/event.models';
import Timeout = NodeJS.Timeout;
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { EntityVersionCreateComponent } from '@home/components/vc/entity-version-create.component';
import { VersionCreationResult } from '@shared/models/vc.models';
import { VersionControlComponent } from '@home/components/vc/version-control.component';
@Component({
selector: 'tb-rulechain-page',
@ -231,6 +236,8 @@ export class RuleChainPageComponent extends PageComponent
flowchartConstants = FlowchartConstants;
updateBreadcrumbs = new EventEmitter();
private rxSubscription: Subscription;
private tooltipTimeout: Timeout;
@ -242,11 +249,13 @@ export class RuleChainPageComponent extends PageComponent
private authService: AuthService,
private translate: TranslateService,
private itembuffer: ItemBufferService,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
public dialog: MatDialog,
public dialogService: DialogService,
public fb: FormBuilder) {
super(store);
this.rxSubscription = this.route.data.subscribe(
() => {
this.reset();
@ -1376,7 +1385,8 @@ export class RuleChainPageComponent extends PageComponent
}, 0);
}
saveRuleChain() {
saveRuleChain(): Observable<any> {
const saveResult = new ReplaySubject();
let saveRuleChainObservable: Observable<RuleChain>;
if (this.isImport) {
saveRuleChainObservable = this.ruleChainService.saveRuleChain(this.ruleChain);
@ -1442,6 +1452,20 @@ export class RuleChainPageComponent extends PageComponent
} else {
this.createRuleChainModel();
}
saveResult.next();
});
});
return saveResult;
}
reloadRuleChain() {
this.ruleChainService.getRuleChain(this.ruleChain.id.id).subscribe((ruleChain) => {
this.ruleChain = ruleChain;
this.updateBreadcrumbs.emit();
this.ruleChainService.getRuleChainMetadata(this.ruleChain.id.id).subscribe((ruleChainMetaData) => {
this.ruleChainMetaData = ruleChainMetaData;
this.isDirtyValue = false;
this.createRuleChainModel();
});
});
}
@ -1509,6 +1533,38 @@ export class RuleChainPageComponent extends PageComponent
}).afterClosed();
}
toggleVersionControl($event: Event, versionControlButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = versionControlButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const versionControlPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, VersionControlComponent, 'leftTop', true, null,
{
detailsMode: true,
popoverMode: true,
active: true,
singleEntityMode: true,
externalEntityId: this.ruleChain.externalId || this.ruleChain.id,
entityId: this.ruleChain.id,
entityName: this.ruleChain.name,
onBeforeCreateVersion: () => {
if (this.isDirty) {
return this.saveRuleChain();
} else {
return of(null);
}
}
}, {width: '800px', height: '600px'}, {}, {}, true);
versionControlPopover.tbComponentRef.instance.versionRestored.subscribe(() => {
this.reloadRuleChain();
});
}
}
private updateNodeErrorTooltip(node: FcRuleNode) {
if (node.error) {
const element = $('#' + node.id);

View File

@ -15,7 +15,7 @@
///
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, Subject, Subscription } from 'rxjs';
import { BreadCrumb, BreadCrumbConfig } from './breadcrumb';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
@ -32,10 +32,20 @@ import { BroadcastService } from '@core/services/broadcast.service';
export class BreadcrumbComponent implements OnInit, OnDestroy {
activeComponentValue: any;
updateBreadcrumbsSubscription: Subscription = null;
@Input()
set activeComponent(activeComponent: any) {
if (this.updateBreadcrumbsSubscription) {
this.updateBreadcrumbsSubscription.unsubscribe();
this.updateBreadcrumbsSubscription = null;
}
this.activeComponentValue = activeComponent;
if (this.activeComponentValue && this.activeComponentValue.updateBreadcrumbs) {
this.updateBreadcrumbsSubscription = this.activeComponentValue.updateBreadcrumbs.subscribe(() => {
this.breadcrumbs$.next(this.buildBreadCrumbs(this.activatedRoute.snapshot));
});
}
}
breadcrumbs$: Subject<Array<BreadCrumb>> = new BehaviorSubject<Array<BreadCrumb>>(this.buildBreadCrumbs(this.activatedRoute.snapshot));