BugFix: #90,#86 + Restrictions on seller
This commit is contained in:
parent
f66badbfb1
commit
8157dcc376
|
|
@ -52,7 +52,7 @@ export class CommercialPropertyService {
|
||||||
if (!user?.roles?.includes('ADMIN') ?? false) {
|
if (!user?.roles?.includes('ADMIN') ?? false) {
|
||||||
whereConditions.push(or(eq(commercials.email, user?.username), ne(commercials.draft, true)));
|
whereConditions.push(or(eq(commercials.email, user?.username), ne(commercials.draft, true)));
|
||||||
}
|
}
|
||||||
whereConditions.push(and(eq(schema.users.customerType, 'professional')));
|
// whereConditions.push(and(eq(schema.users.customerType, 'professional')));
|
||||||
return whereConditions;
|
return whereConditions;
|
||||||
}
|
}
|
||||||
// #### Find by criteria ########################################
|
// #### Find by criteria ########################################
|
||||||
|
|
|
||||||
|
|
@ -104,12 +104,22 @@ const USStates = z.enum([
|
||||||
]);
|
]);
|
||||||
export const AreasServedSchema = z.object({
|
export const AreasServedSchema = z.object({
|
||||||
county: z.string().optional().nullable(),
|
county: z.string().optional().nullable(),
|
||||||
state: z.string().nonempty('State is required'),
|
state: z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.refine(val => val !== null && val !== '', {
|
||||||
|
message: 'State is required',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const LicensedInSchema = z.object({
|
export const LicensedInSchema = z.object({
|
||||||
registerNo: z.string().nonempty('Registration number is required'),
|
state: z
|
||||||
state: z.string().nonempty('State is required'),
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.refine(val => val !== null && val !== '', {
|
||||||
|
message: 'State is required',
|
||||||
|
}),
|
||||||
|
registerNo: z.string().nonempty('License number is required'),
|
||||||
});
|
});
|
||||||
export const GeoSchema = z.object({
|
export const GeoSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
|
|
||||||
|
|
@ -1,115 +1,3 @@
|
||||||
<!-- <nav class="bg-white border-gray-200 dark:bg-gray-900">
|
|
||||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
|
||||||
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
|
||||||
<img src="assets/images/header-logo.png" class="h-8" alt="Flowbite Logo" />
|
|
||||||
</a>
|
|
||||||
<div class="flex items-center md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="flex text-sm bg-gray-200 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
|
||||||
id="user-menu-button"
|
|
||||||
aria-expanded="false"
|
|
||||||
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
|
|
||||||
data-dropdown-placement="bottom"
|
|
||||||
>
|
|
||||||
<span class="sr-only">Open user menu</span>
|
|
||||||
@if(user){
|
|
||||||
<img class="w-8 h-8 rounded-full object-cover" src="{{ profileUrl }}" alt="user photo" />
|
|
||||||
} @else {
|
|
||||||
<i class="flex justify-center items-center text-stone-50 w-8 h-8 rounded-full fa-solid fa-bars"></i>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
@if(user){
|
|
||||||
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-login">
|
|
||||||
<div class="px-4 py-3">
|
|
||||||
<span class="block text-sm text-gray-900 dark:text-white">Welcome, {{ user.firstname }} </span>
|
|
||||||
<span class="block text-sm text-gray-500 truncate dark:text-gray-400">{{ user.email }}</span>
|
|
||||||
</div>
|
|
||||||
<ul class="py-2" aria-labelledby="user-menu-button">
|
|
||||||
<li>
|
|
||||||
<a routerLink="/account" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Account</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a routerLink="/createBusinessListing" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
|
|
||||||
>Create Listing</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a routerLink="/myListings" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Listings</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a routerLink="/emailUs" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">EMail Us</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a routerLink="/logout" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Logout</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-unknown">
|
|
||||||
<ul class="py-2" aria-labelledby="user-menu-button">
|
|
||||||
<li>
|
|
||||||
<a (click)="login()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Log In</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a (click)="register()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Register</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<button
|
|
||||||
data-collapse-toggle="navbar-user"
|
|
||||||
type="button"
|
|
||||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
|
||||||
aria-controls="navbar-user"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span class="sr-only">Open main menu</span>
|
|
||||||
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
|
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1" id="navbar-user">
|
|
||||||
<ul
|
|
||||||
class="flex flex-col font-medium p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700"
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
routerLinkActive="active-link"
|
|
||||||
routerLink="/businessListings"
|
|
||||||
[ngClass]="{ 'bg-blue-700 text-white md:text-blue-700 md:bg-transparent md:dark:text-blue-500': isActive('/businessListings') }"
|
|
||||||
class="block py-2 px-3 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
|
|
||||||
aria-current="page"
|
|
||||||
(click)="closeMenus()"
|
|
||||||
>Businesses</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
routerLinkActive="active-link"
|
|
||||||
routerLink="/commercialPropertyListings"
|
|
||||||
[ngClass]="{ 'bg-blue-700 text-white md:text-blue-700 md:bg-transparent md:dark:text-blue-500': isActive('/commercialPropertyListings') }"
|
|
||||||
class="block py-2 px-3 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
|
|
||||||
(click)="closeMenus()"
|
|
||||||
>Properties</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
routerLinkActive="active-link"
|
|
||||||
routerLink="/brokerListings"
|
|
||||||
[ngClass]="{ 'bg-blue-700 text-white md:text-blue-700 md:bg-transparent md:dark:text-blue-500': isActive('/brokerListings') }"
|
|
||||||
class="block py-2 px-3 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
|
|
||||||
(click)="closeMenus()"
|
|
||||||
>Professionals</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav> -->
|
|
||||||
|
|
||||||
<nav class="bg-white border-gray-200 dark:bg-gray-900">
|
<nav class="bg-white border-gray-200 dark:bg-gray-900">
|
||||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
||||||
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
<a routerLink="/home" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||||
|
|
@ -154,11 +42,17 @@
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/account" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Account</a>
|
<a routerLink="/account" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Account</a>
|
||||||
</li>
|
</li>
|
||||||
@if(user.customerType==='professional' || isAdmin()){
|
@if(user.customerType==='professional' || user.customerType==='seller' || isAdmin()){
|
||||||
<li>
|
<li>
|
||||||
|
@if(user.customerSubType==='broker'){
|
||||||
<a routerLink="/createBusinessListing" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
|
<a routerLink="/createBusinessListing" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
|
||||||
>Create Listing</a
|
>Create Listing</a
|
||||||
>
|
>
|
||||||
|
}@else {
|
||||||
|
<a routerLink="/createCommercialPropertyListing" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
|
||||||
|
>Create Listing</a
|
||||||
|
>
|
||||||
|
}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/myListings" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Listings</a>
|
<a routerLink="/myListings" (click)="closeDropdown()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">My Listings</a>
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,3 @@
|
||||||
<!-- <div class="surface-ground h-full">
|
|
||||||
<div class="px-6 py-5">
|
|
||||||
@if (user){
|
|
||||||
<div class="surface-card p-4 shadow-2 border-round">
|
|
||||||
<div class="surface-section px-6 pt-5">
|
|
||||||
<div class="flex align-items-start flex-column lg:flex-row lg:justify-content-between">
|
|
||||||
<div class="flex align-items-start flex-column md:flex-row">
|
|
||||||
@if(user.hasProfile){
|
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures//profile/{{ emailToDirName(user.email) }}.avif?_ts={{ ts }}" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
|
|
||||||
} @else {
|
|
||||||
<img src="assets/images/person_placeholder.jpg" class="mr-5 mb-3 lg:mb-0" style="width: 90px" />
|
|
||||||
}
|
|
||||||
<div>
|
|
||||||
<span class="text-900 font-medium text-3xl">{{ user.firstname }} {{ user.lastname }}</span>
|
|
||||||
<i class="pi pi-star text-2xl ml-4 text-yellow-500"></i>
|
|
||||||
<div class="flex align-items-center flex-wrap text-sm">
|
|
||||||
<div class="mr-5 mt-3">
|
|
||||||
<span class="font-medium text-500">Company</span>
|
|
||||||
<div class="text-700 mt-2">{{ user.companyName }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mr-5 mt-3">
|
|
||||||
<span class="font-medium text-500">For Sale</span>
|
|
||||||
<div class="text-700 mt-2">{{ businessListings?.length + commercialPropListings?.length }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex align-items-center mt-3">
|
|
||||||
<div>
|
|
||||||
@if(user.hasCompanyLogo){
|
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ emailToDirName(user.email) }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="max-height: 60px; max-width: 100px" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if(historyService.canGoBack){
|
|
||||||
<p-button icon="pi pi-times" [rounded]="true" severity="danger" (click)="historyService.goBack()"></p-button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<p class="mt-2 text-700 line-height-3 text-l font-semibold">{{ user.description }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="px-6 py-5">
|
|
||||||
<div class="surface-card p-4 shadow-2 border-round">
|
|
||||||
<div class="font-medium text-3xl text-900 mb-3">Company Profile</div>
|
|
||||||
<div class="text-500 mb-5" [innerHTML]="companyOverview"></div>
|
|
||||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
|
||||||
<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">Name</div>
|
|
||||||
<div class="text-900 w-full md:w-10">{{ user.firstname }} {{ user.lastname }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Phone Number</div>
|
|
||||||
<div class="text-900 w-full md:w-10 line-height-3">{{ formatPhoneNumber(user.phoneNumber) }}</div>
|
|
||||||
</li>
|
|
||||||
<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">EMail Address</div>
|
|
||||||
<div class="text-900 w-full md:w-10 line-height-3">{{ user.email }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Company Location</div>
|
|
||||||
<div class="text-900 w-full md:w-10 line-height-3">{{ user.companyLocation }}</div>
|
|
||||||
</li>
|
|
||||||
<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">Services we offer</div>
|
|
||||||
<div class="text-900 w-full md:w-10" [innerHTML]="offeredServices"></div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Areas (Counties) we serve</div>
|
|
||||||
<div class="text-900 w-full md:w-10">
|
|
||||||
@for (area of user.areasServed; track area) {
|
|
||||||
<p-tag styleClass="mr-2" value="{{ area.county }}-{{ area.state }}" [rounded]="true"></p-tag>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
|
||||||
<div class="text-500 w-full md:w-2 font-medium">Licensed In</div>
|
|
||||||
<div class="text-900 w-full md:w-10">
|
|
||||||
@for (license of user.licensedIn; track license) {
|
|
||||||
<p-tag styleClass="mr-2" value="{{ license.registerNo }}-{{ license.state }}" [rounded]="true" severity="success"></p-tag>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
@if(businessListings?.length>0){
|
|
||||||
<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">My Business Listings For Sale</div>
|
|
||||||
<div class="text-900 w-full md:w-10">
|
|
||||||
<div class="grid mt-0 mr-0">
|
|
||||||
@for (listing of businessListings; track listing) {
|
|
||||||
<div class="col-12 md:col-6 cursor-pointer" [routerLink]="['/details-business-listing', listing.id]">
|
|
||||||
<div class="p-3 border-1 surface-border border-round surface-card">
|
|
||||||
<div class="text-900 mb-2">
|
|
||||||
<span class="inline-flex border-circle align-items-center justify-content-center mr-3" style="width: 38px; height: 38px">
|
|
||||||
<i [class]="selectOptions.getIconAndTextColorType(listing.type)" class="pi text-xl"></i>
|
|
||||||
</span>
|
|
||||||
<span class="font-medium">{{ selectOptions.getBusiness(listing.type) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-700">{{ listing.title }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
} @if(commercialPropListings?.length>0){
|
|
||||||
<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">My Commercial Property Listings For Sale</div>
|
|
||||||
<div class="text-900 w-full md:w-10">
|
|
||||||
<div class="grid mt-0 mr-0">
|
|
||||||
@for (listing of commercialPropListings; track listing) {
|
|
||||||
<div class="col-12 md:col-6 cursor-pointer" [routerLink]="['/details-commercial-property-listing', listing.id]">
|
|
||||||
<div class="p-3 border-1 surface-border border-round surface-card">
|
|
||||||
<div class="text-900 mb-2 flex align-items-center">
|
|
||||||
@if (listing.imageOrder?.length>0){
|
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.serialId }}/{{ listing.imageOrder[0] }}?_ts={{ ts }}" class="mr-3" style="width: 45px; height: 45px" />
|
|
||||||
} @else {
|
|
||||||
<img src="assets/images/placeholder_properties.jpg" class="mr-3" style="width: 45px; height: 45px" />
|
|
||||||
}
|
|
||||||
<span class="font-medium">{{ selectOptions.getCommercialProperty(listing.type) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-700">{{ listing.title }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if( user?.email===keycloakUser?.email || isAdmin()){
|
|
||||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto" [routerLink]="['/account', user.id]"></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
@if(user){
|
@if(user){
|
||||||
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
||||||
|
|
@ -199,7 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||||
<span class="font-semibold w-40 p-2">Company Location</span>
|
<span class="font-semibold w-40 p-2">Company Location</span>
|
||||||
<span class="p-2 flex-grow">{{ user.companyLocation.name }} - {{ user.companyLocation.state }}</span>
|
<span class="p-2 flex-grow">{{ user.companyLocation?.name }} - {{ user.companyLocation?.state }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center bg-gray-100">
|
<div class="flex flex-col sm:flex-row sm:items-center bg-gray-100">
|
||||||
<span class="font-semibold w-40 p-2">Professional Type</span>
|
<span class="font-semibold w-40 p-2">Professional Type</span>
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export class DetailsUserComponent {
|
||||||
const token = await this.keycloakService.getToken();
|
const token = await this.keycloakService.getToken();
|
||||||
this.keycloakUser = map2User(token);
|
this.keycloakUser = map2User(token);
|
||||||
this.companyOverview = this.sanitizer.bypassSecurityTrustHtml(this.user.companyOverview);
|
this.companyOverview = this.sanitizer.bypassSecurityTrustHtml(this.user.companyOverview);
|
||||||
this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices);
|
this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices ? this.user.offeredServices : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,3 @@
|
||||||
<!-- <div id="sky-line" class="hidden-lg-down"></div>
|
|
||||||
<div class="search">
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="grid p-4 align-items-center">
|
|
||||||
<div class="col-2">
|
|
||||||
<p-dropdown [filter]="true" filterBy="name" [options]="states" [(ngModel)]="criteria.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '100%' }">
|
|
||||||
<ng-template let-state pTemplate="item">
|
|
||||||
<div class="flex align-items-center gap-2">
|
|
||||||
<div>{{ state.name }} ({{ state.count }})</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<p-dropdown
|
|
||||||
[filter]="true"
|
|
||||||
filterBy="name"
|
|
||||||
[options]="selectOptions.typesOfBusiness"
|
|
||||||
[(ngModel)]="criteria.type"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="value"
|
|
||||||
[showClear]="true"
|
|
||||||
placeholder="Categorie of Business"
|
|
||||||
[style]="{ width: '100%' }"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.minPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Min Price" [style]="{ width: '100%' }"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<p-dropdown [options]="selectOptions.prices" [(ngModel)]="criteria.maxPrice" optionLabel="name" optionValue="value" [showClear]="true" placeholder="Max Price" [style]="{ width: '100%' }"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<p-inputGroup>
|
|
||||||
<input id="name" type="text" pInputText [(ngModel)]="criteria.title" placeholder="Title" />
|
|
||||||
<button type="button" pButton icon="pi pi-times" class="p-button-secondary" (click)="reset()"></button>
|
|
||||||
</p-inputGroup>
|
|
||||||
</div>
|
|
||||||
<div class="col-1" pTooltip="Real Estate excluded/included" tooltipPosition="top">
|
|
||||||
<p-toggleButton [(ngModel)]="criteria.realEstateChecked" onLabel="RE incl." offLabel="RE excl."></p-toggleButton>
|
|
||||||
</div>
|
|
||||||
<div class="col-1">
|
|
||||||
<p-button label="Refine" (click)="refine()"></p-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="surface-200 h-full">
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="grid">
|
|
||||||
@for (listing of listings; track listing.id) {
|
|
||||||
<div class="col-12 lg:col-3 p-3">
|
|
||||||
<div class="shadow-2 border-round surface-card h-full flex-column justify-content-between flex">
|
|
||||||
<div class="p-4 flex flex-column relative h-full">
|
|
||||||
<div class="flex align-items-center">
|
|
||||||
<span [class]="selectOptions.getBgColorType(listing.type)" class="inline-flex border-circle align-items-center justify-content-center mr-3" style="width: 38px; height: 38px">
|
|
||||||
<i [class]="selectOptions.getIconAndTextColorType(listing.type)" class="pi text-xl"></i>
|
|
||||||
</span>
|
|
||||||
<span class="text-900 font-medium text-2xl">{{ selectOptions.getBusiness(listing.type) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-900 my-3 text-xl font-medium">{{ listing.title }}</div>
|
|
||||||
<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">Net profit: {{ listing.cashFlow | currency }}</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>
|
|
||||||
<div class="icon-pos">
|
|
||||||
<a routerLink="/details-user/{{ listing.userId }}" class="mr-2"
|
|
||||||
><img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" (error)="imageErrorHandler(listing)" class="rounded-image"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="px-4 py-3 surface-100 text-left">
|
|
||||||
<button pButton pRipple icon="pi pi-arrow-right" iconPos="right" label="View Full Listing" class="p-button-rounded p-button-success" [routerLink]="['/details-business-listing', listing.id]"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="mb-2 surface-200 flex align-items-center justify-content-center paginator-bar">
|
|
||||||
<div class="mx-1 text-color">Total number of Listings: {{ totalRecords }}</div>
|
|
||||||
<p-paginator (onPageChange)="onPageChange($event)" [first]="first" [rows]="rows" [totalRecords]="totalRecords" [rowsPerPageOptions]="[12, 24, 48]"></p-paginator>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<!-- business-listing.component.html -->
|
|
||||||
<!-- <div class="w-full bg-slate-100"> -->
|
|
||||||
|
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
@for (listing of listings; track listing.id) {
|
@for (listing of listings; track listing.id) {
|
||||||
|
|
@ -120,17 +32,3 @@
|
||||||
@if(pageCount>1){
|
@if(pageCount>1){
|
||||||
<app-paginator [page]="page" [pageCount]="pageCount" (pageChange)="onPageChange($event)"></app-paginator>
|
<app-paginator [page]="page" [pageCount]="pageCount" (pageChange)="onPageChange($event)"></app-paginator>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- <div class="container mx-auto px-4 py-8">
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
@for (listing of listings; track listing.id) {
|
|
||||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
|
||||||
<div class="p-4">
|
|
||||||
<h3 class="text-lg font-semibold mb-2">{{ listing.title }}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
@for (listing of listings; track listing.id) {
|
@for (listing of listings; track listing.id) {
|
||||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
<div class="bg-white rounded-lg shadow-md overflow-hidden flex flex-col h-full">
|
||||||
@if (listing.imageOrder?.length>0){
|
@if (listing.imageOrder?.length>0){
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.serialId }}/{{ listing.imageOrder[0] }}" alt="Image" class="w-full h-48 object-cover" />
|
<img src="{{ env.imageBaseUrl }}/pictures/property/{{ listing.imagePath }}/{{ listing.serialId }}/{{ listing.imageOrder[0] }}" alt="Image" class="w-full h-48 object-cover" />
|
||||||
} @else {
|
} @else {
|
||||||
<img src="assets/images/placeholder_properties.jpg" alt="Image" class="w-full h-48 object-cover" />
|
<img src="assets/images/placeholder_properties.jpg" alt="Image" class="w-full h-48 object-cover" />
|
||||||
}
|
}
|
||||||
<div class="p-4">
|
<div class="p-4 flex flex-col flex-grow">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<span class="bg-gray-200 text-gray-700 text-xs font-semibold px-2 py-1 rounded">{{ selectOptions.getState(listing.location.state) }}</span>
|
<span class="bg-gray-200 text-gray-700 text-xs font-semibold px-2 py-1 rounded">{{ selectOptions.getState(listing.location.state) }}</span>
|
||||||
<span class="text-gray-600 text-sm"><i [class]="selectOptions.getIconTypeOfCommercials(listing.type)" class="mr-1"></i> {{ selectOptions.getCommercialProperty(listing.type) }}</span>
|
<span class="text-gray-600 text-sm"><i [class]="selectOptions.getIconTypeOfCommercials(listing.type)" class="mr-1"></i> {{ selectOptions.getCommercialProperty(listing.type) }}</span>
|
||||||
|
|
@ -20,7 +20,8 @@
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-gray-600 mb-2">{{ listing.location.name }}</p>
|
<p class="text-gray-600 mb-2">{{ listing.location.name }}</p>
|
||||||
<p class="text-xl font-bold mb-4">{{ listing.price | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
|
<p class="text-xl font-bold mb-4">{{ listing.price | currency : 'USD' : 'symbol' : '1.0-0' }}</p>
|
||||||
<button [routerLink]="['/details-commercial-property-listing', listing.id]" class="bg-green-500 text-white px-4 py-2 rounded-full w-full hover:bg-green-600 transition duration-300">
|
<div class="flex-grow"></div>
|
||||||
|
<button [routerLink]="['/details-commercial-property-listing', listing.id]" class="bg-green-500 text-white px-4 py-2 rounded-full w-full hover:bg-green-600 transition duration-300 mt-auto">
|
||||||
View Full Listing <i class="fas fa-arrow-right ml-1"></i>
|
View Full Listing <i class="fas fa-arrow-right ml-1"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
<div class="container mx-auto px-4 py-16">
|
<div class="container mx-auto px-4 py-16">
|
||||||
<h1 class="text-4xl font-bold text-center mb-12">Choose the Right Plan for Your Business</h1>
|
<h1 class="text-4xl font-bold text-center mb-12">Choose the Right Plan for Your Business</h1>
|
||||||
|
|
||||||
<div class="grid md:grid-cols-3 gap-8">
|
<div
|
||||||
|
[ngClass]="{
|
||||||
|
'grid gap-8 mx-auto': true,
|
||||||
|
'md:grid-cols-3 max-w-7xl': !user || !user.subscriptionPlan,
|
||||||
|
'md:grid-cols-2 max-w-4xl': user && user.subscriptionPlan
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
@if(!user || !user.subscriptionPlan) {
|
||||||
<!-- Free Plan -->
|
<!-- Free Plan -->
|
||||||
<div class="bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-full">
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-full">
|
||||||
<div class="px-6 py-8 bg-gray-50 text-center border-b">
|
<div class="px-6 py-8 bg-gray-50 text-center border-b">
|
||||||
|
|
@ -30,6 +37,7 @@
|
||||||
<button (click)="register()" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Sign Up Now</button>
|
<button (click)="register()" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Sign Up Now</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Professional Plan -->
|
<!-- Professional Plan -->
|
||||||
<div class="bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-full">
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-full">
|
||||||
|
|
@ -72,7 +80,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-6 py-4 mt-auto">
|
<div class="px-6 py-4 mt-auto">
|
||||||
<!-- <button routerLink="/payment" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Get Started</button> -->
|
|
||||||
<button (click)="register('price_1PpSkpDjmFBOcNBs9UDPgBos')" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Get Started</button>
|
<button (click)="register('price_1PpSkpDjmFBOcNBs9UDPgBos')" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Get Started</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -122,7 +129,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-6 py-4 mt-auto">
|
<div class="px-6 py-4 mt-auto">
|
||||||
<!-- <button routerLink="/payment" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Start Listing Now</button> -->
|
|
||||||
<button (click)="register('price_1PpSmRDjmFBOcNBsaaSp2nk9')" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Start Listing Now</button>
|
<button (click)="register('price_1PpSmRDjmFBOcNBsaaSp2nk9')" class="w-full bg-blue-500 text-white rounded-full px-4 py-2 font-semibold hover:bg-blue-600 transition duration-300">Start Listing Now</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { StripeService } from 'ngx-stripe';
|
import { StripeService } from 'ngx-stripe';
|
||||||
import { switchMap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
|
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { Checkout, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
|
import { Checkout, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { UserService } from '../../services/user.service';
|
import { UserService } from '../../services/user.service';
|
||||||
|
|
@ -21,6 +22,7 @@ export class PricingComponent {
|
||||||
private apiBaseUrl = environment.apiBaseUrl;
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||||
keycloakUser: KeycloakUser;
|
keycloakUser: KeycloakUser;
|
||||||
|
user: User;
|
||||||
constructor(public keycloakService: KeycloakService, private http: HttpClient, private stripeService: StripeService, private activatedRoute: ActivatedRoute, private userService: UserService, private router: Router) {}
|
constructor(public keycloakService: KeycloakService, private http: HttpClient, private stripeService: StripeService, private activatedRoute: ActivatedRoute, private userService: UserService, private router: Router) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|
@ -29,14 +31,20 @@ export class PricingComponent {
|
||||||
if (this.id) {
|
if (this.id) {
|
||||||
this.checkout({ priceId: atob(this.id), email: this.keycloakUser.email, name: `${this.keycloakUser.firstName} ${this.keycloakUser.lastName}` });
|
this.checkout({ priceId: atob(this.id), email: this.keycloakUser.email, name: `${this.keycloakUser.firstName} ${this.keycloakUser.lastName}` });
|
||||||
}
|
}
|
||||||
|
if (this.keycloakUser && !this.id) {
|
||||||
|
this.user = await this.userService.getByMail(this.keycloakUser.email);
|
||||||
|
if (this.user.subscriptionId) {
|
||||||
|
this.router.navigate([`/account`]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async register(priceId?: string) {
|
async register(priceId?: string) {
|
||||||
if (this.keycloakUser) {
|
if (this.keycloakUser) {
|
||||||
if (!priceId) {
|
if (!priceId) {
|
||||||
const user = await this.userService.getByMail(this.keycloakUser.email);
|
this.user = await this.userService.getByMail(this.keycloakUser.email);
|
||||||
user.subscriptionPlan = 'free';
|
this.user.subscriptionPlan = 'free';
|
||||||
await this.userService.save(user);
|
await this.userService.save(this.user);
|
||||||
this.router.navigate([`/account`]);
|
this.router.navigate([`/account`]);
|
||||||
} else {
|
} else {
|
||||||
this.checkout({ priceId: priceId, email: this.keycloakUser.email, name: `${this.keycloakUser.firstName} ${this.keycloakUser.lastName}` });
|
this.checkout({ priceId: priceId, email: this.keycloakUser.email, name: `${this.keycloakUser.firstName} ${this.keycloakUser.lastName}` });
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,12 @@
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2 relative w-fit">
|
<h3 class="text-lg font-medium text-gray-700 mb-2 relative w-fit">
|
||||||
Areas We Serve @if(getValidationMessage('areasServed')){
|
Areas We Serve @if(getValidationMessage('areasServed')){
|
||||||
<div
|
<div
|
||||||
[attr.data-tooltip-target]="tooltipTarget"
|
[attr.data-tooltip-target]="tooltipTargetAreasServed"
|
||||||
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
!
|
!
|
||||||
</div>
|
</div>
|
||||||
<app-tooltip [id]="tooltipTarget" [text]="getValidationMessage('areasServed')"></app-tooltip>
|
<app-tooltip [id]="tooltipTargetAreasServed" [text]="getValidationMessage('areasServed')"></app-tooltip>
|
||||||
}
|
}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|
@ -174,7 +174,17 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Licensed In</h3>
|
<h3 class="text-lg font-medium text-gray-700 mb-2 relative">
|
||||||
|
Licensed In@if(getValidationMessage('licensedIn')){
|
||||||
|
<div
|
||||||
|
[attr.data-tooltip-target]="tooltipTargetLicensed"
|
||||||
|
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</div>
|
||||||
|
<app-tooltip [id]="tooltipTargetLicensed" [text]="getValidationMessage('licensedIn')"></app-tooltip>
|
||||||
|
}
|
||||||
|
</h3>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,8 @@ export class AccountComponent {
|
||||||
validationMessages: ValidationMessage[] = [];
|
validationMessages: ValidationMessage[] = [];
|
||||||
customerTypeOptions: Array<{ value: string; label: string }> = [];
|
customerTypeOptions: Array<{ value: string; label: string }> = [];
|
||||||
customerSubTypeOptions: Array<{ value: string; label: string }> = [];
|
customerSubTypeOptions: Array<{ value: string; label: string }> = [];
|
||||||
tooltipTarget = 'tooltip-areasServed';
|
tooltipTargetAreasServed = 'tooltip-areasServed';
|
||||||
|
tooltipTargetLicensed = 'tooltip-licensedIn';
|
||||||
subscriptions: StripeSubscription[] | any[];
|
subscriptions: StripeSubscription[] | any[];
|
||||||
constructor(
|
constructor(
|
||||||
public userService: UserService,
|
public userService: UserService,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue