diff --git a/bizmatch-server/data/businesses.json b/bizmatch-server/data/businesses.json index 0059418..24e59ef 100644 --- a/bizmatch-server/data/businesses.json +++ b/bizmatch-server/data/businesses.json @@ -11922,7 +11922,7 @@ "description": "

Thriving Caribbean Restaurant in the Heart of Washington, D.C.

Well-established Caribbean restaurant with a loyal customer base. Known for its authentic cuisine and lively atmosphere.

Fully equipped kitchen and dining area. Experienced staff and management team in place. Strong financials and consistent growth. Perfect opportunity for someone in the food service industry or looking for a profitable investment.

", "type": 13, "state": "DC", - "city": "Washington", + "city": "Washington D.C.", "id": "b148ad7f-7f4a-4319-bc8c-df23cf85be3b", "price": 1500000, "salesRevenue": 2200000, @@ -12322,7 +12322,7 @@ "description": "

Thriving Indian Restaurant in the Heart of Washington, D.C.

Well-established Indian restaurant with a loyal customer base. Known for its authentic cuisine and inviting atmosphere.

Fully equipped kitchen and dining area. Experienced staff and management team in place. Strong financials and consistent growth. Perfect opportunity for someone in the food service industry or looking for a profitable investment.

", "type": 13, "state": "DC", - "city": "Washington", + "city": "Washington D.C.", "id": "d8d0f4e3-fdbf-4c4b-b2bd-d8847f0d248f", "price": 1500000, "salesRevenue": 2200000, diff --git a/bizmatch-server/data/commercials.json b/bizmatch-server/data/commercials.json index c530856..8353b21 100644 --- a/bizmatch-server/data/commercials.json +++ b/bizmatch-server/data/commercials.json @@ -344,7 +344,7 @@ "state": "HI", "hasImages": true, "price": 50000000, - "city": "Maui", + "city": "Hana", "description": "

Luxurious beachfront resort and spa on the island of Maui. This iconic property offers a once-in-a-lifetime opportunity to acquire a premier hospitality asset in a world-renowned destination.

Resort Features:

- 200 elegantly appointed guest rooms and suites
- Full-service spa and fitness center
- Multiple dining options, including fine dining and casual fare
- Infinity pool and direct beach access
- Event space for weddings and conferences

With a prime location on one of Maui's most stunning beaches, this resort and spa offers unparalleled potential for investors seeking a trophy asset in the highly sought-after Hawaiian market.

", "type": 105, "imageOrder": [] @@ -932,7 +932,7 @@ "state": "HI", "hasImages": true, "price": 75000000, - "city": "Maui", + "city": "Hana", "description": "

Oceanfront Luxury Resort and Spa

Exquisite luxury resort and spa situated on the pristine shores of Maui. This world-class property features elegant accommodations, top-tier amenities, and unparalleled ocean views, attracting discerning travelers from around the globe.

Resort Features:

- 200 beautifully appointed guest rooms and suites
- Full-service spa, fitness center, and infinity pool
- Gourmet dining options showcasing local cuisine
- Extensive meeting and event spaces
- Prime beachfront location with direct access to water activities

With a strong brand reputation, consistent occupancy, and a truly idyllic setting, this luxury resort and spa presents an exceptional opportunity for investors seeking a trophy asset in one of the world's most desirable destinations.

", "type": 104, "imageOrder": [] @@ -2278,7 +2278,7 @@ "state": "NY", "hasImages": true, "price": 500000000, - "city": "New York", + "city": "New York City", "description": "

Acquire this premier Class A office tower located in the heart of Midtown Manhattan, New York City. The property features state-of-the-art amenities, efficient floor plates, and unparalleled views of the city skyline, offering a prestigious address for top-tier tenants.

Class A office tower features:

- 1,000,000 square feet of Class A office space
- 95% occupancy rate with a diverse mix of creditworthy tenants
- LEED Platinum certified with advanced sustainability features
- Expansive lobby with 24/7 concierge and security services
- Multiple high-speed elevators and advanced building systems
- Prime Midtown location with access to transportation and amenities

Invest in this exceptional Class A office tower and benefit from the strong tenant demand and long-term value appreciation potential in the highly coveted Midtown Manhattan office market.

", "type": 103, "imageOrder": [] @@ -2434,7 +2434,7 @@ "state": "DC", "hasImages": true, "price": 225000000, - "city": "Washington", + "city": "Washington D.C.", "description": "

Acquire this stunning historic office building located in the heart of downtown Washington, D.C. The property features classic architecture, modern amenities, and a prime location just steps from the White House, offering a prestigious address for government relations and lobbying firms.

Historic office building features:

- 250,000 square feet of beautifully renovated office space
- Elegant lobbies and common areas with historic details
- State-of-the-art building systems and infrastructure
- Rooftop terrace with panoramic views of the Washington Monument
- On-site retail and dining amenities
- Unparalleled access to government agencies, embassies, and influential organizations

Invest in this exceptional historic office building and benefit from the strong and stable demand for prestigious office space in the heart of the nation's capital.

", "type": 103, "imageOrder": [] @@ -2668,7 +2668,7 @@ "state": "DC", "hasImages": true, "price": 400000000, - "city": "Washington", + "city": "Washington D.C.", "description": "

Acquire this iconic trophy office building located in the heart of Washington, D.C.'s central business district. The property features a stunning architectural design, premium amenities, and unparalleled views of the nation's capital, offering a prestigious address for top-tier tenants.

Trophy office building features:

- 600,000 square feet of Class A+ office space
- LEED Platinum certification for sustainability and energy efficiency
- Grand lobby with 24/7 concierge and security services
- Rooftop terrace with panoramic views of the Washington Monument and Capitol Building
- Private club with fine dining, fitness center, and conference facilities
- Direct access to multiple Metro lines and nearby amenities

Invest in this exceptional trophy office building and benefit from the strong and stable demand for premier office space in the nation's capital, driven by the presence of government agencies, lobbyists, and prestigious private sector tenants.

", "type": 103, "imageOrder": [] diff --git a/bizmatch-server/dbschema.ts b/bizmatch-server/dbschema.ts new file mode 100644 index 0000000..0874b5f --- /dev/null +++ b/bizmatch-server/dbschema.ts @@ -0,0 +1,239 @@ +/* tslint:disable */ +/* eslint-disable */ + + +/** + * AUTO-GENERATED FILE - DO NOT EDIT! + * + * This file was automatically generated by pg-to-ts v.4.1.1 + * $ pg-to-ts generate -c postgresql://username:password@localhost:5432/bizmatch -t businesses -t commercials -t users -s public + * + */ + + +export type Json = unknown; +export type customerSubType = 'appraiser' | 'attorney' | 'broker' | 'cpa' | 'surveyor' | 'titleCompany'; +export type customerType = 'buyer' | 'professional'; +export type gender = 'female' | 'male'; +export type listingsCategory = 'business' | 'commercialProperty'; + +// Table businesses +export interface Businesses { + id: string; + email: string | null; + type: string | null; + title: string | null; + description: string | null; + city: string | null; + state: string | null; + zipCode: number | null; + county: string | null; + price: number | null; + favoritesForUser: string[] | null; + draft: boolean | null; + listingsCategory: listingsCategory | null; + realEstateIncluded: boolean | null; + leasedLocation: boolean | null; + franchiseResale: boolean | null; + salesRevenue: number | null; + cashFlow: number | null; + supportAndTraining: string | null; + employees: number | null; + established: number | null; + internalListingNumber: number | null; + reasonForSale: string | null; + brokerLicencing: string | null; + internals: string | null; + imageName: string | null; + created: Date | null; + updated: Date | null; + visits: number | null; + lastVisit: Date | null; + latitude: number | null; + longitude: number | null; +} +export interface BusinessesInput { + id?: string; + email?: string | null; + type?: string | null; + title?: string | null; + description?: string | null; + city?: string | null; + state?: string | null; + zipCode?: number | null; + county?: string | null; + price?: number | null; + favoritesForUser?: string[] | null; + draft?: boolean | null; + listingsCategory?: listingsCategory | null; + realEstateIncluded?: boolean | null; + leasedLocation?: boolean | null; + franchiseResale?: boolean | null; + salesRevenue?: number | null; + cashFlow?: number | null; + supportAndTraining?: string | null; + employees?: number | null; + established?: number | null; + internalListingNumber?: number | null; + reasonForSale?: string | null; + brokerLicencing?: string | null; + internals?: string | null; + imageName?: string | null; + created?: Date | null; + updated?: Date | null; + visits?: number | null; + lastVisit?: Date | null; + latitude?: number | null; + longitude?: number | null; +} +const businesses = { + tableName: 'businesses', + columns: ['id', 'email', 'type', 'title', 'description', 'city', 'state', 'zipCode', 'county', 'price', 'favoritesForUser', 'draft', 'listingsCategory', 'realEstateIncluded', 'leasedLocation', 'franchiseResale', 'salesRevenue', 'cashFlow', 'supportAndTraining', 'employees', 'established', 'internalListingNumber', 'reasonForSale', 'brokerLicencing', 'internals', 'imageName', 'created', 'updated', 'visits', 'lastVisit', 'latitude', 'longitude'], + requiredForInsert: [], + primaryKey: 'id', + foreignKeys: { email: { table: 'users', column: 'email', $type: null as unknown as Users }, }, + $type: null as unknown as Businesses, + $input: null as unknown as BusinessesInput +} as const; + +// Table commercials +export interface Commercials { + id: string; + serialId: number; + email: string | null; + type: string | null; + title: string | null; + description: string | null; + city: string | null; + state: string | null; + price: number | null; + favoritesForUser: string[] | null; + listingsCategory: listingsCategory | null; + hideImage: boolean | null; + draft: boolean | null; + zipCode: number | null; + county: string | null; + imageOrder: string[] | null; + imagePath: string | null; + created: Date | null; + updated: Date | null; + visits: number | null; + lastVisit: Date | null; + latitude: number | null; + longitude: number | null; +} +export interface CommercialsInput { + id?: string; + serialId?: number; + email?: string | null; + type?: string | null; + title?: string | null; + description?: string | null; + city?: string | null; + state?: string | null; + price?: number | null; + favoritesForUser?: string[] | null; + listingsCategory?: listingsCategory | null; + hideImage?: boolean | null; + draft?: boolean | null; + zipCode?: number | null; + county?: string | null; + imageOrder?: string[] | null; + imagePath?: string | null; + created?: Date | null; + updated?: Date | null; + visits?: number | null; + lastVisit?: Date | null; + latitude?: number | null; + longitude?: number | null; +} +const commercials = { + tableName: 'commercials', + columns: ['id', 'serialId', 'email', 'type', 'title', 'description', 'city', 'state', 'price', 'favoritesForUser', 'listingsCategory', 'hideImage', 'draft', 'zipCode', 'county', 'imageOrder', 'imagePath', 'created', 'updated', 'visits', 'lastVisit', 'latitude', 'longitude'], + requiredForInsert: [], + primaryKey: 'id', + foreignKeys: { email: { table: 'users', column: 'email', $type: null as unknown as Users }, }, + $type: null as unknown as Commercials, + $input: null as unknown as CommercialsInput +} as const; + +// Table users +export interface Users { + id: string; + firstname: string; + lastname: string; + email: string; + phoneNumber: string | null; + description: string | null; + companyName: string | null; + companyOverview: string | null; + companyWebsite: string | null; + companyLocation: string | null; + offeredServices: string | null; + areasServed: Json | null; + hasProfile: boolean | null; + hasCompanyLogo: boolean | null; + licensedIn: Json | null; + gender: gender | null; + customerType: customerType | null; + customerSubType: customerSubType | null; + created: Date | null; + updated: Date | null; + latitude: number | null; + longitude: number | null; +} +export interface UsersInput { + id?: string; + firstname: string; + lastname: string; + email: string; + phoneNumber?: string | null; + description?: string | null; + companyName?: string | null; + companyOverview?: string | null; + companyWebsite?: string | null; + companyLocation?: string | null; + offeredServices?: string | null; + areasServed?: Json | null; + hasProfile?: boolean | null; + hasCompanyLogo?: boolean | null; + licensedIn?: Json | null; + gender?: gender | null; + customerType?: customerType | null; + customerSubType?: customerSubType | null; + created?: Date | null; + updated?: Date | null; + latitude?: number | null; + longitude?: number | null; +} +const users = { + tableName: 'users', + columns: ['id', 'firstname', 'lastname', 'email', 'phoneNumber', 'description', 'companyName', 'companyOverview', 'companyWebsite', 'companyLocation', 'offeredServices', 'areasServed', 'hasProfile', 'hasCompanyLogo', 'licensedIn', 'gender', 'customerType', 'customerSubType', 'created', 'updated', 'latitude', 'longitude'], + requiredForInsert: ['firstname', 'lastname', 'email'], + primaryKey: 'id', + foreignKeys: {}, + $type: null as unknown as Users, + $input: null as unknown as UsersInput +} as const; + + +export interface TableTypes { + businesses: { + select: Businesses; + input: BusinessesInput; + }; + commercials: { + select: Commercials; + input: CommercialsInput; + }; + users: { + select: Users; + input: UsersInput; + }; +} + +export const tables = { + businesses, + commercials, + users, +} diff --git a/bizmatch-server/importlog.txt b/bizmatch-server/importlog.txt new file mode 100644 index 0000000..8fe72a2 --- /dev/null +++ b/bizmatch-server/importlog.txt @@ -0,0 +1 @@ +Query: delete from "commercials" diff --git a/bizmatch-server/src/drizzle/import.ts b/bizmatch-server/src/drizzle/import.ts index ab241ac..a0efc28 100644 --- a/bizmatch-server/src/drizzle/import.ts +++ b/bizmatch-server/src/drizzle/import.ts @@ -44,10 +44,13 @@ const logger = winston.createLogger({ await db.delete(schema.commercials); await db.delete(schema.businesses); await db.delete(schema.users); +let filePath = `./src/assets/geo.json`; +const rawData = readFileSync(filePath, 'utf8'); +const geos = JSON.parse(rawData); const sso = new SelectOptionsService(); //Broker -let filePath = `./data/broker.json`; +filePath = `./data/broker.json`; let data: string = readFileSync(filePath, 'utf8'); const usersData: UserData[] = JSON.parse(data); // Erwartet ein Array von Objekten const generatedUserData = []; @@ -70,7 +73,7 @@ fs.ensureDirSync(`./pictures/property`); //for (const userData of usersData) { for (let index = 0; index < usersData.length; index++) { const userData = usersData[index]; - const user: User = { firstname: '', lastname: '', email: '' }; + const user: User = { id: undefined, firstname: '', lastname: '', email: '' }; user.licensedIn = []; userData.licensedIn.forEach(l => { console.log(l['value'], l['name']); @@ -91,6 +94,10 @@ for (let index = 0; index < usersData.length; index++) { user.companyOverview = userData.companyOverview; user.companyWebsite = userData.companyWebsite; user.companyLocation = userData.companyLocation; + const [city, state] = user.companyLocation.split('-').map(e => e.trim()); + const cityGeo = geos.states.find(s => s.state_code === state).cities.find(c => c.name === city); + const latitude = cityGeo.latitude; + const longitude = cityGeo.longitude; user.offeredServices = userData.offeredServices; user.gender = userData.gender; user.customerType = 'professional'; @@ -142,6 +149,13 @@ for (let index = 0; index < commercialJsonData.length; index++) { commercial.email = user.email; commercial.draft = false; commercial.type = sso.typesOfCommercialProperty.find(e => e.oldValue === String(commercial.type)).value; + const cityGeo = geos.states.find(s => s.state_code === commercial.state).cities.find(c => c.name === commercial.city); + try { + const latitude = cityGeo.latitude; + const longitude = cityGeo.longitude; + } catch (e) { + console.log(`----------------> ERROR ${commercial.state} - ${commercial.city}`); + } // const reducedCommercial = { // city: commercial.city, // description: commercial.description, @@ -175,6 +189,13 @@ for (let index = 0; index < businessJsonData.length; index++) { const user = getRandomItem(generatedUserData); business.email = user.email; business.imageName = emailToDirName(user.email); + const cityGeo = geos.states.find(s => s.state_code === business.state).cities.find(c => c.name === business.city); + try { + const latitude = cityGeo.latitude; + const longitude = cityGeo.longitude; + } catch (e) { + console.log(`----------------> ERROR ${business.state} - ${business.city}`); + } // const embeddingText = JSON.stringify({ // type: typesOfBusiness.find(b => b.value === String(business.type))?.name, // title: business.title, diff --git a/bizmatch-server/src/drizzle/migrations/0000_slim_nova.sql b/bizmatch-server/src/drizzle/migrations/0000_freezing_vengeance.sql similarity index 84% rename from bizmatch-server/src/drizzle/migrations/0000_slim_nova.sql rename to bizmatch-server/src/drizzle/migrations/0000_freezing_vengeance.sql index 220656d..723934c 100644 --- a/bizmatch-server/src/drizzle/migrations/0000_slim_nova.sql +++ b/bizmatch-server/src/drizzle/migrations/0000_freezing_vengeance.sql @@ -16,18 +16,26 @@ EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint +DO $$ BEGIN + CREATE TYPE "public"."listingsCategory" AS ENUM('commercialProperty', 'business'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint CREATE TABLE IF NOT EXISTS "businesses" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "email" varchar(255), - "type" integer, + "type" varchar(255), "title" varchar(255), "description" text, "city" varchar(255), "state" char(2), + "zipCode" integer, + "county" varchar(255), "price" double precision, "favoritesForUser" varchar(30)[], "draft" boolean, - "listingsCategory" varchar(255), + "listingsCategory" "listingsCategory", "realEstateIncluded" boolean, "leasedLocation" boolean, "franchiseResale" boolean, @@ -40,26 +48,27 @@ CREATE TABLE IF NOT EXISTS "businesses" ( "reasonForSale" varchar(255), "brokerLicencing" varchar(255), "internals" text, - "imagePath" varchar(200), + "imageName" varchar(200), "created" timestamp, "updated" timestamp, "visits" integer, "lastVisit" timestamp, - "embedding" vector(1536) + "latitude" double precision, + "longitude" double precision ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "commercials" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "serial_id" serial NOT NULL, "email" varchar(255), - "type" integer, + "type" varchar(255), "title" varchar(255), "description" text, "city" varchar(255), "state" char(2), "price" double precision, "favoritesForUser" varchar(30)[], - "listingsCategory" varchar(255), + "listingsCategory" "listingsCategory", "hideImage" boolean, "draft" boolean, "zipCode" integer, @@ -70,7 +79,8 @@ CREATE TABLE IF NOT EXISTS "commercials" ( "updated" timestamp, "visits" integer, "lastVisit" timestamp, - "embedding" vector(1536) + "latitude" double precision, + "longitude" double precision ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "users" ( @@ -94,7 +104,8 @@ CREATE TABLE IF NOT EXISTS "users" ( "customerSubType" "customerSubType", "created" timestamp, "updated" timestamp, - "embedding" vector(1536), + "latitude" double precision, + "longitude" double precision, CONSTRAINT "users_email_unique" UNIQUE("email") ); --> statement-breakpoint diff --git a/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql b/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql deleted file mode 100644 index 4d3d684..0000000 --- a/bizmatch-server/src/drizzle/migrations/0001_heavy_bloodscream.sql +++ /dev/null @@ -1,2 +0,0 @@ -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/0001_steady_phantom_reporter.sql b/bizmatch-server/src/drizzle/migrations/0001_steady_phantom_reporter.sql new file mode 100644 index 0000000..c296ca7 --- /dev/null +++ b/bizmatch-server/src/drizzle/migrations/0001_steady_phantom_reporter.sql @@ -0,0 +1 @@ +ALTER TABLE "commercials" RENAME COLUMN "serial_id" TO "serialId"; \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/migrations/meta/0000_snapshot.json b/bizmatch-server/src/drizzle/migrations/meta/0000_snapshot.json index a3e762a..b789012 100644 --- a/bizmatch-server/src/drizzle/migrations/meta/0000_snapshot.json +++ b/bizmatch-server/src/drizzle/migrations/meta/0000_snapshot.json @@ -1,5 +1,5 @@ { - "id": "2d8edad3-5544-4cb1-a543-84c07737ea9f", + "id": "aa3e53ed-4f1b-4e00-84ea-58939189a427", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -23,7 +23,7 @@ }, "type": { "name": "type", - "type": "integer", + "type": "varchar(255)", "primaryKey": false, "notNull": false }, @@ -51,6 +51,18 @@ "primaryKey": false, "notNull": false }, + "zipCode": { + "name": "zipCode", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "county": { + "name": "county", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, "price": { "name": "price", "type": "double precision", @@ -71,7 +83,8 @@ }, "listingsCategory": { "name": "listingsCategory", - "type": "varchar(255)", + "type": "listingsCategory", + "typeSchema": "public", "primaryKey": false, "notNull": false }, @@ -147,8 +160,8 @@ "primaryKey": false, "notNull": false }, - "imagePath": { - "name": "imagePath", + "imageName": { + "name": "imageName", "type": "varchar(200)", "primaryKey": false, "notNull": false @@ -177,9 +190,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -228,7 +247,7 @@ }, "type": { "name": "type", - "type": "integer", + "type": "varchar(255)", "primaryKey": false, "notNull": false }, @@ -270,7 +289,8 @@ }, "listingsCategory": { "name": "listingsCategory", - "type": "varchar(255)", + "type": "listingsCategory", + "typeSchema": "public", "primaryKey": false, "notNull": false }, @@ -334,9 +354,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -488,9 +514,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -537,6 +569,14 @@ "male", "female" ] + }, + "public.listingsCategory": { + "name": "listingsCategory", + "schema": "public", + "values": [ + "commercialProperty", + "business" + ] } }, "schemas": {}, diff --git a/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json index 158db44..a21e64c 100644 --- a/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json +++ b/bizmatch-server/src/drizzle/migrations/meta/0001_snapshot.json @@ -1,6 +1,6 @@ { - "id": "93be31d4-beec-4ba8-8d4a-a52763342335", - "prevId": "2d8edad3-5544-4cb1-a543-84c07737ea9f", + "id": "ff415931-0de6-4c89-900f-c6fd64830b2e", + "prevId": "aa3e53ed-4f1b-4e00-84ea-58939189a427", "version": "7", "dialect": "postgresql", "tables": { @@ -51,6 +51,18 @@ "primaryKey": false, "notNull": false }, + "zipCode": { + "name": "zipCode", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "county": { + "name": "county", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, "price": { "name": "price", "type": "double precision", @@ -71,7 +83,8 @@ }, "listingsCategory": { "name": "listingsCategory", - "type": "varchar(255)", + "type": "listingsCategory", + "typeSchema": "public", "primaryKey": false, "notNull": false }, @@ -147,8 +160,8 @@ "primaryKey": false, "notNull": false }, - "imagePath": { - "name": "imagePath", + "imageName": { + "name": "imageName", "type": "varchar(200)", "primaryKey": false, "notNull": false @@ -177,9 +190,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -214,8 +233,8 @@ "notNull": true, "default": "gen_random_uuid()" }, - "serial_id": { - "name": "serial_id", + "serialId": { + "name": "serialId", "type": "serial", "primaryKey": false, "notNull": true @@ -270,7 +289,8 @@ }, "listingsCategory": { "name": "listingsCategory", - "type": "varchar(255)", + "type": "listingsCategory", + "typeSchema": "public", "primaryKey": false, "notNull": false }, @@ -334,9 +354,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -488,9 +514,15 @@ "primaryKey": false, "notNull": false }, - "embedding": { - "name": "embedding", - "type": "vector(1536)", + "latitude": { + "name": "latitude", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "double precision", "primaryKey": false, "notNull": false } @@ -537,6 +569,14 @@ "male", "female" ] + }, + "public.listingsCategory": { + "name": "listingsCategory", + "schema": "public", + "values": [ + "commercialProperty", + "business" + ] } }, "schemas": {}, diff --git a/bizmatch-server/src/drizzle/migrations/meta/_journal.json b/bizmatch-server/src/drizzle/migrations/meta/_journal.json index 26a0d44..ba9d316 100644 --- a/bizmatch-server/src/drizzle/migrations/meta/_journal.json +++ b/bizmatch-server/src/drizzle/migrations/meta/_journal.json @@ -5,15 +5,15 @@ { "idx": 0, "version": "7", - "when": 1720872296432, - "tag": "0000_slim_nova", + "when": 1721737805677, + "tag": "0000_freezing_vengeance", "breakpoints": true }, { "idx": 1, "version": "7", - "when": 1721134224160, - "tag": "0001_heavy_bloodscream", + "when": 1721738173220, + "tag": "0001_steady_phantom_reporter", "breakpoints": true } ] diff --git a/bizmatch-server/src/drizzle/schema.ts b/bizmatch-server/src/drizzle/schema.ts index d4fcade..798ae21 100644 --- a/bizmatch-server/src/drizzle/schema.ts +++ b/bizmatch-server/src/drizzle/schema.ts @@ -1,9 +1,10 @@ -import { boolean, char, doublePrecision, integer, jsonb, pgEnum, pgTable, serial, text, timestamp, uuid, varchar, vector } from 'drizzle-orm/pg-core'; +import { boolean, char, doublePrecision, integer, jsonb, pgEnum, pgTable, serial, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core'; import { AreasServed, LicensedIn } from '../models/db.model'; export const PG_CONNECTION = 'PG_CONNECTION'; export const genderEnum = pgEnum('gender', ['male', 'female']); export const customerTypeEnum = pgEnum('customerType', ['buyer', 'professional']); export const customerSubTypeEnum = pgEnum('customerSubType', ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser']); +export const listingsCategoryEnum = pgEnum('listingsCategory', ['commercialProperty', 'business']); export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), @@ -26,7 +27,9 @@ export const users = pgTable('users', { customerSubType: customerSubTypeEnum('customerSubType'), created: timestamp('created'), updated: timestamp('updated'), - embedding: vector('embedding', { dimensions: 1536 }), + latitude: doublePrecision('latitude'), + longitude: doublePrecision('longitude'), + // embedding: vector('embedding', { dimensions: 1536 }), }); export const businesses = pgTable('businesses', { @@ -37,10 +40,12 @@ export const businesses = pgTable('businesses', { description: text('description'), city: varchar('city', { length: 255 }), state: char('state', { length: 2 }), + zipCode: integer('zipCode'), + county: varchar('county', { length: 255 }), price: doublePrecision('price'), favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(), draft: boolean('draft'), - listingsCategory: varchar('listingsCategory', { length: 255 }), + listingsCategory: listingsCategoryEnum('listingsCategory'), //varchar('listingsCategory', { length: 255 }), realEstateIncluded: boolean('realEstateIncluded'), leasedLocation: boolean('leasedLocation'), franchiseResale: boolean('franchiseResale'), @@ -53,18 +58,19 @@ export const businesses = pgTable('businesses', { reasonForSale: varchar('reasonForSale', { length: 255 }), brokerLicencing: varchar('brokerLicencing', { length: 255 }), internals: text('internals'), - imageName: varchar('imagePath', { length: 200 }), + imageName: varchar('imageName', { length: 200 }), created: timestamp('created'), updated: timestamp('updated'), visits: integer('visits'), lastVisit: timestamp('lastVisit'), - // Neue Spalte für das OpenAI Embedding - embedding: vector('embedding', { dimensions: 1536 }), + latitude: doublePrecision('latitude'), + longitude: doublePrecision('longitude'), + // embedding: vector('embedding', { dimensions: 1536 }), }); export const commercials = pgTable('commercials', { id: uuid('id').primaryKey().defaultRandom(), - serialId: serial('serial_id'), + serialId: serial('serialId'), email: varchar('email', { length: 255 }).references(() => users.email), type: varchar('type', { length: 255 }), title: varchar('title', { length: 255 }), @@ -73,7 +79,7 @@ export const commercials = pgTable('commercials', { state: char('state', { length: 2 }), price: doublePrecision('price'), favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(), - listingsCategory: varchar('listingsCategory', { length: 255 }), + listingsCategory: listingsCategoryEnum('listingsCategory'), //listingsCategory: varchar('listingsCategory', { length: 255 }), hideImage: boolean('hideImage'), draft: boolean('draft'), zipCode: integer('zipCode'), @@ -84,5 +90,7 @@ export const commercials = pgTable('commercials', { updated: timestamp('updated'), visits: integer('visits'), lastVisit: timestamp('lastVisit'), - embedding: vector('embedding', { dimensions: 1536 }), + latitude: doublePrecision('latitude'), + longitude: doublePrecision('longitude'), + // embedding: vector('embedding', { dimensions: 1536 }), }); diff --git a/bizmatch-server/src/listings/business-listing.service.ts b/bizmatch-server/src/listings/business-listing.service.ts index 54504ae..e4b240e 100644 --- a/bizmatch-server/src/listings/business-listing.service.ts +++ b/bizmatch-server/src/listings/business-listing.service.ts @@ -9,6 +9,16 @@ import { FileService } from '../file/file.service.js'; import { BusinessListing, CommercialPropertyListing } from '../models/db.model'; import { BusinessListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js'; +const EARTH_RADIUS_KM = 6371; // Erdradius in Kilometern + +const getDistanceQuery = (lat: number, lon: number) => sql` + ${EARTH_RADIUS_KM} * 2 * ASIN(SQRT( + POWER(SIN((${lat} - ${businesses.latitude}) * PI() / 180 / 2), 2) + + COS(${lat} * PI() / 180) * COS(${businesses.latitude} * PI() / 180) * + POWER(SIN((${lon} - ${businesses.longitude}) * PI() / 180 / 2), 2) + )) +`; + @Injectable() export class BusinessListingService { constructor( @@ -142,7 +152,7 @@ export class BusinessListingService { const [{ value: totalCount }] = await countQuery; return totalCount; } - async findBusinessesById(id: string, user: JwtUser): Promise { + async findBusinessesById(id: string, user: JwtUser): Promise { let result = await this.conn .select() .from(businesses) @@ -159,7 +169,7 @@ export class BusinessListingService { return (await this.conn .select() .from(businesses) - .where(and(...conditions))) as CommercialPropertyListing[]; + .where(and(...conditions))) as BusinessListing[]; } // #### CREATE ######################################## async createListing(data: BusinessListing): Promise { diff --git a/bizmatch-server/src/listings/commercial-property.service.ts b/bizmatch-server/src/listings/commercial-property.service.ts index 834a5ac..1abc58b 100644 --- a/bizmatch-server/src/listings/commercial-property.service.ts +++ b/bizmatch-server/src/listings/commercial-property.service.ts @@ -6,7 +6,7 @@ import { Logger } from 'winston'; import * as schema from '../drizzle/schema.js'; import { commercials, PG_CONNECTION } from '../drizzle/schema.js'; import { FileService } from '../file/file.service.js'; -import { BusinessListing, CommercialPropertyListing } from '../models/db.model'; +import { CommercialPropertyListing } from '../models/db.model'; import { CommercialPropertyListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js'; @Injectable() @@ -133,7 +133,7 @@ export class CommercialPropertyService { data.imageOrder = imageOrder; } const [updateListing] = await this.conn.update(commercials).set(data).where(eq(commercials.id, id)).returning(); - return updateListing as BusinessListing | CommercialPropertyListing; + return updateListing as CommercialPropertyListing; } // ############################################################## // Images for commercial Properties diff --git a/bizmatch-server/src/models/db.model.ts b/bizmatch-server/src/models/db.model.ts index 9b656f8..f6aae67 100644 --- a/bizmatch-server/src/models/db.model.ts +++ b/bizmatch-server/src/models/db.model.ts @@ -1,25 +1,25 @@ -export interface User { - id?: string; - firstname: string; - lastname: string; - email: string; - phoneNumber?: string; - description?: string; - companyName?: string; - companyOverview?: string; - companyWebsite?: string; - companyLocation?: string; - offeredServices?: string; - areasServed?: AreasServed[]; - hasProfile?: boolean; - hasCompanyLogo?: boolean; - licensedIn?: LicensedIn[]; - gender?: 'male' | 'female'; - customerType?: 'buyer' | 'professional'; - customerSubType?: 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser'; - created?: Date; - updated?: Date; -} +// export interface User { +// id?: string; +// firstname: string; +// lastname: string; +// email: string; +// phoneNumber?: string; +// description?: string; +// companyName?: string; +// companyOverview?: string; +// companyWebsite?: string; +// companyLocation?: string; +// offeredServices?: string; +// areasServed?: AreasServed[]; +// hasProfile?: boolean; +// hasCompanyLogo?: boolean; +// licensedIn?: LicensedIn[]; +// gender?: 'male' | 'female'; +// customerType?: 'buyer' | 'professional'; +// customerSubType?: 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser'; +// created?: Date; +// updated?: Date; +// } export interface UserData { id?: string; firstname: string; @@ -42,22 +42,53 @@ export interface UserData { created?: Date; updated?: Date; } +export type Gender = 'male' | 'female'; +export type CustomerType = 'buyer' | 'professional'; +export type CustomerSubType = 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser'; +export type ListingsCategory = 'commercialProperty' | 'business'; +export interface User { + id: string; // UUID as a string + firstname: string; + lastname: string; + email: string; + phoneNumber?: string; + description?: string; + companyName?: string; + companyOverview?: string; + companyWebsite?: string; + companyLocation?: string; + offeredServices?: string; + areasServed?: AreasServed[]; + hasProfile?: boolean; + hasCompanyLogo?: boolean; + licensedIn?: LicensedIn[]; + gender?: Gender; + customerType?: CustomerType; + customerSubType?: CustomerSubType; + created?: Date; + updated?: Date; + latitude?: number; + longitude?: number; +} export interface BusinessListing { - id: string; - email?: string; + id: string; // UUID as a string + email: string; // References users.email type?: string; title?: string; description?: string; city?: string; - state?: string; - price?: number; - favoritesForUser?: string[]; + state?: string; // 2-character state code + zipCode?: number; + county?: string; + price?: number; // double precision + favoritesForUser?: string[]; // Array of strings draft?: boolean; + listingsCategory?: ListingsCategory; realEstateIncluded?: boolean; leasedLocation?: boolean; franchiseResale?: boolean; - salesRevenue?: number; - cashFlow?: number; + salesRevenue?: number; // double precision + cashFlow?: number; // double precision supportAndTraining?: string; employees?: number; established?: number; @@ -70,31 +101,35 @@ export interface BusinessListing { updated?: Date; visits?: number; lastVisit?: Date; - listingsCategory?: 'commercialProperty' | 'business'; + latitude?: number; // double precision + longitude?: number; // double precision } export interface CommercialPropertyListing { - id: string; - serialId?: number; - email?: string; + id: string; // UUID as a string + serialId: number; // Serial ID + email: string; // References users.email type?: string; title?: string; description?: string; city?: string; - state?: string; - price?: number; - favoritesForUser?: string[]; + state?: string; // 2-character state code + price?: number; // double precision + favoritesForUser?: string[]; // Array of strings + listingsCategory?: ListingsCategory; hideImage?: boolean; draft?: boolean; zipCode?: number; county?: string; - imageOrder?: string[]; + imageOrder?: string[]; // Array of strings imagePath?: string; created?: Date; updated?: Date; visits?: number; lastVisit?: Date; - listingsCategory?: 'commercialProperty' | 'business'; + latitude?: number; // double precision + longitude?: number; // double precision + // embedding?: number[]; // Uncomment if needed for vector embedding } export interface AreasServed { county: string; diff --git a/bizmatch-server/src/user/user.service.ts b/bizmatch-server/src/user/user.service.ts index 5e69070..7f8ec61 100644 --- a/bizmatch-server/src/user/user.service.ts +++ b/bizmatch-server/src/user/user.service.ts @@ -103,7 +103,7 @@ export class UserService { .from(schema.users) .where(sql`email = ${email}`)) as User[]; if (users.length === 0) { - const user: User = { email, firstname: jwtuser.firstname, lastname: jwtuser.lastname, customerType: 'buyer' }; + const user: User = { id: undefined, email, firstname: jwtuser.firstname, lastname: jwtuser.lastname, customerType: 'buyer' }; this.saveUser(user); return user; } else { 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 b7ee178..c0391d9 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 @@ -82,7 +82,7 @@ export class DetailsCommercialPropertyListingComponent { this.user = await this.userService.getByMail(this.keycloakUser.email); this.mailinfo.sender = { name: `${this.user.firstname} ${this.user.lastname}`, email: this.user.email, phoneNumber: this.user.phoneNumber, state: this.user.companyLocation }; } - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); + this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing; this.listingUser = await this.userService.getByMail(this.listing.email); this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description); import('flowbite').then(flowbite => { 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 dd4834a..72d7997 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 @@ -53,7 +53,7 @@ export class DetailsUserComponent { const results = await Promise.all([await this.listingsService.getListingsByEmail(this.user.email, 'business'), await this.listingsService.getListingsByEmail(this.user.email, 'commercialProperty')]); // Zuweisen der Ergebnisse zu den Member-Variablen der Klasse this.businessListings = results[0]; - this.commercialPropListings = results[1]; + this.commercialPropListings = results[1] as CommercialPropertyListing[]; //this.user$ = this.userService.getUserObservable(); const token = await this.keycloakService.getToken(); this.keycloakUser = map2User(token); diff --git a/bizmatch/src/app/pages/home/home.component.html b/bizmatch/src/app/pages/home/home.component.html index 5a927f9..cd7c470 100644 --- a/bizmatch/src/app/pages/home/home.component.html +++ b/bizmatch/src/app/pages/home/home.component.html @@ -84,7 +84,7 @@ -
Or search using filters ▼
+
Or search using filters ▼
diff --git a/bizmatch/src/app/pages/home/home.component.ts b/bizmatch/src/app/pages/home/home.component.ts index 6b58610..47bee9f 100644 --- a/bizmatch/src/app/pages/home/home.component.ts +++ b/bizmatch/src/app/pages/home/home.component.ts @@ -5,9 +5,12 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { KeycloakService } from 'keycloak-angular'; import onChange from 'on-change'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; +import { ModalService } from '../../components/search-modal/modal.service'; +import { CriteriaChangeService } from '../../services/criteria-change.service'; import { ListingsService } from '../../services/listings.service'; +import { SearchService } from '../../services/search.service'; import { SelectOptionsService } from '../../services/select-options.service'; -import { getCriteriaStateObject, getSessionStorageHandlerWrapper, map2User } from '../../utils/utils'; +import { getCriteriaStateObject, map2User } from '../../utils/utils'; @Component({ selector: 'app-home', standalone: true, @@ -25,30 +28,54 @@ export class HomeComponent { 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('business'), getSessionStorageHandlerWrapper(this.activeTabAction)); - } + public constructor( + private router: Router, + private modalService: ModalService, + private searchService: SearchService, + private activatedRoute: ActivatedRoute, + public selectOptions: SelectOptionsService, + public keycloakService: KeycloakService, + private listingsService: ListingsService, + private criteriaChangeService: CriteriaChangeService, + ) {} async ngOnInit() { const token = await this.keycloakService.getToken(); + sessionStorage.removeItem('business_criteria'); + sessionStorage.removeItem('commercialProperty_criteria'); + sessionStorage.removeItem('broker_criteria'); + this.criteria = this.createEnhancedProxy(getCriteriaStateObject('business')); this.user = map2User(token); - // this.router.events.subscribe(event => { - // if (event instanceof NavigationEnd) { - // initFlowbite(); - // } - // }); } async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') { this.activeTabAction = tabname; - // if (this.activeTabAction === 'business' || this.activeTabAction === 'commercialProperty') { - // const statesResult = await this.listingsService.getAllStates(this.activeTabAction); - // this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); - // } else { - // this.states = this.selectOptions.states; - // } + if ('business' === tabname) { + this.criteria = this.createEnhancedProxy(getCriteriaStateObject('business')); + } else if ('commercialProperty' === tabname) { + this.criteria = this.createEnhancedProxy(getCriteriaStateObject('commercialProperty')); + } else if ('broker' === tabname) { + this.criteria = this.createEnhancedProxy(getCriteriaStateObject('broker')); + } else { + this.criteria = undefined; + } + } + private createEnhancedProxy(obj: any) { + const component = this; + + const sessionStorageHandler = function (path, value, previous, applyData) { + let criteriaType = this.criteriaType; + sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this)); + }; + + return onChange(obj, function (path, value, previous, applyData) { + // Call the original sessionStorageHandler + sessionStorageHandler.call(this, path, value, previous, applyData); + + // Notify about the criteria change using the component's context + component.criteriaChangeService.notifyCriteriaChange(); + }); } search() { const data = { keep: true }; - this.criteria.prompt = this.prompt; this.router.navigate([`${this.activeTabAction}Listings`]); } @@ -63,4 +90,11 @@ export class HomeComponent { toggleMenu() { this.isMenuOpen = !this.isMenuOpen; } + async openModal() { + const accepted = await this.modalService.showModal(this.criteria); + if (accepted) { + //this.searchService.search(this.criteria); + this.router.navigate([`${this.activeTabAction}Listings`]); + } + } } diff --git a/bizmatch/src/app/pages/subscription/account/account.component.ts b/bizmatch/src/app/pages/subscription/account/account.component.ts index 6f40089..5436041 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.ts +++ b/bizmatch/src/app/pages/subscription/account/account.component.ts @@ -77,6 +77,7 @@ export class AccountComponent { this.user = await this.userService.getByMail(email); } catch (e) { this.user = { + id: undefined, email, firstname: keycloakUser.firstName, lastname: keycloakUser.lastName, diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts index fb699c5..9756652 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts @@ -120,7 +120,7 @@ export class EditCommercialPropertyListingComponent { const token = await this.keycloakService.getToken(); const keycloakUser = map2User(token); if (this.mode === 'edit') { - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); + this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing; } else { this.listing = createDefaultCommercialPropertyListing(); const listingUser = await this.userService.getByMail(keycloakUser.email); @@ -134,7 +134,7 @@ export class EditCommercialPropertyListingComponent { } async save() { - this.listing = await this.listingsService.save(this.listing, this.listing.listingsCategory); + this.listing = (await this.listingsService.save(this.listing, this.listing.listingsCategory)) as CommercialPropertyListing; this.router.navigate(['editCommercialPropertyListing', this.listing.id]); this.messageService.addMessage({ severity: 'success', text: 'Listing changes have been persisted', duration: 3000 }); } @@ -166,7 +166,7 @@ export class EditCommercialPropertyListingComponent { if (this.croppedImage) { this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId).subscribe( async () => { - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); + this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing; this.closeModal(); }, error => { @@ -182,7 +182,7 @@ export class EditCommercialPropertyListingComponent { this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName); await this.imageService.deleteListingImage(this.listing.imagePath, this.listing.serialId, imageName); await this.listingsService.save(this.listing, 'commercialProperty'); - this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); + this.listing = (await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'))) as CommercialPropertyListing; this.messageService.addMessage({ severity: 'success', text: 'Image has been deleted', duration: 3000 }); } else { console.log('deny'); diff --git a/bizmatch/src/app/utils/utils.ts b/bizmatch/src/app/utils/utils.ts index c0df0ab..29cf0f1 100644 --- a/bizmatch/src/app/utils/utils.ts +++ b/bizmatch/src/app/utils/utils.ts @@ -6,6 +6,7 @@ import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, K export function createDefaultUser(email: string, firstname: string, lastname: string): User { return { + id: undefined, email, firstname, lastname, @@ -31,6 +32,7 @@ export function createDefaultUser(email: string, firstname: string, lastname: st export function createDefaultCommercialPropertyListing(): CommercialPropertyListing { return { id: undefined, + serialId: undefined, email: '', type: null, title: '', @@ -49,12 +51,15 @@ export function createDefaultCommercialPropertyListing(): CommercialPropertyList updated: null, visits: null, lastVisit: null, + latitude: null, + longitude: null, listingsCategory: 'commercialProperty', }; } export function createDefaultBusinessListing(): BusinessListing { return { id: undefined, + email: '', type: null, title: '', description: '', @@ -79,6 +84,8 @@ export function createDefaultBusinessListing(): BusinessListing { updated: null, visits: null, lastVisit: null, + latitude: null, + longitude: null, listingsCategory: 'business', }; }