opf/openproject

View on GitHub
frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts

Summary

Maintainability
B
6 hrs
Test Coverage
import {
  ApplicationRef,
  ChangeDetectorRef,
  Component,
  ComponentFactoryResolver,
  ElementRef,
  EventEmitter,
  Inject,
  InjectionToken,
  Injector,
  OnDestroy,
  OnInit,
  Optional,
  ViewChild,
} from '@angular/core';
import { ConfigurationService } from 'core-app/core/config/configuration.service';
import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service';
import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service';
import {
  ActiveTabInterface,
  TabComponent,
  TabInterface,
  TabPortalOutlet,
} from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet';
import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service';
import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space';
import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service';
import { OpModalComponent } from 'core-app/shared/components/modal/modal.component';
import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types';
import { ComponentType } from '@angular/cdk/portal';
import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource';
import { QueryResource } from 'core-app/features/hal/resources/query-resource';
import { StateService } from '@uirouter/angular';

export const WpTableConfigurationModalPrependToken = new InjectionToken<ComponentType<any>>('WpTableConfigurationModalPrependComponent');

@Component({
  templateUrl: './wp-table-configuration.modal.html',
})
export class WpTableConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy {
  public text = {
    title: this.I18n.t('js.work_packages.table_configuration.modal_title'),
    closePopup: this.I18n.t('js.close_popup_title'),

    columnsLabel: this.I18n.t('js.label_columns'),
    selectedColumns: this.I18n.t('js.description_selected_columns'),
    multiSelectLabel: this.I18n.t('js.work_packages.label_column_multiselect'),
    applyButton: this.I18n.t('js.modals.button_apply'),
    cancelButton: this.I18n.t('js.modals.button_cancel'),

    upsaleRelationColumns: this.I18n.t('js.modals.upsale_relation_columns'),
    upsaleRelationColumnsLink: this.I18n.t('js.modals.upsale_relation_columns_link'),
  };

  public onDataUpdated = new EventEmitter<void>();

  public selectedColumnMap:{ [id:string]:boolean } = {};

  // Get the view child we'll use as the portal host
  @ViewChild('tabContentOutlet', { static: true }) tabContentOutlet:ElementRef;

  // And a reference to the actual portal host interface
  public tabPortalHost:TabPortalOutlet;

  // Try to load an optional provided configuration service, and fall back to the default one
  private wpTableConfigurationService:WpTableConfigurationService =
  this.injector.get(WpTableConfigurationService, new WpTableConfigurationService(this.I18n, this.$state));

  constructor(
    @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap,
    @Optional() @Inject(WpTableConfigurationModalPrependToken) public prependModalComponent:ComponentType<any>|null,
    readonly I18n:I18nService,
    readonly injector:Injector,
    readonly appRef:ApplicationRef,
    readonly componentFactoryResolver:ComponentFactoryResolver,
    readonly loadingIndicator:LoadingIndicatorService,
    readonly querySpace:IsolatedQuerySpace,
    readonly wpStatesInitialization:WorkPackageStatesInitializationService,
    readonly apiV3Service:ApiV3Service,
    readonly notificationService:WorkPackageNotificationService,
    readonly wpTableColumns:WorkPackageViewColumnsService,
    readonly cdRef:ChangeDetectorRef,
    readonly ConfigurationService:ConfigurationService,
    readonly elementRef:ElementRef,
    readonly $state:StateService,
  ) {
    super(locals, cdRef, elementRef);
  }

  ngOnInit() {
    this.$element = this.elementRef.nativeElement as HTMLElement;

    this.tabPortalHost = new TabPortalOutlet(
      this.wpTableConfigurationService.tabs,
      this.tabContentOutlet.nativeElement,
      this.componentFactoryResolver,
      this.appRef,
      this.injector,
    );

    this.loadingIndicator.indicator('modal').promise = this.loadForm()
      .then(() => {
        const initialTabName = this.locals.initialTab;
        const initialTab = this.availableTabs.find((el) => el.id === initialTabName);
        this.switchTo(initialTab || this.availableTabs[0]);
        this.cdRef.detectChanges();
      });
  }

  ngOnDestroy():void {
    this.onDataUpdated.complete();
    this.tabPortalHost.dispose();
  }

  public get availableTabs():TabInterface[] {
    return this.tabPortalHost.availableTabs;
  }

  public get currentTab():ActiveTabInterface|null {
    return this.tabPortalHost.currentTab;
  }

  public switchTo(tab:TabInterface):void {
    this.tabPortalHost.switchTo(tab);
  }

  public saveChanges():void {
    this.tabPortalHost.activeComponents.forEach((component:TabComponent) => {
      component.onSave();
    });

    this.onDataUpdated.emit();
    this.service.close();
  }

  /**
   * Called when the user attempts to close the modal window.
   * The service will close this modal if this method returns true
   * @returns {boolean}
   */
  public onClose():boolean {
    this.afterFocusOn.focus();
    return true;
  }

  protected get afterFocusOn():HTMLElement {
    return this.$element;
  }

  protected loadForm() {
    const query = this.querySpace.query.value!;
    return this
      .apiV3Service
      .queries
      .form
      .load(query)
      .toPromise()
      .then(([form, _]:[QueryFormResource, QueryResource]) => {
        this.wpStatesInitialization.updateStatesFromForm(query, form);

        return form;
      })
      .catch((error) => this.notificationService.handleRawError(error));
  }
}