import { BreakpointObserver } from '@angular/cdk/layout'; import { CommonModule } from '@angular/common'; import { Component, HostListener } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { faUserGear } from '@fortawesome/free-solid-svg-icons'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { debounceTime, filter, Observable, Subject, Subscription } from 'rxjs'; import { SortByOptions, User } from '../../../../../bizmatch-server/src/models/db.model'; import { BusinessListingCriteria, emailToDirName, KeycloakUser, KeyValueAsSortBy } from '../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../environments/environment'; import { SharedService } from '../../services/shared.service'; import { Collapse, Dropdown } from 'flowbite'; import { AuthService } from '../../services/auth.service'; import { CriteriaChangeService } from '../../services/criteria-change.service'; import { ListingsService } from '../../services/listings.service'; import { ModalService } from '../../services/modal.service'; import { SearchService } from '../../services/search.service'; import { SelectOptionsService } from '../../services/select-options.service'; import { UserService } from '../../services/user.service'; import { assignProperties, compareObjects, createEmptyBusinessListingCriteria, getCriteriaProxy, map2User } from '../../utils/utils'; @UntilDestroy() @Component({ selector: 'header', imports: [CommonModule, RouterModule, FormsModule], templateUrl: './header.component.html', styleUrl: './header.component.scss', }) export class HeaderComponent { public buildVersion = environment.buildVersion; user$: Observable; keycloakUser: KeycloakUser; user: User; activeItem; faUserGear = faUserGear; profileUrl: string; env = environment; private filterDropdown: Dropdown | null = null; isMobile: boolean = false; private destroy$ = new Subject(); prompt: string; private subscription: Subscription; criteria: BusinessListingCriteria; private routerSubscription: Subscription | undefined; baseRoute: string; sortDropdownVisible: boolean; sortByOptions: KeyValueAsSortBy[] = []; numberOfBroker$: Observable; numberOfCommercial$: Observable; userMenuVisible: boolean = false; constructor( private router: Router, private userService: UserService, private sharedService: SharedService, private breakpointObserver: BreakpointObserver, private modalService: ModalService, private searchService: SearchService, private criteriaChangeService: CriteriaChangeService, public selectOptions: SelectOptionsService, public authService: AuthService, private listingService: ListingsService, ) {} @HostListener('document:click', ['$event']) handleGlobalClick(event: Event) { const target = event.target as HTMLElement; if (target.id !== 'sortDropdownButton' && target.id !== 'sortDropdownMobileButton') { this.sortDropdownVisible = false; } } @HostListener('document:click', ['$event']) handleOutsideClick(event: Event) { const target = event.target as HTMLElement; // Schließe das User-Menü, wenn außerhalb geklickt wird if (this.userMenuVisible && !target.closest('#user-menu-button') && !target.closest('#user-menu-dropdown')) { this.userMenuVisible = false; } } async ngOnInit() { this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { } }); const token = await this.authService.getToken(); this.keycloakUser = map2User(token); if (this.keycloakUser) { this.user = await this.userService.getByMail(this.keycloakUser?.email); this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`; } this.sharedService.currentProfilePhoto.subscribe(photoUrl => { this.profileUrl = photoUrl; }); this.checkCurrentRoute(this.router.url); this.setupSortByOptions(); this.routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: any) => { this.checkCurrentRoute(event.urlAfterRedirects); this.setupSortByOptions(); }); this.subscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => { this.criteria = getCriteriaProxy(this.baseRoute, this); }); this.userService.currentUser.pipe(untilDestroyed(this)).subscribe(u => { this.user = u; }); } private checkCurrentRoute(url: string): void { this.baseRoute = url.split('/')[1]; // Nimmt den ersten Teil der Route nach dem ersten '/' const specialRoutes = [, '', '']; this.criteria = getCriteriaProxy(this.baseRoute, this); // this.searchService.search(this.criteria); } setupSortByOptions() { this.sortByOptions = []; if (this.isProfessionalListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'professional')]; } if (this.isBusinessListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'business' || s.type === 'listing')]; } if (this.isCommercialPropertyListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'commercial' || s.type === 'listing')]; } this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => !s.type)]; } ngAfterViewInit() {} async openModal() { const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { this.searchService.search(this.criteria); } else { this.criteria = assignProperties(this.criteria, modalResult.criteria); } } navigateWithState(dest: string, state: any) { this.router.navigate([dest], { state: state }); } isActive(route: string): boolean { return this.router.url === route; } isEmailUsUrl(): boolean { return ['/emailUs'].includes(this.router.url); } isFilterUrl(): boolean { return ['/businessListings', '/commercialPropertyListings', '/brokerListings'].includes(this.router.url); } isBusinessListing(): boolean { return ['/businessListings'].includes(this.router.url); } isCommercialPropertyListing(): boolean { return ['/commercialPropertyListings'].includes(this.router.url); } isProfessionalListing(): boolean { return ['/brokerListings'].includes(this.router.url); } // isSortingUrl(): boolean { // return ['/businessListings', '/commercialPropertyListings'].includes(this.router.url); // } closeDropdown() { const dropdownButton = document.getElementById('user-menu-button'); const dropdownMenu = this.user ? document.getElementById('user-login') : document.getElementById('user-unknown'); if (dropdownButton && dropdownMenu) { const dropdown = new Dropdown(dropdownMenu, dropdownButton); dropdown.hide(); } } closeMobileMenu() { const targetElement = document.getElementById('navbar-user'); const triggerElement = document.querySelector('[data-collapse-toggle="navbar-user"]'); if (targetElement instanceof HTMLElement && triggerElement instanceof HTMLElement) { const collapse = new Collapse(targetElement, triggerElement); collapse.collapse(); } } closeMenusAndSetCriteria(path: string) { this.closeDropdown(); this.closeMobileMenu(); const criteria = getCriteriaProxy(path, this); criteria.page = 1; criteria.start = 0; } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } getNumberOfFiltersSet() { if (this.criteria?.criteriaType === 'businessListings') { return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius', 'sortBy']); } else { return 0; } } sortBy(sortBy: SortByOptions) { this.criteria.sortBy = sortBy; this.sortDropdownVisible = false; this.searchService.search(this.criteria); } toggleSortDropdown() { this.sortDropdownVisible = !this.sortDropdownVisible; } toggleUserMenu() { this.userMenuVisible = !this.userMenuVisible; } get isProfessional() { return this.user?.customerType === 'professional'; } }