Issue: #109
This commit is contained in:
parent
17213ba4b0
commit
83307684ee
|
|
@ -2,19 +2,18 @@ import 'dotenv/config';
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from 'fs';
|
import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from 'fs';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import OpenAI from 'openai';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import pkg from 'pg';
|
import { Pool } from 'pg';
|
||||||
import { rimraf } from 'rimraf';
|
import { rimraf } from 'rimraf';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { BusinessListingService } from 'src/listings/business-listing.service';
|
import { BusinessListingService } from 'src/listings/business-listing.service';
|
||||||
import { CommercialPropertyService } from 'src/listings/commercial-property.service';
|
import { CommercialPropertyService } from 'src/listings/commercial-property.service';
|
||||||
import { Geo } from 'src/models/server.model';
|
import { Geo } from 'src/models/server.model';
|
||||||
|
import { UserService } from 'src/user/user.service';
|
||||||
import winston from 'winston';
|
import winston from 'winston';
|
||||||
import { User, UserData } from '../models/db.model';
|
import { User, UserData } from '../models/db.model';
|
||||||
import { createDefaultBusinessListing, createDefaultCommercialPropertyListing, createDefaultUser, emailToDirName, KeyValueStyle } from '../models/main.model';
|
import { createDefaultBusinessListing, createDefaultCommercialPropertyListing, createDefaultUser, emailToDirName } from '../models/main.model';
|
||||||
import { SelectOptionsService } from '../select-options/select-options.service';
|
import { SelectOptionsService } from '../select-options/select-options.service';
|
||||||
import { convertUserToDrizzleUser } from '../utils';
|
|
||||||
import * as schema from './schema';
|
import * as schema from './schema';
|
||||||
interface PropertyImportListing {
|
interface PropertyImportListing {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -54,27 +53,27 @@ interface BusinessImportListing {
|
||||||
internals: string;
|
internals: string;
|
||||||
created: string;
|
created: string;
|
||||||
}
|
}
|
||||||
const typesOfBusiness: Array<KeyValueStyle> = [
|
// const typesOfBusiness: Array<KeyValueStyle> = [
|
||||||
{ name: 'Automotive', value: '1', icon: 'fa-solid fa-car', textColorClass: 'text-green-400' },
|
// { name: 'Automotive', value: '1', icon: 'fa-solid fa-car', textColorClass: 'text-green-400' },
|
||||||
{ name: 'Industrial Services', value: '2', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' },
|
// { name: 'Industrial Services', value: '2', icon: 'fa-solid fa-industry', textColorClass: 'text-yellow-400' },
|
||||||
{ name: 'Real Estate', value: '3', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' },
|
// { name: 'Real Estate', value: '3', icon: 'fa-solid fa-building', textColorClass: 'text-blue-400' },
|
||||||
{ name: 'Uncategorized', value: '4', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' },
|
// { name: 'Uncategorized', value: '4', icon: 'fa-solid fa-question', textColorClass: 'text-cyan-400' },
|
||||||
{ name: 'Retail', value: '5', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' },
|
// { name: 'Retail', value: '5', icon: 'fa-solid fa-money-bill-wave', textColorClass: 'text-pink-400' },
|
||||||
{ name: 'Oilfield SVE and MFG.', value: '6', icon: 'fa-solid fa-oil-well', textColorClass: 'text-indigo-400' },
|
// { name: 'Oilfield SVE and MFG.', value: '6', icon: 'fa-solid fa-oil-well', textColorClass: 'text-indigo-400' },
|
||||||
{ name: 'Service', value: '7', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' },
|
// { name: 'Service', value: '7', icon: 'fa-solid fa-umbrella', textColorClass: 'text-teal-400' },
|
||||||
{ name: 'Advertising', value: '8', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' },
|
// { name: 'Advertising', value: '8', icon: 'fa-solid fa-rectangle-ad', textColorClass: 'text-orange-400' },
|
||||||
{ name: 'Agriculture', value: '9', icon: 'fa-solid fa-wheat-awn', textColorClass: 'text-sky-400' },
|
// { name: 'Agriculture', value: '9', icon: 'fa-solid fa-wheat-awn', textColorClass: 'text-sky-400' },
|
||||||
{ name: 'Franchise', value: '10', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' },
|
// { name: 'Franchise', value: '10', icon: 'fa-solid fa-star', textColorClass: 'text-purple-400' },
|
||||||
{ name: 'Professional', value: '11', icon: 'fa-solid fa-user-gear', textColorClass: 'text-gray-400' },
|
// { name: 'Professional', value: '11', icon: 'fa-solid fa-user-gear', textColorClass: 'text-gray-400' },
|
||||||
{ name: 'Manufacturing', value: '12', icon: 'fa-solid fa-industry', textColorClass: 'text-red-400' },
|
// { name: 'Manufacturing', value: '12', icon: 'fa-solid fa-industry', textColorClass: 'text-red-400' },
|
||||||
{ name: 'Food and Restaurant', value: '13', icon: 'fa-solid fa-utensils', textColorClass: 'text-amber-700' },
|
// { name: 'Food and Restaurant', value: '13', icon: 'fa-solid fa-utensils', textColorClass: 'text-amber-700' },
|
||||||
];
|
// ];
|
||||||
const { Pool } = pkg;
|
// const { Pool } = pkg;
|
||||||
|
|
||||||
const openai = new OpenAI({
|
|
||||||
apiKey: process.env.OPENAI_API_KEY, // Stellen Sie sicher, dass Sie Ihren API-Key als Umgebungsvariable setzen
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// const openai = new OpenAI({
|
||||||
|
// apiKey: process.env.OPENAI_API_KEY, // Stellen Sie sicher, dass Sie Ihren API-Key als Umgebungsvariable setzen
|
||||||
|
// });
|
||||||
|
(async () => {
|
||||||
const connectionString = process.env.DATABASE_URL;
|
const connectionString = process.env.DATABASE_URL;
|
||||||
// const pool = new Pool({connectionString})
|
// const pool = new Pool({connectionString})
|
||||||
const client = new Pool({ connectionString });
|
const client = new Pool({ connectionString });
|
||||||
|
|
@ -84,6 +83,7 @@ const logger = winston.createLogger({
|
||||||
});
|
});
|
||||||
const commService = new CommercialPropertyService(null, db);
|
const commService = new CommercialPropertyService(null, db);
|
||||||
const businessService = new BusinessListingService(null, db);
|
const businessService = new BusinessListingService(null, db);
|
||||||
|
const userService = new UserService(null, db, null, null);
|
||||||
//Delete Content
|
//Delete Content
|
||||||
await db.delete(schema.commercials);
|
await db.delete(schema.commercials);
|
||||||
await db.delete(schema.businesses);
|
await db.delete(schema.businesses);
|
||||||
|
|
@ -136,12 +136,12 @@ for (let index = 0; index < usersData.length; index++) {
|
||||||
user.companyOverview = userData.companyOverview;
|
user.companyOverview = userData.companyOverview;
|
||||||
user.companyWebsite = userData.companyWebsite;
|
user.companyWebsite = userData.companyWebsite;
|
||||||
const [city, state] = userData.companyLocation.split('-').map(e => e.trim());
|
const [city, state] = userData.companyLocation.split('-').map(e => e.trim());
|
||||||
user.companyLocation = {};
|
user.location = {};
|
||||||
user.companyLocation.name = city;
|
user.location.name = city;
|
||||||
user.companyLocation.state = state;
|
user.location.state = state;
|
||||||
const cityGeo = geos.states.find(s => s.state_code === state).cities.find(c => c.name === city);
|
const cityGeo = geos.states.find(s => s.state_code === state).cities.find(c => c.name === city);
|
||||||
user.companyLocation.latitude = cityGeo.latitude;
|
user.location.latitude = cityGeo.latitude;
|
||||||
user.companyLocation.longitude = cityGeo.longitude;
|
user.location.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';
|
||||||
|
|
@ -149,24 +149,25 @@ for (let index = 0; index < usersData.length; index++) {
|
||||||
user.created = new Date();
|
user.created = new Date();
|
||||||
user.updated = new Date();
|
user.updated = new Date();
|
||||||
|
|
||||||
const u = await db
|
// const u = await db
|
||||||
.insert(schema.users)
|
// .insert(schema.users)
|
||||||
.values(convertUserToDrizzleUser(user))
|
// .values(convertUserToDrizzleUser(user))
|
||||||
.returning({ insertedId: schema.users.id, gender: schema.users.gender, email: schema.users.email, firstname: schema.users.firstname, lastname: schema.users.lastname });
|
// .returning({ insertedId: schema.users.id, gender: schema.users.gender, email: schema.users.email, firstname: schema.users.firstname, lastname: schema.users.lastname });
|
||||||
generatedUserData.push(u[0]);
|
const u = await userService.saveUser(user);
|
||||||
|
generatedUserData.push(u);
|
||||||
i++;
|
i++;
|
||||||
logger.info(`user_${index} inserted`);
|
logger.info(`user_${index} inserted`);
|
||||||
if (u[0].gender === 'male') {
|
if (u.gender === 'male') {
|
||||||
male++;
|
male++;
|
||||||
const data = readFileSync(`./pictures_base/profile/Mann_${male}.jpg`);
|
const data = readFileSync(`./pictures_base/profile/Mann_${male}.jpg`);
|
||||||
await storeProfilePicture(data, emailToDirName(u[0].email));
|
await storeProfilePicture(data, emailToDirName(u.email));
|
||||||
} else {
|
} else {
|
||||||
female++;
|
female++;
|
||||||
const data = readFileSync(`./pictures_base/profile/Frau_${female}.jpg`);
|
const data = readFileSync(`./pictures_base/profile/Frau_${female}.jpg`);
|
||||||
await storeProfilePicture(data, emailToDirName(u[0].email));
|
await storeProfilePicture(data, emailToDirName(u.email));
|
||||||
}
|
}
|
||||||
const data = readFileSync(`./pictures_base/logo/${i}.jpg`);
|
const data = readFileSync(`./pictures_base/logo/${i}.jpg`);
|
||||||
await storeCompanyLogo(data, emailToDirName(u[0].email));
|
await storeCompanyLogo(data, emailToDirName(u.email));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Corporate Listings
|
//Corporate Listings
|
||||||
|
|
@ -261,17 +262,17 @@ for (let index = 0; index < businessJsonData.length; index++) {
|
||||||
|
|
||||||
//End
|
//End
|
||||||
await client.end();
|
await client.end();
|
||||||
|
})();
|
||||||
function sleep(ms) {
|
// function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
// return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
// }
|
||||||
async function createEmbedding(text: string): Promise<number[]> {
|
// async function createEmbedding(text: string): Promise<number[]> {
|
||||||
const response = await openai.embeddings.create({
|
// const response = await openai.embeddings.create({
|
||||||
model: 'text-embedding-3-small',
|
// model: 'text-embedding-3-small',
|
||||||
input: text,
|
// input: text,
|
||||||
});
|
// });
|
||||||
return response.data[0].embedding;
|
// return response.data[0].embedding;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getRandomItem<T>(arr: T[]): T {
|
function getRandomItem<T>(arr: T[]): T {
|
||||||
if (arr.length === 0) {
|
if (arr.length === 0) {
|
||||||
|
|
@ -283,7 +284,7 @@ function getRandomItem<T>(arr: T[]): T {
|
||||||
}
|
}
|
||||||
function getFilenames(id: string): string[] {
|
function getFilenames(id: string): string[] {
|
||||||
try {
|
try {
|
||||||
let filePath = `./pictures_base/property/${id}`;
|
const filePath = `./pictures_base/property/${id}`;
|
||||||
return readdirSync(filePath);
|
return readdirSync(filePath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -300,7 +301,7 @@ function getRandomDateWithinLastYear(): Date {
|
||||||
return randomDate;
|
return randomDate;
|
||||||
}
|
}
|
||||||
async function storeProfilePicture(buffer: Buffer, userId: string) {
|
async function storeProfilePicture(buffer: Buffer, userId: string) {
|
||||||
let quality = 50;
|
const quality = 50;
|
||||||
const output = await sharp(buffer)
|
const output = await sharp(buffer)
|
||||||
.resize({ width: 300 })
|
.resize({ width: 300 })
|
||||||
.avif({ quality }) // Verwende AVIF
|
.avif({ quality }) // Verwende AVIF
|
||||||
|
|
@ -310,7 +311,7 @@ async function storeProfilePicture(buffer: Buffer, userId: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storeCompanyLogo(buffer: Buffer, adjustedEmail: string) {
|
async function storeCompanyLogo(buffer: Buffer, adjustedEmail: string) {
|
||||||
let quality = 50;
|
const quality = 50;
|
||||||
const output = await sharp(buffer)
|
const output = await sharp(buffer)
|
||||||
.resize({ width: 300 })
|
.resize({ width: 300 })
|
||||||
.avif({ quality }) // Verwende AVIF
|
.avif({ quality }) // Verwende AVIF
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { boolean, char, doublePrecision, integer, jsonb, pgEnum, pgTable, serial, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
|
import { sql } from 'drizzle-orm';
|
||||||
|
import { boolean, doublePrecision, index, 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']);
|
||||||
|
|
@ -7,7 +8,9 @@ export const customerSubTypeEnum = pgEnum('customerSubType', ['broker', 'cpa', '
|
||||||
export const listingsCategoryEnum = pgEnum('listingsCategory', ['commercialProperty', 'business']);
|
export const listingsCategoryEnum = pgEnum('listingsCategory', ['commercialProperty', 'business']);
|
||||||
export const subscriptionTypeEnum = pgEnum('subscriptionType', ['free', 'professional', 'broker']);
|
export const subscriptionTypeEnum = pgEnum('subscriptionType', ['free', 'professional', 'broker']);
|
||||||
|
|
||||||
export const users = pgTable('users', {
|
export const users = pgTable(
|
||||||
|
'users',
|
||||||
|
{
|
||||||
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||||
firstname: varchar('firstname', { length: 255 }).notNull(),
|
firstname: varchar('firstname', { length: 255 }).notNull(),
|
||||||
lastname: varchar('lastname', { length: 255 }).notNull(),
|
lastname: varchar('lastname', { length: 255 }).notNull(),
|
||||||
|
|
@ -17,8 +20,6 @@ export const users = pgTable('users', {
|
||||||
companyName: varchar('companyName', { length: 255 }),
|
companyName: varchar('companyName', { length: 255 }),
|
||||||
companyOverview: text('companyOverview'),
|
companyOverview: text('companyOverview'),
|
||||||
companyWebsite: varchar('companyWebsite', { length: 255 }),
|
companyWebsite: varchar('companyWebsite', { length: 255 }),
|
||||||
city: varchar('city', { length: 255 }),
|
|
||||||
state: char('state', { length: 2 }),
|
|
||||||
offeredServices: text('offeredServices'),
|
offeredServices: text('offeredServices'),
|
||||||
areasServed: jsonb('areasServed').$type<AreasServed[]>(),
|
areasServed: jsonb('areasServed').$type<AreasServed[]>(),
|
||||||
hasProfile: boolean('hasProfile'),
|
hasProfile: boolean('hasProfile'),
|
||||||
|
|
@ -29,23 +30,28 @@ export const users = pgTable('users', {
|
||||||
customerSubType: customerSubTypeEnum('customerSubType'),
|
customerSubType: customerSubTypeEnum('customerSubType'),
|
||||||
created: timestamp('created'),
|
created: timestamp('created'),
|
||||||
updated: timestamp('updated'),
|
updated: timestamp('updated'),
|
||||||
latitude: doublePrecision('latitude'),
|
|
||||||
longitude: doublePrecision('longitude'),
|
|
||||||
subscriptionId: text('subscriptionId'),
|
subscriptionId: text('subscriptionId'),
|
||||||
subscriptionPlan: subscriptionTypeEnum('subscriptionPlan'),
|
subscriptionPlan: subscriptionTypeEnum('subscriptionPlan'),
|
||||||
// embedding: vector('embedding', { dimensions: 1536 }),
|
location: jsonb('location'),
|
||||||
});
|
// city: varchar('city', { length: 255 }),
|
||||||
|
// state: char('state', { length: 2 }),
|
||||||
export const businesses = pgTable('businesses', {
|
// latitude: doublePrecision('latitude'),
|
||||||
|
// longitude: doublePrecision('longitude'),
|
||||||
|
},
|
||||||
|
table => ({
|
||||||
|
locationUserCityStateIdx: index('idx_user_location_city_state').on(
|
||||||
|
sql`((${table.location}->>'name')::varchar), ((${table.location}->>'state')::varchar), ((${table.location}->>'latitude')::float), ((${table.location}->>'longitude')::float)`,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const businesses = pgTable(
|
||||||
|
'businesses',
|
||||||
|
{
|
||||||
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||||
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 }),
|
||||||
description: text('description'),
|
description: text('description'),
|
||||||
city: varchar('city', { length: 255 }),
|
|
||||||
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'),
|
||||||
|
|
@ -65,31 +71,79 @@ export const businesses = pgTable('businesses', {
|
||||||
imageName: varchar('imageName', { length: 200 }),
|
imageName: varchar('imageName', { length: 200 }),
|
||||||
created: timestamp('created'),
|
created: timestamp('created'),
|
||||||
updated: timestamp('updated'),
|
updated: timestamp('updated'),
|
||||||
latitude: doublePrecision('latitude'),
|
location: jsonb('location'),
|
||||||
longitude: doublePrecision('longitude'),
|
// city: varchar('city', { length: 255 }),
|
||||||
// embedding: vector('embedding', { dimensions: 1536 }),
|
// state: char('state', { length: 2 }),
|
||||||
});
|
// zipCode: integer('zipCode'),
|
||||||
|
// county: varchar('county', { length: 255 }),
|
||||||
export const commercials = pgTable('commercials', {
|
// street: varchar('street', { length: 255 }),
|
||||||
|
// housenumber: varchar('housenumber', { length: 10 }),
|
||||||
|
// latitude: doublePrecision('latitude'),
|
||||||
|
// longitude: doublePrecision('longitude'),
|
||||||
|
},
|
||||||
|
table => ({
|
||||||
|
locationBusinessCityStateIdx: index('idx_business_location_city_state').on(
|
||||||
|
sql`((${table.location}->>'name')::varchar), ((${table.location}->>'state')::varchar), ((${table.location}->>'latitude')::float), ((${table.location}->>'longitude')::float)`,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const commercials = pgTable(
|
||||||
|
'commercials',
|
||||||
|
{
|
||||||
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||||
serialId: serial('serialId'),
|
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 }),
|
||||||
description: text('description'),
|
description: text('description'),
|
||||||
city: varchar('city', { length: 255 }),
|
|
||||||
state: char('state', { length: 2 }),
|
|
||||||
price: doublePrecision('price'),
|
price: doublePrecision('price'),
|
||||||
favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(),
|
favoritesForUser: varchar('favoritesForUser', { length: 30 }).array(),
|
||||||
listingsCategory: listingsCategoryEnum('listingsCategory'), //listingsCategory: varchar('listingsCategory', { length: 255 }),
|
listingsCategory: listingsCategoryEnum('listingsCategory'), //listingsCategory: varchar('listingsCategory', { length: 255 }),
|
||||||
draft: boolean('draft'),
|
draft: boolean('draft'),
|
||||||
// zipCode: integer('zipCode'),
|
|
||||||
// county: varchar('county', { length: 255 }),
|
|
||||||
imageOrder: varchar('imageOrder', { length: 200 }).array(),
|
imageOrder: varchar('imageOrder', { length: 200 }).array(),
|
||||||
imagePath: varchar('imagePath', { length: 200 }),
|
imagePath: varchar('imagePath', { length: 200 }),
|
||||||
created: timestamp('created'),
|
created: timestamp('created'),
|
||||||
updated: timestamp('updated'),
|
updated: timestamp('updated'),
|
||||||
latitude: doublePrecision('latitude'),
|
location: jsonb('location'),
|
||||||
longitude: doublePrecision('longitude'),
|
// city: varchar('city', { length: 255 }),
|
||||||
// embedding: vector('embedding', { dimensions: 1536 }),
|
// state: char('state', { length: 2 }),
|
||||||
|
// zipCode: integer('zipCode'),
|
||||||
|
// county: varchar('county', { length: 255 }),
|
||||||
|
// street: varchar('street', { length: 255 }),
|
||||||
|
// housenumber: varchar('housenumber', { length: 10 }),
|
||||||
|
// latitude: doublePrecision('latitude'),
|
||||||
|
// longitude: doublePrecision('longitude'),
|
||||||
|
},
|
||||||
|
table => ({
|
||||||
|
locationCommercialsCityStateIdx: index('idx_commercials_location_city_state').on(
|
||||||
|
sql`((${table.location}->>'name')::varchar), ((${table.location}->>'state')::varchar), ((${table.location}->>'latitude')::float), ((${table.location}->>'longitude')::float)`,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// export const geo = pgTable('geo', {
|
||||||
|
// id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||||
|
// country: varchar('country', { length: 255 }).default('us'),
|
||||||
|
// state: char('state', { length: 2 }),
|
||||||
|
// city: varchar('city', { length: 255 }),
|
||||||
|
// zipCode: integer('zipCode'),
|
||||||
|
// county: varchar('county', { length: 255 }),
|
||||||
|
// street: varchar('street', { length: 255 }),
|
||||||
|
// housenumber: varchar('housenumber', { length: 10 }),
|
||||||
|
// latitude: doublePrecision('latitude'),
|
||||||
|
// longitude: doublePrecision('longitude'),
|
||||||
|
// });
|
||||||
|
export const listingEvents = pgTable('listing_events', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom().notNull(),
|
||||||
|
listingId: uuid('listing_id').notNull(), // Assuming listings are referenced by UUID, adjust as necessary
|
||||||
|
userId: uuid('user_id'), // Nullable, if user is logged in, otherwise null
|
||||||
|
eventType: varchar('event_type', { length: 50 }).notNull(), // 'view', 'print', 'email', 'facebook', 'x', 'linkedin', 'contact'
|
||||||
|
eventTimestamp: timestamp('event_timestamp').defaultNow().notNull(),
|
||||||
|
userIp: varchar('user_ip', { length: 45 }), // Optional if you choose to track IP in frontend or backend
|
||||||
|
userAgent: varchar('user_agent', { length: 255 }), // Store User-Agent as string
|
||||||
|
locationCountry: varchar('location_country', { length: 100 }), // Country from IP
|
||||||
|
locationCity: varchar('location_city', { length: 100 }), // City from IP
|
||||||
|
locationLat: varchar('location_lat', { length: 20 }), // Latitude from IP, stored as varchar
|
||||||
|
locationLng: varchar('location_lng', { length: 20 }), // Longitude from IP, stored as varchar
|
||||||
|
referrer: varchar('referrer', { length: 255 }), // Referrer URL if applicable
|
||||||
|
additionalData: jsonb('additional_data'), // JSON for any other optional data (like email, social shares etc.)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { FileService } from '../file/file.service';
|
||||||
import { GeoService } from '../geo/geo.service';
|
import { GeoService } from '../geo/geo.service';
|
||||||
import { BusinessListing, BusinessListingSchema } from '../models/db.model';
|
import { BusinessListing, BusinessListingSchema } from '../models/db.model';
|
||||||
import { BusinessListingCriteria, JwtUser } from '../models/main.model';
|
import { BusinessListingCriteria, JwtUser } from '../models/main.model';
|
||||||
import { convertBusinessToDrizzleBusiness, convertDrizzleBusinessToBusiness, getDistanceQuery, splitName } from '../utils';
|
import { getDistanceQuery, splitName } from '../utils';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BusinessListingService {
|
export class BusinessListingService {
|
||||||
|
|
@ -25,7 +25,8 @@ export class BusinessListingService {
|
||||||
const whereConditions: SQL[] = [];
|
const whereConditions: SQL[] = [];
|
||||||
|
|
||||||
if (criteria.city && criteria.searchType === 'exact') {
|
if (criteria.city && criteria.searchType === 'exact') {
|
||||||
whereConditions.push(ilike(businesses.city, `%${criteria.city.name}%`));
|
whereConditions.push(sql`${businesses.location}->>'name' ilike ${criteria.city.name}`);
|
||||||
|
//whereConditions.push(ilike(businesses.location-->'city', `%${criteria.city.name}%`));
|
||||||
}
|
}
|
||||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
|
|
@ -36,7 +37,7 @@ export class BusinessListingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.state) {
|
if (criteria.state) {
|
||||||
whereConditions.push(eq(businesses.state, criteria.state));
|
whereConditions.push(sql`${businesses.location}->>'state' = ${criteria.city.state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.minPrice) {
|
if (criteria.minPrice) {
|
||||||
|
|
@ -165,7 +166,7 @@ export class BusinessListingService {
|
||||||
|
|
||||||
const data = await query;
|
const data = await query;
|
||||||
const totalCount = await this.getBusinessListingsCount(criteria, user);
|
const totalCount = await this.getBusinessListingsCount(criteria, user);
|
||||||
const results = data.map(r => r.business).map(r => convertDrizzleBusinessToBusiness(r));
|
const results = data.map(r => r.business);
|
||||||
return {
|
return {
|
||||||
results,
|
results,
|
||||||
totalCount,
|
totalCount,
|
||||||
|
|
@ -197,7 +198,7 @@ export class BusinessListingService {
|
||||||
.from(businesses)
|
.from(businesses)
|
||||||
.where(and(...conditions));
|
.where(and(...conditions));
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
return convertDrizzleBusinessToBusiness(result[0]) as BusinessListing;
|
return result[0] as BusinessListing;
|
||||||
} else {
|
} else {
|
||||||
throw new BadRequestException(`No entry available for ${id}`);
|
throw new BadRequestException(`No entry available for ${id}`);
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +215,7 @@ export class BusinessListingService {
|
||||||
.from(businesses)
|
.from(businesses)
|
||||||
.where(and(...conditions))) as BusinessListing[];
|
.where(and(...conditions))) as BusinessListing[];
|
||||||
|
|
||||||
return listings.map(l => convertDrizzleBusinessToBusiness(l));
|
return listings;
|
||||||
}
|
}
|
||||||
// #### Find Favorites ########################################
|
// #### Find Favorites ########################################
|
||||||
async findFavoriteListings(user: JwtUser): Promise<BusinessListing[]> {
|
async findFavoriteListings(user: JwtUser): Promise<BusinessListing[]> {
|
||||||
|
|
@ -222,7 +223,7 @@ export class BusinessListingService {
|
||||||
.select()
|
.select()
|
||||||
.from(businesses)
|
.from(businesses)
|
||||||
.where(arrayContains(businesses.favoritesForUser, [user.username]));
|
.where(arrayContains(businesses.favoritesForUser, [user.username]));
|
||||||
return userFavorites.map(l => convertDrizzleBusinessToBusiness(l));
|
return userFavorites;
|
||||||
}
|
}
|
||||||
// #### CREATE ########################################
|
// #### CREATE ########################################
|
||||||
async createListing(data: BusinessListing): Promise<BusinessListing> {
|
async createListing(data: BusinessListing): Promise<BusinessListing> {
|
||||||
|
|
@ -230,10 +231,10 @@ export class BusinessListingService {
|
||||||
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
||||||
data.updated = new Date();
|
data.updated = new Date();
|
||||||
BusinessListingSchema.parse(data);
|
BusinessListingSchema.parse(data);
|
||||||
const convertedBusinessListing = convertBusinessToDrizzleBusiness(data);
|
const convertedBusinessListing = data;
|
||||||
delete convertedBusinessListing.id;
|
delete convertedBusinessListing.id;
|
||||||
const [createdListing] = await this.conn.insert(businesses).values(convertedBusinessListing).returning();
|
const [createdListing] = await this.conn.insert(businesses).values(convertedBusinessListing).returning();
|
||||||
return convertDrizzleBusinessToBusiness(createdListing);
|
return createdListing;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const filteredErrors = error.errors
|
const filteredErrors = error.errors
|
||||||
|
|
@ -253,9 +254,9 @@ export class BusinessListingService {
|
||||||
data.updated = new Date();
|
data.updated = new Date();
|
||||||
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
||||||
BusinessListingSchema.parse(data);
|
BusinessListingSchema.parse(data);
|
||||||
const convertedBusinessListing = convertBusinessToDrizzleBusiness(data);
|
const convertedBusinessListing = data;
|
||||||
const [updateListing] = await this.conn.update(businesses).set(convertedBusinessListing).where(eq(businesses.id, id)).returning();
|
const [updateListing] = await this.conn.update(businesses).set(convertedBusinessListing).where(eq(businesses.id, id)).returning();
|
||||||
return convertDrizzleBusinessToBusiness(updateListing);
|
return updateListing;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const filteredErrors = error.errors
|
const filteredErrors = error.errors
|
||||||
|
|
@ -285,11 +286,11 @@ export class BusinessListingService {
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
// States
|
// States
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
async getStates(): Promise<any[]> {
|
// async getStates(): Promise<any[]> {
|
||||||
return await this.conn
|
// return await this.conn
|
||||||
.select({ state: businesses.state, count: sql<number>`count(${businesses.id})`.mapWith(Number) })
|
// .select({ state: businesses.state, count: sql<number>`count(${businesses.id})`.mapWith(Number) })
|
||||||
.from(businesses)
|
// .from(businesses)
|
||||||
.groupBy(sql`${businesses.state}`)
|
// .groupBy(sql`${businesses.state}`)
|
||||||
.orderBy(sql`count desc`);
|
// .orderBy(sql`count desc`);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,10 @@ export class BusinessListingsController {
|
||||||
deleteById(@Param('id') id: string) {
|
deleteById(@Param('id') id: string) {
|
||||||
this.listingsService.deleteListing(id);
|
this.listingsService.deleteListing(id);
|
||||||
}
|
}
|
||||||
@Get('states/all')
|
// @Get('states/all')
|
||||||
getStates(): any {
|
// getStates(): any {
|
||||||
return this.listingsService.getStates();
|
// return this.listingsService.getStates();
|
||||||
}
|
// }
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Delete('favorites/:id')
|
@Delete('favorites/:id')
|
||||||
deleteFavorite(@Request() req, @Param('id') id: string) {
|
deleteFavorite(@Request() req, @Param('id') id: string) {
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ export class CommercialPropertyListingsController {
|
||||||
findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<number> {
|
findTotal(@Request() req, @Body() criteria: CommercialPropertyListingCriteria): Promise<number> {
|
||||||
return this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser);
|
return this.listingsService.getCommercialPropertiesCount(criteria, req.user as JwtUser);
|
||||||
}
|
}
|
||||||
@Get('states/all')
|
// @Get('states/all')
|
||||||
getStates(): any {
|
// getStates(): any {
|
||||||
return this.listingsService.getStates();
|
// return this.listingsService.getStates();
|
||||||
}
|
// }
|
||||||
@Post()
|
@Post()
|
||||||
async create(@Body() listing: any) {
|
async create(@Body() listing: any) {
|
||||||
this.logger.info(`Save Listing`);
|
this.logger.info(`Save Listing`);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { FileService } from '../file/file.service';
|
||||||
import { GeoService } from '../geo/geo.service';
|
import { GeoService } from '../geo/geo.service';
|
||||||
import { CommercialPropertyListing, CommercialPropertyListingSchema } from '../models/db.model';
|
import { CommercialPropertyListing, CommercialPropertyListingSchema } from '../models/db.model';
|
||||||
import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model';
|
import { CommercialPropertyListingCriteria, JwtUser } from '../models/main.model';
|
||||||
import { convertCommercialToDrizzleCommercial, convertDrizzleBusinessToBusiness, convertDrizzleCommercialToCommercial, getDistanceQuery } from '../utils';
|
import { getDistanceQuery } from '../utils';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommercialPropertyService {
|
export class CommercialPropertyService {
|
||||||
|
|
@ -24,7 +24,7 @@ export class CommercialPropertyService {
|
||||||
const whereConditions: SQL[] = [];
|
const whereConditions: SQL[] = [];
|
||||||
|
|
||||||
if (criteria.city && criteria.searchType === 'exact') {
|
if (criteria.city && criteria.searchType === 'exact') {
|
||||||
whereConditions.push(ilike(schema.commercials.city, `%${criteria.city.name}%`));
|
whereConditions.push(sql`${commercials.location}->>'name' ilike ${criteria.city.name}`);
|
||||||
}
|
}
|
||||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
|
|
@ -35,7 +35,7 @@ export class CommercialPropertyService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.state) {
|
if (criteria.state) {
|
||||||
whereConditions.push(eq(schema.commercials.state, criteria.state));
|
whereConditions.push(sql`${schema.commercials.location}->>'state' = ${criteria.city.state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.minPrice) {
|
if (criteria.minPrice) {
|
||||||
|
|
@ -89,7 +89,7 @@ export class CommercialPropertyService {
|
||||||
query.limit(length).offset(start);
|
query.limit(length).offset(start);
|
||||||
|
|
||||||
const data = await query;
|
const data = await query;
|
||||||
const results = data.map(r => r.commercial).map(r => convertDrizzleCommercialToCommercial(r));
|
const results = data.map(r => r.commercial);
|
||||||
const totalCount = await this.getCommercialPropertiesCount(criteria, user);
|
const totalCount = await this.getCommercialPropertiesCount(criteria, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -122,7 +122,7 @@ export class CommercialPropertyService {
|
||||||
.from(commercials)
|
.from(commercials)
|
||||||
.where(and(...conditions));
|
.where(and(...conditions));
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
return convertDrizzleCommercialToCommercial(result[0]) as CommercialPropertyListing;
|
return result[0] as CommercialPropertyListing;
|
||||||
} else {
|
} else {
|
||||||
throw new BadRequestException(`No entry available for ${id}`);
|
throw new BadRequestException(`No entry available for ${id}`);
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ export class CommercialPropertyService {
|
||||||
.select()
|
.select()
|
||||||
.from(commercials)
|
.from(commercials)
|
||||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||||
return listings.map(l => convertDrizzleCommercialToCommercial(l)) as CommercialPropertyListing[];
|
return listings as CommercialPropertyListing[];
|
||||||
}
|
}
|
||||||
// #### Find Favorites ########################################
|
// #### Find Favorites ########################################
|
||||||
async findFavoriteListings(user: JwtUser): Promise<CommercialPropertyListing[]> {
|
async findFavoriteListings(user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||||
|
|
@ -147,7 +147,7 @@ export class CommercialPropertyService {
|
||||||
.select()
|
.select()
|
||||||
.from(commercials)
|
.from(commercials)
|
||||||
.where(arrayContains(commercials.favoritesForUser, [user.username]));
|
.where(arrayContains(commercials.favoritesForUser, [user.username]));
|
||||||
return userFavorites.map(l => convertDrizzleBusinessToBusiness(l));
|
return userFavorites;
|
||||||
}
|
}
|
||||||
// #### Find by imagePath ########################################
|
// #### Find by imagePath ########################################
|
||||||
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
||||||
|
|
@ -155,7 +155,7 @@ export class CommercialPropertyService {
|
||||||
.select()
|
.select()
|
||||||
.from(commercials)
|
.from(commercials)
|
||||||
.where(and(sql`${commercials.imagePath} = ${imagePath}`, sql`${commercials.serialId} = ${serial}`));
|
.where(and(sql`${commercials.imagePath} = ${imagePath}`, sql`${commercials.serialId} = ${serial}`));
|
||||||
return convertDrizzleCommercialToCommercial(result[0]) as CommercialPropertyListing;
|
return result[0] as CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
// #### CREATE ########################################
|
// #### CREATE ########################################
|
||||||
async createListing(data: CommercialPropertyListing): Promise<CommercialPropertyListing> {
|
async createListing(data: CommercialPropertyListing): Promise<CommercialPropertyListing> {
|
||||||
|
|
@ -163,10 +163,10 @@ export class CommercialPropertyService {
|
||||||
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
||||||
data.updated = new Date();
|
data.updated = new Date();
|
||||||
CommercialPropertyListingSchema.parse(data);
|
CommercialPropertyListingSchema.parse(data);
|
||||||
const convertedCommercialPropertyListing = convertCommercialToDrizzleCommercial(data);
|
const convertedCommercialPropertyListing = data;
|
||||||
delete convertedCommercialPropertyListing.id;
|
delete convertedCommercialPropertyListing.id;
|
||||||
const [createdListing] = await this.conn.insert(commercials).values(convertedCommercialPropertyListing).returning();
|
const [createdListing] = await this.conn.insert(commercials).values(convertedCommercialPropertyListing).returning();
|
||||||
return convertDrizzleCommercialToCommercial(createdListing);
|
return createdListing;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const filteredErrors = error.errors
|
const filteredErrors = error.errors
|
||||||
|
|
@ -192,9 +192,9 @@ export class CommercialPropertyService {
|
||||||
this.logger.warn(`changes between image directory and imageOrder in listing ${data.serialId}: ${difference.join(',')}`);
|
this.logger.warn(`changes between image directory and imageOrder in listing ${data.serialId}: ${difference.join(',')}`);
|
||||||
data.imageOrder = imageOrder;
|
data.imageOrder = imageOrder;
|
||||||
}
|
}
|
||||||
const convertedCommercialPropertyListing = convertCommercialToDrizzleCommercial(data);
|
const convertedCommercialPropertyListing = data;
|
||||||
const [updateListing] = await this.conn.update(commercials).set(convertedCommercialPropertyListing).where(eq(commercials.id, id)).returning();
|
const [updateListing] = await this.conn.update(commercials).set(convertedCommercialPropertyListing).where(eq(commercials.id, id)).returning();
|
||||||
return convertDrizzleCommercialToCommercial(updateListing);
|
return updateListing;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const filteredErrors = error.errors
|
const filteredErrors = error.errors
|
||||||
|
|
@ -240,11 +240,11 @@ export class CommercialPropertyService {
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
// States
|
// States
|
||||||
// ##############################################################
|
// ##############################################################
|
||||||
async getStates(): Promise<any[]> {
|
// async getStates(): Promise<any[]> {
|
||||||
return await this.conn
|
// return await this.conn
|
||||||
.select({ state: commercials.state, count: sql<number>`count(${commercials.id})`.mapWith(Number) })
|
// .select({ state: commercials.state, count: sql<number>`count(${commercials.id})`.mapWith(Number) })
|
||||||
.from(commercials)
|
// .from(commercials)
|
||||||
.groupBy(sql`${commercials.state}`)
|
// .groupBy(sql`${commercials.state}`)
|
||||||
.orderBy(sql`count desc`);
|
// .orderBy(sql`count desc`);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,10 @@ export const GeoSchema = z.object({
|
||||||
message: 'Longitude muss zwischen -180 und 180 liegen',
|
message: 'Longitude muss zwischen -180 und 180 liegen',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
county: z.string().optional().nullable(),
|
||||||
|
housenumber: z.string().optional().nullable(),
|
||||||
|
street: z.string().optional().nullable(),
|
||||||
|
zipCode: z.number().optional().nullable(),
|
||||||
});
|
});
|
||||||
const phoneRegex = /^(\+1|1)?[-.\s]?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
|
const phoneRegex = /^(\+1|1)?[-.\s]?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
|
||||||
export const UserSchema = z
|
export const UserSchema = z
|
||||||
|
|
@ -157,7 +161,7 @@ export const UserSchema = z
|
||||||
companyName: z.string().optional().nullable(),
|
companyName: z.string().optional().nullable(),
|
||||||
companyOverview: z.string().optional().nullable(),
|
companyOverview: z.string().optional().nullable(),
|
||||||
companyWebsite: z.string().url({ message: 'Invalid URL format' }).optional().nullable(),
|
companyWebsite: z.string().url({ message: 'Invalid URL format' }).optional().nullable(),
|
||||||
companyLocation: GeoSchema.optional().nullable(),
|
location: GeoSchema.optional().nullable(),
|
||||||
offeredServices: z.string().optional().nullable(),
|
offeredServices: z.string().optional().nullable(),
|
||||||
areasServed: z.array(AreasServedSchema).optional().nullable(),
|
areasServed: z.array(AreasServedSchema).optional().nullable(),
|
||||||
hasProfile: z.boolean().optional().nullable(),
|
hasProfile: z.boolean().optional().nullable(),
|
||||||
|
|
@ -213,7 +217,7 @@ export const UserSchema = z
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.companyLocation) {
|
if (!data.location) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: 'Company location is required for professional customers',
|
message: 'Company location is required for professional customers',
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,10 @@ export interface UploadParams {
|
||||||
export interface GeoResult {
|
export interface GeoResult {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
street?: string;
|
||||||
|
housenumber?: string;
|
||||||
|
county?: string;
|
||||||
|
zipCode?: number;
|
||||||
state: string;
|
state: string;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
|
|
@ -318,7 +322,7 @@ export function createDefaultUser(email: string, firstname: string, lastname: st
|
||||||
companyName: null,
|
companyName: null,
|
||||||
companyOverview: null,
|
companyOverview: null,
|
||||||
companyWebsite: null,
|
companyWebsite: null,
|
||||||
companyLocation: null,
|
location: null,
|
||||||
offeredServices: null,
|
offeredServices: null,
|
||||||
areasServed: [],
|
areasServed: [],
|
||||||
hasProfile: false,
|
hasProfile: false,
|
||||||
|
|
|
||||||
|
|
@ -70,3 +70,34 @@ export interface CountyRequest {
|
||||||
prefix: string;
|
prefix: string;
|
||||||
states: string[];
|
states: string[];
|
||||||
}
|
}
|
||||||
|
export interface Address {
|
||||||
|
house_number: string;
|
||||||
|
road: string;
|
||||||
|
quarter: string;
|
||||||
|
suburb: string;
|
||||||
|
city: string;
|
||||||
|
county: string;
|
||||||
|
state: string;
|
||||||
|
ISO3166_2_lvl4: string;
|
||||||
|
postcode: string;
|
||||||
|
country: string;
|
||||||
|
country_code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Place {
|
||||||
|
place_id: number;
|
||||||
|
licence: string;
|
||||||
|
osm_type: string;
|
||||||
|
osm_id: number;
|
||||||
|
lat: string;
|
||||||
|
lon: string;
|
||||||
|
class: string;
|
||||||
|
type: string;
|
||||||
|
place_rank: number;
|
||||||
|
importance: number;
|
||||||
|
addresstype: string;
|
||||||
|
name: string;
|
||||||
|
display_name: string;
|
||||||
|
address: Address;
|
||||||
|
boundingbox: [string, string, string, string];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { FileService } from '../file/file.service';
|
||||||
import { GeoService } from '../geo/geo.service';
|
import { GeoService } from '../geo/geo.service';
|
||||||
import { User, UserSchema } from '../models/db.model';
|
import { User, UserSchema } from '../models/db.model';
|
||||||
import { createDefaultUser, emailToDirName, JwtUser, UserListingCriteria } from '../models/main.model';
|
import { createDefaultUser, emailToDirName, JwtUser, UserListingCriteria } from '../models/main.model';
|
||||||
import { convertDrizzleUserToUser, convertUserToDrizzleUser, getDistanceQuery, splitName } from '../utils';
|
import { DrizzleUser, getDistanceQuery, splitName } from '../utils';
|
||||||
|
|
||||||
type CustomerSubType = (typeof customerSubTypeEnum.enumValues)[number];
|
type CustomerSubType = (typeof customerSubTypeEnum.enumValues)[number];
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -25,7 +25,7 @@ export class UserService {
|
||||||
const whereConditions: SQL[] = [];
|
const whereConditions: SQL[] = [];
|
||||||
whereConditions.push(eq(schema.users.customerType, 'professional'));
|
whereConditions.push(eq(schema.users.customerType, 'professional'));
|
||||||
if (criteria.city && criteria.searchType === 'exact') {
|
if (criteria.city && criteria.searchType === 'exact') {
|
||||||
whereConditions.push(ilike(schema.users.city, `%${criteria.city.name}%`));
|
whereConditions.push(sql`${schema.users.location}->>'name' ilike ${criteria.city.name}`);
|
||||||
}
|
}
|
||||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
|
|
@ -80,7 +80,7 @@ export class UserService {
|
||||||
query.limit(length).offset(start);
|
query.limit(length).offset(start);
|
||||||
|
|
||||||
const data = await query;
|
const data = await query;
|
||||||
const results = data.map(r => convertDrizzleUserToUser(r));
|
const results = data;
|
||||||
const totalCount = await this.getUserListingsCount(criteria);
|
const totalCount = await this.getUserListingsCount(criteria);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -108,12 +108,12 @@ export class UserService {
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
const user: User = { id: undefined, customerType: 'buyer', ...createDefaultUser(email, jwtuser.firstname, jwtuser.lastname, null) };
|
const user: User = { id: undefined, customerType: 'buyer', ...createDefaultUser(email, jwtuser.firstname, jwtuser.lastname, null) };
|
||||||
const u = await this.saveUser(user, false);
|
const u = await this.saveUser(user, false);
|
||||||
return convertDrizzleUserToUser(u);
|
return u;
|
||||||
} else {
|
} else {
|
||||||
const user = users[0];
|
const user = users[0];
|
||||||
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
|
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
|
||||||
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
|
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
|
||||||
return convertDrizzleUserToUser(user);
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getUserById(id: string) {
|
async getUserById(id: string) {
|
||||||
|
|
@ -125,7 +125,7 @@ export class UserService {
|
||||||
const user = users[0];
|
const user = users[0];
|
||||||
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
|
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
|
||||||
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
|
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
|
||||||
return convertDrizzleUserToUser(user);
|
return user;
|
||||||
}
|
}
|
||||||
async saveUser(user: User, processValidation = true): Promise<User> {
|
async saveUser(user: User, processValidation = true): Promise<User> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -139,13 +139,14 @@ export class UserService {
|
||||||
if (processValidation) {
|
if (processValidation) {
|
||||||
validatedUser = UserSchema.parse(user);
|
validatedUser = UserSchema.parse(user);
|
||||||
}
|
}
|
||||||
const drizzleUser = convertUserToDrizzleUser(validatedUser);
|
//const drizzleUser = convertUserToDrizzleUser(validatedUser);
|
||||||
|
const drizzleUser = validatedUser as DrizzleUser;
|
||||||
if (user.id) {
|
if (user.id) {
|
||||||
const [updateUser] = await this.conn.update(schema.users).set(drizzleUser).where(eq(schema.users.id, user.id)).returning();
|
const [updateUser] = await this.conn.update(schema.users).set(drizzleUser).where(eq(schema.users.id, user.id)).returning();
|
||||||
return convertDrizzleUserToUser(updateUser) as User;
|
return updateUser as User;
|
||||||
} else {
|
} else {
|
||||||
const [newUser] = await this.conn.insert(schema.users).values(drizzleUser).returning();
|
const [newUser] = await this.conn.insert(schema.users).values(drizzleUser).returning();
|
||||||
return convertDrizzleUserToUser(newUser) as User;
|
return newUser as User;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { sql } from 'drizzle-orm';
|
import { sql } from 'drizzle-orm';
|
||||||
import { businesses, commercials, users } from './drizzle/schema';
|
import { businesses, commercials, users } from './drizzle/schema';
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from './models/db.model';
|
|
||||||
export const EARTH_RADIUS_KM = 6371; // Erdradius in Kilometern
|
export const EARTH_RADIUS_KM = 6371; // Erdradius in Kilometern
|
||||||
export const EARTH_RADIUS_MILES = 3959; // Erdradius in Meilen
|
export const EARTH_RADIUS_MILES = 3959; // Erdradius in Meilen
|
||||||
export function convertStringToNullUndefined(value) {
|
export function convertStringToNullUndefined(value) {
|
||||||
|
|
@ -20,125 +19,140 @@ export function convertStringToNullUndefined(value) {
|
||||||
export const getDistanceQuery = (schema: typeof businesses | typeof commercials | typeof users, lat: number, lon: number, unit: 'km' | 'miles' = 'miles') => {
|
export const getDistanceQuery = (schema: typeof businesses | typeof commercials | typeof users, lat: number, lon: number, unit: 'km' | 'miles' = 'miles') => {
|
||||||
const radius = unit === 'km' ? EARTH_RADIUS_KM : EARTH_RADIUS_MILES;
|
const radius = unit === 'km' ? EARTH_RADIUS_KM : EARTH_RADIUS_MILES;
|
||||||
|
|
||||||
|
// return sql`
|
||||||
|
// ${radius} * 2 * ASIN(SQRT(
|
||||||
|
// POWER(SIN((${lat} - ${schema.latitude}) * PI() / 180 / 2), 2) +
|
||||||
|
// COS(${lat} * PI() / 180) * COS(${schema.latitude} * PI() / 180) *
|
||||||
|
// POWER(SIN((${lon} - ${schema.longitude}) * PI() / 180 / 2), 2)
|
||||||
|
// ))
|
||||||
|
// `;
|
||||||
return sql`
|
return sql`
|
||||||
${radius} * 2 * ASIN(SQRT(
|
${radius} * 2 * ASIN(SQRT(
|
||||||
POWER(SIN((${lat} - ${schema.latitude}) * PI() / 180 / 2), 2) +
|
POWER(SIN((${lat} - (${schema.location}->>'latitude')::float) * PI() / 180 / 2), 2) +
|
||||||
COS(${lat} * PI() / 180) * COS(${schema.latitude} * PI() / 180) *
|
COS(${lat} * PI() / 180) * COS((${schema.location}->>'latitude')::float * PI() / 180) *
|
||||||
POWER(SIN((${lon} - ${schema.longitude}) * PI() / 180 / 2), 2)
|
POWER(SIN((${lon} - (${schema.location}->>'longitude')::float) * PI() / 180 / 2), 2)
|
||||||
))
|
))
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DrizzleUser = typeof users.$inferSelect;
|
export type DrizzleUser = typeof users.$inferSelect;
|
||||||
type DrizzleBusinessListing = typeof businesses.$inferSelect;
|
export type DrizzleBusinessListing = typeof businesses.$inferSelect;
|
||||||
type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect;
|
export type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect;
|
||||||
export function convertBusinessToDrizzleBusiness(businessListing: Partial<BusinessListing>): DrizzleBusinessListing {
|
// export function convertBusinessToDrizzleBusiness(businessListing: Partial<BusinessListing>): DrizzleBusinessListing {
|
||||||
const drizzleBusinessListing = flattenObject(businessListing);
|
// const drizzleBusinessListing = flattenObject(businessListing);
|
||||||
drizzleBusinessListing.city = drizzleBusinessListing.name;
|
// drizzleBusinessListing.city = drizzleBusinessListing.name;
|
||||||
delete drizzleBusinessListing.name;
|
// delete drizzleBusinessListing.name;
|
||||||
return drizzleBusinessListing;
|
// return drizzleBusinessListing;
|
||||||
}
|
// }
|
||||||
export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial<DrizzleBusinessListing>): BusinessListing {
|
// export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial<DrizzleBusinessListing>): BusinessListing {
|
||||||
const o = {
|
// const o = {
|
||||||
location: drizzleBusinessListing.city ? undefined : null,
|
// location: drizzleBusinessListing.city ? undefined : null,
|
||||||
location_name: drizzleBusinessListing.city ? drizzleBusinessListing.city : undefined,
|
// location_name: drizzleBusinessListing.city ? drizzleBusinessListing.city : undefined,
|
||||||
location_state: drizzleBusinessListing.state ? drizzleBusinessListing.state : undefined,
|
// location_state: drizzleBusinessListing.state ? drizzleBusinessListing.state : undefined,
|
||||||
location_latitude: drizzleBusinessListing.latitude ? drizzleBusinessListing.latitude : undefined,
|
// location_latitude: drizzleBusinessListing.latitude ? drizzleBusinessListing.latitude : undefined,
|
||||||
location_longitude: drizzleBusinessListing.longitude ? drizzleBusinessListing.longitude : undefined,
|
// location_longitude: drizzleBusinessListing.longitude ? drizzleBusinessListing.longitude : undefined,
|
||||||
...drizzleBusinessListing,
|
// ...drizzleBusinessListing,
|
||||||
};
|
// };
|
||||||
Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
// Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
||||||
delete o.city;
|
// delete o.city;
|
||||||
delete o.state;
|
// delete o.state;
|
||||||
delete o.latitude;
|
// delete o.latitude;
|
||||||
delete o.longitude;
|
// delete o.longitude;
|
||||||
return unflattenObject(o);
|
// return unflattenObject(o);
|
||||||
}
|
// }
|
||||||
export function convertCommercialToDrizzleCommercial(commercialPropertyListing: Partial<CommercialPropertyListing>): DrizzleCommercialPropertyListing {
|
// export function convertCommercialToDrizzleCommercial(commercialPropertyListing: Partial<CommercialPropertyListing>): DrizzleCommercialPropertyListing {
|
||||||
const drizzleCommercialPropertyListing = flattenObject(commercialPropertyListing);
|
// const drizzleCommercialPropertyListing = flattenObject(commercialPropertyListing);
|
||||||
drizzleCommercialPropertyListing.city = drizzleCommercialPropertyListing.name;
|
// drizzleCommercialPropertyListing.city = drizzleCommercialPropertyListing.name;
|
||||||
delete drizzleCommercialPropertyListing.name;
|
// delete drizzleCommercialPropertyListing.name;
|
||||||
return drizzleCommercialPropertyListing;
|
// return drizzleCommercialPropertyListing;
|
||||||
}
|
// }
|
||||||
export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyListing: Partial<DrizzleCommercialPropertyListing>): CommercialPropertyListing {
|
// export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyListing: Partial<DrizzleCommercialPropertyListing>): CommercialPropertyListing {
|
||||||
const o = {
|
// const o = {
|
||||||
location: drizzleCommercialPropertyListing.city ? undefined : null,
|
// location: drizzleCommercialPropertyListing.city ? undefined : null,
|
||||||
location_name: drizzleCommercialPropertyListing.city ? drizzleCommercialPropertyListing.city : undefined,
|
// location_name: drizzleCommercialPropertyListing.city ? drizzleCommercialPropertyListing.city : undefined,
|
||||||
location_state: drizzleCommercialPropertyListing.state ? drizzleCommercialPropertyListing.state : undefined,
|
// location_state: drizzleCommercialPropertyListing.state ? drizzleCommercialPropertyListing.state : undefined,
|
||||||
location_latitude: drizzleCommercialPropertyListing.latitude ? drizzleCommercialPropertyListing.latitude : undefined,
|
// location_street: drizzleCommercialPropertyListing.street ? drizzleCommercialPropertyListing.street : undefined,
|
||||||
location_longitude: drizzleCommercialPropertyListing.longitude ? drizzleCommercialPropertyListing.longitude : undefined,
|
// location_housenumber: drizzleCommercialPropertyListing.housenumber ? drizzleCommercialPropertyListing.housenumber : undefined,
|
||||||
...drizzleCommercialPropertyListing,
|
// location_county: drizzleCommercialPropertyListing.county ? drizzleCommercialPropertyListing.county : undefined,
|
||||||
};
|
// location_zipCode: drizzleCommercialPropertyListing.zipCode ? drizzleCommercialPropertyListing.zipCode : undefined,
|
||||||
Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
// location_latitude: drizzleCommercialPropertyListing.latitude ? drizzleCommercialPropertyListing.latitude : undefined,
|
||||||
delete o.city;
|
// location_longitude: drizzleCommercialPropertyListing.longitude ? drizzleCommercialPropertyListing.longitude : undefined,
|
||||||
delete o.state;
|
// ...drizzleCommercialPropertyListing,
|
||||||
delete o.latitude;
|
// };
|
||||||
delete o.longitude;
|
// Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
||||||
return unflattenObject(o);
|
// delete o.city;
|
||||||
}
|
// delete o.state;
|
||||||
export function convertUserToDrizzleUser(user: Partial<User>): DrizzleUser {
|
// delete o.street;
|
||||||
const drizzleUser = flattenObject(user);
|
// delete o.housenumber;
|
||||||
drizzleUser.city = drizzleUser.name;
|
// delete o.county;
|
||||||
delete drizzleUser.name;
|
// delete o.zipCode;
|
||||||
return drizzleUser;
|
// delete o.latitude;
|
||||||
}
|
// delete o.longitude;
|
||||||
|
// return unflattenObject(o);
|
||||||
|
// }
|
||||||
|
// export function convertUserToDrizzleUser(user: Partial<User>): DrizzleUser {
|
||||||
|
// const drizzleUser = flattenObject(user);
|
||||||
|
// drizzleUser.city = drizzleUser.name;
|
||||||
|
// delete drizzleUser.name;
|
||||||
|
// return drizzleUser;
|
||||||
|
// }
|
||||||
|
|
||||||
export function convertDrizzleUserToUser(drizzleUser: Partial<DrizzleUser>): User {
|
// export function convertDrizzleUserToUser(drizzleUser: Partial<DrizzleUser>): User {
|
||||||
const o: any = {
|
// const o: any = {
|
||||||
companyLocation: drizzleUser.city ? undefined : null,
|
// companyLocation: drizzleUser.city ? undefined : null,
|
||||||
companyLocation_name: drizzleUser.city ? drizzleUser.city : undefined,
|
// companyLocation_name: drizzleUser.city ? drizzleUser.city : undefined,
|
||||||
companyLocation_state: drizzleUser.state ? drizzleUser.state : undefined,
|
// companyLocation_state: drizzleUser.state ? drizzleUser.state : undefined,
|
||||||
companyLocation_latitude: drizzleUser.latitude ? drizzleUser.latitude : undefined,
|
// companyLocation_latitude: drizzleUser.latitude ? drizzleUser.latitude : undefined,
|
||||||
companyLocation_longitude: drizzleUser.longitude ? drizzleUser.longitude : undefined,
|
// companyLocation_longitude: drizzleUser.longitude ? drizzleUser.longitude : undefined,
|
||||||
...drizzleUser,
|
// ...drizzleUser,
|
||||||
};
|
// };
|
||||||
Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
// Object.keys(o).forEach(key => (o[key] === undefined ? delete o[key] : {}));
|
||||||
delete o.city;
|
// delete o.city;
|
||||||
delete o.state;
|
// delete o.state;
|
||||||
delete o.latitude;
|
// delete o.latitude;
|
||||||
delete o.longitude;
|
// delete o.longitude;
|
||||||
|
|
||||||
return unflattenObject(o);
|
// return unflattenObject(o);
|
||||||
}
|
// }
|
||||||
function flattenObject(obj: any, res: any = {}): any {
|
// function flattenObject(obj: any, res: any = {}): any {
|
||||||
for (const key in obj) {
|
// for (const key in obj) {
|
||||||
if (obj.hasOwnProperty(key)) {
|
// if (obj.hasOwnProperty(key)) {
|
||||||
const value = obj[key];
|
// const value = obj[key];
|
||||||
|
|
||||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
// if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||||
if (value instanceof Date) {
|
// if (value instanceof Date) {
|
||||||
res[key] = value;
|
// res[key] = value;
|
||||||
} else {
|
// } else {
|
||||||
flattenObject(value, res);
|
// flattenObject(value, res);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
res[key] = value;
|
// res[key] = value;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return res;
|
// return res;
|
||||||
}
|
// }
|
||||||
function unflattenObject(obj: any, separator: string = '_'): any {
|
// function unflattenObject(obj: any, separator: string = '_'): any {
|
||||||
const result: any = {};
|
// const result: any = {};
|
||||||
|
|
||||||
for (const key in obj) {
|
// for (const key in obj) {
|
||||||
if (obj.hasOwnProperty(key)) {
|
// if (obj.hasOwnProperty(key)) {
|
||||||
const keys = key.split(separator);
|
// const keys = key.split(separator);
|
||||||
keys.reduce((acc, curr, idx) => {
|
// keys.reduce((acc, curr, idx) => {
|
||||||
if (idx === keys.length - 1) {
|
// if (idx === keys.length - 1) {
|
||||||
acc[curr] = obj[key];
|
// acc[curr] = obj[key];
|
||||||
} else {
|
// } else {
|
||||||
if (!acc[curr]) {
|
// if (!acc[curr]) {
|
||||||
acc[curr] = {};
|
// acc[curr] = {};
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return acc[curr];
|
// return acc[curr];
|
||||||
}, result);
|
// }, result);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return result;
|
// return result;
|
||||||
}
|
// }
|
||||||
export function splitName(fullName: string): { firstname: string; lastname: string } {
|
export function splitName(fullName: string): { firstname: string; lastname: string } {
|
||||||
const parts = fullName.trim().split(/\s+/); // Teile den Namen am Leerzeichen auf
|
const parts = fullName.trim().split(/\s+/); // Teile den Namen am Leerzeichen auf
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2021",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<div>
|
||||||
|
<label for="type" class="block text-sm font-bold text-gray-700 mb-1 relative w-fit {{ labelClasses }}"
|
||||||
|
>{{ label }} @if(validationMessage){
|
||||||
|
<div
|
||||||
|
attr.data-tooltip-target="tooltip-{{ name }}"
|
||||||
|
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||||
|
(click)="toggleTooltip($event)"
|
||||||
|
(touchstart)="toggleTooltip($event)"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</div>
|
||||||
|
<app-tooltip id="tooltip-{{ name }}" [text]="validationMessage" [isVisible]="isTooltipVisible"></app-tooltip>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
<ng-select
|
||||||
|
class="custom"
|
||||||
|
[multiple]="false"
|
||||||
|
[hideSelected]="true"
|
||||||
|
[trackByFn]="trackByFn"
|
||||||
|
[minTermLength]="2"
|
||||||
|
[loading]="placeLoading"
|
||||||
|
typeToSearchText="Please enter 2 or more characters"
|
||||||
|
[typeahead]="placeInput$"
|
||||||
|
ngModel="{{ formatGeoAddress(value) }}"
|
||||||
|
(ngModelChange)="onInputChange($event)"
|
||||||
|
>
|
||||||
|
@for (place of places$ | async; track place.place_id) {
|
||||||
|
<ng-option [value]="place">{{ formatPlaceAddress(place) }}</ng-option>
|
||||||
|
}
|
||||||
|
</ng-select>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
:host ::ng-deep .ng-select.custom .ng-select-container {
|
||||||
|
// --tw-bg-opacity: 1;
|
||||||
|
// background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||||
|
// height: 42px;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
.ng-value-container .ng-input {
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { Component, forwardRef, Input } from '@angular/core';
|
||||||
|
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
|
import { catchError, concat, debounceTime, distinctUntilChanged, Observable, of, Subject, switchMap, tap } from 'rxjs';
|
||||||
|
import { GeoResult } from '../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { Place } from '../../../../../bizmatch-server/src/models/server.model';
|
||||||
|
import { GeoService } from '../../services/geo.service';
|
||||||
|
import { SelectOptionsService } from '../../services/select-options.service';
|
||||||
|
import { BaseInputComponent } from '../base-input/base-input.component';
|
||||||
|
import { TooltipComponent } from '../tooltip/tooltip.component';
|
||||||
|
import { ValidationMessagesService } from '../validation-messages.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-validated-location',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, FormsModule, NgSelectModule, TooltipComponent],
|
||||||
|
templateUrl: './validated-location.component.html',
|
||||||
|
styleUrl: './validated-location.component.scss',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => ValidatedLocationComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ValidatedLocationComponent extends BaseInputComponent {
|
||||||
|
@Input() items;
|
||||||
|
@Input() labelClasses: string;
|
||||||
|
places$: Observable<Place[]>;
|
||||||
|
placeInput$ = new Subject<string>();
|
||||||
|
placeLoading = false;
|
||||||
|
constructor(validationMessagesService: ValidationMessagesService, private geoService: GeoService, public selectOptions: SelectOptionsService) {
|
||||||
|
super(validationMessagesService);
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnInit() {
|
||||||
|
super.ngOnInit();
|
||||||
|
this.loadCities();
|
||||||
|
}
|
||||||
|
onInputChange(event: Place): void {
|
||||||
|
this.value = event; //{ ...event, longitude: parseFloat(event.longitude), latitude: parseFloat(event.latitude) };
|
||||||
|
this.value = {
|
||||||
|
id: event?.place_id,
|
||||||
|
name: event?.address.city,
|
||||||
|
county: event?.address.county,
|
||||||
|
street: event?.address.road,
|
||||||
|
housenumber: event?.address.house_number,
|
||||||
|
state: event?.address['ISO3166-2-lvl4'].substr(3),
|
||||||
|
latitude: event ? parseFloat(event?.lat) : undefined,
|
||||||
|
longitude: event ? parseFloat(event?.lon) : undefined,
|
||||||
|
};
|
||||||
|
this.onChange(this.value);
|
||||||
|
}
|
||||||
|
private loadCities() {
|
||||||
|
this.places$ = concat(
|
||||||
|
of([]), // default items
|
||||||
|
this.placeInput$.pipe(
|
||||||
|
debounceTime(300),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
tap(() => (this.placeLoading = true)),
|
||||||
|
switchMap(term =>
|
||||||
|
this.geoService.findLocationStartingWith(term).pipe(
|
||||||
|
catchError(() => of([])), // empty list on error
|
||||||
|
// map(cities => cities.map(city => city.city)), // transform the list of objects to a list of city names
|
||||||
|
tap(() => (this.placeLoading = false)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
trackByFn(item: GeoResult) {
|
||||||
|
return item.id;
|
||||||
|
}
|
||||||
|
compareFn = (item, selected) => {
|
||||||
|
return item.id === selected.id;
|
||||||
|
};
|
||||||
|
formatGeoAddress(geoResult: GeoResult | null | undefined): string {
|
||||||
|
// Überprüfen, ob geoResult null oder undefined ist
|
||||||
|
if (!geoResult) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let addressParts: string[] = [];
|
||||||
|
|
||||||
|
// Füge Hausnummer hinzu, wenn vorhanden
|
||||||
|
if (geoResult.housenumber) {
|
||||||
|
addressParts.push(geoResult.housenumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge Straße hinzu, wenn vorhanden
|
||||||
|
if (geoResult.street) {
|
||||||
|
addressParts.push(geoResult.street);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kombiniere Hausnummer und Straße
|
||||||
|
let address = addressParts.join(' ');
|
||||||
|
|
||||||
|
// Füge Namen hinzu, wenn vorhanden
|
||||||
|
if (geoResult.name) {
|
||||||
|
address = address ? `${address}, ${geoResult.name}` : geoResult.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge County hinzu, wenn vorhanden
|
||||||
|
if (geoResult.county) {
|
||||||
|
address = address ? `${address}, ${geoResult.county}` : geoResult.county;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge Bundesland hinzu, wenn vorhanden
|
||||||
|
if (geoResult.state) {
|
||||||
|
address = address ? `${address} - ${geoResult.state}` : geoResult.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
formatPlaceAddress(place: Place | null | undefined): string {
|
||||||
|
// Überprüfen, ob place null oder undefined ist
|
||||||
|
if (!place) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { house_number, road, city, county, state } = place.address;
|
||||||
|
|
||||||
|
let addressParts: string[] = [];
|
||||||
|
|
||||||
|
// Füge Hausnummer hinzu, wenn vorhanden
|
||||||
|
if (house_number) {
|
||||||
|
addressParts.push(house_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge Straße hinzu, wenn vorhanden
|
||||||
|
if (road) {
|
||||||
|
addressParts.push(road);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kombiniere Hausnummer und Straße
|
||||||
|
let address = addressParts.join(' ');
|
||||||
|
|
||||||
|
// Füge Stadt hinzu, wenn vorhanden
|
||||||
|
if (city) {
|
||||||
|
address = address ? `${address}, ${city}` : city;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge County hinzu, wenn vorhanden
|
||||||
|
if (county) {
|
||||||
|
address = address ? `${address}, ${county}` : county;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Füge Bundesland hinzu, wenn vorhanden
|
||||||
|
if (state) {
|
||||||
|
address = address ? `${address} - ${state}` : state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||||
<span class="font-semibold w-40 p-2">Company Location</span>
|
<span class="font-semibold w-40 p-2">Company Location</span>
|
||||||
<span class="p-2 flex-grow">{{ user.companyLocation?.name }} - {{ user.companyLocation?.state }}</span>
|
<span class="p-2 flex-grow">{{ user.location?.name }} - {{ user.location?.state }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center bg-gray-100">
|
<div class="flex flex-col sm:flex-row sm:items-center bg-gray-100">
|
||||||
<span class="font-semibold w-40 p-2">Professional Type</span>
|
<span class="font-semibold w-40 p-2">Professional Type</span>
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,8 @@
|
||||||
<app-validated-input label="Your Phone Number" name="phoneNumber" [(ngModel)]="user.phoneNumber" mask="(000) 000-0000"></app-validated-input>
|
<app-validated-input label="Your Phone Number" name="phoneNumber" [(ngModel)]="user.phoneNumber" mask="(000) 000-0000"></app-validated-input>
|
||||||
<app-validated-input label="Company Website" name="companyWebsite" [(ngModel)]="user.companyWebsite"></app-validated-input>
|
<app-validated-input label="Company Website" name="companyWebsite" [(ngModel)]="user.companyWebsite"></app-validated-input>
|
||||||
<!-- <app-validated-input label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-input> -->
|
<!-- <app-validated-input label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-input> -->
|
||||||
<app-validated-city label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-city>
|
<!-- <app-validated-city label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-city> -->
|
||||||
|
<app-validated-location label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-location>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { TooltipComponent } from '../../../components/tooltip/tooltip.component'
|
||||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||||
import { ValidatedCountyComponent } from '../../../components/validated-county/validated-county.component';
|
import { ValidatedCountyComponent } from '../../../components/validated-county/validated-county.component';
|
||||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||||
|
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||||
import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component';
|
import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component';
|
||||||
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
import { ValidationMessagesService } from '../../../components/validation-messages.service';
|
||||||
|
|
@ -52,6 +53,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
ValidatedCityComponent,
|
ValidatedCityComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
ValidatedCountyComponent,
|
ValidatedCountyComponent,
|
||||||
|
ValidatedLocationComponent,
|
||||||
],
|
],
|
||||||
providers: [TitleCasePipe, DatePipe],
|
providers: [TitleCasePipe, DatePipe],
|
||||||
templateUrl: './account.component.html',
|
templateUrl: './account.component.html',
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<!-- <app-validated-ng-select label="State" name="state" [(ngModel)]="listing.location.state" [items]="selectOptions?.states"></app-validated-ng-select>
|
<!-- <app-validated-ng-select label="State" name="state" [(ngModel)]="listing.location.state" [items]="selectOptions?.states"></app-validated-ng-select>
|
||||||
<app-validated-input label="City" name="city" [(ngModel)]="listing.location.city"></app-validated-input> -->
|
<app-validated-input label="City" name="city" [(ngModel)]="listing.location.city"></app-validated-input> -->
|
||||||
<app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city>
|
<!-- <app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city> -->
|
||||||
|
<app-validated-location label="Location" name="location" [(ngModel)]="listing.location"></app-validated-location>
|
||||||
<app-validated-price label="Price" name="price" [(ngModel)]="listing.price"></app-validated-price>
|
<app-validated-price label="Price" name="price" [(ngModel)]="listing.price"></app-validated-price>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import { environment } from '../../../../environments/environment';
|
||||||
import { MessageService } from '../../../components/message/message.service';
|
import { MessageService } from '../../../components/message/message.service';
|
||||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||||
|
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||||
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
||||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||||
|
|
@ -47,6 +48,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
ValidatedPriceComponent,
|
ValidatedPriceComponent,
|
||||||
ValidatedTextareaComponent,
|
ValidatedTextareaComponent,
|
||||||
ValidatedCityComponent,
|
ValidatedCityComponent,
|
||||||
|
ValidatedLocationComponent,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
templateUrl: './edit-business-listing.component.html',
|
templateUrl: './edit-business-listing.component.html',
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<app-validated-ng-select label="Property Category" name="type" [(ngModel)]="listing.type" [items]="typesOfCommercialProperty"></app-validated-ng-select>
|
<app-validated-ng-select label="Property Category" name="type" [(ngModel)]="listing.type" [items]="typesOfCommercialProperty"></app-validated-ng-select>
|
||||||
<app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city>
|
<!-- <app-validated-city label="Location" name="location" [(ngModel)]="listing.location"></app-validated-city> -->
|
||||||
|
<app-validated-location label="Location" name="location" [(ngModel)]="listing.location"></app-validated-location>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div class="flex mb-4 space-x-4">
|
<!-- <div class="flex mb-4 space-x-4">
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { ImageCropAndUploadComponent, UploadReponse } from '../../../components/
|
||||||
import { MessageService } from '../../../components/message/message.service';
|
import { MessageService } from '../../../components/message/message.service';
|
||||||
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
import { ValidatedCityComponent } from '../../../components/validated-city/validated-city.component';
|
||||||
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component';
|
||||||
|
import { ValidatedLocationComponent } from '../../../components/validated-location/validated-location.component';
|
||||||
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
import { ValidatedNgSelectComponent } from '../../../components/validated-ng-select/validated-ng-select.component';
|
||||||
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
import { ValidatedPriceComponent } from '../../../components/validated-price/validated-price.component';
|
||||||
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
|
||||||
|
|
@ -52,6 +53,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
ValidatedQuillComponent,
|
ValidatedQuillComponent,
|
||||||
ValidatedNgSelectComponent,
|
ValidatedNgSelectComponent,
|
||||||
ValidatedPriceComponent,
|
ValidatedPriceComponent,
|
||||||
|
ValidatedLocationComponent,
|
||||||
ValidatedCityComponent,
|
ValidatedCityComponent,
|
||||||
ImageCropAndUploadComponent,
|
ImageCropAndUploadComponent,
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { CityAndStateResult, CountyResult, GeoResult } from '../../../../bizmatch-server/src/models/main.model';
|
import { CityAndStateResult, CountyResult, GeoResult } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { Place } from '../../../../bizmatch-server/src/models/server.model';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|
@ -9,6 +10,7 @@ import { environment } from '../../environments/environment';
|
||||||
})
|
})
|
||||||
export class GeoService {
|
export class GeoService {
|
||||||
private apiBaseUrl = environment.apiBaseUrl;
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
|
private baseUrl: string = 'https://nominatim.openstreetmap.org/search';
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
findCitiesStartingWith(prefix: string, state?: string): Observable<GeoResult[]> {
|
findCitiesStartingWith(prefix: string, state?: string): Observable<GeoResult[]> {
|
||||||
|
|
@ -21,4 +23,8 @@ export class GeoService {
|
||||||
findCountiesStartingWith(prefix: string, states?: string[]): Observable<CountyResult[]> {
|
findCountiesStartingWith(prefix: string, states?: string[]): Observable<CountyResult[]> {
|
||||||
return this.http.post<CountyResult[]>(`${this.apiBaseUrl}/bizmatch/geo/counties`, { prefix, states });
|
return this.http.post<CountyResult[]>(`${this.apiBaseUrl}/bizmatch/geo/counties`, { prefix, states });
|
||||||
}
|
}
|
||||||
|
findLocationStartingWith(prefix: string): Observable<Place[]> {
|
||||||
|
let headers = new HttpHeaders().set('X-Hide-Loading', 'true').set('Accept-Language', 'en-US');
|
||||||
|
return this.http.get(`${this.baseUrl}?q=${prefix},US&format=json&addressdetails=1&limit=5`, { headers }) as Observable<Place[]>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||||
import { BehaviorSubject, lastValueFrom, Observable } from 'rxjs';
|
import { BehaviorSubject, lastValueFrom, Observable } from 'rxjs';
|
||||||
import urlcat from 'urlcat';
|
import urlcat from 'urlcat';
|
||||||
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
import { User } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
import { ResponseUsersArray, StatesResult, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
import { ResponseUsersArray, UserListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|
@ -46,7 +46,7 @@ export class UserService {
|
||||||
getNumberOfBroker(criteria?: UserListingCriteria): Observable<number> {
|
getNumberOfBroker(criteria?: UserListingCriteria): Observable<number> {
|
||||||
return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/user/findTotal`, criteria);
|
return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/user/findTotal`, criteria);
|
||||||
}
|
}
|
||||||
async getAllStates(): Promise<any> {
|
// async getAllStates(): Promise<any> {
|
||||||
return await lastValueFrom(this.http.get<StatesResult[]>(`${this.apiBaseUrl}/bizmatch/user/states/all`));
|
// return await lastValueFrom(this.http.get<StatesResult[]>(`${this.apiBaseUrl}/bizmatch/user/states/all`));
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ export function resetUserListingCriteria(criteria: UserListingCriteria) {
|
||||||
|
|
||||||
export function createMailInfo(user: User): MailInfo {
|
export function createMailInfo(user: User): MailInfo {
|
||||||
return {
|
return {
|
||||||
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.companyLocation?.state, comments: null },
|
sender: { name: `${user.firstname} ${user.lastname}`, email: user.email, phoneNumber: user.phoneNumber, state: user.location?.state, comments: null },
|
||||||
email: null,
|
email: null,
|
||||||
url: environment.mailinfoUrl,
|
url: environment.mailinfoUrl,
|
||||||
listing: null,
|
listing: null,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue