src/app/core/import/import-confirm-summary/import-confirm-summary.component.ts

Description

Summary screen and confirmation / execution dialog for running an import.

Example

Metadata

Relationships

Used by

No results matching.

Depends on

Index

Properties
Methods

Constructor

constructor()

Methods

Async executeImport
executeImport()
Returns : any

Properties

data
Type : unknown
Default value : inject<ImportDialogData>(MAT_DIALOG_DATA)
importInProgress
Type : unknown
Default value : signal(false)
showInheritanceImportWarning
Type : unknown
Default value : signal(false)
import {
  Component,
  inject,
  ChangeDetectionStrategy,
  signal,
} from "@angular/core";
import { ImportService } from "../import.service";
import {
  MAT_DIALOG_DATA,
  MatDialogModule,
  MatDialogRef,
} from "@angular/material/dialog";
import { Entity } from "../../entity/model/entity";
import { ImportMetadata, ImportSettings } from "../import-metadata";
import { MatSnackBar } from "@angular/material/snack-bar";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { MatButtonModule } from "@angular/material/button";
import { Logging } from "../../logging/logging.service";
import { ConfirmationDialogService } from "../../common-components/confirmation-dialog/confirmation-dialog.service";
import { OkButton } from "../../common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component";
import { EntityRegistry } from "../../entity/database-entity.decorator";
import { HintBoxComponent } from "../../common-components/hint-box/hint-box.component";
import { hasMappedInheritedSourceField } from "../import-inheritance-warning.util";

/**
 * Data passed into Import Confirmation Dialog.
 */
export interface ImportDialogData {
  entitiesToImport: Entity[];
  importSettings: ImportSettings;
}

/**
 * Result returned from Import Confirmation Dialog.
 */
export interface ImportDialogResult {
  completedImport?: ImportMetadata;
  errorOccured?: boolean;
}

/**
 * Summary screen and confirmation / execution dialog for running an import.
 */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-import-confirm-summary",
  templateUrl: "./import-confirm-summary.component.html",
  styleUrls: ["./import-confirm-summary.component.scss"],
  imports: [
    MatDialogModule,
    MatProgressBarModule,
    MatButtonModule,
    HintBoxComponent,
  ],
})
export class ImportConfirmSummaryComponent {
  private readonly dialogRef =
    inject<MatDialogRef<ImportConfirmSummaryComponent>>(MatDialogRef);
  data = inject<ImportDialogData>(MAT_DIALOG_DATA);
  private readonly snackBar = inject(MatSnackBar);
  private readonly confirmationService = inject(ConfirmationDialogService);
  private readonly importService = inject(ImportService);
  private readonly entityRegistry = inject(EntityRegistry);

  importInProgress = signal(false);
  showInheritanceImportWarning = signal(false);

  constructor() {
    const entityType = this.data?.importSettings?.entityType;
    const entityCtor = entityType
      ? this.entityRegistry.get(entityType)
      : undefined;

    this.showInheritanceImportWarning.set(
      !!entityCtor &&
        hasMappedInheritedSourceField(
          entityCtor,
          this.data?.importSettings?.columnMapping ?? [],
        ),
    );
  }

  // TODO: detailed summary including warnings of unmapped columns, ignored values, etc. (#1943)

  async executeImport() {
    this.importInProgress.set(true);
    this.dialogRef.disableClose = true;

    try {
      const completedImport = await this.importService.executeImport(
        this.data.entitiesToImport,
        this.data.importSettings,
      );
      this.showImportSuccessToast(completedImport);
      this.dialogRef.close({ completedImport });
    } catch (error) {
      if (this.isPutAllConflictError(error)) {
        this.showImportPutAllConflictWarning();
      } else {
        // Handle all other errors
        Logging.warn("Import failed with error", error);
        this.showImportErrorMessage(error);
      }
      this.dialogRef.close({ errorOccured: true });
    } finally {
      this.importInProgress.set(false);
      this.dialogRef.disableClose = false;
    }
  }

  private showImportSuccessToast(completedImport: ImportMetadata) {
    const snackBarRef = this.snackBar.open(
      $localize`Import completed`,
      $localize`Undo`,
      {
        duration: 8000,
      },
    );
    snackBarRef.onAction().subscribe(async () => {
      await this.importService.undoImport(completedImport);
    });
  }

  private isPutAllConflictError(error: unknown): boolean {
    if (!Array.isArray(error)) {
      return false;
    }

    return error.some((entry) => {
      const putAllError = entry as {
        status?: number;
        name?: string;
        error?: string;
      };

      return (
        putAllError.status === 409 ||
        putAllError.name === "conflict" ||
        putAllError.error === "conflict"
      );
    });
  }

  private showImportPutAllConflictWarning() {
    this.confirmationService.getConfirmation(
      $localize`Conflicts overwriting updated data`,
      $localize`Some records changed from synchronisation while preparing the import. We are refreshing the data for you. Please review and run import again.`,
      OkButton,
    );
  }

  private showImportErrorMessage(error) {
    this.confirmationService.getConfirmation(
      $localize`Import failed`,
      $localize`Sorry, some error occurred during import. Please try again. If the problem persists, contact support. [${JSON.stringify(error)}]`,
      OkButton,
    );
  }
}
@if (!importInProgress()) {
  <h2 mat-dialog-title i18n>Start Import?</h2>
} @else {
  <h2 mat-dialog-title i18n>Importing Data</h2>
}

<mat-dialog-content>
  <!-- Summary -->
  <div i18n>{{ data.entitiesToImport?.length }} records will be imported.</div>

  @if (showInheritanceImportWarning()) {
    <app-hint-box type="warning" class="margin-bottom-regular">
      <strong i18n>⚠️ Manual update required</strong>
      <div i18n>
        Inherited values do not update automatically after import and must be
        updated manually.
      </div>
    </app-hint-box>
  }

  <!-- Progress -->
  @if (importInProgress()) {
    <div class="margin-top-large">
      <mat-progress-bar mode="indeterminate"></mat-progress-bar>
      <div i18n>Importing data ...</div>
    </div>
  }
</mat-dialog-content>

<mat-dialog-actions>
  <button
    mat-stroked-button
    color="accent"
    (click)="executeImport()"
    [disabled]="importInProgress()"
    i18n
  >
    Confirm & Run Import
  </button>
  <button
    mat-stroked-button
    [mat-dialog-close]="false"
    [disabled]="importInProgress()"
    i18n
  >
    Cancel
  </button>
</mat-dialog-actions>

./import-confirm-summary.component.scss

Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""