bizmatch-project/bizmatch/src/app/components/search-modal/search-modal.component.ts

294 lines
11 KiB
TypeScript

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<GeoResult[]>;
counties$: Observable<CountyResult[]>;
// cityLoading = false;
countyLoading = false;
// cityInput$ = new Subject<string>();
countyInput$ = new Subject<string>();
private criteriaChangeSubscription: Subscription;
public criteria: BusinessListingCriteria;
private debounceTimeout: any;
public backupCriteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria = getCriteriaStateObject('businessListings');
numberOfResults$: Observable<number>;
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
(<BusinessListingCriteria>this.criteria).realEstateChecked = false;
(<BusinessListingCriteria>this.criteria).leasedLocation = false;
(<BusinessListingCriteria>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 ((<BusinessListingCriteria>this.criteria).realEstateChecked) this.selectedPropertyType = 'realEstateChecked';
else if ((<BusinessListingCriteria>this.criteria).leasedLocation) this.selectedPropertyType = 'leasedLocation';
else if ((<BusinessListingCriteria>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) {
(<BusinessListingCriteria>this.criteria).realEstateChecked = false;
(<BusinessListingCriteria>this.criteria).leasedLocation = false;
(<BusinessListingCriteria>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);
}
}