import { Component, OnInit, computed, inject, signal, } from '@angular/core'; import { resolveApiBaseUrl } from '../shared/api-base-url'; 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 OnInit { private readonly dashboardService = inject(DashboardService); private readonly apiBaseUrl = resolveApiBaseUrl(); protected readonly dashboard = signal(null); protected readonly dashboardLoading = signal(false); protected readonly dashboardError = signal(null); protected readonly selectedSportType = signal(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]), ), ); ngOnInit(): void { this.loadDashboard(); } protected loadDashboard(): void { 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)); } }