import { CommonModule, NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { UntilDestroy } from '@ngneat/until-destroy'; import { Subject, takeUntil } from 'rxjs'; import { BusinessListing, SortByOptions, User } from '../../../../../../bizmatch-server/src/models/db.model'; import { LISTINGS_PER_PAGE, ListingType, UserListingCriteria, emailToDirName, KeycloakUser } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { BreadcrumbItem, BreadcrumbsComponent } from '../../../components/breadcrumbs/breadcrumbs.component'; import { CustomerSubTypeComponent } from '../../../components/customer-sub-type/customer-sub-type.component'; import { PaginatorComponent } from '../../../components/paginator/paginator.component'; import { SearchModalBrokerComponent } from '../../../components/search-modal/search-modal-broker.component'; import { ModalService } from '../../../components/search-modal/modal.service'; import { AltTextService } from '../../../services/alt-text.service'; import { CriteriaChangeService } from '../../../services/criteria-change.service'; import { FilterStateService } from '../../../services/filter-state.service'; import { ImageService } from '../../../services/image.service'; import { ListingsService } from '../../../services/listings.service'; import { SearchService } from '../../../services/search.service'; import { SelectOptionsService } from '../../../services/select-options.service'; import { UserService } from '../../../services/user.service'; import { AuthService } from '../../../services/auth.service'; import { assignProperties, resetUserListingCriteria, map2User } from '../../../utils/utils'; @UntilDestroy() @Component({ selector: 'app-broker-listings', standalone: true, imports: [CommonModule, FormsModule, RouterModule, PaginatorComponent, CustomerSubTypeComponent, BreadcrumbsComponent, SearchModalBrokerComponent], templateUrl: './broker-listings.component.html', styleUrls: ['./broker-listings.component.scss', '../../pages.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class BrokerListingsComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); breadcrumbs: BreadcrumbItem[] = [ { label: 'Home', url: '/home', icon: 'fas fa-home' }, { label: 'Professionals', url: '/brokerListings' } ]; environment = environment; listings: Array; users: Array; filteredListings: Array; criteria: UserListingCriteria; realEstateChecked: boolean; maxPrice: string; minPrice: string; type: string; statesSet = new Set(); state: string; first: number = 0; rows: number = 12; totalRecords: number = 0; ts = new Date().getTime(); env = environment; public category: 'business' | 'commercialProperty' | 'professionals_brokers' | undefined; emailToDirName = emailToDirName; page = 1; pageCount = 1; sortBy: SortByOptions = null; // Neu: Separate Property currentUser: KeycloakUser | null = null; // Current logged-in user constructor( public altText: AltTextService, public selectOptions: SelectOptionsService, private listingsService: ListingsService, private userService: UserService, private activatedRoute: ActivatedRoute, private router: Router, private cdRef: ChangeDetectorRef, private imageService: ImageService, private route: ActivatedRoute, private searchService: SearchService, private modalService: ModalService, private criteriaChangeService: CriteriaChangeService, private filterStateService: FilterStateService, private authService: AuthService, ) { this.loadSortBy(); } private loadSortBy() { const storedSortBy = sessionStorage.getItem('professionalsSortBy'); this.sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; } async ngOnInit(): Promise { // Get current logged-in user const token = await this.authService.getToken(); this.currentUser = map2User(token); // Subscribe to FilterStateService for criteria changes this.filterStateService .getState$('brokerListings') .pipe(takeUntil(this.destroy$)) .subscribe(state => { this.criteria = state.criteria as UserListingCriteria; this.sortBy = state.sortBy; this.search(); }); // Subscribe to SearchService for search triggers this.searchService.searchTrigger$ .pipe(takeUntil(this.destroy$)) .subscribe(type => { if (type === 'brokerListings') { this.search(); } }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } async search() { const usersReponse = await this.userService.search(this.criteria); this.users = usersReponse.results; this.totalRecords = usersReponse.totalCount; this.pageCount = this.totalRecords % LISTINGS_PER_PAGE === 0 ? this.totalRecords / LISTINGS_PER_PAGE : Math.floor(this.totalRecords / LISTINGS_PER_PAGE) + 1; this.page = this.criteria.page ? this.criteria.page : 1; this.cdRef.markForCheck(); this.cdRef.detectChanges(); } onPageChange(page: any) { this.criteria.start = (page - 1) * LISTINGS_PER_PAGE; this.criteria.length = LISTINGS_PER_PAGE; this.criteria.page = page; this.search(); } reset() { } // New methods for filter actions clearAllFilters() { // Reset criteria to default values resetUserListingCriteria(this.criteria); // Reset pagination this.criteria.page = 1; this.criteria.start = 0; this.criteriaChangeService.notifyCriteriaChange(); // Search with cleared filters this.searchService.search('brokerListings'); } async openFilterModal() { // Open the search modal with current criteria const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { this.searchService.search('brokerListings'); } else { this.criteria = assignProperties(this.criteria, modalResult.criteria); } } /** * Check if professional/user is already in current user's favorites */ isFavorite(professional: User): boolean { if (!this.currentUser?.email || !professional.favoritesForUser) return false; return professional.favoritesForUser.includes(this.currentUser.email); } /** * Toggle favorite status for a professional */ async toggleFavorite(event: Event, professional: User): Promise { event.stopPropagation(); event.preventDefault(); if (!this.currentUser?.email) { // User not logged in - redirect to login this.router.navigate(['/login']); return; } try { console.log('Toggling favorite for:', professional.email, 'Current user:', this.currentUser.email); console.log('Before update, favorites:', professional.favoritesForUser); if (this.isFavorite(professional)) { // Remove from favorites await this.listingsService.removeFavorite(professional.id, 'user'); professional.favoritesForUser = professional.favoritesForUser.filter( email => email !== this.currentUser!.email ); } else { // Add to favorites await this.listingsService.addToFavorites(professional.id, 'user'); if (!professional.favoritesForUser) { professional.favoritesForUser = []; } // Use spread to create new array reference professional.favoritesForUser = [...professional.favoritesForUser, this.currentUser.email]; } console.log('After update, favorites:', professional.favoritesForUser); this.cdRef.markForCheck(); this.cdRef.detectChanges(); } catch (error) { console.error('Error toggling favorite:', error); } } /** * Share professional profile */ async shareProfessional(event: Event, user: User): Promise { event.stopPropagation(); event.preventDefault(); const url = `${window.location.origin}/details-user/${user.id}`; const title = `${user.firstname} ${user.lastname} - ${user.companyName}`; // Try native share API first (works on mobile and some desktop browsers) if (navigator.share) { try { await navigator.share({ title: title, text: `Check out this professional: ${title}`, url: url, }); } catch (err) { // User cancelled or share failed - fall back to clipboard this.copyToClipboard(url); } } else { // Fallback: open Facebook share dialog const encodedUrl = encodeURIComponent(url); window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`, '_blank', 'width=600,height=400'); } } /** * Copy URL to clipboard and show feedback */ private copyToClipboard(url: string): void { navigator.clipboard.writeText(url).then(() => { console.log('Link copied to clipboard!'); }).catch(err => { console.error('Failed to copy link:', err); }); } }