import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { NgSelectModule } from '@ng-select/ng-select'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { KeycloakService } from 'keycloak-angular'; import onChange from 'on-change'; import { catchError, concat, debounceTime, distinctUntilChanged, Observable, of, Subject, Subscription, switchMap, tap } from 'rxjs'; import { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingCriteria, GeoResult, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; import { ModalService } from '../../components/search-modal/modal.service'; import { CriteriaChangeService } from '../../services/criteria-change.service'; import { GeoService } from '../../services/geo.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 { compareObjects, createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, getCriteriaStateObject, map2User } from '../../utils/utils'; @UntilDestroy() @Component({ selector: 'app-home', standalone: true, imports: [CommonModule, FormsModule, RouterModule, NgSelectModule], templateUrl: './home.component.html', styleUrl: './home.component.scss', }) export class HomeComponent { activeTabAction: 'business' | 'commercialProperty' | 'broker' = 'business'; type: string; maxPrice: string; minPrice: string; criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria; states = []; isMenuOpen = false; user: KeycloakUser; prompt: string; cities$: Observable; cityLoading = false; cityInput$ = new Subject(); cityOrState = undefined; private criteriaChangeSubscription: Subscription; numberOfResults$: Observable; public constructor( private router: Router, private modalService: ModalService, private searchService: SearchService, private activatedRoute: ActivatedRoute, public selectOptions: SelectOptionsService, public keycloakService: KeycloakService, private criteriaChangeService: CriteriaChangeService, private geoService: GeoService, public cdRef: ChangeDetectorRef, private listingService: ListingsService, private userService: UserService, ) {} async ngOnInit() { const token = await this.keycloakService.getToken(); sessionStorage.removeItem('business_criteria'); sessionStorage.removeItem('commercialProperty_criteria'); sessionStorage.removeItem('broker_criteria'); this.criteria = this.createEnhancedProxy(getCriteriaStateObject('business')); this.user = map2User(token); this.loadCities(); this.setupCriteriaChangeListener(); } async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') { this.activeTabAction = tabname; if ('business' === tabname) { this.criteria = this.createEnhancedProxy(getCriteriaStateObject('business')); } else if ('commercialProperty' === tabname) { this.criteria = this.createEnhancedProxy(getCriteriaStateObject('commercialProperty')); } else if ('broker' === tabname) { this.criteria = this.createEnhancedProxy(getCriteriaStateObject('broker')); } else { this.criteria = undefined; } } private createEnhancedProxy(obj: any) { const component = this; const sessionStorageHandler = function (path, value, previous, applyData) { let criteriaType = this.criteriaType; sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this)); }; return onChange(obj, function (path, value, previous, applyData) { // Call the original sessionStorageHandler sessionStorageHandler.call(this, path, value, previous, applyData); // Notify about the criteria change using the component's context component.criteriaChangeService.notifyCriteriaChange(); }); } search() { this.router.navigate([`${this.activeTabAction}Listings`]); } private setupCriteriaChangeListener() { this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(untilDestroyed(this), debounceTime(400)).subscribe(() => this.setTotalNumberOfResults()); } login() { this.keycloakService.login({ redirectUri: window.location.href, }); } register() { this.keycloakService.register({ redirectUri: `${window.location.origin}/account` }); } toggleMenu() { this.isMenuOpen = !this.isMenuOpen; } onTypesChange(value) { if (value === '') { // Wenn keine Option ausgewählt ist, setzen Sie types zurück auf ein leeres Array this.criteria.types = []; } else { this.criteria.types = [value]; } } onRadiusChange(value) { if (value === 'null') { // Wenn keine Option ausgewählt ist, setzen Sie types zurück auf ein leeres Array this.criteria.radius = null; } else { this.criteria.radius = parseInt(value); } } async openModal() { const accepted = await this.modalService.showModal(this.criteria); if (accepted) { //this.searchService.search(this.criteria); this.router.navigate([`${this.activeTabAction}Listings`]); } } private loadCities() { this.cities$ = concat( of([]), // default items this.cityInput$.pipe( distinctUntilChanged(), tap(() => (this.cityLoading = true)), switchMap(term => //this.geoService.findCitiesStartingWith(term).pipe( this.geoService.findCitiesAndStatesStartingWith(term).pipe( catchError(() => of([])), // empty list on error // map(cities => cities.map(city => city.city)), // transform the list of objects to a list of city names tap(() => (this.cityLoading = false)), ), ), ), ); } trackByFn(item: GeoResult) { return item.id; } setCityOrState(cityOrState: CityAndStateResult) { if (cityOrState) { if (cityOrState.type === 'state') { this.criteria.state = cityOrState.state_code; } else { this.criteria.city = cityOrState.name; this.criteria.state = cityOrState.state_code; this.criteria.searchType = 'radius'; this.criteria.radius = 20; } } else { this.criteria.state = null; this.criteria.city = null; this.criteria.radius = null; this.criteria.searchType = 'exact'; } } getTypes() { if (this.criteria.criteriaType === 'business') { return this.selectOptions.typesOfBusiness; } else if (this.criteria.criteriaType === 'commercialProperty') { return this.selectOptions.typesOfCommercialProperty; } else { return this.selectOptions.customerSubTypes; } } getPlaceholderLabel() { if (this.criteria.criteriaType === 'business') { return 'Business Type'; } else if (this.criteria.criteriaType === 'commercialProperty') { return 'Property Type'; } else { return 'Professional Type'; } } setTotalNumberOfResults() { if (this.criteria) { console.log(`Getting total number of results for ${this.criteria.criteriaType}`); if (this.criteria.criteriaType === 'business' || this.criteria.criteriaType === 'commercialProperty') { this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, this.criteria.criteriaType); } else if (this.criteria.criteriaType === 'broker') { this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria); } else { this.numberOfResults$ = of(); } } } getNumberOfFiltersSet() { if (this.criteria?.criteriaType === 'broker') { return compareObjects(createEmptyUserListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']); } else if (this.criteria?.criteriaType === 'business') { return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']); } else if (this.criteria?.criteriaType === 'commercialProperty') { return compareObjects(createEmptyCommercialPropertyListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']); } else { return 0; } } }