From cfddabbfe04b249d9ff64c2b199fa6d7cb1d3cee Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Thu, 13 Mar 2025 16:59:50 +0100 Subject: [PATCH] BugFixing --- .../src/listings/business-listing.service.ts | 14 +-- .../listings/commercial-property.service.ts | 14 +-- bizmatch-server/src/models/db.model.ts | 94 +++++++++++++------ bizmatch-server/src/models/main.model.ts | 8 +- bizmatch-server/src/user/user.service.ts | 10 +- .../components/header/header.component.html | 2 +- .../details-business-listing.component.ts | 6 +- 7 files changed, 89 insertions(+), 59 deletions(-) diff --git a/bizmatch-server/src/listings/business-listing.service.ts b/bizmatch-server/src/listings/business-listing.service.ts index 5014b3a..37fcfc4 100644 --- a/bizmatch-server/src/listings/business-listing.service.ts +++ b/bizmatch-server/src/listings/business-listing.service.ts @@ -103,8 +103,8 @@ export class BusinessListingService { whereConditions.push(and(ilike(schema.users.firstname, `%${firstname}%`), ilike(schema.users.lastname, `%${lastname}%`))); } } - if (!user?.roles?.includes('ADMIN')) { - whereConditions.push(or(eq(businesses.email, user?.username), ne(businesses.draft, true))); + if (user?.role !== 'admin') { + whereConditions.push(or(eq(businesses.email, user?.email), ne(businesses.draft, true))); } whereConditions.push(and(eq(schema.users.customerType, 'professional'), eq(schema.users.customerSubType, 'broker'))); return whereConditions; @@ -186,8 +186,8 @@ export class BusinessListingService { async findBusinessesById(id: string, user: JwtUser): Promise { const conditions = []; - if (!user?.roles?.includes('ADMIN')) { - conditions.push(or(eq(businesses.email, user?.username), ne(businesses.draft, true))); + if (user?.role !== 'admin') { + conditions.push(or(eq(businesses.email, user?.email), ne(businesses.draft, true))); } conditions.push(sql`${businesses.id} = ${id}`); const result = await this.conn @@ -204,7 +204,7 @@ export class BusinessListingService { async findBusinessesByEmail(email: string, user: JwtUser): Promise { const conditions = []; conditions.push(eq(businesses.email, email)); - if (email !== user?.username && (!user?.roles?.includes('ADMIN'))) { + if (email !== user?.email && user?.role !== 'admin') { conditions.push(ne(businesses.draft, true)); } const listings = (await this.conn @@ -219,7 +219,7 @@ export class BusinessListingService { const userFavorites = await this.conn .select() .from(businesses) - .where(arrayContains(businesses.favoritesForUser, [user.username])); + .where(arrayContains(businesses.favoritesForUser, [user.email])); return userFavorites; } // #### CREATE ######################################## @@ -276,7 +276,7 @@ export class BusinessListingService { await this.conn .update(businesses) .set({ - favoritesForUser: sql`array_remove(${businesses.favoritesForUser}, ${user.username})`, + favoritesForUser: sql`array_remove(${businesses.favoritesForUser}, ${user.email})`, }) .where(sql`${businesses.id} = ${id}`); } diff --git a/bizmatch-server/src/listings/commercial-property.service.ts b/bizmatch-server/src/listings/commercial-property.service.ts index fcb7e7d..ca2469f 100644 --- a/bizmatch-server/src/listings/commercial-property.service.ts +++ b/bizmatch-server/src/listings/commercial-property.service.ts @@ -49,8 +49,8 @@ export class CommercialPropertyService { if (criteria.title) { whereConditions.push(or(ilike(schema.commercials.title, `%${criteria.title}%`), ilike(schema.commercials.description, `%${criteria.title}%`))); } - if (!user?.roles?.includes('ADMIN')) { - whereConditions.push(or(eq(commercials.email, user?.username), ne(commercials.draft, true))); + if (user?.role !== 'admin') { + whereConditions.push(or(eq(commercials.email, user?.email), ne(commercials.draft, true))); } // whereConditions.push(and(eq(schema.users.customerType, 'professional'))); return whereConditions; @@ -113,8 +113,8 @@ export class CommercialPropertyService { // #### Find by ID ######################################## async findCommercialPropertiesById(id: string, user: JwtUser): Promise { const conditions = []; - if (!user?.roles?.includes('ADMIN')) { - conditions.push(or(eq(commercials.email, user?.username), ne(commercials.draft, true))); + if (user?.role !== 'admin') { + conditions.push(or(eq(commercials.email, user?.email), ne(commercials.draft, true))); } conditions.push(sql`${commercials.id} = ${id}`); const result = await this.conn @@ -132,7 +132,7 @@ export class CommercialPropertyService { async findCommercialPropertiesByEmail(email: string, user: JwtUser): Promise { const conditions = []; conditions.push(eq(commercials.email, email)); - if (email !== user?.username && (!user?.roles?.includes('ADMIN'))) { + if (email !== user?.email && user?.role !== 'admin') { conditions.push(ne(commercials.draft, true)); } const listings = (await this.conn @@ -146,7 +146,7 @@ export class CommercialPropertyService { const userFavorites = await this.conn .select() .from(commercials) - .where(arrayContains(commercials.favoritesForUser, [user.username])); + .where(arrayContains(commercials.favoritesForUser, [user.email])); return userFavorites; } // #### Find by imagePath ######################################## @@ -233,7 +233,7 @@ export class CommercialPropertyService { await this.conn .update(commercials) .set({ - favoritesForUser: sql`array_remove(${commercials.favoritesForUser}, ${user.username})`, + favoritesForUser: sql`array_remove(${commercials.favoritesForUser}, ${user.email})`, }) .where(sql`${commercials.id} = ${id}`); } diff --git a/bizmatch-server/src/models/db.model.ts b/bizmatch-server/src/models/db.model.ts index 3d831e2..2d676e2 100644 --- a/bizmatch-server/src/models/db.model.ts +++ b/bizmatch-server/src/models/db.model.ts @@ -258,35 +258,59 @@ export type AreasServed = z.infer; export type LicensedIn = z.infer; export type User = z.infer; -export const BusinessListingSchema = z.object({ - id: z.string().uuid().optional().nullable(), - email: z.string().email(), - type: z.string().refine(val => TypeEnum.safeParse(val).success, { - message: 'Invalid type. Must be one of: ' + TypeEnum.options.join(', '), - }), - title: z.string().min(10), - description: z.string().min(10), - location: GeoSchema, - price: z.number().positive().max(1000000000), - favoritesForUser: z.array(z.string()), - draft: z.boolean(), - listingsCategory: ListingsCategoryEnum, - realEstateIncluded: z.boolean().optional().nullable(), - leasedLocation: z.boolean().optional().nullable(), - franchiseResale: z.boolean().optional().nullable(), - salesRevenue: z.number().positive().max(100000000), - cashFlow: z.number().positive().max(100000000), - supportAndTraining: z.string().min(5), - employees: z.number().int().positive().max(100000).optional().nullable(), - established: z.number().int().min(1800).max(2030).optional().nullable(), - internalListingNumber: z.number().int().positive().optional().nullable(), - reasonForSale: z.string().min(5).optional().nullable(), - brokerLicencing: z.string().optional().nullable(), - internals: z.string().min(5).optional().nullable(), - imageName: z.string().optional().nullable(), - created: z.date(), - updated: z.date(), -}); +export const BusinessListingSchema = z + .object({ + id: z.string().uuid().optional().nullable(), + email: z.string().email(), + type: z.string().refine(val => TypeEnum.safeParse(val).success, { + message: 'Invalid type. Must be one of: ' + TypeEnum.options.join(', '), + }), + title: z.string().min(10), + description: z.string().min(10), + location: GeoSchema, + price: z.number().positive(), + favoritesForUser: z.array(z.string()), + draft: z.boolean(), + listingsCategory: ListingsCategoryEnum, + realEstateIncluded: z.boolean().optional().nullable(), + leasedLocation: z.boolean().optional().nullable(), + franchiseResale: z.boolean().optional().nullable(), + salesRevenue: z.number().positive().nullable(), + cashFlow: z.number().positive().max(100000000), + supportAndTraining: z.string().min(5), + employees: z.number().int().positive().max(100000).optional().nullable(), + established: z.number().int().min(1800).max(2030).optional().nullable(), + internalListingNumber: z.number().int().positive().optional().nullable(), + reasonForSale: z.string().min(5).optional().nullable(), + brokerLicencing: z.string().optional().nullable(), + internals: z.string().min(5).optional().nullable(), + imageName: z.string().optional().nullable(), + created: z.date(), + updated: z.date(), + }) + .superRefine((data, ctx) => { + if (data.price && data.price > 1000000000) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Price must less than or equal $1,000,000,000', + path: ['price'], + }); + } + if (data.salesRevenue && data.salesRevenue > 100000000) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'SalesRevenue must less than or equal $100,000,000', + path: ['salesRevenue'], + }); + } + if (data.cashFlow && data.cashFlow > 100000000) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'CashFlow must less than or equal $100,000,000', + path: ['cashFlow'], + }); + } + }); export type BusinessListing = z.infer; export const CommercialPropertyListingSchema = z @@ -300,7 +324,7 @@ export const CommercialPropertyListingSchema = z title: z.string().min(10), description: z.string().min(10), location: GeoSchema, - price: z.number().positive().max(1000000000), + price: z.number().positive(), favoritesForUser: z.array(z.string()), listingsCategory: ListingsCategoryEnum, draft: z.boolean(), @@ -309,7 +333,15 @@ export const CommercialPropertyListingSchema = z created: z.date(), updated: z.date(), }) - .strict(); + .superRefine((data, ctx) => { + if (data.price && data.price > 1000000000) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Price must less than or equal $1,000,000,000', + path: ['price'], + }); + } + }); export type CommercialPropertyListing = z.infer; diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index c6f4f6c..19b1f71 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -123,11 +123,9 @@ export interface KeycloakUser { attributes?: Attributes; } export interface JwtUser { - userId: string; - username: string; - firstname: string; - lastname: string; - roles: string[]; + email: string; + role: string; + uid: string; } interface Attributes { [key: string]: any; diff --git a/bizmatch-server/src/user/user.service.ts b/bizmatch-server/src/user/user.service.ts index b41ec48..124bb69 100644 --- a/bizmatch-server/src/user/user.service.ts +++ b/bizmatch-server/src/user/user.service.ts @@ -52,10 +52,10 @@ export class UserService { if (criteria.state) { whereConditions.push(sql`EXISTS (SELECT 1 FROM jsonb_array_elements(${schema.users.areasServed}) AS area WHERE area->>'state' = ${criteria.state})`); } - + //never show user which denied - whereConditions.push(eq(schema.users.showInDirectory, true)) - + whereConditions.push(eq(schema.users.showInDirectory, true)); + return whereConditions; } async searchUserListings(criteria: UserListingCriteria): Promise<{ results: User[]; totalCount: number }> { @@ -63,7 +63,7 @@ export class UserService { const length = criteria.length ? criteria.length : 12; const query = this.conn.select().from(schema.users); const whereConditions = this.getWhereConditions(criteria); - + if (whereConditions.length > 0) { const whereClause = and(...whereConditions); query.where(whereClause); @@ -110,7 +110,7 @@ export class UserService { .from(schema.users) .where(sql`email = ${email}`)) as User[]; if (users.length === 0) { - const user: User = { id: undefined, customerType: 'professional', ...createDefaultUser(email, jwtuser.firstname ? jwtuser.firstname : '', jwtuser.lastname ? jwtuser.lastname : '', null) }; + const user: User = { id: undefined, customerType: 'professional', ...createDefaultUser(email, '', '', null) }; const u = await this.saveUser(user, false); return u; } else { diff --git a/bizmatch/src/app/components/header/header.component.html b/bizmatch/src/app/components/header/header.component.html index 6fe3106..201592c 100644 --- a/bizmatch/src/app/components/header/header.component.html +++ b/bizmatch/src/app/components/header/header.component.html @@ -83,10 +83,10 @@
  • My Listings
  • + }
  • My Favorites
  • - }
  • EMail Us
  • 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 aca76d8..18f7fa0 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 @@ -149,9 +149,9 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent { const result = [ { label: 'Category', value: this.selectOptions.getBusiness(this.listing.type) }, { label: 'Located in', value: `${this.listing.location.name ? this.listing.location.name : this.listing.location.county}, ${this.selectOptions.getState(this.listing.location.state)}` }, - { label: 'Asking Price', value: `$${this.listing.price?.toLocaleString()}` }, - { label: 'Sales revenue', value: `$${this.listing.salesRevenue?.toLocaleString()}` }, - { label: 'Cash flow', value: `$${this.listing.cashFlow?.toLocaleString()}` }, + { label: 'Asking Price', value: `${this.listing.price ? `$${this.listing.price.toLocaleString()}` : ''}` }, + { label: 'Sales revenue', value: `${this.listing.salesRevenue ? `$${this.listing.salesRevenue.toLocaleString()}` : ''}` }, + { label: 'Cash flow', value: `${this.listing.cashFlow ? `$${this.listing.cashFlow.toLocaleString()}` : ''}` }, { label: 'Type of Real Estate', value: typeOfRealEstate }, { label: 'Employees', value: this.listing.employees }, { label: 'Established since', value: this.listing.established },