From 1e1d5cea57debd250d112963248921fe7842ab77 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Fri, 9 Aug 2024 17:59:49 +0200 Subject: [PATCH] =?UTF-8?q?=C3=9Cberarbeitung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bizmatch-server/.gitignore | 5 ++++ bizmatch-server/src/drizzle/import.ts | 6 ++-- bizmatch-server/src/geo/geo.service.ts | 20 ++++++------- .../src/listings/business-listing.service.ts | 28 +++++++++++-------- .../listings/commercial-property.service.ts | 28 +++++++++++-------- bizmatch-server/src/models/db.model.ts | 2 +- bizmatch-server/src/models/main.model.ts | 20 ++++++++----- bizmatch-server/src/user/user.service.ts | 16 ++++++----- bizmatch-server/src/utils.ts | 21 ++++++++++---- .../search-modal/search-modal.component.html | 27 +++++++++++------- .../search-modal/search-modal.component.ts | 5 ++-- .../validated-city.component.html | 6 ++-- .../validated-city.component.scss | 2 +- .../validated-city.component.ts | 4 ++- .../details-business-listing.component.ts | 2 +- ...s-commercial-property-listing.component.ts | 2 +- .../details-user/details-user.component.html | 2 +- .../src/app/pages/home/home.component.html | 4 +-- bizmatch/src/app/pages/home/home.component.ts | 23 +++------------ .../business-listings.component.html | 2 +- ...ommercial-property-listings.component.html | 2 +- .../account/account.component.html | 2 +- .../subscription/account/account.component.ts | 2 +- .../edit-business-listing.component.ts | 2 +- ...t-commercial-property-listing.component.ts | 2 +- .../my-listing/my-listing.component.html | 2 +- bizmatch/src/app/utils/utils.ts | 10 +++---- 27 files changed, 135 insertions(+), 112 deletions(-) diff --git a/bizmatch-server/.gitignore b/bizmatch-server/.gitignore index 563139f..8bc5360 100644 --- a/bizmatch-server/.gitignore +++ b/bizmatch-server/.gitignore @@ -60,3 +60,8 @@ pictures_base src/*.js bun.lockb + +#drizzle migrations +src/drizzle/migrations + +importlog.txt \ No newline at end of file diff --git a/bizmatch-server/src/drizzle/import.ts b/bizmatch-server/src/drizzle/import.ts index 80932a5..20f8d30 100644 --- a/bizmatch-server/src/drizzle/import.ts +++ b/bizmatch-server/src/drizzle/import.ts @@ -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}`); diff --git a/bizmatch-server/src/geo/geo.service.ts b/bizmatch-server/src/geo/geo.service.ts index bfdce45..0a90c2c 100644 --- a/bizmatch-server/src/geo/geo.service.ts +++ b/bizmatch-server/src/geo/geo.service.ts @@ -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 { + const results: Array = []; 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 { diff --git a/bizmatch-server/src/listings/business-listing.service.ts b/bizmatch-server/src/listings/business-listing.service.ts index c96163c..d075b21 100644 --- a/bizmatch-server/src/listings/business-listing.service.ts +++ b/bizmatch-server/src/listings/business-listing.service.ts @@ -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; } diff --git a/bizmatch-server/src/listings/commercial-property.service.ts b/bizmatch-server/src/listings/commercial-property.service.ts index 3b05146..7399d4b 100644 --- a/bizmatch-server/src/listings/commercial-property.service.ts +++ b/bizmatch-server/src/listings/commercial-property.service.ts @@ -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; } diff --git a/bizmatch-server/src/models/db.model.ts b/bizmatch-server/src/models/db.model.ts index 9c39cb8..0b716b5 100644 --- a/bizmatch-server/src/models/db.model.ts +++ b/bizmatch-server/src/models/db.model.ts @@ -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.', }), diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index e4d332c..0d91bf2 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -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; diff --git a/bizmatch-server/src/user/user.service.ts b/bizmatch-server/src/user/user.service.ts index 8b0d974..cc876f0 100644 --- a/bizmatch-server/src/user/user.service.ts +++ b/bizmatch-server/src/user/user.service.ts @@ -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; } diff --git a/bizmatch-server/src/utils.ts b/bizmatch-server/src/utils.ts index d25832a..e9a3cce 100644 --- a/bizmatch-server/src/utils.ts +++ b/bizmatch-server/src/utils.ts @@ -33,11 +33,14 @@ type DrizzleUser = typeof users.$inferSelect; type DrizzleBusinessListing = typeof businesses.$inferSelect; type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect; export function convertBusinessToDrizzleBusiness(businessListing: Partial): DrizzleBusinessListing { - return flattenObject(businessListing); + const drizzleBusinessListing = flattenObject(businessListing); + drizzleBusinessListing.city = drizzleBusinessListing.name; + delete drizzleBusinessListing.name; + return drizzleBusinessListing; } export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial): 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): DrizzleCommercialPropertyListing { - return flattenObject(commercialPropertyListing); + const drizzleCommercialPropertyListing = flattenObject(commercialPropertyListing); + drizzleCommercialPropertyListing.city = drizzleCommercialPropertyListing.name; + delete drizzleCommercialPropertyListing.name; + return drizzleCommercialPropertyListing; } export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyListing: Partial): 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): DrizzleUser { - return flattenObject(user); + const drizzleUser = flattenObject(user); + drizzleUser.city = drizzleUser.name; + delete drizzleUser.name; + return drizzleUser; } export function convertDrizzleUserToUser(drizzleUser: Partial): User { const o = { - companyLocation_city: drizzleUser.city, + companyLocation_name: drizzleUser.city, companyLocation_state: drizzleUser.state, companyLocation_latitude: drizzleUser.latitude, companyLocation_longitude: drizzleUser.longitude, diff --git a/bizmatch/src/app/components/search-modal/search-modal.component.html b/bizmatch/src/app/components/search-modal/search-modal.component.html index 1ea9f1e..d92f328 100644 --- a/bizmatch/src/app/components/search-modal/search-modal.component.html +++ b/bizmatch/src/app/components/search-modal/search-modal.component.html @@ -2,7 +2,13 @@
+ @if(criteria.criteriaType==='businessListings'){

Business Listing Search

+ } @else if (criteria.criteriaType==='commercialPropertyListings'){ +

Property Listing Search

+ } @else { +

Professional Listing Search

+ }

{{ listing.title }}

-

{{ listing.location.city }}

+

{{ listing.location.name }}

{{ listing.price | currency }}

--> - +
diff --git a/bizmatch/src/app/pages/subscription/account/account.component.ts b/bizmatch/src/app/pages/subscription/account/account.component.ts index 66737be..6b1f0d6 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.ts +++ b/bizmatch/src/app/pages/subscription/account/account.component.ts @@ -169,7 +169,7 @@ export class AccountComponent { async search(event: AutoCompleteCompleteEvent) { const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query)); - this.suggestions = result.map(r => `${r.city} - ${r.state}`).slice(0, 5); + this.suggestions = result.map(r => `${r.name} - ${r.state}`).slice(0, 5); } addLicence() { this.user.licensedIn.push({ registerNo: '', state: '' }); diff --git a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts index d7fcdde..e38c2f7 100644 --- a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts @@ -140,7 +140,7 @@ export class EditBusinessListingComponent { async search(event: AutoCompleteCompleteEvent) { const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query)); - this.suggestions = result.map(r => r.city).slice(0, 5); + this.suggestions = result.map(r => r.name).slice(0, 5); } changeListingCategory(value: 'business' | 'commercialProperty') { diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts index 72aba13..812da23 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts @@ -177,7 +177,7 @@ export class EditCommercialPropertyListingComponent { async search(event: AutoCompleteCompleteEvent) { const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query)); - this.suggestions = result.map(r => r.city).slice(0, 5); + this.suggestions = result.map(r => r.name).slice(0, 5); } openFileDialog() { this.fileInput.nativeElement.click(); diff --git a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html index 9606224..c7b3939 100644 --- a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html +++ b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html @@ -52,7 +52,7 @@

{{ listing.title }}

Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}

-

Located in: {{ listing.location.city }} - {{ listing.location.state }}

+

Located in: {{ listing.location.name }} - {{ listing.location.state }}