reset criteria on home, show filter on home, new BOM generation, Schema overhaul

This commit is contained in:
Andreas Knuth 2024-07-23 20:46:38 +02:00
parent 9db23c2177
commit acec14d372
23 changed files with 577 additions and 131 deletions

View File

@ -11922,7 +11922,7 @@
"description": "<h3>Thriving Caribbean Restaurant in the Heart of Washington, D.C.</h3><p>Well-established Caribbean restaurant with a loyal customer base. Known for its authentic cuisine and lively atmosphere.</p><p>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.</p>", "description": "<h3>Thriving Caribbean Restaurant in the Heart of Washington, D.C.</h3><p>Well-established Caribbean restaurant with a loyal customer base. Known for its authentic cuisine and lively atmosphere.</p><p>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.</p>",
"type": 13, "type": 13,
"state": "DC", "state": "DC",
"city": "Washington", "city": "Washington D.C.",
"id": "b148ad7f-7f4a-4319-bc8c-df23cf85be3b", "id": "b148ad7f-7f4a-4319-bc8c-df23cf85be3b",
"price": 1500000, "price": 1500000,
"salesRevenue": 2200000, "salesRevenue": 2200000,
@ -12322,7 +12322,7 @@
"description": "<h3>Thriving Indian Restaurant in the Heart of Washington, D.C.</h3><p>Well-established Indian restaurant with a loyal customer base. Known for its authentic cuisine and inviting atmosphere.</p><p>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.</p>", "description": "<h3>Thriving Indian Restaurant in the Heart of Washington, D.C.</h3><p>Well-established Indian restaurant with a loyal customer base. Known for its authentic cuisine and inviting atmosphere.</p><p>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.</p>",
"type": 13, "type": 13,
"state": "DC", "state": "DC",
"city": "Washington", "city": "Washington D.C.",
"id": "d8d0f4e3-fdbf-4c4b-b2bd-d8847f0d248f", "id": "d8d0f4e3-fdbf-4c4b-b2bd-d8847f0d248f",
"price": 1500000, "price": 1500000,
"salesRevenue": 2200000, "salesRevenue": 2200000,

View File

@ -344,7 +344,7 @@
"state": "HI", "state": "HI",
"hasImages": true, "hasImages": true,
"price": 50000000, "price": 50000000,
"city": "Maui", "city": "Hana",
"description": "<p>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.</p><h3>Resort Features:</h3><p>- 200 elegantly appointed guest rooms and suites<br>- Full-service spa and fitness center<br>- Multiple dining options, including fine dining and casual fare<br>- Infinity pool and direct beach access<br>- Event space for weddings and conferences</p><p>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.</p>", "description": "<p>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.</p><h3>Resort Features:</h3><p>- 200 elegantly appointed guest rooms and suites<br>- Full-service spa and fitness center<br>- Multiple dining options, including fine dining and casual fare<br>- Infinity pool and direct beach access<br>- Event space for weddings and conferences</p><p>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.</p>",
"type": 105, "type": 105,
"imageOrder": [] "imageOrder": []
@ -932,7 +932,7 @@
"state": "HI", "state": "HI",
"hasImages": true, "hasImages": true,
"price": 75000000, "price": 75000000,
"city": "Maui", "city": "Hana",
"description": "<h2>Oceanfront Luxury Resort and Spa</h2><p>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.</p><p>Resort Features:</p><p>- 200 beautifully appointed guest rooms and suites<br>- Full-service spa, fitness center, and infinity pool<br>- Gourmet dining options showcasing local cuisine<br>- Extensive meeting and event spaces<br>- Prime beachfront location with direct access to water activities</p><p>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.</p>", "description": "<h2>Oceanfront Luxury Resort and Spa</h2><p>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.</p><p>Resort Features:</p><p>- 200 beautifully appointed guest rooms and suites<br>- Full-service spa, fitness center, and infinity pool<br>- Gourmet dining options showcasing local cuisine<br>- Extensive meeting and event spaces<br>- Prime beachfront location with direct access to water activities</p><p>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.</p>",
"type": 104, "type": 104,
"imageOrder": [] "imageOrder": []
@ -2278,7 +2278,7 @@
"state": "NY", "state": "NY",
"hasImages": true, "hasImages": true,
"price": 500000000, "price": 500000000,
"city": "New York", "city": "New York City",
"description": "<p>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.</p><h3>Class A office tower features:</h3><p>- 1,000,000 square feet of Class A office space<br>- 95% occupancy rate with a diverse mix of creditworthy tenants<br>- LEED Platinum certified with advanced sustainability features<br>- Expansive lobby with 24/7 concierge and security services<br>- Multiple high-speed elevators and advanced building systems<br>- Prime Midtown location with access to transportation and amenities</p><p>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.</p>", "description": "<p>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.</p><h3>Class A office tower features:</h3><p>- 1,000,000 square feet of Class A office space<br>- 95% occupancy rate with a diverse mix of creditworthy tenants<br>- LEED Platinum certified with advanced sustainability features<br>- Expansive lobby with 24/7 concierge and security services<br>- Multiple high-speed elevators and advanced building systems<br>- Prime Midtown location with access to transportation and amenities</p><p>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.</p>",
"type": 103, "type": 103,
"imageOrder": [] "imageOrder": []
@ -2434,7 +2434,7 @@
"state": "DC", "state": "DC",
"hasImages": true, "hasImages": true,
"price": 225000000, "price": 225000000,
"city": "Washington", "city": "Washington D.C.",
"description": "<p>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.</p><h3>Historic office building features:</h3><p>- 250,000 square feet of beautifully renovated office space<br>- Elegant lobbies and common areas with historic details<br>- State-of-the-art building systems and infrastructure<br>- Rooftop terrace with panoramic views of the Washington Monument<br>- On-site retail and dining amenities<br>- Unparalleled access to government agencies, embassies, and influential organizations</p><p>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.</p>", "description": "<p>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.</p><h3>Historic office building features:</h3><p>- 250,000 square feet of beautifully renovated office space<br>- Elegant lobbies and common areas with historic details<br>- State-of-the-art building systems and infrastructure<br>- Rooftop terrace with panoramic views of the Washington Monument<br>- On-site retail and dining amenities<br>- Unparalleled access to government agencies, embassies, and influential organizations</p><p>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.</p>",
"type": 103, "type": 103,
"imageOrder": [] "imageOrder": []
@ -2668,7 +2668,7 @@
"state": "DC", "state": "DC",
"hasImages": true, "hasImages": true,
"price": 400000000, "price": 400000000,
"city": "Washington", "city": "Washington D.C.",
"description": "<p>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.</p><h3>Trophy office building features:</h3><p>- 600,000 square feet of Class A+ office space<br>- LEED Platinum certification for sustainability and energy efficiency<br>- Grand lobby with 24/7 concierge and security services<br>- Rooftop terrace with panoramic views of the Washington Monument and Capitol Building<br>- Private club with fine dining, fitness center, and conference facilities<br>- Direct access to multiple Metro lines and nearby amenities</p><p>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.</p>", "description": "<p>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.</p><h3>Trophy office building features:</h3><p>- 600,000 square feet of Class A+ office space<br>- LEED Platinum certification for sustainability and energy efficiency<br>- Grand lobby with 24/7 concierge and security services<br>- Rooftop terrace with panoramic views of the Washington Monument and Capitol Building<br>- Private club with fine dining, fitness center, and conference facilities<br>- Direct access to multiple Metro lines and nearby amenities</p><p>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.</p>",
"type": 103, "type": 103,
"imageOrder": [] "imageOrder": []

239
bizmatch-server/dbschema.ts Normal file
View File

@ -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,
}

View File

@ -0,0 +1 @@
Query: delete from "commercials"

View File

@ -44,10 +44,13 @@ const logger = winston.createLogger({
await db.delete(schema.commercials); await db.delete(schema.commercials);
await db.delete(schema.businesses); await db.delete(schema.businesses);
await db.delete(schema.users); 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(); const sso = new SelectOptionsService();
//Broker //Broker
let filePath = `./data/broker.json`; filePath = `./data/broker.json`;
let data: string = readFileSync(filePath, 'utf8'); let data: string = readFileSync(filePath, 'utf8');
const usersData: UserData[] = JSON.parse(data); // Erwartet ein Array von Objekten const usersData: UserData[] = JSON.parse(data); // Erwartet ein Array von Objekten
const generatedUserData = []; const generatedUserData = [];
@ -70,7 +73,7 @@ fs.ensureDirSync(`./pictures/property`);
//for (const userData of usersData) { //for (const userData of usersData) {
for (let index = 0; index < usersData.length; index++) { for (let index = 0; index < usersData.length; index++) {
const userData = usersData[index]; const userData = usersData[index];
const user: User = { firstname: '', lastname: '', email: '' }; const user: User = { id: undefined, firstname: '', lastname: '', email: '' };
user.licensedIn = []; user.licensedIn = [];
userData.licensedIn.forEach(l => { userData.licensedIn.forEach(l => {
console.log(l['value'], l['name']); console.log(l['value'], l['name']);
@ -91,6 +94,10 @@ for (let index = 0; index < usersData.length; index++) {
user.companyOverview = userData.companyOverview; user.companyOverview = userData.companyOverview;
user.companyWebsite = userData.companyWebsite; user.companyWebsite = userData.companyWebsite;
user.companyLocation = userData.companyLocation; 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.offeredServices = userData.offeredServices;
user.gender = userData.gender; user.gender = userData.gender;
user.customerType = 'professional'; user.customerType = 'professional';
@ -142,6 +149,13 @@ for (let index = 0; index < commercialJsonData.length; index++) {
commercial.email = user.email; commercial.email = user.email;
commercial.draft = false; commercial.draft = false;
commercial.type = sso.typesOfCommercialProperty.find(e => e.oldValue === String(commercial.type)).value; 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 = { // const reducedCommercial = {
// city: commercial.city, // city: commercial.city,
// description: commercial.description, // description: commercial.description,
@ -175,6 +189,13 @@ for (let index = 0; index < businessJsonData.length; index++) {
const user = getRandomItem(generatedUserData); const user = getRandomItem(generatedUserData);
business.email = user.email; business.email = user.email;
business.imageName = emailToDirName(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({ // const embeddingText = JSON.stringify({
// type: typesOfBusiness.find(b => b.value === String(business.type))?.name, // type: typesOfBusiness.find(b => b.value === String(business.type))?.name,
// title: business.title, // title: business.title,

View File

@ -16,18 +16,26 @@ EXCEPTION
WHEN duplicate_object THEN null; WHEN duplicate_object THEN null;
END $$; END $$;
--> statement-breakpoint --> 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" ( CREATE TABLE IF NOT EXISTS "businesses" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"email" varchar(255), "email" varchar(255),
"type" integer, "type" varchar(255),
"title" varchar(255), "title" varchar(255),
"description" text, "description" text,
"city" varchar(255), "city" varchar(255),
"state" char(2), "state" char(2),
"zipCode" integer,
"county" varchar(255),
"price" double precision, "price" double precision,
"favoritesForUser" varchar(30)[], "favoritesForUser" varchar(30)[],
"draft" boolean, "draft" boolean,
"listingsCategory" varchar(255), "listingsCategory" "listingsCategory",
"realEstateIncluded" boolean, "realEstateIncluded" boolean,
"leasedLocation" boolean, "leasedLocation" boolean,
"franchiseResale" boolean, "franchiseResale" boolean,
@ -40,26 +48,27 @@ CREATE TABLE IF NOT EXISTS "businesses" (
"reasonForSale" varchar(255), "reasonForSale" varchar(255),
"brokerLicencing" varchar(255), "brokerLicencing" varchar(255),
"internals" text, "internals" text,
"imagePath" varchar(200), "imageName" varchar(200),
"created" timestamp, "created" timestamp,
"updated" timestamp, "updated" timestamp,
"visits" integer, "visits" integer,
"lastVisit" timestamp, "lastVisit" timestamp,
"embedding" vector(1536) "latitude" double precision,
"longitude" double precision
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE IF NOT EXISTS "commercials" ( CREATE TABLE IF NOT EXISTS "commercials" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"serial_id" serial NOT NULL, "serial_id" serial NOT NULL,
"email" varchar(255), "email" varchar(255),
"type" integer, "type" varchar(255),
"title" varchar(255), "title" varchar(255),
"description" text, "description" text,
"city" varchar(255), "city" varchar(255),
"state" char(2), "state" char(2),
"price" double precision, "price" double precision,
"favoritesForUser" varchar(30)[], "favoritesForUser" varchar(30)[],
"listingsCategory" varchar(255), "listingsCategory" "listingsCategory",
"hideImage" boolean, "hideImage" boolean,
"draft" boolean, "draft" boolean,
"zipCode" integer, "zipCode" integer,
@ -70,7 +79,8 @@ CREATE TABLE IF NOT EXISTS "commercials" (
"updated" timestamp, "updated" timestamp,
"visits" integer, "visits" integer,
"lastVisit" timestamp, "lastVisit" timestamp,
"embedding" vector(1536) "latitude" double precision,
"longitude" double precision
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" ( CREATE TABLE IF NOT EXISTS "users" (
@ -94,7 +104,8 @@ CREATE TABLE IF NOT EXISTS "users" (
"customerSubType" "customerSubType", "customerSubType" "customerSubType",
"created" timestamp, "created" timestamp,
"updated" timestamp, "updated" timestamp,
"embedding" vector(1536), "latitude" double precision,
"longitude" double precision,
CONSTRAINT "users_email_unique" UNIQUE("email") CONSTRAINT "users_email_unique" UNIQUE("email")
); );
--> statement-breakpoint --> statement-breakpoint

View File

@ -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);

View File

@ -0,0 +1 @@
ALTER TABLE "commercials" RENAME COLUMN "serial_id" TO "serialId";

View File

@ -1,5 +1,5 @@
{ {
"id": "2d8edad3-5544-4cb1-a543-84c07737ea9f", "id": "aa3e53ed-4f1b-4e00-84ea-58939189a427",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "7", "version": "7",
"dialect": "postgresql", "dialect": "postgresql",
@ -23,7 +23,7 @@
}, },
"type": { "type": {
"name": "type", "name": "type",
"type": "integer", "type": "varchar(255)",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -51,6 +51,18 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"zipCode": {
"name": "zipCode",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"county": {
"name": "county",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"price": { "price": {
"name": "price", "name": "price",
"type": "double precision", "type": "double precision",
@ -71,7 +83,8 @@
}, },
"listingsCategory": { "listingsCategory": {
"name": "listingsCategory", "name": "listingsCategory",
"type": "varchar(255)", "type": "listingsCategory",
"typeSchema": "public",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -147,8 +160,8 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"imagePath": { "imageName": {
"name": "imagePath", "name": "imageName",
"type": "varchar(200)", "type": "varchar(200)",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
@ -177,9 +190,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -228,7 +247,7 @@
}, },
"type": { "type": {
"name": "type", "name": "type",
"type": "integer", "type": "varchar(255)",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -270,7 +289,8 @@
}, },
"listingsCategory": { "listingsCategory": {
"name": "listingsCategory", "name": "listingsCategory",
"type": "varchar(255)", "type": "listingsCategory",
"typeSchema": "public",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -334,9 +354,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -488,9 +514,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -537,6 +569,14 @@
"male", "male",
"female" "female"
] ]
},
"public.listingsCategory": {
"name": "listingsCategory",
"schema": "public",
"values": [
"commercialProperty",
"business"
]
} }
}, },
"schemas": {}, "schemas": {},

View File

@ -1,6 +1,6 @@
{ {
"id": "93be31d4-beec-4ba8-8d4a-a52763342335", "id": "ff415931-0de6-4c89-900f-c6fd64830b2e",
"prevId": "2d8edad3-5544-4cb1-a543-84c07737ea9f", "prevId": "aa3e53ed-4f1b-4e00-84ea-58939189a427",
"version": "7", "version": "7",
"dialect": "postgresql", "dialect": "postgresql",
"tables": { "tables": {
@ -51,6 +51,18 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"zipCode": {
"name": "zipCode",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"county": {
"name": "county",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"price": { "price": {
"name": "price", "name": "price",
"type": "double precision", "type": "double precision",
@ -71,7 +83,8 @@
}, },
"listingsCategory": { "listingsCategory": {
"name": "listingsCategory", "name": "listingsCategory",
"type": "varchar(255)", "type": "listingsCategory",
"typeSchema": "public",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -147,8 +160,8 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"imagePath": { "imageName": {
"name": "imagePath", "name": "imageName",
"type": "varchar(200)", "type": "varchar(200)",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
@ -177,9 +190,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -214,8 +233,8 @@
"notNull": true, "notNull": true,
"default": "gen_random_uuid()" "default": "gen_random_uuid()"
}, },
"serial_id": { "serialId": {
"name": "serial_id", "name": "serialId",
"type": "serial", "type": "serial",
"primaryKey": false, "primaryKey": false,
"notNull": true "notNull": true
@ -270,7 +289,8 @@
}, },
"listingsCategory": { "listingsCategory": {
"name": "listingsCategory", "name": "listingsCategory",
"type": "varchar(255)", "type": "listingsCategory",
"typeSchema": "public",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
@ -334,9 +354,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -488,9 +514,15 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"embedding": { "latitude": {
"name": "embedding", "name": "latitude",
"type": "vector(1536)", "type": "double precision",
"primaryKey": false,
"notNull": false
},
"longitude": {
"name": "longitude",
"type": "double precision",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
@ -537,6 +569,14 @@
"male", "male",
"female" "female"
] ]
},
"public.listingsCategory": {
"name": "listingsCategory",
"schema": "public",
"values": [
"commercialProperty",
"business"
]
} }
}, },
"schemas": {}, "schemas": {},

View File

@ -5,15 +5,15 @@
{ {
"idx": 0, "idx": 0,
"version": "7", "version": "7",
"when": 1720872296432, "when": 1721737805677,
"tag": "0000_slim_nova", "tag": "0000_freezing_vengeance",
"breakpoints": true "breakpoints": true
}, },
{ {
"idx": 1, "idx": 1,
"version": "7", "version": "7",
"when": 1721134224160, "when": 1721738173220,
"tag": "0001_heavy_bloodscream", "tag": "0001_steady_phantom_reporter",
"breakpoints": true "breakpoints": true
} }
] ]

View File

@ -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'; import { AreasServed, LicensedIn } from '../models/db.model';
export const PG_CONNECTION = 'PG_CONNECTION'; export const PG_CONNECTION = 'PG_CONNECTION';
export const genderEnum = pgEnum('gender', ['male', 'female']); export const genderEnum = pgEnum('gender', ['male', 'female']);
export const customerTypeEnum = pgEnum('customerType', ['buyer', 'professional']); export const customerTypeEnum = pgEnum('customerType', ['buyer', 'professional']);
export const customerSubTypeEnum = pgEnum('customerSubType', ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser']); export const customerSubTypeEnum = pgEnum('customerSubType', ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser']);
export const listingsCategoryEnum = pgEnum('listingsCategory', ['commercialProperty', 'business']);
export const users = pgTable('users', { export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(), id: uuid('id').primaryKey().defaultRandom(),
@ -26,7 +27,9 @@ export const users = pgTable('users', {
customerSubType: customerSubTypeEnum('customerSubType'), customerSubType: customerSubTypeEnum('customerSubType'),
created: timestamp('created'), created: timestamp('created'),
updated: timestamp('updated'), updated: timestamp('updated'),
embedding: vector('embedding', { dimensions: 1536 }), latitude: doublePrecision('latitude'),
longitude: doublePrecision('longitude'),
// embedding: vector('embedding', { dimensions: 1536 }),
}); });
export const businesses = pgTable('businesses', { export const businesses = pgTable('businesses', {
@ -37,10 +40,12 @@ export const businesses = pgTable('businesses', {
description: text('description'), description: text('description'),
city: varchar('city', { length: 255 }), city: varchar('city', { length: 255 }),
state: char('state', { length: 2 }), state: char('state', { length: 2 }),
zipCode: integer('zipCode'),
county: varchar('county', { length: 255 }),
price: doublePrecision('price'), price: doublePrecision('price'),
favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(), favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(),
draft: boolean('draft'), draft: boolean('draft'),
listingsCategory: varchar('listingsCategory', { length: 255 }), listingsCategory: listingsCategoryEnum('listingsCategory'), //varchar('listingsCategory', { length: 255 }),
realEstateIncluded: boolean('realEstateIncluded'), realEstateIncluded: boolean('realEstateIncluded'),
leasedLocation: boolean('leasedLocation'), leasedLocation: boolean('leasedLocation'),
franchiseResale: boolean('franchiseResale'), franchiseResale: boolean('franchiseResale'),
@ -53,18 +58,19 @@ export const businesses = pgTable('businesses', {
reasonForSale: varchar('reasonForSale', { length: 255 }), reasonForSale: varchar('reasonForSale', { length: 255 }),
brokerLicencing: varchar('brokerLicencing', { length: 255 }), brokerLicencing: varchar('brokerLicencing', { length: 255 }),
internals: text('internals'), internals: text('internals'),
imageName: varchar('imagePath', { length: 200 }), imageName: varchar('imageName', { length: 200 }),
created: timestamp('created'), created: timestamp('created'),
updated: timestamp('updated'), updated: timestamp('updated'),
visits: integer('visits'), visits: integer('visits'),
lastVisit: timestamp('lastVisit'), lastVisit: timestamp('lastVisit'),
// Neue Spalte für das OpenAI Embedding latitude: doublePrecision('latitude'),
embedding: vector('embedding', { dimensions: 1536 }), longitude: doublePrecision('longitude'),
// embedding: vector('embedding', { dimensions: 1536 }),
}); });
export const commercials = pgTable('commercials', { export const commercials = pgTable('commercials', {
id: uuid('id').primaryKey().defaultRandom(), id: uuid('id').primaryKey().defaultRandom(),
serialId: serial('serial_id'), serialId: serial('serialId'),
email: varchar('email', { length: 255 }).references(() => users.email), email: varchar('email', { length: 255 }).references(() => users.email),
type: varchar('type', { length: 255 }), type: varchar('type', { length: 255 }),
title: varchar('title', { length: 255 }), title: varchar('title', { length: 255 }),
@ -73,7 +79,7 @@ export const commercials = pgTable('commercials', {
state: char('state', { length: 2 }), state: char('state', { length: 2 }),
price: doublePrecision('price'), price: doublePrecision('price'),
favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(), favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(),
listingsCategory: varchar('listingsCategory', { length: 255 }), listingsCategory: listingsCategoryEnum('listingsCategory'), //listingsCategory: varchar('listingsCategory', { length: 255 }),
hideImage: boolean('hideImage'), hideImage: boolean('hideImage'),
draft: boolean('draft'), draft: boolean('draft'),
zipCode: integer('zipCode'), zipCode: integer('zipCode'),
@ -84,5 +90,7 @@ export const commercials = pgTable('commercials', {
updated: timestamp('updated'), updated: timestamp('updated'),
visits: integer('visits'), visits: integer('visits'),
lastVisit: timestamp('lastVisit'), lastVisit: timestamp('lastVisit'),
embedding: vector('embedding', { dimensions: 1536 }), latitude: doublePrecision('latitude'),
longitude: doublePrecision('longitude'),
// embedding: vector('embedding', { dimensions: 1536 }),
}); });

View File

@ -9,6 +9,16 @@ import { FileService } from '../file/file.service.js';
import { BusinessListing, CommercialPropertyListing } from '../models/db.model'; import { BusinessListing, CommercialPropertyListing } from '../models/db.model';
import { BusinessListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js'; 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() @Injectable()
export class BusinessListingService { export class BusinessListingService {
constructor( constructor(
@ -142,7 +152,7 @@ export class BusinessListingService {
const [{ value: totalCount }] = await countQuery; const [{ value: totalCount }] = await countQuery;
return totalCount; return totalCount;
} }
async findBusinessesById(id: string, user: JwtUser): Promise<CommercialPropertyListing> { async findBusinessesById(id: string, user: JwtUser): Promise<BusinessListing> {
let result = await this.conn let result = await this.conn
.select() .select()
.from(businesses) .from(businesses)
@ -159,7 +169,7 @@ export class BusinessListingService {
return (await this.conn return (await this.conn
.select() .select()
.from(businesses) .from(businesses)
.where(and(...conditions))) as CommercialPropertyListing[]; .where(and(...conditions))) as BusinessListing[];
} }
// #### CREATE ######################################## // #### CREATE ########################################
async createListing(data: BusinessListing): Promise<BusinessListing> { async createListing(data: BusinessListing): Promise<BusinessListing> {

View File

@ -6,7 +6,7 @@ import { Logger } from 'winston';
import * as schema from '../drizzle/schema.js'; import * as schema from '../drizzle/schema.js';
import { commercials, PG_CONNECTION } from '../drizzle/schema.js'; import { commercials, PG_CONNECTION } from '../drizzle/schema.js';
import { FileService } from '../file/file.service.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'; import { CommercialPropertyListingCriteria, emailToDirName, JwtUser } from '../models/main.model.js';
@Injectable() @Injectable()
@ -133,7 +133,7 @@ export class CommercialPropertyService {
data.imageOrder = imageOrder; data.imageOrder = imageOrder;
} }
const [updateListing] = await this.conn.update(commercials).set(data).where(eq(commercials.id, id)).returning(); 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 // Images for commercial Properties

View File

@ -1,25 +1,25 @@
export interface User { // export interface User {
id?: string; // id?: string;
firstname: string; // firstname: string;
lastname: string; // lastname: string;
email: string; // email: string;
phoneNumber?: string; // phoneNumber?: string;
description?: string; // description?: string;
companyName?: string; // companyName?: string;
companyOverview?: string; // companyOverview?: string;
companyWebsite?: string; // companyWebsite?: string;
companyLocation?: string; // companyLocation?: string;
offeredServices?: string; // offeredServices?: string;
areasServed?: AreasServed[]; // areasServed?: AreasServed[];
hasProfile?: boolean; // hasProfile?: boolean;
hasCompanyLogo?: boolean; // hasCompanyLogo?: boolean;
licensedIn?: LicensedIn[]; // licensedIn?: LicensedIn[];
gender?: 'male' | 'female'; // gender?: 'male' | 'female';
customerType?: 'buyer' | 'professional'; // customerType?: 'buyer' | 'professional';
customerSubType?: 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser'; // customerSubType?: 'broker' | 'cpa' | 'attorney' | 'titleCompany' | 'surveyor' | 'appraiser';
created?: Date; // created?: Date;
updated?: Date; // updated?: Date;
} // }
export interface UserData { export interface UserData {
id?: string; id?: string;
firstname: string; firstname: string;
@ -42,22 +42,53 @@ export interface UserData {
created?: Date; created?: Date;
updated?: 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 { export interface BusinessListing {
id: string; id: string; // UUID as a string
email?: string; email: string; // References users.email
type?: string; type?: string;
title?: string; title?: string;
description?: string; description?: string;
city?: string; city?: string;
state?: string; state?: string; // 2-character state code
price?: number; zipCode?: number;
favoritesForUser?: string[]; county?: string;
price?: number; // double precision
favoritesForUser?: string[]; // Array of strings
draft?: boolean; draft?: boolean;
listingsCategory?: ListingsCategory;
realEstateIncluded?: boolean; realEstateIncluded?: boolean;
leasedLocation?: boolean; leasedLocation?: boolean;
franchiseResale?: boolean; franchiseResale?: boolean;
salesRevenue?: number; salesRevenue?: number; // double precision
cashFlow?: number; cashFlow?: number; // double precision
supportAndTraining?: string; supportAndTraining?: string;
employees?: number; employees?: number;
established?: number; established?: number;
@ -70,31 +101,35 @@ export interface BusinessListing {
updated?: Date; updated?: Date;
visits?: number; visits?: number;
lastVisit?: Date; lastVisit?: Date;
listingsCategory?: 'commercialProperty' | 'business'; latitude?: number; // double precision
longitude?: number; // double precision
} }
export interface CommercialPropertyListing { export interface CommercialPropertyListing {
id: string; id: string; // UUID as a string
serialId?: number; serialId: number; // Serial ID
email?: string; email: string; // References users.email
type?: string; type?: string;
title?: string; title?: string;
description?: string; description?: string;
city?: string; city?: string;
state?: string; state?: string; // 2-character state code
price?: number; price?: number; // double precision
favoritesForUser?: string[]; favoritesForUser?: string[]; // Array of strings
listingsCategory?: ListingsCategory;
hideImage?: boolean; hideImage?: boolean;
draft?: boolean; draft?: boolean;
zipCode?: number; zipCode?: number;
county?: string; county?: string;
imageOrder?: string[]; imageOrder?: string[]; // Array of strings
imagePath?: string; imagePath?: string;
created?: Date; created?: Date;
updated?: Date; updated?: Date;
visits?: number; visits?: number;
lastVisit?: Date; lastVisit?: Date;
listingsCategory?: 'commercialProperty' | 'business'; latitude?: number; // double precision
longitude?: number; // double precision
// embedding?: number[]; // Uncomment if needed for vector embedding
} }
export interface AreasServed { export interface AreasServed {
county: string; county: string;

View File

@ -103,7 +103,7 @@ export class UserService {
.from(schema.users) .from(schema.users)
.where(sql`email = ${email}`)) as User[]; .where(sql`email = ${email}`)) as User[];
if (users.length === 0) { 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); this.saveUser(user);
return user; return user;
} else { } else {

View File

@ -82,7 +82,7 @@ export class DetailsCommercialPropertyListingComponent {
this.user = await this.userService.getByMail(this.keycloakUser.email); 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.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.listingUser = await this.userService.getByMail(this.listing.email);
this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description); this.description = this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
import('flowbite').then(flowbite => { import('flowbite').then(flowbite => {

View File

@ -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')]); 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 // Zuweisen der Ergebnisse zu den Member-Variablen der Klasse
this.businessListings = results[0]; this.businessListings = results[0];
this.commercialPropListings = results[1]; this.commercialPropListings = results[1] as CommercialPropertyListing[];
//this.user$ = this.userService.getUserObservable(); //this.user$ = this.userService.getUserObservable();
const token = await this.keycloakService.getToken(); const token = await this.keycloakService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);

View File

@ -84,7 +84,7 @@
</svg> </svg>
</button> </button>
</div> </div>
<div class="mt-4 text-gray-600 text-sm md:text-base text-center">Or search using filters ▼</div> <div class="mt-4 text-gray-600 text-sm md:text-base text-center hover:cursor-pointer" (click)="openModal()">Or search using filters ▼</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,9 +5,12 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular'; import { KeycloakService } from 'keycloak-angular';
import onChange from 'on-change'; import onChange from 'on-change';
import { BusinessListingCriteria, CommercialPropertyListingCriteria, KeycloakUser, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; 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 { ListingsService } from '../../services/listings.service';
import { SearchService } from '../../services/search.service';
import { SelectOptionsService } from '../../services/select-options.service'; import { SelectOptionsService } from '../../services/select-options.service';
import { getCriteriaStateObject, getSessionStorageHandlerWrapper, map2User } from '../../utils/utils'; import { getCriteriaStateObject, map2User } from '../../utils/utils';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
standalone: true, standalone: true,
@ -25,30 +28,54 @@ export class HomeComponent {
isMenuOpen = false; isMenuOpen = false;
user: KeycloakUser; user: KeycloakUser;
prompt: string; prompt: string;
public constructor(private router: Router, private activatedRoute: ActivatedRoute, public selectOptions: SelectOptionsService, public keycloakService: KeycloakService, private listingsService: ListingsService) { public constructor(
this.criteria = onChange(getCriteriaStateObject('business'), getSessionStorageHandlerWrapper(this.activeTabAction)); 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() { async ngOnInit() {
const token = await this.keycloakService.getToken(); 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.user = map2User(token);
// this.router.events.subscribe(event => {
// if (event instanceof NavigationEnd) {
// initFlowbite();
// }
// });
} }
async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') { async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') {
this.activeTabAction = tabname; this.activeTabAction = tabname;
// if (this.activeTabAction === 'business' || this.activeTabAction === 'commercialProperty') { if ('business' === tabname) {
// const statesResult = await this.listingsService.getAllStates(this.activeTabAction); this.criteria = this.createEnhancedProxy(getCriteriaStateObject('business'));
// this.states = statesResult.map(s => s.state).map(ls => ({ name: this.selectOptions.getState(ls as string), value: ls })); } else if ('commercialProperty' === tabname) {
// } else { this.criteria = this.createEnhancedProxy(getCriteriaStateObject('commercialProperty'));
// this.states = this.selectOptions.states; } 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() { search() {
const data = { keep: true }; const data = { keep: true };
this.criteria.prompt = this.prompt;
this.router.navigate([`${this.activeTabAction}Listings`]); this.router.navigate([`${this.activeTabAction}Listings`]);
} }
@ -63,4 +90,11 @@ export class HomeComponent {
toggleMenu() { toggleMenu() {
this.isMenuOpen = !this.isMenuOpen; 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`]);
}
}
} }

View File

@ -77,6 +77,7 @@ export class AccountComponent {
this.user = await this.userService.getByMail(email); this.user = await this.userService.getByMail(email);
} catch (e) { } catch (e) {
this.user = { this.user = {
id: undefined,
email, email,
firstname: keycloakUser.firstName, firstname: keycloakUser.firstName,
lastname: keycloakUser.lastName, lastname: keycloakUser.lastName,

View File

@ -120,7 +120,7 @@ export class EditCommercialPropertyListingComponent {
const token = await this.keycloakService.getToken(); const token = await this.keycloakService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
if (this.mode === 'edit') { 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 { } else {
this.listing = createDefaultCommercialPropertyListing(); this.listing = createDefaultCommercialPropertyListing();
const listingUser = await this.userService.getByMail(keycloakUser.email); const listingUser = await this.userService.getByMail(keycloakUser.email);
@ -134,7 +134,7 @@ export class EditCommercialPropertyListingComponent {
} }
async save() { 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.router.navigate(['editCommercialPropertyListing', this.listing.id]);
this.messageService.addMessage({ severity: 'success', text: 'Listing changes have been persisted', duration: 3000 }); this.messageService.addMessage({ severity: 'success', text: 'Listing changes have been persisted', duration: 3000 });
} }
@ -166,7 +166,7 @@ export class EditCommercialPropertyListingComponent {
if (this.croppedImage) { if (this.croppedImage) {
this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId).subscribe( this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId).subscribe(
async () => { 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(); this.closeModal();
}, },
error => { error => {
@ -182,7 +182,7 @@ export class EditCommercialPropertyListingComponent {
this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName); this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName);
await this.imageService.deleteListingImage(this.listing.imagePath, this.listing.serialId, imageName); await this.imageService.deleteListingImage(this.listing.imagePath, this.listing.serialId, imageName);
await this.listingsService.save(this.listing, 'commercialProperty'); 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 }); this.messageService.addMessage({ severity: 'success', text: 'Image has been deleted', duration: 3000 });
} else { } else {
console.log('deny'); console.log('deny');

View File

@ -6,6 +6,7 @@ import { BusinessListingCriteria, CommercialPropertyListingCriteria, JwtToken, K
export function createDefaultUser(email: string, firstname: string, lastname: string): User { export function createDefaultUser(email: string, firstname: string, lastname: string): User {
return { return {
id: undefined,
email, email,
firstname, firstname,
lastname, lastname,
@ -31,6 +32,7 @@ export function createDefaultUser(email: string, firstname: string, lastname: st
export function createDefaultCommercialPropertyListing(): CommercialPropertyListing { export function createDefaultCommercialPropertyListing(): CommercialPropertyListing {
return { return {
id: undefined, id: undefined,
serialId: undefined,
email: '', email: '',
type: null, type: null,
title: '', title: '',
@ -49,12 +51,15 @@ export function createDefaultCommercialPropertyListing(): CommercialPropertyList
updated: null, updated: null,
visits: null, visits: null,
lastVisit: null, lastVisit: null,
latitude: null,
longitude: null,
listingsCategory: 'commercialProperty', listingsCategory: 'commercialProperty',
}; };
} }
export function createDefaultBusinessListing(): BusinessListing { export function createDefaultBusinessListing(): BusinessListing {
return { return {
id: undefined, id: undefined,
email: '',
type: null, type: null,
title: '', title: '',
description: '', description: '',
@ -79,6 +84,8 @@ export function createDefaultBusinessListing(): BusinessListing {
updated: null, updated: null,
visits: null, visits: null,
lastVisit: null, lastVisit: null,
latitude: null,
longitude: null,
listingsCategory: 'business', listingsCategory: 'business',
}; };
} }