UI: Add resources in use dialog with force to delete
This commit is contained in:
		
							parent
							
								
									055a1ae56d
								
							
						
					
					
						commit
						8a561456b4
					
				@ -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);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user