Einbau klassische Filter als Overlay ...
This commit is contained in:
@@ -31,3 +31,4 @@
|
||||
</div>
|
||||
}
|
||||
<app-message-container></app-message-container>
|
||||
<app-search-modal></app-search-modal>
|
||||
|
||||
@@ -2,21 +2,20 @@ import { CommonModule } from '@angular/common';
|
||||
import { Component, HostListener } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import onChange from 'on-change';
|
||||
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { ListingCriteria } from '../../../bizmatch-server/src/models/main.model';
|
||||
import build from '../build';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { MessageContainerComponent } from './components/message/message-container.component';
|
||||
import { SearchModalComponent } from './components/search-modal/search-modal.component';
|
||||
import { LoadingService } from './services/loading.service';
|
||||
import { UserService } from './services/user.service';
|
||||
import { createDefaultListingCriteria } from './utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterOutlet, HeaderComponent, FooterComponent, MessageContainerComponent],
|
||||
imports: [CommonModule, RouterOutlet, HeaderComponent, FooterComponent, MessageContainerComponent, SearchModalComponent],
|
||||
providers: [],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss',
|
||||
@@ -25,9 +24,7 @@ export class AppComponent {
|
||||
build = build;
|
||||
title = 'bizmatch';
|
||||
actualRoute = '';
|
||||
listingCriteria: ListingCriteria = onChange(createDefaultListingCriteria(), (path, value, previousValue, applyData) => {
|
||||
sessionStorage.setItem('criteria', JSON.stringify(value));
|
||||
});
|
||||
|
||||
public constructor(public loadingService: LoadingService, private router: Router, private activatedRoute: ActivatedRoute, private keycloakService: KeycloakService, private userService: UserService) {
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
|
||||
let currentRoute = this.activatedRoute.root;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createPopper, Instance as PopperInstance } from '@popperjs/core';
|
||||
@Component({
|
||||
selector: 'app-dropdown',
|
||||
template: `
|
||||
<div #targetEl [class.hidden]="!isVisible" class="z-10">
|
||||
<div #targetEl [class.hidden]="!isVisible" class="z-50">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
<button
|
||||
type="button"
|
||||
#triggerButton
|
||||
(click)="openModal()"
|
||||
id="filterDropdownButton"
|
||||
class="max-sm:hidden px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 md:me-2"
|
||||
>
|
||||
@@ -243,24 +244,14 @@
|
||||
</button>
|
||||
</div> -->
|
||||
</nav>
|
||||
|
||||
<!-- ############################### -->
|
||||
<!-- Filter Dropdown -->
|
||||
<!-- ############################### -->
|
||||
<app-dropdown [triggerEl]="triggerButton" [triggerType]="'click'">
|
||||
<div id="filterDropdown" class="z-10 w-80 p-3 bg-slate-200 rounded-lg shadow-lg dark:bg-gray-700">
|
||||
<h3 class="mb-3 text-sm font-medium text-gray-900 dark:text-white">Filter</h3>
|
||||
|
||||
<!-- Price Range -->
|
||||
<!-- <app-dropdown [triggerEl]="triggerButton" [triggerType]="'click'">
|
||||
<div id="filterDropdown" class="z-[50] w-80 p-3 bg-slate-200 rounded-lg shadow-lg dark:bg-gray-700">
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<input
|
||||
type="text"
|
||||
[ngModel]="prompt"
|
||||
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 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
value="300"
|
||||
/>
|
||||
</div>
|
||||
<!-- <label for="price-range" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Price Range</label>
|
||||
<label for="price-range" class="block text-sm font-medium text-gray-900 dark:text-white">Price Range</label>
|
||||
<div class="flex items-center space-x-4">
|
||||
<input
|
||||
type="number"
|
||||
@@ -276,12 +267,10 @@
|
||||
placeholder="To"
|
||||
value="3500"
|
||||
/>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sales -->
|
||||
<!-- <div class="mb-4">
|
||||
<label for="sales-range" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sales</label>
|
||||
<div class="mb-4">
|
||||
<label for="sales-range" class="block text-sm font-medium text-gray-900 dark:text-white">Sales Revenue</label>
|
||||
<div class="flex items-center space-x-4">
|
||||
<input
|
||||
type="number"
|
||||
@@ -298,10 +287,8 @@
|
||||
value="100"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Category -->
|
||||
<!-- <div class="mb-4">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Category</label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
@@ -335,10 +322,8 @@
|
||||
Watch
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- State -->
|
||||
<!-- <div class="mb-4">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">State</label>
|
||||
<ul class="w-48 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
|
||||
<li class="w-full border-b border-gray-200 rounded-t-lg dark:border-gray-600">
|
||||
@@ -379,9 +364,8 @@
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex justify-between">
|
||||
<button
|
||||
type="button"
|
||||
@@ -397,4 +381,4 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</app-dropdown>
|
||||
</app-dropdown> -->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
@@ -6,14 +6,15 @@ import { NavigationEnd, Router, RouterModule } from '@angular/router';
|
||||
import { faUserGear } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Collapse, Dropdown, initFlowbite } from 'flowbite';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { Observable, Subject, takeUntil } from 'rxjs';
|
||||
import { Observable, Subject, Subscription } from 'rxjs';
|
||||
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||
import { emailToDirName, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { map2User } from '../../utils/utils';
|
||||
import { createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, map2User } from '../../utils/utils';
|
||||
import { DropdownComponent } from '../dropdown/dropdown.component';
|
||||
import { ModalService } from '../search-modal/modal.service';
|
||||
@Component({
|
||||
selector: 'header',
|
||||
standalone: true,
|
||||
@@ -34,7 +35,15 @@ export class HeaderComponent {
|
||||
isMobile: boolean = false;
|
||||
private destroy$ = new Subject<void>();
|
||||
prompt: string;
|
||||
constructor(public keycloakService: KeycloakService, private router: Router, private userService: UserService, private sharedService: SharedService, private breakpointObserver: BreakpointObserver) {}
|
||||
private subscription: Subscription;
|
||||
constructor(
|
||||
public keycloakService: KeycloakService,
|
||||
private router: Router,
|
||||
private userService: UserService,
|
||||
private sharedService: SharedService,
|
||||
private breakpointObserver: BreakpointObserver,
|
||||
private modalService: ModalService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
const token = await this.keycloakService.getToken();
|
||||
@@ -52,30 +61,40 @@ export class HeaderComponent {
|
||||
initFlowbite();
|
||||
}
|
||||
});
|
||||
this.breakpointObserver
|
||||
.observe([Breakpoints.Handset])
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(result => {
|
||||
this.isMobile = result.matches;
|
||||
const targetEl = document.getElementById('filterDropdown');
|
||||
const triggerEl = this.isMobile ? document.getElementById('filterDropdownMobileButton') : document.getElementById('filterDropdownButton');
|
||||
if (targetEl && triggerEl) {
|
||||
this.filterDropdown = new Dropdown(targetEl, triggerEl);
|
||||
}
|
||||
});
|
||||
|
||||
// this.breakpointObserver
|
||||
// .observe([Breakpoints.Handset])
|
||||
// .pipe(takeUntil(this.destroy$))
|
||||
// .subscribe(result => {
|
||||
// this.isMobile = result.matches;
|
||||
// const targetEl = document.getElementById('filterDropdown');
|
||||
// const triggerEl = this.isMobile ? document.getElementById('filterDropdownMobileButton') : document.getElementById('filterDropdownButton');
|
||||
// if (targetEl && triggerEl) {
|
||||
// this.filterDropdown = new Dropdown(targetEl, triggerEl);
|
||||
// }
|
||||
// });
|
||||
this.sharedService.currentProfilePhoto.subscribe(photoUrl => {
|
||||
if (photoUrl) {
|
||||
this.profileUrl = photoUrl;
|
||||
}
|
||||
});
|
||||
}
|
||||
toggleFilterDropdown() {
|
||||
if (this.filterDropdown) {
|
||||
this.filterDropdown.toggle();
|
||||
|
||||
// toggleFilterDropdown() {
|
||||
// if (this.filterDropdown) {
|
||||
// this.filterDropdown.toggle();
|
||||
// }
|
||||
// }
|
||||
ngAfterViewInit() {}
|
||||
openModal() {
|
||||
if (this.isActive('/businessListings')) {
|
||||
this.modalService.showModal(createEmptyBusinessListingCriteria());
|
||||
} else if (this.isActive('/commercialPropertyListings')) {
|
||||
this.modalService.showModal(createEmptyCommercialPropertyListingCriteria());
|
||||
} else if (this.isActive('/brokerListings')) {
|
||||
this.modalService.showModal(createEmptyUserListingCriteria());
|
||||
}
|
||||
}
|
||||
ngAfterViewInit() {}
|
||||
|
||||
navigateWithState(dest: string, state: any) {
|
||||
this.router.navigate([dest], { state: state });
|
||||
}
|
||||
|
||||
34
bizmatch/src/app/components/search-modal/modal.service.ts
Normal file
34
bizmatch/src/app/components/search-modal/modal.service.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
// 1. Shared Service (modal.service.ts)
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ModalService {
|
||||
private modalVisibleSubject = new BehaviorSubject<boolean>(false);
|
||||
private messageSubject = new BehaviorSubject<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria>(null);
|
||||
private resolvePromise!: (value: boolean) => void;
|
||||
|
||||
modalVisible$: Observable<boolean> = this.modalVisibleSubject.asObservable();
|
||||
message$: Observable<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria> = this.messageSubject.asObservable();
|
||||
|
||||
showModal(message: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): Promise<boolean> {
|
||||
this.messageSubject.next(message);
|
||||
this.modalVisibleSubject.next(true);
|
||||
return new Promise<boolean>(resolve => {
|
||||
this.resolvePromise = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
accept(): void {
|
||||
this.modalVisibleSubject.next(false);
|
||||
this.resolvePromise(true);
|
||||
}
|
||||
|
||||
reject(): void {
|
||||
this.modalVisibleSubject.next(false);
|
||||
this.resolvePromise(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
<!-- <div class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full"> -->
|
||||
<div *ngIf="modalService.modalVisible$ | async" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full flex items-center justify-center">
|
||||
<div class="relative w-full max-w-4xl max-h-full">
|
||||
<div class="relative bg-white rounded-lg shadow">
|
||||
<div class="flex items-start justify-between p-4 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Business Listing Search</h3>
|
||||
<button (click)="modalService.reject()" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
<span class="sr-only">Close Modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 space-y-6">
|
||||
<div class="flex space-x-4 mb-4">
|
||||
<button class="text-blue-600 font-medium border-b-2 border-blue-600 pb-2">Classic Search</button>
|
||||
<button class="text-gray-500">AI Search <span class="bg-gray-200 text-xs font-semibold px-2 py-1 rounded">BETA</span></button>
|
||||
</div>
|
||||
@if(criteria.criteriaType==='business'){
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 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>
|
||||
<select id="state" [(ngModel)]="criteria.state" 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">
|
||||
<option selected>Arkansas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||
<input
|
||||
type="text"
|
||||
id="city"
|
||||
[(ngModel)]="criteria.city"
|
||||
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. Houston"
|
||||
/>
|
||||
</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">
|
||||
<input
|
||||
type="number"
|
||||
id="price-from"
|
||||
[(ngModel)]="criteria.minPrice"
|
||||
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"
|
||||
placeholder="From"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="price-to"
|
||||
[(ngModel)]="criteria.maxPrice"
|
||||
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"
|
||||
placeholder="To"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<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">
|
||||
<input
|
||||
type="number"
|
||||
id="salesRevenue-from"
|
||||
[(ngModel)]="criteria.minRevenue"
|
||||
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"
|
||||
placeholder="From"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="salesRevenue-to"
|
||||
[(ngModel)]="criteria.maxRevenue"
|
||||
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"
|
||||
placeholder="To"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="cashflow" class="block mb-2 text-sm font-medium text-gray-900">Cashflow</label>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input
|
||||
type="number"
|
||||
id="cashflow-from"
|
||||
[(ngModel)]="criteria.minCashFlow"
|
||||
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"
|
||||
placeholder="From"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="cashflow-to"
|
||||
[(ngModel)]="criteria.maxCashFlow"
|
||||
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"
|
||||
placeholder="To"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="title" class="block mb-2 text-sm font-medium text-gray-900">Title / Description (Free Search)</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
[(ngModel)]="criteria.title"
|
||||
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. 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>
|
||||
<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>
|
||||
</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" type="radio" id="realEstateChecked" name="wbs" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500" checked />
|
||||
<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" type="radio" id="leasedLocation" name="wbs" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 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" type="radio" id="franchiseResale" name="wbs" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500" />
|
||||
<label for="franchiseResale" class="ml-2 text-sm font-medium text-gray-900">Franchise</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="numberEmployees" class="block mb-2 text-sm font-medium text-gray-900">Number of Employees</label>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input
|
||||
type="number"
|
||||
id="numberEmployees-from"
|
||||
[(ngModel)]="criteria.minNumberEmployees"
|
||||
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"
|
||||
placeholder="From"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="numberEmployees-to"
|
||||
[(ngModel)]="criteria.maxNumberEmployees"
|
||||
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"
|
||||
placeholder="To"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="establishedSince" class="block mb-2 text-sm font-medium text-gray-900">Established Since</label>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input
|
||||
type="number"
|
||||
id="establishedSince-From"
|
||||
[(ngModel)]="criteria.establishedSince"
|
||||
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"
|
||||
placeholder="YYYY"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="establishedSince-To"
|
||||
[(ngModel)]="criteria.establishedUntil"
|
||||
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"
|
||||
placeholder="YYYY"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @if(criteria.criteriaType==='commercialProperty'){
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 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>
|
||||
<select id="state" [(ngModel)]="criteria.state" 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">
|
||||
<option selected>Arkansas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||
<input
|
||||
type="text"
|
||||
id="city"
|
||||
[(ngModel)]="criteria.city"
|
||||
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. Houston"
|
||||
/>
|
||||
</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">
|
||||
<input
|
||||
type="number"
|
||||
id="price-from"
|
||||
[(ngModel)]="criteria.minPrice"
|
||||
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"
|
||||
placeholder="From"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
type="number"
|
||||
id="price-to"
|
||||
[(ngModel)]="criteria.maxPrice"
|
||||
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"
|
||||
placeholder="To"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="title" class="block mb-2 text-sm font-medium text-gray-900">Title / Description (Free Search)</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
[(ngModel)]="criteria.title"
|
||||
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. Restaurant"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<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.typesOfCommercialProperty; 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @if(criteria.criteriaType==='user'){
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 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>
|
||||
<select id="states" [(ngModel)]="criteria.states" 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">
|
||||
<option selected>Arkansas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="counties" class="block mb-2 text-sm font-medium text-gray-900">Locations served - Counties</label>
|
||||
<select id="counties" [(ngModel)]="criteria.counties" 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">
|
||||
<option selected>Arkansas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||
<input
|
||||
type="text"
|
||||
id="city"
|
||||
[(ngModel)]="criteria.city"
|
||||
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. Houston"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<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.customerSubTypes; track tob){
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="automotive"
|
||||
[ngModel]="isTypeOfProfessionalClicked(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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b">
|
||||
<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
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
(click)="modalService.reject()"
|
||||
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeyValue, KeyValueStyle, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { SharedModule } from '../../shared/shared/shared.module';
|
||||
import { ModalService } from './modal.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-modal',
|
||||
standalone: true,
|
||||
imports: [SharedModule, AsyncPipe, NgIf],
|
||||
templateUrl: './search-modal.component.html',
|
||||
})
|
||||
export class SearchModalComponent {
|
||||
constructor(public selectOptions: SelectOptionsService, public modalService: ModalService) {}
|
||||
ngOnInit() {
|
||||
this.modalService.message$.subscribe(msg => {
|
||||
this.criteria = msg;
|
||||
});
|
||||
}
|
||||
public criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
search() {
|
||||
console.log('Search criteria:', this.criteria);
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
console.log('Closing modal');
|
||||
}
|
||||
isTypeOfBusinessClicked(v: KeyValueStyle) {
|
||||
return this.criteria.types.find(t => t === v.value);
|
||||
}
|
||||
isTypeOfProfessionalClicked(v: KeyValue) {
|
||||
return this.criteria.types.find(t => t === v.value);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { KeycloakService } from 'keycloak-angular';
|
||||
import onChange from 'on-change';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { BusinessListingCriteria, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
@@ -43,7 +43,7 @@ export class DetailsBusinessListingComponent {
|
||||
];
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
listing: BusinessListing;
|
||||
criteria: ListingCriteria;
|
||||
criteria: BusinessListingCriteria;
|
||||
mailinfo: MailInfo;
|
||||
environment = environment;
|
||||
keycloakUser: KeycloakUser;
|
||||
@@ -70,7 +70,7 @@ export class DetailsBusinessListingComponent {
|
||||
}
|
||||
});
|
||||
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { KeycloakService } from 'keycloak-angular';
|
||||
import onChange from 'on-change';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ErrorResponse, KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { CommercialPropertyListingCriteria, ErrorResponse, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
@@ -45,7 +45,7 @@ export class DetailsCommercialPropertyListingComponent {
|
||||
];
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
listing: CommercialPropertyListing;
|
||||
criteria: ListingCriteria;
|
||||
criteria: CommercialPropertyListingCriteria;
|
||||
mailinfo: MailInfo;
|
||||
environment = environment;
|
||||
keycloakUser: KeycloakUser;
|
||||
@@ -73,7 +73,7 @@ export class DetailsCommercialPropertyListingComponent {
|
||||
) {
|
||||
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
|
||||
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
this.criteria = onChange(getCriteriaStateObject('commercialProperty'), getSessionStorageHandler);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { KeycloakUser, ListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { KeycloakUser, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HistoryService } from '../../../services/history.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
@@ -27,7 +27,6 @@ export class DetailsUserComponent {
|
||||
user$: Observable<KeycloakUser>;
|
||||
keycloakUser: KeycloakUser;
|
||||
environment = environment;
|
||||
criteria: ListingCriteria;
|
||||
businessListings: BusinessListing[];
|
||||
commercialPropListings: CommercialPropertyListing[];
|
||||
companyOverview: SafeHtml;
|
||||
|
||||
@@ -4,10 +4,10 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import onChange from 'on-change';
|
||||
import { KeycloakUser, ListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { ListingsService } from '../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler, map2User, resetCriteria } from '../../utils/utils';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler, map2User } from '../../utils/utils';
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
standalone: true,
|
||||
@@ -20,14 +20,13 @@ export class HomeComponent {
|
||||
type: string;
|
||||
maxPrice: string;
|
||||
minPrice: string;
|
||||
criteria: ListingCriteria;
|
||||
criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
||||
states = [];
|
||||
isMenuOpen = false;
|
||||
user: KeycloakUser;
|
||||
prompt: string;
|
||||
public constructor(private router: Router, private activatedRoute: ActivatedRoute, public selectOptions: SelectOptionsService, public keycloakService: KeycloakService, private listingsService: ListingsService) {
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
resetCriteria(this.criteria);
|
||||
this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler);
|
||||
}
|
||||
async ngOnInit() {
|
||||
const token = await this.keycloakService.getToken();
|
||||
|
||||
@@ -4,13 +4,13 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import onChange from 'on-change';
|
||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-broker-listings',
|
||||
@@ -24,7 +24,7 @@ export class BrokerListingsComponent {
|
||||
listings: Array<BusinessListing>;
|
||||
users: Array<User>;
|
||||
filteredListings: Array<ListingType>;
|
||||
criteria: ListingCriteria;
|
||||
criteria: UserListingCriteria;
|
||||
realEstateChecked: boolean;
|
||||
maxPrice: string;
|
||||
minPrice: string;
|
||||
@@ -49,10 +49,9 @@ export class BrokerListingsComponent {
|
||||
private imageService: ImageService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
this.criteria = onChange(getCriteriaStateObject('user'), getSessionStorageHandler);
|
||||
this.route.data.subscribe(async () => {
|
||||
if (this.router.getCurrentNavigation().extras.state) {
|
||||
resetCriteria(this.criteria);
|
||||
} else {
|
||||
this.first = this.criteria.page * this.criteria.length;
|
||||
this.rows = this.criteria.length;
|
||||
@@ -86,7 +85,5 @@ export class BrokerListingsComponent {
|
||||
this.criteria.pageCount = event.pageCount;
|
||||
this.search();
|
||||
}
|
||||
reset() {
|
||||
this.criteria.name = '';
|
||||
}
|
||||
reset() {}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import onChange from 'on-change';
|
||||
import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { BusinessListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-business-listings',
|
||||
@@ -22,7 +22,7 @@ export class BusinessListingsComponent {
|
||||
environment = environment;
|
||||
listings: Array<BusinessListing>;
|
||||
filteredListings: Array<BusinessListing>;
|
||||
criteria: ListingCriteria;
|
||||
criteria: BusinessListingCriteria;
|
||||
realEstateChecked: boolean;
|
||||
maxPrice: string;
|
||||
minPrice: string;
|
||||
@@ -45,10 +45,9 @@ export class BusinessListingsComponent {
|
||||
private imageService: ImageService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler);
|
||||
this.route.data.subscribe(async () => {
|
||||
if (this.router.getCurrentNavigation().extras.state) {
|
||||
resetCriteria(this.criteria);
|
||||
} else {
|
||||
this.first = this.criteria.page * this.criteria.length;
|
||||
this.rows = this.criteria.length;
|
||||
@@ -70,12 +69,12 @@ export class BusinessListingsComponent {
|
||||
this.search();
|
||||
}
|
||||
async search() {
|
||||
this.listings = await this.listingsService.getListingsByPrompt(this.criteria);
|
||||
// const listingReponse = await this.listingsService.getListings(this.criteria, 'business');
|
||||
// this.listings = listingReponse.data;
|
||||
// this.totalRecords = listingReponse.total;
|
||||
// this.cdRef.markForCheck();
|
||||
// this.cdRef.detectChanges();
|
||||
//this.listings = await this.listingsService.getListingsByPrompt(this.criteria);
|
||||
const listingReponse = await this.listingsService.getListings(this.criteria, 'business');
|
||||
this.listings = listingReponse.data;
|
||||
this.totalRecords = listingReponse.total;
|
||||
this.cdRef.markForCheck();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
onPageChange(event: any) {
|
||||
this.criteria.start = event.first;
|
||||
|
||||
@@ -4,12 +4,12 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import onChange from 'on-change';
|
||||
import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { CommercialPropertyListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-commercial-property-listings',
|
||||
@@ -22,7 +22,7 @@ export class CommercialPropertyListingsComponent {
|
||||
environment = environment;
|
||||
listings: Array<CommercialPropertyListing>;
|
||||
filteredListings: Array<CommercialPropertyListing>;
|
||||
criteria: ListingCriteria;
|
||||
criteria: CommercialPropertyListingCriteria;
|
||||
realEstateChecked: boolean;
|
||||
first: number = 0;
|
||||
rows: number = 12;
|
||||
@@ -44,10 +44,9 @@ export class CommercialPropertyListingsComponent {
|
||||
private imageService: ImageService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler);
|
||||
this.criteria = onChange(getCriteriaStateObject('commercialProperty'), getSessionStorageHandler);
|
||||
this.route.data.subscribe(async () => {
|
||||
if (this.router.getCurrentNavigation().extras.state) {
|
||||
resetCriteria(this.criteria);
|
||||
} else {
|
||||
this.first = this.criteria.page * this.criteria.length;
|
||||
this.rows = this.criteria.length;
|
||||
|
||||
@@ -2,7 +2,14 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, lastValueFrom } from 'rxjs';
|
||||
import { BusinessListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, ListingType, ResponseBusinessListingArray, ResponseCommercialPropertyListingArray, StatesResult } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import {
|
||||
BusinessListingCriteria,
|
||||
CommercialPropertyListingCriteria,
|
||||
ListingType,
|
||||
ResponseBusinessListingArray,
|
||||
ResponseCommercialPropertyListingArray,
|
||||
StatesResult,
|
||||
} from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
@@ -12,11 +19,14 @@ export class ListingsService {
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
async getListings(criteria: ListingCriteria, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty'): Promise<ResponseBusinessListingArray | ResponseCommercialPropertyListingArray> {
|
||||
async getListings(
|
||||
criteria: BusinessListingCriteria | CommercialPropertyListingCriteria,
|
||||
listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty',
|
||||
): Promise<ResponseBusinessListingArray | ResponseCommercialPropertyListingArray> {
|
||||
const result = await lastValueFrom(this.http.post<ResponseBusinessListingArray | ResponseCommercialPropertyListingArray>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/find`, criteria));
|
||||
return result;
|
||||
}
|
||||
async getListingsByPrompt(criteria: ListingCriteria): Promise<BusinessListing[]> {
|
||||
async getListingsByPrompt(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria): Promise<BusinessListing[]> {
|
||||
const result = await lastValueFrom(this.http.post<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/business/search`, criteria));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ export class SelectOptionsService {
|
||||
getState(value: string): string {
|
||||
return this.states.find(l => l.value === value)?.name;
|
||||
}
|
||||
getBusiness(value: number): string {
|
||||
return this.typesOfBusiness.find(t => t.value === String(value))?.name;
|
||||
getBusiness(value: string): string {
|
||||
return this.typesOfBusiness.find(t => t.value === value)?.name;
|
||||
}
|
||||
getCommercialProperty(value: number): string {
|
||||
return this.typesOfCommercialProperty.find(t => t.value === String(value))?.name;
|
||||
getCommercialProperty(value: string): string {
|
||||
return this.typesOfCommercialProperty.find(t => t.value === value)?.name;
|
||||
}
|
||||
getListingsCategory(value: string): string {
|
||||
return this.listingCategories.find(l => l.value === value)?.name;
|
||||
@@ -57,14 +57,14 @@ export class SelectOptionsService {
|
||||
getIconType(value: string): string {
|
||||
return this.typesOfBusiness.find(c => c.value === value)?.icon;
|
||||
}
|
||||
getTextColorType(value: number): string {
|
||||
return this.typesOfBusiness.find(c => c.value === String(value))?.textColorClass;
|
||||
getTextColorType(value: string): string {
|
||||
return this.typesOfBusiness.find(c => c.value === value)?.textColorClass;
|
||||
}
|
||||
getIconAndTextColorType(value: number): string {
|
||||
const category = this.typesOfBusiness.find(c => c.value === String(value));
|
||||
getIconAndTextColorType(value: string): string {
|
||||
const category = this.typesOfBusiness.find(c => c.value === value);
|
||||
return `${category?.icon} ${category?.textColorClass}`;
|
||||
}
|
||||
getIconTypeOfCommercials(value: number): string {
|
||||
return this.typesOfCommercialProperty.find(c => c.value === String(value))?.icon;
|
||||
getIconTypeOfCommercials(value: string): string {
|
||||
return this.typesOfCommercialProperty.find(c => c.value === value)?.icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import urlcat from 'urlcat';
|
||||
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria, ResponseUsersArray, StatesResult } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { ResponseUsersArray, StatesResult, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
@@ -27,7 +27,7 @@ export class UserService {
|
||||
const url = urlcat(`${this.apiBaseUrl}/bizmatch/user`, { mail });
|
||||
return await lastValueFrom(this.http.get<User>(url));
|
||||
}
|
||||
async search(criteria?: ListingCriteria): Promise<ResponseUsersArray> {
|
||||
async search(criteria?: UserListingCriteria): Promise<ResponseUsersArray> {
|
||||
return await lastValueFrom(this.http.post<ResponseUsersArray>(`${this.apiBaseUrl}/bizmatch/user/search`, criteria));
|
||||
}
|
||||
async getAllStates(): Promise<any> {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Router } from '@angular/router';
|
||||
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { JwtToken, KeycloakUser, ListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, KeycloakUser, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
|
||||
export function createDefaultUser(email: string, firstname: string, lastname: string): User {
|
||||
return {
|
||||
@@ -82,21 +82,69 @@ export function createDefaultBusinessListing(): BusinessListing {
|
||||
listingsCategory: 'business',
|
||||
};
|
||||
}
|
||||
export function createDefaultListingCriteria(): ListingCriteria {
|
||||
export function createEmptyBusinessListingCriteria(): BusinessListingCriteria {
|
||||
return {
|
||||
start: 0,
|
||||
length: 12,
|
||||
length: 0,
|
||||
page: 0,
|
||||
pageCount: 0,
|
||||
type: 0,
|
||||
state: '',
|
||||
city: '',
|
||||
types: [],
|
||||
prompt: '',
|
||||
criteriaType: 'business',
|
||||
county: '',
|
||||
minPrice: null,
|
||||
maxPrice: null,
|
||||
minRevenue: null,
|
||||
maxRevenue: null,
|
||||
minCashFlow: null,
|
||||
maxCashFlow: null,
|
||||
minNumberEmployees: null,
|
||||
maxNumberEmployees: null,
|
||||
establishedSince: null,
|
||||
establishedUntil: null,
|
||||
realEstateChecked: false,
|
||||
leasedLocation: false,
|
||||
franchiseResale: false,
|
||||
title: '',
|
||||
brokerName: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function createEmptyCommercialPropertyListingCriteria(): CommercialPropertyListingCriteria {
|
||||
return {
|
||||
start: 0,
|
||||
length: 0,
|
||||
page: 0,
|
||||
pageCount: 0,
|
||||
state: '',
|
||||
city: '',
|
||||
types: [],
|
||||
prompt: '',
|
||||
criteriaType: 'commercialProperty',
|
||||
county: '',
|
||||
minPrice: 0,
|
||||
maxPrice: 0,
|
||||
realEstateChecked: false,
|
||||
title: '',
|
||||
category: 'broker',
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function createEmptyUserListingCriteria(): UserListingCriteria {
|
||||
return {
|
||||
start: 0,
|
||||
length: 0,
|
||||
page: 0,
|
||||
pageCount: 0,
|
||||
city: '',
|
||||
types: [],
|
||||
prompt: '',
|
||||
criteriaType: 'user',
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
companyName: '',
|
||||
counties: [],
|
||||
states: [],
|
||||
};
|
||||
}
|
||||
export function createLogger(name: string, level: number = INFO, options: any = {}) {
|
||||
@@ -120,8 +168,15 @@ export const getSessionStorageHandler = function (path, value, previous, applyDa
|
||||
sessionStorage.setItem('criteria', JSON.stringify(this));
|
||||
};
|
||||
|
||||
export function getCriteriaStateObject() {
|
||||
const initialState = createDefaultListingCriteria();
|
||||
export function getCriteriaStateObject(criteriaType: 'business' | 'commercialProperty' | 'user') {
|
||||
let initialState;
|
||||
if (criteriaType === 'business') {
|
||||
initialState = createEmptyBusinessListingCriteria();
|
||||
} else if (criteriaType === 'commercialProperty') {
|
||||
initialState = createEmptyCommercialPropertyListingCriteria();
|
||||
} else {
|
||||
initialState = createEmptyUserListingCriteria();
|
||||
}
|
||||
const storedState = sessionStorage.getItem('criteria');
|
||||
return storedState ? JSON.parse(storedState) : initialState;
|
||||
}
|
||||
@@ -134,17 +189,6 @@ export function routeListingWithState(router: Router, value: string, data: any)
|
||||
}
|
||||
}
|
||||
|
||||
export function resetCriteria(criteria: ListingCriteria) {
|
||||
criteria.type = null;
|
||||
criteria.state = null;
|
||||
criteria.minPrice = null;
|
||||
criteria.maxPrice = null;
|
||||
criteria.start = 0;
|
||||
criteria.length = 12;
|
||||
criteria.realEstateChecked = null;
|
||||
criteria.title = null;
|
||||
criteria.name = null;
|
||||
}
|
||||
export function map2User(jwt: string): KeycloakUser {
|
||||
if (jwt) {
|
||||
const token = jwtDecode<JwtToken>(jwt);
|
||||
|
||||
Reference in New Issue
Block a user