Description
Summary screen and confirmation / execution dialog for running an import.
Example
Methods
|
Async
executeImport
|
executeImport()
|
|
|
|
|
|
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>
Legend
Html element with directive