geo coding, user service, listing for business
This commit is contained in:
parent
06f349d1c3
commit
6ad40b6dca
|
|
@ -1,7 +0,0 @@
|
||||||
/// <reference types="multer" />
|
|
||||||
import { FileService } from '../file/file.service.js';
|
|
||||||
export declare class AccountController {
|
|
||||||
private fileService;
|
|
||||||
constructor(fileService: FileService);
|
|
||||||
uploadFile(file: Express.Multer.File, id: string): void;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export declare class AccountService {
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { AppService } from './app.service.js';
|
|
||||||
export declare class AppController {
|
|
||||||
private readonly appService;
|
|
||||||
constructor(appService: AppService);
|
|
||||||
getHello(): string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export declare class AppModule {
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export declare class AppService {
|
|
||||||
getHello(): string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"id":"1",
|
|
||||||
"userId":"1",
|
|
||||||
"listingsCategory": "business",
|
|
||||||
"title": "Industrial Service Company In Corpus Christi For Sale - 1954",
|
|
||||||
"summary": ["Asking price: $5,500,000","Sales revenue: $1,200,000","Net profit: $650,000"],
|
|
||||||
"description": ["This company services a wide variety of industries. Asking price includes Business and the Real Estate and is approx 30,000 sq ft with room for expansion including approx 5 acres. Absentee run business."],
|
|
||||||
"type": "2",
|
|
||||||
"location": "Texas",
|
|
||||||
"price":5500000,
|
|
||||||
"salesRevenue":1200000,
|
|
||||||
"cashFlow":650000,
|
|
||||||
"brokerLicencing":"TREC Broker #516788",
|
|
||||||
"established":1954,
|
|
||||||
"realEstateIncluded":true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id":"2",
|
|
||||||
"userId":"1",
|
|
||||||
"listingsCategory": "business",
|
|
||||||
"title": "Coastal Bend Manufacturing Business Plastic Injection For Sale - 1950",
|
|
||||||
"summary": ["Asking price: $165,000","Sales revenue: Undisclosed","Net profit: Undisclosed"],
|
|
||||||
"description": [""],
|
|
||||||
"type": "12",
|
|
||||||
"location": "Texas",
|
|
||||||
"price":165000,
|
|
||||||
"salesRevenue":null,
|
|
||||||
"cashFlow":null,
|
|
||||||
"brokerLicencing":"TREC Broker #516788",
|
|
||||||
"established":1950,
|
|
||||||
"realEstateIncluded":false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id":"3",
|
|
||||||
"userId":"1",
|
|
||||||
"listingsCategory": "business",
|
|
||||||
"title": "Corner Property On Everhart South-side Corpus Christi For Sale - 1944",
|
|
||||||
"summary": ["Asking price: $830,000","Sales revenue: Undisclosed","Net profit: Undisclosed"],
|
|
||||||
"description": [""],
|
|
||||||
"type": "3",
|
|
||||||
"location": "Texas",
|
|
||||||
"price":830000,
|
|
||||||
"salesRevenue":null,
|
|
||||||
"cashFlow":null,
|
|
||||||
"brokerLicencing":"TREC Broker #516788",
|
|
||||||
"established":1944,
|
|
||||||
"realEstateIncluded":false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id":"4",
|
|
||||||
"userId":"1",
|
|
||||||
"listingsCategory": "business",
|
|
||||||
"title": "Corpus Christi Dessert Business For Sale - 1941",
|
|
||||||
"summary": ["Asking price: $124,900","Sales revenue: $225,000","Net profit: $50,000"],
|
|
||||||
"description": [""],
|
|
||||||
"type": "13",
|
|
||||||
"location": "Texas",
|
|
||||||
"price":830000,
|
|
||||||
"salesRevenue":225000,
|
|
||||||
"cashFlow":50000,
|
|
||||||
"brokerLicencing":"TREC Broker #516788",
|
|
||||||
"established":1941,
|
|
||||||
"realEstateIncluded":false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[{
|
|
||||||
"id":"1",
|
|
||||||
"userId":"e0811669-c7eb-4e5e-a699-e8334d5c5b01",
|
|
||||||
"level":"Business Broker",
|
|
||||||
"start":"2024-02-12T21:54:20.603Z",
|
|
||||||
"modified":"2024-02-12T21:54:20.603Z",
|
|
||||||
"end":"9999-02-12T21:54:20.603Z",
|
|
||||||
"status":"active",
|
|
||||||
"invoices":[{
|
|
||||||
"date":"2024-02-12T21:54:20.603Z",
|
|
||||||
"id":"C991853B99",
|
|
||||||
"price":0
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
/// <reference types="multer" />
|
|
||||||
export declare class FileService {
|
|
||||||
private subscriptions;
|
|
||||||
constructor();
|
|
||||||
private loadSubscriptions;
|
|
||||||
getSubscriptions(): any;
|
|
||||||
storeFile(file: Express.Multer.File, id: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import { ListingsService } from './listings.service.js';
|
|
||||||
import { Logger } from 'winston';
|
|
||||||
export declare class ListingsController {
|
|
||||||
private readonly listingsService;
|
|
||||||
private readonly logger;
|
|
||||||
constructor(listingsService: ListingsService, logger: Logger);
|
|
||||||
findAll(): any;
|
|
||||||
findById(id: string): any;
|
|
||||||
find(criteria: any): any;
|
|
||||||
updateById(id: string, listing: any): void;
|
|
||||||
create(listing: any): void;
|
|
||||||
deleteById(id: string): void;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import { BusinessListing, InvestmentsListing, ListingCriteria, ProfessionalsBrokersListing } from '../models/main.model.js';
|
|
||||||
import { RedisService } from '../redis/redis.service.js';
|
|
||||||
import { Logger } from 'winston';
|
|
||||||
export declare class ListingsService {
|
|
||||||
private redisService;
|
|
||||||
private readonly logger;
|
|
||||||
constructor(redisService: RedisService, logger: Logger);
|
|
||||||
setListing(value: BusinessListing | ProfessionalsBrokersListing | InvestmentsListing, id?: string): Promise<void>;
|
|
||||||
getListingById(id: string): Promise<any>;
|
|
||||||
deleteListing(id: string): void;
|
|
||||||
getAllListings(start?: number, end?: number): Promise<any>;
|
|
||||||
find(criteria: ListingCriteria): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import { MailService } from './mail.service.js';
|
|
||||||
import { MailInfo } from '../models/server.model.js';
|
|
||||||
export declare class MailController {
|
|
||||||
private mailService;
|
|
||||||
constructor(mailService: MailService);
|
|
||||||
sendEMail(id: string, mailInfo: MailInfo): any;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export declare class MailModule {
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { MailerService } from '@nestjs-modules/mailer';
|
|
||||||
import { AuthService } from '../auth/auth.service.js';
|
|
||||||
import { MailInfo } from '../models/server.model.js';
|
|
||||||
export declare class MailService {
|
|
||||||
private mailerService;
|
|
||||||
private authService;
|
|
||||||
constructor(mailerService: MailerService, authService: AuthService);
|
|
||||||
sendInquiry(userId: string, mailInfo: MailInfo): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<p>Hey {{ name }},</p>
|
|
||||||
<p>You got an inquiry a</p>
|
|
||||||
<p>
|
|
||||||
{{inquiry}}
|
|
||||||
</p>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export {};
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
export interface KeyValue {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
export interface KeyValueStyle {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
icon: string;
|
|
||||||
bgColorClass: string;
|
|
||||||
textColorClass: string;
|
|
||||||
}
|
|
||||||
export type SelectOption<T = number> = {
|
|
||||||
value: T;
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
export interface Listing {
|
|
||||||
id: string;
|
|
||||||
userId: string;
|
|
||||||
title: string;
|
|
||||||
description: Array<string>;
|
|
||||||
location: string;
|
|
||||||
favoritesForUser: Array<string>;
|
|
||||||
hideImage?: boolean;
|
|
||||||
created: Date;
|
|
||||||
updated: Date;
|
|
||||||
}
|
|
||||||
export interface BusinessListing extends Listing {
|
|
||||||
listingsCategory: 'business';
|
|
||||||
summary: Array<string>;
|
|
||||||
type: string;
|
|
||||||
price?: number;
|
|
||||||
realEstateIncluded?: boolean;
|
|
||||||
salesRevenue?: number;
|
|
||||||
cashFlow?: number;
|
|
||||||
netProfit?: number;
|
|
||||||
inventory?: string;
|
|
||||||
employees?: number;
|
|
||||||
established?: number;
|
|
||||||
reasonForSale?: string;
|
|
||||||
brokerLicencing?: string;
|
|
||||||
internals?: string;
|
|
||||||
}
|
|
||||||
export interface ProfessionalsBrokersListing extends Listing {
|
|
||||||
listingsCategory: 'professionals_brokers';
|
|
||||||
summary: string;
|
|
||||||
address?: string;
|
|
||||||
email?: string;
|
|
||||||
website?: string;
|
|
||||||
category?: 'Professionals' | 'Broker';
|
|
||||||
}
|
|
||||||
export interface InvestmentsListing extends Listing {
|
|
||||||
listingsCategory: 'investment';
|
|
||||||
email?: string;
|
|
||||||
website?: string;
|
|
||||||
phoneNumber?: string;
|
|
||||||
}
|
|
||||||
export type ListingType = BusinessListing | ProfessionalsBrokersListing | InvestmentsListing;
|
|
||||||
export interface ListingCriteria {
|
|
||||||
type: string;
|
|
||||||
location: string;
|
|
||||||
minPrice: string;
|
|
||||||
maxPrice: string;
|
|
||||||
realEstateChecked: boolean;
|
|
||||||
listingsCategory: 'business' | 'professionals_brokers' | 'investment';
|
|
||||||
category: 'professional|broker';
|
|
||||||
}
|
|
||||||
export interface User {
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
firstname: string;
|
|
||||||
lastname: string;
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
export interface Subscription {
|
|
||||||
id: string;
|
|
||||||
userId: string;
|
|
||||||
level: string;
|
|
||||||
start: Date;
|
|
||||||
modified: Date;
|
|
||||||
end: Date;
|
|
||||||
status: string;
|
|
||||||
invoices: Array<Invoice>;
|
|
||||||
}
|
|
||||||
export interface Invoice {
|
|
||||||
id: string;
|
|
||||||
date: Date;
|
|
||||||
price: number;
|
|
||||||
}
|
|
||||||
export interface JwtToken {
|
|
||||||
exp: number;
|
|
||||||
iat: number;
|
|
||||||
auth_time: number;
|
|
||||||
jti: string;
|
|
||||||
iss: string;
|
|
||||||
aud: string;
|
|
||||||
sub: string;
|
|
||||||
typ: string;
|
|
||||||
azp: string;
|
|
||||||
nonce: string;
|
|
||||||
session_state: string;
|
|
||||||
acr: string;
|
|
||||||
realm_access: Realmaccess;
|
|
||||||
resource_access: Resourceaccess;
|
|
||||||
scope: string;
|
|
||||||
sid: string;
|
|
||||||
email_verified: boolean;
|
|
||||||
name: string;
|
|
||||||
preferred_username: string;
|
|
||||||
given_name: string;
|
|
||||||
family_name: string;
|
|
||||||
email: string;
|
|
||||||
user_id: string;
|
|
||||||
}
|
|
||||||
interface Resourceaccess {
|
|
||||||
account: Realmaccess;
|
|
||||||
}
|
|
||||||
interface Realmaccess {
|
|
||||||
roles: string[];
|
|
||||||
}
|
|
||||||
export interface PageEvent {
|
|
||||||
first: number;
|
|
||||||
rows: number;
|
|
||||||
page: number;
|
|
||||||
pageCount: number;
|
|
||||||
}
|
|
||||||
export {};
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
export interface KeyValue {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
export interface KeyValueStyle {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
icon:string;
|
|
||||||
bgColorClass:string;
|
|
||||||
textColorClass:string;
|
|
||||||
}
|
|
||||||
export type SelectOption<T = number> = {
|
|
||||||
value: T;
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
export interface Listing {
|
|
||||||
id: string;
|
|
||||||
userId: string;
|
|
||||||
title: string;
|
|
||||||
description: Array<string>;
|
|
||||||
location: string;//enum
|
|
||||||
favoritesForUser:Array<string>;
|
|
||||||
hideImage?:boolean;
|
|
||||||
created:Date;
|
|
||||||
updated:Date;
|
|
||||||
}
|
|
||||||
export interface BusinessListing extends Listing {
|
|
||||||
listingsCategory: 'business'; //enum
|
|
||||||
summary: Array<string>;
|
|
||||||
type: string; //enum
|
|
||||||
price?: number;
|
|
||||||
realEstateIncluded?: boolean;
|
|
||||||
salesRevenue?: number;
|
|
||||||
cashFlow?: number;
|
|
||||||
netProfit?: number;
|
|
||||||
inventory?: string;
|
|
||||||
employees?: number;
|
|
||||||
established?: number;
|
|
||||||
reasonForSale?: string;
|
|
||||||
brokerLicencing?: string;
|
|
||||||
internals?: string;
|
|
||||||
}
|
|
||||||
export interface ProfessionalsBrokersListing extends Listing {
|
|
||||||
listingsCategory: 'professionals_brokers'; //enum
|
|
||||||
summary: string;
|
|
||||||
address?: string;
|
|
||||||
email?: string;
|
|
||||||
website?: string;
|
|
||||||
category?: 'Professionals' | 'Broker';
|
|
||||||
}
|
|
||||||
export interface InvestmentsListing extends Listing {
|
|
||||||
listingsCategory: 'investment'; //enum
|
|
||||||
email?: string;
|
|
||||||
website?: string;
|
|
||||||
phoneNumber?: string;
|
|
||||||
}
|
|
||||||
export type ListingType =
|
|
||||||
| BusinessListing
|
|
||||||
| ProfessionalsBrokersListing
|
|
||||||
| InvestmentsListing;
|
|
||||||
|
|
||||||
export interface ListingCriteria {
|
|
||||||
type:string,
|
|
||||||
location:string,
|
|
||||||
minPrice:string,
|
|
||||||
maxPrice:string,
|
|
||||||
realEstateChecked:boolean,
|
|
||||||
listingsCategory:'business'|'professionals_brokers'|'investment',
|
|
||||||
category:'professional|broker'
|
|
||||||
}
|
|
||||||
export interface User {
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
firstname: string;
|
|
||||||
lastname: string;
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
export interface Subscription {
|
|
||||||
id: string;
|
|
||||||
userId:string
|
|
||||||
level: string;
|
|
||||||
start: Date;
|
|
||||||
modified: Date;
|
|
||||||
end: Date;
|
|
||||||
status: string;
|
|
||||||
invoices: Array<Invoice>;
|
|
||||||
}
|
|
||||||
export interface Invoice {
|
|
||||||
id: string,
|
|
||||||
date: Date,
|
|
||||||
price: number
|
|
||||||
}
|
|
||||||
export interface JwtToken {
|
|
||||||
exp: number;
|
|
||||||
iat: number;
|
|
||||||
auth_time: number;
|
|
||||||
jti: string;
|
|
||||||
iss: string;
|
|
||||||
aud: string;
|
|
||||||
sub: string;
|
|
||||||
typ: string;
|
|
||||||
azp: string;
|
|
||||||
nonce: string;
|
|
||||||
session_state: string;
|
|
||||||
acr: string;
|
|
||||||
realm_access: Realmaccess;
|
|
||||||
resource_access: Resourceaccess;
|
|
||||||
scope: string;
|
|
||||||
sid: string;
|
|
||||||
email_verified: boolean;
|
|
||||||
name: string;
|
|
||||||
preferred_username: string;
|
|
||||||
given_name: string;
|
|
||||||
family_name: string;
|
|
||||||
email: string;
|
|
||||||
user_id: string;
|
|
||||||
}
|
|
||||||
interface Resourceaccess {
|
|
||||||
account: Realmaccess;
|
|
||||||
}
|
|
||||||
interface Realmaccess {
|
|
||||||
roles: string[];
|
|
||||||
}
|
|
||||||
export interface PageEvent {
|
|
||||||
first: number;
|
|
||||||
rows: number;
|
|
||||||
page: number;
|
|
||||||
pageCount: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
export interface MailInfo {
|
|
||||||
sender: Sender;
|
|
||||||
userId: string;
|
|
||||||
}
|
|
||||||
export interface Sender {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
phoneNumber: string;
|
|
||||||
state: string;
|
|
||||||
comments: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { RedisService } from './redis.service.js';
|
|
||||||
export declare class RedisController {
|
|
||||||
private redisService;
|
|
||||||
constructor(redisService: RedisService);
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export declare class RedisModule {
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
export declare const LISTINGS = "LISTINGS";
|
|
||||||
export declare const SUBSCRIPTIONS = "SUBSCRIPTIONS";
|
|
||||||
export declare const USERS = "USERS";
|
|
||||||
export declare class RedisService {
|
|
||||||
private redis;
|
|
||||||
setJson(id: string, value: any): Promise<void>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
getJson(id: string, prefix: string): Promise<any>;
|
|
||||||
getId(prefix: 'LISTINGS' | 'SUBSCRIPTIONS' | 'USERS'): Promise<string>;
|
|
||||||
search(prefix: 'LISTINGS' | 'SUBSCRIPTIONS' | 'USERS', clause: string): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { SelectOptionsService } from './select-options.service.js';
|
|
||||||
export declare class SelectOptionsController {
|
|
||||||
private selectOptionsService;
|
|
||||||
constructor(selectOptionsService: SelectOptionsService);
|
|
||||||
getSelectOption(): any;
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { KeyValue, KeyValueStyle } from '../models/main.model.js';
|
|
||||||
export declare class SelectOptionsService {
|
|
||||||
constructor();
|
|
||||||
typesOfBusiness: Array<KeyValueStyle>;
|
|
||||||
prices: Array<KeyValue>;
|
|
||||||
listingCategories: Array<KeyValue>;
|
|
||||||
categories: Array<KeyValueStyle>;
|
|
||||||
private usStates;
|
|
||||||
locations: Array<any>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { FileService } from '../file/file.service.js';
|
|
||||||
export declare class SubscriptionsController {
|
|
||||||
private readonly fileService;
|
|
||||||
constructor(fileService: FileService);
|
|
||||||
findAll(): any;
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export declare function convertStringToNullUndefined(value: any): any;
|
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@nestjs/serve-static": "^4.0.1",
|
"@nestjs/serve-static": "^4.0.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"ioredis": "^5.3.2",
|
|
||||||
"ky": "^1.2.0",
|
"ky": "^1.2.0",
|
||||||
"nest-winston": "^1.9.4",
|
"nest-winston": "^1.9.4",
|
||||||
"nodemailer": "^6.9.10",
|
"nodemailer": "^6.9.10",
|
||||||
|
|
@ -39,6 +38,8 @@
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"redis": "^4.6.13",
|
||||||
|
"redis-om": "^0.4.3",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "^0.2.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"urlcat": "^3.1.0",
|
"urlcat": "^3.1.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AccountController } from './account.controller.js';
|
||||||
|
import { AccountService } from './account.service.js';
|
||||||
|
import { FileService } from '../file/file.service.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [AccountController],
|
||||||
|
providers: [AccountService,FileService]
|
||||||
|
})
|
||||||
|
export class AccountModule {}
|
||||||
|
|
@ -20,11 +20,16 @@ import { utilities as nestWinstonModuleUtilities, WinstonModule } from 'nest-win
|
||||||
import * as winston from 'winston';
|
import * as winston from 'winston';
|
||||||
import { MailModule } from './mail/mail.module.js';
|
import { MailModule } from './mail/mail.module.js';
|
||||||
import { AuthModule } from './auth/auth.module.js';
|
import { AuthModule } from './auth/auth.module.js';
|
||||||
|
import { GeoModule } from './geo/geo.module.js';
|
||||||
|
import { UserModule } from './user/user.module.js';
|
||||||
|
import { ListingsModule } from './listings/listings.module.js';
|
||||||
|
import { AccountModule } from './account/account.module.js';
|
||||||
|
import { SelectOptionsModule } from './select-options/select-options.module.js';
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule.forRoot(), RedisModule, MailModule, AuthModule,
|
imports: [ConfigModule.forRoot(), MailModule, AuthModule,
|
||||||
ServeStaticModule.forRoot({
|
ServeStaticModule.forRoot({
|
||||||
rootPath: join(__dirname, '..', 'public'), // `public` ist das Verzeichnis, wo Ihre statischen Dateien liegen
|
rootPath: join(__dirname, '..', 'public'), // `public` ist das Verzeichnis, wo Ihre statischen Dateien liegen
|
||||||
}),
|
}),
|
||||||
|
|
@ -43,9 +48,15 @@ const __dirname = path.dirname(__filename);
|
||||||
// other transports...
|
// other transports...
|
||||||
],
|
],
|
||||||
// other options
|
// other options
|
||||||
})
|
}),
|
||||||
|
GeoModule,
|
||||||
|
UserModule,
|
||||||
|
ListingsModule,
|
||||||
|
AccountModule,
|
||||||
|
SelectOptionsModule,
|
||||||
|
RedisModule
|
||||||
],
|
],
|
||||||
controllers: [AppController, ListingsController, SelectOptionsController, SubscriptionsController, AccountController],
|
controllers: [AppController, SubscriptionsController],
|
||||||
providers: [AppService, FileService, SelectOptionsService, ListingsService, AccountService],
|
providers: [AppService, FileService],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Controller, Get, Param } from '@nestjs/common';
|
||||||
|
import { GeoService } from './geo.service.js';
|
||||||
|
|
||||||
|
@Controller('geo')
|
||||||
|
export class GeoController {
|
||||||
|
constructor(private geoService:GeoService){}
|
||||||
|
|
||||||
|
@Get(':prefix')
|
||||||
|
findByPrefix(@Param('prefix') prefix:string): any {
|
||||||
|
return this.geoService.findCitiesStartingWith(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':prefix/:state')
|
||||||
|
findByPrefixAndState(@Param('prefix') prefix:string,@Param('state') state:string): any {
|
||||||
|
return this.geoService.findCitiesStartingWith(prefix,state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { GeoController } from './geo.controller.js';
|
||||||
|
import { GeoService } from './geo.service.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [GeoController],
|
||||||
|
providers: [GeoService]
|
||||||
|
})
|
||||||
|
export class GeoModule {}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import path, { join } from 'path';
|
||||||
|
import { City, Geo, State } from 'src/models/server.model.js';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GeoService {
|
||||||
|
geo:Geo;
|
||||||
|
constructor() {
|
||||||
|
this.loadGeo();
|
||||||
|
}
|
||||||
|
private loadGeo(): void {
|
||||||
|
const filePath = join(__dirname,'..', 'assets', 'geo.json');
|
||||||
|
const rawData = readFileSync(filePath, 'utf8');
|
||||||
|
this.geo = JSON.parse(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
findCitiesStartingWith( prefix: string, state?:string): { city: string; state: string; state_code: string }[] {
|
||||||
|
const result: { city: string; state: string; state_code: string }[] = [];
|
||||||
|
|
||||||
|
this.geo.states.forEach((state: State) => {
|
||||||
|
state.cities.forEach((city: City) => {
|
||||||
|
if (city.name.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||||
|
result.push({
|
||||||
|
city: city.name,
|
||||||
|
state: state.name,
|
||||||
|
state_code: state.state_code
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return state ? result.filter(e=>e.state_code.toLowerCase()===state.toLowerCase()) :result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ListingsController } from './listings.controller.js';
|
||||||
|
import { ListingsService } from './listings.service.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [ListingsController],
|
||||||
|
providers: [ListingsService]
|
||||||
|
})
|
||||||
|
export class ListingsModule {}
|
||||||
|
|
@ -5,55 +5,51 @@ import {
|
||||||
ListingCriteria,
|
ListingCriteria,
|
||||||
ProfessionalsBrokersListing,
|
ProfessionalsBrokersListing,
|
||||||
} from '../models/main.model.js';
|
} from '../models/main.model.js';
|
||||||
import { LISTINGS, RedisService } from '../redis/redis.service.js';
|
|
||||||
import { convertStringToNullUndefined } from '../utils.js';
|
import { convertStringToNullUndefined } from '../utils.js';
|
||||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
import { Logger } from 'winston';
|
import { Logger } from 'winston';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ListingsService {
|
export class ListingsService {
|
||||||
// private readonly logger = new Logger(ListingsService.name);
|
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
||||||
constructor(private redisService: RedisService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
|
||||||
|
|
||||||
async setListing(
|
async setListing(
|
||||||
value: BusinessListing | ProfessionalsBrokersListing | InvestmentsListing,
|
value: BusinessListing | ProfessionalsBrokersListing | InvestmentsListing,
|
||||||
id?: string,
|
id?: string,
|
||||||
) {
|
) {
|
||||||
if (!id) {
|
// if (!id) {
|
||||||
id = await this.redisService.getId(LISTINGS);
|
// id = await this.redisService.getId(LISTINGS);
|
||||||
value.id = id;
|
// value.id = id;
|
||||||
this.logger.info(`No ID - creating new one:${id}`)
|
// this.logger.info(`No ID - creating new one:${id}`)
|
||||||
} else {
|
// } else {
|
||||||
this.logger.info(`ID available:${id}`)
|
// this.logger.info(`ID available:${id}`)
|
||||||
}
|
// }
|
||||||
this.redisService.setJson(id, value);
|
//this.redisService.setJson(id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getListingById(id: string) {
|
async getListingById(id: string) {
|
||||||
return await this.redisService.getJson(id, LISTINGS);
|
//return await this.redisService.getJson(id, LISTINGS);
|
||||||
}
|
}
|
||||||
deleteListing(id: string){
|
deleteListing(id: string){
|
||||||
this.redisService.delete(id);
|
//this.redisService.delete(id);
|
||||||
this.logger.info(`delete listing with ID:${id}`)
|
this.logger.info(`delete listing with ID:${id}`)
|
||||||
}
|
}
|
||||||
async getAllListings(start?: number, end?: number) {
|
async getAllListings(start?: number, end?: number) {
|
||||||
const searchResult = await this.redisService.search(LISTINGS, '*');
|
// const searchResult = await this.redisService.search(LISTINGS, '*');
|
||||||
const listings = searchResult.slice(1).reduce((acc, item, index, array) => {
|
// const listings = searchResult.slice(1).reduce((acc, item, index, array) => {
|
||||||
// Jedes zweite Element (beginnend mit dem ersten) ist ein JSON-String des Listings
|
// if (index % 2 === 1) {
|
||||||
if (index % 2 === 1) {
|
// try {
|
||||||
try {
|
// const listing = JSON.parse(item[1]);
|
||||||
const listing = JSON.parse(item[1]); // Parsen des JSON-Strings
|
// acc.push(listing);
|
||||||
acc.push(listing);
|
// } catch (error) {
|
||||||
} catch (error) {
|
// console.error('Fehler beim Parsen des JSON-Strings: ', error);
|
||||||
console.error('Fehler beim Parsen des JSON-Strings: ', error);
|
// }
|
||||||
|
// }
|
||||||
|
// return acc;
|
||||||
|
// }, []);
|
||||||
|
// return listings;
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
return listings;
|
|
||||||
}
|
|
||||||
//criteria.type,criteria.location,criteria.minPrice,criteria.maxPrice,criteria.realEstateChecked,criteria.listingsCategory
|
|
||||||
//async find(type:string,location:string,minPrice:string,maxPrice:string,realEstateChecked:boolean,listingsCategory:string): Promise<any> {
|
|
||||||
async find(criteria:ListingCriteria): Promise<any> {
|
async find(criteria:ListingCriteria): Promise<any> {
|
||||||
let listings = await this.getAllListings();
|
let listings = await this.getAllListings();
|
||||||
listings=listings.filter(l=>l.listingsCategory===criteria.listingsCategory);
|
listings=listings.filter(l=>l.listingsCategory===criteria.listingsCategory);
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,16 @@ const __dirname = path.dirname(__filename);
|
||||||
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
|
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
|
||||||
// or
|
// or
|
||||||
transport: {
|
transport: {
|
||||||
host: 'smtp.gmail.com',
|
host: 'email-smtp.us-east-2.amazonaws.com',
|
||||||
secure: true,
|
secure: false,
|
||||||
|
port:587,
|
||||||
|
// auth: {
|
||||||
|
// user: 'andreas.knuth@gmail.com',
|
||||||
|
// pass: 'ksnh xjae dqbv xana',
|
||||||
|
// },
|
||||||
auth: {
|
auth: {
|
||||||
user: 'andreas.knuth@gmail.com',
|
user: 'AKIAU6GDWVAQ2QNFLNWN',
|
||||||
pass: 'ksnh xjae dqbv xana',
|
pass: 'BDE9nZv/ARbpotim1mIOir52WgIbpSi9cv1oJoH8oEf7',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,80 @@
|
||||||
|
import { Entity } from "redis-om";
|
||||||
|
import { UserBase } from "./main.model.js";
|
||||||
|
|
||||||
export interface MailInfo {
|
export interface MailInfo {
|
||||||
sender:Sender;
|
sender: Sender;
|
||||||
userId:string;
|
userId: string;
|
||||||
}
|
}
|
||||||
export interface Sender {
|
export interface Sender {
|
||||||
name:string;
|
name: string;
|
||||||
email:string;
|
email: string;
|
||||||
phoneNumber:string;
|
phoneNumber: string;
|
||||||
state:string;
|
state: string;
|
||||||
comments:string;
|
comments: string;
|
||||||
|
}
|
||||||
|
export interface Geo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
iso3: string;
|
||||||
|
iso2: string;
|
||||||
|
numeric_code: string;
|
||||||
|
phone_code: string;
|
||||||
|
capital: string;
|
||||||
|
currency: string;
|
||||||
|
currency_name: string;
|
||||||
|
currency_symbol: string;
|
||||||
|
tld: string;
|
||||||
|
native: string;
|
||||||
|
region: string;
|
||||||
|
region_id: string;
|
||||||
|
subregion: string;
|
||||||
|
subregion_id: string;
|
||||||
|
nationality: string;
|
||||||
|
timezones: Timezone[];
|
||||||
|
translations: Translations;
|
||||||
|
latitude: string;
|
||||||
|
longitude: string;
|
||||||
|
emoji: string;
|
||||||
|
emojiU: string;
|
||||||
|
states: State[];
|
||||||
|
}
|
||||||
|
export interface State {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
state_code: string;
|
||||||
|
latitude: string;
|
||||||
|
longitude: string;
|
||||||
|
type: string;
|
||||||
|
cities: City[];
|
||||||
|
}
|
||||||
|
export interface City {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
latitude: string;
|
||||||
|
longitude: string;
|
||||||
|
}
|
||||||
|
export interface Translations {
|
||||||
|
kr: string;
|
||||||
|
'pt-BR': string;
|
||||||
|
pt: string;
|
||||||
|
nl: string;
|
||||||
|
hr: string;
|
||||||
|
fa: string;
|
||||||
|
de: string;
|
||||||
|
es: string;
|
||||||
|
fr: string;
|
||||||
|
ja: string;
|
||||||
|
it: string;
|
||||||
|
cn: string;
|
||||||
|
tr: string;
|
||||||
|
}
|
||||||
|
export interface Timezone {
|
||||||
|
zoneName: string;
|
||||||
|
gmtOffset: number;
|
||||||
|
gmtOffsetName: string;
|
||||||
|
abbreviation: string;
|
||||||
|
tzName: string;
|
||||||
|
}
|
||||||
|
export interface UserEntity extends UserBase, Entity {
|
||||||
|
licensedIn?: string[];
|
||||||
}
|
}
|
||||||
|
|
@ -6,13 +6,4 @@ import { RedisService } from './redis.service.js';
|
||||||
export class RedisController {
|
export class RedisController {
|
||||||
constructor(private redisService:RedisService){}
|
constructor(private redisService:RedisService){}
|
||||||
|
|
||||||
// @Get(':id')
|
|
||||||
// getById(@Param('id') id:string){
|
|
||||||
// return this.redisService.getListingById(id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Post(':id')
|
|
||||||
// updateById(@Body() listing: any){
|
|
||||||
// this.redisService.setListing(listing);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,30 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [RedisService],
|
providers: [
|
||||||
exports: [RedisService],
|
{
|
||||||
controllers: [RedisController],
|
provide: 'REDIS_OPTIONS',
|
||||||
|
useValue: {
|
||||||
|
url: 'redis://localhost:6379'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inject: ['REDIS_OPTIONS'],
|
||||||
|
provide: 'REDIS_CLIENT',
|
||||||
|
useFactory: async (options: { url: string }) => {
|
||||||
|
const client = createClient(options);
|
||||||
|
await client.connect();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
exports:['REDIS_CLIENT']
|
||||||
})
|
})
|
||||||
export class RedisModule {}
|
export class RedisModule {}
|
||||||
|
export const REDIS_CLIENT = "REDIS_CLIENT";
|
||||||
// redis.service.ts
|
// redis.service.ts
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { RedisService } from './redis.service.js';
|
import { RedisService } from './redis.service.js';
|
||||||
import { RedisController } from './redis.controller.js';
|
import { RedisController } from './redis.controller.js';
|
||||||
|
import { createClient } from 'redis';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,50 @@
|
||||||
// redis.service.ts
|
// redis.service.ts
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Redis } from 'ioredis';
|
|
||||||
import { BusinessListing, InvestmentsListing,ProfessionalsBrokersListing } from '../models/main.model.js';
|
import { BusinessListing, InvestmentsListing,ProfessionalsBrokersListing } from '../models/main.model.js';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import { createClient } from 'redis';
|
||||||
|
|
||||||
export const LISTINGS = 'LISTINGS';
|
export const LISTINGS = 'LISTINGS';
|
||||||
export const SUBSCRIPTIONS = 'SUBSCRIPTIONS';
|
export const SUBSCRIPTIONS = 'SUBSCRIPTIONS';
|
||||||
export const USERS = 'USERS'
|
export const USERS = 'USERS'
|
||||||
|
export const redis = createClient({ url: 'redis://localhost:6379' })
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RedisService {
|
export class RedisService {
|
||||||
// private redis = new Redis(); // Verbindungsparameter nach Bedarf anpassen
|
//private redis = new Redis(); // Verbindungsparameter nach Bedarf anpassen
|
||||||
private redis = new Redis({
|
// private redis = new Redis({
|
||||||
port: 6379, // Der TLS-Port von Redis
|
// port: 6379, // Der TLS-Port von Redis
|
||||||
host: '2.56.188.138',
|
//host: '2.56.188.138',
|
||||||
password: 'bizmatchRedis:5600Wuppertal11', // ACL Benutzername und Passwort
|
// host: '127.0.0.1',
|
||||||
|
//password: 'bizmatchRedis:5600Wuppertal11', // ACL Benutzername und Passwort
|
||||||
// tls: {
|
// tls: {
|
||||||
// key: fs.readFileSync('/home/aknuth/ssl/private/redis.key'),
|
// key: fs.readFileSync('/home/aknuth/ssl/private/redis.key'),
|
||||||
// cert: fs.readFileSync('/home/aknuth/ssl/certs/redis.crt')
|
// cert: fs.readFileSync('/home/aknuth/ssl/certs/redis.crt')
|
||||||
// }
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ######################################
|
// ######################################
|
||||||
// general methods
|
// general methods
|
||||||
// ######################################
|
// ######################################
|
||||||
async setJson(id: string, value: any): Promise<void> {
|
// async setJson(id: string, value: any): Promise<void> {
|
||||||
await this.redis.call("JSON.SET", `${LISTINGS}:${id}`, "$", JSON.stringify(value));
|
// await this.redis.call("JSON.SET", `${LISTINGS}:${id}`, "$", JSON.stringify(value));
|
||||||
}
|
// }
|
||||||
async delete(id: string): Promise<void> {
|
// async delete(id: string): Promise<void> {
|
||||||
await this.redis.del(`${LISTINGS}:${id}`);
|
// await this.redis.del(`${LISTINGS}:${id}`);
|
||||||
}
|
// }
|
||||||
async getJson(id: string, prefix:string): Promise<any> {
|
// async getJson(id: string, prefix:string): Promise<any> {
|
||||||
const result:string = await this.redis.call("JSON.GET", `${prefix}:${id}`) as string;
|
// const result:string = await this.redis.call("JSON.GET", `${prefix}:${id}`) as string;
|
||||||
return JSON.parse(result);
|
// return JSON.parse(result);
|
||||||
}
|
// }
|
||||||
|
|
||||||
async getId(prefix:'LISTINGS'|'SUBSCRIPTIONS'|'USERS'):Promise<string>{
|
// async getId(prefix:'LISTINGS'|'SUBSCRIPTIONS'|'USERS'):Promise<string>{
|
||||||
const counter = await this.redis.call("INCR",`${prefix}_ID_COUNTER`) as number;
|
// const counter = await this.redis.call("INCR",`${prefix}_ID_COUNTER`) as number;
|
||||||
return counter.toString().padStart(15, '0')
|
// return counter.toString().padStart(15, '0')
|
||||||
}
|
// }
|
||||||
|
|
||||||
async search(prefix:'LISTINGS'|'SUBSCRIPTIONS'|'USERS',clause:string):Promise<any>{
|
// async search(prefix:'LISTINGS'|'SUBSCRIPTIONS'|'USERS',clause:string):Promise<any>{
|
||||||
const result = await this.redis.call(`FT.SEARCH`, `${prefix}_INDEX`, `${clause}`, 'LIMIT', 0, 200);
|
// const result = await this.redis.call(`FT.SEARCH`, `${prefix}_INDEX`, `${clause}`, 'LIMIT', 0, 200);
|
||||||
return result;
|
// return result;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { SelectOptionsController } from './select-options.controller.js';
|
||||||
|
import { SelectOptionsService } from './select-options.service.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [SelectOptionsController],
|
||||||
|
providers: [SelectOptionsService]
|
||||||
|
})
|
||||||
|
export class SelectOptionsModule {}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Body, Controller, Get, Inject, Param, Post, Put } from '@nestjs/common';
|
||||||
|
import { UserService } from './user.service.js';
|
||||||
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||||
|
import { Logger } from 'winston';
|
||||||
|
import { UserEntity } from 'src/models/server.model.js';
|
||||||
|
|
||||||
|
@Controller('user')
|
||||||
|
export class UserController {
|
||||||
|
|
||||||
|
constructor(private userService:UserService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger){}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
findById(@Param('id') id:string): any {
|
||||||
|
return this.userService.getUserById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
save(@Body() user: any):Promise<UserEntity>{
|
||||||
|
this.logger.info(`User persisted: `);
|
||||||
|
return this.userService.saveUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Put()
|
||||||
|
// update(@Body() user: any):Promise<UserEntity>{
|
||||||
|
// this.logger.info(`update User`);
|
||||||
|
// return this.userService.saveUser(user);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserController } from './user.controller.js';
|
||||||
|
import { UserService } from './user.service.js';
|
||||||
|
import { RedisModule } from '../redis/redis.module.js';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [RedisModule],
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [UserService]
|
||||||
|
})
|
||||||
|
export class UserModule {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Get, Inject, Injectable, Param } from '@nestjs/common';
|
||||||
|
import { createClient } from 'redis';
|
||||||
|
import { Entity, Repository, Schema } from 'redis-om';
|
||||||
|
import { User } from '../models/main.model.js';
|
||||||
|
import { REDIS_CLIENT } from '../redis/redis.module.js';
|
||||||
|
import { UserEntity } from '../models/server.model.js';
|
||||||
|
// export const redis = createClient({ url: 'redis://localhost:6379' })
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
userRepository:Repository;
|
||||||
|
userSchema = new Schema('user',{
|
||||||
|
// id: string;
|
||||||
|
firstname: { type: 'string' },
|
||||||
|
lastname: { type: 'string' },
|
||||||
|
email: { type: 'string' },
|
||||||
|
phoneNumber: { type: 'string' },
|
||||||
|
companyOverview:{ type: 'string' },
|
||||||
|
companyWebsite:{ type: 'string' },
|
||||||
|
companyLocation:{ type: 'string' },
|
||||||
|
offeredServices:{ type: 'string' },
|
||||||
|
areasServed:{ type: 'string' },
|
||||||
|
names:{ type: 'string[]', path:'$.licensedIn.name' },
|
||||||
|
values:{ type: 'string[]', path:'$.licensedIn.value' }
|
||||||
|
}, {
|
||||||
|
dataStructure: 'JSON'
|
||||||
|
})
|
||||||
|
constructor(@Inject(REDIS_CLIENT) private readonly client: any){
|
||||||
|
// const redis = createClient({ url: 'redis://localhost:6379' })
|
||||||
|
this.userRepository = new Repository(this.userSchema, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserById( id:string){
|
||||||
|
return await this.userRepository.fetch(id);
|
||||||
|
}
|
||||||
|
async saveUser(user:any):Promise<UserEntity>{
|
||||||
|
return await this.userRepository.save(user.id,user) as UserEntity
|
||||||
|
}
|
||||||
|
// createUser(){
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// updateById(id:string){
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="col-12 md:col-6">
|
<div class="col-12 md:col-6">
|
||||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
||||||
@if (listing && (listing.listingsCategory==='business' || listing.listingsCategory==='professionals_brokers')){
|
<!-- @if (listing && (listing.listingsCategory==='business')){
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Summary</div>
|
<div class="text-500 w-full md:w-2 font-medium">Summary</div>
|
||||||
<div class="w-full md:w-10">
|
<div class="w-full md:w-10">
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
} -->
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
||||||
<div class="text-900 w-full md:w-10 line-height-3">{{listing?.description}}</div>
|
<div class="text-900 w-full md:w-10 line-height-3">{{listing?.description}}</div>
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
<div class="text-900 w-full md:w-10">{{selectOptions.getState(listing.state)}}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Asking Price</div>
|
<div class="text-500 w-full md:w-2 font-medium">Asking Price</div>
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
<div class="text-900 w-full md:w-10">{{listing.brokerLicencing}}</div>
|
<div class="text-900 w-full md:w-10">{{listing.brokerLicencing}}</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if (listing && (listing.listingsCategory==='professionals_brokers')){
|
<!-- @if (listing && (listing.listingsCategory==='professionals_brokers')){
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
||||||
|
|
@ -84,11 +84,11 @@
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Category</div>
|
<div class="text-500 w-full md:w-2 font-medium">Category</div>
|
||||||
<div class="text-900 w-full md:w-10">{{listing.category}}</div>
|
<div class="text-900 w-full md:w-10">{{listing.category}}</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
} -->
|
||||||
@if (listing && (listing.listingsCategory==='investment')){
|
@if (listing && (listing.listingsCategory==='investment')){
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
<div class="text-500 w-full md:w-2 font-medium">Located in</div>
|
||||||
<div class="text-900 w-full md:w-10">{{selectOptions.getLocation(listing.location)}}</div>
|
<div class="text-900 w-full md:w-10">{{selectOptions.getState(listing.state)}}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||||
<div class="text-500 w-full md:w-2 font-medium">EMail</div>
|
<div class="text-500 w-full md:w-2 font-medium">EMail</div>
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="locations" [(ngModel)]="location" optionLabel="criteria.location" optionLabel="name" optionValue="value"
|
<p-dropdown [options]="states" [(ngModel)]="state" optionLabel="criteria.location" optionLabel="name" optionValue="value"
|
||||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
[showClear]="true" placeholder="State" [style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name"
|
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name"
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
}
|
}
|
||||||
@if (listingCategory==='investment'){
|
@if (listingCategory==='investment'){
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="locations" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
<p-dropdown [options]="states" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
||||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<p-dropdown [options]="locations" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
<p-dropdown [options]="states" [(ngModel)]="criteria.location" optionLabel="name" optionValue="value"
|
||||||
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
[showClear]="true" placeholder="Location" [style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -94,17 +94,17 @@
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Asking price: {{listing.price | currency}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Asking price: {{listing.price | currency}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Sales revenue: {{listing.salesRevenue | currency}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Sales revenue: {{listing.salesRevenue | currency}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Net profit: {{listing.cashFlow | currency}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Net profit: {{listing.cashFlow | currency}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getState(listing.state)}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{listing.established}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Established: {{listing.established}}</p>
|
||||||
}
|
}
|
||||||
@if (listing.listingsCategory==='professionals_brokers'){
|
@if (listing.listingsCategory==='professionals_brokers'){
|
||||||
<!-- <p class="mt-0 mb-1 text-700 line-height-3">Category: {{listing.category}}</p> -->
|
<!-- <p class="mt-0 mb-1 text-700 line-height-3">Category: {{listing.category}}</p> -->
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getState(listing.state)}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
||||||
}
|
}
|
||||||
@if (listing.listingsCategory==='investment'){
|
@if (listing.listingsCategory==='investment'){
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getLocation(listing.location)}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Location: {{selectOptions.getState(listing.state)}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">EMail: {{listing.email}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Website: {{listing.website}}</p>
|
||||||
<p class="mt-0 mb-1 text-700 line-height-3">Phone Number: {{listing.phoneNumber}}</p>
|
<p class="mt-0 mb-1 text-700 line-height-3">Phone Number: {{listing.phoneNumber}}</p>
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ export class ListingsComponent {
|
||||||
maxPrice: string;
|
maxPrice: string;
|
||||||
minPrice: string;
|
minPrice: string;
|
||||||
type:string;
|
type:string;
|
||||||
locations = [];
|
states = [];
|
||||||
locationsSet = new Set();
|
statesSet = new Set();
|
||||||
location:string;
|
state:string;
|
||||||
first: number = 0;
|
first: number = 0;
|
||||||
rows: number = 12;
|
rows: number = 12;
|
||||||
totalRecords:number = 0;
|
totalRecords:number = 0;
|
||||||
|
|
@ -61,25 +61,25 @@ export class ListingsComponent {
|
||||||
}
|
}
|
||||||
async init(){
|
async init(){
|
||||||
this.listings=await this.listingsService.getListings(this.criteria);
|
this.listings=await this.listingsService.getListings(this.criteria);
|
||||||
this.setLocations();
|
this.setStates();
|
||||||
this.filteredListings=[...this.listings];
|
this.filteredListings=[...this.listings];
|
||||||
this.totalRecords=this.listings.length
|
this.totalRecords=this.listings.length
|
||||||
this.filteredListings=[...this.listings].splice(this.first,this.rows);
|
this.filteredListings=[...this.listings].splice(this.first,this.rows);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
}
|
}
|
||||||
setLocations(){
|
setStates(){
|
||||||
this.locationsSet=new Set();
|
this.statesSet=new Set();
|
||||||
this.listings.forEach(l=>{
|
this.listings.forEach(l=>{
|
||||||
if (l.location){
|
if (l.state){
|
||||||
this.locationsSet.add(l.location)
|
this.statesSet.add(l.state)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.locations = [...this.locationsSet].map((ls) =>({name:this.selectOptions.getLocation(ls as string),value:ls}))
|
this.states = [...this.statesSet].map((ls) =>({name:this.selectOptions.getState(ls as string),value:ls}))
|
||||||
}
|
}
|
||||||
async search() {
|
async search() {
|
||||||
this.listings= await this.listingsService.getListings(this.criteria);
|
this.listings= await this.listingsService.getListings(this.criteria);
|
||||||
this.setLocations();
|
this.setStates();
|
||||||
this.totalRecords=this.listings.length
|
this.totalRecords=this.listings.length
|
||||||
this.filteredListings =[...this.listings].splice(this.first,this.rows);
|
this.filteredListings =[...this.listings].splice(this.first,this.rows);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
|
||||||
|
|
@ -8,41 +8,90 @@
|
||||||
<p-divider></p-divider>
|
<p-divider></p-divider>
|
||||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||||
<div class="flex-auto p-fluid">
|
<div class="flex-auto p-fluid">
|
||||||
<div class="mb-4">
|
<!-- <div class="mb-4">
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Username</label>
|
<label for="email" class="block font-medium text-900 mb-2">Username</label>
|
||||||
<input id="email" type="text" [disabled]="true" pInputText [(ngModel)]="user.username">
|
<input id="email" type="text" [disabled]="true" pInputText [(ngModel)]="user.username">
|
||||||
<p class="font-italic text-sm line-height-1">Usernames cannot be changed.</p>
|
<p class="font-italic text-sm line-height-1">Usernames cannot be changed.</p>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="mb-4">
|
|
||||||
<label for="state" class="block font-medium text-900 mb-2">First Name</label>
|
|
||||||
<input id="state" type="text" pInputText [(ngModel)]="user.firstname">
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="state" class="block font-medium text-900 mb-2">Last Name</label>
|
|
||||||
<input id="state" type="text" pInputText [(ngModel)]="user.lastname">
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="state" class="block font-medium text-900 mb-2">E-mail (required)</label>
|
<label for="state" class="block font-medium text-900 mb-2">E-mail (required)</label>
|
||||||
<input id="state" type="text" pInputText [(ngModel)]="user.email">
|
<input id="state" type="text" [disabled]="true" pInputText [(ngModel)]="user.email">
|
||||||
|
<p class="font-italic text-sm line-height-1">You can only modify your email by contacting us at emailchange@bizmatch.net</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
|
<label for="firstname" class="block font-medium text-900 mb-2">First Name</label>
|
||||||
|
<input id="firstname" type="text" pInputText [(ngModel)]="user.firstname">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="lastname" class="block font-medium text-900 mb-2">Last Name</label>
|
||||||
|
<input id="lastname" type="text" pInputText [(ngModel)]="user.lastname">
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-4">
|
||||||
|
<label for="phoneNumber" class="block font-medium text-900 mb-2">Your Phone Number</label>
|
||||||
|
<input id="phoneNumber" type="text" pInputText [(ngModel)]="user.phoneNumber">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-4">
|
||||||
|
<label for="companyWebsite" class="block font-medium text-900 mb-2">Company Website</label>
|
||||||
|
<input id="companyWebsite" type="text" pInputText [(ngModel)]="user.companyWebsite">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-4">
|
||||||
|
<label for="companyLocation" class="block font-medium text-900 mb-2">Company Location</label>
|
||||||
|
<p-autoComplete [(ngModel)]="user.companyLocation" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="companyOverview" class="block font-medium text-900 mb-2">Company Overview</label>
|
||||||
|
<textarea id="companyOverview" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="user.companyOverview"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="areasServed" class="block font-medium text-900 mb-2">Areas We Serve</label>
|
||||||
|
<textarea id="areasServed" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="user.areasServed"></textarea>
|
||||||
|
</div>
|
||||||
|
<div >
|
||||||
|
<label for="companyOverview" class="block font-medium text-900 mb-2">Licensed In</label>
|
||||||
|
@for (licensedIn of user.licensedIn; track licensedIn.value){
|
||||||
|
<div class="grid">
|
||||||
|
<div class="flex col-12 md:col-6">
|
||||||
|
<p-dropdown id="states" [options]="selectOptions?.states" [(ngModel)]="licensedIn.name" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [ngStyle]="{ width: '100%'}"></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="flex col-12 md:col-6">
|
||||||
|
<input id="companyWebsite" type="text" pInputText [(ngModel)]="licensedIn.value" placeholder="Licence Number">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="field mb-5 col-12 md:col-6 flex align-items-center">
|
||||||
|
<p-button class="mr-1" icon="pi pi-plus" severity="success" (click)="addLicence()"></p-button>
|
||||||
|
<p-button icon="pi pi-minus" severity="danger" (click)="removeLicence()" [disabled]="user.licensedIn?.length<2"></p-button>
|
||||||
|
<span class="text-xs"> (Add more licenses or remove existing ones.)</span>
|
||||||
|
<!-- <button pButton pRipple label="Add Licence" class="w-auto" (click)="addLicence()"></button> -->
|
||||||
|
</div>
|
||||||
|
<!-- <div class="mb-4">
|
||||||
<label for="state" class="block font-medium text-900 mb-2">New Password</label>
|
<label for="state" class="block font-medium text-900 mb-2">New Password</label>
|
||||||
<p class="font-italic text-sm line-height-1">If you would like to change the password type a new one. Otherwise leave this blank.</p>
|
<p class="font-italic text-sm line-height-1">If you would like to change the password type a new one. Otherwise leave this blank.</p>
|
||||||
<input id="state" type="text" pInputText>
|
<input id="state" type="text" pInputText>
|
||||||
<p class="font-italic text-sm line-height-1">Password repetition</p>
|
<p class="font-italic text-sm line-height-1">Password repetition</p>
|
||||||
<input id="state" type="text" pInputText>
|
<input id="state" type="text" pInputText>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button pButton pRipple label="Update Profile" class="w-auto" (click)="updateProfile(user)"></button>
|
<button pButton pRipple label="Update Profile" class="w-auto" (click)="updateProfile(user)"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-column align-items-center flex-or mb-8">
|
||||||
|
<span class="font-medium text-900 mb-2">Company Logo</span>
|
||||||
|
<span class="font-medium text-xs mb-2">(is shown in every offer)</span>
|
||||||
|
<img [src]="companyLogoUrl" (error)="setImageToFallback($event)" class="rounded-image"/>
|
||||||
|
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUploadCompanyLogo($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||||
|
</div>
|
||||||
|
<p-divider></p-divider>
|
||||||
<div class="flex flex-column align-items-center flex-or">
|
<div class="flex flex-column align-items-center flex-or">
|
||||||
<span class="font-medium text-900 mb-2">Profile Picture</span>
|
<span class="font-medium text-900 mb-2">Your Profile Picture</span>
|
||||||
<img [src]="imageUrl" (error)="setImageToFallback($event)" class="rounded-image"/>
|
<img [src]="profileUrl" (error)="setImageToFallback($event)" class="rounded-image"/>
|
||||||
<!-- <p-image src="http://localhost:3000/public/user.png" alt="Image" width="10rem" [preview]="true"></p-image> -->
|
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUploadProfilePicture($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||||
<!-- <button pButton type="button" icon="pi pi-pencil" class="p-button-rounded -mt-4"></button> -->
|
</div>
|
||||||
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUpload($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-900 font-semibold text-lg mt-3">Membership Level</div>
|
<div class="text-900 font-semibold text-lg mt-3">Membership Level</div>
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wfull{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component } from '@angular/core';
|
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { ButtonModule } from 'primeng/button';
|
import { ButtonModule } from 'primeng/button';
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
import { CheckboxModule } from 'primeng/checkbox';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
|
|
@ -23,7 +23,9 @@ import { lastValueFrom } from 'rxjs';
|
||||||
import { MessageService } from 'primeng/api';
|
import { MessageService } from 'primeng/api';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { FileUploadModule } from 'primeng/fileupload';
|
import { FileUploadModule } from 'primeng/fileupload';
|
||||||
import { Invoice, Subscription, User } from '../../../../../../common-models/src/main.model';
|
import { AutoCompleteCompleteEvent, Invoice, Subscription, User } from '../../../../../../common-models/src/main.model';
|
||||||
|
import { GeoService } from '../../../services/geo.service';
|
||||||
|
import { ChangeDetectionStrategy } from '@angular/compiler';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -41,24 +43,58 @@ export class AccountComponent {
|
||||||
userSubscriptions:Array<Subscription>=[];
|
userSubscriptions:Array<Subscription>=[];
|
||||||
uploadUrl:string;
|
uploadUrl:string;
|
||||||
maxFileSize=1000000;
|
maxFileSize=1000000;
|
||||||
imageUrl:string;
|
companyLogoUrl:string;
|
||||||
constructor(public userService: UserService, private subscriptionService: SubscriptionsService,private messageService: MessageService) {
|
profileUrl:string;
|
||||||
|
constructor(public userService: UserService,
|
||||||
|
private subscriptionService: SubscriptionsService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private geoService:GeoService,
|
||||||
|
public selectOptions:SelectOptionsService,
|
||||||
|
private cdref:ChangeDetectorRef) {
|
||||||
this.user=this.userService.getUser()
|
this.user=this.userService.getUser()
|
||||||
|
|
||||||
}
|
}
|
||||||
async ngOnInit(){
|
async ngOnInit(){
|
||||||
this.imageUrl = `${environment.apiBaseUrl}/profile_${this.user.id}`
|
this.profileUrl = `${environment.apiBaseUrl}/profile_${this.user.id}`
|
||||||
|
this.companyLogoUrl = `${environment.apiBaseUrl}/profile_${this.user.id}`
|
||||||
this.userSubscriptions=await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
this.userSubscriptions=await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/account/uploadPhoto/${this.user.id}`;
|
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/account/uploadPhoto/${this.user.id}`;
|
||||||
|
if (!this.user.licensedIn || this.user.licensedIn?.length===0){
|
||||||
|
this.user.licensedIn = [{name:'',value:''}]
|
||||||
|
}
|
||||||
|
this.user=await this.userService.getById(this.user.id);
|
||||||
}
|
}
|
||||||
printInvoice(invoice:Invoice){}
|
printInvoice(invoice:Invoice){}
|
||||||
updateProfile(user:User){
|
|
||||||
this.messageService.add({ severity: 'warn', summary: 'Information', detail: 'This function is not yet available, please send an email to info@bizmatch.net for changes to your customer data', life: 15000 });
|
async updateProfile(user:User){
|
||||||
|
//this.messageService.add({ severity: 'warn', summary: 'Information', detail: 'This function is not yet available, please send an email to info@bizmatch.net for changes to your customer data', life: 15000 });
|
||||||
|
await this.userService.save(this.user);
|
||||||
}
|
}
|
||||||
onUpload(event:any){
|
|
||||||
|
|
||||||
|
onUploadCompanyLogo(event:any){
|
||||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||||
this.imageUrl = `${environment.apiBaseUrl}/profile_${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
this.companyLogoUrl = `${environment.apiBaseUrl}/company_${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
||||||
|
}
|
||||||
|
onUploadProfilePicture(event:any){
|
||||||
|
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||||
|
this.profileUrl = `${environment.apiBaseUrl}/profile_${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
||||||
}
|
}
|
||||||
setImageToFallback(event: Event) {
|
setImageToFallback(event: Event) {
|
||||||
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild
|
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
suggestions: string[] | undefined;
|
||||||
|
|
||||||
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query))
|
||||||
|
this.suggestions = result.map(r=>`${r.city} - ${r.state_code}`).slice(0,5);
|
||||||
|
}
|
||||||
|
addLicence(){
|
||||||
|
this.user.licensedIn.push({name:'',value:''});
|
||||||
|
}
|
||||||
|
removeLicence(){
|
||||||
|
this.user.licensedIn.splice(this.user.licensedIn.length-2,1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,6 @@
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
||||||
<input id="email" type="text" pInputText [(ngModel)]="listing.title">
|
<input id="email" type="text" pInputText [(ngModel)]="listing.title">
|
||||||
</div>
|
</div>
|
||||||
@if (listing.listingsCategory==='business' || listing.listingsCategory==='professionals_brokers'){
|
|
||||||
<div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="summary" class="block font-medium text-900 mb-2">Summary (Brief description)</label>
|
|
||||||
<textarea id="summary" type="text" pInputTextarea rows="5" [autoResize]="true" [ngModel]="listing.summary | arrayToString:'\n\n'" (ngModelChange)="updateSummary($event)"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
||||||
|
|
@ -40,21 +32,22 @@
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="mb-4">
|
<div class="grid">
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">Location</label>
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<p-dropdown id="listingCategory" [options]="selectOptions?.locations" [(ngModel)]="listing.location" optionLabel="name"
|
<label for="listingCategory" class="block font-medium text-900 mb-2">State</label>
|
||||||
optionValue="value" [showClear]="true" placeholder="Location"
|
<p-dropdown id="listingCategory" [options]="selectOptions?.states" [(ngModel)]="listing.state" optionLabel="name"
|
||||||
|
optionValue="value" [showClear]="true" placeholder="State"
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[style]="{ width: '100%'}"></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@if (listing.listingsCategory==='professionals_brokers'){
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<div>
|
<label for="listingCategory" class="block font-medium text-900 mb-2">City</label>
|
||||||
<div class="mb-4">
|
<!-- <p-dropdown id="listingCategory" [options]="selectOptions?.states" [(ngModel)]="listing.city" optionLabel="name"
|
||||||
<label for="address" class="block font-medium text-900 mb-2">Address</label>
|
optionValue="value" [showClear]="true" placeholder="State"
|
||||||
<input id="address" type="text" pInputText [(ngModel)]="listing.address">
|
[style]="{ width: '100%'}"></p-dropdown> -->
|
||||||
|
<p-autoComplete [(ngModel)]="listing.city" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<!-- @if (listing.listingsCategory==='investment'){
|
||||||
@if (listing.listingsCategory==='professionals_brokers' || listing.listingsCategory==='investment'){
|
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Email</label>
|
<label for="email" class="block font-medium text-900 mb-2">Email</label>
|
||||||
|
|
@ -67,23 +60,13 @@
|
||||||
<input id="address" type="text" pInputText [(ngModel)]="listing.website">
|
<input id="address" type="text" pInputText [(ngModel)]="listing.website">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
@if (listing.listingsCategory==='professionals_brokers'){
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="category" class="block font-medium text-900 mb-2">Category</label>
|
|
||||||
<p-dropdown id="category" [options]="selectOptions?.categories" [(ngModel)]="listing.category" optionLabel="name"
|
|
||||||
optionValue="value" [showClear]="true" placeholder="Category"
|
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (listing.listingsCategory==='investment'){
|
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="phoneNumber" class="block font-medium text-900 mb-2">Phone Number</label>
|
<label for="phoneNumber" class="block font-medium text-900 mb-2">Phone Number</label>
|
||||||
<input id="phoneNumber" type="text" pInputText [(ngModel)]="listing.phoneNumber">
|
<input id="phoneNumber" type="text" pInputText [(ngModel)]="listing.phoneNumber">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
} -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p-divider></p-divider>
|
<p-divider></p-divider>
|
||||||
|
|
@ -93,54 +76,76 @@
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||||
<!-- <input id="price" type="text" pInputText [(ngModel)]="listing.price"> -->
|
|
||||||
<p-inputNumber mode="currency" currency="USD" inputId="price" type="text" [(ngModel)]="listing.price"></p-inputNumber>
|
<p-inputNumber mode="currency" currency="USD" inputId="price" type="text" [(ngModel)]="listing.price"></p-inputNumber>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 col-12 md:col-6 flex align-items-end justify-content-center">
|
|
||||||
<p-checkbox [binary]="true" [(ngModel)]="listing.realEstateIncluded"></p-checkbox>
|
|
||||||
<span class="ml-2 text-900">Real Estate Included</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
||||||
<!-- <input id="salesRevenue" type="text" pInputText [(ngModel)]="listing.salesRevenue"> -->
|
|
||||||
<p-inputNumber mode="currency" currency="USD" inputId="salesRevenue" type="text" [(ngModel)]="listing.salesRevenue"></p-inputNumber>
|
<p-inputNumber mode="currency" currency="USD" inputId="salesRevenue" type="text" [(ngModel)]="listing.salesRevenue"></p-inputNumber>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
|
||||||
<!-- <input id="cashFlow" type="text" pInputText [(ngModel)]="listing.cashFlow"> -->
|
|
||||||
<p-inputNumber mode="currency" currency="USD" inputId="cashFlow" type="text" [(ngModel)]="listing.cashFlow"></p-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<label for="netProfit" class="block font-medium text-900 mb-2">Net Profit</label>
|
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
||||||
<!-- <input id="netProfit" type="text" pInputText [(ngModel)]="listing.netProfit"> -->
|
<p-inputNumber mode="currency" currency="USD" inputId="cashFlow" type="text" [(ngModel)]="listing.cashFlow"></p-inputNumber>
|
||||||
<p-inputNumber mode="currency" currency="USD" inputId="netProfit" type="text" [(ngModel)]="listing.netProfit"></p-inputNumber>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="employees" class="block font-medium text-900 mb-2">Years Established Since</label>
|
||||||
|
<p-inputNumber mode="employees" mode="decimal" inputId="employees" type="text" [(ngModel)]="listing.established"></p-inputNumber>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
||||||
<!-- <input id="employees" type="text" pInputText [(ngModel)]="listing.employees"> -->
|
|
||||||
<p-inputNumber mode="employees" mode="decimal" inputId="employees" type="text" [(ngModel)]="listing.employees"></p-inputNumber>
|
<p-inputNumber mode="employees" mode="decimal" inputId="employees" type="text" [(ngModel)]="listing.employees"></p-inputNumber>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-4 ">
|
||||||
|
<p-checkbox [binary]="true" [(ngModel)]="listing.realEstateIncluded"></p-checkbox>
|
||||||
|
<span class="ml-2 text-900">Real Estate Included</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-4">
|
||||||
|
<p-checkbox [binary]="true" [(ngModel)]="listing.leasedLocation"></p-checkbox>
|
||||||
|
<span class="ml-2 text-900">Leased Location</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-4">
|
||||||
|
<p-checkbox [binary]="true" [(ngModel)]="listing.franchiseResale"></p-checkbox>
|
||||||
|
<span class="ml-2 text-900">Franchise Re-Sale</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="inventory" class="block font-medium text-900 mb-2">Inventory</label>
|
<label for="supportAndTraining" class="block font-medium text-900 mb-2">Support & Training</label>
|
||||||
<textarea id="inventory" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.inventory"></textarea>
|
<!-- <textarea id="inventory" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.supportAndTraining"></textarea> -->
|
||||||
|
<input id="supportAndTraining" type="text" pInputText [(ngModel)]="listing.supportAndTraining">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="reasonForSale" class="block font-medium text-900 mb-2">Reason for Sale</label>
|
<label for="reasonForSale" class="block font-medium text-900 mb-2">Reason for Sale</label>
|
||||||
<textarea id="reasonForSale" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.reasonForSale"></textarea>
|
<textarea id="reasonForSale" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.reasonForSale"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<label for="brokerLicensing" class="block font-medium text-900 mb-2">Broker Licensing</label>
|
<label for="brokerLicensing" class="block font-medium text-900 mb-2">Broker Licensing</label>
|
||||||
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing">
|
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="internalListingNumber" class="block font-medium text-900 mb-2">Internal Listing Number</label>
|
||||||
|
<p-inputNumber mode="decimal" inputId="internalListingNumber" type="text" [(ngModel)]="listing.internalListingNumber"></p-inputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="internalListing" class="block font-medium text-900 mb-2">Internal Listing (Will not be shown on the listing, for your records only.)</label>
|
<label for="internalListing" class="block font-medium text-900 mb-2">Internal Notes (Will not be shown on the listing, for your records only.)</label>
|
||||||
<input id="internalListing" type="text" pInputText [(ngModel)]="listing.internals">
|
<input id="internalListing" type="text" pInputText [(ngModel)]="listing.internals">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6 ">
|
||||||
|
<!-- <p-tag value="New"></p-tag> -->
|
||||||
|
<!-- <p-checkbox [binary]="true" [(ngModel)]="listing.draft"></p-checkbox> -->
|
||||||
|
<p-inputSwitch inputId="draft" [(ngModel)]="listing.draft"></p-inputSwitch>
|
||||||
|
<!-- <span class="ml-2 text-900">Share my data with contacts</span> -->
|
||||||
|
<span class="ml-2 text-900 absolute translate-y-5">Draft Mode (Will not be shown as public listing)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
@if (mode==='create'){
|
@if (mode==='create'){
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
.translate-y-5{
|
||||||
|
transform: translateY(5px);
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,11 @@ import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||||
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 { MessageService } from 'primeng/api';
|
import { MessageService } from 'primeng/api';
|
||||||
import { BusinessListing, ListingType, User } from '../../../../../../common-models/src/main.model';
|
import { AutoCompleteCompleteEvent, BusinessListing, ListingType, User } from '../../../../../../common-models/src/main.model';
|
||||||
|
import { GeoResult, GeoService } from '../../../services/geo.service';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'create-listing',
|
selector: 'create-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
|
|
@ -43,7 +47,13 @@ export class EditListingComponent {
|
||||||
listing:ListingType = createGenericObject<BusinessListing>();
|
listing:ListingType = createGenericObject<BusinessListing>();
|
||||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||||
user:User;
|
user:User;
|
||||||
constructor(public selectOptions:SelectOptionsService,private router: Router,private activatedRoute: ActivatedRoute,private listingsService:ListingsService,public userService: UserService,private messageService: MessageService){
|
constructor(public selectOptions:SelectOptionsService,
|
||||||
|
private router: Router,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private listingsService:ListingsService,
|
||||||
|
public userService: UserService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private geoService:GeoService){
|
||||||
this.user=this.userService.getUser();
|
this.user=this.userService.getUser();
|
||||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
|
|
@ -62,10 +72,10 @@ export class EditListingComponent {
|
||||||
this.listing.listingsCategory='business';
|
this.listing.listingsCategory='business';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateSummary(value: string): void {
|
// updateSummary(value: string): void {
|
||||||
const lines = value.split('\n');
|
// const lines = value.split('\n');
|
||||||
(<BusinessListing>this.listing).summary = lines.filter(l=>l.trim().length>0);
|
// (<BusinessListing>this.listing).summary = lines.filter(l=>l.trim().length>0);
|
||||||
}
|
// }
|
||||||
async update(id:string){
|
async update(id:string){
|
||||||
await this.listingsService.update(this.listing,this.listing.id);
|
await this.listingsService.update(this.listing,this.listing.id);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been updated', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been updated', life: 3000 });
|
||||||
|
|
@ -74,4 +84,11 @@ export class EditListingComponent {
|
||||||
await this.listingsService.create(this.listing);
|
await this.listingsService.create(this.listing);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been created', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been created', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suggestions: string[] | undefined;
|
||||||
|
|
||||||
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query,this.listing.state))//[...Array(5).keys()].map(item => event.query + '-' + item);
|
||||||
|
this.suggestions = result.map(r=>r.city).slice(0,5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||||
<td>{{ selectOptions.getLocation(listing.location) }}</td>
|
<td>{{ selectOptions.getState(listing.state) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button pButton pRipple icon="pi pi-eye" class="p-button-rounded p-button-success mr-2" [routerLink]="['/details',listing.id]"></button>
|
<button pButton pRipple icon="pi pi-eye" class="p-button-rounded p-button-success mr-2" [routerLink]="['/details',listing.id]"></button>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||||
<td>{{ selectOptions.getLocation(listing.location) }}</td>
|
<td>{{ selectOptions.getState(listing.location) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editListing',listing.id]"></button>
|
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editListing',listing.id]"></button>
|
||||||
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event,listing)"></button>
|
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event,listing)"></button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
export interface GeoResult { city: string; state: string; state_code: string }
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GeoService {
|
||||||
|
|
||||||
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
|
findCitiesStartingWith(prefix:string, state?:string):Observable<GeoResult[]>{
|
||||||
|
const stateString = state?`/${state}`:''
|
||||||
|
return this.http.get<GeoResult[]>(`${this.apiBaseUrl}/bizmatch/geo/${prefix}${stateString}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ export class SelectOptionsService {
|
||||||
this.prices = allSelectOptions.prices;
|
this.prices = allSelectOptions.prices;
|
||||||
this.listingCategories = allSelectOptions.listingCategories;
|
this.listingCategories = allSelectOptions.listingCategories;
|
||||||
this.categories = allSelectOptions.categories;
|
this.categories = allSelectOptions.categories;
|
||||||
this.locations = allSelectOptions.locations;
|
this.states = allSelectOptions.locations;
|
||||||
}
|
}
|
||||||
public typesOfBusiness: Array<KeyValueStyle>;
|
public typesOfBusiness: Array<KeyValueStyle>;
|
||||||
|
|
||||||
|
|
@ -30,10 +30,10 @@ export class SelectOptionsService {
|
||||||
|
|
||||||
public categories: Array<KeyValueStyle>;
|
public categories: Array<KeyValueStyle>;
|
||||||
|
|
||||||
public locations: Array<any>;
|
public states: Array<any>;
|
||||||
|
|
||||||
getLocation(value:string):string{
|
getState(value:string):string{
|
||||||
return this.locations.find(l=>l.value===value)?.name
|
return this.states.find(l=>l.value===value)?.name
|
||||||
}
|
}
|
||||||
|
|
||||||
getBusiness(value:string):string{
|
getBusiness(value:string):string{
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
import { Injectable, Signal, WritableSignal, computed, effect, signal } from '@angular/core';
|
import { Injectable, Signal, WritableSignal, computed, effect, signal } from '@angular/core';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { jwtDecode } from 'jwt-decode';
|
import { jwtDecode } from 'jwt-decode';
|
||||||
import { Observable, distinctUntilChanged, filter, from, map } from 'rxjs';
|
import { Observable, distinctUntilChanged, filter, from, lastValueFrom, map } from 'rxjs';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { KeycloakService } from './keycloak.service';
|
import { KeycloakService } from './keycloak.service';
|
||||||
import { JwtToken, User } from '../../../../common-models/src/main.model';
|
import { JwtToken, User } from '../../../../common-models/src/main.model';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
|
// -----------------------------
|
||||||
|
// Keycloak services
|
||||||
|
// -----------------------------
|
||||||
private user$ = new Observable<User>();
|
private user$ = new Observable<User>();
|
||||||
private user:User
|
private user:User
|
||||||
public $isLoggedIn : Signal<boolean>;
|
public $isLoggedIn : Signal<boolean>;
|
||||||
constructor(public keycloak:KeycloakService){
|
constructor(public keycloak:KeycloakService,private http: HttpClient){
|
||||||
this.user$ = from(this.keycloak.getToken()).pipe(
|
this.user$ = from(this.keycloak.getToken()).pipe(
|
||||||
filter(t => !!t),
|
filter(t => !!t),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
|
|
@ -50,15 +55,12 @@ export class UserService {
|
||||||
const token = await this.keycloak.getToken();
|
const token = await this.keycloak.getToken();
|
||||||
this.user = this.map2User(token);
|
this.user = this.map2User(token);
|
||||||
}
|
}
|
||||||
getUserName(){
|
|
||||||
return this.user?.username
|
|
||||||
}
|
|
||||||
|
|
||||||
private map2User(jwt:string):User{
|
private map2User(jwt:string):User{
|
||||||
const token = jwtDecode<JwtToken>(jwt);
|
const token = jwtDecode<JwtToken>(jwt);
|
||||||
return {
|
return {
|
||||||
id:token.user_id,
|
id:token.user_id,
|
||||||
username:token.preferred_username,
|
|
||||||
firstname:token.given_name,
|
firstname:token.given_name,
|
||||||
lastname:token.family_name,
|
lastname:token.family_name,
|
||||||
email:token.email
|
email:token.email
|
||||||
|
|
@ -91,4 +93,14 @@ export class UserService {
|
||||||
register(url:string){
|
register(url:string){
|
||||||
this.keycloak.register({redirectUri:url});
|
this.keycloak.register({redirectUri:url});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
// Redis services
|
||||||
|
// -----------------------------
|
||||||
|
async save(user:User):Promise<User>{
|
||||||
|
return await lastValueFrom(this.http.post<User>(`${this.apiBaseUrl}/bizmatch/user`,user));
|
||||||
|
}
|
||||||
|
async getById(id:string):Promise<User>{
|
||||||
|
return await lastValueFrom(this.http.get<User>(`${this.apiBaseUrl}/bizmatch/user/${id}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,15 @@ import { InputNumberModule } from 'primeng/inputnumber';
|
||||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
||||||
import { ToastModule } from 'primeng/toast';
|
import { ToastModule } from 'primeng/toast';
|
||||||
|
import { AutoCompleteModule } from 'primeng/autocomplete';
|
||||||
|
import { InputSwitchModule } from 'primeng/inputswitch';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule, RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule
|
CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule, RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule, AutoCompleteModule,InputSwitchModule
|
||||||
],
|
],
|
||||||
exports:[
|
exports:[
|
||||||
CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule,RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule
|
CommonModule, StyleClassModule, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule,RouterModule,FontAwesomeModule,MenuAccountComponent,InputNumberModule,ConfirmDialogModule,ConfirmPopupModule, ToastModule, CheckboxModule, AutoCompleteModule, TagModule,InputSwitchModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
export class SharedModule { }
|
||||||
|
|
|
||||||
|
|
@ -18,24 +18,30 @@ export interface Listing {
|
||||||
userId: string;
|
userId: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: Array<string>;
|
description: Array<string>;
|
||||||
location: string;//enum
|
country: string;
|
||||||
|
city: string,
|
||||||
|
state: string;//enum
|
||||||
favoritesForUser:Array<string>;
|
favoritesForUser:Array<string>;
|
||||||
hideImage?:boolean;
|
hideImage?:boolean;
|
||||||
|
draft?:boolean;
|
||||||
created:Date;
|
created:Date;
|
||||||
updated:Date;
|
updated:Date;
|
||||||
}
|
}
|
||||||
export interface BusinessListing extends Listing {
|
export interface BusinessListing extends Listing {
|
||||||
listingsCategory: 'business'; //enum
|
listingsCategory: 'business'; //enum
|
||||||
summary: Array<string>;
|
// summary: Array<string>;
|
||||||
type: string; //enum
|
type: string; //enum
|
||||||
price?: number;
|
price?: number;
|
||||||
realEstateIncluded?: boolean;
|
realEstateIncluded?: boolean;
|
||||||
|
leasedLocation?:boolean;
|
||||||
|
franchiseResale?:boolean;
|
||||||
salesRevenue?: number;
|
salesRevenue?: number;
|
||||||
cashFlow?: number;
|
cashFlow?: number;
|
||||||
netProfit?: number;
|
// netProfit?: number;
|
||||||
inventory?: string;
|
supportAndTraining?: string;
|
||||||
employees?: number;
|
employees?: number;
|
||||||
established?: number;
|
established?: number;
|
||||||
|
internalListingNumber?:number;
|
||||||
reasonForSale?: string;
|
reasonForSale?: string;
|
||||||
brokerLicencing?: string;
|
brokerLicencing?: string;
|
||||||
internals?: string;
|
internals?: string;
|
||||||
|
|
@ -68,13 +74,22 @@ export interface ListingCriteria {
|
||||||
listingsCategory:'business'|'professionals_brokers'|'investment',
|
listingsCategory:'business'|'professionals_brokers'|'investment',
|
||||||
category:'professional|broker'
|
category:'professional|broker'
|
||||||
}
|
}
|
||||||
export interface User {
|
export interface UserBase {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
phoneNumber?: string;
|
||||||
|
companyOverview?:string;
|
||||||
|
companyWebsite?:string;
|
||||||
|
companyLocation?:string;
|
||||||
|
offeredServices?:string;
|
||||||
|
areasServed?:string;
|
||||||
}
|
}
|
||||||
|
export interface User extends UserBase {
|
||||||
|
licensedIn?:KeyValue[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Subscription {
|
export interface Subscription {
|
||||||
id: string;
|
id: string;
|
||||||
userId:string
|
userId:string
|
||||||
|
|
@ -127,3 +142,7 @@ export interface PageEvent {
|
||||||
page: number;
|
page: number;
|
||||||
pageCount: number;
|
pageCount: number;
|
||||||
}
|
}
|
||||||
|
export interface AutoCompleteCompleteEvent {
|
||||||
|
originalEvent: Event;
|
||||||
|
query: string;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue