src/app/core/basic-datatypes/date/date-import-config/date-import-dialog.component.ts

Description

Configuration dialog for parsing date value of data imported from a file.

Example

Metadata

Relationships

Used by

No results matching.

Depends on

Index

Properties
Methods

Constructor

constructor()

Methods

Async checkDateValues
checkDateValues()
Returns : any
Async save
save()
Returns : any

Properties

data
Type : unknown
Default value : inject<MappingDialogData>(MAT_DIALOG_DATA)
format
Type : unknown
Default value : new FormControl("")
hasLowercaseMM
Type : boolean

The date formatting interprets lowercase "mm" as minutes instead of months, this may lead to misunderstandings, so we check for it and show a warning if detected.

valid
Type : unknown
Default value : false
values
Type : { value: string; parsed?: Date }[]
Default value : []
import { ChangeDetectionStrategy, Component, inject } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import {
  MAT_DIALOG_DATA,
  MatDialogModule,
  MatDialogRef,
} from "@angular/material/dialog";
import { FormControl, ReactiveFormsModule } from "@angular/forms";
import { from } from "rxjs";
import { switchMap } from "rxjs/operators";
import { ConfirmationDialogService } from "../../../common-components/confirmation-dialog/confirmation-dialog.service";
import { MappingDialogData } from "app/core/import/import-column-mapping/mapping-dialog-data";
import { MatInputModule } from "@angular/material/input";
import { CustomDatePipe } from "../custom-date.pipe";
import { MatListModule } from "@angular/material/list";
import { MatButtonModule } from "@angular/material/button";
import { HelpButtonComponent } from "../../../common-components/help-button/help-button.component";
import { DynamicComponent } from "../../../config/dynamic-components/dynamic-component.decorator";
import { DateDatatype } from "../date.datatype";

/**
 * Configuration dialog for parsing date value of data imported from a file.
 */
@DynamicComponent("DateImportDialog")
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-date-import-dialog",
  templateUrl: "./date-import-dialog.component.html",
  styleUrls: ["./date-import-dialog.component.scss"],
  imports: [
    MatDialogModule,
    MatInputModule,
    ReactiveFormsModule,
    MatListModule,
    CustomDatePipe,
    MatButtonModule,
    HelpButtonComponent,
  ],
})
export class DateImportDialogComponent {
  data = inject<MappingDialogData>(MAT_DIALOG_DATA);
  private readonly confirmation = inject(ConfirmationDialogService);
  private readonly dialog = inject<MatDialogRef<any>>(MatDialogRef);

  format = new FormControl("");
  valid = false;
  values: { value: string; parsed?: Date }[] = [];

  /**
   * The date formatting interprets lowercase "mm" as minutes instead of months,
   * this may lead to misunderstandings, so we check for it and show a warning if detected.
   */
  hasLowercaseMM: boolean;

  constructor() {
    this.values = this.data.values
      .filter((val) => !!val)
      .map((value) => ({ value }));
    this.format.valueChanges
      .pipe(
        switchMap(() => from(this.checkDateValues())),
        takeUntilDestroyed(),
      )
      .subscribe();
    this.format.setValue(this.data.col.additional);
  }

  async checkDateValues() {
    this.format.setErrors(undefined);
    this.hasLowercaseMM = /mm/.test(this.format.value || "");
    const dateType = new DateDatatype();
    for (const val of this.values) {
      // TODO: check and improve the date parsing. Tests fail with moment.js > 2.29
      const date = await dateType.importMapFunction(
        val.value,
        undefined, // the schema is not needed here, we can skip loading it
        this.format.value,
      );
      if (date instanceof Date && !isNaN(date.getTime())) {
        val.parsed = date;
      } else {
        delete val.parsed;
        this.format.setErrors({ parsingError: true });
      }
    }
    // Sort unparsed dates to front
    this.values.sort((v1, v2) =>
      v1.parsed && !v2.parsed ? 1 : !v1.parsed && v2.parsed ? -1 : 0,
    );
  }

  async save() {
    const confirmed =
      !this.format.errors ||
      (await this.confirmation.getConfirmation(
        $localize`Ignore values?`,
        $localize`Some values don't have a mapping and will not be imported. Are you sure you want to keep it like this?`,
      ));

    if (confirmed) {
      // an empty format is a valid choice (dates the system reads on its own), but it has to be
      // stored as a value so that the column does not count as unconfigured afterwards
      this.data.col.additional = this.format.value ?? "";
      this.dialog.close();
    }
  }
}
<mat-dialog-content>
  <mat-form-field>
    <mat-label i18n>Date format</mat-label>
    <input [formControl]="format" matInput />

    <mat-hint>
      <div i18n>
        e.g. DD.MM.YYYY
        <a
          href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format"
          target="_blank"
          rel="noopener"
          >(help)</a
        >
      </div>

      @if (hasLowercaseMM) {
        <div i18n>
          Warning: "mm" is used for minutes, for months use "MM" instead.
        </div>
      }

      @if (format.errors) {
        <div class="warning" i18n>This format cannot parse all dates.</div>
      }
    </mat-hint>
  </mat-form-field>

  <app-help-button
    text="Define how date values in your imported data are formatted, so that the system correctly understands them. Values that do not match the given format will be ignored (remain empty) during import."
    i18n-text="import - value mapping (date) - help text"
  ></app-help-button>

  <mat-list>
    @for (val of values; track val) {
      <mat-list-item [class.invalid]="!val.parsed">
        {{ val.value }} -&gt; {{ val.parsed | customDate }}
      </mat-list-item>
    }
  </mat-list>
</mat-dialog-content>

<mat-dialog-actions>
  <button mat-raised-button color="accent" (click)="save()" i18n>
    Save & Close
  </button>
  <button mat-stroked-button matDialogClose i18n>Cancel</button>
</mat-dialog-actions>

./date-import-dialog.component.scss

.invalid {
  background-color: rgba(256, 0, 0, 0.5);
}

mat-list-item {
  margin: 5px 0;
  border-radius: 5px;
}

.warning {
  color: red;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""