#146,147view Filter + dropdowns ...

This commit is contained in:
Andreas Knuth 2025-07-21 17:29:05 -05:00
parent 24db8927e8
commit 01b5679e54
3 changed files with 162 additions and 74 deletions

View File

@ -216,8 +216,8 @@
</div>
</div>
</div>
} @if(criteria.criteriaType==='commercialPropertyListings') {
<div class="grid grid-cols-1 gap-6">
<!-- } @if(criteria.criteriaType==='commercialPropertyListings') { -->
<!-- <div class="grid grid-cols-1 gap-6">
<div class="space-y-4">
<div>
<label for="state" class="block mb-2 text-sm font-medium text-gray-900">Location - State</label>
@ -291,9 +291,9 @@
</div>
</div>
</div>
</div>
} @if(criteria.criteriaType==='brokerListings') {
<div class="grid grid-cols-1 gap-6">
</div> -->
<!-- } @if(criteria.criteriaType==='brokerListings') { -->
<!-- <div class="grid grid-cols-1 gap-6">
<div class="space-y-4">
<div>
<label for="states" class="block mb-2 text-sm font-medium text-gray-900">Locations served - States</label>
@ -363,7 +363,7 @@
/>
</div>
</div>
</div>
</div> -->
}
</div>
}
@ -383,6 +383,9 @@
</div>
</div>
</div>
<!-- ################################################################################## -->
<!-- ################################################################################## -->
<!-- ################################################################################## -->
<div *ngIf="!isModal" class="space-y-6">
<div class="flex space-x-4 mb-4">
<h3 class="text-xl font-semibold text-gray-900">Filter ({{ numberOfResults$ | async }})</h3>
@ -392,6 +395,31 @@
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
<!-- Display active filters as tags -->
<div class="flex flex-wrap gap-2" *ngIf="hasActiveFilters()">
<span *ngIf="criteria.state" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
State: {{ criteria.state }} <button (click)="removeFilter('state')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="criteria.city" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
City: {{ criteria.city }} <button (click)="removeFilter('city')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="criteria.minPrice || criteria.maxPrice" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
Price: {{ criteria.minPrice || 'Any' }} - {{ criteria.maxPrice || 'Any' }} <button (click)="removeFilter('price')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="criteria.minRevenue || criteria.maxRevenue" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
Revenue: {{ criteria.minRevenue || 'Any' }} - {{ criteria.maxRevenue || 'Any' }} <button (click)="removeFilter('revenue')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="criteria.minCashFlow || criteria.maxCashFlow" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
Cashflow: {{ criteria.minCashFlow || 'Any' }} - {{ criteria.maxCashFlow || 'Any' }} <button (click)="removeFilter('cashflow')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="criteria.types.length" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
Categories: {{ criteria.types.join(', ') }} <button (click)="removeFilter('types')" class="ml-1 text-red-500 hover:text-red-700">&times;</button>
</span>
<span *ngIf="selectedPropertyType" class="bg-gray-200 text-gray-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
Property Type: {{ selectedPropertyTypeName }} <button (click)="removeFilter('propertyType')" class="ml-1 text-red-500 hover:text-red-700">×</button>
</span>
<!-- Add more filters as needed (e.g., radius, establishedSince, etc.) -->
</div>
@if(criteria.criteriaType==='businessListings') {
<div class="space-y-4">
<div>
@ -466,56 +494,29 @@
</div>
<div>
<label class="block mb-2 text-sm font-medium text-gray-900">Category</label>
<div class="grid grid-cols-2 gap-2">
@for(tob of selectOptions.typesOfBusiness; track tob) {
<div class="flex items-center">
<input
type="checkbox"
id="automotive"
[ngModel]="isTypeOfBusinessClicked(tob)"
(ngModelChange)="categoryClicked($event, tob.value)"
value="{{ tob.value }}"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
/>
<label for="automotive" class="ml-2 text-sm font-medium text-gray-900">{{ tob.name }}</label>
</div>
}
</div>
<ng-select
class="custom"
[items]="selectOptions.typesOfBusiness"
bindLabel="name"
bindValue="value"
[ngModel]="criteria.types"
(ngModelChange)="onCategoryChange($event)"
[multiple]="true"
[closeOnSelect]="true"
placeholder="Select categories"
></ng-select>
</div>
<div>
<label class="block mb-2 text-sm font-medium text-gray-900">Type of Property</label>
<div class="space-y-2">
<div class="flex items-center">
<input
[(ngModel)]="criteria.realEstateChecked"
(ngModelChange)="onCheckboxChange('realEstateChecked', $event)"
type="checkbox"
name="realEstateChecked"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
/>
<label for="realEstateChecked" class="ml-2 text-sm font-medium text-gray-900">Real Estate</label>
</div>
<div class="flex items-center">
<input
[(ngModel)]="criteria.leasedLocation"
(ngModelChange)="onCheckboxChange('leasedLocation', $event)"
type="checkbox"
name="leasedLocation"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
/>
<label for="leasedLocation" class="ml-2 text-sm font-medium text-gray-900">Leased Location</label>
</div>
<div class="flex items-center">
<input
[(ngModel)]="criteria.franchiseResale"
(ngModelChange)="onCheckboxChange('franchiseResale', $event)"
type="checkbox"
name="franchiseResale"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
/>
<label for="franchiseResale" class="ml-2 text-sm font-medium text-gray-900">Franchise</label>
</div>
</div>
<ng-select
class="custom"
[items]="propertyTypeOptions"
bindLabel="name"
bindValue="value"
[ngModel]="selectedPropertyType"
(ngModelChange)="onPropertyTypeChange($event)"
placeholder="Select property type"
></ng-select>
</div>
<div>
<label for="numberEmployees" class="block mb-2 text-sm font-medium text-gray-900">Number of Employees</label>
@ -572,8 +573,8 @@
/>
</div>
</div>
} @if(criteria.criteriaType==='commercialPropertyListings') {
<div class="space-y-4">
<!-- } @if(criteria.criteriaType==='commercialPropertyListings') { -->
<!-- <div class="space-y-4">
<div>
<label for="state" class="block mb-2 text-sm font-medium text-gray-900">Location - State</label>
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"></ng-select>
@ -645,9 +646,9 @@
}
</div>
</div>
</div>
} @if(criteria.criteriaType==='brokerListings') {
<div class="space-y-4">
</div> -->
<!-- } @if(criteria.criteriaType==='brokerListings') { -->
<!-- <div class="space-y-4">
<div>
<label for="states" class="block mb-2 text-sm font-medium text-gray-900">Locations served - States</label>
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state" [multiple]="false"></ng-select>
@ -703,11 +704,6 @@
<label for="brokername" class="block mb-2 text-sm font-medium text-gray-900">Name of Professional</label>
<input type="text" id="brokername" [(ngModel)]="criteria.brokerName" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" />
</div>
</div>
}
<!-- <div class="flex items-center space-x-2">
<button type="button" (click)="modalService.accept()" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center">
Search ({{ numberOfResults$ | async }})
</button>
</div> -->
}
</div>

View File

@ -1,9 +1,12 @@
:host ::ng-deep .ng-select.custom .ng-select-container {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
height: 46px;
min-height: 46px;
border-radius: 0.5rem;
.ng-value-container .ng-input {
top: 10px;
}
}
:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder {
position: unset;
}

View File

@ -11,7 +11,7 @@ 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, resetCommercialPropertyListingCriteria, resetUserListingCriteria } from '../../utils/utils';
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';
@ -34,7 +34,7 @@ export class SearchModalComponent {
// cityInput$ = new Subject<string>();
countyInput$ = new Subject<string>();
private criteriaChangeSubscription: Subscription;
public criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
public criteria: BusinessListingCriteria;
public backupCriteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria = getCriteriaStateObject('businessListings');
numberOfResults$: Observable<number>;
@ -48,11 +48,19 @@ export class SearchModalComponent {
private userService: UserService,
private searchService: SearchService,
) {}
// Define property type options
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;
this.criteria = msg as BusinessListingCriteria;
this.backupCriteria = JSON.parse(JSON.stringify(msg));
this.setTotalNumberOfResults();
});
@ -64,9 +72,89 @@ export class SearchModalComponent {
});
// this.loadCities();
this.loadCounties();
this.updateSelectedPropertyType();
}
hasActiveFilters(): boolean {
return !!(
this.criteria.state ||
this.criteria.city ||
(<BusinessListingCriteria>this.criteria).minPrice ||
(<BusinessListingCriteria>this.criteria).maxPrice ||
(<BusinessListingCriteria>this.criteria).minRevenue ||
(<BusinessListingCriteria>this.criteria).maxRevenue ||
(<BusinessListingCriteria>this.criteria).minCashFlow ||
(<BusinessListingCriteria>this.criteria).maxCashFlow ||
this.criteria.types.length ||
this.selectedPropertyType
);
}
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':
(<BusinessListingCriteria>this.criteria).minPrice = null;
(<BusinessListingCriteria>this.criteria).maxPrice = null;
break;
case 'revenue':
(<BusinessListingCriteria>this.criteria).minRevenue = null;
(<BusinessListingCriteria>this.criteria).maxRevenue = null;
break;
case 'cashflow':
(<BusinessListingCriteria>this.criteria).minCashFlow = null;
(<BusinessListingCriteria>this.criteria).maxCashFlow = null;
break;
case 'types':
this.criteria.types = [];
break;
case 'propertyType':
(<BusinessListingCriteria>this.criteria).realEstateChecked = false;
(<BusinessListingCriteria>this.criteria).leasedLocation = false;
(<BusinessListingCriteria>this.criteria).franchiseResale = false;
this.selectedPropertyType = null;
break;
}
this.searchService.search(this.criteria);
}
// Handle category change
onCategoryChange(selectedCategories: string[]) {
this.criteria.types = selectedCategories;
this.searchService.search(this.criteria);
}
ngOnChanges() {}
// 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.updateSelectedPropertyTypeName();
this.searchService.search(this.criteria);
}
// 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;
this.updateSelectedPropertyTypeName();
}
updateSelectedPropertyTypeName() {
this.selectedPropertyTypeName = this.selectedPropertyType ? this.propertyTypeOptions.find(opt => opt.value === this.selectedPropertyType)?.name : null;
}
categoryClicked(checked: boolean, value: string) {
if (checked) {
this.criteria.types.push(value);
@ -151,20 +239,21 @@ export class SearchModalComponent {
if (this.criteria.criteriaType === 'businessListings' || this.criteria.criteriaType === 'commercialPropertyListings') {
this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty');
} else if (this.criteria.criteriaType === 'brokerListings') {
this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria);
//this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria);
} else {
this.numberOfResults$ = of();
}
}
}
clearFilter() {
if (this.criteria.criteriaType === 'businessListings') {
//if (this.criteria.criteriaType === 'businessListings') {
resetBusinessListingCriteria(this.criteria);
} else if (this.criteria.criteriaType === 'commercialPropertyListings') {
/* } else if (this.criteria.criteriaType === 'commercialPropertyListings') {
resetCommercialPropertyListingCriteria(this.criteria);
} else {
resetUserListingCriteria(this.criteria);
}
} */
this.searchService.search(this.criteria);
}
close() {
this.modalService.reject(this.backupCriteria);