Überarbeitung
This commit is contained in:
parent
6d1c50d5df
commit
1e1d5cea57
|
|
@ -60,3 +60,8 @@ pictures_base
|
||||||
|
|
||||||
src/*.js
|
src/*.js
|
||||||
bun.lockb
|
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;
|
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.companyLocation = {};
|
||||||
user.companyLocation.city = city;
|
user.companyLocation.name = city;
|
||||||
user.companyLocation.state = state;
|
user.companyLocation.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.companyLocation.latitude = cityGeo.latitude;
|
||||||
|
|
@ -188,7 +188,7 @@ for (let index = 0; index < commercialJsonData.length; index++) {
|
||||||
commercial.location = {};
|
commercial.location = {};
|
||||||
commercial.location.latitude = cityGeo.latitude;
|
commercial.location.latitude = cityGeo.latitude;
|
||||||
commercial.location.longitude = cityGeo.longitude;
|
commercial.location.longitude = cityGeo.longitude;
|
||||||
commercial.location.city = commercialJsonData[index].city;
|
commercial.location.name = commercialJsonData[index].city;
|
||||||
commercial.location.state = commercialJsonData[index].state;
|
commercial.location.state = commercialJsonData[index].state;
|
||||||
// console.log(JSON.stringify(commercial.location));
|
// console.log(JSON.stringify(commercial.location));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -229,7 +229,7 @@ for (let index = 0; index < businessJsonData.length; index++) {
|
||||||
business.location = {};
|
business.location = {};
|
||||||
business.location.latitude = cityGeo.latitude;
|
business.location.latitude = cityGeo.latitude;
|
||||||
business.location.longitude = cityGeo.longitude;
|
business.location.longitude = cityGeo.longitude;
|
||||||
business.location.city = businessJsonData[index].city;
|
business.location.name = businessJsonData[index].city;
|
||||||
business.location.state = businessJsonData[index].state;
|
business.location.state = businessJsonData[index].state;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`----------------> ERROR ${businessJsonData[index].state} - ${businessJsonData[index].city}`);
|
console.log(`----------------> ERROR ${businessJsonData[index].state} - ${businessJsonData[index].city}`);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import path, { join } from 'path';
|
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 { fileURLToPath } from 'url';
|
||||||
import { City, CountyData, Geo, State } from '../models/server.model.js';
|
import { City, CountyData, Geo, State } from '../models/server.model.js';
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ export class GeoService {
|
||||||
if (city.name.toLowerCase().startsWith(prefix.toLowerCase())) {
|
if (city.name.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||||
result.push({
|
result.push({
|
||||||
id: city.id,
|
id: city.id,
|
||||||
city: city.name,
|
name: city.name,
|
||||||
state: state.state_code,
|
state: state.state_code,
|
||||||
//state_code: state.state_code,
|
//state_code: state.state_code,
|
||||||
latitude: city.latitude,
|
latitude: city.latitude,
|
||||||
|
|
@ -63,8 +63,8 @@ export class GeoService {
|
||||||
});
|
});
|
||||||
return state ? result.filter(e => e.state.toLowerCase() === state.toLowerCase()) : result;
|
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 }> {
|
findCitiesAndStatesStartingWith(prefix: string, state?: string): Array<CityAndStateResult> {
|
||||||
const results: Array<{ id: string; name: string; type: 'city' | 'state'; state: string }> = [];
|
const results: Array<CityAndStateResult> = [];
|
||||||
|
|
||||||
const lowercasePrefix = prefix.toLowerCase();
|
const lowercasePrefix = prefix.toLowerCase();
|
||||||
|
|
||||||
|
|
@ -73,10 +73,9 @@ export class GeoService {
|
||||||
for (const state of this.geo.states) {
|
for (const state of this.geo.states) {
|
||||||
if (state.name.toLowerCase().startsWith(lowercasePrefix)) {
|
if (state.name.toLowerCase().startsWith(lowercasePrefix)) {
|
||||||
results.push({
|
results.push({
|
||||||
id: state.id.toString(),
|
id: state.id,
|
||||||
name: state.name,
|
|
||||||
type: 'state',
|
type: 'state',
|
||||||
state: state.state_code,
|
content: state,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,10 +83,9 @@ export class GeoService {
|
||||||
for (const city of state.cities) {
|
for (const city of state.cities) {
|
||||||
if (city.name.toLowerCase().startsWith(lowercasePrefix)) {
|
if (city.name.toLowerCase().startsWith(lowercasePrefix)) {
|
||||||
results.push({
|
results.push({
|
||||||
id: city.id.toString(),
|
id: city.id,
|
||||||
name: city.name,
|
|
||||||
type: 'city',
|
type: 'city',
|
||||||
state: state.state_code,
|
content: { state: state.state_code, ...city },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +95,7 @@ export class GeoService {
|
||||||
return results.sort((a, b) => {
|
return results.sort((a, b) => {
|
||||||
if (a.type === 'state' && b.type === 'city') return -1;
|
if (a.type === 'state' && b.type === 'city') return -1;
|
||||||
if (a.type === 'city' && b.type === 'state') 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 {
|
getCityWithCoords(state: string, city: string): City {
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ 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}%`));
|
whereConditions.push(ilike(businesses.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);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
whereConditions.push(sql`${getDistanceQuery(businesses, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
whereConditions.push(sql`${getDistanceQuery(businesses, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||||
}
|
}
|
||||||
if (criteria.types && criteria.types.length > 0) {
|
if (criteria.types && criteria.types.length > 0) {
|
||||||
|
|
@ -180,11 +180,13 @@ export class BusinessListingService {
|
||||||
return convertDrizzleBusinessToBusiness(createdListing);
|
return convertDrizzleBusinessToBusiness(createdListing);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const formattedErrors = error.errors.map(err => ({
|
const filteredErrors = error.errors
|
||||||
field: err.path.join('.'),
|
.map(item => ({
|
||||||
message: err.message,
|
...item,
|
||||||
}));
|
field: item.path[0],
|
||||||
throw new BadRequestException(formattedErrors);
|
}))
|
||||||
|
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||||
|
throw new BadRequestException(filteredErrors);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -200,11 +202,13 @@ export class BusinessListingService {
|
||||||
return convertDrizzleBusinessToBusiness(updateListing);
|
return convertDrizzleBusinessToBusiness(updateListing);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const formattedErrors = error.errors.map(err => ({
|
const filteredErrors = error.errors
|
||||||
field: err.path.join('.'),
|
.map(item => ({
|
||||||
message: err.message,
|
...item,
|
||||||
}));
|
field: item.path[0],
|
||||||
throw new BadRequestException(formattedErrors);
|
}))
|
||||||
|
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||||
|
throw new BadRequestException(filteredErrors);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ 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}%`));
|
whereConditions.push(ilike(schema.commercials.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);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
whereConditions.push(sql`${getDistanceQuery(commercials, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
whereConditions.push(sql`${getDistanceQuery(commercials, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||||
}
|
}
|
||||||
if (criteria.types && criteria.types.length > 0) {
|
if (criteria.types && criteria.types.length > 0) {
|
||||||
|
|
@ -131,11 +131,13 @@ export class CommercialPropertyService {
|
||||||
return convertDrizzleCommercialToCommercial(createdListing);
|
return convertDrizzleCommercialToCommercial(createdListing);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const formattedErrors = error.errors.map(err => ({
|
const filteredErrors = error.errors
|
||||||
field: err.path.join('.'),
|
.map(item => ({
|
||||||
message: err.message,
|
...item,
|
||||||
}));
|
field: item.path[0],
|
||||||
throw new BadRequestException(formattedErrors);
|
}))
|
||||||
|
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||||
|
throw new BadRequestException(filteredErrors);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -157,11 +159,13 @@ export class CommercialPropertyService {
|
||||||
return convertDrizzleCommercialToCommercial(updateListing);
|
return convertDrizzleCommercialToCommercial(updateListing);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const formattedErrors = error.errors.map(err => ({
|
const filteredErrors = error.errors
|
||||||
field: err.path.join('.'),
|
.map(item => ({
|
||||||
message: err.message,
|
...item,
|
||||||
}));
|
field: item.path[0],
|
||||||
throw new BadRequestException(formattedErrors);
|
}))
|
||||||
|
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||||
|
throw new BadRequestException(filteredErrors);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ export const LicensedInSchema = z.object({
|
||||||
state: z.string().nonempty('State is required'),
|
state: z.string().nonempty('State is required'),
|
||||||
});
|
});
|
||||||
export const GeoSchema = z.object({
|
export const GeoSchema = z.object({
|
||||||
city: z.string(),
|
name: z.string(),
|
||||||
state: z.string().refine(val => USStates.safeParse(val).success, {
|
state: z.string().refine(val => USStates.safeParse(val).success, {
|
||||||
message: 'Invalid state. Must be a valid 2-letter US state code.',
|
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 { BusinessListing, CommercialPropertyListing, Sender, User } from './db.model.js';
|
||||||
|
import { State } from './server.model.js';
|
||||||
|
|
||||||
export interface StatesResult {
|
export interface StatesResult {
|
||||||
state: string;
|
state: string;
|
||||||
|
|
@ -59,7 +60,7 @@ export interface ListCriteria {
|
||||||
page: number;
|
page: number;
|
||||||
types: string[];
|
types: string[];
|
||||||
state: string;
|
state: string;
|
||||||
city: string;
|
city: GeoResult;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
searchType: 'exact' | 'radius';
|
searchType: 'exact' | 'radius';
|
||||||
// radius: '5' | '20' | '50' | '100' | '200' | '300' | '400' | '500';
|
// radius: '5' | '20' | '50' | '100' | '200' | '300' | '400' | '500';
|
||||||
|
|
@ -224,18 +225,23 @@ export interface UploadParams {
|
||||||
}
|
}
|
||||||
export interface GeoResult {
|
export interface GeoResult {
|
||||||
id: number;
|
id: number;
|
||||||
city: string;
|
name: string;
|
||||||
state: string;
|
state: string;
|
||||||
// state_code: string;
|
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
}
|
}
|
||||||
export interface CityAndStateResult {
|
interface CityResult {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
type: 'city';
|
||||||
type: string;
|
content: GeoResult;
|
||||||
state: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StateResult {
|
||||||
|
id: number;
|
||||||
|
type: 'state';
|
||||||
|
content: State;
|
||||||
|
}
|
||||||
|
export type CityAndStateResult = CityResult | StateResult;
|
||||||
export interface CountyResult {
|
export interface CountyResult {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ 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}%`));
|
whereConditions.push(ilike(schema.users.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);
|
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||||
whereConditions.push(sql`${getDistanceQuery(schema.users, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
whereConditions.push(sql`${getDistanceQuery(schema.users, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||||
}
|
}
|
||||||
if (criteria.types && criteria.types.length > 0) {
|
if (criteria.types && criteria.types.length > 0) {
|
||||||
|
|
@ -139,11 +139,13 @@ export class UserService {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ZodError) {
|
if (error instanceof ZodError) {
|
||||||
const formattedErrors = error.errors.map(err => ({
|
const filteredErrors = error.errors
|
||||||
field: err.path.join('.'),
|
.map(item => ({
|
||||||
message: err.message,
|
...item,
|
||||||
}));
|
field: item.path[0],
|
||||||
throw new BadRequestException(formattedErrors);
|
}))
|
||||||
|
.filter((item, index, self) => index === self.findIndex(t => t.path[0] === item.path[0]));
|
||||||
|
throw new BadRequestException(filteredErrors);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,14 @@ type DrizzleUser = typeof users.$inferSelect;
|
||||||
type DrizzleBusinessListing = typeof businesses.$inferSelect;
|
type DrizzleBusinessListing = typeof businesses.$inferSelect;
|
||||||
type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect;
|
type DrizzleCommercialPropertyListing = typeof commercials.$inferSelect;
|
||||||
export function convertBusinessToDrizzleBusiness(businessListing: Partial<BusinessListing>): DrizzleBusinessListing {
|
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 {
|
export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial<DrizzleBusinessListing>): BusinessListing {
|
||||||
const o = {
|
const o = {
|
||||||
location_city: drizzleBusinessListing.city,
|
location_name: drizzleBusinessListing.city,
|
||||||
location_state: drizzleBusinessListing.state,
|
location_state: drizzleBusinessListing.state,
|
||||||
location_latitude: drizzleBusinessListing.latitude,
|
location_latitude: drizzleBusinessListing.latitude,
|
||||||
location_longitude: drizzleBusinessListing.longitude,
|
location_longitude: drizzleBusinessListing.longitude,
|
||||||
|
|
@ -50,11 +53,14 @@ export function convertDrizzleBusinessToBusiness(drizzleBusinessListing: Partial
|
||||||
return unflattenObject(o);
|
return unflattenObject(o);
|
||||||
}
|
}
|
||||||
export function convertCommercialToDrizzleCommercial(commercialPropertyListing: Partial<CommercialPropertyListing>): DrizzleCommercialPropertyListing {
|
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 {
|
export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyListing: Partial<DrizzleCommercialPropertyListing>): CommercialPropertyListing {
|
||||||
const o = {
|
const o = {
|
||||||
location_city: drizzleCommercialPropertyListing.city,
|
location_name: drizzleCommercialPropertyListing.city,
|
||||||
location_state: drizzleCommercialPropertyListing.state,
|
location_state: drizzleCommercialPropertyListing.state,
|
||||||
location_latitude: drizzleCommercialPropertyListing.latitude,
|
location_latitude: drizzleCommercialPropertyListing.latitude,
|
||||||
location_longitude: drizzleCommercialPropertyListing.longitude,
|
location_longitude: drizzleCommercialPropertyListing.longitude,
|
||||||
|
|
@ -67,12 +73,15 @@ export function convertDrizzleCommercialToCommercial(drizzleCommercialPropertyLi
|
||||||
return unflattenObject(o);
|
return unflattenObject(o);
|
||||||
}
|
}
|
||||||
export function convertUserToDrizzleUser(user: Partial<User>): DrizzleUser {
|
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 {
|
export function convertDrizzleUserToUser(drizzleUser: Partial<DrizzleUser>): User {
|
||||||
const o = {
|
const o = {
|
||||||
companyLocation_city: drizzleUser.city,
|
companyLocation_name: drizzleUser.city,
|
||||||
companyLocation_state: drizzleUser.state,
|
companyLocation_state: drizzleUser.state,
|
||||||
companyLocation_latitude: drizzleUser.latitude,
|
companyLocation_latitude: drizzleUser.latitude,
|
||||||
companyLocation_longitude: drizzleUser.longitude,
|
companyLocation_longitude: drizzleUser.longitude,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@
|
||||||
<div class="relative w-full max-w-4xl max-h-full">
|
<div class="relative w-full max-w-4xl max-h-full">
|
||||||
<div class="relative bg-white rounded-lg shadow">
|
<div class="relative bg-white rounded-lg shadow">
|
||||||
<div class="flex items-start justify-between p-4 border-b rounded-t">
|
<div class="flex items-start justify-between p-4 border-b rounded-t">
|
||||||
|
@if(criteria.criteriaType==='businessListings'){
|
||||||
<h3 class="text-xl font-semibold text-gray-900">Business Listing Search</h3>
|
<h3 class="text-xl font-semibold text-gray-900">Business Listing Search</h3>
|
||||||
|
} @else if (criteria.criteriaType==='commercialPropertyListings'){
|
||||||
|
<h3 class="text-xl font-semibold text-gray-900">Property Listing Search</h3>
|
||||||
|
} @else {
|
||||||
|
<h3 class="text-xl font-semibold text-gray-900">Professional Listing Search</h3>
|
||||||
|
}
|
||||||
<button (click)="modalService.reject()" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
|
<button (click)="modalService.reject()" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center">
|
||||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||||
|
|
@ -20,10 +26,12 @@
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block mb-2 text-sm font-medium text-gray-900">Location - State</label>
|
<label for="state" class="block mb-2 text-sm font-medium text-gray-900">Location - State</label>
|
||||||
|
|
||||||
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-gray-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
|
</div>
|
||||||
|
<!-- <div>
|
||||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||||
|
|
||||||
<ng-select
|
<ng-select
|
||||||
|
|
@ -42,7 +50,7 @@
|
||||||
<ng-option [value]="city">{{ city.city }} - {{ city.state }}</ng-option>
|
<ng-option [value]="city">{{ city.city }} - {{ city.state }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select>
|
</ng-select>
|
||||||
</div>
|
</div> -->
|
||||||
<!-- New section for city search type -->
|
<!-- New section for city search type -->
|
||||||
<div *ngIf="criteria.city">
|
<div *ngIf="criteria.city">
|
||||||
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
|
<label class="block mb-2 text-sm font-medium text-gray-900">Search Type</label>
|
||||||
|
|
@ -241,7 +249,7 @@
|
||||||
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
|
<ng-select class="custom" [items]="selectOptions?.states" bindLabel="name" bindValue="value" [ngModel]="criteria.state" (ngModelChange)="setState($event)" name="state"> </ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
<!-- <label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||||
<ng-select
|
<ng-select
|
||||||
class="custom"
|
class="custom"
|
||||||
[multiple]="false"
|
[multiple]="false"
|
||||||
|
|
@ -257,7 +265,8 @@
|
||||||
@for (city of cities$ | async; track city.id) {
|
@for (city of cities$ | async; track city.id) {
|
||||||
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select>
|
</ng-select> -->
|
||||||
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-gray-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
</div>
|
</div>
|
||||||
<!-- New section for city search type -->
|
<!-- New section for city search type -->
|
||||||
<div *ngIf="criteria.city">
|
<div *ngIf="criteria.city">
|
||||||
|
|
@ -363,13 +372,10 @@
|
||||||
[typeahead]="countyInput$"
|
[typeahead]="countyInput$"
|
||||||
[(ngModel)]="criteria.counties"
|
[(ngModel)]="criteria.counties"
|
||||||
>
|
>
|
||||||
<!-- @for (county of counties$ | async; track county.id) {
|
|
||||||
<ng-option [value]="city.city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
|
||||||
} -->
|
|
||||||
</ng-select>
|
</ng-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
<!-- <label for="city" class="block mb-2 text-sm font-medium text-gray-900">Location - City</label>
|
||||||
<ng-select
|
<ng-select
|
||||||
class="custom"
|
class="custom"
|
||||||
[multiple]="false"
|
[multiple]="false"
|
||||||
|
|
@ -383,9 +389,10 @@
|
||||||
(ngModelChange)="setCity($event)"
|
(ngModelChange)="setCity($event)"
|
||||||
>
|
>
|
||||||
@for (city of cities$ | async; track city.id) {
|
@for (city of cities$ | async; track city.id) {
|
||||||
<ng-option [value]="city">{{ city.city }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
<ng-option [value]="city">{{ city.name }} - {{ selectOptions.getStateInitials(city.state) }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select>
|
</ng-select> -->
|
||||||
|
<app-validated-city label="Location - City" name="city" [ngModel]="criteria.city" (ngModelChange)="setCity($event)" labelClasses="text-gray-900 font-medium" [state]="criteria.state"></app-validated-city>
|
||||||
</div>
|
</div>
|
||||||
<!-- New section for city search type -->
|
<!-- New section for city search type -->
|
||||||
<div *ngIf="criteria.city">
|
<div *ngIf="criteria.city">
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@ import { ListingsService } from '../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../services/select-options.service';
|
import { SelectOptionsService } from '../../services/select-options.service';
|
||||||
import { UserService } from '../../services/user.service';
|
import { UserService } from '../../services/user.service';
|
||||||
import { SharedModule } from '../../shared/shared/shared.module';
|
import { SharedModule } from '../../shared/shared/shared.module';
|
||||||
|
import { ValidatedCityComponent } from '../validated-city/validated-city.component';
|
||||||
import { ModalService } from './modal.service';
|
import { ModalService } from './modal.service';
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-search-modal',
|
selector: 'app-search-modal',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, AsyncPipe, NgIf, NgSelectModule],
|
imports: [SharedModule, AsyncPipe, NgIf, NgSelectModule, ValidatedCityComponent],
|
||||||
templateUrl: './search-modal.component.html',
|
templateUrl: './search-modal.component.html',
|
||||||
styleUrl: './search-modal.component.scss',
|
styleUrl: './search-modal.component.scss',
|
||||||
})
|
})
|
||||||
|
|
@ -98,7 +99,7 @@ export class SearchModalComponent {
|
||||||
}
|
}
|
||||||
setCity(city) {
|
setCity(city) {
|
||||||
if (city) {
|
if (city) {
|
||||||
this.criteria.city = city.city;
|
this.criteria.city = city;
|
||||||
this.criteria.state = city.state;
|
this.criteria.state = city.state;
|
||||||
} else {
|
} else {
|
||||||
this.criteria.city = null;
|
this.criteria.city = null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div>
|
<div>
|
||||||
<label for="type" class="block text-sm font-bold text-gray-700 mb-1 relative w-fit"
|
<label for="type" class="block text-sm font-bold text-gray-700 mb-1 relative w-fit {{ labelClasses }}"
|
||||||
>{{ label }} @if(validationMessage){
|
>{{ label }} @if(validationMessage){
|
||||||
<div
|
<div
|
||||||
attr.data-tooltip-target="tooltip-{{ name }}"
|
attr.data-tooltip-target="tooltip-{{ name }}"
|
||||||
|
|
@ -19,11 +19,11 @@
|
||||||
[loading]="cityLoading"
|
[loading]="cityLoading"
|
||||||
typeToSearchText="Please enter 2 or more characters"
|
typeToSearchText="Please enter 2 or more characters"
|
||||||
[typeahead]="cityInput$"
|
[typeahead]="cityInput$"
|
||||||
ngModel="{{ value?.city }} {{ value ? '-' : '' }} {{ value?.state }}"
|
ngModel="{{ value?.name }} {{ value ? '-' : '' }} {{ value?.state }}"
|
||||||
(ngModelChange)="onInputChange($event)"
|
(ngModelChange)="onInputChange($event)"
|
||||||
>
|
>
|
||||||
@for (city of cities$ | async; track city.id) {
|
@for (city of cities$ | async; track city.id) {
|
||||||
<ng-option [value]="city">{{ city.city }} - {{ city.state }}</ng-option>
|
<ng-option [value]="city">{{ city.name }} - {{ city.state }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select>
|
</ng-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
:host ::ng-deep .ng-select.custom .ng-select-container {
|
:host ::ng-deep .ng-select.custom .ng-select-container {
|
||||||
// --tw-bg-opacity: 1;
|
// --tw-bg-opacity: 1;
|
||||||
// background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
// background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||||
height: 42px;
|
// height: 42px;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
.ng-value-container .ng-input {
|
.ng-value-container .ng-input {
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import { ValidationMessagesService } from '../validation-messages.service';
|
||||||
})
|
})
|
||||||
export class ValidatedCityComponent extends BaseInputComponent {
|
export class ValidatedCityComponent extends BaseInputComponent {
|
||||||
@Input() items;
|
@Input() items;
|
||||||
|
@Input() labelClasses: string;
|
||||||
|
@Input() state: string;
|
||||||
cities$: Observable<GeoResult[]>;
|
cities$: Observable<GeoResult[]>;
|
||||||
cityInput$ = new Subject<string>();
|
cityInput$ = new Subject<string>();
|
||||||
countyInput$ = new Subject<string>();
|
countyInput$ = new Subject<string>();
|
||||||
|
|
@ -50,7 +52,7 @@ export class ValidatedCityComponent extends BaseInputComponent {
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
tap(() => (this.cityLoading = true)),
|
tap(() => (this.cityLoading = true)),
|
||||||
switchMap(term =>
|
switchMap(term =>
|
||||||
this.geoService.findCitiesStartingWith(term).pipe(
|
this.geoService.findCitiesStartingWith(term, this.state).pipe(
|
||||||
catchError(() => of([])), // empty list on error
|
catchError(() => of([])), // empty list on error
|
||||||
// map(cities => cities.map(city => city.city)), // transform the list of objects to a list of city names
|
// map(cities => cities.map(city => city.city)), // transform the list of objects to a list of city names
|
||||||
tap(() => (this.cityLoading = false)),
|
tap(() => (this.cityLoading = false)),
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ export class DetailsBusinessListingComponent {
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
{ label: 'Category', value: this.selectOptions.getBusiness(this.listing.type) },
|
{ label: 'Category', value: this.selectOptions.getBusiness(this.listing.type) },
|
||||||
{ label: 'Located in', value: `${this.listing.location.city}, ${this.selectOptions.getState(this.listing.location.state)}` },
|
{ label: 'Located in', value: `${this.listing.location.name}, ${this.selectOptions.getState(this.listing.location.state)}` },
|
||||||
{ label: 'Asking Price', value: `$${this.listing.price?.toLocaleString()}` },
|
{ label: 'Asking Price', value: `$${this.listing.price?.toLocaleString()}` },
|
||||||
{ label: 'Sales revenue', value: `$${this.listing.salesRevenue?.toLocaleString()}` },
|
{ label: 'Sales revenue', value: `$${this.listing.salesRevenue?.toLocaleString()}` },
|
||||||
{ label: 'Cash flow', value: `$${this.listing.cashFlow?.toLocaleString()}` },
|
{ label: 'Cash flow', value: `$${this.listing.cashFlow?.toLocaleString()}` },
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ export class DetailsCommercialPropertyListingComponent {
|
||||||
this.propertyDetails = [
|
this.propertyDetails = [
|
||||||
{ label: 'Property Category', value: this.selectOptions.getCommercialProperty(this.listing.type) },
|
{ label: 'Property Category', value: this.selectOptions.getCommercialProperty(this.listing.type) },
|
||||||
{ label: 'Located in', value: this.selectOptions.getState(this.listing.location.state) },
|
{ label: 'Located in', value: this.selectOptions.getState(this.listing.location.state) },
|
||||||
{ label: 'City', value: this.listing.location.city },
|
{ label: 'City', value: this.listing.location.name },
|
||||||
{ label: 'Asking Price:', value: `$${this.listing.price?.toLocaleString()}` },
|
{ label: 'Asking Price:', value: `$${this.listing.price?.toLocaleString()}` },
|
||||||
];
|
];
|
||||||
//this.initFlowbite();
|
//this.initFlowbite();
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,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.city }} - {{ user.companyLocation.state }}</span>
|
<span class="p-2 flex-grow">{{ user.companyLocation.name }} - {{ user.companyLocation.state }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@
|
||||||
placeholder="Enter City or State ..."
|
placeholder="Enter City or State ..."
|
||||||
groupBy="type"
|
groupBy="type"
|
||||||
>
|
>
|
||||||
@for (city of cities$ | async; track city.id) {
|
@for (city of cities$ | async; track city.id) { @let state = city.type==='city'?city.content.state:''; @let separator = city.type==='city'?' - ':'';
|
||||||
<ng-option [value]="city">{{ city.name }} - {{ city.state }}</ng-option>
|
<ng-option [value]="city">{{ city.content.name }}{{ separator }}{{ state }}</ng-option>
|
||||||
}
|
}
|
||||||
</ng-select>
|
</ng-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ export class HomeComponent {
|
||||||
}
|
}
|
||||||
async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') {
|
async changeTab(tabname: 'business' | 'commercialProperty' | 'broker') {
|
||||||
this.activeTabAction = tabname;
|
this.activeTabAction = tabname;
|
||||||
|
this.cityOrState = null;
|
||||||
if ('business' === tabname) {
|
if ('business' === tabname) {
|
||||||
this.criteria = createEnhancedProxy(getCriteriaStateObject('businessListings'), this);
|
this.criteria = createEnhancedProxy(getCriteriaStateObject('businessListings'), this);
|
||||||
} else if ('commercialProperty' === tabname) {
|
} else if ('commercialProperty' === tabname) {
|
||||||
|
|
@ -74,22 +75,7 @@ export class HomeComponent {
|
||||||
this.criteria = undefined;
|
this.criteria = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// private createEnhancedProxy(obj: any) {
|
|
||||||
// const component = this;
|
|
||||||
|
|
||||||
// const sessionStorageHandler = function (path, value, previous, applyData) {
|
|
||||||
// let criteriaType = this.criteriaType;
|
|
||||||
// sessionStorage.setItem(`${criteriaType}_criteria`, JSON.stringify(this));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return onChange(obj, function (path, value, previous, applyData) {
|
|
||||||
// // Call the original sessionStorageHandler
|
|
||||||
// sessionStorageHandler.call(this, path, value, previous, applyData);
|
|
||||||
|
|
||||||
// // Notify about the criteria change using the component's context
|
|
||||||
// component.criteriaChangeService.notifyCriteriaChange();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
search() {
|
search() {
|
||||||
this.router.navigate([`${this.activeTabAction}Listings`]);
|
this.router.navigate([`${this.activeTabAction}Listings`]);
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +112,6 @@ export class HomeComponent {
|
||||||
async openModal() {
|
async openModal() {
|
||||||
const accepted = await this.modalService.showModal(this.criteria);
|
const accepted = await this.modalService.showModal(this.criteria);
|
||||||
if (accepted) {
|
if (accepted) {
|
||||||
//this.searchService.search(this.criteria);
|
|
||||||
this.router.navigate([`${this.activeTabAction}Listings`]);
|
this.router.navigate([`${this.activeTabAction}Listings`]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,10 +138,10 @@ export class HomeComponent {
|
||||||
setCityOrState(cityOrState: CityAndStateResult) {
|
setCityOrState(cityOrState: CityAndStateResult) {
|
||||||
if (cityOrState) {
|
if (cityOrState) {
|
||||||
if (cityOrState.type === 'state') {
|
if (cityOrState.type === 'state') {
|
||||||
this.criteria.state = cityOrState.state;
|
this.criteria.state = cityOrState.content.state_code;
|
||||||
} else {
|
} else {
|
||||||
this.criteria.city = cityOrState.name;
|
this.criteria.city = cityOrState.content as GeoResult;
|
||||||
this.criteria.state = cityOrState.state;
|
this.criteria.state = cityOrState.content.state;
|
||||||
this.criteria.searchType = 'radius';
|
this.criteria.searchType = 'radius';
|
||||||
this.criteria.radius = 20;
|
this.criteria.radius = 20;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
<p class="text-sm text-gray-600 mb-1">Asking price: {{ listing.price | currency }}</p>
|
<p class="text-sm text-gray-600 mb-1">Asking price: {{ listing.price | currency }}</p>
|
||||||
<p class="text-sm text-gray-600 mb-1">Sales revenue: {{ listing.salesRevenue | currency }}</p>
|
<p class="text-sm text-gray-600 mb-1">Sales revenue: {{ listing.salesRevenue | currency }}</p>
|
||||||
<p class="text-sm text-gray-600 mb-1">Net profit: {{ listing.cashFlow | currency }}</p>
|
<p class="text-sm text-gray-600 mb-1">Net profit: {{ listing.cashFlow | currency }}</p>
|
||||||
<p class="text-sm text-gray-600 mb-1">Location: {{ listing.location.city }} - {{ listing.location.state }}</p>
|
<p class="text-sm text-gray-600 mb-1">Location: {{ listing.location.name }} - {{ listing.location.state }}</p>
|
||||||
<p class="text-sm text-gray-600 mb-1">Established: {{ listing.established }}</p>
|
<p class="text-sm text-gray-600 mb-1">Established: {{ listing.established }}</p>
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" alt="Company logo" class="absolute bottom-[70px] right-[30px] h-[35px] w-auto" />
|
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" alt="Company logo" class="absolute bottom-[70px] right-[30px] h-[35px] w-auto" />
|
||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<span class="text-gray-600 text-sm"><i [class]="selectOptions.getIconTypeOfCommercials(listing.type)" class="mr-1"></i> {{ selectOptions.getCommercialProperty(listing.type) }}</span>
|
<span class="text-gray-600 text-sm"><i [class]="selectOptions.getIconTypeOfCommercials(listing.type)" class="mr-1"></i> {{ selectOptions.getCommercialProperty(listing.type) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ listing.title }}</h3>
|
<h3 class="text-lg font-semibold mb-2">{{ listing.title }}</h3>
|
||||||
<p class="text-gray-600 mb-2">{{ listing.location.city }}</p>
|
<p class="text-gray-600 mb-2">{{ listing.location.name }}</p>
|
||||||
<p class="text-xl font-bold mb-4">{{ listing.price | currency }}</p>
|
<p class="text-xl font-bold mb-4">{{ listing.price | currency }}</p>
|
||||||
<button [routerLink]="['/details-commercial-property-listing', listing.id]" class="bg-green-500 text-white px-4 py-2 rounded-full w-full hover:bg-green-600 transition duration-300">
|
<button [routerLink]="['/details-commercial-property-listing', listing.id]" class="bg-green-500 text-white px-4 py-2 rounded-full w-full hover:bg-green-600 transition duration-300">
|
||||||
View Full Listing <i class="fas fa-arrow-right ml-1"></i>
|
View Full Listing <i class="fas fa-arrow-right ml-1"></i>
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@
|
||||||
<input type="text" id="description" name="description" [(ngModel)]="user.description" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
|
<input type="text" id="description" name="description" [(ngModel)]="user.description" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
|
||||||
</div> -->
|
</div> -->
|
||||||
<app-validated-input label="Company Name" name="companyName" [(ngModel)]="user.companyName"></app-validated-input>
|
<app-validated-input label="Company Name" name="companyName" [(ngModel)]="user.companyName"></app-validated-input>
|
||||||
<app-validated-input label="Describe yourself" name="description" [(ngModel)]="user.description"></app-validated-input>
|
<app-validated-input label="Describe Yourself" name="description" [(ngModel)]="user.description"></app-validated-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ export class AccountComponent {
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
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() {
|
addLicence() {
|
||||||
this.user.licensedIn.push({ registerNo: '', state: '' });
|
this.user.licensedIn.push({ registerNo: '', state: '' });
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ export class EditBusinessListingComponent {
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
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') {
|
changeListingCategory(value: 'business' | 'commercialProperty') {
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ export class EditCommercialPropertyListingComponent {
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
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() {
|
openFileDialog() {
|
||||||
this.fileInput.nativeElement.click();
|
this.fileInput.nativeElement.click();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
||||||
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
||||||
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
||||||
<p class="text-gray-600 mb-4">Located in: {{ listing.location.city }} - {{ listing.location.state }}</p>
|
<p class="text-gray-600 mb-4">Located in: {{ listing.location.name }} - {{ listing.location.state }}</p>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button class="bg-green-500 text-white p-2 rounded-full mr-2">
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,8 @@ export function createEmptyBusinessListingCriteria(): BusinessListingCriteria {
|
||||||
start: 0,
|
start: 0,
|
||||||
length: 0,
|
length: 0,
|
||||||
page: 0,
|
page: 0,
|
||||||
state: '',
|
state: null,
|
||||||
city: '',
|
city: null,
|
||||||
types: [],
|
types: [],
|
||||||
prompt: '',
|
prompt: '',
|
||||||
criteriaType: 'businessListings',
|
criteriaType: 'businessListings',
|
||||||
|
|
@ -123,8 +123,8 @@ export function createEmptyCommercialPropertyListingCriteria(): CommercialProper
|
||||||
start: 0,
|
start: 0,
|
||||||
length: 0,
|
length: 0,
|
||||||
page: 0,
|
page: 0,
|
||||||
state: '',
|
state: null,
|
||||||
city: '',
|
city: null,
|
||||||
types: [],
|
types: [],
|
||||||
prompt: '',
|
prompt: '',
|
||||||
criteriaType: 'commercialPropertyListings',
|
criteriaType: 'commercialPropertyListings',
|
||||||
|
|
@ -141,7 +141,7 @@ export function createEmptyUserListingCriteria(): UserListingCriteria {
|
||||||
start: 0,
|
start: 0,
|
||||||
length: 0,
|
length: 0,
|
||||||
page: 0,
|
page: 0,
|
||||||
city: '',
|
city: null,
|
||||||
types: [],
|
types: [],
|
||||||
prompt: '',
|
prompt: '',
|
||||||
criteriaType: 'brokerListings',
|
criteriaType: 'brokerListings',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue