diff --git a/bizmatch-server/src/drizzle/schema.ts b/bizmatch-server/src/drizzle/schema.ts index 3c4546e..8256179 100644 --- a/bizmatch-server/src/drizzle/schema.ts +++ b/bizmatch-server/src/drizzle/schema.ts @@ -73,14 +73,6 @@ export const businesses = pgTable( created: timestamp('created'), updated: timestamp('updated'), location: jsonb('location'), - // city: varchar('city', { length: 255 }), - // state: char('state', { length: 2 }), - // zipCode: integer('zipCode'), - // county: varchar('county', { length: 255 }), - // street: varchar('street', { length: 255 }), - // housenumber: varchar('housenumber', { length: 10 }), - // latitude: doublePrecision('latitude'), - // longitude: doublePrecision('longitude'), }, table => ({ locationBusinessCityStateIdx: index('idx_business_location_city_state').on( diff --git a/bizmatch-server/src/listings/business-listing.service.ts b/bizmatch-server/src/listings/business-listing.service.ts index 37fcfc4..b471c34 100644 --- a/bizmatch-server/src/listings/business-listing.service.ts +++ b/bizmatch-server/src/listings/business-listing.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Inject, Injectable } from '@nestjs/common'; +import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common'; import { and, arrayContains, asc, count, desc, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; @@ -246,10 +246,18 @@ export class BusinessListingService { } } // #### UPDATE Business ######################################## - async updateBusinessListing(id: string, data: BusinessListing): Promise { + async updateBusinessListing(id: string, data: BusinessListing, user: JwtUser): Promise { try { + const [existingListing] = await this.conn.select().from(businesses).where(eq(businesses.id, id)); + + if (!existingListing) { + throw new NotFoundException(`Business listing with id ${id} not found`); + } data.updated = new Date(); data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date(); + if (existingListing.email === user?.email) { + data.favoritesForUser = existingListing.favoritesForUser; + } BusinessListingSchema.parse(data); const convertedBusinessListing = data; const [updateListing] = await this.conn.update(businesses).set(convertedBusinessListing).where(eq(businesses.id, id)).returning(); diff --git a/bizmatch-server/src/listings/business-listings.controller.ts b/bizmatch-server/src/listings/business-listings.controller.ts index 84d93d9..cf7d1a4 100644 --- a/bizmatch-server/src/listings/business-listings.controller.ts +++ b/bizmatch-server/src/listings/business-listings.controller.ts @@ -50,8 +50,8 @@ export class BusinessListingsController { @UseGuards(OptionalAuthGuard) @Put() - async update(@Body() listing: any) { - return await this.listingsService.updateBusinessListing(listing.id, listing); + async update(@Request() req, @Body() listing: any) { + return await this.listingsService.updateBusinessListing(listing.id, listing, req.user as JwtUser); } @UseGuards(OptionalAuthGuard) diff --git a/bizmatch-server/src/listings/commercial-property-listings.controller.ts b/bizmatch-server/src/listings/commercial-property-listings.controller.ts index 3e98787..e54a957 100644 --- a/bizmatch-server/src/listings/commercial-property-listings.controller.ts +++ b/bizmatch-server/src/listings/commercial-property-listings.controller.ts @@ -54,8 +54,8 @@ export class CommercialPropertyListingsController { @UseGuards(OptionalAuthGuard) @Put() - async update(@Body() listing: any) { - return await this.listingsService.updateCommercialPropertyListing(listing.id, listing); + async update(@Request() req, @Body() listing: any) { + return await this.listingsService.updateCommercialPropertyListing(listing.id, listing, req.user as JwtUser); } @UseGuards(OptionalAuthGuard) diff --git a/bizmatch-server/src/listings/commercial-property.service.ts b/bizmatch-server/src/listings/commercial-property.service.ts index ca2469f..d3f816e 100644 --- a/bizmatch-server/src/listings/commercial-property.service.ts +++ b/bizmatch-server/src/listings/commercial-property.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Inject, Injectable } from '@nestjs/common'; +import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common'; import { and, arrayContains, asc, count, desc, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; @@ -181,10 +181,18 @@ export class CommercialPropertyService { } } // #### UPDATE CommercialProps ######################################## - async updateCommercialPropertyListing(id: string, data: CommercialPropertyListing): Promise { + async updateCommercialPropertyListing(id: string, data: CommercialPropertyListing, user: JwtUser): Promise { try { + const [existingListing] = await this.conn.select().from(commercials).where(eq(commercials.id, id)); + + if (!existingListing) { + throw new NotFoundException(`Business listing with id ${id} not found`); + } data.updated = new Date(); data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date(); + if (existingListing.email === user?.email || !user) { + data.favoritesForUser = existingListing.favoritesForUser; + } CommercialPropertyListingSchema.parse(data); const imageOrder = await this.fileService.getPropertyImages(data.imagePath, String(data.serialId)); const difference = imageOrder.filter(x => !data.imageOrder.includes(x)).concat(data.imageOrder.filter(x => !imageOrder.includes(x))); @@ -216,13 +224,13 @@ export class CommercialPropertyService { const index = listing.imageOrder.findIndex(im => im === name); if (index > -1) { listing.imageOrder.splice(index, 1); - await this.updateCommercialPropertyListing(listing.id, listing); + await this.updateCommercialPropertyListing(listing.id, listing, null); } } async addImage(imagePath: string, serial: string, imagename: string) { const listing = (await this.findByImagePath(imagePath, serial)) as unknown as CommercialPropertyListing; listing.imageOrder.push(imagename); - await this.updateCommercialPropertyListing(listing.id, listing); + await this.updateCommercialPropertyListing(listing.id, listing, null); } // #### DELETE ######################################## async deleteListing(id: string): Promise { diff --git a/bizmatch/src/app/components/header/header.component.html b/bizmatch/src/app/components/header/header.component.html index ab5838c..5330fe9 100644 --- a/bizmatch/src/app/components/header/header.component.html +++ b/bizmatch/src/app/components/header/header.component.html @@ -51,7 +51,7 @@ data-dropdown-placement="bottom" > Open user menu - @if(user?.hasProfile){ + @if(isProfessional || (authService.isAdmin() | async) && user?.hasProfile){ user photo } @else { diff --git a/bizmatch/src/app/components/header/header.component.ts b/bizmatch/src/app/components/header/header.component.ts index 9d32bcd..3d888d5 100644 --- a/bizmatch/src/app/components/header/header.component.ts +++ b/bizmatch/src/app/components/header/header.component.ts @@ -200,4 +200,7 @@ export class HeaderComponent { toggleSortDropdown() { this.sortDropdownVisible = !this.sortDropdownVisible; } + get isProfessional() { + return this.user?.customerType === 'professional'; + } } diff --git a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts index 923373c..f205a37 100644 --- a/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts +++ b/bizmatch/src/app/pages/details/details-business-listing/details-business-listing.component.ts @@ -123,6 +123,7 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent { this.mailinfo.email = this.listingUser.email; this.mailinfo.listing = this.listing; await this.mailService.mail(this.mailinfo); + this.validationMessagesService.clearMessages(); this.auditService.createEvent(this.listing.id, 'contact', this.user?.email, this.mailinfo.sender); this.messageService.addMessage({ severity: 'success', text: 'Your message has been sent to the creator of the listing', duration: 3000 }); this.mailinfo = createMailInfo(this.user); diff --git a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts index bf99028..9ed0084 100644 --- a/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/details/details-commercial-property-listing/details-commercial-property-listing.component.ts @@ -156,6 +156,7 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon this.mailinfo.email = this.listingUser.email; this.mailinfo.listing = this.listing; await this.mailService.mail(this.mailinfo); + this.validationMessagesService.clearMessages(); this.auditService.createEvent(this.listing.id, 'contact', this.user?.email, this.mailinfo.sender); this.messageService.addMessage({ severity: 'success', text: 'Your message has been sent to the creator of the listing', duration: 3000 }); this.mailinfo = createMailInfo(this.user); diff --git a/bizmatch/src/app/services/auth.service.ts b/bizmatch/src/app/services/auth.service.ts index a7d7dc6..7e0796d 100644 --- a/bizmatch/src/app/services/auth.service.ts +++ b/bizmatch/src/app/services/auth.service.ts @@ -159,7 +159,8 @@ export class AuthService { // Cache zurücksetzen, wenn die Caching-Zeit abgelaufen ist oder kein Cache existiert if (!this.cachedUserRole$ || now - this.lastCacheTime > this.cacheDuration) { this.lastCacheTime = now; - this.cachedUserRole$ = this.http.get<{ role: UserRole | null }>(`${environment.apiBaseUrl}/bizmatch/auth/me/role`).pipe( + let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US'); + this.cachedUserRole$ = this.http.get<{ role: UserRole | null }>(`${environment.apiBaseUrl}/bizmatch/auth/me/role`, { headers }).pipe( map(response => response.role), tap(role => this.userRoleSubject.next(role)), catchError(error => {