location radius search

This commit is contained in:
2024-07-24 16:16:25 +02:00
parent acec14d372
commit 38e943c18e
15 changed files with 213 additions and 60 deletions

View File

@@ -172,11 +172,11 @@ export class HeaderComponent {
}
getNumberOfFiltersSet() {
if (this.criteria?.criteriaType === 'broker') {
return compareObjects(createEmptyUserListingCriteria(), this.criteria, ['start', 'length', 'page']);
return compareObjects(createEmptyUserListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
} else if (this.criteria?.criteriaType === 'business') {
return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page']);
return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
} else if (this.criteria?.criteriaType === 'commercialProperty') {
return compareObjects(createEmptyCommercialPropertyListingCriteria(), this.criteria, ['start', 'length', 'page']);
return compareObjects(createEmptyCommercialPropertyListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius']);
} else {
return 0;
}

View File

@@ -21,7 +21,7 @@
<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" name="state"> </ng-select>
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
</div>
<div>
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
@@ -35,13 +35,44 @@
[loading]="cityLoading"
typeToSearchText="Please enter 2 or more characters"
[typeahead]="cityInput$"
[(ngModel)]="criteria.city"
[ngModel]="criteria.city"
(ngModelChange)="setCity($event)"
>
@for (city of cities$ | async; track city.id) {
<ng-option [value]="city.city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
}
</ng-select>
</div>
<!-- New section for city search type -->
<div *ngIf="criteria.city">
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4">
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="exact" />
<span class="ml-2">Exact City</span>
</label>
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="radius" />
<span class="ml-2">Radius Search</span>
</label>
</div>
</div>
<!-- New section for radius selection -->
<div *ngIf="criteria.city && criteria.searchType === 'radius'" class="space-y-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Select Radius (in miles)</label>
<div class="flex flex-wrap">
@for (radius of [5, 20, 50, 100, 200, 300, 400, 500]; track radius) {
<button
type="button"
class="px-3 py-2 text-xs font-medium text-center border border-gray-200 hover:bg-gray-500 hover:text-white"
[ngClass]="criteria.radius === radius ? 'text-white bg-gray-500' : 'text-gray-900 bg-white'"
(click)="criteria.radius = radius"
>
{{ radius }}
</button>
}
</div>
</div>
<div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
@@ -113,16 +144,6 @@
placeholder="e.g. Restaurant"
/>
</div>
<div>
<label for="brokername" class="block mb-2 text-sm font-medium text-gray-900">Broker Name / Company Name</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"
placeholder="e.g. Brokers Invest"
/>
</div>
</div>
<div class="space-y-4">
<div>
@@ -200,6 +221,16 @@
/>
</div>
</div>
<div>
<label for="brokername" class="block mb-2 text-sm font-medium text-gray-900">Broker Name / Company Name</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"
placeholder="e.g. Brokers Invest"
/>
</div>
</div>
</div>
} @if(criteria.criteriaType==='commercialProperty'){
@@ -207,7 +238,7 @@
<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" name="state"> </ng-select>
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
</div>
<div>
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
@@ -220,13 +251,44 @@
[loading]="cityLoading"
typeToSearchText="Please enter 2 or more characters"
[typeahead]="cityInput$"
[(ngModel)]="criteria.city"
[ngModel]="criteria.city"
(ngModelChange)="setCity($event)"
>
@for (city of cities$ | async; track city.id) {
<ng-option [value]="city.city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
}
</ng-select>
</div>
<!-- New section for city search type -->
<div *ngIf="criteria.city">
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4">
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="exact" />
<span class="ml-2">Exact City</span>
</label>
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="radius" />
<span class="ml-2">Radius Search</span>
</label>
</div>
</div>
<!-- New section for radius selection -->
<div *ngIf="criteria.city && criteria.searchType === 'radius'" class="space-y-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Select Radius (in miles)</label>
<div class="flex flex-wrap">
@for (radius of [5, 20, 50, 100, 200, 300, 400, 500]; track radius) {
<button
type="button"
class="px-3 py-2 text-xs font-medium text-center border border-gray-200 hover:bg-gray-500 hover:text-white"
[ngClass]="criteria.radius === radius ? 'text-white bg-gray-500' : 'text-gray-900 bg-white'"
(click)="criteria.radius = radius"
>
{{ radius }}
</button>
}
</div>
</div>
<div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
<div class="flex items-center space-x-2">
@@ -284,7 +346,7 @@
<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.states" name="states" [multiple]="true"> </ng-select>
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state" [multiple]="false"> </ng-select>
</div>
<div>
<label for="counties" class="block mb-2 text-sm font-medium text-gray-900">Locations served - Counties</label>
@@ -292,7 +354,7 @@
[items]="counties$ | async"
bindLabel="name"
class="custom"
[multiple]="true"
[multiple]="false"
[hideSelected]="true"
[trackByFn]="trackByFn"
[minTermLength]="2"
@@ -317,13 +379,44 @@
[loading]="cityLoading"
typeToSearchText="Please enter 2 or more characters"
[typeahead]="cityInput$"
[(ngModel)]="criteria.city"
[ngModel]="criteria.city"
(ngModelChange)="setCity($event)"
>
@for (city of cities$ | async; track city.id) {
<ng-option [value]="city.city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
}
</ng-select>
</div>
<!-- New section for city search type -->
<div *ngIf="criteria.city">
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4">
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="exact" />
<span class="ml-2">Exact City</span>
</label>
<label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" value="radius" />
<span class="ml-2">Radius Search</span>
</label>
</div>
</div>
<!-- New section for radius selection -->
<div *ngIf="criteria.city && criteria.searchType === 'radius'" class="space-y-2">
<label class="block mb-2 text-sm font-medium text-gray-900">Select Radius (in miles)</label>
<div class="flex flex-wrap">
@for (radius of [5, 20, 50, 100, 200, 300, 400, 500]; track radius) {
<button
type="button"
class="px-3 py-2 text-xs font-medium text-center border border-gray-200 hover:bg-gray-500 hover:text-white"
[ngClass]="criteria.radius === radius ? 'text-white bg-gray-500' : 'text-gray-900 bg-white'"
(click)="criteria.radius = radius"
>
{{ radius }}
</button>
}
</div>
</div>
</div>
<div class="space-y-4">
<div>

View File

@@ -89,6 +89,24 @@ export class SearchModalComponent {
),
);
}
setCity(city) {
if (city) {
this.criteria.city = city.city;
this.criteria.state = city.state_code;
} else {
this.criteria.city = null;
this.criteria.radius = null;
this.criteria.searchType = 'exact';
}
}
setState(state: string) {
if (state) {
this.criteria.state = state;
} else {
this.criteria.state = null;
this.setCity(null);
}
}
private setupCriteriaChangeListener() {
this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => this.setTotalNumberOfResults());
}

View File

@@ -99,7 +99,7 @@
<p class="text-sm text-gray-600 mb-1">Asking price: {{ listing.price | currency }}</p>
<p class="text-sm text-gray-600 mb-1">Sales revenue: {{ listing.salesRevenue | currency }}</p>
<p class="text-sm text-gray-600 mb-1">Net profit: {{ listing.cashFlow | currency }}</p>
<p class="text-sm text-gray-600 mb-1">Location: {{ selectOptions.getState(listing.state) }}</p>
<p class="text-sm text-gray-600 mb-1">Location: {{ listing.city }} - {{ selectOptions.getState(listing.state) }}</p>
<p class="text-sm text-gray-600 mb-1">Established: {{ listing.established }}</p>
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" alt="Company logo" class="absolute bottom-[70px] right-[30px] h-[35px] w-auto" />
<div class="flex-grow"></div>

View File

@@ -115,6 +115,8 @@ export function createEmptyBusinessListingCriteria(): BusinessListingCriteria {
franchiseResale: false,
title: '',
brokerName: '',
searchType: 'exact',
radius: null,
};
}
@@ -132,6 +134,8 @@ export function createEmptyCommercialPropertyListingCriteria(): CommercialProper
minPrice: null,
maxPrice: null,
title: '',
searchType: 'exact',
radius: null,
};
}
@@ -148,7 +152,9 @@ export function createEmptyUserListingCriteria(): UserListingCriteria {
lastname: '',
companyName: '',
counties: [],
states: [],
state: '',
searchType: 'exact',
radius: null,
};
}
export function createLogger(name: string, level: number = INFO, options: any = {}) {