From 4dcff1d883c224e763c91e9c0ff0f480130e9c65 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Thu, 7 Aug 2025 16:57:51 -0500 Subject: [PATCH] #152 --- bizmatch-server/src/models/main.model.ts | 2 +- .../components/header/header.component.html | 30 ++-------- .../app/components/header/header.component.ts | 56 +++++++++++-------- .../search-modal-commercial.component.ts | 20 +++---- .../search-modal/search-modal.component.ts | 26 ++++----- bizmatch/src/app/pages/home/home.component.ts | 46 ++------------- .../broker-listings.component.ts | 17 ++++-- .../business-listings.component.ts | 19 +++++-- .../commercial-property-listings.component.ts | 26 +++++---- bizmatch/src/app/services/listings.service.ts | 26 +++++---- bizmatch/src/app/services/search.service.ts | 19 ++++++- bizmatch/src/app/utils/utils.ts | 32 +++++++---- 12 files changed, 158 insertions(+), 161 deletions(-) diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index 47de0ab..abb9c89 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -69,11 +69,11 @@ export interface ListCriteria { state: string; city: GeoResult; prompt: string; - sortBy: SortByOptions; searchType: 'exact' | 'radius'; // radius: '5' | '20' | '50' | '100' | '200' | '300' | '400' | '500'; radius: number; criteriaType: 'businessListings' | 'commercialPropertyListings' | 'brokerListings'; + sortBy?: SortByOptions; } export interface BusinessListingCriteria extends ListCriteria { minPrice: number; diff --git a/bizmatch/src/app/components/header/header.component.html b/bizmatch/src/app/components/header/header.component.html index 1a3689d..2d59435 100644 --- a/bizmatch/src/app/components/header/header.component.html +++ b/bizmatch/src/app/components/header/header.component.html @@ -6,32 +6,23 @@
@if(isFilterUrl()){ - - +
    @for(item of sortByOptions; track item){ -
  • {{ item.selectName ? item.selectName : item.name }}
  • +
  • {{ item.selectName ? item.selectName : item.name }}
  • }
@@ -217,23 +208,14 @@
- -
diff --git a/bizmatch/src/app/components/header/header.component.ts b/bizmatch/src/app/components/header/header.component.ts index 3d888d5..285a795 100644 --- a/bizmatch/src/app/components/header/header.component.ts +++ b/bizmatch/src/app/components/header/header.component.ts @@ -17,7 +17,7 @@ import { SearchService } from '../../services/search.service'; import { SelectOptionsService } from '../../services/select-options.service'; import { SharedService } from '../../services/shared.service'; import { UserService } from '../../services/user.service'; -import { assignProperties, compareObjects, createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, getCriteriaProxy, map2User } from '../../utils/utils'; +import { assignProperties, createEmptyUserListingCriteria, getCriteriaProxy, map2User } from '../../utils/utils'; import { DropdownComponent } from '../dropdown/dropdown.component'; import { ModalService } from '../search-modal/modal.service'; @UntilDestroy() @@ -49,6 +49,7 @@ export class HeaderComponent { sortByOptions: KeyValueAsSortBy[] = []; numberOfBroker$: Observable; numberOfCommercial$: Observable; + sortBy: SortByOptions = null; // Neu: Separate Property constructor( private router: Router, private userService: UserService, @@ -76,7 +77,7 @@ export class HeaderComponent { this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`; } this.numberOfBroker$ = this.userService.getNumberOfBroker(createEmptyUserListingCriteria()); - this.numberOfCommercial$ = this.listingService.getNumberOfListings(createEmptyCommercialPropertyListingCriteria(), 'commercialProperty'); + this.numberOfCommercial$ = this.listingService.getNumberOfListings('commercialProperty'); setTimeout(() => { initFlowbite(); }, 10); @@ -87,7 +88,7 @@ export class HeaderComponent { this.checkCurrentRoute(this.router.url); this.setupSortByOptions(); - + this.loadSortBy(); // Neu this.routerSubscription = this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: any) => { this.checkCurrentRoute(event.urlAfterRedirects); this.setupSortByOptions(); @@ -99,31 +100,56 @@ export class HeaderComponent { this.user = u; }); } + private loadSortBy() { + const storedSortBy = sessionStorage.getItem(this.getSortByKey()); + this.sortBy = storedSortBy ? (storedSortBy as SortByOptions) : null; + } + + private saveSortBy() { + sessionStorage.setItem(this.getSortByKey(), this.sortBy); + } + private getSortByKey(): string { + // Basierend auf Route (für Business/Commercial unterscheiden) + if (this.isBusinessListing()) return 'businessSortBy'; + if (this.isCommercialPropertyListing()) return 'commercialSortBy'; + if (this.isProfessionalListing()) return 'professionalsSortBy'; + return 'defaultSortBy'; // Fallback + } + sortByFct(selectedSortBy: SortByOptions) { + this.sortBy = selectedSortBy; + this.saveSortBy(); // Speichere separat + this.sortDropdownVisible = false; + this.searchService.search(this.criteria.criteriaType); // Neu: Übergebe sortBy separat + } private checkCurrentRoute(url: string): void { this.baseRoute = url.split('/')[1]; // Nimmt den ersten Teil der Route nach dem ersten '/' const specialRoutes = [, '', '']; this.criteria = getCriteriaProxy(this.baseRoute, this); - // this.searchService.search(this.criteria); } setupSortByOptions() { this.sortByOptions = []; + let storedSortBy = null; if (this.isProfessionalListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'professional')]; + storedSortBy = sessionStorage.getItem('professionalsSortBy'); } if (this.isBusinessListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'business' || s.type === 'listing')]; + storedSortBy = sessionStorage.getItem('businessSortBy'); } if (this.isCommercialPropertyListing()) { this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => s.type === 'commercial' || s.type === 'listing')]; + storedSortBy = sessionStorage.getItem('commercialSortBy'); } this.sortByOptions = [...this.sortByOptions, ...this.selectOptions.sortByOptions.filter(s => !s.type)]; + this.sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; } ngAfterViewInit() {} async openModal() { const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } else { this.criteria = assignProperties(this.criteria, modalResult.criteria); } @@ -147,9 +173,7 @@ export class HeaderComponent { isProfessionalListing(): boolean { return ['/brokerListings'].includes(this.router.url); } - // isSortingUrl(): boolean { - // return ['/businessListings', '/commercialPropertyListings'].includes(this.router.url); - // } + closeDropdown() { const dropdownButton = document.getElementById('user-menu-button'); const dropdownMenu = this.user ? document.getElementById('user-login') : document.getElementById('user-unknown'); @@ -180,23 +204,7 @@ export class HeaderComponent { this.destroy$.next(); this.destroy$.complete(); } - getNumberOfFiltersSet() { - if (this.criteria?.criteriaType === 'brokerListings') { - return compareObjects(createEmptyUserListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius', 'sortBy']); - } else if (this.criteria?.criteriaType === 'businessListings') { - return compareObjects(createEmptyBusinessListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius', 'sortBy']); - } else if (this.criteria?.criteriaType === 'commercialPropertyListings') { - return compareObjects(createEmptyCommercialPropertyListingCriteria(), this.criteria, ['start', 'length', 'page', 'searchType', 'radius', 'sortBy']); - } else { - return 0; - } - } - sortBy(sortBy: SortByOptions) { - this.criteria.sortBy = sortBy; - this.sortDropdownVisible = false; - this.searchService.search(this.criteria); - } toggleSortDropdown() { this.sortDropdownVisible = !this.sortDropdownVisible; } diff --git a/bizmatch/src/app/components/search-modal/search-modal-commercial.component.ts b/bizmatch/src/app/components/search-modal/search-modal-commercial.component.ts index 9ccc2b9..2a5c1f3 100644 --- a/bizmatch/src/app/components/search-modal/search-modal-commercial.component.ts +++ b/bizmatch/src/app/components/search-modal/search-modal-commercial.component.ts @@ -95,11 +95,11 @@ export class SearchModalCommercialComponent { this.criteria.title = null; break; } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } clearFilter() { resetCommercialPropertyListingCriteria(this.criteria); - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } // Handle category change onCategoryChange(event: any[]) { @@ -116,7 +116,7 @@ export class SearchModalCommercialComponent { this.criteria.types.splice(index, 1); } } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } private loadCounties() { this.counties$ = concat( @@ -135,7 +135,7 @@ export class SearchModalCommercialComponent { ); } onCriteriaChange() { - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setCity(city) { if (city) { @@ -146,7 +146,7 @@ export class SearchModalCommercialComponent { this.criteria.radius = null; this.criteria.searchType = 'exact'; } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setState(state: string) { if (state) { @@ -155,11 +155,11 @@ export class SearchModalCommercialComponent { this.criteria.state = null; this.setCity(null); } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setRadius(radius: number) { this.criteria.radius = radius; - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } private setupCriteriaChangeListener() { this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => { @@ -181,14 +181,14 @@ export class SearchModalCommercialComponent { } closeAndSearch() { this.modalService.accept(); - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); this.close(); } setTotalNumberOfResults() { if (this.criteria) { console.log(`Getting total number of results for ${this.criteria.criteriaType}`); if (this.criteria.criteriaType === 'commercialPropertyListings') { - this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, 'commercialProperty'); + this.numberOfResults$ = this.listingService.getNumberOfListings('commercialProperty'); } else { this.numberOfResults$ = of(); } @@ -202,7 +202,7 @@ export class SearchModalCommercialComponent { debouncedSearch() { clearTimeout(this.debounceTimeout); this.debounceTimeout = setTimeout(() => { - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); }, 1000); } } diff --git a/bizmatch/src/app/components/search-modal/search-modal.component.ts b/bizmatch/src/app/components/search-modal/search-modal.component.ts index ac9496f..3ef04f5 100644 --- a/bizmatch/src/app/components/search-modal/search-modal.component.ts +++ b/bizmatch/src/app/components/search-modal/search-modal.component.ts @@ -140,12 +140,12 @@ export class SearchModalComponent { this.criteria.title = null; break; } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } // Handle category change onCategoryChange(selectedCategories: string[]) { this.criteria.types = selectedCategories; - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } // Handle property type change @@ -159,7 +159,7 @@ export class SearchModalComponent { this.criteria[value] = true; } this.selectedPropertyType = value; - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } // Update selected property type based on current criteria @@ -181,7 +181,7 @@ export class SearchModalComponent { this.criteria.types.splice(index, 1); } } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } private loadCounties() { this.counties$ = concat( @@ -200,7 +200,7 @@ export class SearchModalComponent { ); } onCriteriaChange() { - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setCity(city) { if (city) { @@ -211,7 +211,7 @@ export class SearchModalComponent { this.criteria.radius = null; this.criteria.searchType = 'exact'; } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setState(state: string) { if (state) { @@ -220,11 +220,11 @@ export class SearchModalComponent { this.criteria.state = null; this.setCity(null); } - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } setRadius(radius: number) { this.criteria.radius = radius; - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } private setupCriteriaChangeListener() { this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => { @@ -246,7 +246,7 @@ export class SearchModalComponent { } closeAndSearch() { this.modalService.accept(); - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); this.close(); } isTypeOfBusinessClicked(v: KeyValueStyle) { @@ -259,7 +259,7 @@ export class SearchModalComponent { if (this.criteria) { console.log(`Getting total number of results for ${this.criteria.criteriaType}`); if (this.criteria.criteriaType === 'businessListings' || this.criteria.criteriaType === 'commercialPropertyListings') { - this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty'); + this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty'); } else if (this.criteria.criteriaType === 'brokerListings') { //this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria); } else { @@ -270,7 +270,7 @@ export class SearchModalComponent { clearFilter() { resetBusinessListingCriteria(this.criteria); - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } close() { this.modalService.reject(this.backupCriteria); @@ -282,12 +282,12 @@ export class SearchModalComponent { // Aktivieren Sie nur die aktuell ausgewählte Checkbox this.criteria[checkbox] = value; - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); } debouncedSearch() { clearTimeout(this.debounceTimeout); this.debounceTimeout = setTimeout(() => { - this.searchService.search(this.criteria); + this.searchService.search(this.criteria.criteriaType); }, 1000); } } diff --git a/bizmatch/src/app/pages/home/home.component.ts b/bizmatch/src/app/pages/home/home.component.ts index 15b92cd..dbd5da4 100644 --- a/bizmatch/src/app/pages/home/home.component.ts +++ b/bizmatch/src/app/pages/home/home.component.ts @@ -5,7 +5,7 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { NgSelectModule } from '@ng-select/ng-select'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { initFlowbite } from 'flowbite'; -import { catchError, concat, debounceTime, distinctUntilChanged, lastValueFrom, Observable, of, Subject, Subscription, switchMap, tap } from 'rxjs'; +import { catchError, concat, debounceTime, distinctUntilChanged, Observable, of, Subject, Subscription, switchMap, tap } from 'rxjs'; import { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingCriteria, GeoResult, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; import { ModalService } from '../../components/search-modal/modal.service'; import { TooltipComponent } from '../../components/tooltip/tooltip.component'; @@ -18,7 +18,6 @@ import { SearchService } from '../../services/search.service'; import { SelectOptionsService } from '../../services/select-options.service'; import { UserService } from '../../services/user.service'; import { - assignProperties, compareObjects, createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, @@ -26,6 +25,7 @@ import { createEnhancedProxy, getCriteriaStateObject, map2User, + removeSortByStorage, } from '../../utils/utils'; @UntilDestroy() @Component({ @@ -86,12 +86,13 @@ export class HomeComponent { initFlowbite(); }, 0); this.numberOfBroker$ = this.userService.getNumberOfBroker(createEmptyUserListingCriteria()); - this.numberOfCommercial$ = this.listingService.getNumberOfListings(createEmptyCommercialPropertyListingCriteria(), 'commercialProperty'); + this.numberOfCommercial$ = this.listingService.getNumberOfListings('commercialProperty'); const token = await this.authService.getToken(); sessionStorage.removeItem('businessListings'); sessionStorage.removeItem('commercialPropertyListings'); sessionStorage.removeItem('brokerListings'); this.criteria = createEnhancedProxy(getCriteriaStateObject('businessListings'), this); + removeSortByStorage(); this.user = map2User(token); this.loadCities(); this.setupCriteriaChangeListener(); @@ -201,7 +202,7 @@ export class HomeComponent { if (this.criteria) { console.log(`Getting total number of results for ${this.criteria.criteriaType}`); if (this.criteria.criteriaType === 'businessListings' || this.criteria.criteriaType === 'commercialPropertyListings') { - this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria, this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty'); + this.numberOfResults$ = this.listingService.getNumberOfListings(this.criteria.criteriaType === 'businessListings' ? 'business' : 'commercialProperty'); } else if (this.criteria.criteriaType === 'brokerListings') { this.numberOfResults$ = this.userService.getNumberOfBroker(this.criteria); } else { @@ -273,41 +274,4 @@ export class HomeComponent { }, this.pauseTime); } } - async generateAiResponse() { - this.loadingAi = true; - this.aiSearchFailed = false; - try { - const result = await this.aiService.generateAiReponse(this.aiSearchText); - let criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria | any; - if (result.criteriaType === 'businessListings') { - this.changeTab('business'); - criteria = result as BusinessListingCriteria; - } else if (result.criteriaType === 'commercialPropertyListings') { - this.changeTab('commercialProperty'); - criteria = result as CommercialPropertyListingCriteria; - } else { - this.changeTab('broker'); - criteria = result as UserListingCriteria; - } - const city = criteria.city as string; - if (city && city.length > 0) { - let results = await lastValueFrom(this.geoService.findCitiesStartingWith(city, criteria.state)); - if (results.length > 0) { - criteria.city = results[0]; - } else { - criteria.city = null; - } - } - if (criteria.radius && criteria.radius.length > 0) { - criteria.radius = parseInt(criteria.radius); - } - this.loadingAi = false; - this.criteria = assignProperties(this.criteria, criteria); - this.search(); - } catch (error) { - console.log(error); - this.aiSearchFailed = true; - this.loadingAi = false; - } - } } diff --git a/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts b/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts index 767ff98..fb58fd5 100644 --- a/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts +++ b/bizmatch/src/app/pages/listings/broker-listings/broker-listings.component.ts @@ -3,7 +3,7 @@ import { ChangeDetectorRef, Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; +import { BusinessListing, SortByOptions, User } from '../../../../../../bizmatch-server/src/models/db.model'; import { LISTINGS_PER_PAGE, ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { CustomerSubTypeComponent } from '../../../components/customer-sub-type/customer-sub-type.component'; @@ -45,6 +45,7 @@ export class BrokerListingsComponent { emailToDirName = emailToDirName; page = 1; pageCount = 1; + sortBy: SortByOptions = null; // Neu: Separate Property constructor( public selectOptions: SelectOptionsService, private listingsService: ListingsService, @@ -60,13 +61,19 @@ export class BrokerListingsComponent { ) { this.criteria = getCriteriaProxy('brokerListings', this) as UserListingCriteria; this.init(); - this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { - if (criteria && criteria.criteriaType === 'brokerListings') { + this.loadSortBy(); + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(({ criteria, sortBy }) => { + if (criteria.criteriaType === 'brokerListings') { this.criteria = criteria as UserListingCriteria; + this.sortBy = sortBy || this.sortBy || null; this.search(); } }); } + private loadSortBy() { + const storedSortBy = sessionStorage.getItem('professionalsSortBy'); + this.sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; + } async ngOnInit() {} async init() { this.search(); @@ -101,14 +108,14 @@ export class BrokerListingsComponent { this.criteriaChangeService.notifyCriteriaChange(); // Search with cleared filters - this.searchService.search(this.criteria); + this.searchService.search('brokerListings'); } async openFilterModal() { // Open the search modal with current criteria const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { - this.searchService.search(this.criteria); + this.searchService.search('brokerListings'); } else { this.criteria = assignProperties(this.criteria, modalResult.criteria); } diff --git a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts index 0a740b9..6f1fed2 100644 --- a/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts +++ b/bizmatch/src/app/pages/listings/business-listings/business-listings.component.ts @@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import dayjs from 'dayjs'; -import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model'; +import { BusinessListing, SortByOptions } from '../../../../../../bizmatch-server/src/models/db.model'; import { BusinessListingCriteria, LISTINGS_PER_PAGE, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { PaginatorComponent } from '../../../components/paginator/paginator.component'; @@ -43,6 +43,7 @@ export class BusinessListingsComponent { page = 1; pageCount = 1; emailToDirName = emailToDirName; + sortBy: SortByOptions = null; // Neu: Separate Property constructor( public selectOptions: SelectOptionsService, private listingsService: ListingsService, @@ -58,13 +59,19 @@ export class BusinessListingsComponent { this.criteria = getCriteriaProxy('businessListings', this) as BusinessListingCriteria; this.modalService.sendCriteria(this.criteria); this.init(); - this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { - if (criteria && criteria.criteriaType === 'businessListings') { + this.loadSortBy(); + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(({ criteria, sortBy }) => { + if (criteria.criteriaType === 'businessListings') { this.criteria = criteria as BusinessListingCriteria; + this.sortBy = sortBy || this.sortBy || null; this.search(); } }); } + private loadSortBy() { + const storedSortBy = sessionStorage.getItem('businessSortBy'); + this.sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; + } async ngOnInit() { this.search(); } @@ -73,7 +80,7 @@ export class BusinessListingsComponent { } async search() { - const listingReponse = await this.listingsService.getListings(this.criteria, 'business'); + const listingReponse = await this.listingsService.getListings('business'); this.listings = listingReponse.results; this.totalRecords = listingReponse.totalCount; this.pageCount = this.totalRecords % LISTINGS_PER_PAGE === 0 ? this.totalRecords / LISTINGS_PER_PAGE : Math.floor(this.totalRecords / LISTINGS_PER_PAGE) + 1; @@ -106,7 +113,7 @@ export class BusinessListingsComponent { this.criteriaChangeService.notifyCriteriaChange(); // Search with cleared filters - this.searchService.search(this.criteria); + this.searchService.search('businessListings'); } async openFilterModal() { @@ -114,7 +121,7 @@ export class BusinessListingsComponent { const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { this.criteria = assignProperties(this.criteria, modalResult.criteria); // Update criteria with modal result - this.searchService.search(this.criteria); // Trigger search with updated criteria + this.searchService.search('businessListings'); // Trigger search with updated criteria } } } diff --git a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts index c974f5f..d7ddf8b 100644 --- a/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts +++ b/bizmatch/src/app/pages/listings/commercial-property-listings/commercial-property-listings.component.ts @@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import dayjs from 'dayjs'; -import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model'; +import { CommercialPropertyListing, SortByOptions } from '../../../../../../bizmatch-server/src/models/db.model'; import { CommercialPropertyListingCriteria, LISTINGS_PER_PAGE, ResponseCommercialPropertyListingArray } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { PaginatorComponent } from '../../../components/paginator/paginator.component'; @@ -43,7 +43,7 @@ export class CommercialPropertyListingsComponent { page = 1; pageCount = 1; ts = new Date().getTime(); - + sortBy: SortByOptions = null; // Neu: Separate Property constructor( public selectOptions: SelectOptionsService, private listingsService: ListingsService, @@ -58,23 +58,25 @@ export class CommercialPropertyListingsComponent { ) { this.criteria = getCriteriaProxy('commercialPropertyListings', this) as CommercialPropertyListingCriteria; this.modalService.sendCriteria(this.criteria); - this.init(); - this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(criteria => { - if (criteria && criteria.criteriaType === 'commercialPropertyListings') { + this.loadSortBy(); + this.searchService.currentCriteria.pipe(untilDestroyed(this)).subscribe(({ criteria, sortBy }) => { + if (criteria.criteriaType === 'commercialPropertyListings') { this.criteria = criteria as CommercialPropertyListingCriteria; + this.sortBy = sortBy || this.sortBy || null; this.search(); } }); } - - async ngOnInit() {} - - async init() { + private loadSortBy() { + const storedSortBy = sessionStorage.getItem('commercialSortBy'); + this.sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; + } + async ngOnInit() { this.search(); } async search() { - const listingReponse = await this.listingsService.getListings(this.criteria, 'commercialProperty'); + const listingReponse = await this.listingsService.getListings('commercialProperty'); this.listings = (listingReponse).results; this.totalRecords = (listingReponse).totalCount; this.pageCount = this.totalRecords % LISTINGS_PER_PAGE === 0 ? this.totalRecords / LISTINGS_PER_PAGE : Math.floor(this.totalRecords / LISTINGS_PER_PAGE) + 1; @@ -114,14 +116,14 @@ export class CommercialPropertyListingsComponent { this.criteriaChangeService.notifyCriteriaChange(); // Search with cleared filters - this.searchService.search(this.criteria); + this.searchService.search('commercialPropertyListings'); } async openFilterModal() { const modalResult = await this.modalService.showModal(this.criteria); if (modalResult.accepted) { this.criteria = assignProperties(this.criteria, modalResult.criteria); // Update criteria with modal result - this.searchService.search(this.criteria); // Trigger search with updated criteria + this.searchService.search('commercialPropertyListings'); // Trigger search with updated criteria } } } diff --git a/bizmatch/src/app/services/listings.service.ts b/bizmatch/src/app/services/listings.service.ts index 5a973d5..06bcc02 100644 --- a/bizmatch/src/app/services/listings.service.ts +++ b/bizmatch/src/app/services/listings.service.ts @@ -2,8 +2,9 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, lastValueFrom } from 'rxjs'; import { BusinessListing, CommercialPropertyListing } from '../../../../bizmatch-server/src/models/db.model'; -import { BusinessListingCriteria, CommercialPropertyListingCriteria, ListingType, ResponseBusinessListingArray, ResponseCommercialPropertyListingArray } from '../../../../bizmatch-server/src/models/main.model'; +import { ListingType, ResponseBusinessListingArray, ResponseCommercialPropertyListingArray } from '../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../environments/environment'; +import { getCriteriaByListingCategory, getSortByListingCategory } from '../utils/utils'; @Injectable({ providedIn: 'root', @@ -12,20 +13,21 @@ export class ListingsService { private apiBaseUrl = environment.apiBaseUrl; constructor(private http: HttpClient) {} - async getListings( - criteria: BusinessListingCriteria | CommercialPropertyListingCriteria, - listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty', - ): Promise { - const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/find`, criteria)); + async getListings(listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty'): Promise { + const criteria = getCriteriaByListingCategory(listingsCategory); + const sortBy = getSortByListingCategory(listingsCategory); + const body = { ...criteria, sortBy }; // Merge sortBy in Body + const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/find`, body)); return result; } - getNumberOfListings(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria, listingsCategory: 'business' | 'commercialProperty'): Observable { - return this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/findTotal`, criteria); - } - async getListingsByPrompt(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria): Promise { - const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/business/search`, criteria)); - return result; + + getNumberOfListings(listingsCategory: 'business' | 'commercialProperty'): Observable { + const criteria = getCriteriaByListingCategory(listingsCategory); + const sortBy = getSortByListingCategory(listingsCategory); + const body = { ...criteria, sortBy }; // Merge, falls relevant (wenn Backend sortBy für Count braucht; sonst ignorieren) + return this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/findTotal`, body); } + getListingById(id: string, listingsCategory?: 'business' | 'commercialProperty'): Observable { const result = this.http.get(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`); return result; diff --git a/bizmatch/src/app/services/search.service.ts b/bizmatch/src/app/services/search.service.ts index 2183cbf..1d6e1e0 100644 --- a/bizmatch/src/app/services/search.service.ts +++ b/bizmatch/src/app/services/search.service.ts @@ -1,17 +1,30 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; +import { SortByOptions } from '../../../../bizmatch-server/src/models/db.model'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; +import { getCriteriaProxy } from '../utils/utils'; @Injectable({ providedIn: 'root', }) export class SearchService { - private criteriaSource = new Subject(); + private criteriaSource = new Subject<{ + criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria; + sortBy?: SortByOptions; + }>(); currentCriteria = this.criteriaSource.asObservable(); constructor() {} - search(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): void { - this.criteriaSource.next(criteria); + search(criteriaType: 'businessListings' | 'commercialPropertyListings' | 'brokerListings'): void { + const criteria = getCriteriaProxy(criteriaType, this); + const storedSortBy = + criteriaType === 'businessListings' + ? sessionStorage.getItem('businessSortBy') + : criteriaType === 'commercialPropertyListings' + ? sessionStorage.getItem('commercialSortBy') + : sessionStorage.getItem('professionalsSortBy'); + const sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; + this.criteriaSource.next({ criteria, sortBy }); } } diff --git a/bizmatch/src/app/utils/utils.ts b/bizmatch/src/app/utils/utils.ts index 9f6daa0..9279fb3 100644 --- a/bizmatch/src/app/utils/utils.ts +++ b/bizmatch/src/app/utils/utils.ts @@ -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 onChange from 'on-change'; -import { User } from '../../../../bizmatch-server/src/models/db.model'; +import { SortByOptions, User } from '../../../../bizmatch-server/src/models/db.model'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, KeycloakUser, MailInfo, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../environments/environment'; @@ -15,7 +15,6 @@ export function createEmptyBusinessListingCriteria(): BusinessListingCriteria { city: null, types: [], prompt: '', - sortBy: null, criteriaType: 'businessListings', minPrice: null, maxPrice: null, @@ -46,7 +45,6 @@ export function createEmptyCommercialPropertyListingCriteria(): CommercialProper city: null, types: [], prompt: '', - sortBy: null, criteriaType: 'commercialPropertyListings', minPrice: null, maxPrice: null, @@ -64,7 +62,6 @@ export function createEmptyUserListingCriteria(): UserListingCriteria { city: null, types: [], prompt: '', - sortBy: null, criteriaType: 'brokerListings', brokerName: '', companyName: '', @@ -82,7 +79,6 @@ export function resetBusinessListingCriteria(criteria: BusinessListingCriteria) criteria.city = null; criteria.types = []; criteria.prompt = ''; - criteria.sortBy = null; criteria.criteriaType = 'businessListings'; criteria.minPrice = null; criteria.maxPrice = null; @@ -110,7 +106,6 @@ export function resetCommercialPropertyListingCriteria(criteria: CommercialPrope criteria.city = null; criteria.types = []; criteria.prompt = ''; - criteria.sortBy = null; criteria.criteriaType = 'commercialPropertyListings'; criteria.minPrice = null; criteria.maxPrice = null; @@ -126,7 +121,6 @@ export function resetUserListingCriteria(criteria: UserListingCriteria) { criteria.city = null; criteria.types = []; criteria.prompt = ''; - criteria.sortBy = null; criteria.criteriaType = 'brokerListings'; criteria.brokerName = ''; criteria.companyName = ''; @@ -300,6 +294,11 @@ export function checkAndUpdate(changed: boolean, condition: boolean, assignment: } return changed || condition; } +export function removeSortByStorage() { + sessionStorage.removeItem('businessSortBy'); + sessionStorage.removeItem('commercialSortBy'); + sessionStorage.removeItem('professionalsSortBy'); +} // ----------------------------- // Criteria Proxy // ----------------------------- @@ -341,6 +340,19 @@ export function createEnhancedProxy(obj: BusinessListingCriteria | CommercialPro } }); } -// export function isAdmin(email: string) { -// return 'andreas.knuth@gmail.com' === email; -// } +export function getCriteriaByListingCategory(listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') { + const storedState = + listingsCategory === 'business' + ? sessionStorage.getItem('businessListings') + : listingsCategory === 'commercialProperty' + ? sessionStorage.getItem('commercialPropertyListings') + : sessionStorage.getItem('brokerListings'); + return JSON.parse(storedState); +} + +export function getSortByListingCategory(listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') { + const storedSortBy = + listingsCategory === 'business' ? sessionStorage.getItem('businessSortBy') : listingsCategory === 'commercialProperty' ? sessionStorage.getItem('commercialSortBy') : sessionStorage.getItem('professionalsSortBy'); + const sortBy = storedSortBy && storedSortBy !== 'null' ? (storedSortBy as SortByOptions) : null; + return sortBy; +}