import { AsyncPipe, NgIf } from '@angular/common'; import { Component, Input, Output } from '@angular/core'; import { NgSelectModule } from '@ng-select/ng-select'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { catchError, concat, debounceTime, distinctUntilChanged, map, Observable, of, Subject, Subscription, switchMap, tap } from 'rxjs'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, CountyResult, GeoResult, KeyValue, KeyValueStyle, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; 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 { SharedModule } from '../../shared/shared/shared.module'; import { getCriteriaStateObject, resetBusinessListingCriteria } from '../../utils/utils'; import { ValidatedCityComponent } from '../validated-city/validated-city.component'; import { ValidatedPriceComponent } from '../validated-price/validated-price.component'; import { ModalService } from './modal.service'; @UntilDestroy() @Component({ selector: 'app-search-modal', standalone: true, imports: [SharedModule, AsyncPipe, NgIf, NgSelectModule, ValidatedCityComponent, ValidatedPriceComponent], templateUrl: './search-modal.component.html', styleUrl: './search-modal.component.scss', }) export class SearchModalComponent { @Output() @Input() isModal: boolean = true; // cities$: Observable; counties$: Observable; // cityLoading = false; countyLoading = false; // cityInput$ = new Subject(); countyInput$ = new Subject(); private criteriaChangeSubscription: Subscription; public criteria: BusinessListingCriteria; private debounceTimeout: any; public backupCriteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria = getCriteriaStateObject('businessListings'); numberOfResults$: Observable; cancelDisable = false; constructor( public selectOptions: SelectOptionsService, public modalService: ModalService, private geoService: GeoService, private criteriaChangeService: CriteriaChangeService, private listingService: ListingsService, private userService: UserService, private searchService: SearchService, ) {} // Define property type options public propertyTypeOptions = [ { name: 'Real Estate', value: 'realEstateChecked' }, { name: 'Leased Location', value: 'leasedLocation' }, { name: 'Franchise', value: 'franchiseResale' }, ]; selectedPropertyType: string | null = null; selectedPropertyTypeName: string | null = null; ngOnInit() { this.setupCriteriaChangeListener(); this.modalService.message$.pipe(untilDestroyed(this)).subscribe(msg => { this.criteria = msg as BusinessListingCriteria; this.backupCriteria = JSON.parse(JSON.stringify(msg)); this.setTotalNumberOfResults(); }); this.modalService.modalVisible$.pipe(untilDestroyed(this)).subscribe(val => { if (val.visible) { this.criteria.page = 1; this.criteria.start = 0; } }); // this.loadCities(); this.loadCounties(); this.updateSelectedPropertyType(); this.modalService.sendCriteria(this.criteria); } hasActiveFilters(): boolean { return !!( this.criteria.state || this.criteria.city || this.criteria.minPrice || this.criteria.maxPrice || this.criteria.minRevenue || this.criteria.maxRevenue || this.criteria.minCashFlow || this.criteria.maxCashFlow || this.criteria.types.length || this.selectedPropertyType || this.criteria.minNumberEmployees || this.criteria.maxNumberEmployees || this.criteria.establishedMin || this.criteria.brokerName || this.criteria.title ); } removeFilter(filterType: string) { switch (filterType) { case 'state': this.criteria.state = null; this.setCity(null); break; case 'city': this.criteria.city = null; this.criteria.radius = null; this.criteria.searchType = 'exact'; break; case 'price': this.criteria.minPrice = null; this.criteria.maxPrice = null; break; case 'revenue': this.criteria.minRevenue = null; this.criteria.maxRevenue = null; break; case 'cashflow': this.criteria.minCashFlow = null; this.criteria.maxCashFlow = null; break; case 'types': this.criteria.types = []; break; case 'propertyType': this.criteria.realEstateChecked = false; this.criteria.leasedLocation = false; this.criteria.franchiseResale = false; this.selectedPropertyType = null; break; case 'employees': this.criteria.minNumberEmployees = null; this.criteria.maxNumberEmployees = null; break; case 'established': this.criteria.establishedMin = null; break; case 'brokerName': this.criteria.brokerName = null; break; case 'title': this.criteria.title = null; break; } this.searchService.search(this.criteria.criteriaType); } // Handle category change onCategoryChange(selectedCategories: string[]) { this.criteria.types = selectedCategories; this.searchService.search(this.criteria.criteriaType); } // Handle property type change onPropertyTypeChange(value: string) { // Reset all property type flags (this.criteria).realEstateChecked = false; (this.criteria).leasedLocation = false; (this.criteria).franchiseResale = false; // Set the selected property type if (value) { this.criteria[value] = true; } this.selectedPropertyType = value; this.searchService.search(this.criteria.criteriaType); } // Update selected property type based on current criteria updateSelectedPropertyType() { if ((this.criteria).realEstateChecked) this.selectedPropertyType = 'realEstateChecked'; else if ((this.criteria).leasedLocation) this.selectedPropertyType = 'leasedLocation'; else if ((this.criteria).franchiseResale) this.selectedPropertyType = 'franchiseResale'; else this.selectedPropertyType = null; } getSelectedPropertyTypeName() { return this.selectedPropertyType ? this.propertyTypeOptions.find(opt => opt.value === this.selectedPropertyType)?.name : null; } categoryClicked(checked: boolean, value: string) { if (checked) { this.criteria.types.push(value); } else { const index = this.criteria.types.findIndex(t => t === value); if (index > -1) { this.criteria.types.splice(index, 1); } } this.searchService.search(this.criteria.criteriaType); } private loadCounties() { this.counties$ = concat( of([]), // default items this.countyInput$.pipe( distinctUntilChanged(), tap(() => (this.countyLoading = true)), switchMap(term => this.geoService.findCountiesStartingWith(term).pipe( catchError(() => of([])), // empty list on error map(counties => counties.map(county => county.name)), // transform the list of objects to a list of city names tap(() => (this.countyLoading = false)), ), ), ), ); } onCriteriaChange() { this.searchService.search(this.criteria.criteriaType); } setCity(city) { if (city) { this.criteria.city = city; this.criteria.state = city.state; } else { this.criteria.city = null; this.criteria.radius = null; this.criteria.searchType = 'exact'; } this.searchService.search(this.criteria.criteriaType); } setState(state: string) { if (state) { this.criteria.state = state; } else { this.criteria.state = null; this.setCity(null); } this.searchService.search(this.criteria.criteriaType); } setRadius(radius: number) { this.criteria.radius = radius; this.searchService.search(this.criteria.criteriaType); } private setupCriteriaChangeListener() { this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => { this.setTotalNumberOfResults(); this.cancelDisable = true; }); } trackByFn(item: GeoResult) { return item.id; } search() { console.log('Search criteria:', this.criteria); } getCounties() { this.geoService.findCountiesStartingWith(''); } closeModal() { console.log('Closing modal'); } closeAndSearch() { this.modalService.accept(); this.searchService.search(this.criteria.criteriaType); this.close(); } isTypeOfBusinessClicked(v: KeyValueStyle) { return this.criteria.types.find(t => t === v.value); } isTypeOfProfessionalClicked(v: KeyValue) { return this.criteria.types.find(t => t === v.value); } setTotalNumberOfResults() { if (this.criteria) { console.log(`Getting total number of results for ${this.criteria.criteriaType}`); if (this.criteria.criteriaType === 'businessListings' || this.criteria.criteriaType === 'commercialPropertyListings') { this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty'); } else if (this.criteria.criteriaType === 'brokerListings') { //this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria); } else { this.numberOfResults$ = of(); } } } clearFilter() { resetBusinessListingCriteria(this.criteria); this.searchService.search(this.criteria.criteriaType); } close() { this.modalService.reject(this.backupCriteria); } onCheckboxChange(checkbox: string, value: boolean) { (this.criteria).realEstateChecked = false; (this.criteria).leasedLocation = false; (this.criteria).franchiseResale = false; // Aktivieren Sie nur die aktuell ausgewählte Checkbox this.criteria[checkbox] = value; this.searchService.search(this.criteria.criteriaType); } debouncedSearch() { clearTimeout(this.debounceTimeout); this.debounceTimeout = setTimeout(() => { this.searchService.search(this.criteria.criteriaType); }, 1000); } }