Merge pull request #11426 from maxunbearable/fix/version-conflict
Version Conflict dialog fixes
This commit is contained in:
commit
9627a59550
@ -185,8 +185,9 @@ export class WidgetService {
|
|||||||
public saveWidgetTypeDetails(widgetInfo: WidgetInfo,
|
public saveWidgetTypeDetails(widgetInfo: WidgetInfo,
|
||||||
id: WidgetTypeId,
|
id: WidgetTypeId,
|
||||||
createdTime: number,
|
createdTime: number,
|
||||||
|
version: number,
|
||||||
config?: RequestConfig): Observable<WidgetTypeDetails> {
|
config?: RequestConfig): Observable<WidgetTypeDetails> {
|
||||||
const widgetTypeDetails = toWidgetTypeDetails(widgetInfo, id, undefined, createdTime);
|
const widgetTypeDetails = toWidgetTypeDetails(widgetInfo, id, undefined, createdTime, version);
|
||||||
return this.http.post<WidgetTypeDetails>('/api/widgetType', widgetTypeDetails,
|
return this.http.post<WidgetTypeDetails>('/api/widgetType', widgetTypeDetails,
|
||||||
defaultHttpOptionsFromConfig(config)).pipe(
|
defaultHttpOptionsFromConfig(config)).pipe(
|
||||||
tap((savedWidgetType) => {
|
tap((savedWidgetType) => {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
HttpEvent,
|
HttpEvent,
|
||||||
HttpHandler,
|
HttpHandler,
|
||||||
HttpInterceptor,
|
HttpInterceptor,
|
||||||
|
HttpParams,
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
HttpStatusCode
|
HttpStatusCode
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
@ -32,6 +33,8 @@ import {
|
|||||||
import { HasId } from '@shared/models/base-data';
|
import { HasId } from '@shared/models/base-data';
|
||||||
import { HasVersion } from '@shared/models/entity.models';
|
import { HasVersion } from '@shared/models/entity.models';
|
||||||
import { getInterceptorConfig } from './interceptor.util';
|
import { getInterceptorConfig } from './interceptor.util';
|
||||||
|
import { isDefined } from '@core/utils';
|
||||||
|
import { InterceptorConfig } from '@core/interceptors/interceptor-config';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EntityConflictInterceptor implements HttpInterceptor {
|
export class EntityConflictInterceptor implements HttpInterceptor {
|
||||||
@ -67,9 +70,13 @@ export class EntityConflictInterceptor implements HttpInterceptor {
|
|||||||
|
|
||||||
return this.openConflictDialog(request.body, error.error.message).pipe(
|
return this.openConflictDialog(request.body, error.error.message).pipe(
|
||||||
switchMap(result => {
|
switchMap(result => {
|
||||||
|
if (isDefined(result)) {
|
||||||
if (result) {
|
if (result) {
|
||||||
return next.handle(this.updateRequestVersion(request));
|
return next.handle(this.updateRequestVersion(request));
|
||||||
}
|
}
|
||||||
|
(request.params as HttpParams & { interceptorConfig: InterceptorConfig }).interceptorConfig.ignoreErrors = true;
|
||||||
|
return throwError(() => error);
|
||||||
|
}
|
||||||
return of(null);
|
return of(null);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -82,7 +89,9 @@ export class EntityConflictInterceptor implements HttpInterceptor {
|
|||||||
|
|
||||||
private openConflictDialog(entity: unknown & HasId & HasVersion, message: string): Observable<boolean> {
|
private openConflictDialog(entity: unknown & HasId & HasVersion, message: string): Observable<boolean> {
|
||||||
const dialogRef = this.dialog.open(EntityConflictDialogComponent, {
|
const dialogRef = this.dialog.open(EntityConflictDialogComponent, {
|
||||||
data: { message, entity }
|
disableClose: true,
|
||||||
|
data: { message, entity },
|
||||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||||
});
|
});
|
||||||
|
|
||||||
return dialogRef.afterClosed();
|
return dialogRef.afterClosed();
|
||||||
|
|||||||
@ -86,7 +86,7 @@ import { Authority } from '@shared/models/authority.enum';
|
|||||||
import { DialogService } from '@core/services/dialog.service';
|
import { DialogService } from '@core/services/dialog.service';
|
||||||
import { EntityService } from '@core/http/entity.service';
|
import { EntityService } from '@core/http/entity.service';
|
||||||
import { AliasController } from '@core/api/alias-controller';
|
import { AliasController } from '@core/api/alias-controller';
|
||||||
import { BehaviorSubject, Observable, of, Subject, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, of, Subject, Subscription, throwError } from 'rxjs';
|
||||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
||||||
import { DashboardService } from '@core/http/dashboard.service';
|
import { DashboardService } from '@core/http/dashboard.service';
|
||||||
import {
|
import {
|
||||||
@ -147,7 +147,7 @@ import { IAliasController } from '@core/api/widget-api.models';
|
|||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { VersionControlComponent } from '@home/components/vc/version-control.component';
|
import { VersionControlComponent } from '@home/components/vc/version-control.component';
|
||||||
import { TbPopoverService } from '@shared/components/popover.service';
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { distinctUntilChanged, map, skip, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, map, skip, tap } from 'rxjs/operators';
|
||||||
import { LayoutFixedSize, LayoutWidthType } from '@home/components/dashboard-page/layout/layout.models';
|
import { LayoutFixedSize, LayoutWidthType } from '@home/components/dashboard-page/layout/layout.models';
|
||||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
import { ResizeObserver } from '@juggle/resize-observer';
|
import { ResizeObserver } from '@juggle/resize-observer';
|
||||||
@ -156,6 +156,7 @@ import {
|
|||||||
MoveWidgetsDialogComponent,
|
MoveWidgetsDialogComponent,
|
||||||
MoveWidgetsDialogResult
|
MoveWidgetsDialogResult
|
||||||
} from '@home/components/dashboard-page/layout/move-widgets-dialog.component';
|
} from '@home/components/dashboard-page/layout/move-widgets-dialog.component';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
|
||||||
// @dynamic
|
// @dynamic
|
||||||
@Component({
|
@Component({
|
||||||
@ -1092,7 +1093,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
|
|
||||||
public saveDashboard() {
|
public saveDashboard() {
|
||||||
this.translatedDashboardTitle = this.getTranslatedDashboardTitle();
|
this.translatedDashboardTitle = this.getTranslatedDashboardTitle();
|
||||||
this.setEditMode(false, false);
|
|
||||||
this.notifyDashboardUpdated();
|
this.notifyDashboardUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1204,8 +1204,33 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
data: widget
|
data: widget
|
||||||
};
|
};
|
||||||
this.window.parent.postMessage(JSON.stringify(message), '*');
|
this.window.parent.postMessage(JSON.stringify(message), '*');
|
||||||
|
this.setEditMode(false, false);
|
||||||
} else {
|
} else {
|
||||||
this.dashboardService.saveDashboard(this.dashboard).subscribe();
|
let reInitDashboard = false;
|
||||||
|
this.dashboardService.saveDashboard(this.dashboard).pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
if (err.status === HttpStatusCode.Conflict) {
|
||||||
|
reInitDashboard = true;
|
||||||
|
return this.dashboardService.getDashboard(this.dashboard.id.id).pipe(
|
||||||
|
map(dashboard => this.dashboardUtils.validateAndUpdateDashboard(dashboard))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return throwError(() => err);
|
||||||
|
})
|
||||||
|
).subscribe((dashboard) => {
|
||||||
|
if (reInitDashboard) {
|
||||||
|
const dashboardPageInitData: DashboardPageInitData = {
|
||||||
|
dashboard,
|
||||||
|
currentDashboardId: dashboard.id ? dashboard.id.id : null,
|
||||||
|
widgetEditMode: this.widgetEditMode,
|
||||||
|
singlePageMode: this.singlePageMode
|
||||||
|
};
|
||||||
|
this.init(dashboardPageInitData);
|
||||||
|
} else {
|
||||||
|
this.dashboard = dashboard;
|
||||||
|
this.setEditMode(false, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -402,7 +402,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
|
|||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData(closeDetails: boolean = true) {
|
updateData(closeDetails: boolean = true, reloadEntity: boolean = true) {
|
||||||
if (closeDetails) {
|
if (closeDetails) {
|
||||||
this.isDetailsOpen = false;
|
this.isDetailsOpen = false;
|
||||||
}
|
}
|
||||||
@ -427,7 +427,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
|
|||||||
timePageLink.endTime = interval.endTime;
|
timePageLink.endTime = interval.endTime;
|
||||||
}
|
}
|
||||||
this.dataSource.loadEntities(this.pageLink);
|
this.dataSource.loadEntities(this.pageLink);
|
||||||
if (this.isDetailsOpen && this.entityDetailsPanel) {
|
if (reloadEntity && this.isDetailsOpen && this.entityDetailsPanel) {
|
||||||
this.entityDetailsPanel.reloadEntity();
|
this.entityDetailsPanel.reloadEntity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEntityUpdated(entity: BaseData<HasId>) {
|
onEntityUpdated(entity: BaseData<HasId>) {
|
||||||
this.updateData(false);
|
this.updateData(false, false);
|
||||||
this.entitiesTableConfig.entityUpdated(entity);
|
this.entitiesTableConfig.entityUpdated(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,11 +40,12 @@ import { UntypedFormGroup } from '@angular/forms';
|
|||||||
import { EntityComponent } from './entity.component';
|
import { EntityComponent } from './entity.component';
|
||||||
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
|
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
|
||||||
import { EntityAction } from '@home/models/entity/entity-component.models';
|
import { EntityAction } from '@home/models/entity/entity-component.models';
|
||||||
import { Observable, ReplaySubject, Subscription } from 'rxjs';
|
import { Observable, ReplaySubject, Subscription, throwError } from 'rxjs';
|
||||||
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
||||||
import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component';
|
import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component';
|
||||||
import { deepClone, mergeDeep } from '@core/utils';
|
import { deepClone, mergeDeep } from '@core/utils';
|
||||||
import { entityIdEquals } from '@shared/models/id/entity-id';
|
import { catchError } from 'rxjs/operators';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-entity-details-panel',
|
selector: 'tb-entity-details-panel',
|
||||||
@ -288,7 +289,16 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
|
|||||||
editingEntity.additionalInfo =
|
editingEntity.additionalInfo =
|
||||||
mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo);
|
mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo);
|
||||||
}
|
}
|
||||||
this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe(
|
this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity)
|
||||||
|
.pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
if (err.status === HttpStatusCode.Conflict) {
|
||||||
|
return this.entitiesTableConfig.loadEntity(this.currentEntityId);
|
||||||
|
}
|
||||||
|
return throwError(() => err);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
(entity) => {
|
(entity) => {
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.entityComponent.entity = entity;
|
this.entityComponent.entity = entity;
|
||||||
|
|||||||
@ -113,7 +113,7 @@ export class WidgetComponentService {
|
|||||||
hasBasicMode: this.utils.editWidgetInfo.hasBasicMode,
|
hasBasicMode: this.utils.editWidgetInfo.hasBasicMode,
|
||||||
basicModeDirective: this.utils.editWidgetInfo.basicModeDirective,
|
basicModeDirective: this.utils.editWidgetInfo.basicModeDirective,
|
||||||
defaultConfig: this.utils.editWidgetInfo.defaultConfig
|
defaultConfig: this.utils.editWidgetInfo.defaultConfig
|
||||||
}, new WidgetTypeId('1'), new TenantId( NULL_UUID ), undefined
|
}, new WidgetTypeId('1'), new TenantId( NULL_UUID ), undefined, undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const initSubject = new ReplaySubject<void>();
|
const initSubject = new ReplaySubject<void>();
|
||||||
|
|||||||
@ -669,7 +669,7 @@ export const detailsToWidgetInfo = (widgetTypeDetailsEntity: WidgetTypeDetails):
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId,
|
export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId,
|
||||||
createdTime: number): WidgetType => {
|
createdTime: number, version: number): WidgetType => {
|
||||||
const descriptor: WidgetTypeDescriptor = {
|
const descriptor: WidgetTypeDescriptor = {
|
||||||
type: widgetInfo.type,
|
type: widgetInfo.type,
|
||||||
sizeX: widgetInfo.sizeX,
|
sizeX: widgetInfo.sizeX,
|
||||||
@ -692,6 +692,7 @@ export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId:
|
|||||||
id,
|
id,
|
||||||
tenantId,
|
tenantId,
|
||||||
createdTime,
|
createdTime,
|
||||||
|
version,
|
||||||
fqn: widgetTypeFqn(widgetInfo.fullFqn),
|
fqn: widgetTypeFqn(widgetInfo.fullFqn),
|
||||||
name: widgetInfo.widgetName,
|
name: widgetInfo.widgetName,
|
||||||
deprecated: widgetInfo.deprecated,
|
deprecated: widgetInfo.deprecated,
|
||||||
@ -701,8 +702,8 @@ export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId:
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const toWidgetTypeDetails = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId,
|
export const toWidgetTypeDetails = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId,
|
||||||
createdTime: number): WidgetTypeDetails => {
|
createdTime: number, version: number): WidgetTypeDetails => {
|
||||||
const widgetTypeEntity = toWidgetType(widgetInfo, id, tenantId, createdTime);
|
const widgetTypeEntity = toWidgetType(widgetInfo, id, tenantId, createdTime, version);
|
||||||
return {
|
return {
|
||||||
...widgetTypeEntity,
|
...widgetTypeEntity,
|
||||||
description: widgetInfo.description,
|
description: widgetInfo.description,
|
||||||
|
|||||||
@ -59,12 +59,13 @@ import {
|
|||||||
SaveWidgetTypeAsDialogComponent,
|
SaveWidgetTypeAsDialogComponent,
|
||||||
SaveWidgetTypeAsDialogResult
|
SaveWidgetTypeAsDialogResult
|
||||||
} from '@home/pages/widget/save-widget-type-as-dialog.component';
|
} from '@home/pages/widget/save-widget-type-as-dialog.component';
|
||||||
import { forkJoin, mergeMap, of, Subscription } from 'rxjs';
|
import { forkJoin, mergeMap, of, Subscription, throwError } from 'rxjs';
|
||||||
import { ResizeObserver } from '@juggle/resize-observer';
|
import { ResizeObserver } from '@juggle/resize-observer';
|
||||||
import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models';
|
import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { map, tap } from 'rxjs/operators';
|
import { catchError, map, tap } from 'rxjs/operators';
|
||||||
import { beautifyCss, beautifyHtml, beautifyJs } from '@shared/models/beautify.models';
|
import { beautifyCss, beautifyHtml, beautifyJs } from '@shared/models/beautify.models';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
|
||||||
// @dynamic
|
// @dynamic
|
||||||
@ -569,9 +570,12 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe
|
|||||||
|
|
||||||
private commitSaveWidget() {
|
private commitSaveWidget() {
|
||||||
const id = (this.widgetTypeDetails && this.widgetTypeDetails.id) ? this.widgetTypeDetails.id : undefined;
|
const id = (this.widgetTypeDetails && this.widgetTypeDetails.id) ? this.widgetTypeDetails.id : undefined;
|
||||||
|
const version = this.widgetTypeDetails?.version ?? null;
|
||||||
const createdTime = (this.widgetTypeDetails && this.widgetTypeDetails.createdTime) ? this.widgetTypeDetails.createdTime : undefined;
|
const createdTime = (this.widgetTypeDetails && this.widgetTypeDetails.createdTime) ? this.widgetTypeDetails.createdTime : undefined;
|
||||||
this.widgetService.saveWidgetTypeDetails(this.widget, id, createdTime).pipe(
|
this.saveWidgetPending = false;
|
||||||
|
this.widgetService.saveWidgetTypeDetails(this.widget, id, createdTime, version).pipe(
|
||||||
mergeMap((widgetTypeDetails) => {
|
mergeMap((widgetTypeDetails) => {
|
||||||
|
this.saveWidgetPending = true;
|
||||||
const widgetsBundleId = this.route.snapshot.params.widgetsBundleId as string;
|
const widgetsBundleId = this.route.snapshot.params.widgetsBundleId as string;
|
||||||
if (widgetsBundleId && !id) {
|
if (widgetsBundleId && !id) {
|
||||||
return this.widgetService.addWidgetFqnToWidgetBundle(widgetsBundleId, widgetTypeDetails.fqn).pipe(
|
return this.widgetService.addWidgetFqnToWidgetBundle(widgetsBundleId, widgetTypeDetails.fqn).pipe(
|
||||||
@ -579,7 +583,13 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return of(widgetTypeDetails);
|
return of(widgetTypeDetails);
|
||||||
})
|
}),
|
||||||
|
catchError((err) => {
|
||||||
|
if (id && err.status === HttpStatusCode.Conflict) {
|
||||||
|
return this.widgetService.getWidgetTypeById(id.id);
|
||||||
|
}
|
||||||
|
return throwError(() => err);
|
||||||
|
}),
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (widgetTypeDetails) => {
|
next: (widgetTypeDetails) => {
|
||||||
this.saveWidgetPending = false;
|
this.saveWidgetPending = false;
|
||||||
@ -612,7 +622,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe
|
|||||||
config.title = this.widget.widgetName;
|
config.title = this.widget.widgetName;
|
||||||
this.widget.defaultConfig = JSON.stringify(config);
|
this.widget.defaultConfig = JSON.stringify(config);
|
||||||
this.isDirty = false;
|
this.isDirty = false;
|
||||||
this.widgetService.saveWidgetTypeDetails(this.widget, undefined, undefined).pipe(
|
this.widgetService.saveWidgetTypeDetails(this.widget, undefined, undefined, undefined).pipe(
|
||||||
mergeMap((widget) => {
|
mergeMap((widget) => {
|
||||||
if (saveWidgetAsData.widgetBundleId) {
|
if (saveWidgetAsData.widgetBundleId) {
|
||||||
return this.widgetService.addWidgetFqnToWidgetBundle(saveWidgetAsData.widgetBundleId, widget.fqn).pipe(
|
return this.widgetService.addWidgetFqnToWidgetBundle(saveWidgetAsData.widgetBundleId, widget.fqn).pipe(
|
||||||
|
|||||||
@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<mat-toolbar color="primary">
|
<mat-toolbar color="primary">
|
||||||
<h2 class="main-label">{{ 'entity.version-conflict.label' | translate }}</h2>
|
<h2 class="main-label">
|
||||||
|
{{ data.message }}
|
||||||
|
</h2>
|
||||||
<span fxFlex></span>
|
<span fxFlex></span>
|
||||||
<button mat-icon-button
|
<button mat-icon-button
|
||||||
(click)="onCancel()"
|
(click)="onCancel()"
|
||||||
@ -26,23 +28,23 @@
|
|||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<div class="message-container">
|
<div class="message-container">
|
||||||
<span>{{ data.message }}.</span>
|
|
||||||
<span>
|
<span>
|
||||||
{{ 'entity.version-conflict.link' | translate:
|
{{ 'entity.version-conflict.link' | translate:
|
||||||
{ entityType: (entityTypeTranslations.get(data.entity.id.entityType).type | translate) }
|
{ entityType: (entityTypeTranslations.get(data.entity.id.entityType).type | translate) }
|
||||||
}}
|
}}
|
||||||
<a class="cursor-pointer" (click)="onLinkClick($event)">{{ 'entity.link' | translate }}</a>.
|
<a class="cursor-pointer" (click)="onLinkClick($event)">{{ 'entity.link' | translate }}</a>.
|
||||||
</span>
|
</span>
|
||||||
|
<br/>
|
||||||
<span>{{ 'entity.version-conflict.message' | translate }}</span>
|
<span>{{ 'entity.version-conflict.message' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
|
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
|
||||||
<button mat-button color="primary"
|
<button mat-button color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
(click)="onCancel()"
|
(click)="onDiscard()"
|
||||||
cdkFocusInitial
|
cdkFocusInitial
|
||||||
>
|
>
|
||||||
{{ 'entity.version-conflict.cancel' | translate }}
|
{{ 'entity.version-conflict.discard' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary"
|
<button mat-raised-button color="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
$conflict-dialog-width: 700px;
|
$conflict-dialog-width: 530px;
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
.main-label {
|
.main-label {
|
||||||
|
|||||||
@ -47,6 +47,10 @@ export class EntityConflictDialogComponent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
onCancel(): void {
|
onCancel(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDiscard(): void {
|
||||||
this.dialogRef.close(false);
|
this.dialogRef.close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,23 +24,23 @@
|
|||||||
<mat-icon class="material-icons">close</mat-icon>
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="(isLoading$ | async) && !ignoreLoading">
|
||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<fieldset [disabled]="isLoading$ | async">
|
<fieldset [disabled]="(isLoading$ | async) && !ignoreLoading">
|
||||||
<mat-checkbox [formControl]="exportWidgetsFormControl">{{ 'widgets-bundle.export-widgets-bundle-widgets-prompt' | translate }}</mat-checkbox>
|
<mat-checkbox [formControl]="exportWidgetsFormControl">{{ 'widgets-bundle.export-widgets-bundle-widgets-prompt' | translate }}</mat-checkbox>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions fxLayoutAlign="end center">
|
<div mat-dialog-actions fxLayoutAlign="end center">
|
||||||
<button mat-button color="primary"
|
<button mat-button color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
[disabled]="(isLoading$ | async)"
|
[disabled]="(isLoading$ | async) && !ignoreLoading"
|
||||||
(click)="cancel()">
|
(click)="cancel()">
|
||||||
{{ 'action.cancel' | translate }}
|
{{ 'action.cancel' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button mat-raised-button color="primary"
|
<button mat-raised-button color="primary"
|
||||||
(click)="export()"
|
(click)="export()"
|
||||||
[disabled]="(isLoading$ | async)">
|
[disabled]="(isLoading$ | async) && !ignoreLoading">
|
||||||
{{ 'action.export' | translate }}
|
{{ 'action.export' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import { isDefinedAndNotNull } from '@core/utils';
|
|||||||
export interface ExportWidgetsBundleDialogData {
|
export interface ExportWidgetsBundleDialogData {
|
||||||
widgetsBundle: WidgetsBundle;
|
widgetsBundle: WidgetsBundle;
|
||||||
includeBundleWidgetsInExport: boolean;
|
includeBundleWidgetsInExport: boolean;
|
||||||
|
ignoreLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExportWidgetsBundleDialogResult {
|
export interface ExportWidgetsBundleDialogResult {
|
||||||
@ -44,6 +45,8 @@ export class ExportWidgetsBundleDialogComponent extends DialogComponent<ExportWi
|
|||||||
|
|
||||||
widgetsBundle: WidgetsBundle;
|
widgetsBundle: WidgetsBundle;
|
||||||
|
|
||||||
|
ignoreLoading = false;
|
||||||
|
|
||||||
exportWidgetsFormControl = new FormControl(true);
|
exportWidgetsFormControl = new FormControl(true);
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
@ -52,6 +55,7 @@ export class ExportWidgetsBundleDialogComponent extends DialogComponent<ExportWi
|
|||||||
public dialogRef: MatDialogRef<ExportWidgetsBundleDialogComponent, ExportWidgetsBundleDialogResult>) {
|
public dialogRef: MatDialogRef<ExportWidgetsBundleDialogComponent, ExportWidgetsBundleDialogResult>) {
|
||||||
super(store, router, dialogRef);
|
super(store, router, dialogRef);
|
||||||
this.widgetsBundle = data.widgetsBundle;
|
this.widgetsBundle = data.widgetsBundle;
|
||||||
|
this.ignoreLoading = data.ignoreLoading;
|
||||||
if (isDefinedAndNotNull(data.includeBundleWidgetsInExport)) {
|
if (isDefinedAndNotNull(data.includeBundleWidgetsInExport)) {
|
||||||
this.exportWidgetsFormControl.patchValue(data.includeBundleWidgetsInExport, {emitEvent: false});
|
this.exportWidgetsFormControl.patchValue(data.includeBundleWidgetsInExport, {emitEvent: false});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -352,28 +352,7 @@ export class ImportExportService {
|
|||||||
|
|
||||||
forkJoin(tasks).subscribe({
|
forkJoin(tasks).subscribe({
|
||||||
next: ({includeBundleWidgetsInExport, widgetsBundle}) => {
|
next: ({includeBundleWidgetsInExport, widgetsBundle}) => {
|
||||||
this.dialog.open<ExportWidgetsBundleDialogComponent, ExportWidgetsBundleDialogData,
|
this.handleExportWidgetsBundle(widgetsBundle, includeBundleWidgetsInExport);
|
||||||
ExportWidgetsBundleDialogResult>(ExportWidgetsBundleDialogComponent, {
|
|
||||||
disableClose: true,
|
|
||||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
||||||
data: {
|
|
||||||
widgetsBundle,
|
|
||||||
includeBundleWidgetsInExport
|
|
||||||
}
|
|
||||||
}).afterClosed().subscribe(
|
|
||||||
(result) => {
|
|
||||||
if (result) {
|
|
||||||
if (includeBundleWidgetsInExport !== result.exportWidgets) {
|
|
||||||
this.store.dispatch(new ActionPreferencesPutUserSettings({includeBundleWidgetsInExport: result.exportWidgets}));
|
|
||||||
}
|
|
||||||
if (result.exportWidgets) {
|
|
||||||
this.exportWidgetsBundleWithWidgetTypes(widgetsBundle);
|
|
||||||
} else {
|
|
||||||
this.exportWidgetsBundleWithWidgetTypeFqns(widgetsBundle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.handleExportError(e, 'widgets-bundle.export-failed-error');
|
this.handleExportError(e, 'widgets-bundle.export-failed-error');
|
||||||
@ -401,6 +380,9 @@ export class ImportExportService {
|
|||||||
}))
|
}))
|
||||||
.subscribe(ruleChainData => this.exportToPc(ruleChainData, entityData.name));
|
.subscribe(ruleChainData => this.exportToPc(ruleChainData, entityData.name));
|
||||||
return;
|
return;
|
||||||
|
case EntityType.WIDGETS_BUNDLE:
|
||||||
|
this.exportSelectedWidgetsBundle(entityData as WidgetsBundle);
|
||||||
|
return;
|
||||||
case EntityType.DASHBOARD:
|
case EntityType.DASHBOARD:
|
||||||
preparedData = this.prepareDashboardExport(entityData as Dashboard);
|
preparedData = this.prepareDashboardExport(entityData as Dashboard);
|
||||||
break;
|
break;
|
||||||
@ -410,6 +392,43 @@ export class ImportExportService {
|
|||||||
this.exportToPc(preparedData, entityData.name);
|
this.exportToPc(preparedData, entityData.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private exportSelectedWidgetsBundle(widgetsBundle: WidgetsBundle): void {
|
||||||
|
this.store.pipe(select(selectUserSettingsProperty( 'includeBundleWidgetsInExport'))).pipe(take(1)).subscribe({
|
||||||
|
next: (includeBundleWidgetsInExport) => {
|
||||||
|
this.handleExportWidgetsBundle(widgetsBundle, includeBundleWidgetsInExport, true);
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.handleExportError(e, 'widgets-bundle.export-failed-error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleExportWidgetsBundle(widgetsBundle: WidgetsBundle, includeBundleWidgetsInExport: boolean, ignoreLoading?: boolean): void {
|
||||||
|
this.dialog.open<ExportWidgetsBundleDialogComponent, ExportWidgetsBundleDialogData,
|
||||||
|
ExportWidgetsBundleDialogResult>(ExportWidgetsBundleDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||||
|
data: {
|
||||||
|
widgetsBundle,
|
||||||
|
includeBundleWidgetsInExport,
|
||||||
|
ignoreLoading
|
||||||
|
}
|
||||||
|
}).afterClosed().subscribe(
|
||||||
|
(result) => {
|
||||||
|
if (result) {
|
||||||
|
if (includeBundleWidgetsInExport !== result.exportWidgets) {
|
||||||
|
this.store.dispatch(new ActionPreferencesPutUserSettings({includeBundleWidgetsInExport: result.exportWidgets}));
|
||||||
|
}
|
||||||
|
if (result.exportWidgets) {
|
||||||
|
this.exportWidgetsBundleWithWidgetTypes(widgetsBundle);
|
||||||
|
} else {
|
||||||
|
this.exportWidgetsBundleWithWidgetTypeFqns(widgetsBundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private exportWidgetsBundleWithWidgetTypes(widgetsBundle: WidgetsBundle) {
|
private exportWidgetsBundleWithWidgetTypes(widgetsBundle: WidgetsBundle) {
|
||||||
this.widgetService.exportBundleWidgetTypesDetails(widgetsBundle.id.id).subscribe({
|
this.widgetService.exportBundleWidgetTypesDetails(widgetsBundle.id.id).subscribe({
|
||||||
next: (widgetTypesDetails) => {
|
next: (widgetTypesDetails) => {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ import { isNotEmptyStr, mergeDeepIgnoreArray } from '@core/utils';
|
|||||||
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
|
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
|
||||||
import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
|
import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
|
||||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||||
import { HasTenantId } from '@shared/models/entity.models';
|
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||||
import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
|
import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
|
||||||
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
|
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ export interface WidgetControllerDescriptor {
|
|||||||
actionSources?: {[actionSourceId: string]: WidgetActionSource};
|
actionSources?: {[actionSourceId: string]: WidgetActionSource};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseWidgetType extends BaseData<WidgetTypeId>, HasTenantId {
|
export interface BaseWidgetType extends BaseData<WidgetTypeId>, HasTenantId, HasVersion {
|
||||||
tenantId: TenantId;
|
tenantId: TenantId;
|
||||||
fqn: string;
|
fqn: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -2311,11 +2311,10 @@
|
|||||||
"list-of-edges": "{ count, plural, =1 {One edge} other {List of # edges} }",
|
"list-of-edges": "{ count, plural, =1 {One edge} other {List of # edges} }",
|
||||||
"edge-name-starts-with": "Edges whose names start with '{{prefix}}'",
|
"edge-name-starts-with": "Edges whose names start with '{{prefix}}'",
|
||||||
"version-conflict": {
|
"version-conflict": {
|
||||||
"label": "Version conflict",
|
"message": "Do you want to overwrite existing version or discard changes and load the latest version?",
|
||||||
"message": "Do you want to cancel your changes or overwrite existing version?",
|
|
||||||
"link": "You can download your version of the {{entityType}} using this",
|
"link": "You can download your version of the {{entityType}} using this",
|
||||||
"overwrite": "Overwrite version",
|
"overwrite": "Overwrite version",
|
||||||
"cancel": "Cancel changes"
|
"discard": "Discard changes"
|
||||||
},
|
},
|
||||||
"type-tb-resource": "Resource",
|
"type-tb-resource": "Resource",
|
||||||
"type-tb-resources": "Resources",
|
"type-tb-resources": "Resources",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user