diff --git a/bizmatch-server/src/drizzle/import.ts b/bizmatch-server/src/drizzle/import.ts index fd1ac96..c816ab3 100644 --- a/bizmatch-server/src/drizzle/import.ts +++ b/bizmatch-server/src/drizzle/import.ts @@ -12,7 +12,6 @@ import winston from 'winston'; import { BusinessListing, CommercialPropertyListing, User, UserData } from '../models/db.model.js'; import { emailToDirName, KeyValueStyle } from '../models/main.model.js'; import * as schema from './schema.js'; -import { users } from './schema.js'; const typesOfBusiness: Array = [ { name: 'Automotive', value: '1', icon: 'fa-solid fa-car', textColorClass: 'text-green-400' }, { name: 'Industrial Services', value: '2', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' }, @@ -65,9 +64,9 @@ deleteFilesOfDir(targetPathProperty); fs.ensureDirSync(`./pictures/logo`); fs.ensureDirSync(`./pictures/profile`); fs.ensureDirSync(`./pictures/property`); -type UserProfile = Omit; +// type UserProfile = Omit; -type NewUser = typeof users.$inferInsert; +// type NewUser = typeof users.$inferInsert; //for (const userData of usersData) { for (let index = 0; index < usersData.length; index++) { const userData = usersData[index]; @@ -98,20 +97,17 @@ for (let index = 0; index < usersData.length; index++) { user.customerSubType = 'broker'; user.created = new Date(); user.updated = new Date(); - const createUserProfile = (user: User): UserProfile => { - const { id, created, updated, hasCompanyLogo, hasProfile, ...userProfile } = user; - return userProfile; - }; - const userProfile = createUserProfile(user); - logger.info(`${index} - ${JSON.stringify(userProfile)}`); - const embedding = await createEmbedding(JSON.stringify(userProfile)); - sleep(200); + // const createUserProfile = (user: User): UserProfile => { + // const { id, created, updated, hasCompanyLogo, hasProfile, ...userProfile } = user; + // return userProfile; + // }; + // const userProfile = createUserProfile(user); + // logger.info(`${index} - ${JSON.stringify(userProfile)}`); + // const embedding = await createEmbedding(JSON.stringify(userProfile)); + //sleep(200); const u = await db .insert(schema.users) - .values({ - ...user, - embedding: embedding, - } as NewUser) + .values(user) .returning({ insertedId: schema.users.id, gender: schema.users.gender, email: schema.users.email, firstname: schema.users.firstname, lastname: schema.users.lastname }); generatedUserData.push(u[0]); i++; @@ -145,25 +141,20 @@ for (let index = 0; index < commercialJsonData.length; index++) { commercial.updated = insertionDate; commercial.email = user.email; commercial.draft = false; - const reducedCommercial = { - city: commercial.city, - description: commercial.description, - email: commercial.email, - price: commercial.price, - state: sso.locations.find(l => l.value === commercial.state)?.name, - title: commercial.title, - name: `${user.firstname} ${user.lastname}`, - }; - const embedding = await createEmbedding(JSON.stringify(reducedCommercial)); - sleep(200); - const result = await db - .insert(schema.commercials) - .values({ - ...commercial, - embedding: embedding, - }) - .returning(); - logger.info(`commercial_${index} inserted`); + commercial.type = sso.typesOfCommercialProperty.find(e => e.oldValue === String(commercial.type)).value; + // const reducedCommercial = { + // city: commercial.city, + // description: commercial.description, + // email: commercial.email, + // price: commercial.price, + // state: sso.locations.find(l => l.value === commercial.state)?.name, + // title: commercial.title, + // name: `${user.firstname} ${user.lastname}`, + // }; + // const embedding = await createEmbedding(JSON.stringify(reducedCommercial)); + // sleep(200); + const result = await db.insert(schema.commercials).values(commercial).returning(); + // logger.info(`commercial_${index} inserted`); try { fs.copySync(`./pictures_base/property/${id}`, `./pictures/property/${result[0].imagePath}/${result[0].serialId}`); } catch (err) { @@ -178,37 +169,34 @@ const businessJsonData = JSON.parse(data) as BusinessListing[]; // Erwartet ein for (let index = 0; index < businessJsonData.length; index++) { const business = businessJsonData[index]; delete business.id; + business.type = sso.typesOfBusiness.find(e => e.oldValue === String(business.type)).value; business.created = new Date(business.created); business.updated = new Date(business.created); const user = getRandomItem(generatedUserData); business.email = user.email; business.imageName = emailToDirName(user.email); - const embeddingText = JSON.stringify({ - type: typesOfBusiness.find(b => b.value === String(business.type))?.name, - title: business.title, - description: business.description, - email: business.email, - city: business.city, - state: sso.locations.find(l => l.value === business.state)?.name, - price: business.price, - realEstateIncluded: business.realEstateIncluded, - leasedLocation: business.leasedLocation, - franchiseResale: business.franchiseResale, - salesRevenue: business.salesRevenue, - cashFlow: business.cashFlow, - supportAndTraining: business.supportAndTraining, - employees: business.employees, - established: business.established, - reasonForSale: business.reasonForSale, - name: `${user.firstname} ${user.lastname}`, - }); - const embedding = await createEmbedding(embeddingText); + // const embeddingText = JSON.stringify({ + // type: typesOfBusiness.find(b => b.value === String(business.type))?.name, + // title: business.title, + // description: business.description, + // email: business.email, + // city: business.city, + // state: sso.locations.find(l => l.value === business.state)?.name, + // price: business.price, + // realEstateIncluded: business.realEstateIncluded, + // leasedLocation: business.leasedLocation, + // franchiseResale: business.franchiseResale, + // salesRevenue: business.salesRevenue, + // cashFlow: business.cashFlow, + // supportAndTraining: business.supportAndTraining, + // employees: business.employees, + // established: business.established, + // reasonForSale: business.reasonForSale, + // name: `${user.firstname} ${user.lastname}`, + // }); + // const embedding = await createEmbedding(embeddingText); sleep(200); - await db.insert(schema.businesses).values({ - ...business, - embedding: embedding, - }); - logger.info(`business_${index} inserted`); + await db.insert(schema.businesses).values(business); } //End diff --git a/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql b/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql new file mode 100644 index 0000000..4d3d684 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql @@ -0,0 +1,2 @@ +ALTER TABLE "businesses" ALTER COLUMN "type" SET DATA TYPE varchar(255);--> statement-breakpoint +ALTER TABLE "commercials" ALTER COLUMN "type" SET DATA TYPE varchar(255); \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..158db44 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json @@ -0,0 +1,549 @@ +{ + "id": "93be31d4-beec-4ba8-8d4a-a52763342335", + "prevId": "2d8edad3-5544-4cb1-a543-84c07737ea9f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.businesses": { + "name": "businesses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "listingsCategory": { + "name": "listingsCategory", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "realEstateIncluded": { + "name": "realEstateIncluded", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "leasedLocation": { + "name": "leasedLocation", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "franchiseResale": { + "name": "franchiseResale", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "salesRevenue": { + "name": "salesRevenue", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "cashFlow": { + "name": "cashFlow", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "supportAndTraining": { + "name": "supportAndTraining", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "employees": { + "name": "employees", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "established": { + "name": "established", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "internalListingNumber": { + "name": "internalListingNumber", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reasonForSale": { + "name": "reasonForSale", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "brokerLicencing": { + "name": "brokerLicencing", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "internals": { + "name": "internals", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imagePath": { + "name": "imagePath", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "businesses_email_users_email_fk": { + "name": "businesses_email_users_email_fk", + "tableFrom": "businesses", + "tableTo": "users", + "columnsFrom": [ + "email" + ], + "columnsTo": [ + "email" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.commercials": { + "name": "commercials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "serial_id": { + "name": "serial_id", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "favoritesForUser": { + "name": "favoritesForUser", + "type": "varchar(30)[]", + "primaryKey": false, + "notNull": false + }, + "listingsCategory": { + "name": "listingsCategory", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "hideImage": { + "name": "hideImage", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "zipCode": { + "name": "zipCode", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "county": { + "name": "county", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "imageOrder": { + "name": "imageOrder", + "type": "varchar(200)[]", + "primaryKey": false, + "notNull": false + }, + "imagePath": { + "name": "imagePath", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "visits": { + "name": "visits", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lastVisit": { + "name": "lastVisit", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "commercials_email_users_email_fk": { + "name": "commercials_email_users_email_fk", + "tableFrom": "commercials", + "tableTo": "users", + "columnsFrom": [ + "email" + ], + "columnsTo": [ + "email" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "firstname": { + "name": "firstname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "lastname": { + "name": "lastname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "phoneNumber": { + "name": "phoneNumber", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyName": { + "name": "companyName", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyOverview": { + "name": "companyOverview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyWebsite": { + "name": "companyWebsite", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "companyLocation": { + "name": "companyLocation", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "offeredServices": { + "name": "offeredServices", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "areasServed": { + "name": "areasServed", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "hasProfile": { + "name": "hasProfile", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "hasCompanyLogo": { + "name": "hasCompanyLogo", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "licensedIn": { + "name": "licensedIn", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "gender": { + "name": "gender", + "type": "gender", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "customerType": { + "name": "customerType", + "type": "customerType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "customerSubType": { + "name": "customerSubType", + "type": "customerSubType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + } + }, + "enums": { + "public.customerSubType": { + "name": "customerSubType", + "schema": "public", + "values": [ + "broker", + "cpa", + "attorney", + "titleCompany", + "surveyor", + "appraiser" + ] + }, + "public.customerType": { + "name": "customerType", + "schema": "public", + "values": [ + "buyer", + "professional" + ] + }, + "public.gender": { + "name": "gender", + "schema": "public", + "values": [ + "male", + "female" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/_journal.json b/bizmatch-server/src/drizzle/migrations/meta/_journal.json index 94b280d..26a0d44 100644 --- a/bizmatch-server/src/drizzle/migrations/meta/_journal.json +++ b/bizmatch-server/src/drizzle/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1720872296432, "tag": "0000_slim_nova", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1721134224160, + "tag": "0001_heavy_bloodscream", + "breakpoints": true } ] } \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/schema.ts b/bizmatch-server/src/drizzle/schema.ts index b39663f..d4fcade 100644 --- a/bizmatch-server/src/drizzle/schema.ts +++ b/bizmatch-server/src/drizzle/schema.ts @@ -32,7 +32,7 @@ export const users = pgTable('users', { export const businesses = pgTable('businesses', { id: uuid('id').primaryKey().defaultRandom(), email: varchar('email', { length: 255 }).references(() => users.email), - type: integer('type'), + type: varchar('type', { length: 255 }), title: varchar('title', { length: 255 }), description: text('description'), city: varchar('city', { length: 255 }), @@ -66,7 +66,7 @@ export const commercials = pgTable('commercials', { id: uuid('id').primaryKey().defaultRandom(), serialId: serial('serial_id'), email: varchar('email', { length: 255 }).references(() => users.email), - type: integer('type'), + type: varchar('type', { length: 255 }), title: varchar('title', { length: 255 }), description: text('description'), city: varchar('city', { length: 255 }), diff --git a/bizmatch-server/src/listings/business-listings.controller.ts b/bizmatch-server/src/listings/business-listings.controller.ts index 4644f49..8c85f49 100644 --- a/bizmatch-server/src/listings/business-listings.controller.ts +++ b/bizmatch-server/src/listings/business-listings.controller.ts @@ -3,7 +3,7 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; import { businesses } from '../drizzle/schema.js'; import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js'; -import { JwtUser, ListingCriteria } from '../models/main.model.js'; +import { BusinessListingCriteria, JwtUser } from '../models/main.model.js'; import { ListingsService } from './listings.service.js'; @Controller('listings/business') @@ -27,13 +27,13 @@ export class BusinessListingsController { @UseGuards(OptionalJwtAuthGuard) @Post('find') - find(@Request() req, @Body() criteria: ListingCriteria): any { + find(@Request() req, @Body() criteria: BusinessListingCriteria): any { return this.listingsService.findBusinessListings(criteria, req.user as JwtUser); } @UseGuards(OptionalJwtAuthGuard) @Post('search') - search(@Request() req, @Body() criteria: ListingCriteria): any { + search(@Request() req, @Body() criteria: BusinessListingCriteria): any { return this.listingsService.searchBusinessListings(criteria.prompt); } diff --git a/bizmatch-server/src/listings/commercial-property-listings.controller.ts b/bizmatch-server/src/listings/commercial-property-listings.controller.ts index a0e4a49..3c89899 100644 --- a/bizmatch-server/src/listings/commercial-property-listings.controller.ts +++ b/bizmatch-server/src/listings/commercial-property-listings.controller.ts @@ -5,7 +5,7 @@ import { commercials } from '../drizzle/schema.js'; import { FileService } from '../file/file.service.js'; import { OptionalJwtAuthGuard } from '../jwt-auth/optional-jwt-auth.guard.js'; import { CommercialPropertyListing } from '../models/db.model'; -import { JwtUser, ListingCriteria } from '../models/main.model.js'; +import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model.js'; import { ListingsService } from './listings.service.js'; @Controller('listings/commercialProperty') @@ -29,7 +29,7 @@ export class CommercialPropertyListingsController { } @UseGuards(OptionalJwtAuthGuard) @Post('find') - async find(@Request() req, @Body() criteria: ListingCriteria): Promise { + async find(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise { return await this.listingsService.findCommercialPropertyListings(criteria, req.user as JwtUser); } @Get('states/all') diff --git a/bizmatch-server/src/listings/listings.service.ts b/bizmatch-server/src/listings/listings.service.ts index f4fa35c..ec9d5e0 100644 --- a/bizmatch-server/src/listings/listings.service.ts +++ b/bizmatch-server/src/listings/listings.service.ts @@ -1,14 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; -import { and, eq, gte, ilike, lte, ne, or, sql } from 'drizzle-orm'; +import { and, eq, gte, ilike, inArray, lte, ne, or, sql } from 'drizzle-orm'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import OpenAI from 'openai'; import { Logger } from 'winston'; import * as schema from '../drizzle/schema.js'; -import { PG_CONNECTION, businesses, commercials } from '../drizzle/schema.js'; +import { businesses, commercials, PG_CONNECTION } from '../drizzle/schema.js'; import { FileService } from '../file/file.service.js'; import { BusinessListing, CommercialPropertyListing } from '../models/db.model'; -import { JwtUser, ListingCriteria, emailToDirName } from '../models/main.model.js'; +import { BusinessListingCriteria, CommercialPropertyListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js'; @Injectable() export class ListingsService { @@ -22,10 +22,10 @@ export class ListingsService { apiKey: process.env.OPENAI_API_KEY, // Stellen Sie sicher, dass Sie Ihren API-Key als Umgebungsvariable setzen }); } - private getConditions(criteria: ListingCriteria, table: typeof businesses | typeof commercials, user: JwtUser): any[] { + private getConditions(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria, table: typeof businesses | typeof commercials, user: JwtUser): any[] { const conditions = []; - if (criteria.type) { - conditions.push(eq(table.type, criteria.type)); + if (criteria.types?.length > 0) { + conditions.push(inArray(table.type, criteria.types)); } if (criteria.state) { conditions.push(eq(table.state, criteria.state)); @@ -36,9 +36,6 @@ export class ListingsService { if (criteria.maxPrice) { conditions.push(lte(table.price, criteria.maxPrice)); } - if (criteria.realEstateChecked) { - conditions.push(eq(businesses.realEstateIncluded, true)); - } if (criteria.title) { conditions.push(ilike(table.title, `%${criteria.title}%`)); } @@ -61,7 +58,7 @@ export class ListingsService { return results as BusinessListing[]; } // #### Find by criteria ######################################## - async findCommercialPropertyListings(criteria: ListingCriteria, user: JwtUser): Promise { + async findCommercialPropertyListings(criteria: CommercialPropertyListingCriteria, user: JwtUser): Promise { const start = criteria.start ? criteria.start : 0; const length = criteria.length ? criteria.length : 12; const conditions = this.getConditions(criteria, commercials, user); @@ -83,7 +80,7 @@ export class ListingsService { ]); return { total, data }; } - async findBusinessListings(criteria: ListingCriteria, user: JwtUser): Promise { + async findBusinessListings(criteria: BusinessListingCriteria, user: JwtUser): Promise { const start = criteria.start ? criteria.start : 0; const length = criteria.length ? criteria.length : 12; const conditions = this.getConditions(criteria, businesses, user); diff --git a/bizmatch-server/src/models/db.model.ts b/bizmatch-server/src/models/db.model.ts index 349541a..9b656f8 100644 --- a/bizmatch-server/src/models/db.model.ts +++ b/bizmatch-server/src/models/db.model.ts @@ -45,7 +45,7 @@ export interface UserData { export interface BusinessListing { id: string; email?: string; - type?: number; + type?: string; title?: string; description?: string; city?: string; @@ -77,7 +77,7 @@ export interface CommercialPropertyListing { id: string; serialId?: number; email?: string; - type?: number; + type?: string; title?: string; description?: string; city?: string; diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index a2f4e72..c595e3d 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -16,6 +16,7 @@ export interface KeyValueRatio { export interface KeyValueStyle { name: string; value: string; + oldValue?: string; icon: string; textColorClass: string; } @@ -52,20 +53,51 @@ export type ResponseUsersArray = { data: User[]; total: number; }; -export interface ListingCriteria { +export interface ListCriteria { start: number; length: number; page: number; pageCount: number; - type: number; + city: string; + types: string[]; + prompt: string; + criteriaType: 'business' | 'commercialProperty' | 'user'; +} +export interface BusinessListingCriteria extends ListCriteria { state: string; + county: string; minPrice: number; maxPrice: number; + minRevenue: number; + maxRevenue: number; + minCashFlow: number; + maxCashFlow: number; + minNumberEmployees: number; + maxNumberEmployees: number; + establishedSince: number; + establishedUntil: number; realEstateChecked: boolean; + leasedLocation: boolean; + franchiseResale: boolean; title: string; - category: 'professional' | 'broker'; - name: string; - prompt: string; + brokerName: string; + criteriaType: 'business'; +} +export interface CommercialPropertyListingCriteria extends ListCriteria { + state: string; + county: string; + minPrice: number; + maxPrice: number; + title: string; + criteriaType: 'commercialProperty'; +} +export interface UserListingCriteria extends ListCriteria { + firstname: string; + lastname: string; + companyName: string; + counties: string[]; + states: string[]; + criteriaType: 'user'; } export interface KeycloakUser { diff --git a/bizmatch-server/src/select-options/select-options.service.ts b/bizmatch-server/src/select-options/select-options.service.ts index 1f2c4f3..d79da62 100644 --- a/bizmatch-server/src/select-options/select-options.service.ts +++ b/bizmatch-server/src/select-options/select-options.service.ts @@ -5,28 +5,28 @@ import { ImageType, KeyValue, KeyValueStyle } from '../models/main.model.js'; export class SelectOptionsService { constructor() {} public typesOfBusiness: Array = [ - { name: 'Automotive', value: '1', icon: 'fa-solid fa-car', textColorClass: 'text-green-400' }, - { name: 'Industrial Services', value: '2', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' }, - { name: 'Real Estate', value: '3', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' }, - { name: 'Uncategorized', value: '4', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' }, - { name: 'Retail', value: '5', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' }, - { name: 'Oilfield SVE and MFG.', value: '6', icon: 'fa-solid fa-oil-well', textColorClass: 'text-indigo-400' }, - { name: 'Service', value: '7', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' }, - { name: 'Advertising', value: '8', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' }, - { name: 'Agriculture', value: '9', icon: 'fa-solid fa-wheat-awn', textColorClass: 'text-sky-400' }, - { name: 'Franchise', value: '10', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' }, - { name: 'Professional', value: '11', icon: 'fa-solid fa-user-gear', textColorClass: 'text-gray-400' }, - { name: 'Manufacturing', value: '12', icon: 'fa-solid fa-industry', textColorClass: 'text-red-400' }, - { name: 'Food and Restaurant', value: '13', icon: 'fa-solid fa-utensils', textColorClass: 'text-amber-700' }, + { name: 'Automotive', value: 'automotive', oldValue: '1', icon: 'fa-solid fa-car', textColorClass: 'text-green-400' }, + { name: 'Industrial Services', value: 'industrialServices', oldValue: '2', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' }, + { name: 'Food and Restaurant', value: 'foodAndRestaurant', oldValue: '13', icon: 'fa-solid fa-utensils', textColorClass: 'text-amber-700' }, + { name: 'Real Estate', value: 'realEstate', oldValue: '3', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' }, + { name: 'Retail', value: 'retail', oldValue: '5', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' }, + { name: 'Oilfield SVE and MFG.', value: 'oilfield', oldValue: '6', icon: 'fa-solid fa-oil-well', textColorClass: 'text-indigo-400' }, + { name: 'Service', value: 'service', oldValue: '7', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' }, + { name: 'Advertising', value: 'advertising', oldValue: '8', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' }, + { name: 'Agriculture', value: 'agriculture', oldValue: '9', icon: 'fa-solid fa-wheat-awn', textColorClass: 'text-sky-400' }, + { name: 'Franchise', value: 'franchise', oldValue: '10', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' }, + { name: 'Professional', value: 'professional', oldValue: '11', icon: 'fa-solid fa-user-gear', textColorClass: 'text-gray-400' }, + { name: 'Manufacturing', value: 'manufacturing', oldValue: '12', icon: 'fa-solid fa-industry', textColorClass: 'text-red-400' }, + { name: 'Uncategorized', value: 'uncategorized', oldValue: '4', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' }, ]; public typesOfCommercialProperty: Array = [ - { name: 'Retail', value: '100', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' }, - { name: 'Land', value: '101', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' }, - { name: 'Industrial', value: '102', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' }, - { name: 'Office', value: '103', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' }, - { name: 'Mixed Use', value: '104', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' }, - { name: 'Multifamily', value: '105', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' }, - { name: 'Uncategorized', value: '106', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' }, + { name: 'Retail', value: 'retail', oldValue: '100', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' }, + { name: 'Land', value: 'land', oldValue: '101', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' }, + { name: 'Industrial', value: 'industrial', oldValue: '102', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' }, + { name: 'Office', value: 'office', oldValue: '103', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' }, + { name: 'Mixed Use', value: 'mixedUse', oldValue: '104', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' }, + { name: 'Multifamily', value: 'multifamily', oldValue: '105', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' }, + { name: 'Uncategorized', value: 'uncategorized', oldValue: '106', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' }, ]; public prices: Array = [ { name: '$100K', value: '100000' }, diff --git a/bizmatch-server/src/user/user.service.ts b/bizmatch-server/src/user/user.service.ts index ed8912c..f3acb45 100644 --- a/bizmatch-server/src/user/user.service.ts +++ b/bizmatch-server/src/user/user.service.ts @@ -7,7 +7,7 @@ import * as schema from '../drizzle/schema.js'; import { PG_CONNECTION } from '../drizzle/schema.js'; import { FileService } from '../file/file.service.js'; import { User } from '../models/db.model.js'; -import { ListingCriteria, emailToDirName } from '../models/main.model.js'; +import { UserListingCriteria, emailToDirName } from '../models/main.model.js'; @Injectable() export class UserService { @@ -16,14 +16,14 @@ export class UserService { @Inject(PG_CONNECTION) private conn: NodePgDatabase, private fileService: FileService, ) {} - private getConditions(criteria: ListingCriteria): any[] { + private getConditions(criteria: UserListingCriteria): any[] { const conditions = []; if (criteria.state) { //conditions.push(sql`EXISTS (SELECT 1 FROM unnest(users."areasServed") AS area WHERE area LIKE '%' || ${criteria.state} || '%')`); conditions.push(sql`${schema.users.areasServed} @> ${JSON.stringify([{ state: criteria.state }])}`); } - if (criteria.name) { - conditions.push(or(ilike(schema.users.firstname, `%${criteria.name}%`), ilike(schema.users.lastname, `%${criteria.name}%`))); + if (criteria.firstname || criteria.lastname) { + conditions.push(or(ilike(schema.users.firstname, `%${criteria.lastname}%`), ilike(schema.users.lastname, `%${criteria.lastname}%`))); } return conditions; } @@ -60,7 +60,7 @@ export class UserService { return newUser as User; } } - async findUser(criteria: ListingCriteria) { + async findUser(criteria: UserListingCriteria) { const start = criteria.start ? criteria.start : 0; const length = criteria.length ? criteria.length : 12; const conditions = this.getConditions(criteria); diff --git a/bizmatch/src/app/app.component.html b/bizmatch/src/app/app.component.html index 1364f24..92f37ba 100644 --- a/bizmatch/src/app/app.component.html +++ b/bizmatch/src/app/app.component.html @@ -31,3 +31,4 @@ } + diff --git a/bizmatch/src/app/app.component.ts b/bizmatch/src/app/app.component.ts index 09e47cd..61be7a4 100644 --- a/bizmatch/src/app/app.component.ts +++ b/bizmatch/src/app/app.component.ts @@ -2,21 +2,20 @@ import { CommonModule } from '@angular/common'; import { Component, HostListener } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router, RouterOutlet } from '@angular/router'; import { KeycloakService } from 'keycloak-angular'; -import onChange from 'on-change'; import { filter } from 'rxjs/operators'; -import { ListingCriteria } from '../../../bizmatch-server/src/models/main.model'; import build from '../build'; import { FooterComponent } from './components/footer/footer.component'; import { HeaderComponent } from './components/header/header.component'; import { MessageContainerComponent } from './components/message/message-container.component'; +import { SearchModalComponent } from './components/search-modal/search-modal.component'; import { LoadingService } from './services/loading.service'; import { UserService } from './services/user.service'; -import { createDefaultListingCriteria } from './utils/utils'; + @Component({ selector: 'app-root', standalone: true, - imports: [CommonModule, RouterOutlet, HeaderComponent, FooterComponent, MessageContainerComponent], + imports: [CommonModule, RouterOutlet, HeaderComponent, FooterComponent, MessageContainerComponent, SearchModalComponent], providers: [], templateUrl: './app.component.html', styleUrl: './app.component.scss', @@ -25,9 +24,7 @@ export class AppComponent { build = build; title = 'bizmatch'; actualRoute = ''; - listingCriteria: ListingCriteria = onChange(createDefaultListingCriteria(), (path, value, previousValue, applyData) => { - sessionStorage.setItem('criteria', JSON.stringify(value)); - }); + public constructor(public loadingService: LoadingService, private router: Router, private activatedRoute: ActivatedRoute, private keycloakService: KeycloakService, private userService: UserService) { this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { let currentRoute = this.activatedRoute.root; diff --git a/bizmatch/src/app/components/dropdown/dropdown.component.ts b/bizmatch/src/app/components/dropdown/dropdown.component.ts index 468b220..19a7374 100644 --- a/bizmatch/src/app/components/dropdown/dropdown.component.ts +++ b/bizmatch/src/app/components/dropdown/dropdown.component.ts @@ -4,7 +4,7 @@ import { createPopper, Instance as PopperInstance } from '@popperjs/core'; @Component({ selector: 'app-dropdown', template: ` -
+
`, diff --git a/bizmatch/src/app/components/header/header.component.html b/bizmatch/src/app/components/header/header.component.html index 40a6660..332bd1a 100644 --- a/bizmatch/src/app/components/header/header.component.html +++ b/bizmatch/src/app/components/header/header.component.html @@ -120,6 +120,7 @@
--> + - -
-

Filter

- - + +
- - - - - - - - - + -
-
+ --> diff --git a/bizmatch/src/app/components/header/header.component.ts b/bizmatch/src/app/components/header/header.component.ts index ebe3d15..f553d0e 100644 --- a/bizmatch/src/app/components/header/header.component.ts +++ b/bizmatch/src/app/components/header/header.component.ts @@ -1,4 +1,4 @@ -import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; +import { BreakpointObserver } from '@angular/cdk/layout'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; @@ -6,14 +6,15 @@ import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { faUserGear } from '@fortawesome/free-solid-svg-icons'; import { Collapse, Dropdown, initFlowbite } from 'flowbite'; import { KeycloakService } from 'keycloak-angular'; -import { Observable, Subject, takeUntil } from 'rxjs'; +import { Observable, Subject, Subscription } from 'rxjs'; import { User } from '../../../../../bizmatch-server/src/models/db.model'; import { emailToDirName, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../environments/environment'; import { SharedService } from '../../services/shared.service'; import { UserService } from '../../services/user.service'; -import { map2User } from '../../utils/utils'; +import { createEmptyBusinessListingCriteria, createEmptyCommercialPropertyListingCriteria, createEmptyUserListingCriteria, map2User } from '../../utils/utils'; import { DropdownComponent } from '../dropdown/dropdown.component'; +import { ModalService } from '../search-modal/modal.service'; @Component({ selector: 'header', standalone: true, @@ -34,7 +35,15 @@ export class HeaderComponent { isMobile: boolean = false; private destroy$ = new Subject(); prompt: string; - constructor(public keycloakService: KeycloakService, private router: Router, private userService: UserService, private sharedService: SharedService, private breakpointObserver: BreakpointObserver) {} + private subscription: Subscription; + constructor( + public keycloakService: KeycloakService, + private router: Router, + private userService: UserService, + private sharedService: SharedService, + private breakpointObserver: BreakpointObserver, + private modalService: ModalService, + ) {} async ngOnInit() { const token = await this.keycloakService.getToken(); @@ -52,30 +61,40 @@ export class HeaderComponent { initFlowbite(); } }); - this.breakpointObserver - .observe([Breakpoints.Handset]) - .pipe(takeUntil(this.destroy$)) - .subscribe(result => { - this.isMobile = result.matches; - const targetEl = document.getElementById('filterDropdown'); - const triggerEl = this.isMobile ? document.getElementById('filterDropdownMobileButton') : document.getElementById('filterDropdownButton'); - if (targetEl && triggerEl) { - this.filterDropdown = new Dropdown(targetEl, triggerEl); - } - }); + + // this.breakpointObserver + // .observe([Breakpoints.Handset]) + // .pipe(takeUntil(this.destroy$)) + // .subscribe(result => { + // this.isMobile = result.matches; + // const targetEl = document.getElementById('filterDropdown'); + // const triggerEl = this.isMobile ? document.getElementById('filterDropdownMobileButton') : document.getElementById('filterDropdownButton'); + // if (targetEl && triggerEl) { + // this.filterDropdown = new Dropdown(targetEl, triggerEl); + // } + // }); this.sharedService.currentProfilePhoto.subscribe(photoUrl => { if (photoUrl) { this.profileUrl = photoUrl; } }); } - toggleFilterDropdown() { - if (this.filterDropdown) { - this.filterDropdown.toggle(); + + // toggleFilterDropdown() { + // if (this.filterDropdown) { + // this.filterDropdown.toggle(); + // } + // } + ngAfterViewInit() {} + openModal() { + if (this.isActive('/businessListings')) { + this.modalService.showModal(createEmptyBusinessListingCriteria()); + } else if (this.isActive('/commercialPropertyListings')) { + this.modalService.showModal(createEmptyCommercialPropertyListingCriteria()); + } else if (this.isActive('/brokerListings')) { + this.modalService.showModal(createEmptyUserListingCriteria()); } } - ngAfterViewInit() {} - navigateWithState(dest: string, state: any) { this.router.navigate([dest], { state: state }); } diff --git a/bizmatch/src/app/components/search-modal/modal.service.ts b/bizmatch/src/app/components/search-modal/modal.service.ts new file mode 100644 index 0000000..259d487 --- /dev/null +++ b/bizmatch/src/app/components/search-modal/modal.service.ts @@ -0,0 +1,34 @@ +// 1. Shared Service (modal.service.ts) +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { BusinessListingCriteria, CommercialPropertyListingCriteria, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; + +@Injectable({ + providedIn: 'root', +}) +export class ModalService { + private modalVisibleSubject = new BehaviorSubject(false); + private messageSubject = new BehaviorSubject(null); + private resolvePromise!: (value: boolean) => void; + + modalVisible$: Observable = this.modalVisibleSubject.asObservable(); + message$: Observable = this.messageSubject.asObservable(); + + showModal(message: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria): Promise { + this.messageSubject.next(message); + this.modalVisibleSubject.next(true); + return new Promise(resolve => { + this.resolvePromise = resolve; + }); + } + + accept(): void { + this.modalVisibleSubject.next(false); + this.resolvePromise(true); + } + + reject(): void { + this.modalVisibleSubject.next(false); + this.resolvePromise(false); + } +} diff --git a/bizmatch/src/app/components/search-modal/search-modal.component.html b/bizmatch/src/app/components/search-modal/search-modal.component.html new file mode 100644 index 0000000..b473929 --- /dev/null +++ b/bizmatch/src/app/components/search-modal/search-modal.component.html @@ -0,0 +1,332 @@ + +
+
+
+
+

Business Listing Search

+ +
+
+
+ + +
+ @if(criteria.criteriaType==='business'){ +
+
+
+ + +
+
+ + +
+ +
+ +
+ + - + +
+
+
+ +
+ + - + +
+
+
+ +
+ + - + +
+
+
+ + +
+
+ + +
+
+
+
+ +
+ @for(tob of selectOptions.typesOfBusiness; track tob){ +
+ + +
+ } +
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+ + - + +
+
+
+ +
+ + - + +
+
+
+
+ } @if(criteria.criteriaType==='commercialProperty'){ +
+
+
+ + +
+
+ + +
+
+ +
+ + - + +
+
+
+ + +
+
+
+
+ +
+ @for(tob of selectOptions.typesOfCommercialProperty; track tob){ +
+ + +
+ } +
+
+
+
+ } @if(criteria.criteriaType==='user'){ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+ @for(tob of selectOptions.customerSubTypes; track tob){ +
+ + +
+ } +
+
+
+
+ } +
+
+ + +
+
+
+
diff --git a/bizmatch/src/app/components/search-modal/search-modal.component.scss b/bizmatch/src/app/components/search-modal/search-modal.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/bizmatch/src/app/components/search-modal/search-modal.component.ts b/bizmatch/src/app/components/search-modal/search-modal.component.ts new file mode 100644 index 0000000..6084705 --- /dev/null +++ b/bizmatch/src/app/components/search-modal/search-modal.component.ts @@ -0,0 +1,46 @@ +import { AsyncPipe, NgIf } from '@angular/common'; +import { Component } from '@angular/core'; +import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeyValue, KeyValueStyle, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; +import { SelectOptionsService } from '../../services/select-options.service'; +import { SharedModule } from '../../shared/shared/shared.module'; +import { ModalService } from './modal.service'; + +@Component({ + selector: 'app-search-modal', + standalone: true, + imports: [SharedModule, AsyncPipe, NgIf], + templateUrl: './search-modal.component.html', +}) +export class SearchModalComponent { + constructor(public selectOptions: SelectOptionsService, public modalService: ModalService) {} + ngOnInit() { + this.modalService.message$.subscribe(msg => { + this.criteria = msg; + }); + } + public criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria; + + categoryClicked(checked: boolean, value: string) { + if (checked) { + this.criteria.types.push(value); + } else { + const index = this.criteria.types.findIndex(t => t === value); + if (index > -1) { + this.criteria.types.splice(index, 1); + } + } + } + search() { + console.log('Search criteria:', this.criteria); + } + + closeModal() { + console.log('Closing modal'); + } + isTypeOfBusinessClicked(v: KeyValueStyle) { + return this.criteria.types.find(t => t === v.value); + } + isTypeOfProfessionalClicked(v: KeyValue) { + return this.criteria.types.find(t => t === v.value); + } +} 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 b01b81d..9ef33e3 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 @@ -5,7 +5,7 @@ import { KeycloakService } from 'keycloak-angular'; import onChange from 'on-change'; import { lastValueFrom } from 'rxjs'; import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model'; +import { BusinessListingCriteria, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { HistoryService } from '../../../services/history.service'; import { ListingsService } from '../../../services/listings.service'; @@ -43,7 +43,7 @@ export class DetailsBusinessListingComponent { ]; private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined; listing: BusinessListing; - criteria: ListingCriteria; + criteria: BusinessListingCriteria; mailinfo: MailInfo; environment = environment; keycloakUser: KeycloakUser; @@ -70,7 +70,7 @@ export class DetailsBusinessListingComponent { } }); this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl }; - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler); } async ngOnInit() { 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 91e39fb..6e00fc8 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 @@ -6,7 +6,7 @@ import { KeycloakService } from 'keycloak-angular'; import onChange from 'on-change'; import { lastValueFrom } from 'rxjs'; import { CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { ErrorResponse, KeycloakUser, ListingCriteria, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model'; +import { CommercialPropertyListingCriteria, ErrorResponse, KeycloakUser, MailInfo } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { HistoryService } from '../../../services/history.service'; import { ImageService } from '../../../services/image.service'; @@ -45,7 +45,7 @@ export class DetailsCommercialPropertyListingComponent { ]; private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined; listing: CommercialPropertyListing; - criteria: ListingCriteria; + criteria: CommercialPropertyListingCriteria; mailinfo: MailInfo; environment = environment; keycloakUser: KeycloakUser; @@ -73,7 +73,7 @@ export class DetailsCommercialPropertyListingComponent { ) { this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl }; - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria = onChange(getCriteriaStateObject('commercialProperty'), getSessionStorageHandler); } async ngOnInit() { diff --git a/bizmatch/src/app/pages/details/details-user/details-user.component.ts b/bizmatch/src/app/pages/details/details-user/details-user.component.ts index 3986041..7d01357 100644 --- a/bizmatch/src/app/pages/details/details-user/details-user.component.ts +++ b/bizmatch/src/app/pages/details/details-user/details-user.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { KeycloakService } from 'keycloak-angular'; import { Observable } from 'rxjs'; import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { KeycloakUser, ListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; +import { KeycloakUser, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { HistoryService } from '../../../services/history.service'; import { ImageService } from '../../../services/image.service'; @@ -27,7 +27,6 @@ export class DetailsUserComponent { user$: Observable; keycloakUser: KeycloakUser; environment = environment; - criteria: ListingCriteria; businessListings: BusinessListing[]; commercialPropListings: CommercialPropertyListing[]; companyOverview: SafeHtml; diff --git a/bizmatch/src/app/pages/home/home.component.ts b/bizmatch/src/app/pages/home/home.component.ts index 85b4397..db546a6 100644 --- a/bizmatch/src/app/pages/home/home.component.ts +++ b/bizmatch/src/app/pages/home/home.component.ts @@ -4,10 +4,10 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { KeycloakService } from 'keycloak-angular'; import onChange from 'on-change'; -import { KeycloakUser, ListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; +import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; import { ListingsService } from '../../services/listings.service'; import { SelectOptionsService } from '../../services/select-options.service'; -import { getCriteriaStateObject, getSessionStorageHandler, map2User, resetCriteria } from '../../utils/utils'; +import { getCriteriaStateObject, getSessionStorageHandler, map2User } from '../../utils/utils'; @Component({ selector: 'app-home', standalone: true, @@ -20,14 +20,13 @@ export class HomeComponent { type: string; maxPrice: string; minPrice: string; - criteria: ListingCriteria; + criteria: BusinessListingCriteria | CommercialPropertyListingCriteria | UserListingCriteria; states = []; isMenuOpen = false; user: KeycloakUser; prompt: string; public constructor(private router: Router, private activatedRoute: ActivatedRoute, public selectOptions: SelectOptionsService, public keycloakService: KeycloakService, private listingsService: ListingsService) { - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); - resetCriteria(this.criteria); + this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler); } async ngOnInit() { const token = await this.keycloakService.getToken(); 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 e503d26..c30fe23 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 @@ -4,13 +4,13 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import onChange from 'on-change'; import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; +import { ListingType, UserListingCriteria, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { ImageService } from '../../../services/image.service'; import { ListingsService } from '../../../services/listings.service'; import { SelectOptionsService } from '../../../services/select-options.service'; import { UserService } from '../../../services/user.service'; -import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils'; +import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils'; @Component({ selector: 'app-broker-listings', @@ -24,7 +24,7 @@ export class BrokerListingsComponent { listings: Array; users: Array; filteredListings: Array; - criteria: ListingCriteria; + criteria: UserListingCriteria; realEstateChecked: boolean; maxPrice: string; minPrice: string; @@ -49,10 +49,9 @@ export class BrokerListingsComponent { private imageService: ImageService, private route: ActivatedRoute, ) { - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria = onChange(getCriteriaStateObject('user'), getSessionStorageHandler); this.route.data.subscribe(async () => { if (this.router.getCurrentNavigation().extras.state) { - resetCriteria(this.criteria); } else { this.first = this.criteria.page * this.criteria.length; this.rows = this.criteria.length; @@ -86,7 +85,5 @@ export class BrokerListingsComponent { this.criteria.pageCount = event.pageCount; this.search(); } - reset() { - this.criteria.name = ''; - } + reset() {} } 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 db3b469..19051f9 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,12 +4,12 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import onChange from 'on-change'; import { BusinessListing } from '../../../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; +import { BusinessListingCriteria, ListingType, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { ImageService } from '../../../services/image.service'; import { ListingsService } from '../../../services/listings.service'; import { SelectOptionsService } from '../../../services/select-options.service'; -import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils'; +import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils'; @Component({ selector: 'app-business-listings', @@ -22,7 +22,7 @@ export class BusinessListingsComponent { environment = environment; listings: Array; filteredListings: Array; - criteria: ListingCriteria; + criteria: BusinessListingCriteria; realEstateChecked: boolean; maxPrice: string; minPrice: string; @@ -45,10 +45,9 @@ export class BusinessListingsComponent { private imageService: ImageService, private route: ActivatedRoute, ) { - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandler); this.route.data.subscribe(async () => { if (this.router.getCurrentNavigation().extras.state) { - resetCriteria(this.criteria); } else { this.first = this.criteria.page * this.criteria.length; this.rows = this.criteria.length; @@ -70,12 +69,12 @@ export class BusinessListingsComponent { this.search(); } async search() { - this.listings = await this.listingsService.getListingsByPrompt(this.criteria); - // const listingReponse = await this.listingsService.getListings(this.criteria, 'business'); - // this.listings = listingReponse.data; - // this.totalRecords = listingReponse.total; - // this.cdRef.markForCheck(); - // this.cdRef.detectChanges(); + //this.listings = await this.listingsService.getListingsByPrompt(this.criteria); + const listingReponse = await this.listingsService.getListings(this.criteria, 'business'); + this.listings = listingReponse.data; + this.totalRecords = listingReponse.total; + this.cdRef.markForCheck(); + this.cdRef.detectChanges(); } onPageChange(event: any) { this.criteria.start = event.first; 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 f435506..5705004 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,12 +4,12 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import onChange from 'on-change'; import { CommercialPropertyListing } from '../../../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model'; +import { CommercialPropertyListingCriteria } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { ImageService } from '../../../services/image.service'; import { ListingsService } from '../../../services/listings.service'; import { SelectOptionsService } from '../../../services/select-options.service'; -import { getCriteriaStateObject, getSessionStorageHandler, resetCriteria } from '../../../utils/utils'; +import { getCriteriaStateObject, getSessionStorageHandler } from '../../../utils/utils'; @Component({ selector: 'app-commercial-property-listings', @@ -22,7 +22,7 @@ export class CommercialPropertyListingsComponent { environment = environment; listings: Array; filteredListings: Array; - criteria: ListingCriteria; + criteria: CommercialPropertyListingCriteria; realEstateChecked: boolean; first: number = 0; rows: number = 12; @@ -44,10 +44,9 @@ export class CommercialPropertyListingsComponent { private imageService: ImageService, private route: ActivatedRoute, ) { - this.criteria = onChange(getCriteriaStateObject(), getSessionStorageHandler); + this.criteria = onChange(getCriteriaStateObject('commercialProperty'), getSessionStorageHandler); this.route.data.subscribe(async () => { if (this.router.getCurrentNavigation().extras.state) { - resetCriteria(this.criteria); } else { this.first = this.criteria.page * this.criteria.length; this.rows = this.criteria.length; diff --git a/bizmatch/src/app/services/listings.service.ts b/bizmatch/src/app/services/listings.service.ts index dd5cc6a..463d269 100644 --- a/bizmatch/src/app/services/listings.service.ts +++ b/bizmatch/src/app/services/listings.service.ts @@ -2,7 +2,14 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, lastValueFrom } from 'rxjs'; import { BusinessListing } from '../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria, ListingType, ResponseBusinessListingArray, ResponseCommercialPropertyListingArray, StatesResult } from '../../../../bizmatch-server/src/models/main.model'; +import { + BusinessListingCriteria, + CommercialPropertyListingCriteria, + ListingType, + ResponseBusinessListingArray, + ResponseCommercialPropertyListingArray, + StatesResult, +} from '../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../environments/environment'; @Injectable({ @@ -12,11 +19,14 @@ export class ListingsService { private apiBaseUrl = environment.apiBaseUrl; constructor(private http: HttpClient) {} - async getListings(criteria: ListingCriteria, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty'): Promise { + 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)); return result; } - async getListingsByPrompt(criteria: ListingCriteria): Promise { + async getListingsByPrompt(criteria: BusinessListingCriteria | CommercialPropertyListingCriteria): Promise { const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/business/search`, criteria)); return result; } diff --git a/bizmatch/src/app/services/select-options.service.ts b/bizmatch/src/app/services/select-options.service.ts index 921ef64..f02f124 100644 --- a/bizmatch/src/app/services/select-options.service.ts +++ b/bizmatch/src/app/services/select-options.service.ts @@ -39,11 +39,11 @@ export class SelectOptionsService { getState(value: string): string { return this.states.find(l => l.value === value)?.name; } - getBusiness(value: number): string { - return this.typesOfBusiness.find(t => t.value === String(value))?.name; + getBusiness(value: string): string { + return this.typesOfBusiness.find(t => t.value === value)?.name; } - getCommercialProperty(value: number): string { - return this.typesOfCommercialProperty.find(t => t.value === String(value))?.name; + getCommercialProperty(value: string): string { + return this.typesOfCommercialProperty.find(t => t.value === value)?.name; } getListingsCategory(value: string): string { return this.listingCategories.find(l => l.value === value)?.name; @@ -57,14 +57,14 @@ export class SelectOptionsService { getIconType(value: string): string { return this.typesOfBusiness.find(c => c.value === value)?.icon; } - getTextColorType(value: number): string { - return this.typesOfBusiness.find(c => c.value === String(value))?.textColorClass; + getTextColorType(value: string): string { + return this.typesOfBusiness.find(c => c.value === value)?.textColorClass; } - getIconAndTextColorType(value: number): string { - const category = this.typesOfBusiness.find(c => c.value === String(value)); + getIconAndTextColorType(value: string): string { + const category = this.typesOfBusiness.find(c => c.value === value); return `${category?.icon} ${category?.textColorClass}`; } - getIconTypeOfCommercials(value: number): string { - return this.typesOfCommercialProperty.find(c => c.value === String(value))?.icon; + getIconTypeOfCommercials(value: string): string { + return this.typesOfCommercialProperty.find(c => c.value === value)?.icon; } } diff --git a/bizmatch/src/app/services/user.service.ts b/bizmatch/src/app/services/user.service.ts index 6dc8fe1..85cca21 100644 --- a/bizmatch/src/app/services/user.service.ts +++ b/bizmatch/src/app/services/user.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { lastValueFrom } from 'rxjs'; import urlcat from 'urlcat'; import { User } from '../../../../bizmatch-server/src/models/db.model'; -import { ListingCriteria, ResponseUsersArray, StatesResult } from '../../../../bizmatch-server/src/models/main.model'; +import { ResponseUsersArray, StatesResult, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../environments/environment'; @Injectable({ @@ -27,7 +27,7 @@ export class UserService { const url = urlcat(`${this.apiBaseUrl}/bizmatch/user`, { mail }); return await lastValueFrom(this.http.get(url)); } - async search(criteria?: ListingCriteria): Promise { + async search(criteria?: UserListingCriteria): Promise { return await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/user/search`, criteria)); } async getAllStates(): Promise { diff --git a/bizmatch/src/app/utils/utils.ts b/bizmatch/src/app/utils/utils.ts index f6bbe43..1b9bb20 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 { BusinessListing, CommercialPropertyListing, User } from '../../../../bizmatch-server/src/models/db.model'; -import { JwtToken, KeycloakUser, ListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; +import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, KeycloakUser, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model'; export function createDefaultUser(email: string, firstname: string, lastname: string): User { return { @@ -82,21 +82,69 @@ export function createDefaultBusinessListing(): BusinessListing { listingsCategory: 'business', }; } -export function createDefaultListingCriteria(): ListingCriteria { +export function createEmptyBusinessListingCriteria(): BusinessListingCriteria { return { start: 0, - length: 12, + length: 0, page: 0, pageCount: 0, - type: 0, state: '', + city: '', + types: [], + prompt: '', + criteriaType: 'business', + county: '', + minPrice: null, + maxPrice: null, + minRevenue: null, + maxRevenue: null, + minCashFlow: null, + maxCashFlow: null, + minNumberEmployees: null, + maxNumberEmployees: null, + establishedSince: null, + establishedUntil: null, + realEstateChecked: false, + leasedLocation: false, + franchiseResale: false, + title: '', + brokerName: '', + }; +} + +export function createEmptyCommercialPropertyListingCriteria(): CommercialPropertyListingCriteria { + return { + start: 0, + length: 0, + page: 0, + pageCount: 0, + state: '', + city: '', + types: [], + prompt: '', + criteriaType: 'commercialProperty', + county: '', minPrice: 0, maxPrice: 0, - realEstateChecked: false, title: '', - category: 'broker', - name: '', + }; +} + +export function createEmptyUserListingCriteria(): UserListingCriteria { + return { + start: 0, + length: 0, + page: 0, + pageCount: 0, + city: '', + types: [], prompt: '', + criteriaType: 'user', + firstname: '', + lastname: '', + companyName: '', + counties: [], + states: [], }; } export function createLogger(name: string, level: number = INFO, options: any = {}) { @@ -120,8 +168,15 @@ export const getSessionStorageHandler = function (path, value, previous, applyDa sessionStorage.setItem('criteria', JSON.stringify(this)); }; -export function getCriteriaStateObject() { - const initialState = createDefaultListingCriteria(); +export function getCriteriaStateObject(criteriaType: 'business' | 'commercialProperty' | 'user') { + let initialState; + if (criteriaType === 'business') { + initialState = createEmptyBusinessListingCriteria(); + } else if (criteriaType === 'commercialProperty') { + initialState = createEmptyCommercialPropertyListingCriteria(); + } else { + initialState = createEmptyUserListingCriteria(); + } const storedState = sessionStorage.getItem('criteria'); return storedState ? JSON.parse(storedState) : initialState; } @@ -134,17 +189,6 @@ export function routeListingWithState(router: Router, value: string, data: any) } } -export function resetCriteria(criteria: ListingCriteria) { - criteria.type = null; - criteria.state = null; - criteria.minPrice = null; - criteria.maxPrice = null; - criteria.start = 0; - criteria.length = 12; - criteria.realEstateChecked = null; - criteria.title = null; - criteria.name = null; -} export function map2User(jwt: string): KeycloakUser { if (jwt) { const token = jwtDecode(jwt);