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);
|
||||
// }
|
||||
@Post('search')
|
||||
find(@Body() criteria: ListingCriteria): any {
|
||||
return this.listingsService.findListingsByCriteria(criteria,commercials);
|
||||
async find(@Body() criteria: ListingCriteria): Promise<any> {
|
||||
return await this.listingsService.findListingsByCriteria(criteria,commercials);
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() listing: any){
|
||||
async create(@Body() listing: any){
|
||||
this.logger.info(`Save Listing`);
|
||||
this.listingsService.createListing(listing,commercials)
|
||||
return await this.listingsService.createListing(listing,commercials)
|
||||
}
|
||||
@Put()
|
||||
update(@Body() listing: any){
|
||||
async update(@Body() listing: any){
|
||||
this.logger.info(`Save Listing`);
|
||||
this.listingsService.updateListing(listing.id,listing,commercials)
|
||||
return await this.listingsService.updateListing(listing.id,listing,commercials)
|
||||
}
|
||||
@Delete(':id')
|
||||
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> {
|
||||
const newListing = { data, created: data.created, updated: data.updated, visits: 0, last_visit: null }
|
||||
const [createdListing] = await this.conn.insert(table).values(newListing).returning();
|
||||
data.created=new Date()
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
return updateListing as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
|
|
@ -107,95 +111,5 @@ export class ListingsService {
|
|||
listing.imageOrder.push(imagename);
|
||||
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',
|
||||
component: HomeComponent,
|
||||
},
|
||||
// #########
|
||||
// Listings Details
|
||||
{
|
||||
path: 'details-business-listing/:id',
|
||||
component: DetailsBusinessListingComponent,
|
||||
|
|
@ -44,15 +46,21 @@ export const routes: Routes = [
|
|||
path: 'details-commercial-property-listing/:id',
|
||||
component: DetailsCommercialPropertyListingComponent,
|
||||
},
|
||||
// #########
|
||||
// User Details
|
||||
{
|
||||
path: 'details-user/:id',
|
||||
component: DetailsUserComponent,
|
||||
},
|
||||
// #########
|
||||
// User edit
|
||||
{
|
||||
path: 'account',
|
||||
component: AccountComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// Create, Update Listings
|
||||
{
|
||||
path: 'editBusinessListing/:id',
|
||||
component: EditBusinessListingComponent,
|
||||
|
|
@ -73,26 +81,36 @@ export const routes: Routes = [
|
|||
component: EditCommercialPropertyListingComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// My Listings
|
||||
{
|
||||
path: 'myListings',
|
||||
component: MyListingComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// My Favorites
|
||||
{
|
||||
path: 'myFavorites',
|
||||
component: FavoritesComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// EMAil Us
|
||||
{
|
||||
path: 'emailUs',
|
||||
component: EmailUsComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// Logout
|
||||
{
|
||||
path: 'logout',
|
||||
component: LogoutComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
// #########
|
||||
// Pricing
|
||||
{
|
||||
path: 'pricing',
|
||||
component: PricingComponent,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export class HeaderComponent {
|
|||
{
|
||||
label: 'Create Listing',
|
||||
icon: 'pi pi-plus-circle',
|
||||
routerLink: '/createListing',
|
||||
routerLink: '/createBusinessListing',
|
||||
visible: this.isUserLogedIn(),
|
||||
},
|
||||
{
|
||||
|
|
@ -90,13 +90,13 @@ export class HeaderComponent {
|
|||
fragment: '',
|
||||
},
|
||||
{
|
||||
label: 'Professionals/Brokers Directory',
|
||||
routerLink: '/brokerListings',
|
||||
label: 'Commercial Property',
|
||||
routerLink: '/commercialPropertyListings',
|
||||
fragment: '',
|
||||
},
|
||||
{
|
||||
label: 'Commercial Property',
|
||||
routerLink: '/commercialPropertyListings',
|
||||
label: 'Professionals/Brokers Directory',
|
||||
routerLink: '/brokerListings',
|
||||
fragment: '',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@
|
|||
<div class="text-900 w-full md:w-10">
|
||||
<div class="grid mt-0 mr-0">
|
||||
@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="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">
|
||||
|
|
|
|||
|
|
@ -1,42 +1,62 @@
|
|||
<div class="surface-ground ">
|
||||
<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">
|
||||
<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" >
|
||||
<i class="pi pi-user md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">Account</span>
|
||||
</a>
|
||||
</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">
|
||||
<i class="pi pi-plus-circle md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">Create Listing</span>
|
||||
</a>
|
||||
</li>
|
||||
<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-list md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Listings</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<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">
|
||||
<i class="pi pi-star md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Favorites</span>
|
||||
</a>
|
||||
</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>
|
||||
<div class="surface-ground">
|
||||
<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">
|
||||
<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">
|
||||
<i class="pi pi-user md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">Account</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
routerLink="/createBusinessListing"
|
||||
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>
|
||||
<span class="font-medium hidden md:block">Create Listing</span>
|
||||
</a>
|
||||
</li>
|
||||
<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-list md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Listings</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<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"
|
||||
>
|
||||
<i class="pi pi-star md:mr-2"></i>
|
||||
<span class="font-medium hidden md:block">My Favorites</span>
|
||||
</a>
|
||||
</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="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)]="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>
|
||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }"
|
||||
[modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<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 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]="listingsCategory"
|
||||
optionLabel="name"
|
||||
optionValue="value"
|
||||
(ngModelChange)="changeListingCategory($event)"
|
||||
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>
|
||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</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 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>
|
||||
<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>
|
||||
<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>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmDialog></p-confirmDialog>
|
||||
|
|
@ -1,98 +1,92 @@
|
|||
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 { 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 { 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 { GeoService } from '../../../services/geo.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { LoadingService } from '../../../services/loading.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
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 { 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({
|
||||
selector: 'create-listing',
|
||||
selector: 'business-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
||||
imports: [
|
||||
SharedModule,
|
||||
ArrayToStringPipe,
|
||||
InputNumberModule,
|
||||
CarouselModule,
|
||||
DialogModule,
|
||||
AngularCropperjsModule,
|
||||
FileUploadModule,
|
||||
EditorModule,
|
||||
DynamicDialogModule,
|
||||
DragDropModule,
|
||||
ConfirmDialogModule,
|
||||
MixedCdkDragDropModule,
|
||||
],
|
||||
providers: [MessageService, DialogService, ConfirmationService],
|
||||
templateUrl: './edit-business-listing.component.html',
|
||||
styleUrl: './edit-business-listing.component.scss'
|
||||
styleUrl: './edit-business-listing.component.scss',
|
||||
})
|
||||
export class EditBusinessListingComponent {
|
||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||
listingsCategory = 'commercialProperty'
|
||||
listingsCategory = 'business';
|
||||
category: string;
|
||||
location: string;
|
||||
mode: 'edit' | 'create';
|
||||
separator: '\n\n'
|
||||
listing: BusinessListing
|
||||
separator: '\n\n';
|
||||
listing: BusinessListing;
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user: User;
|
||||
maxFileSize = 3000000;
|
||||
uploadUrl: string;
|
||||
environment = environment;
|
||||
propertyImages: ImageProperty[]
|
||||
propertyImages: ImageProperty[];
|
||||
responsiveOptions = [
|
||||
{
|
||||
breakpoint: '1199px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '991px',
|
||||
numVisible: 2,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '767px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
}
|
||||
numScroll: 1,
|
||||
},
|
||||
];
|
||||
config = { aspectRatio: 16 / 9 }
|
||||
editorModules = TOOLBAR_OPTIONS
|
||||
config = { aspectRatio: 16 / 9 };
|
||||
editorModules = TOOLBAR_OPTIONS;
|
||||
dialogRef: DynamicDialogRef | undefined;
|
||||
draggedImage: ImageProperty
|
||||
draggedImage: ImageProperty;
|
||||
faTrash = faTrash;
|
||||
constructor(public selectOptions: SelectOptionsService,
|
||||
data: CommercialPropertyListing;
|
||||
constructor(
|
||||
public selectOptions: SelectOptionsService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
|
|
@ -102,106 +96,48 @@ export class EditBusinessListingComponent {
|
|||
private imageService: ImageService,
|
||||
private loadingService: LoadingService,
|
||||
public dialogService: DialogService,
|
||||
private confirmationService: ConfirmationService) {
|
||||
this.user = this.userService.getUser();
|
||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||
private confirmationService: ConfirmationService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
this.router.events.subscribe(event => {
|
||||
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() {
|
||||
if (this.mode === 'edit') {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||
} else {
|
||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
||||
sessionStorage.setItem('uuid', uuid);
|
||||
this.listing = createGenericObject<BusinessListing>();
|
||||
this.listing.id = uuid
|
||||
this.listing.userId = this.user.id
|
||||
this.listing.listingsCategory = 'business';
|
||||
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.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
}
|
||||
|
||||
async save() {
|
||||
sessionStorage.removeItem('uuid')
|
||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||
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 });
|
||||
}
|
||||
|
||||
suggestions: string[] | undefined;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
select(event: any) {
|
||||
const imageUrl = URL.createObjectURL(event.files[0]);
|
||||
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();
|
||||
}
|
||||
})
|
||||
changeListingCategory(value: 'business' | 'commercialProperty') {
|
||||
routeListingWithState(this.router, value, this.listing);
|
||||
}
|
||||
|
||||
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="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)]="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>
|
||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }"
|
||||
[modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
</div>
|
||||
<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="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 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]="listingsCategory"
|
||||
optionLabel="name"
|
||||
optionValue="value"
|
||||
(ngModelChange)="changeListingCategory($event)"
|
||||
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>
|
||||
<!-- <textarea id="description" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="listing.description"></textarea> -->
|
||||
<p-editor [(ngModel)]="listing.description" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</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>
|
||||
<!-- <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">
|
||||
</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>
|
||||
|
||||
<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 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="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>
|
||||
<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>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmDialog></p-confirmDialog>
|
||||
|
|
@ -1,98 +1,96 @@
|
|||
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 { 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 { 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 { GeoService } from '../../../services/geo.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { LoadingService } from '../../../services/loading.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
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 { 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({
|
||||
selector: 'create-listing',
|
||||
selector: 'commercial-property-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
||||
imports: [
|
||||
SharedModule,
|
||||
ArrayToStringPipe,
|
||||
InputNumberModule,
|
||||
CarouselModule,
|
||||
DialogModule,
|
||||
AngularCropperjsModule,
|
||||
FileUploadModule,
|
||||
EditorModule,
|
||||
DynamicDialogModule,
|
||||
DragDropModule,
|
||||
ConfirmDialogModule,
|
||||
MixedCdkDragDropModule,
|
||||
],
|
||||
providers: [MessageService, DialogService, ConfirmationService],
|
||||
templateUrl: './edit-commercial-property-listing.component.html',
|
||||
styleUrl: './edit-commercial-property-listing.component.scss'
|
||||
styleUrl: './edit-commercial-property-listing.component.scss',
|
||||
})
|
||||
export class EditCommercialPropertyListingComponent {
|
||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||
listingsCategory = 'commercialProperty'
|
||||
listingsCategory = 'commercialProperty';
|
||||
category: string;
|
||||
location: string;
|
||||
mode: 'edit' | 'create';
|
||||
separator: '\n\n'
|
||||
listing: CommercialPropertyListing
|
||||
separator: '\n\n';
|
||||
listing: CommercialPropertyListing;
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user: User;
|
||||
maxFileSize = 3000000;
|
||||
uploadUrl: string;
|
||||
environment = environment;
|
||||
propertyImages: ImageProperty[]
|
||||
propertyImages: ImageProperty[];
|
||||
responsiveOptions = [
|
||||
{
|
||||
breakpoint: '1199px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '991px',
|
||||
numVisible: 2,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '767px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
}
|
||||
numScroll: 1,
|
||||
},
|
||||
];
|
||||
config = { aspectRatio: 16 / 9 }
|
||||
editorModules = TOOLBAR_OPTIONS
|
||||
config = { aspectRatio: 16 / 9 };
|
||||
editorModules = TOOLBAR_OPTIONS;
|
||||
dialogRef: DynamicDialogRef | undefined;
|
||||
draggedImage: ImageProperty
|
||||
draggedImage: ImageProperty;
|
||||
faTrash = faTrash;
|
||||
constructor(public selectOptions: SelectOptionsService,
|
||||
suggestions: string[] | undefined;
|
||||
data: BusinessListing;
|
||||
userId: string;
|
||||
constructor(
|
||||
public selectOptions: SelectOptionsService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
|
|
@ -102,40 +100,43 @@ export class EditCommercialPropertyListingComponent {
|
|||
private imageService: ImageService,
|
||||
private loadingService: LoadingService,
|
||||
public dialogService: DialogService,
|
||||
private confirmationService: ConfirmationService) {
|
||||
this.user = this.userService.getUser();
|
||||
private confirmationService: ConfirmationService,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
// this.user = this.userService.getUser();
|
||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||
this.router.events.subscribe(event => {
|
||||
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() {
|
||||
if (this.mode === 'edit') {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||
} else {
|
||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
||||
sessionStorage.setItem('uuid', uuid);
|
||||
this.listing = createGenericObject<CommercialPropertyListing>();
|
||||
this.listing.id = uuid
|
||||
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.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
}
|
||||
|
||||
async save() {
|
||||
sessionStorage.removeItem('uuid')
|
||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||
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 });
|
||||
}
|
||||
|
||||
suggestions: string[] | undefined;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ export class EditCommercialPropertyListingComponent {
|
|||
data: {
|
||||
imageUrl: imageUrl,
|
||||
fileUpload: this.fileUpload,
|
||||
ratioVariable: false
|
||||
ratioVariable: false,
|
||||
},
|
||||
header: 'Edit Image',
|
||||
width: '50vw',
|
||||
|
|
@ -155,24 +156,27 @@ export class EditCommercialPropertyListingComponent {
|
|||
closable: false,
|
||||
breakpoints: {
|
||||
'960px': '75vw',
|
||||
'640px': '90vw'
|
||||
'640px': '90vw',
|
||||
},
|
||||
});
|
||||
this.dialogRef.onClose.subscribe(cropper => {
|
||||
if (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));
|
||||
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) {
|
||||
|
|
@ -181,27 +185,28 @@ export class EditCommercialPropertyListingComponent {
|
|||
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",
|
||||
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)
|
||||
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
},
|
||||
reject: () => {
|
||||
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
||||
console.log('deny')
|
||||
}
|
||||
console.log('deny');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
||||
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 { 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 { 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 { 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 { GeoService } from '../../../services/geo.service';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { LoadingService } from '../../../services/loading.service';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
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 { 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({
|
||||
selector: 'create-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
|
||||
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
|
||||
ConfirmDialogModule, MixedCdkDragDropModule],
|
||||
imports: [
|
||||
SharedModule,
|
||||
ArrayToStringPipe,
|
||||
InputNumberModule,
|
||||
CarouselModule,
|
||||
DialogModule,
|
||||
AngularCropperjsModule,
|
||||
FileUploadModule,
|
||||
EditorModule,
|
||||
DynamicDialogModule,
|
||||
DragDropModule,
|
||||
ConfirmDialogModule,
|
||||
MixedCdkDragDropModule,
|
||||
],
|
||||
providers: [MessageService, DialogService, ConfirmationService],
|
||||
templateUrl: './edit-listing.component.html',
|
||||
styleUrl: './edit-listing.component.scss'
|
||||
styleUrl: './edit-listing.component.scss',
|
||||
})
|
||||
export class EditListingComponent {
|
||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||
|
|
@ -62,37 +57,38 @@ export class EditListingComponent {
|
|||
category: string;
|
||||
location: string;
|
||||
mode: 'edit' | 'create';
|
||||
separator: '\n\n'
|
||||
listing: ListingType
|
||||
separator: '\n\n';
|
||||
listing: ListingType;
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user: User;
|
||||
maxFileSize = 3000000;
|
||||
uploadUrl: string;
|
||||
environment = environment;
|
||||
propertyImages: ImageProperty[]
|
||||
propertyImages: ImageProperty[];
|
||||
responsiveOptions = [
|
||||
{
|
||||
breakpoint: '1199px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '991px',
|
||||
numVisible: 2,
|
||||
numScroll: 1
|
||||
numScroll: 1,
|
||||
},
|
||||
{
|
||||
breakpoint: '767px',
|
||||
numVisible: 1,
|
||||
numScroll: 1
|
||||
}
|
||||
numScroll: 1,
|
||||
},
|
||||
];
|
||||
config = { aspectRatio: 16 / 9 }
|
||||
editorModules = TOOLBAR_OPTIONS
|
||||
config = { aspectRatio: 16 / 9 };
|
||||
editorModules = TOOLBAR_OPTIONS;
|
||||
dialogRef: DynamicDialogRef | undefined;
|
||||
draggedImage: ImageProperty
|
||||
draggedImage: ImageProperty;
|
||||
faTrash = faTrash;
|
||||
constructor(public selectOptions: SelectOptionsService,
|
||||
constructor(
|
||||
public selectOptions: SelectOptionsService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
|
|
@ -102,7 +98,8 @@ export class EditListingComponent {
|
|||
private imageService: ImageService,
|
||||
private loadingService: LoadingService,
|
||||
public dialogService: DialogService,
|
||||
private confirmationService: ConfirmationService) {
|
||||
private confirmationService: ConfirmationService,
|
||||
) {
|
||||
this.user = this.userService.getUser();
|
||||
// Abonniere Router-Events, um den aktiven Link zu ermitteln
|
||||
this.router.events.subscribe(event => {
|
||||
|
|
@ -110,7 +107,6 @@ export class EditListingComponent {
|
|||
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
async ngOnInit() {
|
||||
if (this.mode === 'edit') {
|
||||
|
|
@ -119,23 +115,23 @@ export class EditListingComponent {
|
|||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
||||
sessionStorage.setItem('uuid', uuid);
|
||||
this.listing = createGenericObject<BusinessListing>();
|
||||
this.listing.id = uuid
|
||||
this.listing.userId = this.user.id
|
||||
this.listing.id = uuid;
|
||||
this.listing.userId = this.user.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() {
|
||||
sessionStorage.removeItem('uuid')
|
||||
await this.listingsService.save(this.listing, getListingType(this.listing));
|
||||
sessionStorage.removeItem('uuid');
|
||||
// await this.listingsService.save(this.listing, this.listing.listingsCategory);
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing changes have been persisted', life: 3000 });
|
||||
}
|
||||
|
||||
suggestions: string[] | undefined;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +141,7 @@ export class EditListingComponent {
|
|||
data: {
|
||||
imageUrl: imageUrl,
|
||||
fileUpload: this.fileUpload,
|
||||
ratioVariable: false
|
||||
ratioVariable: false,
|
||||
},
|
||||
header: 'Edit Image',
|
||||
width: '50vw',
|
||||
|
|
@ -155,24 +151,27 @@ export class EditListingComponent {
|
|||
closable: false,
|
||||
breakpoints: {
|
||||
'960px': '75vw',
|
||||
'640px': '90vw'
|
||||
'640px': '90vw',
|
||||
},
|
||||
});
|
||||
this.dialogRef.onClose.subscribe(cropper => {
|
||||
if (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));
|
||||
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) {
|
||||
|
|
@ -181,27 +180,24 @@ export class EditListingComponent {
|
|||
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",
|
||||
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)
|
||||
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id);
|
||||
},
|
||||
reject: () => {
|
||||
// this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' });
|
||||
console.log('deny')
|
||||
}
|
||||
console.log('deny');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onDrop(event: { previousIndex: number; currentIndex: number }) {
|
||||
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="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmPopup></p-confirmPopup>
|
||||
<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>
|
||||
<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">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="wide-column">Title</th>
|
||||
<th>Category</th>
|
||||
<th>Located in</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-listing>
|
||||
<tr>
|
||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||
<td>{{ selectOptions.getListingsCategory(listing.listingsCategory) }}</td>
|
||||
<td>{{ selectOptions.getState(listing.location) }}</td>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success mr-2" [routerLink]="['/editListing',listing.id]"></button>
|
||||
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning" (click)="confirm($event,listing)"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmPopup></p-confirmPopup>
|
||||
<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>
|
||||
<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"
|
||||
>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="wide-column">Title</th>
|
||||
<th>Category</th>
|
||||
<th>Located in</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-listing>
|
||||
<tr>
|
||||
<td class="wide-column line-height-3">{{ listing.title }}</td>
|
||||
<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>
|
||||
|
|
@ -1,52 +1,53 @@
|
|||
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 { User } from '../../../../../../bizmatch-server/src/models/db.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({
|
||||
selector: 'app-my-listing',
|
||||
standalone: true,
|
||||
imports: [MenuAccountComponent, SharedModule],
|
||||
providers:[ConfirmationService,MessageService],
|
||||
providers: [ConfirmationService, MessageService],
|
||||
templateUrl: './my-listing.component.html',
|
||||
styleUrl: './my-listing.component.scss'
|
||||
styleUrl: './my-listing.component.scss',
|
||||
})
|
||||
export class MyListingComponent {
|
||||
user: User;
|
||||
listings: Array<ListingType> =[]//dataListings as unknown as Array<BusinessListing>;
|
||||
myListings: Array<ListingType>
|
||||
constructor(public userService: UserService,private listingsService:ListingsService, private cdRef:ChangeDetectorRef,public selectOptions:SelectOptionsService,private confirmationService: ConfirmationService,private messageService: MessageService){
|
||||
this.user=this.userService.getUser();
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
||||
this.myListings=this.listings.filter(l=>l.userId===this.user.id);
|
||||
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||
myListings: Array<ListingType>;
|
||||
userId: string;
|
||||
isBusinessListing = isBusinessListing;
|
||||
isCommercialPropertyListing = isCommercialPropertyListing;
|
||||
constructor(
|
||||
public userService: UserService,
|
||||
private listingsService: ListingsService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
public selectOptions: SelectOptionsService,
|
||||
private confirmationService: ConfirmationService,
|
||||
private messageService: MessageService,
|
||||
) {}
|
||||
async ngOnInit() {
|
||||
this.userId = await this.userService.getId();
|
||||
this.myListings = await this.listingsService.getListingByUserId(this.userId);
|
||||
}
|
||||
|
||||
async deleteListing(listing:ListingType){
|
||||
await this.listingsService.deleteListing(listing.id,getListingType(listing));
|
||||
// this.listings=await lastValueFrom(this.listingsService.getAllListings());
|
||||
this.myListings=this.listings.filter(l=>l.userId===this.user.id);
|
||||
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) {
|
||||
confirm(event: Event, listing: ListingType) {
|
||||
this.confirmationService.confirm({
|
||||
target: event.target as EventTarget,
|
||||
message: 'Are you sure you want to delet this listing?',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
accept: () => {
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been deleted', life: 3000 });
|
||||
this.deleteListing(listing);
|
||||
}
|
||||
target: event.target as EventTarget,
|
||||
message: 'Are you sure you want to delet this listing?',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
accept: () => {
|
||||
this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Listing has been deleted', life: 3000 });
|
||||
this.deleteListing(listing);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,45 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
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 { ImageProperty, ListingCriteria, ListingType, ResponseBusinessListingArray } from '../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ListingsService {
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
// getAllListings():Observable<ListingType[]>{
|
||||
// return this.http.get<ListingType[]>(`${this.apiBaseUrl}/bizmatch/business-listings`);
|
||||
// }
|
||||
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));
|
||||
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));
|
||||
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}`);
|
||||
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}`));
|
||||
}
|
||||
async save(listing:any,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'){
|
||||
await lastValueFrom(this.http.post<ListingType>(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`,listing));
|
||||
async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') {
|
||||
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}`));
|
||||
}
|
||||
async getPropertyImages(id:string):Promise<ImageProperty[]>{
|
||||
async getPropertyImages(id: string): Promise<ImageProperty[]> {
|
||||
return await lastValueFrom(this.http.get<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/image/${id}`));
|
||||
}
|
||||
async changeImageOrder(id:string, propertyImages: ImageProperty[]):Promise<ImageProperty[]>{
|
||||
return await lastValueFrom(this.http.put<ImageProperty[]>(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/imageOrder/${id}`,propertyImages));
|
||||
async changeImageOrder(id: string, propertyImages: ImageProperty[]): Promise<ImageProperty[]> {
|
||||
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> {
|
||||
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() {
|
||||
sessionStorage.removeItem('USERID');
|
||||
this.keycloak.logout(window.location.origin + '/home');
|
||||
}
|
||||
async login(url: string) {
|
||||
|
|
|
|||
|
|
@ -1,48 +1,56 @@
|
|||
import { INFO, ConsoleFormattedStream, createLogger as _createLogger, stdSerializers } from "browser-bunyan";
|
||||
import { ListingCriteria } from "../../../../bizmatch-server/src/models/main.model";
|
||||
import { BusinessListing, CommercialPropertyListing } from "../../../../bizmatch-server/src/models/db.model";
|
||||
import { Router } from '@angular/router';
|
||||
import { ConsoleFormattedStream, INFO, createLogger as _createLogger, stdSerializers } from 'browser-bunyan';
|
||||
import { BusinessListing, CommercialPropertyListing } from '../../../../bizmatch-server/src/models/db.model';
|
||||
import { ListingCriteria } from '../../../../bizmatch-server/src/models/main.model';
|
||||
|
||||
export function createGenericObject<T>(): T {
|
||||
// Ein leeres Objekt vom Typ T erstellen
|
||||
const ergebnis: Partial<T> = {};
|
||||
// Ein leeres Objekt vom Typ T erstellen
|
||||
const ergebnis: Partial<T> = {};
|
||||
|
||||
// Für ein reales Interface funktioniert diese direkte Iteration nicht,
|
||||
// da Interfaces zur Compile-Zeit entfernt werden. Stattdessen könnten Sie
|
||||
// ein Dummy-Objekt oder spezifische Typtransformationen verwenden.
|
||||
// Hier nur als Pseudocode dargestellt, um die Idee zu vermitteln:
|
||||
for (const key in ergebnis) {
|
||||
ergebnis[key] = null; // oder undefined, je nach Bedarf
|
||||
}
|
||||
|
||||
return ergebnis as T;
|
||||
// Für ein reales Interface funktioniert diese direkte Iteration nicht,
|
||||
// da Interfaces zur Compile-Zeit entfernt werden. Stattdessen könnten Sie
|
||||
// ein Dummy-Objekt oder spezifische Typtransformationen verwenden.
|
||||
// Hier nur als Pseudocode dargestellt, um die Idee zu vermitteln:
|
||||
for (const key in ergebnis) {
|
||||
ergebnis[key] = null; // oder undefined, je nach Bedarf
|
||||
}
|
||||
|
||||
export function createLogger(name:string, level: number = INFO, options:any = {}){
|
||||
return _createLogger({
|
||||
name,
|
||||
streams:[{level, stream: new ConsoleFormattedStream()}],
|
||||
serializers:stdSerializers,
|
||||
src:true,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
return ergebnis as T;
|
||||
}
|
||||
|
||||
export const getSessionStorageHandler = function(path,value,previous,applyData){
|
||||
sessionStorage.setItem('criteria',JSON.stringify(this));
|
||||
}
|
||||
export function createLogger(name: string, level: number = INFO, options: any = {}) {
|
||||
return _createLogger({
|
||||
name,
|
||||
streams: [{ level, stream: new ConsoleFormattedStream() }],
|
||||
serializers: stdSerializers,
|
||||
src: true,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export function getCriteriaStateObject(){
|
||||
const initialState = createGenericObject<ListingCriteria>();
|
||||
const storedState = sessionStorage.getItem('criteria');
|
||||
return storedState ? JSON.parse(storedState) : initialState;
|
||||
}
|
||||
export const getSessionStorageHandler = function (path, value, previous, applyData) {
|
||||
sessionStorage.setItem('criteria', JSON.stringify(this));
|
||||
};
|
||||
|
||||
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 getCriteriaStateObject() {
|
||||
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 } });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue