import { Component, OnInit, signal, computed, ChangeDetectionStrategy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router, RouterLink } from '@angular/router'; import { forkJoin } from 'rxjs'; import { ApiService } from '../../services'; import { Category } from '../../models'; import { environment } from '../../../environments/environment'; import { ItemsCarouselComponent } from '../../components/items-carousel/items-carousel.component'; @Component({ selector: 'app-home', standalone: true, imports: [CommonModule, RouterLink, ItemsCarouselComponent], templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class HomeComponent implements OnInit { brandName = environment.brandFullName; isnovo = environment.theme === 'novo'; categories = signal([]); itemCounts = signal>(new Map()); loading = signal(true); error = signal(null); // Memoized computed values for performance topLevelCategories = computed(() => { return this.categories().filter(cat => cat.parentID === 0); }); // Cache subcategories by parent ID private subcategoriesCache = computed(() => { const cache = new Map(); this.categories().forEach(cat => { if (cat.parentID !== 0) { if (!cache.has(cat.parentID)) { cache.set(cat.parentID, []); } cache.get(cat.parentID)!.push(cat); } }); return cache; }); constructor(private apiService: ApiService, private router: Router) {} ngOnInit(): void { this.loadCategories(); } loadCategories(): void { this.loading.set(true); this.apiService.getCategories().subscribe({ next: (categories) => { this.categories.set(categories); this.loading.set(false); this.loadItemCounts(); }, error: (err) => { this.error.set('Failed to load categories'); this.loading.set(false); console.error('Error loading categories:', err); } }); } private loadItemCounts(): void { const topLevel = this.topLevelCategories(); if (topLevel.length === 0) return; const requests: Record> = {}; topLevel.forEach(cat => { requests[cat.categoryID.toString()] = this.apiService.getCategoryItems(cat.categoryID, 1000); }); forkJoin(requests).subscribe({ next: (results) => { const counts = new Map(); Object.entries(results).forEach(([id, items]) => { counts.set(Number(id), items.length); }); this.itemCounts.set(counts); }, error: (err) => console.error('Error loading item counts:', err) }); } getItemCount(categoryID: number): number { return this.itemCounts().get(categoryID) || 0; } getTopLevelCategories(): Category[] { return this.topLevelCategories(); } getSubCategories(parentID: number): Category[] { return this.subcategoriesCache().get(parentID) || []; } navigateToSearch(): void { this.router.navigate(['/search']); } scrollToCatalog(): void { const target = document.getElementById('catalog'); if (!target) return; const targetY = target.getBoundingClientRect().top + window.scrollY; const startY = window.scrollY; const distance = targetY - startY; const duration = 1200; let start: number | null = null; const easeInOutCubic = (t: number) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; const step = (timestamp: number) => { if (!start) start = timestamp; const elapsed = timestamp - start; const progress = Math.min(elapsed / duration, 1); window.scrollTo(0, startY + distance * easeInOutCubic(progress)); if (progress < 1) requestAnimationFrame(step); }; requestAnimationFrame(step); } }