Rework of major pages
This commit is contained in:
parent
9e03620be7
commit
4230867608
|
|
@ -22,19 +22,19 @@ export class CommercialPropertyListingsController {
|
||||||
// return this.listingsService.findByUserId(userid,commercials);
|
// return this.listingsService.findByUserId(userid,commercials);
|
||||||
// }
|
// }
|
||||||
@Post('search')
|
@Post('search')
|
||||||
find(@Body() criteria: ListingCriteria): any {
|
async find(@Body() criteria: ListingCriteria): Promise<any> {
|
||||||
return this.listingsService.findListingsByCriteria(criteria,commercials);
|
return await this.listingsService.findListingsByCriteria(criteria,commercials);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() listing: any){
|
async create(@Body() listing: any){
|
||||||
this.logger.info(`Save Listing`);
|
this.logger.info(`Save Listing`);
|
||||||
this.listingsService.createListing(listing,commercials)
|
return await this.listingsService.createListing(listing,commercials)
|
||||||
}
|
}
|
||||||
@Put()
|
@Put()
|
||||||
update(@Body() listing: any){
|
async update(@Body() listing: any){
|
||||||
this.logger.info(`Save Listing`);
|
this.logger.info(`Save Listing`);
|
||||||
this.listingsService.updateListing(listing.id,listing,commercials)
|
return await this.listingsService.updateListing(listing.id,listing,commercials)
|
||||||
}
|
}
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
deleteById(@Param('id') id:string){
|
deleteById(@Param('id') id:string){
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,16 @@ export class ListingsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||||
const newListing = { data, created: data.created, updated: data.updated, visits: 0, last_visit: null }
|
data.created=new Date()
|
||||||
const [createdListing] = await this.conn.insert(table).values(newListing).returning();
|
data.updated=new Date()
|
||||||
|
data.visits=0;
|
||||||
|
data.lastVisit=null
|
||||||
|
const [createdListing] = await this.conn.insert(table).values(data).returning();
|
||||||
return createdListing as BusinessListing | CommercialPropertyListing;
|
return createdListing as BusinessListing | CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||||
|
data.updated=new Date();
|
||||||
const [updateListing] = await this.conn.update(table).set(data).where(eq(table.id, id)).returning();
|
const [updateListing] = await this.conn.update(table).set(data).where(eq(table.id, id)).returning();
|
||||||
return updateListing as BusinessListing | CommercialPropertyListing;
|
return updateListing as BusinessListing | CommercialPropertyListing;
|
||||||
}
|
}
|
||||||
|
|
@ -107,95 +111,5 @@ export class ListingsService {
|
||||||
listing.imageOrder.push(imagename);
|
listing.imageOrder.push(imagename);
|
||||||
await this.updateListing(listing.id, listing, commercials)
|
await this.updateListing(listing.id, listing, commercials)
|
||||||
}
|
}
|
||||||
// async getCommercialPropertyListingById(id: string): Promise<CommercialPropertyListing>{
|
|
||||||
// return await this.commercialPropertyListingRepository.fetch(id) as unknown as CommercialPropertyListing;
|
|
||||||
// }
|
|
||||||
// async getBusinessListingById(id: string) {
|
|
||||||
// return await this.businessListingRepository.fetch(id)
|
|
||||||
// }
|
|
||||||
// async getBusinessListingByUserId(userid:string){
|
|
||||||
// return await this.businessListingRepository.search().where('userId').equals(userid).return.all()
|
|
||||||
// }
|
|
||||||
// async deleteBusinessListing(id: string){
|
|
||||||
// return await this.businessListingRepository.remove(id);
|
|
||||||
// }
|
|
||||||
// async deleteCommercialPropertyListing(id: string){
|
|
||||||
// return await this.commercialPropertyListingRepository.remove(id);
|
|
||||||
// }
|
|
||||||
// async getAllBusinessListings(start?: number, end?: number) {
|
|
||||||
// return await this.businessListingRepository.search().return.all()
|
|
||||||
// }
|
|
||||||
// async getAllCommercialListings(start?: number, end?: number) {
|
|
||||||
// return await this.commercialPropertyListingRepository.search().return.all()
|
|
||||||
// }
|
|
||||||
// async findBusinessListings(criteria:ListingCriteria): Promise<any> {
|
|
||||||
// // let listings = await this.getAllBusinessListings();
|
|
||||||
// // return this.find(criteria,listings);
|
|
||||||
// const from=criteria.start?criteria.start:0
|
|
||||||
// const size=criteria.length?criteria.length:24
|
|
||||||
// this.logger.info(`start findBusinessListings: ${JSON.stringify(criteria)}`);
|
|
||||||
// const result = await this.redis.ft.search('business:index','*',{LIMIT:{from,size}});
|
|
||||||
// this.logger.info(`start findBusinessListings: ${JSON.stringify(criteria)}`);
|
|
||||||
// return result
|
|
||||||
// }
|
|
||||||
// async findCommercialPropertyListings(criteria:ListingCriteria): Promise<any> {
|
|
||||||
// let listings = await this.getAllCommercialListings();
|
|
||||||
// return this.find(criteria,listings);
|
|
||||||
// }
|
|
||||||
// async deleteAllBusinessListings(){
|
|
||||||
// const ids = await this.getIdsForRepo(this.schemaNameBusiness.name);
|
|
||||||
// this.businessListingRepository.remove(ids);
|
|
||||||
// }
|
|
||||||
// async deleteAllcommercialListings(){
|
|
||||||
// const ids = await this.getIdsForRepo(this.schemaNameCommercial.name);
|
|
||||||
// this.commercialPropertyListingRepository.remove(ids);
|
|
||||||
// }
|
|
||||||
// async getIdsForRepo(repoName:string, maxcount=100000){
|
|
||||||
// let cursor = 0;
|
|
||||||
// let ids = [];
|
|
||||||
// do {
|
|
||||||
// const reply = await this.redis.scan(cursor, {
|
|
||||||
// MATCH: `${repoName}:*`,
|
|
||||||
// COUNT: maxcount
|
|
||||||
// });
|
|
||||||
// cursor = reply.cursor;
|
|
||||||
// // Extrahiere die ID aus jedem Schlüssel und füge sie zur Liste hinzu
|
|
||||||
// ids = ids.concat(reply.keys.map(key => key.split(':')[1]).filter(id=>id!='index'));
|
|
||||||
// } while (cursor !== 0);
|
|
||||||
// return ids;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async find(criteria:ListingCriteria, listings: any[]): Promise<any> {
|
|
||||||
// listings=listings.filter(l=>l.listingsCategory===criteria.listingsCategory);
|
|
||||||
// if (convertStringToNullUndefined(criteria.type)){
|
|
||||||
// console.log(criteria.type);
|
|
||||||
// listings=listings.filter(l=>l.type===criteria.type);
|
|
||||||
// }
|
|
||||||
// if (convertStringToNullUndefined(criteria.state)){
|
|
||||||
// console.log(criteria.state);
|
|
||||||
// listings=listings.filter(l=>l.state===criteria.state);
|
|
||||||
// }
|
|
||||||
// if (convertStringToNullUndefined(criteria.minPrice)){
|
|
||||||
// console.log(criteria.minPrice);
|
|
||||||
// listings=listings.filter(l=>l.price>=Number(criteria.minPrice));
|
|
||||||
// }
|
|
||||||
// if (convertStringToNullUndefined(criteria.maxPrice)){
|
|
||||||
// console.log(criteria.maxPrice);
|
|
||||||
// listings=listings.filter(l=>l.price<=Number(criteria.maxPrice));
|
|
||||||
// }
|
|
||||||
// if (convertStringToNullUndefined(criteria.realEstateChecked)){
|
|
||||||
// console.log(criteria.realEstateChecked);
|
|
||||||
// listings=listings.filter(l=>l.realEstateIncluded);
|
|
||||||
// }
|
|
||||||
// if (convertStringToNullUndefined(criteria.category)){
|
|
||||||
// console.log(criteria.category);
|
|
||||||
// listings=listings.filter(l=>l.category===criteria.category);
|
|
||||||
// }
|
|
||||||
// return listings
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ export const routes: Routes = [
|
||||||
path: 'home',
|
path: 'home',
|
||||||
component: HomeComponent,
|
component: HomeComponent,
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// Listings Details
|
||||||
{
|
{
|
||||||
path: 'details-business-listing/:id',
|
path: 'details-business-listing/:id',
|
||||||
component: DetailsBusinessListingComponent,
|
component: DetailsBusinessListingComponent,
|
||||||
|
|
@ -44,15 +46,21 @@ export const routes: Routes = [
|
||||||
path: 'details-commercial-property-listing/:id',
|
path: 'details-commercial-property-listing/:id',
|
||||||
component: DetailsCommercialPropertyListingComponent,
|
component: DetailsCommercialPropertyListingComponent,
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// User Details
|
||||||
{
|
{
|
||||||
path: 'details-user/:id',
|
path: 'details-user/:id',
|
||||||
component: DetailsUserComponent,
|
component: DetailsUserComponent,
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// User edit
|
||||||
{
|
{
|
||||||
path: 'account',
|
path: 'account',
|
||||||
component: AccountComponent,
|
component: AccountComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// Create, Update Listings
|
||||||
{
|
{
|
||||||
path: 'editBusinessListing/:id',
|
path: 'editBusinessListing/:id',
|
||||||
component: EditBusinessListingComponent,
|
component: EditBusinessListingComponent,
|
||||||
|
|
@ -73,26 +81,36 @@ export const routes: Routes = [
|
||||||
component: EditCommercialPropertyListingComponent,
|
component: EditCommercialPropertyListingComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// My Listings
|
||||||
{
|
{
|
||||||
path: 'myListings',
|
path: 'myListings',
|
||||||
component: MyListingComponent,
|
component: MyListingComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// My Favorites
|
||||||
{
|
{
|
||||||
path: 'myFavorites',
|
path: 'myFavorites',
|
||||||
component: FavoritesComponent,
|
component: FavoritesComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// EMAil Us
|
||||||
{
|
{
|
||||||
path: 'emailUs',
|
path: 'emailUs',
|
||||||
component: EmailUsComponent,
|
component: EmailUsComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// Logout
|
||||||
{
|
{
|
||||||
path: 'logout',
|
path: 'logout',
|
||||||
component: LogoutComponent,
|
component: LogoutComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
|
// #########
|
||||||
|
// Pricing
|
||||||
{
|
{
|
||||||
path: 'pricing',
|
path: 'pricing',
|
||||||
component: PricingComponent,
|
component: PricingComponent,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export class HeaderComponent {
|
||||||
{
|
{
|
||||||
label: 'Create Listing',
|
label: 'Create Listing',
|
||||||
icon: 'pi pi-plus-circle',
|
icon: 'pi pi-plus-circle',
|
||||||
routerLink: '/createListing',
|
routerLink: '/createBusinessListing',
|
||||||
visible: this.isUserLogedIn(),
|
visible: this.isUserLogedIn(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -90,13 +90,13 @@ export class HeaderComponent {
|
||||||
fragment: '',
|
fragment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Professionals/Brokers Directory',
|
label: 'Commercial Property',
|
||||||
routerLink: '/brokerListings',
|
routerLink: '/commercialPropertyListings',
|
||||||
fragment: '',
|
fragment: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Commercial Property',
|
label: 'Professionals/Brokers Directory',
|
||||||
routerLink: '/commercialPropertyListings',
|
routerLink: '/brokerListings',
|
||||||
fragment: '',
|
fragment: '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
<div class="text-900 w-full md:w-10">
|
<div class="text-900 w-full md:w-10">
|
||||||
<div class="grid mt-0 mr-0">
|
<div class="grid mt-0 mr-0">
|
||||||
@for (listing of userListings; track listing) {
|
@for (listing of userListings; track listing) {
|
||||||
<div class="col-12 md:col-6 cursor-pointer" [routerLink]="['/details-listing/business', listing.id]">
|
<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="p-3 border-1 surface-border border-round surface-card">
|
||||||
<div class="text-900 mb-2">
|
<div class="text-900 mb-2">
|
||||||
<span [class]="selectOptions.getBgColorType(listing.type)" class="inline-flex border-circle align-items-center justify-content-center mr-3" style="width: 38px; height: 38px">
|
<span [class]="selectOptions.getBgColorType(listing.type)" class="inline-flex border-circle align-items-center justify-content-center mr-3" style="width: 38px; height: 38px">
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,62 @@
|
||||||
<div class="surface-ground ">
|
<div class="surface-ground">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<ul class="list-none m-0 p-0 flex flex-row lg:flex-column justify-content-evenly md:justify-content-between lg:justify-content-start mb-5 lg:pr-8 lg:mb-0">
|
<ul class="list-none m-0 p-0 flex flex-row lg:flex-column justify-content-evenly md:justify-content-between lg:justify-content-start mb-5 lg:pr-8 lg:mb-0">
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/account" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline" >
|
<a routerLink="/account" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||||
<i class="pi pi-user md:mr-2"></i>
|
<i class="pi pi-user md:mr-2"></i>
|
||||||
<span class="font-medium hidden md:block">Account</span>
|
<span class="font-medium hidden md:block">Account</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/createListing" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
<a
|
||||||
<i class="pi pi-plus-circle md:mr-2"></i>
|
routerLink="/createBusinessListing"
|
||||||
<span class="font-medium hidden md:block">Create Listing</span>
|
routerLinkActive="text-blue-500"
|
||||||
</a>
|
pRipple
|
||||||
</li>
|
class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline"
|
||||||
<li>
|
>
|
||||||
<a routerLink="/myListings" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
<i class="pi pi-plus-circle md:mr-2"></i>
|
||||||
<i class="pi pi-list md:mr-2"></i>
|
<span class="font-medium hidden md:block">Create Listing</span>
|
||||||
<span class="font-medium hidden md:block">My Listings</span>
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
<a
|
||||||
<a routerLink="/myFavorites" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
routerLink="/myListings"
|
||||||
<i class="pi pi-star md:mr-2"></i>
|
routerLinkActive="text-blue-500"
|
||||||
<span class="font-medium hidden md:block">My Favorites</span>
|
pRipple
|
||||||
</a>
|
class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline"
|
||||||
</li>
|
>
|
||||||
<li>
|
<i class="pi pi-list md:mr-2"></i>
|
||||||
<a routerLink="/emailUs" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
<span class="font-medium hidden md:block">My Listings</span>
|
||||||
<fa-icon [icon]="faEnvelope" class="mr-2 flex"></fa-icon>
|
</a>
|
||||||
<span class="font-medium hidden md:block">Email Us</span>
|
</li>
|
||||||
</a>
|
<li>
|
||||||
</li>
|
<a
|
||||||
<li>
|
routerLink="/myFavorites"
|
||||||
<a (click)="userService.logout()" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
routerLinkActive="text-blue-500"
|
||||||
<fa-icon [icon]="faRightFromBracket" class="mr-2 flex"></fa-icon>
|
pRipple
|
||||||
<span class="font-medium hidden md:block">Logout</span>
|
class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline"
|
||||||
</a>
|
>
|
||||||
</li>
|
<i class="pi pi-star md:mr-2"></i>
|
||||||
</ul>
|
<span class="font-medium hidden md:block">My Favorites</span>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a routerLink="/emailUs" routerLinkActive="text-blue-500" pRipple class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline">
|
||||||
|
<fa-icon [icon]="faEnvelope" class="mr-2 flex"></fa-icon>
|
||||||
|
<span class="font-medium hidden md:block">Email Us</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
(click)="userService.logout()"
|
||||||
|
routerLinkActive="text-blue-500"
|
||||||
|
pRipple
|
||||||
|
class="flex align-items-center cursor-pointer p-3 border-round text-800 hover:surface-200 transition-duration-150 transition-colors no-underline"
|
||||||
|
>
|
||||||
|
<fa-icon [icon]="faRightFromBracket" class="mr-2 flex"></fa-icon>
|
||||||
|
<span class="font-medium hidden md:block">Logout</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,155 +1,160 @@
|
||||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||||
<div class="text-900 font-semibold text-lg mt-3">{{mode==='create'?'New':'Edit'}} Listing</div>
|
<div class="text-900 font-semibold text-lg mt-3">{{ mode === 'create' ? 'New' : 'Edit' }} Listing</div>
|
||||||
<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="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
<label for="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
||||||
<p-dropdown id="listingCategory" [options]="selectOptions?.listingCategories"
|
<p-dropdown
|
||||||
[(ngModel)]="listingsCategory" optionLabel="name" optionValue="value"
|
id="listingCategory"
|
||||||
placeholder="Listing category" [disabled]="mode==='edit'"
|
[options]="selectOptions?.listingCategories"
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[ngModel]="listingsCategory"
|
||||||
</div>
|
optionLabel="name"
|
||||||
<div class="mb-4">
|
optionValue="value"
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
(ngModelChange)="changeListingCategory($event)"
|
||||||
<input id="email" type="text" pInputText [(ngModel)]="listing.title">
|
placeholder="Listing category"
|
||||||
</div>
|
[disabled]="mode === 'edit'"
|
||||||
<div>
|
[style]="{ width: '100%' }"
|
||||||
<div class="mb-4">
|
></p-dropdown>
|
||||||
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
</div>
|
||||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
<div class="mb-4">
|
||||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }"
|
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
||||||
[modules]="editorModules">
|
<input id="email" type="text" pInputText [(ngModel)]="listing.title" />
|
||||||
<ng-template pTemplate="header"></ng-template>
|
</div>
|
||||||
</p-editor>
|
<div>
|
||||||
</div>
|
<div class="mb-4">
|
||||||
</div>
|
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
||||||
<div class="mb-4">
|
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||||
<p-dropdown id="type" [options]="selectOptions?.typesOfBusiness" [(ngModel)]="listing.type"
|
<ng-template pTemplate="header"></ng-template>
|
||||||
optionLabel="name" optionValue="value" [showClear]="true" placeholder="Type of business"
|
</p-editor>
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">State</label>
|
|
||||||
<p-dropdown id="listingCategory" [options]="selectOptions?.states"
|
|
||||||
[(ngModel)]="listing.state" optionLabel="name" optionValue="value" [showClear]="true"
|
|
||||||
placeholder="State" [style]="{ width: '100%'}"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="listingCategory" class="block font-medium text-900 mb-2">City</label>
|
|
||||||
<p-autoComplete [(ngModel)]="listing.city" [suggestions]="suggestions"
|
|
||||||
(completeMethod)="search($event)"></p-autoComplete>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p-divider></p-divider>
|
</div>
|
||||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
<div class="mb-4">
|
||||||
<div class="flex-auto p-fluid">
|
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
||||||
<div class="grid">
|
<p-dropdown
|
||||||
<div class="mb-4 col-12 md:col-6">
|
id="type"
|
||||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
[options]="selectOptions?.typesOfBusiness"
|
||||||
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
[(ngModel)]="listing.type"
|
||||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price"
|
optionLabel="name"
|
||||||
[(ngModel)]="listing.price"></app-inputNumber>
|
optionValue="value"
|
||||||
</div>
|
[showClear]="true"
|
||||||
<div class="mb-4 col-12 md:col-6">
|
placeholder="Type of business"
|
||||||
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
[style]="{ width: '100%' }"
|
||||||
<app-inputNumber mode="currency" currency="USD" inputId="salesRevenue"
|
></p-dropdown>
|
||||||
[(ngModel)]="listing.salesRevenue"></app-inputNumber>
|
</div>
|
||||||
</div>
|
<div class="grid">
|
||||||
</div>
|
<div class="mb-4 col-12 md:col-6">
|
||||||
<div class="grid">
|
<label for="listingCategory" class="block font-medium text-900 mb-2">State</label>
|
||||||
<div class="mb-4 col-12 md:col-6">
|
<p-dropdown
|
||||||
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
id="listingCategory"
|
||||||
<app-inputNumber mode="currency" currency="USD" inputId="cashFlow"
|
[options]="selectOptions?.states"
|
||||||
[(ngModel)]="listing.cashFlow"></app-inputNumber>
|
[(ngModel)]="listing.state"
|
||||||
</div>
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
</div>
|
[showClear]="true"
|
||||||
<div class="grid">
|
placeholder="State"
|
||||||
<div class="mb-4 col-12 md:col-6">
|
[style]="{ width: '100%' }"
|
||||||
<label for="employees" class="block font-medium text-900 mb-2">Years Established
|
></p-dropdown>
|
||||||
Since</label>
|
|
||||||
<app-inputNumber mode="decimal" inputId="established"
|
|
||||||
[(ngModel)]="listing.established"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
|
||||||
<app-inputNumber mode="decimal" inputId="employees"
|
|
||||||
[(ngModel)]="listing.employees"></app-inputNumber>
|
|
||||||
</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">
|
|
||||||
<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.supportAndTraining"></textarea> -->
|
|
||||||
<input id="supportAndTraining" type="text" pInputText [(ngModel)]="listing.supportAndTraining">
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing">
|
|
||||||
</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>
|
|
||||||
<app-inputNumber mode="decimal" inputId="internalListingNumber" type="text"
|
|
||||||
[(ngModel)]="listing.internalListingNumber"></app-inputNumber>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<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">
|
|
||||||
</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>
|
|
||||||
@if (mode==='create'){
|
|
||||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
} @else {
|
|
||||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="listingCategory" class="block font-medium text-900 mb-2">City</label>
|
||||||
|
<p-autoComplete [(ngModel)]="listing.city" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<p-divider></p-divider>
|
||||||
|
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||||
|
<div class="flex-auto p-fluid">
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||||
|
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
||||||
|
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" inputId="salesRevenue" [(ngModel)]="listing.salesRevenue"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" inputId="cashFlow" [(ngModel)]="listing.cashFlow"></app-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>
|
||||||
|
<app-inputNumber mode="decimal" inputId="established" [(ngModel)]="listing.established"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
||||||
|
<app-inputNumber mode="decimal" inputId="employees" [(ngModel)]="listing.employees"></app-inputNumber>
|
||||||
|
</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">
|
||||||
|
<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.supportAndTraining"></textarea> -->
|
||||||
|
<input id="supportAndTraining" type="text" pInputText [(ngModel)]="listing.supportAndTraining" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing" />
|
||||||
|
</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>
|
||||||
|
<app-inputNumber mode="decimal" inputId="internalListingNumber" type="text" [(ngModel)]="listing.internalListingNumber"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<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" />
|
||||||
|
</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>
|
||||||
|
@if (mode==='create'){
|
||||||
|
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
} @else {
|
||||||
|
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<p-confirmDialog></p-confirmDialog>
|
<p-confirmDialog></p-confirmDialog>
|
||||||
|
|
|
||||||
|
|
@ -1,98 +1,92 @@
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { ButtonModule } from 'primeng/button';
|
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
||||||
import { TagModule } from 'primeng/tag';
|
|
||||||
import data from '../../../../assets/data/user.json';
|
|
||||||
import dataListings from '../../../../assets/data/listings.json';
|
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
|
||||||
import { ChipModule } from 'primeng/chip';
|
|
||||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
|
||||||
import { DividerModule } from 'primeng/divider';
|
|
||||||
import { TableModule } from 'primeng/table';
|
|
||||||
import { createGenericObject, getListingType } from '../../../utils/utils';
|
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
import { createGenericObject, getListingType, routeListingWithState } from '../../../utils/utils';
|
||||||
|
|
||||||
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { AngularCropperjsModule } from 'angular-cropperjs';
|
||||||
|
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
||||||
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||||
|
import { CarouselModule } from 'primeng/carousel';
|
||||||
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
|
import { DialogModule } from 'primeng/dialog';
|
||||||
|
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||||
|
import { EditorModule } from 'primeng/editor';
|
||||||
|
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
||||||
|
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { AutoCompleteCompleteEvent, ImageProperty } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
||||||
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||||
|
import { GeoService } from '../../../services/geo.service';
|
||||||
|
import { ImageService } from '../../../services/image.service';
|
||||||
|
import { LoadingService } from '../../../services/loading.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
||||||
import { GeoResult, GeoService } from '../../../services/geo.service';
|
|
||||||
import { InputNumberComponent, InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
|
||||||
import { environment } from '../../../../environments/environment';
|
|
||||||
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
|
||||||
import { CarouselModule } from 'primeng/carousel';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { DialogModule } from 'primeng/dialog';
|
|
||||||
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
|
||||||
import { HttpClient, HttpEventType } from '@angular/common/http';
|
|
||||||
import { ImageService } from '../../../services/image.service'
|
|
||||||
import { LoadingService } from '../../../services/loading.service';
|
|
||||||
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
import { EditorModule } from 'primeng/editor';
|
|
||||||
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
|
||||||
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
|
|
||||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
||||||
import { CdkDragDrop, CdkDragEnter, CdkDragExit, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
|
||||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
|
||||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'create-listing',
|
selector: 'business-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
imports: [
|
||||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
SharedModule,
|
||||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
ArrayToStringPipe,
|
||||||
|
InputNumberModule,
|
||||||
|
CarouselModule,
|
||||||
|
DialogModule,
|
||||||
|
AngularCropperjsModule,
|
||||||
|
FileUploadModule,
|
||||||
|
EditorModule,
|
||||||
|
DynamicDialogModule,
|
||||||
|
DragDropModule,
|
||||||
|
ConfirmDialogModule,
|
||||||
|
MixedCdkDragDropModule,
|
||||||
|
],
|
||||||
providers: [MessageService, DialogService, ConfirmationService],
|
providers: [MessageService, DialogService, ConfirmationService],
|
||||||
templateUrl: './edit-business-listing.component.html',
|
templateUrl: './edit-business-listing.component.html',
|
||||||
styleUrl: './edit-business-listing.component.scss'
|
styleUrl: './edit-business-listing.component.scss',
|
||||||
})
|
})
|
||||||
export class EditBusinessListingComponent {
|
export class EditBusinessListingComponent {
|
||||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||||
listingsCategory = 'commercialProperty'
|
listingsCategory = 'business';
|
||||||
category: string;
|
category: string;
|
||||||
location: string;
|
location: string;
|
||||||
mode: 'edit' | 'create';
|
mode: 'edit' | 'create';
|
||||||
separator: '\n\n'
|
separator: '\n\n';
|
||||||
listing: BusinessListing
|
listing: 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;
|
||||||
maxFileSize = 3000000;
|
maxFileSize = 3000000;
|
||||||
uploadUrl: string;
|
uploadUrl: string;
|
||||||
environment = environment;
|
environment = environment;
|
||||||
propertyImages: ImageProperty[]
|
propertyImages: ImageProperty[];
|
||||||
responsiveOptions = [
|
responsiveOptions = [
|
||||||
{
|
{
|
||||||
breakpoint: '1199px',
|
breakpoint: '1199px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '991px',
|
breakpoint: '991px',
|
||||||
numVisible: 2,
|
numVisible: 2,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '767px',
|
breakpoint: '767px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
config = { aspectRatio: 16 / 9 }
|
config = { aspectRatio: 16 / 9 };
|
||||||
editorModules = TOOLBAR_OPTIONS
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
dialogRef: DynamicDialogRef | undefined;
|
dialogRef: DynamicDialogRef | undefined;
|
||||||
draggedImage: ImageProperty
|
draggedImage: ImageProperty;
|
||||||
faTrash = faTrash;
|
faTrash = faTrash;
|
||||||
constructor(public selectOptions: SelectOptionsService,
|
data: CommercialPropertyListing;
|
||||||
|
constructor(
|
||||||
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private listingsService: ListingsService,
|
private listingsService: ListingsService,
|
||||||
|
|
@ -102,106 +96,48 @@ export class EditBusinessListingComponent {
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
private loadingService: LoadingService,
|
private loadingService: LoadingService,
|
||||||
public dialogService: DialogService,
|
public dialogService: DialogService,
|
||||||
private confirmationService: ConfirmationService) {
|
private confirmationService: ConfirmationService,
|
||||||
this.user = this.userService.getUser();
|
private route: ActivatedRoute,
|
||||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
) {
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
this.mode = event.url === '/createBusinessListing' ? 'create' : 'edit';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.route.data.subscribe(async () => {
|
||||||
|
if (this.router.getCurrentNavigation().extras.state) {
|
||||||
|
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||||
} else {
|
} else {
|
||||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
|
||||||
sessionStorage.setItem('uuid', uuid);
|
|
||||||
this.listing = createGenericObject<BusinessListing>();
|
this.listing = createGenericObject<BusinessListing>();
|
||||||
this.listing.id = uuid
|
this.listing.listingsCategory = 'business';
|
||||||
this.listing.userId = this.user.id
|
this.listing.userId = await this.userService.getId();
|
||||||
|
this.listing.title = this.data?.title;
|
||||||
|
this.listing.description = this.data?.description;
|
||||||
}
|
}
|
||||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
sessionStorage.removeItem('uuid')
|
this.listing = await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
this.router.navigate(['editBusinessListing', this.listing.id]);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions: string[] | undefined;
|
suggestions: string[] | undefined;
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state))
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state));
|
||||||
this.suggestions = result.map(r => r.city).slice(0, 5);
|
this.suggestions = result.map(r => r.city).slice(0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
select(event: any) {
|
changeListingCategory(value: 'business' | 'commercialProperty') {
|
||||||
const imageUrl = URL.createObjectURL(event.files[0]);
|
routeListingWithState(this.router, value, this.listing);
|
||||||
this.dialogRef = this.dialogService.open(ImageCropperComponent, {
|
|
||||||
data: {
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
fileUpload: this.fileUpload,
|
|
||||||
ratioVariable: false
|
|
||||||
},
|
|
||||||
header: 'Edit Image',
|
|
||||||
width: '50vw',
|
|
||||||
modal: true,
|
|
||||||
closeOnEscape: true,
|
|
||||||
keepInViewport: true,
|
|
||||||
closable: false,
|
|
||||||
breakpoints: {
|
|
||||||
'960px': '75vw',
|
|
||||||
'640px': '90vw'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.dialogRef.onClose.subscribe(cropper => {
|
|
||||||
if (cropper){
|
|
||||||
this.loadingService.startLoading('uploadImage');
|
|
||||||
cropper.getCroppedCanvas().toBlob(async (blob) => {
|
|
||||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
|
|
||||||
if (event.type === HttpEventType.Response) {
|
|
||||||
console.log('Upload abgeschlossen', event.body);
|
|
||||||
this.loadingService.stopLoading('uploadImage');
|
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
|
||||||
}
|
|
||||||
}, error => console.error('Fehler beim Upload:', error));
|
|
||||||
}, 'image/jpg');
|
|
||||||
cropper.destroy();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConfirm(imageName: string) {
|
|
||||||
this.confirmationService.confirm({
|
|
||||||
target: event.target as EventTarget,
|
|
||||||
message: `Do you want to delete this image ${imageName}?`,
|
|
||||||
header: 'Delete Confirmation',
|
|
||||||
icon: 'pi pi-info-circle',
|
|
||||||
acceptButtonStyleClass: "p-button-danger p-button-text",
|
|
||||||
rejectButtonStyleClass: "p-button-text p-button-text",
|
|
||||||
acceptIcon: "none",
|
|
||||||
rejectIcon: "none",
|
|
||||||
|
|
||||||
accept: async () => {
|
|
||||||
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
|
||||||
|
|
||||||
},
|
|
||||||
reject: () => {
|
|
||||||
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
|
||||||
console.log('deny')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
|
||||||
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
|
||||||
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,110 +1,126 @@
|
||||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||||
<div class="text-900 font-semibold text-lg mt-3">{{mode==='create'?'New':'Edit'}} Listing</div>
|
<div class="text-900 font-semibold text-lg mt-3">{{ mode === 'create' ? 'New' : 'Edit' }} Listing</div>
|
||||||
<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="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
<label for="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
||||||
<p-dropdown id="listingCategory" [options]="selectOptions?.listingCategories"
|
<p-dropdown
|
||||||
[(ngModel)]="listingsCategory" optionLabel="name" optionValue="value"
|
id="listingCategory"
|
||||||
placeholder="Listing category" [disabled]="mode==='edit'"
|
[options]="selectOptions?.listingCategories"
|
||||||
[style]="{ width: '100%'}"></p-dropdown>
|
[ngModel]="listingsCategory"
|
||||||
</div>
|
optionLabel="name"
|
||||||
<div class="mb-4">
|
optionValue="value"
|
||||||
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
(ngModelChange)="changeListingCategory($event)"
|
||||||
<input id="email" type="text" pInputText [(ngModel)]="listing.title">
|
placeholder="Listing category"
|
||||||
</div>
|
[disabled]="mode === 'edit'"
|
||||||
<div>
|
[style]="{ width: '100%' }"
|
||||||
<div class="mb-4">
|
></p-dropdown>
|
||||||
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
</div>
|
||||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
<div class="mb-4">
|
||||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }"
|
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
||||||
[modules]="editorModules">
|
<input id="email" type="text" pInputText [(ngModel)]="listing.title" />
|
||||||
<ng-template pTemplate="header"></ng-template>
|
</div>
|
||||||
</p-editor>
|
<div>
|
||||||
</div>
|
<div class="mb-4">
|
||||||
</div>
|
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
||||||
<div class="mb-4">
|
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||||
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||||
<p-dropdown id="type" [options]="selectOptions?.typesOfCommercialProperty"
|
<ng-template pTemplate="header"></ng-template>
|
||||||
[(ngModel)]="listing.type" optionLabel="name" optionValue="value" [showClear]="true"
|
</p-editor>
|
||||||
placeholder="Property Category" [style]="{ width: '100%'}"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="states" class="block font-medium text-900 mb-2">State</label>
|
|
||||||
<p-dropdown id="states" [options]="selectOptions?.states"
|
|
||||||
[(ngModel)]="listing.state" optionLabel="name" optionValue="value" [showClear]="true"
|
|
||||||
placeholder="State" [style]="{ width: '100%'}"></p-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="city" class="block font-medium text-900 mb-2">City</label>
|
|
||||||
<p-autoComplete id="city" [(ngModel)]="listing.city" [suggestions]="suggestions"
|
|
||||||
(completeMethod)="search($event)"></p-autoComplete>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="zipCode" class="block font-medium text-900 mb-2">Zip Code</label>
|
|
||||||
<input id="zipCode" type="text" pInputText [(ngModel)]="listing.zipCode">
|
|
||||||
</div>
|
|
||||||
<div class="mb-4 col-12 md:col-6">
|
|
||||||
<label for="county" class="block font-medium text-900 mb-2">County</label>
|
|
||||||
<input id="county" type="text" pInputText [(ngModel)]="listing.county">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p-divider></p-divider>
|
</div>
|
||||||
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
<div class="mb-4">
|
||||||
<div class="flex-auto p-fluid">
|
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
||||||
<div class="grid">
|
<p-dropdown
|
||||||
<div class="mb-4 col-12 md:col-6">
|
id="type"
|
||||||
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
[options]="selectOptions?.typesOfCommercialProperty"
|
||||||
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
[(ngModel)]="listing.type"
|
||||||
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price"
|
optionLabel="name"
|
||||||
[(ngModel)]="listing.price"></app-inputNumber>
|
optionValue="value"
|
||||||
</div>
|
[showClear]="true"
|
||||||
<div class="mb-4 col-12 md:col-6">
|
placeholder="Property Category"
|
||||||
<div class="flex flex-column align-items-center flex-or">
|
[style]="{ width: '100%' }"
|
||||||
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
></p-dropdown>
|
||||||
<!-- <img [src]="propertyPictureUrl" (error)="setImageToFallback($event)" class="image"/> -->
|
</div>
|
||||||
<p-fileUpload mode="basic" chooseLabel="Upload" [customUpload]="true" name="file"
|
<div class="grid">
|
||||||
accept="image/*" [maxFileSize]="maxFileSize" (onSelect)="select($event)"
|
<div class="mb-4 col-12 md:col-6">
|
||||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4">
|
<label for="states" class="block font-medium text-900 mb-2">State</label>
|
||||||
</p-fileUpload>
|
<p-dropdown id="states" [options]="selectOptions?.states" [(ngModel)]="listing.state" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [style]="{ width: '100%' }"></p-dropdown>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup
|
|
||||||
mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
|
||||||
@for (image of propertyImages; track image) {
|
|
||||||
<span cdkDropList mixedCdkDropList>
|
|
||||||
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
|
||||||
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{image.name}}"
|
|
||||||
[alt]="image.name" class="shadow-2" cdkDrag>
|
|
||||||
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image.name)"></fa-icon>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
@if (mode==='create'){
|
|
||||||
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
} @else {
|
|
||||||
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="city" class="block font-medium text-900 mb-2">City</label>
|
||||||
|
<p-autoComplete id="city" [(ngModel)]="listing.city" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="zipCode" class="block font-medium text-900 mb-2">Zip Code</label>
|
||||||
|
<input id="zipCode" type="text" pInputText [(ngModel)]="listing.zipCode" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="county" class="block font-medium text-900 mb-2">County</label>
|
||||||
|
<input id="county" type="text" pInputText [(ngModel)]="listing.county" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<p-divider></p-divider>
|
||||||
|
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||||
|
<div class="flex-auto p-fluid">
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||||
|
<!-- <p-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price" ></p-inputNumber> -->
|
||||||
|
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<div class="flex flex-column align-items-center flex-or">
|
||||||
|
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
||||||
|
<span class="font-light text-sm text-900 mb-2">(Pictures can be uploaded once the listing is posted initially)</span>
|
||||||
|
<!-- <img [src]="propertyPictureUrl" (error)="setImageToFallback($event)" class="image"/> -->
|
||||||
|
<p-fileUpload
|
||||||
|
mode="basic"
|
||||||
|
chooseLabel="Upload"
|
||||||
|
[customUpload]="true"
|
||||||
|
name="file"
|
||||||
|
accept="image/*"
|
||||||
|
[maxFileSize]="maxFileSize"
|
||||||
|
(onSelect)="select($event)"
|
||||||
|
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
||||||
|
[disabled]="true"
|
||||||
|
>
|
||||||
|
</p-fileUpload>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (propertyImages?.length>0){
|
||||||
|
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
||||||
|
@for (image of propertyImages; track image) {
|
||||||
|
<span cdkDropList mixedCdkDropList>
|
||||||
|
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
||||||
|
<img src="{{ environment.apiBaseUrl }}/property/{{ listing.id }}/{{ image.name }}" [alt]="image.name" class="shadow-2" cdkDrag />
|
||||||
|
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image.name)"></fa-icon>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
@if (mode==='create'){
|
||||||
|
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
} @else {
|
||||||
|
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<p-confirmDialog></p-confirmDialog>
|
<p-confirmDialog></p-confirmDialog>
|
||||||
|
|
|
||||||
|
|
@ -1,98 +1,96 @@
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { ButtonModule } from 'primeng/button';
|
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
||||||
import { TagModule } from 'primeng/tag';
|
|
||||||
import data from '../../../../assets/data/user.json';
|
|
||||||
import dataListings from '../../../../assets/data/listings.json';
|
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
|
||||||
import { ChipModule } from 'primeng/chip';
|
|
||||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
|
||||||
import { DividerModule } from 'primeng/divider';
|
|
||||||
import { TableModule } from 'primeng/table';
|
|
||||||
import { createGenericObject, getListingType } from '../../../utils/utils';
|
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
import { createGenericObject, getListingType, routeListingWithState } from '../../../utils/utils';
|
||||||
|
|
||||||
|
import { DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
|
import { HttpEventType } from '@angular/common/http';
|
||||||
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { AngularCropperjsModule } from 'angular-cropperjs';
|
||||||
|
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
||||||
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||||
|
import { CarouselModule } from 'primeng/carousel';
|
||||||
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
|
import { DialogModule } from 'primeng/dialog';
|
||||||
|
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||||
|
import { EditorModule } from 'primeng/editor';
|
||||||
|
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
||||||
|
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { AutoCompleteCompleteEvent, ImageProperty } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
|
||||||
|
import { InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
||||||
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||||
|
import { GeoService } from '../../../services/geo.service';
|
||||||
|
import { ImageService } from '../../../services/image.service';
|
||||||
|
import { LoadingService } from '../../../services/loading.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
||||||
import { GeoResult, GeoService } from '../../../services/geo.service';
|
|
||||||
import { InputNumberComponent, InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
|
||||||
import { environment } from '../../../../environments/environment';
|
|
||||||
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
|
||||||
import { CarouselModule } from 'primeng/carousel';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { DialogModule } from 'primeng/dialog';
|
|
||||||
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
|
||||||
import { HttpClient, HttpEventType } from '@angular/common/http';
|
|
||||||
import { ImageService } from '../../../services/image.service'
|
|
||||||
import { LoadingService } from '../../../services/loading.service';
|
|
||||||
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
import { EditorModule } from 'primeng/editor';
|
|
||||||
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
|
||||||
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
|
|
||||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
||||||
import { CdkDragDrop, CdkDragEnter, CdkDragExit, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
|
||||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
|
||||||
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'create-listing',
|
selector: 'commercial-property-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
imports: [
|
||||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
SharedModule,
|
||||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
ArrayToStringPipe,
|
||||||
|
InputNumberModule,
|
||||||
|
CarouselModule,
|
||||||
|
DialogModule,
|
||||||
|
AngularCropperjsModule,
|
||||||
|
FileUploadModule,
|
||||||
|
EditorModule,
|
||||||
|
DynamicDialogModule,
|
||||||
|
DragDropModule,
|
||||||
|
ConfirmDialogModule,
|
||||||
|
MixedCdkDragDropModule,
|
||||||
|
],
|
||||||
providers: [MessageService, DialogService, ConfirmationService],
|
providers: [MessageService, DialogService, ConfirmationService],
|
||||||
templateUrl: './edit-commercial-property-listing.component.html',
|
templateUrl: './edit-commercial-property-listing.component.html',
|
||||||
styleUrl: './edit-commercial-property-listing.component.scss'
|
styleUrl: './edit-commercial-property-listing.component.scss',
|
||||||
})
|
})
|
||||||
export class EditCommercialPropertyListingComponent {
|
export class EditCommercialPropertyListingComponent {
|
||||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||||
listingsCategory = 'commercialProperty'
|
listingsCategory = 'commercialProperty';
|
||||||
category: string;
|
category: string;
|
||||||
location: string;
|
location: string;
|
||||||
mode: 'edit' | 'create';
|
mode: 'edit' | 'create';
|
||||||
separator: '\n\n'
|
separator: '\n\n';
|
||||||
listing: CommercialPropertyListing
|
listing: CommercialPropertyListing;
|
||||||
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;
|
||||||
maxFileSize = 3000000;
|
maxFileSize = 3000000;
|
||||||
uploadUrl: string;
|
uploadUrl: string;
|
||||||
environment = environment;
|
environment = environment;
|
||||||
propertyImages: ImageProperty[]
|
propertyImages: ImageProperty[];
|
||||||
responsiveOptions = [
|
responsiveOptions = [
|
||||||
{
|
{
|
||||||
breakpoint: '1199px',
|
breakpoint: '1199px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '991px',
|
breakpoint: '991px',
|
||||||
numVisible: 2,
|
numVisible: 2,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '767px',
|
breakpoint: '767px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
config = { aspectRatio: 16 / 9 }
|
config = { aspectRatio: 16 / 9 };
|
||||||
editorModules = TOOLBAR_OPTIONS
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
dialogRef: DynamicDialogRef | undefined;
|
dialogRef: DynamicDialogRef | undefined;
|
||||||
draggedImage: ImageProperty
|
draggedImage: ImageProperty;
|
||||||
faTrash = faTrash;
|
faTrash = faTrash;
|
||||||
constructor(public selectOptions: SelectOptionsService,
|
suggestions: string[] | undefined;
|
||||||
|
data: BusinessListing;
|
||||||
|
userId: string;
|
||||||
|
constructor(
|
||||||
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private listingsService: ListingsService,
|
private listingsService: ListingsService,
|
||||||
|
|
@ -102,40 +100,43 @@ export class EditCommercialPropertyListingComponent {
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
private loadingService: LoadingService,
|
private loadingService: LoadingService,
|
||||||
public dialogService: DialogService,
|
public dialogService: DialogService,
|
||||||
private confirmationService: ConfirmationService) {
|
private confirmationService: ConfirmationService,
|
||||||
this.user = this.userService.getUser();
|
private route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
// 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 => {
|
||||||
if (event instanceof NavigationEnd) {
|
if (event instanceof NavigationEnd) {
|
||||||
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
this.mode = event.url === '/createCommercialPropertyListing' ? 'create' : 'edit';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.route.data.subscribe(async () => {
|
||||||
|
if (this.router.getCurrentNavigation().extras.state) {
|
||||||
|
this.data = this.router.getCurrentNavigation().extras.state['data'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||||
} else {
|
} else {
|
||||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
|
||||||
sessionStorage.setItem('uuid', uuid);
|
|
||||||
this.listing = createGenericObject<CommercialPropertyListing>();
|
this.listing = createGenericObject<CommercialPropertyListing>();
|
||||||
this.listing.id = uuid
|
this.listing.userId = await this.userService.getId();
|
||||||
this.listing.userId = this.user.id
|
this.listing.title = this.data?.title;
|
||||||
|
this.listing.description = this.data?.description;
|
||||||
}
|
}
|
||||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
sessionStorage.removeItem('uuid')
|
this.listing = await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
this.router.navigate(['editCommercialPropertyListing', this.listing.id]);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions: string[] | undefined;
|
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state))
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state));
|
||||||
this.suggestions = result.map(r => r.city).slice(0, 5);
|
this.suggestions = result.map(r => r.city).slice(0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +146,7 @@ export class EditCommercialPropertyListingComponent {
|
||||||
data: {
|
data: {
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
fileUpload: this.fileUpload,
|
fileUpload: this.fileUpload,
|
||||||
ratioVariable: false
|
ratioVariable: false,
|
||||||
},
|
},
|
||||||
header: 'Edit Image',
|
header: 'Edit Image',
|
||||||
width: '50vw',
|
width: '50vw',
|
||||||
|
|
@ -155,24 +156,27 @@ export class EditCommercialPropertyListingComponent {
|
||||||
closable: false,
|
closable: false,
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
'960px': '75vw',
|
'960px': '75vw',
|
||||||
'640px': '90vw'
|
'640px': '90vw',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.dialogRef.onClose.subscribe(cropper => {
|
this.dialogRef.onClose.subscribe(cropper => {
|
||||||
if (cropper){
|
if (cropper) {
|
||||||
this.loadingService.startLoading('uploadImage');
|
this.loadingService.startLoading('uploadImage');
|
||||||
cropper.getCroppedCanvas().toBlob(async (blob) => {
|
cropper.getCroppedCanvas().toBlob(async blob => {
|
||||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
|
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(
|
||||||
if (event.type === HttpEventType.Response) {
|
async event => {
|
||||||
console.log('Upload abgeschlossen', event.body);
|
if (event.type === HttpEventType.Response) {
|
||||||
this.loadingService.stopLoading('uploadImage');
|
console.log('Upload abgeschlossen', event.body);
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.loadingService.stopLoading('uploadImage');
|
||||||
}
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
}, error => console.error('Fehler beim Upload:', error));
|
}
|
||||||
|
},
|
||||||
|
error => console.error('Fehler beim Upload:', error),
|
||||||
|
);
|
||||||
}, 'image/jpg');
|
}, 'image/jpg');
|
||||||
cropper.destroy();
|
cropper.destroy();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConfirm(imageName: string) {
|
deleteConfirm(imageName: string) {
|
||||||
|
|
@ -181,27 +185,28 @@ export class EditCommercialPropertyListingComponent {
|
||||||
message: `Do you want to delete this image ${imageName}?`,
|
message: `Do you want to delete this image ${imageName}?`,
|
||||||
header: 'Delete Confirmation',
|
header: 'Delete Confirmation',
|
||||||
icon: 'pi pi-info-circle',
|
icon: 'pi pi-info-circle',
|
||||||
acceptButtonStyleClass: "p-button-danger p-button-text",
|
acceptButtonStyleClass: 'p-button-danger p-button-text',
|
||||||
rejectButtonStyleClass: "p-button-text p-button-text",
|
rejectButtonStyleClass: 'p-button-text p-button-text',
|
||||||
acceptIcon: "none",
|
acceptIcon: 'none',
|
||||||
rejectIcon: "none",
|
rejectIcon: 'none',
|
||||||
|
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
|
|
||||||
},
|
},
|
||||||
reject: () => {
|
reject: () => {
|
||||||
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
||||||
console.log('deny')
|
console.log('deny');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
||||||
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
||||||
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages)
|
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages);
|
||||||
|
}
|
||||||
|
changeListingCategory(value: 'business' | 'commercialProperty') {
|
||||||
|
routeListingWithState(this.router, value, this.listing);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||||
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
|
<menu-account></menu-account>
|
||||||
|
<p-toast></p-toast>
|
||||||
|
<div *ngIf="listing" class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||||
|
<div class="text-900 font-semibold text-lg mt-3">{{ mode === 'create' ? 'New' : 'Edit' }} Listing</div>
|
||||||
|
<p-divider></p-divider>
|
||||||
|
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||||
|
<div class="flex-auto p-fluid">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="listingCategory" class="block font-medium text-900 mb-2">Listing category</label>
|
||||||
|
<p-dropdown
|
||||||
|
id="listingCategory"
|
||||||
|
[options]="selectOptions?.listingCategories"
|
||||||
|
[(ngModel)]="listing.listingsCategory"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
placeholder="Listing category"
|
||||||
|
[disabled]="mode === 'edit'"
|
||||||
|
[style]="{ width: '100%' }"
|
||||||
|
></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="email" class="block font-medium text-900 mb-2">Title of Listing</label>
|
||||||
|
<input id="email" type="text" pInputText [(ngModel)]="listing.title" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="description" class="block font-medium text-900 mb-2">Description</label>
|
||||||
|
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||||
|
<ng-template pTemplate="header"></ng-template>
|
||||||
|
</p-editor>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (listing.listingsCategory==='business'){
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="type" class="block font-medium text-900 mb-2">Type of business</label>
|
||||||
|
<p-dropdown
|
||||||
|
id="type"
|
||||||
|
[options]="selectOptions?.typesOfBusiness"
|
||||||
|
[(ngModel)]="listing.type"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
[showClear]="true"
|
||||||
|
placeholder="Type of business"
|
||||||
|
[style]="{ width: '100%' }"
|
||||||
|
></p-dropdown>
|
||||||
|
</div>
|
||||||
|
} @if (listing.listingsCategory==='commercialProperty'){
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="type" class="block font-medium text-900 mb-2">Property Category</label>
|
||||||
|
<p-dropdown
|
||||||
|
id="type"
|
||||||
|
[options]="selectOptions?.typesOfCommercialProperty"
|
||||||
|
[(ngModel)]="listing.type"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
[showClear]="true"
|
||||||
|
placeholder="Property Category"
|
||||||
|
[style]="{ width: '100%' }"
|
||||||
|
></p-dropdown>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="listingCategory" class="block font-medium text-900 mb-2">State</label>
|
||||||
|
<p-dropdown
|
||||||
|
id="listingCategory"
|
||||||
|
[options]="selectOptions?.states"
|
||||||
|
[(ngModel)]="listing.state"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="value"
|
||||||
|
[showClear]="true"
|
||||||
|
placeholder="State"
|
||||||
|
[style]="{ width: '100%' }"
|
||||||
|
></p-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="listingCategory" class="block font-medium text-900 mb-2">City</label>
|
||||||
|
<p-autoComplete [(ngModel)]="listing.city" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (listing.listingsCategory==='commercialProperty'){
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="zipCode" class="block font-medium text-900 mb-2">Zip Code</label>
|
||||||
|
<input id="zipCode" type="text" pInputText [(ngModel)]="listing.zipCode" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="county" class="block font-medium text-900 mb-2">County</label>
|
||||||
|
<input id="county" type="text" pInputText [(ngModel)]="listing.county" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p-divider></p-divider>
|
||||||
|
<div class="flex gap-5 flex-column-reverse md:flex-row">
|
||||||
|
<div class="flex-auto p-fluid">
|
||||||
|
@if (listing.listingsCategory==='commercialProperty'){
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<div class="flex flex-column align-items-center flex-or">
|
||||||
|
<span class="font-medium text-900 mb-2">Property Pictures</span>
|
||||||
|
<p-fileUpload
|
||||||
|
mode="basic"
|
||||||
|
chooseLabel="Upload"
|
||||||
|
[customUpload]="true"
|
||||||
|
name="file"
|
||||||
|
accept="image/*"
|
||||||
|
[maxFileSize]="maxFileSize"
|
||||||
|
(onSelect)="select($event)"
|
||||||
|
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"
|
||||||
|
>
|
||||||
|
</p-fileUpload>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop (dropped)="onDrop($event)" cdkDropListOrientation="horizontal">
|
||||||
|
@for (image of propertyImages; track image) {
|
||||||
|
<span cdkDropList mixedCdkDropList>
|
||||||
|
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
|
||||||
|
<img src="{{ environment.apiBaseUrl }}/property/{{ listing.id }}/{{ image.name }}" [alt]="image.name" class="shadow-2" cdkDrag />
|
||||||
|
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image.name)"></fa-icon>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} @if (listing.listingsCategory==='business'){
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="price" class="block font-medium text-900 mb-2">Price</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" locale="en-US" inputId="price" [(ngModel)]="listing.price"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="salesRevenue" class="block font-medium text-900 mb-2">Sales Revenue</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" inputId="salesRevenue" [(ngModel)]="listing.salesRevenue"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="cashFlow" class="block font-medium text-900 mb-2">Cash Flow</label>
|
||||||
|
<app-inputNumber mode="currency" currency="USD" inputId="cashFlow" [(ngModel)]="listing.cashFlow"></app-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>
|
||||||
|
<app-inputNumber mode="decimal" inputId="established" [(ngModel)]="listing.established"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<label for="employees" class="block font-medium text-900 mb-2">Employees</label>
|
||||||
|
<app-inputNumber mode="decimal" inputId="employees" [(ngModel)]="listing.employees"></app-inputNumber>
|
||||||
|
</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">
|
||||||
|
<label for="supportAndTraining" class="block font-medium text-900 mb-2">Support & Training</label>
|
||||||
|
<input id="supportAndTraining" type="text" pInputText [(ngModel)]="listing.supportAndTraining" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
<input id="brokerLicensing" type="text" pInputText [(ngModel)]="listing.brokerLicencing" />
|
||||||
|
</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>
|
||||||
|
<app-inputNumber mode="decimal" inputId="internalListingNumber" type="text" [(ngModel)]="listing.internalListingNumber"></app-inputNumber>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<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" />
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="mb-4 col-12 md:col-6">
|
||||||
|
<p-inputSwitch inputId="draft" [(ngModel)]="listing.draft"></p-inputSwitch>
|
||||||
|
<span class="ml-2 text-900 absolute translate-y-5">Draft Mode (Will not be shown as public listing)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
@if (mode==='create'){
|
||||||
|
<button pButton pRipple label="Post Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
} @else {
|
||||||
|
<button pButton pRipple label="Update Listing" class="w-auto" (click)="save()"></button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p-toast></p-toast>
|
||||||
|
<p-confirmDialog></p-confirmDialog> -->
|
||||||
|
|
@ -1,60 +1,55 @@
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { ButtonModule } from 'primeng/button';
|
|
||||||
import { CheckboxModule } from 'primeng/checkbox';
|
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
|
||||||
import { DropdownModule } from 'primeng/dropdown';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
||||||
import { TagModule } from 'primeng/tag';
|
|
||||||
import data from '../../../../assets/data/user.json';
|
|
||||||
import dataListings from '../../../../assets/data/listings.json';
|
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
|
||||||
import { ChipModule } from 'primeng/chip';
|
|
||||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
|
||||||
import { DividerModule } from 'primeng/divider';
|
|
||||||
import { TableModule } from 'primeng/table';
|
|
||||||
import { createGenericObject, getListingType } from '../../../utils/utils';
|
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
import { createGenericObject } from '../../../utils/utils';
|
||||||
|
|
||||||
|
import { DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
|
import { HttpEventType } from '@angular/common/http';
|
||||||
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { AngularCropperjsModule } from 'angular-cropperjs';
|
||||||
|
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
||||||
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||||
|
import { CarouselModule } from 'primeng/carousel';
|
||||||
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
|
import { DialogModule } from 'primeng/dialog';
|
||||||
|
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||||
|
import { EditorModule } from 'primeng/editor';
|
||||||
|
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { AutoCompleteCompleteEvent, ImageProperty, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
|
||||||
|
import { InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
||||||
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||||
|
import { GeoService } from '../../../services/geo.service';
|
||||||
|
import { ImageService } from '../../../services/image.service';
|
||||||
|
import { LoadingService } from '../../../services/loading.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
|
||||||
import { GeoResult, GeoService } from '../../../services/geo.service';
|
|
||||||
import { InputNumberComponent, InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
|
||||||
import { environment } from '../../../../environments/environment';
|
|
||||||
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
|
||||||
import { CarouselModule } from 'primeng/carousel';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { DialogModule } from 'primeng/dialog';
|
|
||||||
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
|
||||||
import { HttpClient, HttpEventType } from '@angular/common/http';
|
|
||||||
import { ImageService } from '../../../services/image.service'
|
|
||||||
import { LoadingService } from '../../../services/loading.service';
|
|
||||||
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
import { EditorModule } from 'primeng/editor';
|
|
||||||
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
|
||||||
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
|
|
||||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
||||||
import { CdkDragDrop, CdkDragEnter, CdkDragExit, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
|
|
||||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { AutoCompleteCompleteEvent, ImageProperty, ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
|
||||||
import { BusinessListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'create-listing',
|
selector: 'create-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
imports: [
|
||||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
SharedModule,
|
||||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
ArrayToStringPipe,
|
||||||
|
InputNumberModule,
|
||||||
|
CarouselModule,
|
||||||
|
DialogModule,
|
||||||
|
AngularCropperjsModule,
|
||||||
|
FileUploadModule,
|
||||||
|
EditorModule,
|
||||||
|
DynamicDialogModule,
|
||||||
|
DragDropModule,
|
||||||
|
ConfirmDialogModule,
|
||||||
|
MixedCdkDragDropModule,
|
||||||
|
],
|
||||||
providers: [MessageService, DialogService, ConfirmationService],
|
providers: [MessageService, DialogService, ConfirmationService],
|
||||||
templateUrl: './edit-listing.component.html',
|
templateUrl: './edit-listing.component.html',
|
||||||
styleUrl: './edit-listing.component.scss'
|
styleUrl: './edit-listing.component.scss',
|
||||||
})
|
})
|
||||||
export class EditListingComponent {
|
export class EditListingComponent {
|
||||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||||
|
|
@ -62,37 +57,38 @@ export class EditListingComponent {
|
||||||
category: string;
|
category: string;
|
||||||
location: string;
|
location: string;
|
||||||
mode: 'edit' | 'create';
|
mode: 'edit' | 'create';
|
||||||
separator: '\n\n'
|
separator: '\n\n';
|
||||||
listing: ListingType
|
listing: ListingType;
|
||||||
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;
|
||||||
maxFileSize = 3000000;
|
maxFileSize = 3000000;
|
||||||
uploadUrl: string;
|
uploadUrl: string;
|
||||||
environment = environment;
|
environment = environment;
|
||||||
propertyImages: ImageProperty[]
|
propertyImages: ImageProperty[];
|
||||||
responsiveOptions = [
|
responsiveOptions = [
|
||||||
{
|
{
|
||||||
breakpoint: '1199px',
|
breakpoint: '1199px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '991px',
|
breakpoint: '991px',
|
||||||
numVisible: 2,
|
numVisible: 2,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
breakpoint: '767px',
|
breakpoint: '767px',
|
||||||
numVisible: 1,
|
numVisible: 1,
|
||||||
numScroll: 1
|
numScroll: 1,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
config = { aspectRatio: 16 / 9 }
|
config = { aspectRatio: 16 / 9 };
|
||||||
editorModules = TOOLBAR_OPTIONS
|
editorModules = TOOLBAR_OPTIONS;
|
||||||
dialogRef: DynamicDialogRef | undefined;
|
dialogRef: DynamicDialogRef | undefined;
|
||||||
draggedImage: ImageProperty
|
draggedImage: ImageProperty;
|
||||||
faTrash = faTrash;
|
faTrash = faTrash;
|
||||||
constructor(public selectOptions: SelectOptionsService,
|
constructor(
|
||||||
|
public selectOptions: SelectOptionsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private listingsService: ListingsService,
|
private listingsService: ListingsService,
|
||||||
|
|
@ -102,7 +98,8 @@ export class EditListingComponent {
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
private loadingService: LoadingService,
|
private loadingService: LoadingService,
|
||||||
public dialogService: DialogService,
|
public dialogService: DialogService,
|
||||||
private confirmationService: ConfirmationService) {
|
private confirmationService: ConfirmationService,
|
||||||
|
) {
|
||||||
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 => {
|
||||||
|
|
@ -110,7 +107,6 @@ export class EditListingComponent {
|
||||||
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
|
|
@ -119,23 +115,23 @@ export class EditListingComponent {
|
||||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
||||||
sessionStorage.setItem('uuid', uuid);
|
sessionStorage.setItem('uuid', uuid);
|
||||||
this.listing = createGenericObject<BusinessListing>();
|
this.listing = createGenericObject<BusinessListing>();
|
||||||
this.listing.id = uuid
|
this.listing.id = uuid;
|
||||||
this.listing.userId = this.user.id
|
this.listing.userId = this.user.id;
|
||||||
}
|
}
|
||||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
sessionStorage.removeItem('uuid')
|
sessionStorage.removeItem('uuid');
|
||||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
// await this.listingsService.save(this.listing, this.listing.listingsCategory);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions: string[] | undefined;
|
suggestions: string[] | undefined;
|
||||||
|
|
||||||
async search(event: AutoCompleteCompleteEvent) {
|
async search(event: AutoCompleteCompleteEvent) {
|
||||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state))
|
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query, this.listing.state));
|
||||||
this.suggestions = result.map(r => r.city).slice(0, 5);
|
this.suggestions = result.map(r => r.city).slice(0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +141,7 @@ export class EditListingComponent {
|
||||||
data: {
|
data: {
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
fileUpload: this.fileUpload,
|
fileUpload: this.fileUpload,
|
||||||
ratioVariable: false
|
ratioVariable: false,
|
||||||
},
|
},
|
||||||
header: 'Edit Image',
|
header: 'Edit Image',
|
||||||
width: '50vw',
|
width: '50vw',
|
||||||
|
|
@ -155,24 +151,27 @@ export class EditListingComponent {
|
||||||
closable: false,
|
closable: false,
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
'960px': '75vw',
|
'960px': '75vw',
|
||||||
'640px': '90vw'
|
'640px': '90vw',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.dialogRef.onClose.subscribe(cropper => {
|
this.dialogRef.onClose.subscribe(cropper => {
|
||||||
if (cropper){
|
if (cropper) {
|
||||||
this.loadingService.startLoading('uploadImage');
|
this.loadingService.startLoading('uploadImage');
|
||||||
cropper.getCroppedCanvas().toBlob(async (blob) => {
|
cropper.getCroppedCanvas().toBlob(async blob => {
|
||||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
|
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(
|
||||||
if (event.type === HttpEventType.Response) {
|
async event => {
|
||||||
console.log('Upload abgeschlossen', event.body);
|
if (event.type === HttpEventType.Response) {
|
||||||
this.loadingService.stopLoading('uploadImage');
|
console.log('Upload abgeschlossen', event.body);
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.loadingService.stopLoading('uploadImage');
|
||||||
}
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
}, error => console.error('Fehler beim Upload:', error));
|
}
|
||||||
|
},
|
||||||
|
error => console.error('Fehler beim Upload:', error),
|
||||||
|
);
|
||||||
}, 'image/jpg');
|
}, 'image/jpg');
|
||||||
cropper.destroy();
|
cropper.destroy();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConfirm(imageName: string) {
|
deleteConfirm(imageName: string) {
|
||||||
|
|
@ -181,27 +180,24 @@ export class EditListingComponent {
|
||||||
message: `Do you want to delete this image ${imageName}?`,
|
message: `Do you want to delete this image ${imageName}?`,
|
||||||
header: 'Delete Confirmation',
|
header: 'Delete Confirmation',
|
||||||
icon: 'pi pi-info-circle',
|
icon: 'pi pi-info-circle',
|
||||||
acceptButtonStyleClass: "p-button-danger p-button-text",
|
acceptButtonStyleClass: 'p-button-danger p-button-text',
|
||||||
rejectButtonStyleClass: "p-button-text p-button-text",
|
rejectButtonStyleClass: 'p-button-text p-button-text',
|
||||||
acceptIcon: "none",
|
acceptIcon: 'none',
|
||||||
rejectIcon: "none",
|
rejectIcon: 'none',
|
||||||
|
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
await this.imageService.deleteListingImage(this.listing.id, imageName);
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
||||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||||
|
|
||||||
},
|
},
|
||||||
reject: () => {
|
reject: () => {
|
||||||
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
console.log('deny');
|
||||||
console.log('deny')
|
},
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
||||||
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
||||||
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages)
|
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,45 @@
|
||||||
|
|
||||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
<p-toast></p-toast>
|
<p-toast></p-toast>
|
||||||
<p-confirmPopup></p-confirmPopup>
|
<p-confirmPopup></p-confirmPopup>
|
||||||
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
<div class="surface-card p-5 shadow-2 border-round flex-auto">
|
||||||
<div class="text-900 font-semibold text-lg mt-3">My Listings</div>
|
<div class="text-900 font-semibold text-lg mt-3">My Listings</div>
|
||||||
<p-divider></p-divider>
|
<p-divider></p-divider>
|
||||||
<p-table [value]="myListings" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id" [paginator]="true" [rows]="10" [rowsPerPageOptions]="[10, 20, 50]" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
|
<p-table
|
||||||
<ng-template pTemplate="header">
|
[value]="myListings"
|
||||||
<tr>
|
[tableStyle]="{ 'min-width': '50rem' }"
|
||||||
<th class="wide-column">Title</th>
|
dataKey="id"
|
||||||
<th>Category</th>
|
[paginator]="true"
|
||||||
<th>Located in</th>
|
[rows]="10"
|
||||||
<th></th>
|
[rowsPerPageOptions]="[10, 20, 50]"
|
||||||
</tr>
|
[showCurrentPageReport]="true"
|
||||||
</ng-template>
|
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
|
||||||
<ng-template pTemplate="body" let-listing>
|
>
|
||||||
<tr>
|
<ng-template pTemplate="header">
|
||||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
<tr>
|
||||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
<th class="wide-column">Title</th>
|
||||||
<td>{{ selectOptions.getState(listing.location) }}</td>
|
<th>Category</th>
|
||||||
<td>
|
<th>Located in</th>
|
||||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editListing',listing.id]"></button>
|
<th></th>
|
||||||
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event,listing)"></button>
|
</tr>
|
||||||
</td>
|
</ng-template>
|
||||||
</tr>
|
<ng-template pTemplate="body" let-listing>
|
||||||
</ng-template>
|
<tr>
|
||||||
</p-table>
|
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||||
</div>
|
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||||
|
<td>{{ selectOptions.getState(listing.city) }}</td>
|
||||||
|
<td>
|
||||||
|
@if(isBusinessListing(listing)){
|
||||||
|
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editBusinessListing', listing.id]"></button>
|
||||||
|
} @if(isCommercialPropertyListing(listing)){
|
||||||
|
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editCommercialPropertyListing', listing.id]"></button>
|
||||||
|
}
|
||||||
|
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event, listing)"></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</p-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,53 @@
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
|
||||||
import dataListings from '../../../../assets/data/listings.json';
|
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
|
||||||
import { UserService } from '../../../services/user.service';
|
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
|
||||||
import { lastValueFrom } from 'rxjs';
|
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
|
||||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||||
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
|
||||||
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { getListingType } from '../../../utils/utils';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
import { UserService } from '../../../services/user.service';
|
||||||
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
|
import { getListingType, isBusinessListing, isCommercialPropertyListing } from '../../../utils/utils';
|
||||||
|
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-my-listing',
|
selector: 'app-my-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MenuAccountComponent, SharedModule],
|
imports: [MenuAccountComponent, SharedModule],
|
||||||
providers:[ConfirmationService,MessageService],
|
providers: [ConfirmationService, MessageService],
|
||||||
templateUrl: './my-listing.component.html',
|
templateUrl: './my-listing.component.html',
|
||||||
styleUrl: './my-listing.component.scss'
|
styleUrl: './my-listing.component.scss',
|
||||||
})
|
})
|
||||||
export class MyListingComponent {
|
export class MyListingComponent {
|
||||||
user: User;
|
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||||
listings: Array<ListingType> =[]//dataListings as unknown as Array<BusinessListing>;
|
myListings: Array<ListingType>;
|
||||||
myListings: Array<ListingType>
|
userId: string;
|
||||||
constructor(public userService: UserService,private listingsService:ListingsService, private cdRef:ChangeDetectorRef,public selectOptions:SelectOptionsService,private confirmationService: ConfirmationService,private messageService: MessageService){
|
isBusinessListing = isBusinessListing;
|
||||||
this.user=this.userService.getUser();
|
isCommercialPropertyListing = isCommercialPropertyListing;
|
||||||
|
constructor(
|
||||||
}
|
public userService: UserService,
|
||||||
async ngOnInit(){
|
private listingsService: ListingsService,
|
||||||
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
private cdRef: ChangeDetectorRef,
|
||||||
this.myListings=this.listings.filter(l=>l.userId===this.user.id);
|
public selectOptions: SelectOptionsService,
|
||||||
}
|
private confirmationService: ConfirmationService,
|
||||||
|
private messageService: MessageService,
|
||||||
async deleteListing(listing:ListingType){
|
) {}
|
||||||
await this.listingsService.deleteListing(listing.id,getListingType(listing));
|
async ngOnInit() {
|
||||||
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
this.userId = await this.userService.getId();
|
||||||
this.myListings=this.listings.filter(l=>l.userId===this.user.id);
|
this.myListings = await this.listingsService.getListingByUserId(this.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm(event: Event,listing:ListingType) {
|
async deleteListing(listing: ListingType) {
|
||||||
|
await this.listingsService.deleteListing(listing.id, getListingType(listing));
|
||||||
|
this.myListings = await this.listingsService.getListingByUserId(this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm(event: Event, listing: ListingType) {
|
||||||
this.confirmationService.confirm({
|
this.confirmationService.confirm({
|
||||||
target: event.target as EventTarget,
|
target: event.target as EventTarget,
|
||||||
message: 'Are you sure you want to delet this listing?',
|
message: 'Are you sure you want to delet this listing?',
|
||||||
icon: 'pi pi-exclamation-triangle',
|
icon: 'pi pi-exclamation-triangle',
|
||||||
accept: () => {
|
accept: () => {
|
||||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been deleted', life: 3000 });
|
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been deleted', life: 3000 });
|
||||||
this.deleteListing(listing);
|
this.deleteListing(listing);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,45 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, lastValueFrom } from 'rxjs';
|
import { Observable, lastValueFrom } from 'rxjs';
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
import onChange from 'on-change';
|
|
||||||
import { getListingType, getSessionStorageHandler } from '../utils/utils';
|
|
||||||
import { ImageProperty, ListingCriteria, ListingType, ResponseBusinessListingArray, ResponseCommercialPropertyListingArray } from '../../../../bizmatch-server/src/models/main.model';
|
|
||||||
import { BusinessListing } from '../../../../bizmatch-server/src/models/db.model';
|
import { BusinessListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { ImageProperty, ListingCriteria, ListingType, ResponseBusinessListingArray } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ListingsService {
|
export class ListingsService {
|
||||||
private apiBaseUrl = environment.apiBaseUrl;
|
private apiBaseUrl = environment.apiBaseUrl;
|
||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {}
|
||||||
}
|
|
||||||
|
|
||||||
// getAllListings():Observable<ListingType[]>{
|
// getAllListings():Observable<ListingType[]>{
|
||||||
// return this.http.get<ListingType[]>(`${this.apiBaseUrl}/bizmatch/business-listings`);
|
// return this.http.get<ListingType[]>(`${this.apiBaseUrl}/bizmatch/business-listings`);
|
||||||
// }
|
// }
|
||||||
async getListings(criteria:ListingCriteria,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'):Promise<ListingType[]>{
|
async getListings(criteria: ListingCriteria, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty'): Promise<ListingType[]> {
|
||||||
const result = await lastValueFrom(this.http.post<ResponseBusinessListingArray>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/search`,criteria));
|
const result = await lastValueFrom(this.http.post<ResponseBusinessListingArray>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/search`, criteria));
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
getListingById(id:string,listingsCategory?:'business'|'commercialProperty'):Observable<ListingType>{
|
getListingById(id: string, listingsCategory?: 'business' | 'commercialProperty'): Observable<ListingType> {
|
||||||
const result = this.http.get<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`);
|
const result = this.http.get<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
getListingByUserId(userid:string):Promise<BusinessListing[]>{
|
getListingByUserId(userid: string): Promise<ListingType[]> {
|
||||||
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/business/user/${userid}`));
|
return lastValueFrom(this.http.get<BusinessListing[]>(`${this.apiBaseUrl}/bizmatch/listings/business/user/${userid}`));
|
||||||
}
|
}
|
||||||
async save(listing:any,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'){
|
async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
||||||
await lastValueFrom(this.http.post<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`,listing));
|
if (listing.id) {
|
||||||
|
return await lastValueFrom(this.http.put<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`, listing));
|
||||||
|
} else {
|
||||||
|
return await lastValueFrom(this.http.post<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`, listing));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async deleteListing(id:string,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'){
|
async deleteListing(id: string, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
||||||
await lastValueFrom(this.http.delete<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`));
|
await lastValueFrom(this.http.delete<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`));
|
||||||
}
|
}
|
||||||
async getPropertyImages(id:string):Promise<ImageProperty[]>{
|
async getPropertyImages(id: string): Promise<ImageProperty[]> {
|
||||||
return await lastValueFrom(this.http.get<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/image/${id}`));
|
return await lastValueFrom(this.http.get<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/image/${id}`));
|
||||||
}
|
}
|
||||||
async changeImageOrder(id:string, propertyImages: ImageProperty[]):Promise<ImageProperty[]>{
|
async changeImageOrder(id: string, propertyImages: ImageProperty[]): Promise<ImageProperty[]> {
|
||||||
return await lastValueFrom(this.http.put<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/imageOrder/${id}`,propertyImages));
|
return await lastValueFrom(this.http.put<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/imageOrder/${id}`, propertyImages));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,17 @@ export class UserService {
|
||||||
getUserObservable(): Observable<User> {
|
getUserObservable(): Observable<User> {
|
||||||
return this.user$;
|
return this.user$;
|
||||||
}
|
}
|
||||||
|
async getId(): Promise<string> {
|
||||||
|
if (sessionStorage.getItem('USERID')) {
|
||||||
|
return sessionStorage.getItem('USERID');
|
||||||
|
} else {
|
||||||
|
const user = await this.getByMail(this.user.email);
|
||||||
|
sessionStorage.setItem('USERID', user.id);
|
||||||
|
return user.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
logout() {
|
logout() {
|
||||||
|
sessionStorage.removeItem('USERID');
|
||||||
this.keycloak.logout(window.location.origin + '/home');
|
this.keycloak.logout(window.location.origin + '/home');
|
||||||
}
|
}
|
||||||
async login(url: string) {
|
async login(url: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,56 @@
|
||||||
import { INFO, ConsoleFormattedStream, createLogger as _createLogger, stdSerializers } from "browser-bunyan";
|
import { Router } from '@angular/router';
|
||||||
import { ListingCriteria } from "../../../../bizmatch-server/src/models/main.model";
|
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
||||||
import { BusinessListing, CommercialPropertyListing } from "../../../../bizmatch-server/src/models/db.model";
|
import { BusinessListing, CommercialPropertyListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||||
|
import { ListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||||
|
|
||||||
export function createGenericObject<T>(): T {
|
export function createGenericObject<T>(): T {
|
||||||
// Ein leeres Objekt vom Typ T erstellen
|
// Ein leeres Objekt vom Typ T erstellen
|
||||||
const ergebnis: Partial<T> = {};
|
const ergebnis: Partial<T> = {};
|
||||||
|
|
||||||
// Für ein reales Interface funktioniert diese direkte Iteration nicht,
|
// Für ein reales Interface funktioniert diese direkte Iteration nicht,
|
||||||
// da Interfaces zur Compile-Zeit entfernt werden. Stattdessen könnten Sie
|
// da Interfaces zur Compile-Zeit entfernt werden. Stattdessen könnten Sie
|
||||||
// ein Dummy-Objekt oder spezifische Typtransformationen verwenden.
|
// ein Dummy-Objekt oder spezifische Typtransformationen verwenden.
|
||||||
// Hier nur als Pseudocode dargestellt, um die Idee zu vermitteln:
|
// Hier nur als Pseudocode dargestellt, um die Idee zu vermitteln:
|
||||||
for (const key in ergebnis) {
|
for (const key in ergebnis) {
|
||||||
ergebnis[key] = null; // oder undefined, je nach Bedarf
|
ergebnis[key] = null; // oder undefined, je nach Bedarf
|
||||||
}
|
|
||||||
|
|
||||||
return ergebnis as T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLogger(name:string, level: number = INFO, options:any = {}){
|
return ergebnis as T;
|
||||||
return _createLogger({
|
}
|
||||||
name,
|
|
||||||
streams:[{level, stream: new ConsoleFormattedStream()}],
|
|
||||||
serializers:stdSerializers,
|
|
||||||
src:true,
|
|
||||||
...options,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSessionStorageHandler = function(path,value,previous,applyData){
|
export function createLogger(name: string, level: number = INFO, options: any = {}) {
|
||||||
sessionStorage.setItem('criteria',JSON.stringify(this));
|
return _createLogger({
|
||||||
}
|
name,
|
||||||
|
streams: [{ level, stream: new ConsoleFormattedStream() }],
|
||||||
|
serializers: stdSerializers,
|
||||||
|
src: true,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getCriteriaStateObject(){
|
export const getSessionStorageHandler = function (path, value, previous, applyData) {
|
||||||
const initialState = createGenericObject<ListingCriteria>();
|
sessionStorage.setItem('criteria', JSON.stringify(this));
|
||||||
const storedState = sessionStorage.getItem('criteria');
|
};
|
||||||
return storedState ? JSON.parse(storedState) : initialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getListingType(listing:BusinessListing|CommercialPropertyListing):'business'|'commercialProperty'{
|
export function getCriteriaStateObject() {
|
||||||
return listing?.type<100?'business':'commercialProperty';
|
const initialState = createGenericObject<ListingCriteria>();
|
||||||
|
const storedState = sessionStorage.getItem('criteria');
|
||||||
|
return storedState ? JSON.parse(storedState) : initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getListingType(listing: BusinessListing | CommercialPropertyListing): 'business' | 'commercialProperty' {
|
||||||
|
return listing?.type < 100 ? 'business' : 'commercialProperty';
|
||||||
|
}
|
||||||
|
export function isBusinessListing(listing: BusinessListing | CommercialPropertyListing): listing is BusinessListing {
|
||||||
|
return listing?.type < 100;
|
||||||
|
}
|
||||||
|
export function isCommercialPropertyListing(listing: BusinessListing | CommercialPropertyListing): listing is CommercialPropertyListing {
|
||||||
|
return listing?.type >= 100;
|
||||||
|
}
|
||||||
|
export function routeListingWithState(router: Router, value: string, data: any) {
|
||||||
|
if (value === 'business') {
|
||||||
|
router.navigate(['createBusinessListing'], { state: { data } });
|
||||||
|
} else {
|
||||||
|
router.navigate(['createCommercialPropertyListing'], { state: { data } });
|
||||||
}
|
}
|
||||||
export function isBusinessListing(listing: BusinessListing | CommercialPropertyListing): listing is BusinessListing {
|
}
|
||||||
return listing?.type < 100;
|
|
||||||
}
|
|
||||||
export function isCommercialPropertyListing(listing: BusinessListing | CommercialPropertyListing): listing is CommercialPropertyListing {
|
|
||||||
return listing?.type >= 100;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue