v1
This commit is contained in:
184
src/app/directives/auto-font-size.directive.ts
Normal file
184
src/app/directives/auto-font-size.directive.ts
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import { Directive, ElementRef, AfterViewInit, OnDestroy, Input, Renderer2, OnChanges, SimpleChanges } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[appAutoFontSize]'
|
||||||
|
})
|
||||||
|
export class AutoFontSizeDirective implements AfterViewInit, OnDestroy, OnChanges {
|
||||||
|
@Input() minFontSize: number = 12; // Mindestschriftgröße in Pixel
|
||||||
|
@Input() maxFontSize?: number = 70; // Maximale Schriftgröße in Pixel, optional
|
||||||
|
@Input() debounceDuration: number = 0; // Debounce-Dauer in Millisekunden
|
||||||
|
@Input() maxFontSizePercentage: number = 30; // Maximaler Prozentsatz der Elternhöhe für die Schriftgröße
|
||||||
|
|
||||||
|
private resizeObserver!: ResizeObserver;
|
||||||
|
private mutationObserver!: MutationObserver;
|
||||||
|
private resize$ = new Subject<void>();
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
private originalFontSizePx!: number;
|
||||||
|
|
||||||
|
// Internes Property zur Validierung
|
||||||
|
private _maxFontSizePercentage: number = 30;
|
||||||
|
|
||||||
|
constructor(private el: ElementRef, private renderer: Renderer2) { }
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
const element: HTMLElement = this.el.nativeElement;
|
||||||
|
|
||||||
|
// Speichere die originale Schriftgröße
|
||||||
|
const computedStyle = window.getComputedStyle(element);
|
||||||
|
const parsedFontSize = parseFloat(computedStyle.fontSize);
|
||||||
|
this.originalFontSizePx = isNaN(parsedFontSize) ? 16 : parsedFontSize;
|
||||||
|
|
||||||
|
// Set maxFontSize, falls nicht gesetzt
|
||||||
|
const effectiveMaxFontSize = this.maxFontSize ?? this.originalFontSizePx;
|
||||||
|
|
||||||
|
// Set initial font size
|
||||||
|
this.renderer.setStyle(element, 'font-size', `${effectiveMaxFontSize}px`);
|
||||||
|
|
||||||
|
// Initial Anpassung
|
||||||
|
this.adjustFontSize();
|
||||||
|
|
||||||
|
// Initialisiere den ResizeObserver
|
||||||
|
this.resizeObserver = new ResizeObserver(() => {
|
||||||
|
this.resize$.next();
|
||||||
|
});
|
||||||
|
this.resizeObserver.observe(element);
|
||||||
|
|
||||||
|
// Suche das nächstgelegene Element mit der Klasse 'arrival-card'
|
||||||
|
const arrivalCard = element.closest('.arrival-card') as HTMLElement | null;
|
||||||
|
if (arrivalCard) {
|
||||||
|
this.resizeObserver.observe(arrivalCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisiere den MutationObserver
|
||||||
|
this.mutationObserver = new MutationObserver(() => {
|
||||||
|
this.resize$.next();
|
||||||
|
});
|
||||||
|
this.mutationObserver.observe(element, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
characterData: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Debounce der Resize-Events, um übermäßige Aufrufe zu vermeiden
|
||||||
|
this.resize$.pipe(
|
||||||
|
debounceTime(this.debounceDuration),
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.adjustFontSize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes['maxFontSizePercentage']) {
|
||||||
|
// Re-validate und Anpassung der Schriftgröße bei Änderung
|
||||||
|
this.validateMaxFontSizePercentage();
|
||||||
|
this.adjustFontSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes['maxFontSize']) {
|
||||||
|
this.adjustFontSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes['minFontSize']) {
|
||||||
|
this.adjustFontSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.resizeObserver) {
|
||||||
|
this.resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
if (this.mutationObserver) {
|
||||||
|
this.mutationObserver.disconnect();
|
||||||
|
}
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-validate und setze den maxFontSizePercentage
|
||||||
|
*/
|
||||||
|
private validateMaxFontSizePercentage() {
|
||||||
|
if (this.maxFontSizePercentage < 1) {
|
||||||
|
this._maxFontSizePercentage = 1;
|
||||||
|
} else if (this.maxFontSizePercentage > 100) {
|
||||||
|
this._maxFontSizePercentage = 100;
|
||||||
|
} else {
|
||||||
|
this._maxFontSizePercentage = this.maxFontSizePercentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passt die Schriftgröße des Elements an, um Überlauf zu vermeiden und
|
||||||
|
* sicherzustellen, dass die Schriftgröße nicht mehr als maxFontSizePercentage der Elternhöhe beträgt.
|
||||||
|
* Verwendet eine binäre Suche zur effizienten Anpassung.
|
||||||
|
*/
|
||||||
|
private adjustFontSize() {
|
||||||
|
const element: HTMLElement = this.el.nativeElement;
|
||||||
|
// Suche das nächstgelegene Element mit der Klasse 'arrival-card'
|
||||||
|
const parent: HTMLElement | null = element.closest('.arrival-card') as HTMLElement | null;
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentWidth = parent.clientWidth;
|
||||||
|
const parentHeight = parent.clientHeight;
|
||||||
|
const elementScrollWidth = element.scrollWidth;
|
||||||
|
|
||||||
|
// Validierung des maxFontSizePercentage
|
||||||
|
this.validateMaxFontSizePercentage();
|
||||||
|
|
||||||
|
// Berechne die maximale Schriftgröße basierend auf der Elternhöhe (maxFontSizePercentage%)
|
||||||
|
const maxFontSizeByHeight = (this._maxFontSizePercentage / 100) * parentHeight;
|
||||||
|
|
||||||
|
// Bestimme die effektive maximale Schriftgröße
|
||||||
|
const effectiveMaxFontSize = this.maxFontSize
|
||||||
|
? Math.min(this.maxFontSize, maxFontSizeByHeight)
|
||||||
|
: Math.min(this.originalFontSizePx, maxFontSizeByHeight);
|
||||||
|
|
||||||
|
// Setze die Schriftgröße auf die maximale Größe
|
||||||
|
this.renderer.setStyle(element, 'font-size', `${effectiveMaxFontSize}px`);
|
||||||
|
|
||||||
|
// Überprüfe, ob der Inhalt überläuft (nur Breite)
|
||||||
|
if (this.isWidthOverflowing(element, parent)) {
|
||||||
|
let low = this.minFontSize;
|
||||||
|
let high = effectiveMaxFontSize;
|
||||||
|
let fontSize = high;
|
||||||
|
|
||||||
|
// Binäre Suche zur Anpassung der Schriftgröße
|
||||||
|
while (low <= high) {
|
||||||
|
const mid = Math.floor((low + high) / 2);
|
||||||
|
this.renderer.setStyle(element, 'font-size', `${mid}px`);
|
||||||
|
|
||||||
|
if (this.isWidthOverflowing(element, parent)) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
fontSize = mid;
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderer.setStyle(element, 'font-size', `${fontSize}px`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zusätzliche Debugging-Informationen
|
||||||
|
const finalFontSize = parseFloat(window.getComputedStyle(element).fontSize);
|
||||||
|
const finalElementWidth = element.scrollWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Überprüft, ob die Breite des Elements die des Eltern-Elements überschreitet.
|
||||||
|
* @param element Das zu überprüfende HTMLElement.
|
||||||
|
* @param parent Das Eltern-HTMLElement.
|
||||||
|
* @returns Wahr, wenn die Breite überläuft, sonst falsch.
|
||||||
|
*/
|
||||||
|
private isWidthOverflowing(element: HTMLElement, parent: HTMLElement): boolean {
|
||||||
|
const overflowing = element.scrollWidth > parent.clientWidth;
|
||||||
|
return overflowing;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { debounceTime, takeUntil } from 'rxjs/operators';
|
|||||||
})
|
})
|
||||||
export class AutoResizeTextDirective implements AfterViewInit, OnDestroy {
|
export class AutoResizeTextDirective implements AfterViewInit, OnDestroy {
|
||||||
private resizeObserver!: ResizeObserver;
|
private resizeObserver!: ResizeObserver;
|
||||||
|
private mutationObserver!: MutationObserver;
|
||||||
private resize$ = new Subject<void>();
|
private resize$ = new Subject<void>();
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
@@ -14,14 +15,25 @@ export class AutoResizeTextDirective implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
this.adjustFontSizes();
|
this.adjustFontSizes();
|
||||||
|
|
||||||
|
// Initialisiere den ResizeObserver
|
||||||
this.resizeObserver = new ResizeObserver(() => {
|
this.resizeObserver = new ResizeObserver(() => {
|
||||||
this.resize$.next();
|
this.resize$.next();
|
||||||
});
|
});
|
||||||
this.resizeObserver.observe(this.el.nativeElement);
|
this.resizeObserver.observe(this.el.nativeElement);
|
||||||
|
|
||||||
// Debounce der Resize-Events
|
// Initialisiere den MutationObserver
|
||||||
|
this.mutationObserver = new MutationObserver(() => {
|
||||||
|
this.resize$.next();
|
||||||
|
});
|
||||||
|
this.mutationObserver.observe(this.el.nativeElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
characterData: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Debounce der Resize-Events, um übermäßige Aufrufe zu vermeiden
|
||||||
this.resize$.pipe(
|
this.resize$.pipe(
|
||||||
debounceTime(100), // Warte 100ms nach dem letzten Resize-Event
|
|
||||||
takeUntil(this.destroy$)
|
takeUntil(this.destroy$)
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.adjustFontSizes();
|
this.adjustFontSizes();
|
||||||
@@ -32,80 +44,83 @@ export class AutoResizeTextDirective implements AfterViewInit, OnDestroy {
|
|||||||
if (this.resizeObserver) {
|
if (this.resizeObserver) {
|
||||||
this.resizeObserver.disconnect();
|
this.resizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
if (this.mutationObserver) {
|
||||||
|
this.mutationObserver.disconnect();
|
||||||
|
}
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Passt die Schriftgrößen der einzelnen Spans individuell an.
|
* Passt die Schriftgrößen der einzelnen Divs individuell an.
|
||||||
*/
|
*/
|
||||||
private adjustFontSizes() {
|
private adjustFontSizes() {
|
||||||
const container: HTMLElement = this.el.nativeElement;
|
const container: HTMLElement = this.el.nativeElement;
|
||||||
const spans: HTMLElement[] = Array.from(container.querySelectorAll('div.dynamic-font-size'));
|
const divs: HTMLElement[] = Array.from(container.querySelectorAll('div'));
|
||||||
|
|
||||||
// Store the original font sizes
|
// Original-Schriftgrößen speichern, falls noch nicht gespeichert
|
||||||
spans.forEach(span => {
|
divs.forEach(div => {
|
||||||
if (!span.dataset['originalFontSizePx']) {
|
if (!div.dataset['originalFontSizePx']) {
|
||||||
const computedStyle = window.getComputedStyle(span);
|
const computedStyle = window.getComputedStyle(div);
|
||||||
const originalFontSizePx = parseFloat(computedStyle.fontSize);
|
const originalFontSizePx = parseFloat(computedStyle.fontSize);
|
||||||
span.dataset['originalFontSizePx'] = originalFontSizePx.toString();
|
div.dataset['originalFontSizePx'] = originalFontSizePx.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset font sizes to original
|
// Setze die Schriftgrößen auf die Originalwerte zurück
|
||||||
spans.forEach(span => {
|
divs.forEach(div => {
|
||||||
const originalFontSizePx = parseFloat(span.dataset['originalFontSizePx'] || '16');
|
const originalFontSizePx = parseFloat(div.dataset['originalFontSizePx'] || '16');
|
||||||
span.style.fontSize = `${originalFontSizePx}px`;
|
div.style.fontSize = `${originalFontSizePx}px`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If content overflows, adjust font sizes
|
// Wenn der Inhalt überläuft, passe die Schriftgrößen an
|
||||||
if (this.isOverflowing(container)) {
|
if (this.isOverflowing(container)) {
|
||||||
// Find the scaling factor needed to fit the content
|
// Berechne den Skalierungsfaktor, der benötigt wird, um den Inhalt anzupassen
|
||||||
let scalingFactor = this.calculateScalingFactor(container, spans);
|
let scalingFactor = this.calculateScalingFactor(container, divs);
|
||||||
|
|
||||||
// Apply the scaling factor to all spans
|
// Wende den Skalierungsfaktor auf alle Divs an
|
||||||
spans.forEach(span => {
|
divs.forEach(div => {
|
||||||
const originalFontSizePx = parseFloat(span.dataset['originalFontSizePx'] || '16');
|
const originalFontSizePx = parseFloat(div.dataset['originalFontSizePx'] || '16');
|
||||||
let newFontSizePx = originalFontSizePx * scalingFactor;
|
let newFontSizePx = originalFontSizePx * scalingFactor;
|
||||||
|
|
||||||
// Ensure the font size doesn't go below a minimum value
|
// Stelle sicher, dass die Schriftgröße nicht unter einen Mindestwert fällt
|
||||||
const minFontSizePx = 12;
|
const minFontSizePx = 12;
|
||||||
if (newFontSizePx < minFontSizePx) {
|
if (newFontSizePx < minFontSizePx) {
|
||||||
newFontSizePx = minFontSizePx;
|
newFontSizePx = minFontSizePx;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.style.fontSize = `${newFontSizePx}px`;
|
div.style.fontSize = `${newFontSizePx}px`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the scaling factor needed to fit all content within the container.
|
* Berechnet den Skalierungsfaktor, um den gesamten Inhalt anzupassen.
|
||||||
* @param container The container element.
|
* @param container Das Container-Element.
|
||||||
* @param spans The list of span elements.
|
* @param divs Die Liste der Div-Elemente.
|
||||||
* @returns The scaling factor as a decimal.
|
* @returns Der Skalierungsfaktor als Dezimalzahl.
|
||||||
*/
|
*/
|
||||||
private calculateScalingFactor(container: HTMLElement, spans: HTMLElement[]): number {
|
private calculateScalingFactor(container: HTMLElement, divs: HTMLElement[]): number {
|
||||||
const minFontSizePx = 12; // Minimum font size
|
const minFontSizePx = 12; // Mindest-Schriftgröße
|
||||||
let low = 0.1; // 10% scaling
|
let low = 0.1; // 10% Skalierung
|
||||||
let high = 1; // 100% scaling
|
let high = 1; // 100% Skalierung
|
||||||
let mid = 1;
|
let mid = 1;
|
||||||
|
|
||||||
// Binary search to find the optimal scaling factor
|
// Binäre Suche, um den optimalen Skalierungsfaktor zu finden
|
||||||
while (high - low > 0.01) {
|
while (high - low > 0.01) {
|
||||||
mid = (low + high) / 2;
|
mid = (low + high) / 2;
|
||||||
|
|
||||||
// Apply scaling factor
|
// Anwenden des Skalierungsfaktors
|
||||||
spans.forEach(span => {
|
divs.forEach(div => {
|
||||||
const originalFontSizePx = parseFloat(span.dataset['originalFontSizePx'] || '16');
|
const originalFontSizePx = parseFloat(div.dataset['originalFontSizePx'] || '16');
|
||||||
let newFontSizePx = originalFontSizePx * mid;
|
let newFontSizePx = originalFontSizePx * mid;
|
||||||
if (newFontSizePx < minFontSizePx) {
|
if (newFontSizePx < minFontSizePx) {
|
||||||
newFontSizePx = minFontSizePx;
|
newFontSizePx = minFontSizePx;
|
||||||
}
|
}
|
||||||
span.style.fontSize = `${newFontSizePx}px`;
|
div.style.fontSize = `${newFontSizePx}px`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if content overflows
|
// Überprüfen, ob der Inhalt überläuft
|
||||||
if (this.isOverflowing(container)) {
|
if (this.isOverflowing(container)) {
|
||||||
high = mid;
|
high = mid;
|
||||||
} else {
|
} else {
|
||||||
@@ -116,7 +131,6 @@ export class AutoResizeTextDirective implements AfterViewInit, OnDestroy {
|
|||||||
return low;
|
return low;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Überprüft, ob der Inhalt des Containers überläuft.
|
* Überprüft, ob der Inhalt des Containers überläuft.
|
||||||
* @param container Das zu überprüfende HTMLElement.
|
* @param container Das zu überprüfende HTMLElement.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { IonicModule } from '@ionic/angular';
|
|||||||
|
|
||||||
import { AvisoPageRoutingModule } from './aviso-routing.module';
|
import { AvisoPageRoutingModule } from './aviso-routing.module';
|
||||||
import { AutoResizeTextDirective } from '../../directives/auto-resize-text.directive';
|
import { AutoResizeTextDirective } from '../../directives/auto-resize-text.directive';
|
||||||
|
import { AutoFontSizeDirective } from '../../directives/auto-font-size.directive';
|
||||||
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
|
||||||
@@ -15,9 +16,9 @@ import { AutoResizeTextDirective } from '../../directives/auto-resize-text.direc
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
IonicModule,
|
IonicModule,
|
||||||
AvisoPageRoutingModule
|
AvisoPageRoutingModule,
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [AvisoPage, AutoResizeTextDirective]
|
declarations: [AvisoPage, AutoResizeTextDirective, AutoFontSizeDirective]
|
||||||
})
|
})
|
||||||
export class AvisoPageModule {}
|
export class AvisoPageModule {}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<ion-content fullscreen>
|
<ion-content fullscreen>
|
||||||
|
|
||||||
|
<!-- ======================================================
|
||||||
|
Header
|
||||||
|
====================================================== -->
|
||||||
<ion-header>
|
<ion-header>
|
||||||
|
<!-- Ihr Header-Inhalt -->
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
|
<!-- ======================================================
|
||||||
<!-- Fehlernachricht anzeigen -->
|
Fehlernachricht und Ladeindikator
|
||||||
|
====================================================== -->
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
|
<!-- Fehlernachricht anzeigen -->
|
||||||
<ion-row *ngIf="errorMessage">
|
<ion-row *ngIf="errorMessage">
|
||||||
<ion-col size="12" class="ion-text-center">
|
<ion-col size="12" class="ion-text-center">
|
||||||
<ion-text color="danger">
|
<ion-text color="danger">
|
||||||
@@ -23,41 +28,83 @@
|
|||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-grid>
|
</ion-grid>
|
||||||
|
|
||||||
<!-- Grid-Container für Arrivals Kacheln -->
|
<!-- ======================================================
|
||||||
|
Grid-Container für Arrivals Kacheln
|
||||||
|
====================================================== -->
|
||||||
<div class="grid-container" *ngIf="!loadingArrivals && pages.length > 0">
|
<div class="grid-container" *ngIf="!loadingArrivals && pages.length > 0">
|
||||||
<ion-card *ngFor="let aviso of pages[currentPageIndex]; let i = index"
|
<ion-card *ngFor="let aviso of pages[currentPageIndex]; let i = index"
|
||||||
class="arrival-card"
|
class="arrival-card"
|
||||||
[ngClass]="getStatusClass(aviso.status, aviso.lkW_fertig)">
|
[ngClass]="getStatusClass(aviso.status, aviso.lkW_fertig, aviso.avisoTVHinweis)">
|
||||||
|
|
||||||
<!-- Nummerierung in der oberen linken Ecke -->
|
|
||||||
<div class="card-number">{{ getOverallIndex(i) }}</div>
|
<div class="card-number">{{ getOverallIndex(i) }}</div>
|
||||||
|
|
||||||
<ion-card-header>
|
<ng-container *ngIf="shouldShowNormalContent(aviso); else showAvisoHinweis">
|
||||||
<ion-card-title class="ion-text-center">
|
<div class="flex-container">
|
||||||
<strong>{{ aviso.lkW_Nr }}</strong>
|
|
||||||
</ion-card-title>
|
<div class="lkwnr"
|
||||||
</ion-card-header>
|
appAutoFontSize
|
||||||
<ion-card-content>
|
[maxFontSizePercentage]="34"
|
||||||
<!-- Zentrierter Absatz für Ankunft und Dauer -->
|
[maxFontSize]="avisoTvSettings[0].kachelFontSizeLkwNummer">
|
||||||
<div class="centered-content">
|
{{ aviso.lkW_Nr }}
|
||||||
<p class="ion-text-center">
|
</div>
|
||||||
|
|
||||||
|
<div class="ion-text-center"
|
||||||
|
appAutoFontSize
|
||||||
|
[maxFontSizePercentage]="33"
|
||||||
|
[maxFontSize]="avisoTvSettings[0].kachelFontSizeDateTime">
|
||||||
<ion-icon [name]="getStatusIcon(aviso.status)"
|
<ion-icon [name]="getStatusIcon(aviso.status)"
|
||||||
[color]="getStatusColor(aviso.status)"></ion-icon>
|
[color]="getStatusColor(aviso.status)"
|
||||||
|
class="inline-icon"></ion-icon>
|
||||||
{{ aviso.ankunft }}
|
{{ aviso.ankunft }}
|
||||||
<ion-icon name="hourglass-outline" [color]="getStatusColor(aviso.status)"></ion-icon>
|
<ion-icon name="hourglass-outline"
|
||||||
|
[color]="getStatusColor(aviso.status)"
|
||||||
|
class="inline-icon"></ion-icon>
|
||||||
{{ aviso.dauer }}
|
{{ aviso.dauer }}
|
||||||
</p>
|
</div>
|
||||||
<p class="ion-text-center">
|
|
||||||
|
<div class="ion-text-center"
|
||||||
|
appAutoFontSize
|
||||||
|
[maxFontSizePercentage]="33"
|
||||||
|
[maxFontSize]="avisoTvSettings[0].kachelFontSizeDateTime">
|
||||||
<ion-icon name="person-outline"></ion-icon>
|
<ion-icon name="person-outline"></ion-icon>
|
||||||
{{ aviso.letzterMitarbeiter }}
|
{{ aviso.letzterMitarbeiter }}
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</ion-card-content>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div *ngIf="aviso.avisoTVHinweis"
|
||||||
|
[ngClass]="getImageClassButtomLeft(aviso.avisoTVHinweis)"
|
||||||
|
class="blinking-image">
|
||||||
|
<img [src]="getTvHinweisImgSrc(aviso.avisoTVHinweis)" alt="Aviso Hinweis" />
|
||||||
|
</div>
|
||||||
|
<div *ngIf="aviso.lkW_fertig && aviso.avisoTVHinweis == '' " class="bottom-left-image-round">
|
||||||
|
<img [src]="'assets/warnings/okRound.png'" alt="Aviso Hinweis" />
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #showAvisoHinweis>
|
||||||
|
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="lkwnr" style="margin-top: 1%; margin-bottom: 1%"
|
||||||
|
appAutoFontSize
|
||||||
|
[maxFontSizePercentage]="35">
|
||||||
|
{{ aviso.avisoTVHinweis }}
|
||||||
|
</div>
|
||||||
|
<div class="ion-text-center">
|
||||||
|
<img [src]="getTvHinweisImgSrc(aviso.avisoTVHinweis)"
|
||||||
|
[ngClass]="getImageClass(aviso.avisoTVHinweis)"
|
||||||
|
alt="Aviso Hinweis" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ======================================================
|
||||||
<!-- Nachricht, wenn keine Arrivals vorhanden sind -->
|
Nachricht bei fehlenden Arrivals
|
||||||
|
====================================================== -->
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row *ngIf="!loadingArrivals && arrivals.length === 0 && !errorMessage">
|
<ion-row *ngIf="!loadingArrivals && arrivals.length === 0 && !errorMessage">
|
||||||
<ion-col size="12" class="ion-text-center">
|
<ion-col size="12" class="ion-text-center">
|
||||||
@@ -71,45 +118,56 @@
|
|||||||
<!-- Bedingtes Textfeld für TV-Einstellungen -->
|
<!-- Bedingtes Textfeld für TV-Einstellungen -->
|
||||||
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
<!-- ======================================================
|
||||||
|
Logobar mit Progress Bar
|
||||||
|
====================================================== -->
|
||||||
<ion-toolbar class="logobar">
|
<ion-toolbar class="logobar">
|
||||||
<span slot="start" class="title">
|
<span slot="start" class="title">
|
||||||
|
|
||||||
{{ currentDate | date: 'HH:mm:ss' }} <br />
|
{{ currentDate | date: 'HH:mm:ss' }} <br />
|
||||||
{{ currentDate | date: ' dd.MM.yyyy' }}
|
{{ currentDate | date: 'dd.MM.yyyy' }}
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ion-title class="ion-text-center title">
|
<ion-title class="ion-text-center title">
|
||||||
<img [src]="'assets/Logos/' + avisoTvSettings[0].logo" class="logo">
|
<img [src]="'assets/Logos/' + avisoTvSettings[0].logo" class="logo" />
|
||||||
</ion-title>
|
</ion-title>
|
||||||
|
|
||||||
<span slot="end" class="title">({{ totalArrivals }}) <br /> {{ currentPageIndex + 1 }} / {{ pages.length }}</span>
|
<span slot="end" class="title">
|
||||||
<ion-progress-bar [value]="progressBarValue" class="custom-progress-bar" buffer="1"></ion-progress-bar>
|
({{ totalArrivals }}) <br /> {{ currentPageIndex + 1 }} / {{ pages.length }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<ion-progress-bar [value]="progressBarValue"
|
||||||
|
class="custom-progress-bar"
|
||||||
|
buffer="1"></ion-progress-bar>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
<ion-footer>
|
|
||||||
|
<!-- ======================================================
|
||||||
|
Footer mit Tabs
|
||||||
|
====================================================== -->
|
||||||
|
<ion-footer class="no-border-footer">
|
||||||
<ng-container *ngFor="let setting of avisoTvTextSettings; let i = index">
|
<ng-container *ngFor="let setting of avisoTvTextSettings; let i = index">
|
||||||
<ng-container *ngIf="setting.isActive && settingDisplayData[i]?.nonEmptyFixeZeilen?.length">
|
<ng-container *ngIf="setting.isActive && settingDisplayData[i]?.nonEmptyFixeZeilen?.length">
|
||||||
<ion-item>
|
<ion-item lines="none" class="no-border-item">
|
||||||
<div [innerHTML]="settingDisplayData[i].nonEmptyFixeZeilen[settingDisplayData[i].currentDivIndex]?.sanitizedHtml"
|
<div [innerHTML]="settingDisplayData[i].nonEmptyFixeZeilen[settingDisplayData[i].currentDivIndex]?.sanitizedHtml"
|
||||||
autoResizeText
|
appAutoFontSize
|
||||||
|
[minFontSize]="12"
|
||||||
|
[maxFontSize]="24"
|
||||||
[style.text-align]="settingDisplayData[i].nonEmptyFixeZeilen[settingDisplayData[i].currentDivIndex]?.textAlign"
|
[style.text-align]="settingDisplayData[i].nonEmptyFixeZeilen[settingDisplayData[i].currentDivIndex]?.textAlign"
|
||||||
class="htmltext">
|
class="htmltext responsive-title">
|
||||||
</div>
|
</div>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<div style="display:flex;justify-content:center">
|
|
||||||
<div style="width:10vw">
|
<div class="tabs-container"
|
||||||
<ion-segment [(ngModel)]="settingDisplayData[i].currentDivIndex"
|
style="display: flex; justify-content: center; background-color:white;">
|
||||||
class="custom-segment">
|
<div class="custom-segment">
|
||||||
<ion-segment-button *ngFor="let item of settingDisplayData[i].nonEmptyFixeZeilen; let j = index"
|
<button *ngFor="let item of settingDisplayData[i].nonEmptyFixeZeilen; let j = index"
|
||||||
[value]="j"
|
(click)="settingDisplayData[i].currentDivIndex = j"
|
||||||
|
[class.active]="settingDisplayData[i].currentDivIndex === j"
|
||||||
class="custom-segment-button">
|
class="custom-segment-button">
|
||||||
|
<img [src]="getImageForTab(j)" alt="Tab Image" class="tab-image" />
|
||||||
</ion-segment-button>
|
</button>
|
||||||
</ion-segment>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
|
/* ======================================================
|
||||||
|
Allgemeine Stile
|
||||||
|
====================================================== */
|
||||||
|
|
||||||
ion-content {
|
ion-content {
|
||||||
--background: #f0f0f0; /* Heller Hintergrund für besseren Kontrast */
|
--background: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Grid-Container für Arrivals Kacheln
|
||||||
|
====================================================== */
|
||||||
.grid-container {
|
.grid-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(var(--tile-width-percent), 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(var(--tile-width-percent), 1fr));
|
||||||
row-gap: 1vh;
|
row-gap: 1vh;
|
||||||
column-gap: 1vh;
|
column-gap: 1vh;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,9 +37,45 @@ ion-content {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0em;
|
padding: 0;
|
||||||
box-sizing: border-box; /* Padding wird in der Höhe berücksichtigt */
|
box-sizing: border-box;
|
||||||
animation: fadeIn 0.2s ease-in-out; /* Kürzere Animationsdauer */
|
animation: fadeIn 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-number {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
font-size: calc(var(--tile-height-vh) * 0.18);
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #003680;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Typografie
|
||||||
|
====================================================== */
|
||||||
|
|
||||||
|
ion-title {
|
||||||
|
font-size: calc(var(--tile-height-vh) * 0.24);
|
||||||
|
font-weight: bold;
|
||||||
|
color: #003680;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: clamp(12px, 2vw, var(--tile-title-font-size));
|
||||||
|
font-weight: bold;
|
||||||
|
color: #003680;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lkwnr {
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #002050;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.htmltext {
|
.htmltext {
|
||||||
@@ -32,23 +85,41 @@ ion-content {
|
|||||||
transition: box-shadow 0.3s, transform 0.3s;
|
transition: box-shadow 0.3s, transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Icons
|
||||||
|
====================================================== */
|
||||||
|
|
||||||
ion-icon {
|
ion-icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-size: calc(var(--tile-height-vh) * 0.18); /* Anpassung der Icon-Größe */
|
font-size: calc(var(--tile-height-vh) * 0.12);
|
||||||
margin-right: 0.3em; /* Abstand zwischen Icon und Text */
|
margin-right: -3px;
|
||||||
|
margin-left: 0.1em;
|
||||||
|
margin-bottom:0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Statusklassen */
|
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Statusklassen
|
||||||
|
====================================================== */
|
||||||
.lkwfertig {
|
.lkwfertig {
|
||||||
border-left: 10px solid green;
|
border-left: 10px solid #306c0c;
|
||||||
background-color: #e6ffe6; /* Leicht grüner Hintergrund für "fertig" */
|
background-color: #e6ffe6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fehlt {
|
||||||
|
border-left: 10px solid #a00f05;
|
||||||
|
background-color: #f79391;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statusLKW_LKW_neu,
|
.statusLKW_LKW_neu,
|
||||||
.statusLKW_Ankunft {
|
.statusLKW_Ankunft {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Animationen
|
||||||
|
====================================================== */
|
||||||
|
|
||||||
/* Animationen für Ankunftskarten */
|
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -61,138 +132,141 @@ ion-icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Typografie */
|
.blinking-image {
|
||||||
ion-title {
|
animation: blink-animation 1s steps(2, start) infinite;
|
||||||
font-size: calc(var(--tile-height-vh) * 0.24); /* 4% der Kachelhöhe */
|
|
||||||
font-weight: bold;
|
|
||||||
color: #003680;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
@keyframes blink-animation {
|
||||||
font-size: calc(var(--tile-height-vh) * 0.24); /* 4% der Kachelhöhe */
|
0%, 50% {
|
||||||
font-weight: bold;
|
opacity: 1;
|
||||||
color: #003680;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card-title {
|
|
||||||
font-size: calc(var(--tile-height-vh) * 0.06); /* Erhöht auf 6% der Kachelhöhe */
|
|
||||||
margin: 0; /* Abstand unter dem Titel */
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap; /* Erlaubt Zeilenumbrüche */
|
|
||||||
overflow-wrap: normal; /* Bricht lange Wörter um */
|
|
||||||
color: #002050; /* Dunkleres Blau für besseren Kontrast */
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card-content p {
|
|
||||||
font-size: calc(var(--tile-height-vh) * 0.04); /* Erhöht auf 4% der Kachelhöhe */
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap; /* Erlaubt Zeilenumbrüche */
|
|
||||||
overflow-wrap: normal; /* Bricht lange Wörter um */
|
|
||||||
line-height: 1.5; /* Verbesserte Lesbarkeit */
|
|
||||||
color: #333; /* Dunkle Schriftfarbe für besseren Kontrast */
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered-content p {
|
|
||||||
margin-bottom: 0.5em; /* Abstand zwischen den Absätzen */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Icon-Text-Gruppe */
|
|
||||||
.icon-text-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-text-group ion-icon {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Geräte */
|
|
||||||
@media (max-width: 599px) {
|
|
||||||
.arrival-card {
|
|
||||||
height: auto; /* Höhe passt sich dem Inhalt an */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-card-title {
|
50.01%, 100% {
|
||||||
font-size: calc(var(--tile-height-vh) * 0.08);
|
opacity: 0;
|
||||||
}
|
|
||||||
|
|
||||||
ion-card-content p {
|
|
||||||
font-size: calc(var(--tile-height-vh) * 0.05);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tablets */
|
/* ======================================================
|
||||||
@media (min-width: 600px) and (max-width: 1199px) {
|
Bilder und Logos
|
||||||
ion-card-title {
|
====================================================== */
|
||||||
font-size: var(--tile-title-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card-content p {
|
|
||||||
font-size: var(--tile-font-size-date-time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Desktops */
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
ion-card-title {
|
|
||||||
font-size: var(--tile-title-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card-content p {
|
|
||||||
font-size: var(--tile-font-size-date-time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
max-height: calc(var(--tile-height-vh) * 0.40);
|
max-height: 10vh;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logobar {
|
.logobar {
|
||||||
max-height: 10vh;
|
max-height: 12vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.bottom-left-image-problem img {
|
||||||
|
position: absolute;
|
||||||
|
width: 11%;
|
||||||
|
bottom: 2%;
|
||||||
|
left: 2%;
|
||||||
|
aspect-ratio: 9 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left-image-passport img {
|
||||||
|
position: absolute;
|
||||||
|
width: 18%;
|
||||||
|
bottom: 2%;
|
||||||
|
left: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left-image-round img {
|
||||||
|
position: absolute;
|
||||||
|
width: 11%;
|
||||||
|
aspect-ratio: 10 / 9;
|
||||||
|
bottom: 2%;
|
||||||
|
left: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.problem {
|
||||||
|
width: 32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passport {
|
||||||
|
width: 42%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Benutzerdefinierte Segmente
|
||||||
|
====================================================== */
|
||||||
|
.custom-segment {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #ffffff;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-segment-button {
|
||||||
|
min-width: 50px;
|
||||||
|
height: 35px;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-bottom 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-segment-button.active {
|
||||||
|
border-bottom: 2px solid #003680;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-segment-button:hover {
|
||||||
|
border-bottom: 2px solid #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-segment-button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-segment-button.active .tab-label {
|
||||||
|
color: #003680;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-image {
|
||||||
|
width: 24px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================
|
||||||
|
Dynamische und Lade-Indikatoren
|
||||||
|
====================================================== */
|
||||||
|
|
||||||
|
.loading-indicator {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-progress-bar {
|
.custom-progress-bar {
|
||||||
--progress-background: #003680; /* Fortschrittsfarbe (gefüllter Teil) */
|
--progress-background: #003680;
|
||||||
--background: #e0e0e0; /* Hintergrundfarbe der Leiste */
|
|
||||||
--buffer-background: #003680; /* Hintergrundfarbe des Puffers */
|
|
||||||
}
|
|
||||||
.card-number {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
font-size: calc(var(--tile-height-vh) * 0.18);
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 2px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
/* Versteckt die Buttons und zeigt nur den Indikator-Strich an */
|
|
||||||
.custom-segment-button {
|
|
||||||
--color: transparent; /* Text unsichtbar machen */
|
|
||||||
--background: #003680; /* Hintergrund des Buttons transparent */
|
|
||||||
min-height: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
width: 200px;
|
|
||||||
--indicator-color: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styling für das ion-segment */
|
/* ======================================================
|
||||||
.custom-segment {
|
Flex Container
|
||||||
--background: transparent;
|
====================================================== */
|
||||||
height: 0.4vh;
|
.flex-container {
|
||||||
position: relative;
|
display: flex;
|
||||||
margin: 0;
|
flex-direction: column;
|
||||||
padding: 0;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
row-gap: var(--tile-row-gap);
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
// src/app/pages/aviso/aviso.page.ts
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
OnInit,
|
OnInit,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
HostListener,
|
HostListener,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
HostBinding
|
HostBinding,
|
||||||
|
ViewChildren,
|
||||||
|
QueryList,
|
||||||
|
ElementRef,
|
||||||
|
AfterViewInit,
|
||||||
|
Renderer2
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
import { interval, Subject, throwError, lastValueFrom, Subscription } from 'rxjs';
|
import { interval, Subject, throwError, lastValueFrom, Subscription } from 'rxjs';
|
||||||
import { switchMap, takeUntil, distinctUntilChanged, tap, catchError, map, take } from 'rxjs/operators';
|
import { switchMap, takeUntil, distinctUntilChanged, tap, catchError, map, take, debounceTime } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AvisoService, AvisoArrivalsResponse } from '../../services/aviso.service';
|
import { AvisoService, AvisoArrivalsResponse } from '../../services/aviso.service';
|
||||||
import { AvisoDto, TvSettings } from '../../services/aviso.dto';
|
import { AvisoDto, TvSettings } from '../../services/aviso.dto';
|
||||||
@@ -24,9 +27,6 @@ interface AvisoResolvedData {
|
|||||||
arrivals: AvisoArrivalsResponse;
|
arrivals: AvisoArrivalsResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AvisoPageData {
|
|
||||||
avisoData: AvisoResolvedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CssVariables {
|
interface CssVariables {
|
||||||
tileWidthPercent: string;
|
tileWidthPercent: string;
|
||||||
@@ -35,6 +35,7 @@ interface CssVariables {
|
|||||||
tileFontSizeDateTime: string;
|
tileFontSizeDateTime: string;
|
||||||
tileSeitenwechselInSek: string;
|
tileSeitenwechselInSek: string;
|
||||||
textBalkenHeightVh: string;
|
textBalkenHeightVh: string;
|
||||||
|
tileRowGapinPercent: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ARRIVALS_INTERVAL_MS = 10000;
|
const ARRIVALS_INTERVAL_MS = 10000;
|
||||||
@@ -52,6 +53,7 @@ interface StatusInfo {
|
|||||||
const STATUS_MAP: { [key: string]: StatusInfo } = {
|
const STATUS_MAP: { [key: string]: StatusInfo } = {
|
||||||
'3': { class: 'statusLKW_Ankunft', icon: 'time', color: 'medium', text: 'Ankunft' },
|
'3': { class: 'statusLKW_Ankunft', icon: 'time', color: 'medium', text: 'Ankunft' },
|
||||||
'lkwfertig': { class: 'lkwfertig', icon: 'time', color: 'warning', text: 'Ankunft' },
|
'lkwfertig': { class: 'lkwfertig', icon: 'time', color: 'warning', text: 'Ankunft' },
|
||||||
|
'fehlt': { class: 'fehlt', icon: 'time', color: 'danger', text: 'Dokument Fehlt' },
|
||||||
'default': { class: 'status-default', icon: 'help-circle', color: 'medium', text: 'Unbekannt' }
|
'default': { class: 'status-default', icon: 'help-circle', color: 'medium', text: 'Unbekannt' }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ interface SettingDisplayData {
|
|||||||
textAlign: string;
|
textAlign: string;
|
||||||
}[];
|
}[];
|
||||||
currentDivIndex: number;
|
currentDivIndex: number;
|
||||||
|
isDivReady: boolean; // Flag zur Steuerung der Sichtbarkeit
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -69,19 +72,22 @@ interface SettingDisplayData {
|
|||||||
templateUrl: './aviso.page.html',
|
templateUrl: './aviso.page.html',
|
||||||
styleUrls: ['./aviso.page.scss'],
|
styleUrls: ['./aviso.page.scss'],
|
||||||
})
|
})
|
||||||
export class AvisoPage implements OnInit, OnDestroy {
|
export class AvisoPage implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
// HostBindings für CSS-Variablen
|
// HostBindings für CSS-Variablen
|
||||||
@HostBinding('style.--tile-width-percent') tileWidthPercent = '30vw';
|
@HostBinding('style.--tile-width-percent') tileWidthPercent = '50vw';
|
||||||
@HostBinding('style.--tile-height-vh') tileHeightVh = '20vh';
|
@HostBinding('style.--tile-height-vh') tileHeightVh = '40vh';
|
||||||
@HostBinding('style.--tile-title-font-size') tileTitleFontSize = '2vh';
|
@HostBinding('style.--tile-title-font-size') tileTitleFontSize = '5vh';
|
||||||
@HostBinding('style.--tile-font-size-date-time') tileFontSizeDateTime = '1vh';
|
@HostBinding('style.--tile-font-size-date-time') tileFontSizeDateTime = '5vh';
|
||||||
@HostBinding('style.--text-balken-height-vh') textBalkenHeightVh = '5vh';
|
@HostBinding('style.--text-balken-height-vh') textBalkenHeightVh = '10vh';
|
||||||
|
@HostBinding('style.--tile-row-gap') tileRowGapinPercent = '10%';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
progressBarValue: number = 0;
|
progressBarValue: number = 0;
|
||||||
private progressBarSubscription: Subscription = new Subscription();
|
private progressBarSubscription: Subscription = new Subscription();
|
||||||
|
|
||||||
|
|
||||||
currentDate: Date = new Date();
|
currentDate: Date = new Date();
|
||||||
private dateSubscription: Subscription = new Subscription();
|
private dateSubscription: Subscription = new Subscription();
|
||||||
|
|
||||||
@@ -122,11 +128,20 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
// Neues Array für die Anzeige der Fixzeilen
|
// Neues Array für die Anzeige der Fixzeilen
|
||||||
settingDisplayData: SettingDisplayData[] = [];
|
settingDisplayData: SettingDisplayData[] = [];
|
||||||
|
|
||||||
|
// Hinzugefügte Variablen für die Kachel-Umschaltung
|
||||||
|
avisoTimeouts: { [key: string]: any } = {};
|
||||||
|
avisoStates: { [key: string]: boolean } = {};
|
||||||
|
|
||||||
|
// Dauer in Millisekunden
|
||||||
|
contentDuration = 3000; // Zeit in ms, die der normale Inhalt angezeigt wird
|
||||||
|
avisoHinweisDuration = 1500; // Zeit in ms, die der avisoTVHinweis Text angezeigt wird
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private avisoService: AvisoService,
|
private avisoService: AvisoService,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
private sanitizer: DomSanitizer
|
private sanitizer: DomSanitizer,
|
||||||
|
private renderer: Renderer2
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,6 +186,17 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
this.arrivals = data.arrivals.avisos;
|
this.arrivals = data.arrivals.avisos;
|
||||||
this.totalArrivals = data.arrivals.totalCount;
|
this.totalArrivals = data.arrivals.totalCount;
|
||||||
|
|
||||||
|
// Avisos für Testzwecke modifizieren
|
||||||
|
this.arrivals.forEach(aviso => {
|
||||||
|
if (aviso.lkW_Nr === '10AKN963') {
|
||||||
|
aviso.lkW_Nr = '10AKN96310AKN96310AKN96310AKN963';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.paginateArrivals();
|
||||||
|
this.updateAvisoStatesAndCycles();
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
// Process the HTML settings
|
// Process the HTML settings
|
||||||
this.preprocessHtmlSettings();
|
this.preprocessHtmlSettings();
|
||||||
|
|
||||||
@@ -183,6 +209,12 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
// Starten der Seitenrotation
|
// Starten der Seitenrotation
|
||||||
this.paginateArrivals();
|
this.paginateArrivals();
|
||||||
this.startPageRotation();
|
this.startPageRotation();
|
||||||
|
|
||||||
|
// Starten der Aviso-Kachel-Umschaltung
|
||||||
|
this.updateAvisoStatesAndCycles();
|
||||||
|
|
||||||
|
// Nach dem Laden der Daten die Schriftgröße anpassen (falls nicht mehr benötigt)
|
||||||
|
// Da die Direktive jetzt die Schriftgröße anpasst, kann dieser Code eventuell entfernt werden
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -202,8 +234,17 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
this.arrivals = data.avisos;
|
this.arrivals = data.avisos;
|
||||||
this.totalArrivals = data.totalCount;
|
this.totalArrivals = data.totalCount;
|
||||||
this.loadingArrivals = false;
|
this.loadingArrivals = false;
|
||||||
|
this.arrivals.forEach(aviso => {
|
||||||
|
// Beispiel: Setze avisoTVHinweis für ein bestimmtes Aviso
|
||||||
|
if (aviso.lkW_Nr === '10AKN963') {
|
||||||
|
aviso.lkW_Nr = '10AKN96310AKN96310AKN96310AKN963';
|
||||||
|
}
|
||||||
|
});
|
||||||
this.paginateArrivals();
|
this.paginateArrivals();
|
||||||
|
this.updateAvisoStatesAndCycles();
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.errorMessage = 'Fehler beim Laden der Arrivals. Bitte versuche es später erneut.';
|
this.errorMessage = 'Fehler beim Laden der Arrivals. Bitte versuche es später erneut.';
|
||||||
@@ -228,6 +269,30 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
// Die Schriftgrößenanpassung wird jetzt von der Direktive übernommen
|
||||||
|
// Falls zusätzliche Initialisierungen erforderlich sind, können sie hier durchgeführt werden
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
|
||||||
|
if (this.toggleDivInterval) {
|
||||||
|
clearInterval(this.toggleDivInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.progressBarSubscription) {
|
||||||
|
this.progressBarSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dateSubscription) {
|
||||||
|
this.dateSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bereinigen der Aviso-Timer
|
||||||
|
Object.values(this.avisoTimeouts).forEach(timeoutId => clearTimeout(timeoutId));
|
||||||
|
}
|
||||||
|
|
||||||
private startProgressBar(seitenwechselInSek: number): void {
|
private startProgressBar(seitenwechselInSek: number): void {
|
||||||
// Falls eine vorherige Subscription existiert, beenden wir sie
|
// Falls eine vorherige Subscription existiert, beenden wir sie
|
||||||
@@ -261,67 +326,65 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
return count + i + 1;
|
return count + i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private preprocessHtmlSettings() {
|
private preprocessHtmlSettings() {
|
||||||
this.settingDisplayData = this.avisoTvTextSettings.map((setting, index) => {
|
this.settingDisplayData = this.avisoTvTextSettings.map((setting, index) => ({
|
||||||
|
nonEmptyFixeZeilen: this.getNonEmptyFixeZeilen(setting),
|
||||||
|
currentDivIndex: 0,
|
||||||
|
isDivReady: true // Initialisieren auf true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNonEmptyFixeZeilen(setting: AvisoTvTextSettingsDto) {
|
||||||
const nonEmptyFixeZeilen = [];
|
const nonEmptyFixeZeilen = [];
|
||||||
|
|
||||||
// Verarbeiten von fixeZeile1
|
// Verarbeiten von fixeZeile1
|
||||||
if (setting.fixeZeile1) {
|
if (setting.fixeZeile1) {
|
||||||
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile1, index);
|
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile1, setting);
|
||||||
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verarbeiten von fixeZeile2
|
// Verarbeiten von fixeZeile2
|
||||||
if (setting.fixeZeile2) {
|
if (setting.fixeZeile2) {
|
||||||
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile2, index);
|
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile2, setting);
|
||||||
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verarbeiten von fixeZeile3
|
// Verarbeiten von fixeZeile3
|
||||||
if (setting.fixeZeile3) {
|
if (setting.fixeZeile3) {
|
||||||
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile3, index);
|
const { sanitizedHtml, textAlign } = this.processHtml(setting.fixeZeile3, setting);
|
||||||
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
nonEmptyFixeZeilen.push({ sanitizedHtml, textAlign });
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return nonEmptyFixeZeilen;
|
||||||
nonEmptyFixeZeilen,
|
|
||||||
currentDivIndex: 0
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private processHtml(html: string, index: number): { sanitizedHtml: SafeHtml; textAlign: string } {
|
private processHtml(html: string, setting: AvisoTvTextSettingsDto): { sanitizedHtml: SafeHtml; textAlign: string } {
|
||||||
// Vermeide das Modifizieren des HTML-Inhalts, um die Formatierung zu erhalten
|
// Vermeide das Modifizieren des HTML-Inhalts, um die Formatierung zu erhalten
|
||||||
const sanitizedHtml = this.sanitizer.bypassSecurityTrustHtml(html);
|
|
||||||
|
|
||||||
// Extrahiere text-align aus dem ersten Div
|
// Extrahiere text-align aus dem ersten Div
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(html, 'text/html');
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
const firstDiv = doc.querySelector('div');
|
const firstDiv = doc.querySelector('div');
|
||||||
let textAlign = 'left';
|
let textAlign = 'left';
|
||||||
|
const divs = doc.querySelectorAll('div');
|
||||||
|
divs.forEach(div => {
|
||||||
|
const fontSize = div.style.fontSize;
|
||||||
|
if (fontSize) {
|
||||||
|
const fontSizePx = this.convertToPx(fontSize);
|
||||||
|
textAlign = div.style.textAlign || 'left';
|
||||||
|
|
||||||
|
div.dataset['originalFontSizePx'] = fontSizePx.toString();
|
||||||
|
div.dataset['originalTextAlign'] = textAlign;
|
||||||
|
}
|
||||||
|
// Entferne die inline Schriftgröße und füge eine Klasse hinzu
|
||||||
|
div.style.fontSize = '';
|
||||||
|
div.classList.add('dynamic-font-size');
|
||||||
|
});
|
||||||
if (firstDiv) {
|
if (firstDiv) {
|
||||||
textAlign = firstDiv.style.textAlign || 'left';
|
textAlign = firstDiv.style.textAlign || 'left';
|
||||||
}
|
}
|
||||||
|
const sanitizedHtml = this.sanitizer.bypassSecurityTrustHtml(doc.body.innerHTML);
|
||||||
return { sanitizedHtml, textAlign };
|
return { sanitizedHtml, textAlign };
|
||||||
}
|
}
|
||||||
ngOnDestroy() {
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
|
|
||||||
if (this.toggleDivInterval) {
|
|
||||||
clearInterval(this.toggleDivInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.progressBarSubscription) {
|
|
||||||
this.progressBarSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dateSubscription) {
|
|
||||||
this.dateSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private setCSSVariables(): void {
|
private setCSSVariables(): void {
|
||||||
if (this.avisoTvSettings && this.avisoTvSettings.length > 0) {
|
if (this.avisoTvSettings && this.avisoTvSettings.length > 0) {
|
||||||
@@ -333,6 +396,7 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
tileFontSizeDateTime: `${settings.kachelFontSizeDateTime}vh`,
|
tileFontSizeDateTime: `${settings.kachelFontSizeDateTime}vh`,
|
||||||
tileSeitenwechselInSek: `${settings.seitenwechselInSek}s`,
|
tileSeitenwechselInSek: `${settings.seitenwechselInSek}s`,
|
||||||
textBalkenHeightVh: `${settings.textBalkenHeightInPercent}vh`,
|
textBalkenHeightVh: `${settings.textBalkenHeightInPercent}vh`,
|
||||||
|
tileRowGapinPercent: `${settings.kachelRowGapInPercent}%`,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.currentCssVariables && this.hasCssVariablesChanged(newCssVariables)) {
|
if (this.currentCssVariables && this.hasCssVariablesChanged(newCssVariables)) {
|
||||||
@@ -342,11 +406,14 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
Object.assign(this, newCssVariables);
|
Object.assign(this, newCssVariables);
|
||||||
this.currentCssVariables = newCssVariables;
|
this.currentCssVariables = newCssVariables;
|
||||||
|
|
||||||
|
console.log('CSS-Variablen gesetzt:', newCssVariables);
|
||||||
} else {
|
} else {
|
||||||
console.warn('avisoTvSettings ist nicht verfügbar oder leer');
|
console.warn('avisoTvSettings ist nicht verfügbar oder leer');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private hasCssVariablesChanged(newVars: CssVariables): boolean {
|
private hasCssVariablesChanged(newVars: CssVariables): boolean {
|
||||||
return Object.keys(newVars).some(key => (newVars as any)[key] !== (this.currentCssVariables as any)[key]);
|
return Object.keys(newVars).some(key => (newVars as any)[key] !== (this.currentCssVariables as any)[key]);
|
||||||
}
|
}
|
||||||
@@ -402,32 +469,52 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
if (shouldToggle) {
|
if (shouldToggle) {
|
||||||
this.toggleDivInterval = setInterval(() => {
|
this.toggleDivInterval = setInterval(() => {
|
||||||
this.settingDisplayData.forEach(data => {
|
this.settingDisplayData.forEach((data, index) => {
|
||||||
if (data.nonEmptyFixeZeilen.length > 1) {
|
if (data.nonEmptyFixeZeilen.length > 1) {
|
||||||
|
// Setze das Flag auf false, um das aktuelle div zu verstecken
|
||||||
|
this.settingDisplayData[index].isDivReady = false;
|
||||||
|
|
||||||
|
// Aktualisiere den currentDivIndex
|
||||||
data.currentDivIndex = (data.currentDivIndex + 1) % data.nonEmptyFixeZeilen.length;
|
data.currentDivIndex = (data.currentDivIndex + 1) % data.nonEmptyFixeZeilen.length;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
}, TOGGLE_DIV_INTERVAL_MS);
|
}, TOGGLE_DIV_INTERVAL_MS);
|
||||||
} else {
|
} else {
|
||||||
// Initialize currentDivIndex to 0
|
// Initialize currentDivIndex to 0 und setze isDivReady auf true
|
||||||
this.settingDisplayData.forEach(data => data.currentDivIndex = 0);
|
this.settingDisplayData.forEach((data, index) => {
|
||||||
|
data.currentDivIndex = 0;
|
||||||
|
data.isDivReady = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status-Methoden mithilfe des STATUS_MAP
|
// Status-Methoden mithilfe des STATUS_MAP
|
||||||
getStatusClass(status: number, lkwFertig: boolean): string {
|
getStatusClass(status: number, lkwFertig: boolean, avisoTvhinweis: string): string {
|
||||||
if (lkwFertig) {
|
if (lkwFertig && avisoTvhinweis == "") {
|
||||||
return STATUS_MAP['lkwfertig'].class;
|
return STATUS_MAP['lkwfertig'].class;
|
||||||
|
} else if (avisoTvhinweis != "") {
|
||||||
|
return STATUS_MAP['fehlt'].class;
|
||||||
|
} else if (avisoTvhinweis != "" && lkwFertig) {
|
||||||
|
return STATUS_MAP['fehlt'].class;
|
||||||
}
|
}
|
||||||
return STATUS_MAP[status.toString()]?.class || STATUS_MAP['default'].class;
|
return STATUS_MAP[status.toString()]?.class || STATUS_MAP['default'].class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getStatusIcon(status: number): string {
|
getStatusIcon(status: number): string {
|
||||||
return STATUS_MAP[status.toString()]?.icon || STATUS_MAP['default'].icon;
|
return STATUS_MAP[status.toString()]?.icon || STATUS_MAP['default'].icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTvHinweisImgSrc(avisotvhinweis: string): string {
|
||||||
|
if (avisotvhinweis.toLowerCase() == "passport") {
|
||||||
|
return "assets/warnings/passport.png";
|
||||||
|
}
|
||||||
|
else if (avisotvhinweis.toLowerCase() == "problem") {
|
||||||
|
return "assets/warnings/urgent.png"
|
||||||
|
}
|
||||||
|
else { return "assets/warnings/urgent.png" }
|
||||||
|
}
|
||||||
|
|
||||||
getStatusColor(status: number): string {
|
getStatusColor(status: number): string {
|
||||||
return STATUS_MAP[status.toString()]?.color || STATUS_MAP['default'].color;
|
return STATUS_MAP[status.toString()]?.color || STATUS_MAP['default'].color;
|
||||||
}
|
}
|
||||||
@@ -514,9 +601,10 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
if (this.currentPageIndex >= this.pages.length) {
|
if (this.currentPageIndex >= this.pages.length) {
|
||||||
this.currentPageIndex = 0;
|
this.currentPageIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Automatisches Wechseln der Seiten alle seitenwechselInSek Sekunden
|
// Automatisches Wechseln der Seiten alle seitenwechselInSek Sekunden
|
||||||
private startPageRotation(): void {
|
private startPageRotation(): void {
|
||||||
if (!this.avisoTvSettings || this.avisoTvSettings.length === 0) {
|
if (!this.avisoTvSettings || this.avisoTvSettings.length === 0) {
|
||||||
@@ -524,6 +612,12 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Überprüfen, ob mehr als eine Seite vorhanden ist
|
||||||
|
if (this.pages.length <= 1) {
|
||||||
|
console.info('Nur eine Seite vorhanden. Automatischer Seitenwechsel wird nicht gestartet.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const seitenwechselInSek = this.avisoTvSettings[0].seitenwechselInSek;
|
const seitenwechselInSek = this.avisoTvSettings[0].seitenwechselInSek;
|
||||||
console.log('Seitenwechselintervall (Sek):', seitenwechselInSek);
|
console.log('Seitenwechselintervall (Sek):', seitenwechselInSek);
|
||||||
|
|
||||||
@@ -537,15 +631,20 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
interval(intervalMs)
|
interval(intervalMs)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
if (this.pages.length > 0) {
|
if (this.pages.length > 1) {
|
||||||
this.currentPageIndex = (this.currentPageIndex + 1) % this.pages.length;
|
this.currentPageIndex = (this.currentPageIndex + 1) % this.pages.length;
|
||||||
console.log('Wechsel zu Seite:', this.currentPageIndex + 1);
|
console.log('Wechsel zu Seite:', this.currentPageIndex + 1);
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
// Fortschrittsbalken neu starten
|
// Fortschrittsbalken neu starten
|
||||||
this.startProgressBar(seitenwechselInSek);
|
this.startProgressBar(seitenwechselInSek);
|
||||||
|
|
||||||
|
// Nach dem Seitenwechsel die Schriftgröße anpassen (nicht mehr notwendig)
|
||||||
|
// setTimeout(() => {
|
||||||
|
// this.adjustFontSize();
|
||||||
|
// }, 0);
|
||||||
} else {
|
} else {
|
||||||
console.warn('Keine Seiten verfügbar zum Wechseln');
|
console.warn('Keine ausreichende Anzahl von Seiten zum Wechseln');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -558,7 +657,7 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
* Event Listener für Fenstergrößenänderungen, um die Paginierung anzupassen
|
* Event Listener für Fenstergrößenänderungen, um die Paginierung anzupassen
|
||||||
*/
|
*/
|
||||||
@HostListener('window:resize')
|
@HostListener('window:resize')
|
||||||
onResize(): void {
|
onResizeHostListener(): void {
|
||||||
this.paginateArrivals();
|
this.paginateArrivals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,27 +669,168 @@ export class AvisoPage implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private arrivalsAreEqual(a: AvisoDto[], b: AvisoDto[]): boolean {
|
private arrivalsAreEqual(a: AvisoDto[], b: AvisoDto[]): boolean {
|
||||||
if (a.length !== b.length) {
|
if (a.length !== b.length) {
|
||||||
|
console.log(`Arrays haben unterschiedliche Längen: a.length = ${a.length}, b.length = ${b.length}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < a.length; i++) {
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
const aItem = a[i];
|
||||||
|
const bItem = b[i];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
a[i].avisoID !== b[i].avisoID ||
|
aItem.avisoID !== bItem.avisoID ||
|
||||||
a[i].status !== b[i].status ||
|
aItem.status !== bItem.status ||
|
||||||
a[i].lkW_Nr !== b[i].lkW_Nr ||
|
aItem.lkW_Nr !== bItem.lkW_Nr ||
|
||||||
a[i].ankunft !== b[i].ankunft ||
|
aItem.ankunft !== bItem.ankunft ||
|
||||||
a[i].dauer !== b[i].dauer ||
|
aItem.dauer !== bItem.dauer ||
|
||||||
a[i].letzterMitarbeiter !== b[i].letzterMitarbeiter ||
|
aItem.letzterMitarbeiter !== bItem.letzterMitarbeiter ||
|
||||||
a[i].weiterleitungTextTV !== b[i].weiterleitungTextTV ||
|
aItem.weiterleitungTextTV !== bItem.weiterleitungTextTV ||
|
||||||
a[i].imEx !== b[i].imEx ||
|
aItem.imEx !== bItem.imEx ||
|
||||||
a[i].zollDigitalEingereicht !== b[i].zollDigitalEingereicht ||
|
aItem.zollDigitalEingereicht !== bItem.zollDigitalEingereicht ||
|
||||||
a[i].buero !== b[i].buero ||
|
aItem.buero !== bItem.buero ||
|
||||||
a[i].avisoTVHinweis !== b[i].avisoTVHinweis
|
aItem.avisoTVHinweis !== bItem.avisoTVHinweis
|
||||||
) {
|
) {
|
||||||
|
console.log(`Unterschied bei Index ${i}:`);
|
||||||
|
console.log(`a[${i}] =`, aItem);
|
||||||
|
console.log(`b[${i}] =`, bItem);
|
||||||
|
console.log(`Unterschiede:`);
|
||||||
|
if (aItem.avisoID !== bItem.avisoID) console.log(` avisoID: ${aItem.avisoID} !== ${bItem.avisoID}`);
|
||||||
|
if (aItem.status !== bItem.status) console.log(` status: ${aItem.status} !== ${bItem.status}`);
|
||||||
|
if (aItem.lkW_Nr !== bItem.lkW_Nr) console.log(` lkW_Nr: ${aItem.lkW_Nr} !== ${bItem.lkW_Nr}`);
|
||||||
|
if (aItem.ankunft !== bItem.ankunft) console.log(` ankunft: ${aItem.ankunft} !== ${bItem.ankunft}`);
|
||||||
|
if (aItem.dauer !== bItem.dauer) console.log(` dauer: ${aItem.dauer} !== ${bItem.dauer}`);
|
||||||
|
if (aItem.letzterMitarbeiter !== bItem.letzterMitarbeiter)
|
||||||
|
console.log(` letzterMitarbeiter: ${aItem.letzterMitarbeiter} !== ${bItem.letzterMitarbeiter}`);
|
||||||
|
if (aItem.weiterleitungTextTV !== bItem.weiterleitungTextTV)
|
||||||
|
console.log(` weiterleitungTextTV: ${aItem.weiterleitungTextTV} !== ${bItem.weiterleitungTextTV}`);
|
||||||
|
if (aItem.imEx !== bItem.imEx) console.log(` imEx: ${aItem.imEx} !== ${bItem.imEx}`);
|
||||||
|
if (aItem.zollDigitalEingereicht !== bItem.zollDigitalEingereicht)
|
||||||
|
console.log(` zollDigitalEingereicht: ${aItem.zollDigitalEingereicht} !== ${bItem.zollDigitalEingereicht}`);
|
||||||
|
if (aItem.buero !== bItem.buero) console.log(` buero: ${aItem.buero} !== ${bItem.buero}`);
|
||||||
|
if (aItem.avisoTVHinweis !== bItem.avisoTVHinweis)
|
||||||
|
console.log(` avisoTVHinweis: ${aItem.avisoTVHinweis} !== ${bItem.avisoTVHinweis}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Die Arrays sind gleich.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hinzugefügte Methode zum Verwalten der Aviso-Zyklen
|
||||||
|
private updateAvisoStatesAndCycles(): void {
|
||||||
|
const currentKeys = new Set<string>();
|
||||||
|
this.pages.forEach(page => {
|
||||||
|
page.forEach(aviso => {
|
||||||
|
const key = aviso.lkW_Nr; // Eindeutiger Schlüssel
|
||||||
|
currentKeys.add(key);
|
||||||
|
if (aviso.avisoTVHinweis && !(key in this.avisoStates)) {
|
||||||
|
this.avisoStates[key] = true; // Initialer Zustand
|
||||||
|
this.startAvisoCycle(key);
|
||||||
|
} else if (!aviso.avisoTVHinweis) {
|
||||||
|
// Falls kein Hinweis vorhanden ist, Zustand auf true setzen
|
||||||
|
this.avisoStates[key] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bereinigen von Avisos, die nicht mehr vorhanden sind
|
||||||
|
Object.keys(this.avisoStates).forEach(key => {
|
||||||
|
if (!currentKeys.has(key)) {
|
||||||
|
clearTimeout(this.avisoTimeouts[key]);
|
||||||
|
delete this.avisoStates[key];
|
||||||
|
delete this.avisoTimeouts[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private startAvisoCycle(key: string): void {
|
||||||
|
// Aviso mit diesem Schlüssel finden
|
||||||
|
const aviso = this.findAvisoByKey(key);
|
||||||
|
if (!aviso || !aviso.avisoTVHinweis) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.avisoStates[key]) {
|
||||||
|
// Aktuell wird der normale Inhalt angezeigt
|
||||||
|
this.avisoTimeouts[key] = setTimeout(() => {
|
||||||
|
this.avisoStates[key] = false; // Umschalten auf avisoTVHinweis Text
|
||||||
|
this.startAvisoCycle(key); // Nächsten Zyklus starten
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}, this.contentDuration);
|
||||||
|
} else {
|
||||||
|
// Aktuell wird der avisoTVHinweis Text angezeigt
|
||||||
|
this.avisoTimeouts[key] = setTimeout(() => {
|
||||||
|
this.avisoStates[key] = true; // Umschalten auf normalen Inhalt
|
||||||
|
this.startAvisoCycle(key); // Nächsten Zyklus starten
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}, this.avisoHinweisDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsmethode zum Finden eines Aviso anhand des Schlüssels
|
||||||
|
private findAvisoByKey(key: string): AvisoDto | undefined {
|
||||||
|
for (const page of this.pages) {
|
||||||
|
for (const aviso of page) {
|
||||||
|
if (aviso.lkW_Nr === key) {
|
||||||
|
return aviso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Methode zur Entscheidung, welcher Inhalt angezeigt wird
|
||||||
|
shouldShowNormalContent(aviso: AvisoDto): boolean {
|
||||||
|
if (!aviso.avisoTVHinweis) {
|
||||||
|
return true; // Kein Hinweis vorhanden, immer normalen Inhalt anzeigen
|
||||||
|
}
|
||||||
|
return this.avisoStates[aviso.lkW_Nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
getImageForTab(index: number): string {
|
||||||
|
const images = [
|
||||||
|
'assets/flags/DE.gif',
|
||||||
|
'assets/flags/EN.gif',
|
||||||
|
'assets/flags/TR.gif',
|
||||||
|
];
|
||||||
|
|
||||||
|
return images[index] || 'assets/imgs/DE.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event Handler für das `resizeComplete` Event der Directive.
|
||||||
|
* Setzt das `isDivReady` Flag auf `true`, um das Div anzuzeigen.
|
||||||
|
* @param index Der Index des SettingDisplayData
|
||||||
|
*/
|
||||||
|
onResizeComplete(index: number): void {
|
||||||
|
this.settingDisplayData[index].isDivReady = true;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getImageClass(hinweis: string): string {
|
||||||
|
const lowercaseHinweis = hinweis.toLowerCase();
|
||||||
|
|
||||||
|
if (lowercaseHinweis === 'problem') {
|
||||||
|
return 'problem';
|
||||||
|
} else if (lowercaseHinweis === 'passport') {
|
||||||
|
return 'passport';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getImageClassButtomLeft(hinweis: string): string {
|
||||||
|
const lowercaseHinweis = hinweis.toLowerCase();
|
||||||
|
|
||||||
|
if (lowercaseHinweis === 'problem') {
|
||||||
|
return 'bottom-left-image-problem';
|
||||||
|
} else if (lowercaseHinweis === 'passport') {
|
||||||
|
return 'bottom-left-image-passport';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// src/app/resolvers/aviso.resolver.ts
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable, forkJoin, of } from 'rxjs';
|
import { Observable, forkJoin, of } from 'rxjs';
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface TvSettings {
|
|||||||
kachelHeightInPercent: number;
|
kachelHeightInPercent: number;
|
||||||
kachelFontSizeLkwNummer: number;
|
kachelFontSizeLkwNummer: number;
|
||||||
kachelFontSizeDateTime: number;
|
kachelFontSizeDateTime: number;
|
||||||
|
kachelRowGapInPercent: number;
|
||||||
seitenwechselInSek: number;
|
seitenwechselInSek: number;
|
||||||
textBalkenHeightInPercent: number;
|
textBalkenHeightInPercent: number;
|
||||||
logo: string;
|
logo: string;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Observable, throwError } from 'rxjs';
|
|||||||
import { catchError, tap } from 'rxjs/operators';
|
import { catchError, tap } from 'rxjs/operators';
|
||||||
import { AvisoDto, TvSettings } from './aviso.dto'; // Importiere das Interface
|
import { AvisoDto, TvSettings } from './aviso.dto'; // Importiere das Interface
|
||||||
import { AvisoTvTextSettingsDto } from './aviso-tv-settings.dto'; // Importiere das neue Interface
|
import { AvisoTvTextSettingsDto } from './aviso-tv-settings.dto'; // Importiere das neue Interface
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
// Definiere die Response Interfaces
|
// Definiere die Response Interfaces
|
||||||
export interface AvisoArrivalsResponse {
|
export interface AvisoArrivalsResponse {
|
||||||
@@ -18,7 +19,7 @@ export interface AvisoArrivalsResponse {
|
|||||||
})
|
})
|
||||||
export class AvisoService {
|
export class AvisoService {
|
||||||
|
|
||||||
private baseUrl = 'https://localhost:7063/api/Aviso';
|
private baseUrl = environment.baseUrl;
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
|
|||||||
BIN
src/assets/flags/DE.gif
Normal file
BIN
src/assets/flags/DE.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/flags/EN.gif
Normal file
BIN
src/assets/flags/EN.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
BIN
src/assets/flags/TR.gif
Normal file
BIN
src/assets/flags/TR.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 255 KiB |
BIN
src/assets/warnings/okRound.png
Normal file
BIN
src/assets/warnings/okRound.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
src/assets/warnings/passport.png
Normal file
BIN
src/assets/warnings/passport.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
BIN
src/assets/warnings/urgent.png
Normal file
BIN
src/assets/warnings/urgent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -1,3 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true
|
production: true,
|
||||||
|
baseUrl: 'https://avisotv.server.app.verag.ag/api/Aviso'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
baseUrl: 'https://localhost:7063/api/Aviso'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For easier debugging in development mode, you can import the following file
|
* For easier debugging in development mode, you can import the following file
|
||||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||||
|
|||||||
Reference in New Issue
Block a user