src/app/core/import/additional-actions/import-additional/import-additional-actions.component.ts

Description

Import sub-step: Let user select additional import actions like adding entities to a group entity.

Example

Metadata

Relationships

Used by

Depends on

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

entityType
Type : string
importActions
Type : AdditionalImportAction[]
Default value : []

Outputs

importActions
Type : AdditionalImportAction[]

Methods

addAction
addAction()
Returns : void
removeAction
removeAction(actionToRemove: AdditionalImportAction)
Parameters :
Name Type Optional
actionToRemove AdditionalImportAction No
Returns : void

Properties

actionToString
Type : unknown
Default value : () => {...}
availableImportActions
Type : unknown
Default value : computed(() => this.importAdditionalService .getActionsLinkingFor(this.entityType()) // an action links imported records into a related entity, which requires // permission to update that related type (targetType may allow several types) .filter( (action) => !this.ability.initialized || asArray(action.targetType).some((type) => this.ability.can("update", type), ), ) .sort(sortExportOnlyLast), )
linkEntityForm
Type : unknown
Default value : new FormGroup({ action: new FormControl({ value: "", disabled: true }, Validators.required), targetId: new FormControl( { value: "", disabled: true }, Validators.required, ), })
import { EditEntityComponent } from "#src/app/core/basic-datatypes/entity/edit-entity/edit-entity.component";
import {
  Component,
  inject,
  ChangeDetectionStrategy,
  input,
  model,
  computed,
  effect,
  DestroyRef,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import {
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { MatExpansionModule } from "@angular/material/expansion";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatListModule } from "@angular/material/list";
import { MatTooltipModule } from "@angular/material/tooltip";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { EntityBlockComponent } from "../../../basic-datatypes/entity/entity-block/entity-block.component";
import { BasicAutocompleteComponent } from "../../../common-components/basic-autocomplete/basic-autocomplete.component";
import { EntityTypeLabelPipe } from "../../../common-components/entity-type-label/entity-type-label.pipe";
import { HelpButtonComponent } from "../../../common-components/help-button/help-button.component";
import { AdditionalImportAction } from "../additional-import-action";
import { ImportAdditionalService } from "../import-additional.service";
import { EntityAbility } from "../../../permissions/ability/entity-ability";
import { asArray } from "../../../../utils/asArray";

/**
 * Import sub-step: Let user select additional import actions like adding entities to a group entity.
 */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-import-additional-actions",
  templateUrl: "./import-additional-actions.component.html",
  styleUrls: ["./import-additional-actions.component.scss"],
  imports: [
    MatListModule,
    FontAwesomeModule,
    MatTooltipModule,
    EntityTypeLabelPipe,
    EntityBlockComponent,
    ReactiveFormsModule,
    MatFormFieldModule,
    BasicAutocompleteComponent,
    MatButtonModule,
    EditEntityComponent,
    HelpButtonComponent,
    MatExpansionModule,
  ],
  providers: [EntityTypeLabelPipe],
})
export class ImportAdditionalActionsComponent {
  private importAdditionalService = inject(ImportAdditionalService);
  private readonly ability = inject(EntityAbility);

  entityType = input<string>();
  importActions = model<AdditionalImportAction[]>([]);

  availableImportActions = computed(() =>
    this.importAdditionalService
      .getActionsLinkingFor(this.entityType())
      // an action links imported records into a related entity, which requires
      // permission to update that related type (targetType may allow several types)
      .filter(
        (action) =>
          !this.ability.initialized ||
          asArray(action.targetType).some((type) =>
            this.ability.can("update", type),
          ),
      )
      .sort(sortExportOnlyLast),
  );

  // TODO: may need more distinction --> like in ImportModule?
  actionToString = (a: AdditionalImportAction) =>
    this.importAdditionalService.createActionLabel(a);

  linkEntityForm = new FormGroup({
    action: new FormControl({ value: "", disabled: true }, Validators.required),
    targetId: new FormControl(
      { value: "", disabled: true },
      Validators.required,
    ),
  });

  constructor() {
    this.linkEntityForm
      .get("action")
      .valueChanges.pipe(takeUntilDestroyed(inject(DestroyRef)))
      .subscribe((val) =>
        !!val
          ? this.linkEntityForm.get("targetId").enable()
          : this.linkEntityForm.get("targetId").disable(),
      );

    effect(() => {
      const entityType = this.entityType();
      this.linkEntityForm.reset();
      if (entityType) {
        this.linkEntityForm.get("action").enable();
      }
    });
  }

  addAction() {
    const newAction = this.linkEntityForm.get("action").getRawValue();
    this.importActions.update((actions) => [
      ...(actions ?? []),
      {
        ...newAction,
        targetId: this.linkEntityForm.get("targetId").value,
      },
    ]);
    this.linkEntityForm.reset();
  }

  removeAction(actionToRemove: AdditionalImportAction) {
    this.importActions.update((actions) =>
      actions.filter((a) => a !== actionToRemove),
    );
  }
}

function sortExportOnlyLast(
  a: AdditionalImportAction,
  b: AdditionalImportAction,
) {
  // show "non-expert" actions first
  if (a.expertOnly === b.expertOnly) return 0;
  else if (a.expertOnly) return 1;
  else return -1;
}
<mat-expansion-panel
  [expanded]="importActions()?.length > 0"
  [disabled]="!entityType()"
>
  <mat-expansion-panel-header>
    <mat-panel-title>
      <span i18n>Advanced Import Actions [optional]</span>
      <app-help-button
        style="float: right"
        text="In addition to creating new records directly for the imported data you can also define additional actions
                    (e.g. to make all imported records members of an existing activity or group).
                    This is an advanced functionality you do not have to use.
                    First select an import type above to enable this section.
                    If uncertain, please refer to the user guides, videos or support contact."
        i18n-text="import - additional actions - help text"
      >
      </app-help-button>
    </mat-panel-title>
  </mat-expansion-panel-header>

  <!-- Existing additional actions -->
  <div>
    <mat-list>
      @for (action of importActions(); track $index) {
        <mat-list-item>
          <fa-icon
            icon="xmark"
            matListItemIcon
            (click)="removeAction(action)"
            matTooltip="Remove"
            i18n-matTooltip
            class="existing-action-remove"
          ></fa-icon>
          <div matListItemTitle i18n>
            Link all imported records to
            {{ action.targetType | entityTypeLabel }}
          </div>
          <div matListItemLine>
            <app-entity-block
              [entityId]="action.targetId"
              [linkDisabled]="true"
            ></app-entity-block>
          </div>
        </mat-list-item>
      }
    </mat-list>

    @if (!(importActions()?.length > 0)) {
      <div
        class="no-actions margin-bottom-large"
        i18n="import additional actions"
      >
        no additional actions selected
      </div>
    }
  </div>

  <!-- Create new additional action -->
  @if (availableImportActions()?.length > 0) {
    <form [formGroup]="linkEntityForm">
      <div>
        @if (linkEntityForm.disabled) {
          <mat-error i18n>
            Select an Import Target Type before defining additional actions.
          </mat-error>
        }
      </div>

      <div class="flex-row flex-wrap align-start gap-regular">
        <mat-form-field class="action-field">
          <mat-label i18n="Label for linked entity type input"
            >Link all imported records to:
          </mat-label>
          <app-basic-autocomplete
            formControlName="action"
            [multi]="false"
            [options]="availableImportActions()"
            [optionToString]="actionToString"
          ></app-basic-autocomplete>
        </mat-form-field>

        <mat-form-field class="record-field">
          <mat-label i18n>for record</mat-label>
          <app-edit-entity
            formControlName="targetId"
            [entityType]="
              linkEntityForm.controls['action'].value?.['targetType']
            "
            [multi]="false"
            label="for record"
            i18n-label
          >
          </app-edit-entity>
        </mat-form-field>

        <button
          (click)="addAction()"
          [disabled]="!linkEntityForm.valid"
          mat-stroked-button
          color="accent"
          class="action-add-button"
          i18n
        >
          Add Action
        </button>
      </div>
    </form>
  } @else if (entityType()) {
    <p i18n>
      No special import actions are available for importing
      {{ entityType() | entityTypeLabel: true }} records.
    </p>
  }
</mat-expansion-panel>

./import-additional-actions.component.scss

.action-field {
  // larger grow ratio: the action label is the longest text in the row
  flex: 3 1 280px;
}

.record-field {
  flex: 2 1 220px;
  min-width: 200px;
}

.action-add-button {
  margin: auto 0;
}

.existing-action-remove {
  cursor: pointer;
}

.no-actions {
  font-style: italic;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""