UI: Add resources in use dialog with force to delete
This commit is contained in:
		
							parent
							
								
									cd6063fd09
								
							
						
					
					
						commit
						caf9063638
					
				@ -22,7 +22,13 @@ import {
 | 
				
			|||||||
  EntityTableConfig
 | 
					  EntityTableConfig
 | 
				
			||||||
} from '@home/models/entity/entities-table-config.models';
 | 
					} from '@home/models/entity/entities-table-config.models';
 | 
				
			||||||
import { Router } from '@angular/router';
 | 
					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 { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
 | 
				
			||||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
 | 
					import { NULL_UUID } from '@shared/models/id/has-uuid';
 | 
				
			||||||
import { DatePipe } from '@angular/common';
 | 
					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 { ResourcesLibraryComponent } from '@home/components/resources/resources-library.component';
 | 
				
			||||||
import { PageLink } from '@shared/models/page/page-link';
 | 
					import { PageLink } from '@shared/models/page/page-link';
 | 
				
			||||||
import { EntityAction } from '@home/models/entity/entity-component.models';
 | 
					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 { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
 | 
				
			||||||
import { ResourceLibraryTabsComponent } from '@home/pages/admin/resource/resource-library-tabs.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()
 | 
					@Injectable()
 | 
				
			||||||
export class ResourcesLibraryTableConfigResolver  {
 | 
					export class ResourcesLibraryTableConfigResolver  {
 | 
				
			||||||
@ -49,6 +65,8 @@ export class ResourcesLibraryTableConfigResolver  {
 | 
				
			|||||||
              private resourceService: ResourceService,
 | 
					              private resourceService: ResourceService,
 | 
				
			||||||
              private translate: TranslateService,
 | 
					              private translate: TranslateService,
 | 
				
			||||||
              private router: Router,
 | 
					              private router: Router,
 | 
				
			||||||
 | 
					              private dialog: MatDialog,
 | 
				
			||||||
 | 
					              private dialogService: DialogService,
 | 
				
			||||||
              private datePipe: DatePipe) {
 | 
					              private datePipe: DatePipe) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.config.entityType = EntityType.TB_RESOURCE;
 | 
					    this.config.entityType = EntityType.TB_RESOURCE;
 | 
				
			||||||
@ -76,19 +94,27 @@ export class ResourcesLibraryTableConfigResolver  {
 | 
				
			|||||||
        icon: 'file_download',
 | 
					        icon: 'file_download',
 | 
				
			||||||
        isEnabled: () => true,
 | 
					        isEnabled: () => true,
 | 
				
			||||||
        onAction: ($event, entity) => this.downloadResource($event, entity)
 | 
					        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',
 | 
					    this.config.groupActionDescriptors = [{
 | 
				
			||||||
      { resourceTitle: resource.title });
 | 
					      name: this.translate.instant('action.delete'),
 | 
				
			||||||
    this.config.deleteEntityContent = () => this.translate.instant('resource.delete-resource-text');
 | 
					      icon: 'delete',
 | 
				
			||||||
    this.config.deleteEntitiesTitle = count => this.translate.instant('resource.delete-resources-title', {count});
 | 
					      isEnabled: true,
 | 
				
			||||||
    this.config.deleteEntitiesContent = () => this.translate.instant('resource.delete-resources-text');
 | 
					      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.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink, this.config.componentsData.resourceType);
 | 
				
			||||||
    this.config.loadEntity = id => this.resourceService.getResourceInfoById(id.id);
 | 
					    this.config.loadEntity = id => this.resourceService.getResourceInfoById(id.id);
 | 
				
			||||||
    this.config.saveEntity = resource => this.saveResource(resource);
 | 
					    this.config.saveEntity = resource => this.saveResource(resource);
 | 
				
			||||||
    this.config.deleteEntity = id => this.resourceService.deleteResource(id.id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.config.onEntityAction = action => this.onResourceAction(action);
 | 
					    this.config.onEntityAction = action => this.onResourceAction(action);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -147,6 +173,8 @@ export class ResourcesLibraryTableConfigResolver  {
 | 
				
			|||||||
      case 'downloadResource':
 | 
					      case 'downloadResource':
 | 
				
			||||||
        this.downloadResource(action.event, action.entity);
 | 
					        this.downloadResource(action.event, action.entity);
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
 | 
					      case 'deleteLibrary':
 | 
				
			||||||
 | 
					        this.deleteResource(action.event, action.entity);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -165,4 +193,138 @@ export class ResourcesLibraryTableConfigResolver  {
 | 
				
			|||||||
      return authority === Authority.SYS_ADMIN;
 | 
					      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);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4497,7 +4497,12 @@
 | 
				
			|||||||
            "scada-symbol": "Scada symbol",
 | 
					            "scada-symbol": "Scada symbol",
 | 
				
			||||||
            "extension": "Extension",
 | 
					            "extension": "Extension",
 | 
				
			||||||
            "module": "Module"
 | 
					            "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": {
 | 
					    "javascript": {
 | 
				
			||||||
        "add": "Add JavaScript resource",
 | 
					        "add": "Add JavaScript resource",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user