price optional, better labeling, impr. filter

This commit is contained in:
Andreas Knuth 2025-09-29 14:44:47 -05:00
parent 03d075b7d9
commit fbca2ddab5
7 changed files with 98 additions and 79 deletions

View File

@ -268,7 +268,7 @@ export const BusinessListingSchema = z
title: z.string().min(10), title: z.string().min(10),
description: z.string().min(10), description: z.string().min(10),
location: GeoSchema, location: GeoSchema,
price: z.number().positive(), price: z.number().positive().optional().nullable(),
favoritesForUser: z.array(z.string()), favoritesForUser: z.array(z.string()),
draft: z.boolean(), draft: z.boolean(),
listingsCategory: ListingsCategoryEnum, listingsCategory: ListingsCategoryEnum,
@ -326,7 +326,7 @@ export const CommercialPropertyListingSchema = z
title: z.string().min(10), title: z.string().min(10),
description: z.string().min(10), description: z.string().min(10),
location: GeoSchema, location: GeoSchema,
price: z.number().positive(), price: z.number().positive().optional().nullable(),
favoritesForUser: z.array(z.string()), favoritesForUser: z.array(z.string()),
listingsCategory: ListingsCategoryEnum, listingsCategory: ListingsCategoryEnum,
internalListingNumber: z.number().int().positive().optional().nullable(), internalListingNumber: z.number().int().positive().optional().nullable(),

View File

@ -54,11 +54,11 @@
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label> <label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="exact" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="exact" />
<span class="ml-2">Exact City</span> <span class="ml-2">Exact City</span>
</label> </label>
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="radius" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="radius" />
<span class="ml-2">Radius Search</span> <span class="ml-2">Radius Search</span>
</label> </label>
</div> </div>
@ -71,7 +71,7 @@
type="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" 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'" [ngClass]="criteria.radius === radius ? 'text-white bg-gray-500' : 'text-gray-900 bg-white'"
(click)="criteria.radius = radius" (click)="setRadius(radius)"
> >
{{ radius }} {{ radius }}
</button> </button>
@ -81,9 +81,11 @@
<div> <div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label> <label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="price-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minPrice" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-from" [ngModel]="criteria.minPrice" (ngModelChange)="updateCriteria({ minPrice: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="price-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxPrice" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-to" [ngModel]="criteria.maxPrice" (ngModelChange)="updateCriteria({ maxPrice: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
@ -91,9 +93,9 @@
<input <input
type="text" type="text"
id="title" id="title"
[(ngModel)]="criteria.title" [ngModel]="criteria.title"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ title: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Office Space" placeholder="e.g. Office Space"
/> />
</div> </div>
@ -158,11 +160,11 @@
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label> <label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="exact" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="exact" />
<span class="ml-2">Exact City</span> <span class="ml-2">Exact City</span>
</label> </label>
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="radius" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="radius" />
<span class="ml-2">Radius Search</span> <span class="ml-2">Radius Search</span>
</label> </label>
</div> </div>
@ -199,9 +201,9 @@
<div> <div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label> <label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="price-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minPrice" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-from" [ngModel]="criteria.minPrice" (ngModelChange)="updateCriteria({ minPrice: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"> </app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="price-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxPrice" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-to" [ngModel]="criteria.maxPrice" (ngModelChange)="updateCriteria({ maxPrice: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"> </app-validated-price>
</div> </div>
</div> </div>
<div> <div>
@ -209,9 +211,9 @@
<input <input
type="text" type="text"
id="title" id="title"
[(ngModel)]="criteria.title" [ngModel]="criteria.title"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ title: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Office Space" placeholder="e.g. Office Space"
/> />
</div> </div>

View File

@ -77,7 +77,7 @@ export class SearchModalCommercialComponent implements OnInit, OnDestroy {
} }
// Setup debounced search // Setup debounced search
this.searchDebounce$.pipe(debounceTime(400), distinctUntilChanged(), takeUntil(this.destroy$)).subscribe(() => { this.searchDebounce$.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe(() => {
this.triggerSearch(); this.triggerSearch();
}); });
} }
@ -236,7 +236,7 @@ export class SearchModalCommercialComponent implements OnInit, OnDestroy {
} }
// Helper methods // Helper methods
private updateCriteria(updates: any): void { public updateCriteria(updates: any): void {
if (this.isModal) { if (this.isModal) {
// In modal: Update locally only // In modal: Update locally only
this.criteria = { ...this.criteria, ...updates }; this.criteria = { ...this.criteria, ...updates };
@ -262,7 +262,7 @@ export class SearchModalCommercialComponent implements OnInit, OnDestroy {
} }
private setTotalNumberOfResults(): void { private setTotalNumberOfResults(): void {
this.numberOfResults$ = this.listingService.getNumberOfListings('commercialProperty'); this.numberOfResults$ = this.listingService.getNumberOfListings('commercialProperty', this.criteria);
} }
private getDefaultCriteria(): CommercialPropertyListingCriteria { private getDefaultCriteria(): CommercialPropertyListingCriteria {

View File

@ -71,11 +71,11 @@
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label> <label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="exact" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="exact" />
<span class="ml-2">Exact City</span> <span class="ml-2">Exact City</span>
</label> </label>
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" [(ngModel)]="criteria.searchType" (ngModelChange)="onCriteriaChange()" value="radius" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="radius" />
<span class="ml-2">Radius Search</span> <span class="ml-2">Radius Search</span>
</label> </label>
</div> </div>
@ -88,7 +88,7 @@
type="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" 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'" [ngClass]="criteria.radius === radius ? 'text-white bg-gray-500' : 'text-gray-900 bg-white'"
(click)="criteria.radius = radius" (click)="setRadius(radius)"
> >
{{ radius }} {{ radius }}
</button> </button>
@ -98,25 +98,31 @@
<div> <div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label> <label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="price-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minPrice" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-from" [ngModel]="criteria.minPrice" (ngModelChange)="updateCriteria({ minPrice: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="price-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxPrice" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-to" [ngModel]="criteria.maxPrice" (ngModelChange)="updateCriteria({ maxPrice: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
<label for="salesRevenue" class="block mb-2 text-sm font-medium text-gray-900">Sales Revenue</label> <label for="salesRevenue" class="block mb-2 text-sm font-medium text-gray-900">Sales Revenue</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="salesRevenue-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minRevenue" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="salesRevenue-from" [ngModel]="criteria.minRevenue" (ngModelChange)="updateCriteria({ minRevenue: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="salesRevenue-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxRevenue" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="salesRevenue-to" [ngModel]="criteria.maxRevenue" (ngModelChange)="updateCriteria({ maxRevenue: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p.2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
<label for="cashflow" class="block mb-2 text-sm font-medium text-gray-900">Cashflow</label> <label for="cashflow" class="block mb-2 text-sm font-medium text-gray-900">Cashflow</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="cashflow-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minCashFlow" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="cashflow-from" [ngModel]="criteria.minCashFlow" (ngModelChange)="updateCriteria({ minCashFlow: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="cashflow-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxCashFlow" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="cashflow-to" [ngModel]="criteria.maxCashFlow" (ngModelChange)="updateCriteria({ maxCashFlow: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
@ -124,9 +130,9 @@
<input <input
type="text" type="text"
id="title" id="title"
[(ngModel)]="criteria.title" [ngModel]="criteria.title"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ title: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Restaurant" placeholder="e.g. Restaurant"
/> />
</div> </div>
@ -162,18 +168,18 @@
<input <input
type="number" type="number"
id="numberEmployees-from" id="numberEmployees-from"
[(ngModel)]="criteria.minNumberEmployees" [ngModel]="criteria.minNumberEmployees"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ minNumberEmployees: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="From" placeholder="From"
/> />
<span>-</span> <span>-</span>
<input <input
type="number" type="number"
id="numberEmployees-to" id="numberEmployees-to"
[(ngModel)]="criteria.maxNumberEmployees" [ngModel]="criteria.maxNumberEmployees"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ maxNumberEmployees: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="To" placeholder="To"
/> />
</div> </div>
@ -184,9 +190,9 @@
<input <input
type="number" type="number"
id="establishedMin" id="establishedMin"
[(ngModel)]="criteria.establishedMin" [ngModel]="criteria.establishedMin"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ establishedMin: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="YY" placeholder="YY"
/> />
</div> </div>
@ -196,9 +202,9 @@
<input <input
type="text" type="text"
id="brokername" id="brokername"
[(ngModel)]="criteria.brokerName" [ngModel]="criteria.brokerName"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ brokerName: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Brokers Invest" placeholder="e.g. Brokers Invest"
/> />
</div> </div>
@ -269,11 +275,11 @@
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label> <label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" (ngModelChange)="onCriteriaChange()" [(ngModel)]="criteria.searchType" value="exact" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="exact" />
<span class="ml-2">Exact City</span> <span class="ml-2">Exact City</span>
</label> </label>
<label class="inline-flex items-center"> <label class="inline-flex items-center">
<input type="radio" class="form-radio" name="searchType" (ngModelChange)="onCriteriaChange()" [(ngModel)]="criteria.searchType" value="radius" /> <input type="radio" class="form-radio" name="searchType" [ngModel]="criteria.searchType" (ngModelChange)="updateCriteria({ searchType: $event })" value="radius" />
<span class="ml-2">Radius Search</span> <span class="ml-2">Radius Search</span>
</label> </label>
</div> </div>
@ -296,25 +302,29 @@
<div> <div>
<label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label> <label for="price" class="block mb-2 text-sm font-medium text-gray-900">Price</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="price-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minPrice" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-from" [ngModel]="criteria.minPrice" (ngModelChange)="updateCriteria({ minPrice: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"> </app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="price-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxPrice" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="price-to" [ngModel]="criteria.maxPrice" (ngModelChange)="updateCriteria({ maxPrice: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"> </app-validated-price>
</div> </div>
</div> </div>
<div> <div>
<label for="salesRevenue" class="block mb-2 text-sm font-medium text-gray-900">Sales Revenue</label> <label for="salesRevenue" class="block mb-2 text-sm font-medium text-gray-900">Sales Revenue</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="salesRevenue-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minRevenue" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="salesRevenue-from" [ngModel]="criteria.minRevenue" (ngModelChange)="updateCriteria({ minRevenue: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="salesRevenue-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxRevenue" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="salesRevenue-to" [ngModel]="criteria.maxRevenue" (ngModelChange)="updateCriteria({ maxRevenue: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p.2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
<label for="cashflow" class="block mb-2 text-sm font-medium text-gray-900">Cashflow</label> <label for="cashflow" class="block mb-2 text-sm font-medium text-gray-900">Cashflow</label>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<app-validated-price name="cashflow-from" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.minCashFlow" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="cashflow-from" [ngModel]="criteria.minCashFlow" (ngModelChange)="updateCriteria({ minCashFlow: $event })" placeholder="From" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
<span>-</span> <span>-</span>
<app-validated-price name="cashflow-to" (ngModelChange)="debouncedSearch()" [(ngModel)]="criteria.maxCashFlow" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5"></app-validated-price> <app-validated-price name="cashflow-to" [ngModel]="criteria.maxCashFlow" (ngModelChange)="updateCriteria({ maxCashFlow: $event })" placeholder="To" inputClasses="bg-gray-50 text-sm !mt-0 p-2.5">
</app-validated-price>
</div> </div>
</div> </div>
<div> <div>
@ -322,9 +332,9 @@
<input <input
type="text" type="text"
id="title" id="title"
[(ngModel)]="criteria.title" [ngModel]="criteria.title"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ title: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Restaurant" placeholder="e.g. Restaurant"
/> />
</div> </div>
@ -360,18 +370,18 @@
<input <input
type="number" type="number"
id="numberEmployees-from" id="numberEmployees-from"
[(ngModel)]="criteria.minNumberEmployees" [ngModel]="criteria.minNumberEmployees"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ minNumberEmployees: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="From" placeholder="From"
/> />
<span>-</span> <span>-</span>
<input <input
type="number" type="number"
id="numberEmployees-to" id="numberEmployees-to"
[(ngModel)]="criteria.maxNumberEmployees" [ngModel]="criteria.maxNumberEmployees"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ maxNumberEmployees: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="To" placeholder="To"
/> />
</div> </div>
@ -382,9 +392,9 @@
<input <input
type="number" type="number"
id="establishedMin" id="establishedMin"
[(ngModel)]="criteria.establishedMin" [ngModel]="criteria.establishedMin"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ establishedMin: $event })"
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-1/2 p-2.5" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-1/2 p-2.5"
placeholder="YY" placeholder="YY"
/> />
</div> </div>
@ -394,9 +404,9 @@
<input <input
type="text" type="text"
id="brokername" id="brokername"
[(ngModel)]="criteria.brokerName" [ngModel]="criteria.brokerName"
(ngModelChange)="debouncedSearch()" (ngModelChange)="updateCriteria({ brokerName: $event })"
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" class="bg-gray-50 border border-gray-300 text-sm rounded-lg block w-full p-2.5"
placeholder="e.g. Brokers Invest" placeholder="e.g. Brokers Invest"
/> />
</div> </div>

View File

@ -86,14 +86,12 @@ export class SearchModalComponent implements OnInit, OnDestroy {
} }
// Setup debounced search // Setup debounced search
this.searchDebounce$.pipe(debounceTime(400), distinctUntilChanged(), takeUntil(this.destroy$)).subscribe(() => { this.searchDebounce$.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe(() => this.triggerSearch());
this.triggerSearch();
});
} }
private initializeWithCriteria(criteria: any): void { private initializeWithCriteria(criteria: any): void {
this.criteria = criteria; this.criteria = criteria;
this.currentListingType = criteria.criteriaType; this.currentListingType = criteria?.criteriaType;
this.backupCriteria = JSON.parse(JSON.stringify(criteria)); this.backupCriteria = JSON.parse(JSON.stringify(criteria));
this.updateSelectedPropertyType(); this.updateSelectedPropertyType();
this.setTotalNumberOfResults(); this.setTotalNumberOfResults();
@ -312,7 +310,7 @@ export class SearchModalComponent implements OnInit, OnDestroy {
} }
// Helper methods // Helper methods
private updateCriteria(updates: any): void { public updateCriteria(updates: any): void {
if (this.isModal) { if (this.isModal) {
// In modal: Update locally only // In modal: Update locally only
this.criteria = { ...this.criteria, ...updates }; this.criteria = { ...this.criteria, ...updates };
@ -356,10 +354,10 @@ export class SearchModalComponent implements OnInit, OnDestroy {
switch (this.currentListingType) { switch (this.currentListingType) {
case 'businessListings': case 'businessListings':
this.numberOfResults$ = this.listingService.getNumberOfListings('business'); this.numberOfResults$ = this.listingService.getNumberOfListings('business', this.criteria);
break; break;
case 'commercialPropertyListings': case 'commercialPropertyListings':
this.numberOfResults$ = this.listingService.getNumberOfListings('commercialProperty'); this.numberOfResults$ = this.listingService.getNumberOfListings('commercialProperty', this.criteria);
break; break;
case 'brokerListings': case 'brokerListings':
this.numberOfResults$ = this.userService.getNumberOfBroker(); this.numberOfResults$ = this.userService.getNumberOfBroker();

View File

@ -41,10 +41,19 @@
</div> </div>
<p class="text-base font-bold text-gray-800 mb-2"> <p class="text-base font-bold text-gray-800 mb-2">
<strong>Asking price:</strong> <span class="text-green-600"> {{ listing.price | currency : 'USD' : 'symbol' : '1.0-0' }}</span> <strong>Asking price:</strong>
<span class="text-green-600">
{{ listing?.price != null ? (listing.price | currency : 'USD' : 'symbol' : '1.0-0') : 'undisclosed' }}
</span>
</p>
<p class="text-sm text-gray-600 mb-2">
<strong>Sales revenue:</strong>
{{ listing?.salesRevenue != null ? (listing.salesRevenue | currency : 'USD' : 'symbol' : '1.0-0') : 'undisclosed' }}
</p>
<p class="text-sm text-gray-600 mb-2">
<strong>Net profit:</strong>
{{ listing?.cashFlow != null ? (listing.cashFlow | currency : 'USD' : 'symbol' : '1.0-0') : 'undisclosed' }}
</p> </p>
<p class="text-sm text-gray-600 mb-2"><strong>Sales revenue:</strong> {{ listing.salesRevenue | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
<p class="text-sm text-gray-600 mb-2"><strong>Net profit:</strong> {{ listing.cashFlow | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
<p class="text-sm text-gray-600 mb-2"> <p class="text-sm text-gray-600 mb-2">
<strong>Location:</strong> {{ listing.location.name ? listing.location.name : listing.location.county ? listing.location.county : this.selectOptions.getState(listing.location.state) }} <strong>Location:</strong> {{ listing.location.name ? listing.location.name : listing.location.county ? listing.location.county : this.selectOptions.getState(listing.location.state) }}
</p> </p>

View File

@ -21,8 +21,8 @@ export class ListingsService {
return result; return result;
} }
getNumberOfListings(listingsCategory: 'business' | 'commercialProperty'): Observable<number> { getNumberOfListings(listingsCategory: 'business' | 'commercialProperty', crit?: any): Observable<number> {
const criteria = getCriteriaByListingCategory(listingsCategory); const criteria = crit ? crit : getCriteriaByListingCategory(listingsCategory);
const sortBy = getSortByListingCategory(listingsCategory); const sortBy = getSortByListingCategory(listingsCategory);
const body = { ...criteria, sortBy }; // Merge, falls relevant (wenn Backend sortBy für Count braucht; sonst ignorieren) const body = { ...criteria, sortBy }; // Merge, falls relevant (wenn Backend sortBy für Count braucht; sonst ignorieren)
return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/findTotal`, body); return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/findTotal`, body);