Überarbeitung
This commit is contained in:
5
bizmatch-server/.gitignore
vendored
5
bizmatch-server/.gitignore
vendored
@@ -60,3 +60,8 @@ pictures_base
|
||||
|
||||
src/*.js
|
||||
bun.lockb
|
||||
|
||||
#drizzle migrations
|
||||
src/drizzle/migrations
|
||||
|
||||
importlog.txt
|
||||
@@ -137,7 +137,7 @@ for (let index = 0; index < usersData.length; index++) {
|
||||
user.companyWebsite = userData.companyWebsite;
|
||||
const [city, state] = userData.companyLocation.split('-').map(e => e.trim());
|
||||
user.companyLocation = {};
|
||||
user.companyLocation.city = city;
|
||||
user.companyLocation.name = city;
|
||||
user.companyLocation.state = state;
|
||||
const cityGeo = geos.states.find(s => s.state_code === state).cities.find(c => c.name === city);
|
||||
user.companyLocation.latitude = cityGeo.latitude;
|
||||
@@ -188,7 +188,7 @@ for (let index = 0; index < commercialJsonData.length; index++) {
|
||||
commercial.location = {};
|
||||
commercial.location.latitude = cityGeo.latitude;
|
||||
commercial.location.longitude = cityGeo.longitude;
|
||||
commercial.location.city = commercialJsonData[index].city;
|
||||
commercial.location.name = commercialJsonData[index].city;
|
||||
commercial.location.state = commercialJsonData[index].state;
|
||||
// console.log(JSON.stringify(commercial.location));
|
||||
} catch (e) {
|
||||
@@ -229,7 +229,7 @@ for (let index = 0; index < businessJsonData.length; index++) {
|
||||
business.location = {};
|
||||
business.location.latitude = cityGeo.latitude;
|
||||
business.location.longitude = cityGeo.longitude;
|
||||
business.location.city = businessJsonData[index].city;
|
||||
business.location.name = businessJsonData[index].city;
|
||||
business.location.state = businessJsonData[index].state;
|
||||
} catch (e) {
|
||||
console.log(`----------------> ERROR ${businessJsonData[index].state} - ${businessJsonData[index].city}`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { readFileSync } from 'fs';
|
||||
import path, { join } from 'path';
|
||||
import { CountyResult, GeoResult } from 'src/models/main.model.js';
|
||||
import { CityAndStateResult, CountyResult, GeoResult } from 'src/models/main.model.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { City, CountyData, Geo, State } from '../models/server.model.js';
|
||||
|
||||
@@ -52,7 +52,7 @@ export class GeoService {
|
||||
if (city.name.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||
result.push({
|
||||
id: city.id,
|
||||
city: city.name,
|
||||
name: city.name,
|
||||
state: state.state_code,
|
||||
//state_code: state.state_code,
|
||||
latitude: city.latitude,
|
||||
@@ -63,8 +63,8 @@ export class GeoService {
|
||||
});
|
||||
return state ? result.filter(e => e.state.toLowerCase() === state.toLowerCase()) : result;
|
||||
}
|
||||
findCitiesAndStatesStartingWith(prefix: string, state?: string): Array<{ id: string; name: string; type: 'city' | 'state'; state: string }> {
|
||||
const results: Array<{ id: string; name: string; type: 'city' | 'state'; state: string }> = [];
|
||||
findCitiesAndStatesStartingWith(prefix: string, state?: string): Array<CityAndStateResult> {
|
||||
const results: Array<CityAndStateResult> = [];
|
||||
|
||||
const lowercasePrefix = prefix.toLowerCase();
|
||||
|
||||
@@ -73,10 +73,9 @@ export class GeoService {
|
||||
for (const state of this.geo.states) {
|
||||
if (state.name.toLowerCase().startsWith(lowercasePrefix)) {
|
||||
results.push({
|
||||
id: state.id.toString(),
|
||||
name: state.name,
|
||||
id: state.id,
|
||||
type: 'state',
|
||||
state: state.state_code,
|
||||
content: state,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,10 +83,9 @@ export class GeoService {
|
||||
for (const city of state.cities) {
|
||||
if (city.name.toLowerCase().startsWith(lowercasePrefix)) {
|
||||
results.push({
|
||||
id: city.id.toString(),
|
||||
name: city.name,
|
||||
id: city.id,
|
||||
type: 'city',
|
||||
state: state.state_code,
|
||||
content: { state: state.state_code, ...city },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -97,7 +95,7 @@ export class GeoService {
|
||||
return results.sort((a, b) => {
|
||||
if (a.type === 'state' && b.type === 'city') return -1;
|
||||
if (a.type === 'city' && b.type === 'state') return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
return a.content.name.localeCompare(b.content.name);
|
||||
});
|
||||
}
|
||||
getCityWithCoords(state: string, city: string): City {
|
||||
|
||||
@@ -25,10 +25,10 @@ export class BusinessListingService {
|
||||
const whereConditions: SQL[] = [];
|
||||
|
||||
if (criteria.city && criteria.searchType === 'exact') {
|
||||
whereConditions.push(ilike(businesses.city, `%${criteria.city}%`));
|
||||
whereConditions.push(ilike(businesses.city, `%${criteria.city.name}%`));
|
||||
}
|
||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city);
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||
whereConditions.push(sql`${getDistanceQuery(businesses, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||
}
|
||||
if (criteria.types && criteria.types.length > 0) {
|
||||
@@ -180,11 +180,13 @@ export class BusinessListingService {
|
||||
return convertDrizzleBusinessToBusiness(createdListing);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
field: err.path.join('.'),
|
||||
message: err.message,
|
||||
}));
|
||||
throw new BadRequestException(formattedErrors);
|
||||
const filteredErrors = error.errors
|
||||
.map(item => ({
|
||||
...item,
|
||||
field: item.path[0],
|
||||
}))
|
||||
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||
throw new BadRequestException(filteredErrors);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -200,11 +202,13 @@ export class BusinessListingService {
|
||||
return convertDrizzleBusinessToBusiness(updateListing);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
field: err.path.join('.'),
|
||||
message: err.message,
|
||||
}));
|
||||
throw new BadRequestException(formattedErrors);
|
||||
const filteredErrors = error.errors
|
||||
.map(item => ({
|
||||
...item,
|
||||
field: item.path[0],
|
||||
}))
|
||||
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||
throw new BadRequestException(filteredErrors);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ export class CommercialPropertyService {
|
||||
const whereConditions: SQL[] = [];
|
||||
|
||||
if (criteria.city && criteria.searchType === 'exact') {
|
||||
whereConditions.push(ilike(schema.commercials.city, `%${criteria.city}%`));
|
||||
whereConditions.push(ilike(schema.commercials.city, `%${criteria.city.name}%`));
|
||||
}
|
||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city);
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||
whereConditions.push(sql`${getDistanceQuery(commercials, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||
}
|
||||
if (criteria.types && criteria.types.length > 0) {
|
||||
@@ -131,11 +131,13 @@ export class CommercialPropertyService {
|
||||
return convertDrizzleCommercialToCommercial(createdListing);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
field: err.path.join('.'),
|
||||
message: err.message,
|
||||
}));
|
||||
throw new BadRequestException(formattedErrors);
|
||||
const filteredErrors = error.errors
|
||||
.map(item => ({
|
||||
...item,
|
||||
field: item.path[0],
|
||||
}))
|
||||
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||
throw new BadRequestException(filteredErrors);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -157,11 +159,13 @@ export class CommercialPropertyService {
|
||||
return convertDrizzleCommercialToCommercial(updateListing);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
field: err.path.join('.'),
|
||||
message: err.message,
|
||||
}));
|
||||
throw new BadRequestException(formattedErrors);
|
||||
const filteredErrors = error.errors
|
||||
.map(item => ({
|
||||
...item,
|
||||
field: item.path[0],
|
||||
}))
|
||||
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||
throw new BadRequestException(filteredErrors);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export const LicensedInSchema = z.object({
|
||||
state: z.string().nonempty('State is required'),
|
||||
});
|
||||
export const GeoSchema = z.object({
|
||||
city: z.string(),
|
||||
name: z.string(),
|
||||
state: z.string().refine(val => USStates.safeParse(val).success, {
|
||||
message: 'Invalid state. Must be a valid 2-letter US state code.',
|
||||
}),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BusinessListing, CommercialPropertyListing, Sender, User } from './db.model.js';
|
||||
import { State } from './server.model.js';
|
||||
|
||||
export interface StatesResult {
|
||||
state: string;
|
||||
@@ -59,7 +60,7 @@ export interface ListCriteria {
|
||||
page: number;
|
||||
types: string[];
|
||||
state: string;
|
||||
city: string;
|
||||
city: GeoResult;
|
||||
prompt: string;
|
||||
searchType: 'exact' | 'radius';
|
||||
// radius: '5' | '20' | '50' | '100' | '200' | '300' | '400' | '500';
|
||||
@@ -224,18 +225,23 @@ export interface UploadParams {
|
||||
}
|
||||
export interface GeoResult {
|
||||
id: number;
|
||||
city: string;
|
||||
name: string;
|
||||
state: string;
|
||||
// state_code: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
||||
export interface CityAndStateResult {
|
||||
interface CityResult {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
state: string;
|
||||
type: 'city';
|
||||
content: GeoResult;
|
||||
}
|
||||
|
||||
interface StateResult {
|
||||
id: number;
|
||||
type: 'state';
|
||||
content: State;
|
||||
}
|
||||
export type CityAndStateResult = CityResult | StateResult;
|
||||
export interface CountyResult {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@@ -26,10 +26,10 @@ export class UserService {
|
||||
const whereConditions: SQL[] = [];
|
||||
whereConditions.push(eq(schema.users.customerType, 'professional'));
|
||||
if (criteria.city && criteria.searchType === 'exact') {
|
||||
whereConditions.push(ilike(schema.users.city, `%${criteria.city}%`));
|
||||
whereConditions.push(ilike(schema.users.city, `%${criteria.city.name}%`));
|
||||
}
|
||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city);
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||
whereConditions.push(sql`${getDistanceQuery(schema.users, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||
}
|
||||
if (criteria.types && criteria.types.length > 0) {
|
||||
@@ -139,11 +139,13 @@ export class UserService {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const formattedErrors = error.errors.map(err => ({
|
||||
field: err.path.join('.'),
|
||||
message: err.message,
|
||||
}));
|
||||
throw new BadRequestException(formattedErrors);
|
||||
const filteredErrors = error.errors
|
||||
.map(item => ({
|
||||
...item,
|
||||
field: item.path[0],
|
||||
}))
|
||||
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||
throw new BadRequestException(filteredErrors);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,14 @@ type DrizzleUser = typeof users.$inferSelect;
|
||||
type DrizzleBusinessListing = typeof businesses.$inferSelect;
|
||||
type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect;
|
||||
export function convertBusinessToDrizzleBusiness(businessListing: Partial<BusinessListing>): DrizzleBusinessListing {
|
||||
return flattenObject(businessListing);
|
||||
const drizzleBusinessListing = flattenObject(businessListing);
|
||||
drizzleBusinessListing.city = drizzleBusinessListing.name;
|
||||
delete drizzleBusinessListing.name;
|
||||
return drizzleBusinessListing;
|
||||
}
|
||||
export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial<DrizzleBusinessListing>): BusinessListing {
|
||||
const o = {
|
||||
location_city: drizzleBusinessListing.city,
|
||||
location_name: drizzleBusinessListing.city,
|
||||
location_state: drizzleBusinessListing.state,
|
||||
location_latitude: drizzleBusinessListing.latitude,
|
||||
location_longitude: drizzleBusinessListing.longitude,
|
||||
@@ -50,11 +53,14 @@ export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial
|
||||
return unflattenObject(o);
|
||||
}
|
||||
export function convertCommercialToDrizzleCommercial(commercialPropertyListing: Partial<CommercialPropertyListing>): DrizzleCommercialPropertyListing {
|
||||
return flattenObject(commercialPropertyListing);
|
||||
const drizzleCommercialPropertyListing = flattenObject(commercialPropertyListing);
|
||||
drizzleCommercialPropertyListing.city = drizzleCommercialPropertyListing.name;
|
||||
delete drizzleCommercialPropertyListing.name;
|
||||
return drizzleCommercialPropertyListing;
|
||||
}
|
||||
export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyListing: Partial<DrizzleCommercialPropertyListing>): CommercialPropertyListing {
|
||||
const o = {
|
||||
location_city: drizzleCommercialPropertyListing.city,
|
||||
location_name: drizzleCommercialPropertyListing.city,
|
||||
location_state: drizzleCommercialPropertyListing.state,
|
||||
location_latitude: drizzleCommercialPropertyListing.latitude,
|
||||
location_longitude: drizzleCommercialPropertyListing.longitude,
|
||||
@@ -67,12 +73,15 @@ export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyLi
|
||||
return unflattenObject(o);
|
||||
}
|
||||
export function convertUserToDrizzleUser(user: Partial<User>): DrizzleUser {
|
||||
return flattenObject(user);
|
||||
const drizzleUser = flattenObject(user);
|
||||
drizzleUser.city = drizzleUser.name;
|
||||
delete drizzleUser.name;
|
||||
return drizzleUser;
|
||||
}
|
||||
|
||||
export function convertDrizzleUserToUser(drizzleUser: Partial<DrizzleUser>): User {
|
||||
const o = {
|
||||
companyLocation_city: drizzleUser.city,
|
||||
companyLocation_name: drizzleUser.city,
|
||||
companyLocation_state: drizzleUser.state,
|
||||
companyLocation_latitude: drizzleUser.latitude,
|
||||
companyLocation_longitude: drizzleUser.longitude,
|
||||
|
||||
Reference in New Issue
Block a user