not-found page, #85, client logging
This commit is contained in:
parent
1f8febc479
commit
a8bb163acf
|
|
@ -13,6 +13,9 @@ import { FileService } from './file/file.service.js';
|
||||||
import { GeoModule } from './geo/geo.module.js';
|
import { GeoModule } from './geo/geo.module.js';
|
||||||
import { ImageModule } from './image/image.module.js';
|
import { ImageModule } from './image/image.module.js';
|
||||||
import { ListingsModule } from './listings/listings.module.js';
|
import { ListingsModule } from './listings/listings.module.js';
|
||||||
|
import { LogController } from './log/log.controller.js';
|
||||||
|
import { LogModule } from './log/log.module.js';
|
||||||
|
|
||||||
import { MailModule } from './mail/mail.module.js';
|
import { MailModule } from './mail/mail.module.js';
|
||||||
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware.js';
|
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware.js';
|
||||||
import { SelectOptionsModule } from './select-options/select-options.module.js';
|
import { SelectOptionsModule } from './select-options/select-options.module.js';
|
||||||
|
|
@ -75,8 +78,9 @@ loadEnvFiles();
|
||||||
ImageModule,
|
ImageModule,
|
||||||
PassportModule,
|
PassportModule,
|
||||||
AiModule,
|
AiModule,
|
||||||
|
LogModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController, LogController],
|
||||||
providers: [AppService, FileService],
|
providers: [AppService, FileService],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Body, Controller, Inject, Post, Request, UseGuards } from '@nestjs/common';
|
||||||
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
|
import { Logger } from 'winston';
|
||||||
|
import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js';
|
||||||
|
import { LogMessage } from '../models/main.model.js';
|
||||||
|
@Controller('log')
|
||||||
|
export class LogController {
|
||||||
|
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
||||||
|
|
||||||
|
@UseGuards(OptionalJwtAuthGuard)
|
||||||
|
@Post()
|
||||||
|
log(@Request() req, @Body() message: LogMessage) {
|
||||||
|
if (message.severity === 'info') {
|
||||||
|
this.logger.info(message.text);
|
||||||
|
} else {
|
||||||
|
this.logger.error(message.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { LogController } from './log.controller.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [LogController],
|
||||||
|
})
|
||||||
|
export class LogModule {}
|
||||||
|
|
@ -247,6 +247,14 @@ export interface CountyResult {
|
||||||
state: string;
|
state: string;
|
||||||
state_code: string;
|
state_code: string;
|
||||||
}
|
}
|
||||||
|
export interface LogMessage {
|
||||||
|
severity: 'error' | 'info';
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
export interface ModalResult {
|
||||||
|
accepted: boolean;
|
||||||
|
criteria?: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
||||||
|
}
|
||||||
export function isEmpty(value: any): boolean {
|
export function isEmpty(value: any): boolean {
|
||||||
// Check for undefined or null
|
// Check for undefined or null
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ export const routes: Routes = [
|
||||||
canActivate: [ListingCategoryGuard],
|
canActivate: [ListingCategoryGuard],
|
||||||
component: NotFoundComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
|
component: NotFoundComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'notfound',
|
||||||
|
component: NotFoundComponent,
|
||||||
|
},
|
||||||
// #########
|
// #########
|
||||||
// User Details
|
// User Details
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { CriteriaChangeService } from '../../services/criteria-change.service';
|
||||||
import { SearchService } from '../../services/search.service';
|
import { SearchService } from '../../services/search.service';
|
||||||
import { SharedService } from '../../services/shared.service';
|
import { SharedService } from '../../services/shared.service';
|
||||||
import { UserService } from '../../services/user.service';
|
import { UserService } from '../../services/user.service';
|
||||||
import { compareObjects, createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, getCriteriaProxy, map2User } from '../../utils/utils';
|
import { assignProperties, compareObjects, createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, getCriteriaProxy, map2User } from '../../utils/utils';
|
||||||
import { DropdownComponent } from '../dropdown/dropdown.component';
|
import { DropdownComponent } from '../dropdown/dropdown.component';
|
||||||
import { ModalService } from '../search-modal/modal.service';
|
import { ModalService } from '../search-modal/modal.service';
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -86,9 +86,11 @@ export class HeaderComponent {
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {}
|
||||||
|
|
||||||
async openModal() {
|
async openModal() {
|
||||||
const accepted = await this.modalService.showModal(this.criteria);
|
const modalResult = await this.modalService.showModal(this.criteria);
|
||||||
if (accepted) {
|
if (modalResult.accepted) {
|
||||||
this.searchService.search(this.criteria);
|
this.searchService.search(this.criteria);
|
||||||
|
} else {
|
||||||
|
this.criteria = assignProperties(this.criteria, modalResult.criteria);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
navigateWithState(dest: string, state: any) {
|
navigateWithState(dest: string, state: any) {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,35 @@
|
||||||
<p>not-found works!</p>
|
<!-- <section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-sm text-center">
|
||||||
|
<h1 class="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500">404</h1>
|
||||||
|
<p class="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Something's missing.</p>
|
||||||
|
<p class="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">Sorry, we can't find that page.</p>
|
||||||
|
<a
|
||||||
|
routerLink="/home"
|
||||||
|
class="inline-flex text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900 my-4"
|
||||||
|
>Back to Homepage</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section> -->
|
||||||
|
<section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-sm text-center">
|
||||||
|
<h1 class="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-blue-700 dark:text-blue-500">404</h1>
|
||||||
|
<p class="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Something's missing.</p>
|
||||||
|
<p class="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">Sorry, we can't find that page</p>
|
||||||
|
<!-- <a
|
||||||
|
href="#"
|
||||||
|
class="inline-flex text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900 my-4"
|
||||||
|
>Back to Homepage</a
|
||||||
|
> -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
[routerLink]="['/home']"
|
||||||
|
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
|
||||||
|
>
|
||||||
|
Back to Homepage
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-not-found',
|
selector: 'app-not-found',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
template: '<h2>Page not found</h2>',
|
imports: [CommonModule, RouterModule],
|
||||||
|
templateUrl: './not-found.component.html',
|
||||||
})
|
})
|
||||||
export class NotFoundComponent {}
|
export class NotFoundComponent {}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// 1. Shared Service (modal.service.ts)
|
// 1. Shared Service (modal.service.ts)
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
import { BusinessListingCriteria, CommercialPropertyListingCriteria, ModalResult, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
|
@ -9,26 +9,26 @@ import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListing
|
||||||
export class ModalService {
|
export class ModalService {
|
||||||
private modalVisibleSubject = new BehaviorSubject<boolean>(false);
|
private modalVisibleSubject = new BehaviorSubject<boolean>(false);
|
||||||
private messageSubject = new BehaviorSubject<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria>(null);
|
private messageSubject = new BehaviorSubject<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria>(null);
|
||||||
private resolvePromise!: (value: boolean) => void;
|
private resolvePromise!: (value: ModalResult) => void;
|
||||||
|
|
||||||
modalVisible$: Observable<boolean> = this.modalVisibleSubject.asObservable();
|
modalVisible$: Observable<boolean> = this.modalVisibleSubject.asObservable();
|
||||||
message$: Observable<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria> = this.messageSubject.asObservable();
|
message$: Observable<BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria> = this.messageSubject.asObservable();
|
||||||
|
|
||||||
showModal(message: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): Promise<boolean> {
|
showModal(message: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): Promise<ModalResult> {
|
||||||
this.messageSubject.next(message);
|
this.messageSubject.next(message);
|
||||||
this.modalVisibleSubject.next(true);
|
this.modalVisibleSubject.next(true);
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<ModalResult>(resolve => {
|
||||||
this.resolvePromise = resolve;
|
this.resolvePromise = resolve;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
accept(): void {
|
accept(): void {
|
||||||
this.modalVisibleSubject.next(false);
|
this.modalVisibleSubject.next(false);
|
||||||
this.resolvePromise(true);
|
this.resolvePromise({ accepted: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(): void {
|
reject(backupCriteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): void {
|
||||||
this.modalVisibleSubject.next(false);
|
this.modalVisibleSubject.next(false);
|
||||||
this.resolvePromise(false);
|
this.resolvePromise({ accepted: false, criteria: backupCriteria });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
} @else {
|
} @else {
|
||||||
<h3 class="text-xl font-semibold text-gray-900">Professional Listing Search</h3>
|
<h3 class="text-xl font-semibold text-gray-900">Professional 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">
|
<button (click)="close()" 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">
|
<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" />
|
<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>
|
</svg>
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
<div class="flex space-x-4 mb-4">
|
<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-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>
|
<button class="text-gray-500">AI Search <span class="bg-gray-200 text-xs font-semibold px-2 py-1 rounded">BETA</span></button>
|
||||||
|
<i data-tooltip-target="tooltip-light" class="fa-solid fa-trash-can flex self-center ml-2 hover:cursor-pointer text-blue-500" (click)="clearFilter()"></i>
|
||||||
|
<div id="tooltip-light" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg shadow-sm opacity-0 tooltip">
|
||||||
|
Clear all Filter
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if(criteria.criteriaType==='businessListings'){
|
@if(criteria.criteriaType==='businessListings'){
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
|
@ -470,7 +475,7 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
(click)="modalService.reject()"
|
(click)="close()"
|
||||||
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"
|
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
|
Cancel
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { ListingsService } from '../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../services/select-options.service';
|
import { SelectOptionsService } from '../../services/select-options.service';
|
||||||
import { UserService } from '../../services/user.service';
|
import { UserService } from '../../services/user.service';
|
||||||
import { SharedModule } from '../../shared/shared/shared.module';
|
import { SharedModule } from '../../shared/shared/shared.module';
|
||||||
|
import { resetBusinessListingCriteria, resetCommercialPropertyListingCriteria, resetUserListingCriteria } from '../../utils/utils';
|
||||||
import { ValidatedCityComponent } from '../validated-city/validated-city.component';
|
import { ValidatedCityComponent } from '../validated-city/validated-city.component';
|
||||||
import { ModalService } from './modal.service';
|
import { ModalService } from './modal.service';
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
|
|
@ -29,7 +30,9 @@ export class SearchModalComponent {
|
||||||
countyInput$ = new Subject<string>();
|
countyInput$ = new Subject<string>();
|
||||||
private criteriaChangeSubscription: Subscription;
|
private criteriaChangeSubscription: Subscription;
|
||||||
public criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
public criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
||||||
|
backupCriteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria;
|
||||||
numberOfResults$: Observable<number>;
|
numberOfResults$: Observable<number>;
|
||||||
|
cancelDisable = false;
|
||||||
constructor(
|
constructor(
|
||||||
public selectOptions: SelectOptionsService,
|
public selectOptions: SelectOptionsService,
|
||||||
public modalService: ModalService,
|
public modalService: ModalService,
|
||||||
|
|
@ -42,6 +45,7 @@ export class SearchModalComponent {
|
||||||
this.setupCriteriaChangeListener();
|
this.setupCriteriaChangeListener();
|
||||||
this.modalService.message$.pipe(untilDestroyed(this)).subscribe(msg => {
|
this.modalService.message$.pipe(untilDestroyed(this)).subscribe(msg => {
|
||||||
this.criteria = msg;
|
this.criteria = msg;
|
||||||
|
this.backupCriteria = JSON.parse(JSON.stringify(msg));
|
||||||
this.setTotalNumberOfResults();
|
this.setTotalNumberOfResults();
|
||||||
});
|
});
|
||||||
this.modalService.modalVisible$.pipe(untilDestroyed(this)).subscribe(val => {
|
this.modalService.modalVisible$.pipe(untilDestroyed(this)).subscribe(val => {
|
||||||
|
|
@ -54,7 +58,9 @@ export class SearchModalComponent {
|
||||||
this.loadCounties();
|
this.loadCounties();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {}
|
ngOnChanges() {
|
||||||
|
console.log('sdf');
|
||||||
|
}
|
||||||
categoryClicked(checked: boolean, value: string) {
|
categoryClicked(checked: boolean, value: string) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.criteria.types.push(value);
|
this.criteria.types.push(value);
|
||||||
|
|
@ -116,7 +122,10 @@ export class SearchModalComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private setupCriteriaChangeListener() {
|
private setupCriteriaChangeListener() {
|
||||||
this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => this.setTotalNumberOfResults());
|
this.criteriaChangeSubscription = this.criteriaChangeService.criteriaChange$.pipe(debounceTime(400)).subscribe(() => {
|
||||||
|
this.setTotalNumberOfResults();
|
||||||
|
this.cancelDisable = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
trackByFn(item: GeoResult) {
|
trackByFn(item: GeoResult) {
|
||||||
return item.id;
|
return item.id;
|
||||||
|
|
@ -148,4 +157,16 @@ export class SearchModalComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clearFilter() {
|
||||||
|
if (this.criteria.criteriaType === 'businessListings') {
|
||||||
|
resetBusinessListingCriteria(this.criteria);
|
||||||
|
} else if (this.criteria.criteriaType === 'commercialPropertyListings') {
|
||||||
|
resetCommercialPropertyListingCriteria(this.criteria);
|
||||||
|
} else {
|
||||||
|
resetUserListingCriteria(this.criteria);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close() {
|
||||||
|
this.modalService.reject(this.backupCriteria);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { ValidatedTextareaComponent } from '../../../components/validated-textar
|
||||||
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
||||||
import { HistoryService } from '../../../services/history.service';
|
import { HistoryService } from '../../../services/history.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { LogService } from '../../../services/log.service';
|
||||||
import { MailService } from '../../../services/mail.service';
|
import { MailService } from '../../../services/mail.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
|
|
@ -67,6 +68,7 @@ export class DetailsBusinessListingComponent {
|
||||||
public keycloakService: KeycloakService,
|
public keycloakService: KeycloakService,
|
||||||
private validationMessagesService: ValidationMessagesService,
|
private validationMessagesService: ValidationMessagesService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
|
private logService: LogService,
|
||||||
) {
|
) {
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
|
|
@ -88,7 +90,8 @@ export class DetailsBusinessListingComponent {
|
||||||
this.listingUser = await this.userService.getByMail(this.listing.email);
|
this.listingUser = await this.userService.getByMail(this.listing.email);
|
||||||
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
this.logService.log({ severity: 'error', text: error.error.message });
|
||||||
|
this.router.navigate(['notfound']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { ValidationMessagesService } from '../../../components/validation-messag
|
||||||
import { HistoryService } from '../../../services/history.service';
|
import { HistoryService } from '../../../services/history.service';
|
||||||
import { ImageService } from '../../../services/image.service';
|
import { ImageService } from '../../../services/image.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { LogService } from '../../../services/log.service';
|
||||||
import { MailService } from '../../../services/mail.service';
|
import { MailService } from '../../../services/mail.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
|
|
@ -75,6 +76,7 @@ export class DetailsCommercialPropertyListingComponent {
|
||||||
private ngZone: NgZone,
|
private ngZone: NgZone,
|
||||||
private validationMessagesService: ValidationMessagesService,
|
private validationMessagesService: ValidationMessagesService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
|
private logService: LogService,
|
||||||
) {
|
) {
|
||||||
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
|
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
|
||||||
|
|
||||||
|
|
@ -105,7 +107,8 @@ export class DetailsCommercialPropertyListingComponent {
|
||||||
this.propertyDetails.push({ label: 'Draft', value: this.listing.draft ? 'Yes' : 'No' });
|
this.propertyDetails.push({ label: 'Draft', value: this.listing.draft ? 'Yes' : 'No' });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
this.logService.log({ severity: 'error', text: error.error.message });
|
||||||
|
this.router.navigate(['notfound']);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.initFlowbite();
|
//this.initFlowbite();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
import { LogMessage } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class LogService {
|
||||||
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
async log(message: LogMessage): Promise<void> {
|
||||||
|
lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/log`, message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,93 +1,11 @@
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
||||||
import { jwtDecode } from 'jwt-decode';
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
import onChange from 'on-change';
|
||||||
|
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, KeycloakUser, MailInfo, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, KeycloakUser, MailInfo, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
// export function createDefaultUser(email: string, firstname: string, lastname: string): User {
|
|
||||||
// return {
|
|
||||||
// id: undefined,
|
|
||||||
// email,
|
|
||||||
// firstname,
|
|
||||||
// lastname,
|
|
||||||
// phoneNumber: '',
|
|
||||||
// description: '',
|
|
||||||
// companyName: '',
|
|
||||||
// companyOverview: '',
|
|
||||||
// companyWebsite: '',
|
|
||||||
// companyLocation: '',
|
|
||||||
// offeredServices: '',
|
|
||||||
// areasServed: [],
|
|
||||||
// hasProfile: false,
|
|
||||||
// hasCompanyLogo: false,
|
|
||||||
// licensedIn: [],
|
|
||||||
// gender: undefined,
|
|
||||||
// customerType: undefined,
|
|
||||||
// customerSubType: undefined,
|
|
||||||
// created: new Date(),
|
|
||||||
// updated: new Date(),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function createDefaultCommercialPropertyListing(): CommercialPropertyListing {
|
|
||||||
// return {
|
|
||||||
// id: undefined,
|
|
||||||
// serialId: undefined,
|
|
||||||
// email: '',
|
|
||||||
// type: null,
|
|
||||||
// title: '',
|
|
||||||
// description: '',
|
|
||||||
// city: '',
|
|
||||||
// state: '',
|
|
||||||
// price: null,
|
|
||||||
// favoritesForUser: [],
|
|
||||||
// hideImage: false,
|
|
||||||
// draft: false,
|
|
||||||
// zipCode: null,
|
|
||||||
// county: '',
|
|
||||||
// imageOrder: [],
|
|
||||||
// imagePath: '',
|
|
||||||
// created: null,
|
|
||||||
// updated: null,
|
|
||||||
// visits: null,
|
|
||||||
// lastVisit: null,
|
|
||||||
// latitude: null,
|
|
||||||
// longitude: null,
|
|
||||||
// listingsCategory: 'commercialProperty',
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// export function createDefaultBusinessListing(): BusinessListing {
|
|
||||||
// return {
|
|
||||||
// id: undefined,
|
|
||||||
// email: '',
|
|
||||||
// type: null,
|
|
||||||
// title: '',
|
|
||||||
// description: '',
|
|
||||||
// city: '',
|
|
||||||
// state: '',
|
|
||||||
// price: null,
|
|
||||||
// favoritesForUser: [],
|
|
||||||
// draft: false,
|
|
||||||
// realEstateIncluded: false,
|
|
||||||
// leasedLocation: false,
|
|
||||||
// franchiseResale: false,
|
|
||||||
// salesRevenue: null,
|
|
||||||
// cashFlow: null,
|
|
||||||
// supportAndTraining: '',
|
|
||||||
// employees: null,
|
|
||||||
// established: null,
|
|
||||||
// internalListingNumber: null,
|
|
||||||
// reasonForSale: '',
|
|
||||||
// brokerLicencing: '',
|
|
||||||
// internals: '',
|
|
||||||
// created: null,
|
|
||||||
// updated: null,
|
|
||||||
// visits: null,
|
|
||||||
// lastVisit: null,
|
|
||||||
// latitude: null,
|
|
||||||
// longitude: null,
|
|
||||||
// listingsCategory: 'business',
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
export function createEmptyBusinessListingCriteria(): BusinessListingCriteria {
|
export function createEmptyBusinessListingCriteria(): BusinessListingCriteria {
|
||||||
return {
|
return {
|
||||||
start: 0,
|
start: 0,
|
||||||
|
|
@ -153,6 +71,66 @@ export function createEmptyUserListingCriteria(): UserListingCriteria {
|
||||||
radius: null,
|
radius: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export function resetBusinessListingCriteria(criteria: BusinessListingCriteria) {
|
||||||
|
criteria.start = 0;
|
||||||
|
criteria.length = 0;
|
||||||
|
criteria.page = 0;
|
||||||
|
criteria.state = null;
|
||||||
|
criteria.city = null;
|
||||||
|
criteria.types = [];
|
||||||
|
criteria.prompt = '';
|
||||||
|
criteria.criteriaType = 'businessListings';
|
||||||
|
criteria.minPrice = null;
|
||||||
|
criteria.maxPrice = null;
|
||||||
|
criteria.minRevenue = null;
|
||||||
|
criteria.maxRevenue = null;
|
||||||
|
criteria.minCashFlow = null;
|
||||||
|
criteria.maxCashFlow = null;
|
||||||
|
criteria.minNumberEmployees = null;
|
||||||
|
criteria.maxNumberEmployees = null;
|
||||||
|
criteria.establishedSince = null;
|
||||||
|
criteria.establishedUntil = null;
|
||||||
|
criteria.realEstateChecked = false;
|
||||||
|
criteria.leasedLocation = false;
|
||||||
|
criteria.franchiseResale = false;
|
||||||
|
criteria.title = '';
|
||||||
|
criteria.brokerName = '';
|
||||||
|
criteria.searchType = 'exact';
|
||||||
|
criteria.radius = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetCommercialPropertyListingCriteria(criteria: CommercialPropertyListingCriteria) {
|
||||||
|
criteria.start = 0;
|
||||||
|
criteria.length = 0;
|
||||||
|
criteria.page = 0;
|
||||||
|
criteria.state = null;
|
||||||
|
criteria.city = null;
|
||||||
|
criteria.types = [];
|
||||||
|
criteria.prompt = '';
|
||||||
|
criteria.criteriaType = 'commercialPropertyListings';
|
||||||
|
criteria.minPrice = null;
|
||||||
|
criteria.maxPrice = null;
|
||||||
|
criteria.title = '';
|
||||||
|
criteria.searchType = 'exact';
|
||||||
|
criteria.radius = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetUserListingCriteria(criteria: UserListingCriteria) {
|
||||||
|
criteria.start = 0;
|
||||||
|
criteria.length = 0;
|
||||||
|
criteria.page = 0;
|
||||||
|
criteria.city = null;
|
||||||
|
criteria.types = [];
|
||||||
|
criteria.prompt = '';
|
||||||
|
criteria.criteriaType = 'brokerListings';
|
||||||
|
criteria.brokerName = '';
|
||||||
|
criteria.companyName = '';
|
||||||
|
criteria.counties = [];
|
||||||
|
criteria.state = '';
|
||||||
|
criteria.searchType = 'exact';
|
||||||
|
criteria.radius = null;
|
||||||
|
}
|
||||||
|
|
||||||
export function createMailInfo(user: User): MailInfo {
|
export function createMailInfo(user: User): MailInfo {
|
||||||
return {
|
return {
|
||||||
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.companyLocation?.state, comments: null },
|
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.companyLocation?.state, comments: null },
|
||||||
|
|
@ -178,9 +156,7 @@ export function formatPhoneNumber(phone: string): string {
|
||||||
}
|
}
|
||||||
return phone;
|
return phone;
|
||||||
}
|
}
|
||||||
// export const getSessionStorageHandler = function (path, value, previous, applyData) {
|
|
||||||
// sessionStorage.setItem(applyData, JSON.stringify(this));
|
|
||||||
// };
|
|
||||||
export const getSessionStorageHandler = function (criteriaType, path, value, previous, applyData) {
|
export const getSessionStorageHandler = function (criteriaType, path, value, previous, applyData) {
|
||||||
sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this));
|
sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this));
|
||||||
console.log('Zusätzlicher Parameter:', criteriaType);
|
console.log('Zusätzlicher Parameter:', criteriaType);
|
||||||
|
|
@ -191,19 +167,6 @@ export const getSessionStorageHandlerWrapper = param => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getCriteriaStateObject(criteriaType: 'businessListings' | 'commercialPropertyListings' | 'brokerListings') {
|
|
||||||
let initialState;
|
|
||||||
if (criteriaType === 'businessListings') {
|
|
||||||
initialState = createEmptyBusinessListingCriteria();
|
|
||||||
} else if (criteriaType === 'commercialPropertyListings') {
|
|
||||||
initialState = createEmptyCommercialPropertyListingCriteria();
|
|
||||||
} else {
|
|
||||||
initialState = createEmptyUserListingCriteria();
|
|
||||||
}
|
|
||||||
const storedState = sessionStorage.getItem(`${criteriaType}`);
|
|
||||||
return storedState ? JSON.parse(storedState) : initialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function routeListingWithState(router: Router, value: string, data: any) {
|
export function routeListingWithState(router: Router, value: string, data: any) {
|
||||||
if (value === 'business') {
|
if (value === 'business') {
|
||||||
router.navigate(['createBusinessListing'], { state: { data } });
|
router.navigate(['createBusinessListing'], { state: { data } });
|
||||||
|
|
@ -248,70 +211,6 @@ export function getDialogWidth(dimensions): string {
|
||||||
return dialogWidth;
|
return dialogWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
import { initFlowbite } from 'flowbite';
|
|
||||||
import onChange from 'on-change';
|
|
||||||
import { Subject, concatMap, delay, of } from 'rxjs';
|
|
||||||
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
const flowbiteQueue = new Subject<() => void>();
|
|
||||||
|
|
||||||
flowbiteQueue.pipe(concatMap(item => of(item).pipe(delay(100)))).subscribe(x => {
|
|
||||||
x();
|
|
||||||
});
|
|
||||||
|
|
||||||
export function Flowbite() {
|
|
||||||
return function (target: any) {
|
|
||||||
const originalOnInit = target.prototype.ngOnInit;
|
|
||||||
target.prototype.ngOnInit = function () {
|
|
||||||
if (originalOnInit) {
|
|
||||||
originalOnInit.apply(this);
|
|
||||||
}
|
|
||||||
initFlowbiteFix();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initFlowbiteFix() {
|
|
||||||
flowbiteQueue.next(() => {
|
|
||||||
const elements = Array.from(document.querySelectorAll('*'));
|
|
||||||
const flowbiteElements: Element[] = [];
|
|
||||||
const initializedElements = Array.from(document.querySelectorAll('[flowbite-initialized]'));
|
|
||||||
|
|
||||||
for (const element of elements) {
|
|
||||||
const attributes = Array.from(element.attributes);
|
|
||||||
|
|
||||||
for (const attribute of attributes) {
|
|
||||||
if (attribute.name.startsWith('data-') && !initializedElements.includes(element)) {
|
|
||||||
flowbiteElements.push(element);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const element of flowbiteElements) {
|
|
||||||
element.setAttribute('flowbite-initialized', '');
|
|
||||||
}
|
|
||||||
initFlowbite();
|
|
||||||
|
|
||||||
for (const element of flowbiteElements) {
|
|
||||||
const attributes: { name: string; value: string }[] = Array.from(element.attributes);
|
|
||||||
const dataAttributes = attributes.filter(attribute => attribute.name.startsWith('data-'));
|
|
||||||
|
|
||||||
for (const attribute of dataAttributes) {
|
|
||||||
element.setAttribute(attribute.name.replace('data-', 'fb-'), attribute.value);
|
|
||||||
element.removeAttribute(attribute.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// export function arraysEqual(arr1: any[], arr2: any[]): boolean {
|
|
||||||
// if (arr1.length !== arr2.length) return false;
|
|
||||||
// for (let i = 0; i < arr1.length; i++) {
|
|
||||||
// if (arr1[i] !== arr2[i]) return false;
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
export function compareObjects<T extends object>(obj1: T, obj2: T, ignoreProperties: (keyof T)[] = []): number {
|
export function compareObjects<T extends object>(obj1: T, obj2: T, ignoreProperties: (keyof T)[] = []): number {
|
||||||
let differences = 0;
|
let differences = 0;
|
||||||
const keys = Object.keys(obj1) as Array<keyof T>;
|
const keys = Object.keys(obj1) as Array<keyof T>;
|
||||||
|
|
@ -380,10 +279,29 @@ function arraysEqual(arr1: any[] | null | undefined, arr2: any[] | null | undefi
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
export function assignProperties(target, source) {
|
||||||
|
for (let key in source) {
|
||||||
|
if (source.hasOwnProperty(key)) {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Criteria Proxy
|
// Criteria Proxy
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
|
export function getCriteriaStateObject(criteriaType: 'businessListings' | 'commercialPropertyListings' | 'brokerListings') {
|
||||||
|
let initialState;
|
||||||
|
if (criteriaType === 'businessListings') {
|
||||||
|
initialState = createEmptyBusinessListingCriteria();
|
||||||
|
} else if (criteriaType === 'commercialPropertyListings') {
|
||||||
|
initialState = createEmptyCommercialPropertyListingCriteria();
|
||||||
|
} else {
|
||||||
|
initialState = createEmptyUserListingCriteria();
|
||||||
|
}
|
||||||
|
const storedState = sessionStorage.getItem(`${criteriaType}`);
|
||||||
|
return storedState ? JSON.parse(storedState) : initialState;
|
||||||
|
}
|
||||||
export function getCriteriaProxy(path: string, component: any): BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria {
|
export function getCriteriaProxy(path: string, component: any): BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria {
|
||||||
if ('businessListings' === path) {
|
if ('businessListings' === path) {
|
||||||
return createEnhancedProxy(getCriteriaStateObject('businessListings'), component);
|
return createEnhancedProxy(getCriteriaStateObject('businessListings'), component);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue