[BP-841] Add cyclic reporting form info component
Signed-off-by: Christopher Keim <keim@develop-group.de>
diff --git a/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.html b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.html
new file mode 100644
index 0000000..9ee5de1
--- /dev/null
+++ b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.html
@@ -0,0 +1,53 @@
+<!--
+/********************************************************************************
+ * Copyright © 2020 Basys GmbH.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+-->
+
+<div class="card bg-light cyclic-report-info">
+
+ <div class="cyclic-report-info-title">
+ Nächster Auslösezeitpunkt am {{nextTriggerDate | date : dateFormat}}:
+ </div>
+
+ <div class="cyclic-report-info-block">
+
+ <div>
+ Gültigkeit:
+ </div>
+ <div>
+ {{nextValidFromDate | date : dateFormat}} bis {{nextValidToDate | date : dateFormat}}
+ </div>
+
+ <div>
+ Betreff:
+ </div>
+ <div>
+ {{subject}}
+ </div>
+
+ <div>
+ Emailtext:
+ </div>
+ <div>
+ <ng-container *ngFor="let line of emailTextLines; let last = last;">
+ {{line}} <br *ngIf="!last || line === ''">
+ </ng-container>
+ </div>
+
+ <div>
+ Dateiname:
+ </div>
+ <div>
+ {{ fileName + '.' + data?.printFormat?.toLowerCase()}}
+ </div>
+
+ </div>
+
+</div>
diff --git a/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.scss b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.scss
new file mode 100644
index 0000000..e30ce90
--- /dev/null
+++ b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.scss
@@ -0,0 +1,34 @@
+/********************************************************************************
+ * Copyright © 2020 Basys GmbH.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+:host {
+ display: flex;
+ flex-flow: column;
+ overflow: hidden;
+ justify-content: center;
+ align-items: center;
+}
+
+.cyclic-report-info {
+ width: calc(min(100%, 35em));
+ box-sizing: border-box;
+ padding: 1em;
+ display: flex;
+ flex-flow: column;
+ word-break: break-word;
+}
+
+.cyclic-report-info-block {
+ padding-top: 0.25em;
+ padding-left: 1.25em;
+ display: grid;
+ grid-template-columns: max-content auto;
+ gap: 0 0.5em;
+}
diff --git a/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.spec.ts b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.spec.ts
new file mode 100644
index 0000000..abff32a
--- /dev/null
+++ b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.spec.ts
@@ -0,0 +1,106 @@
+/********************************************************************************
+ * Copyright © 2020 Basys GmbH.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {CommonModule} from '@angular/common';
+import {SimpleChange} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {CyclicReportObject} from '@shared/model/CyclicReportObject';
+import {Subject} from 'rxjs';
+import {CyclicReportFormInfoComponent} from './cyclic-report-form-info.component';
+
+describe('CyclicReportFormInfoComponent', () => {
+
+ const refreshInterval = new Subject<number>();
+
+ let component: CyclicReportFormInfoComponent;
+ let fixture: ComponentFixture<CyclicReportFormInfoComponent>;
+
+ let data: CyclicReportObject;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ CyclicReportFormInfoComponent
+ ],
+ imports: [
+ CommonModule
+ ]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CyclicReportFormInfoComponent);
+ component = fixture.componentInstance;
+ component.refreshInterval = refreshInterval;
+ component.dateReplacementTokens = component.cyclicReportingUtilService.dateReplacementTokens;
+ data = {
+ id: 19,
+ name: 'Cyclic Report ' + 19,
+ fileNamePattern: '{Date}_{Time}_{Week}',
+ subject: '{Date}_{Time}_{Week}',
+ to: [],
+ emailText: '',
+
+ reportName: 'reportName',
+ printFormat: 'pdf',
+ standByListId: 19,
+ statusId: 2,
+
+ triggerWeekDay: 1,
+ triggerHour: 7,
+ triggerMinute: 30,
+ validFromDayOffset: 0,
+ validFromHour: 7,
+ validFromMinute: 30,
+ validToDayOffset: 3,
+ validToHour: 18,
+ validToMinute: 0
+ };
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('should refresh on changes', () => {
+ const refreshSpy = spyOn(component, 'refresh').and.callThrough();
+ refreshSpy.calls.reset();
+ component.data = data;
+ component.ngOnChanges({ xyz: new SimpleChange(0, 1, false)});
+ expect(refreshSpy).not.toHaveBeenCalled();
+ component.ngOnChanges({ data: new SimpleChange(undefined, data, false)});
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+
+ it('should refresh automatically after a specific time', () => {
+ const refreshSpy = spyOn(component, 'refresh').and.callThrough();
+ refreshSpy.calls.reset();
+ expect(refreshSpy).not.toHaveBeenCalled();
+ refreshInterval.next(0);
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+
+ it('should ignore errors when refreshing', () => {
+ component.data = data;
+ spyOn(component.cyclicReportingUtilService, 'getNextTriggerDate').and.throwError('');
+ spyOn(console, 'error');
+ expect(() => component.refresh(new Date())).not.toThrow();
+ expect(component.cyclicReportingUtilService.getNextTriggerDate).toHaveBeenCalled();
+ });
+
+ it('should replace tokens', () => {
+ const date = new Date(2020, 11, 24, 19, 0);
+ expect(component.replaceTokens('{Date}', date)).toBe('20201224');
+ expect(component.replaceTokens(null, date)).toBe('');
+ expect(component.replaceTokens('{Date}', null)).toBe('');
+ });
+
+});
diff --git a/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.ts b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.ts
new file mode 100644
index 0000000..cd41c06
--- /dev/null
+++ b/src/app/cyclic-reporting/components/form-info/cyclic-report-form-info.component.ts
@@ -0,0 +1,106 @@
+/********************************************************************************
+ * Copyright © 2020 Basys GmbH.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {DatePipe} from '@angular/common';
+import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core';
+import {CyclicReportObject} from '@shared/model/CyclicReportObject';
+import {interval, Subscription} from 'rxjs';
+import {CyclicReportingUtilService} from '../../services/cyclic-reporting-util.service';
+
+@Component({
+ selector: 'ok-cyclic-report-form-info',
+ styleUrls: ['cyclic-report-form-info.component.scss'],
+ templateUrl: 'cyclic-report-form-info.component.html',
+ providers: [DatePipe]
+})
+export class CyclicReportFormInfoComponent implements OnInit, OnChanges, OnDestroy {
+
+ @Input()
+ public data: CyclicReportObject;
+
+ @Input()
+ public dateReplacementTokens: { [token: string]: string };
+
+ @Input()
+ public dateFormat = 'dd.MM.yyyy, HH:mm \'Uhr\'';
+
+ public nextTriggerDate: Date;
+
+ public nextValidFromDate: Date;
+
+ public nextValidToDate: Date;
+
+ public subject: string;
+
+ public fileName: string;
+
+ public emailTextLines: string[];
+
+ public refreshInterval = interval(1000 * 30);
+
+ private subscriptions: Subscription[] = [];
+
+ public constructor(
+ public datePipe: DatePipe,
+ public cyclicReportingUtilService: CyclicReportingUtilService
+ ) {
+
+ }
+
+ public ngOnInit() {
+ this.subscriptions.push(this.refreshInterval.subscribe(() => this.refresh(new Date())));
+ }
+
+ public ngOnChanges(changes: SimpleChanges) {
+ const keysToRefresh: Array<keyof CyclicReportFormInfoComponent> = [ 'data', 'dateReplacementTokens'];
+ if (keysToRefresh.some((key) => changes[key] != null)) {
+ this.refresh(new Date());
+ }
+ }
+
+ public ngOnDestroy() {
+ this.subscriptions.forEach((subscription) => subscription.unsubscribe());
+ }
+
+ public refresh(date: Date) {
+ try {
+ this.nextTriggerDate = undefined;
+ this.nextValidFromDate = undefined;
+ this.nextValidToDate = undefined;
+ this.subject = '';
+ this.fileName = '';
+
+ if (this.data == null) {
+ return;
+ }
+
+ this.nextTriggerDate = this.cyclicReportingUtilService.getNextTriggerDate(this.data, date);
+ this.nextValidFromDate = this.cyclicReportingUtilService.moveDateByDays(this.nextTriggerDate,
+ this.data.validFromDayOffset, this.data.validFromHour, this.data.validFromMinute);
+ this.nextValidToDate = this.cyclicReportingUtilService.moveDateByDays(this.nextTriggerDate,
+ this.data.validToDayOffset, this.data.validToHour, this.data.validToMinute);
+
+ this.subject = this.replaceTokens(this.data.subject, this.nextTriggerDate);
+ this.fileName = this.replaceTokens(this.data.fileNamePattern, this.nextTriggerDate);
+ this.emailTextLines = this.replaceTokens(this.data.emailText, this.nextTriggerDate).split('\n');
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ public replaceTokens(value: string, date: Date) {
+ const replacements = Object.entries({...this.dateReplacementTokens})
+ .map(([token, dateFormat]) => ({token, value: date == null ? '' : this.datePipe.transform(date, dateFormat)}));
+ return replacements.reduce((result, replacement) => {
+ return result.replace(new RegExp(replacement.token, 'g'), replacement.value);
+ }, typeof value !== 'string' ? '' : value);
+ }
+
+}