git init
This commit is contained in:
140
client/src/app/dashboard/dashboard.component.ts
Normal file
140
client/src/app/dashboard/dashboard.component.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
computed,
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { AnalyticsDashboard } from './dashboard.types';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
standalone: true,
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrl: './dashboard.component.scss',
|
||||
})
|
||||
export class DashboardComponent implements OnChanges {
|
||||
@Input({ required: true }) apiBaseUrl = '';
|
||||
@Input() connected = false;
|
||||
@Input() refreshKey = 0;
|
||||
|
||||
private readonly dashboardService = inject(DashboardService);
|
||||
protected readonly dashboard = signal<AnalyticsDashboard | null>(null);
|
||||
protected readonly dashboardLoading = signal(false);
|
||||
protected readonly dashboardError = signal<string | null>(null);
|
||||
protected readonly selectedSportType = signal<string | null>(null);
|
||||
protected readonly maxWeeklyDistance = computed(() =>
|
||||
Math.max(
|
||||
1,
|
||||
...(this.dashboard()?.weekly.map((week) => week.distanceMeters) ?? [0]),
|
||||
),
|
||||
);
|
||||
protected readonly maxSportDistance = computed(() =>
|
||||
Math.max(
|
||||
1,
|
||||
...(this.dashboard()?.sports.map((sport) => sport.distanceMeters) ?? [0]),
|
||||
),
|
||||
);
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (
|
||||
this.connected &&
|
||||
(changes['connected'] || changes['refreshKey'] || changes['apiBaseUrl'])
|
||||
) {
|
||||
this.loadDashboard();
|
||||
}
|
||||
}
|
||||
|
||||
protected loadDashboard(): void {
|
||||
if (!this.connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dashboardLoading.set(true);
|
||||
this.dashboardError.set(null);
|
||||
|
||||
this.dashboardService
|
||||
.getDashboard(this.apiBaseUrl, 12, this.selectedSportType())
|
||||
.subscribe({
|
||||
next: (dashboard) => {
|
||||
this.dashboard.set(dashboard);
|
||||
this.dashboardLoading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.dashboardError.set('Dashboard konnte nicht geladen werden.');
|
||||
this.dashboardLoading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected selectSportType(value: string): void {
|
||||
this.selectedSportType.set(value === 'all' ? null : value);
|
||||
this.loadDashboard();
|
||||
}
|
||||
|
||||
protected distanceKm(meters: number | null | undefined): string {
|
||||
return `${((meters ?? 0) / 1000).toLocaleString('de-DE', {
|
||||
maximumFractionDigits: 1,
|
||||
})} km`;
|
||||
}
|
||||
|
||||
protected duration(seconds: number | null | undefined): string {
|
||||
const totalSeconds = seconds ?? 0;
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.round((totalSeconds % 3600) / 60);
|
||||
|
||||
return hours > 0 ? `${hours} h ${minutes} min` : `${minutes} min`;
|
||||
}
|
||||
|
||||
protected elevation(meters: number | null | undefined): string {
|
||||
return `${Math.round(meters ?? 0).toLocaleString('de-DE')} m`;
|
||||
}
|
||||
|
||||
protected pace(secondsPerKm: number | null): string {
|
||||
if (!secondsPerKm) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const minutes = Math.floor(secondsPerKm / 60);
|
||||
const seconds = Math.round(secondsPerKm % 60)
|
||||
.toString()
|
||||
.padStart(2, '0');
|
||||
return `${minutes}:${seconds} /km`;
|
||||
}
|
||||
|
||||
protected speed(metersPerSecond: number | null): string {
|
||||
if (!metersPerSecond) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return `${(metersPerSecond * 3.6).toLocaleString('de-DE', {
|
||||
maximumFractionDigits: 1,
|
||||
})} km/h`;
|
||||
}
|
||||
|
||||
protected number(value: number | null | undefined, suffix = ''): string {
|
||||
if (value === null || value === undefined) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return `${Math.round(value).toLocaleString('de-DE')}${suffix}`;
|
||||
}
|
||||
|
||||
protected percent(value: number, max: number): number {
|
||||
return Math.max(3, Math.round((value / max) * 100));
|
||||
}
|
||||
|
||||
protected shortDate(value: string | null): string {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
}).format(new Date(value));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user