UI: Add resources in use dialog with force to delete

This commit is contained in:
ArtemDzhereleiko 2025-09-12 16:23:53 +03:00
parent cd6063fd09
commit caf9063638
2 changed files with 177 additions and 10 deletions

View File

@ -22,7 +22,13 @@ import {
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { Router } from '@angular/router';
import { Resource, ResourceInfo, ResourceType, ResourceTypeTranslationMap } from '@shared/models/resource.models';
import {
Resource,
ResourceInfo, ResourceInfoWithReferences,
ResourceType,
ResourceTypeTranslationMap,
toResourceDeleteResult
} from '@shared/models/resource.models';
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { DatePipe } from '@angular/common';
@ -35,9 +41,19 @@ import { Authority } from '@shared/models/authority.enum';
import { ResourcesLibraryComponent } from '@home/components/resources/resources-library.component';
import { PageLink } from '@shared/models/page/page-link';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { map } from 'rxjs/operators';
import { catchError, map } from 'rxjs/operators';
import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
import { ResourceLibraryTabsComponent } from '@home/pages/admin/resource/resource-library-tabs.component';
import { forkJoin, of } from "rxjs";
import {
ResourcesInUseDialogComponent,
ResourcesInUseDialogData
} from "@shared/components/resource/resources-in-use-dialog.component";
import { parseHttpErrorMessage } from "@core/utils";
import { ActionNotificationShow } from "@core/notification/notification.actions";
import { ResourcesDatasource } from "@home/pages/admin/resource/resources-datasource";
import { MatDialog } from "@angular/material/dialog";
import { DialogService } from "@core/services/dialog.service";
@Injectable()
export class ResourcesLibraryTableConfigResolver {
@ -49,6 +65,8 @@ export class ResourcesLibraryTableConfigResolver {
private resourceService: ResourceService,
private translate: TranslateService,
private router: Router,
private dialog: MatDialog,
private dialogService: DialogService,
private datePipe: DatePipe) {
this.config.entityType = EntityType.TB_RESOURCE;
@ -76,19 +94,27 @@ export class ResourcesLibraryTableConfigResolver {
icon: 'file_download',
isEnabled: () => true,
onAction: ($event, entity) => this.downloadResource($event, entity)
}
},
{
name: this.translate.instant('resource.delete'),
icon: 'delete',
isEnabled: (resource) => this.config.deleteEnabled(resource),
onAction: ($event, entity) => this.deleteResource($event, entity)
},
);
this.config.deleteEntityTitle = resource => this.translate.instant('resource.delete-resource-title',
{ resourceTitle: resource.title });
this.config.deleteEntityContent = () => this.translate.instant('resource.delete-resource-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('resource.delete-resources-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('resource.delete-resources-text');
this.config.groupActionDescriptors = [{
name: this.translate.instant('action.delete'),
icon: 'delete',
isEnabled: true,
onAction: ($event, entities) => this.deleteResources($event, entities)
}];
this.config.entitiesDeleteEnabled = false;
this.config.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink, this.config.componentsData.resourceType);
this.config.loadEntity = id => this.resourceService.getResourceInfoById(id.id);
this.config.saveEntity = resource => this.saveResource(resource);
this.config.deleteEntity = id => this.resourceService.deleteResource(id.id);
this.config.onEntityAction = action => this.onResourceAction(action);
}
@ -147,6 +173,8 @@ export class ResourcesLibraryTableConfigResolver {
case 'downloadResource':
this.downloadResource(action.event, action.entity);
return true;
case 'deleteLibrary':
this.deleteResource(action.event, action.entity);
}
return false;
}
@ -165,4 +193,138 @@ export class ResourcesLibraryTableConfigResolver {
return authority === Authority.SYS_ADMIN;
}
}
private deleteResource($event: Event, resource: ResourceInfo) {
if ($event) {
$event.stopPropagation();
}
this.dialogService.confirm(
this.translate.instant('resource.delete-resource-title', { resourceTitle: resource.title }),
this.translate.instant('resource.delete-resource-text'),
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((result) => {
if (result) {
this.resourceService.deleteResource(resource.id.id, false, {ignoreErrors: true}).pipe(
map(() => toResourceDeleteResult(resource)),
catchError((err) => of(toResourceDeleteResult(resource, err)))
).subscribe(
(deleteResult) => {
if (deleteResult.success) {
if (this.config.getEntityDetailsPage()) {
this.config.getEntityDetailsPage().goBack();
} else {
this.config.updateData(true);
}
} else if (deleteResult.resourceIsReferencedError) {
const resources: ResourceInfoWithReferences[] = [{...resource, ...{references: deleteResult.references}}];
const data = {
multiple: false,
resources,
configuration: {
title: 'resource.resource-is-in-use',
message: this.translate.instant('resource.resource-is-in-use-text', {title: resources[0].title}),
deleteText: 'resource.delete-resource-in-use-text',
selectedText: 'resource.selected-resources',
columns: ['select', 'title', 'references']
}
};
this.dialog.open<ResourcesInUseDialogComponent, ResourcesInUseDialogData,
ResourceInfo[]>(ResourcesInUseDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data
}).afterClosed().subscribe((resources) => {
if (resources) {
this.resourceService.deleteResource(resource.id.id, true).subscribe(() => {
if (this.config.getEntityDetailsPage()) {
this.config.getEntityDetailsPage().goBack();
} else {
this.config.updateData(true);
}
});
}
});
} else {
const errorMessageWithTimeout = parseHttpErrorMessage(deleteResult.error, this.translate);
setTimeout(() => {
this.store.dispatch(new ActionNotificationShow({message: errorMessageWithTimeout.message, type: 'error'}));
}, errorMessageWithTimeout.timeout);
}
}
);
}
});
}
private deleteResources($event: Event, resources: ResourceInfo[]) {
if ($event) {
$event.stopPropagation();
}
if (resources && resources.length) {
const title = this.translate.instant('resource.delete-resources-title', {count: resources.length});
const content = this.translate.instant('resource.delete-resources-text');
this.dialogService.confirm(title, content,
this.translate.instant('action.no'),
this.translate.instant('action.yes')).subscribe((result) => {
if (result) {
const tasks = resources.map((resource) =>
this.resourceService.deleteResource(resource.id.id, false, {ignoreErrors: true}).pipe(
map(() => toResourceDeleteResult(resource)),
catchError((err) => of(toResourceDeleteResult(resource, err)))
)
);
forkJoin(tasks).subscribe(
(deleteResults) => {
const anySuccess = deleteResults.some(res => res.success);
const referenceErrors = deleteResults.filter(res => res.resourceIsReferencedError);
const otherError = deleteResults.find(res => !res.success);
if (anySuccess) {
this.config.updateData();
}
if (referenceErrors?.length) {
const resourcesWithReferences: ResourceInfoWithReferences[] =
referenceErrors.map(ref => ({...ref.resource, ...{references: ref.references}}));
const data = {
multiple: true,
resources: resourcesWithReferences,
configuration: {
title: 'resource.resources-are-in-use',
message: this.translate.instant('resource.resources-are-in-use-text'),
deleteText: 'resource.delete-resource-in-use-text',
selectedText: 'resource.selected-resources',
datasource: new ResourcesDatasource(this.resourceService, resourcesWithReferences, () => true),
columns: ['select', 'title', 'references']
}
};
this.dialog.open<ResourcesInUseDialogComponent, ResourcesInUseDialogData,
ResourceInfo[]>(ResourcesInUseDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data
}).afterClosed().subscribe((forceDeleteResources) => {
if (forceDeleteResources && forceDeleteResources.length) {
const forceDeleteTasks = forceDeleteResources.map((resource) =>
this.resourceService.deleteResource(resource.id.id, true)
);
forkJoin(forceDeleteTasks).subscribe(
() => {
this.config.updateData();
}
);
}
});
} else if (otherError) {
const errorMessageWithTimeout = parseHttpErrorMessage(otherError.error, this.translate);
setTimeout(() => {
this.store.dispatch(new ActionNotificationShow({message: errorMessageWithTimeout.message, type: 'error'}));
}, errorMessageWithTimeout.timeout);
}
}
);
}
});
}
}
}

View File

@ -4497,7 +4497,12 @@
"scada-symbol": "Scada symbol",
"extension": "Extension",
"module": "Module"
}
},
"resource-is-in-use": "Resource is used by other entities",
"resources-are-in-use": "Resources are used by other entities",
"resource-is-in-use-text": "The Resource <b>'{{title}}'</b> was not deleted because it is used by the following entities:",
"resources-are-in-use-text": "Not all Resources have been deleted because they are used by other entities.</br>You can view referenced entities by clicking the <b>References</b> button in the corresponding resource row.</br>If you still want to delete these resources, select them in the table below and click the <b>Delete selected</b> button.",
"delete-resource-in-use-text": "If you still want to delete the resource, click the <b>Delete anyway</b> button."
},
"javascript": {
"add": "Add JavaScript resource",