diff --git a/bizmatch-server/src/listings/commercial-property-listings.controller.ts b/bizmatch-server/src/listings/commercial-property-listings.controller.ts index 9bd5113..f170c1f 100644 --- a/bizmatch-server/src/listings/commercial-property-listings.controller.ts +++ b/bizmatch-server/src/listings/commercial-property-listings.controller.ts @@ -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 { + 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){ diff --git a/bizmatch-server/src/listings/listings.service.ts b/bizmatch-server/src/listings/listings.service.ts index e75d777..734f6a7 100644 --- a/bizmatch-server/src/listings/listings.service.ts +++ b/bizmatch-server/src/listings/listings.service.ts @@ -71,12 +71,16 @@ export class ListingsService { } async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise { - 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 { + 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{ - // 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 { - // // 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 { - // 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 { - // 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 - // } - - - - } diff --git a/bizmatch/src/app/app.routes.ts b/bizmatch/src/app/app.routes.ts index 7daa4d5..068ac27 100644 --- a/bizmatch/src/app/app.routes.ts +++ b/bizmatch/src/app/app.routes.ts @@ -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, diff --git a/bizmatch/src/app/components/header/header.component.ts b/bizmatch/src/app/components/header/header.component.ts index add75b9..3a25b15 100644 --- a/bizmatch/src/app/components/header/header.component.ts +++ b/bizmatch/src/app/components/header/header.component.ts @@ -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: '', }, ]; diff --git a/bizmatch/src/app/pages/details/details-user/details-user.component.html b/bizmatch/src/app/pages/details/details-user/details-user.component.html index 954cfd1..5d0ca4e 100644 --- a/bizmatch/src/app/pages/details/details-user/details-user.component.html +++ b/bizmatch/src/app/pages/details/details-user/details-user.component.html @@ -95,7 +95,7 @@
@for (listing of userListings; track listing) { -
+
diff --git a/bizmatch/src/app/pages/menu-account/menu-account.component.html b/bizmatch/src/app/pages/menu-account/menu-account.component.html index 4d9046e..9f31a9d 100644 --- a/bizmatch/src/app/pages/menu-account/menu-account.component.html +++ b/bizmatch/src/app/pages/menu-account/menu-account.component.html @@ -1,42 +1,62 @@ - \ No newline at end of file + diff --git a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html index 8841929..44121c1 100644 --- a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html +++ b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.html @@ -1,155 +1,160 @@
-
- - -
-
{{mode==='create'?'New':'Edit'}} Listing
- -
-
-
- - -
-
- - -
-
-
- - - - - -
-
-
- - -
-
-
- - -
-
- - -
-
-
+
+ + +
+
{{ mode === 'create' ? 'New' : 'Edit' }} Listing
+ +
+
+
+ + +
+
+ + +
+
+
+ + + + +
- -
-
-
-
- - - -
-
- - -
-
-
-
- - -
- -
-
-
- - -
-
- - -
-
-
-
- - Real Estate Included -
-
- - Leased Location -
-
- - Franchise Re-Sale -
-
-
- - - -
-
- - -
-
-
- - -
-
- - -
-
-
- - -
-
-
- - - - - Draft Mode (Will not be shown as public - listing) -
-
-
- @if (mode==='create'){ - - } @else { - - } -
-
+
+
+ + +
+
+
+ +
+
+ + +
+
+
+ +
+
+
+
+ + + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + Real Estate Included +
+
+ + Leased Location +
+
+ + Franchise Re-Sale +
+
+
+ + + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + + + + Draft Mode (Will not be shown as public listing) +
+
+
+ @if (mode==='create'){ + + } @else { + + } +
+
+
+
- \ No newline at end of file + diff --git a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts index 5a32c2a..73045e0 100644 --- a/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-business-listing/edit-business-listing.component.ts @@ -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(); - 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) - } - } diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html index f56dd90..d3f753e 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.html @@ -1,110 +1,126 @@
-
- - -
-
{{mode==='create'?'New':'Edit'}} Listing
- -
-
-
- - -
-
- - -
-
-
- - - - - -
-
-
- - -
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
-
+
+ + +
+
{{ mode === 'create' ? 'New' : 'Edit' }} Listing
+ +
+
+
+ + +
+
+ + +
+
+
+ + + + +
- -
-
-
-
- - - -
-
-
- Property Pictures - - - -
-
-
-
- @for (image of propertyImages; track image) { - -
- - - -
-
- } -
- -
- @if (mode==='create'){ - - } @else { - - } -
-
+
+
+ + +
+
+
+ +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+
+ + + +
+
+
+ Property Pictures + (Pictures can be uploaded once the listing is posted initially) + + + +
+
+
+ @if (propertyImages?.length>0){ +
+ @for (image of propertyImages; track image) { + +
+ + +
+
+ } +
+ } +
+ @if (mode==='create'){ + + } @else { + + } +
+
+
+
- \ No newline at end of file + diff --git a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts index 27c34e2..d16fb4f 100644 --- a/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-commercial-property-listing/edit-commercial-property-listing.component.ts @@ -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(); - 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){ - 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)); + if (cropper) { + this.loadingService.startLoading('uploadImage'); + cropper.getCroppedCanvas().toBlob(async blob => { + this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe( + async event => { + if (event.type === HttpEventType.Response) { + console.log('Upload abgeschlossen', event.body); + this.loadingService.stopLoading('uploadImage'); + this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); + } + }, + error => console.error('Fehler beim Upload:', error), + ); }, 'image/jpg'); cropper.destroy(); } - }) + }); } deleteConfirm(imageName: string) { @@ -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); } - } diff --git a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html index e69de29..a063a02 100644 --- a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html +++ b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.html @@ -0,0 +1,216 @@ + diff --git a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts index c3ea856..e9a73e6 100644 --- a/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/edit-listing/edit-listing.component.ts @@ -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(); - 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){ - 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)); + if (cropper) { + this.loadingService.startLoading('uploadImage'); + cropper.getCroppedCanvas().toBlob(async blob => { + this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe( + async event => { + if (event.type === HttpEventType.Response) { + console.log('Upload abgeschlossen', event.body); + this.loadingService.stopLoading('uploadImage'); + this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id); + } + }, + error => console.error('Fehler beim Upload:', error), + ); }, 'image/jpg'); cropper.destroy(); } - }) + }); } deleteConfirm(imageName: string) { @@ -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); } - } diff --git a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html index 26f655f..c5fc30c 100644 --- a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html +++ b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.html @@ -1,33 +1,45 @@ -
-
- - - -
-
My Listings
- - - - - Title - Category - Located in - - - - - - {{ listing.title }} - {{ selectOptions.getListingsCategory(listing.listingsCategory) }} - {{ selectOptions.getState(listing.location) }} - - - - - - - -
+
+ + + +
+
My Listings
+ + + + + Title + Category + Located in + + + + + + {{ listing.title }} + {{ selectOptions.getListingsCategory(listing.listingsCategory) }} + {{ selectOptions.getState(listing.city) }} + + @if(isBusinessListing(listing)){ + + } @if(isCommercialPropertyListing(listing)){ + + } + + + + +
-
\ No newline at end of file +
+
diff --git a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts index ced4b55..2adda0c 100644 --- a/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts +++ b/bizmatch/src/app/pages/subscription/my-listing/my-listing.component.ts @@ -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 =[]//dataListings as unknown as Array; - myListings: Array - 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); - } - - 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); + listings: Array = []; //dataListings as unknown as Array; + myListings: Array; + 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); } - confirm(event: Event,listing:ListingType) { + async deleteListing(listing: ListingType) { + await this.listingsService.deleteListing(listing.id, getListingType(listing)); + this.myListings = await this.listingsService.getListingByUserId(this.userId); + } + + confirm(event: Event, listing: ListingType) { this.confirmationService.confirm({ - 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); + }, }); -} + } } diff --git a/bizmatch/src/app/services/listings.service.ts b/bizmatch/src/app/services/listings.service.ts index cf1cf2e..c5d2820 100644 --- a/bizmatch/src/app/services/listings.service.ts +++ b/bizmatch/src/app/services/listings.service.ts @@ -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{ // return this.http.get(`${this.apiBaseUrl}/bizmatch/business-listings`); // } - async getListings(criteria:ListingCriteria,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'):Promise{ - const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/search`,criteria)); + async getListings(criteria: ListingCriteria, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty'): Promise { + const result = await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/search`, criteria)); return result.data; } - getListingById(id:string,listingsCategory?:'business'|'commercialProperty'):Observable{ + getListingById(id: string, listingsCategory?: 'business' | 'commercialProperty'): Observable { const result = this.http.get(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`); return result; } - getListingByUserId(userid:string):Promise{ + getListingByUserId(userid: string): Promise { return lastValueFrom(this.http.get(`${this.apiBaseUrl}/bizmatch/listings/business/user/${userid}`)); } - async save(listing:any,listingsCategory:'business'|'professionals_brokers'|'commercialProperty'){ - await lastValueFrom(this.http.post(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`,listing)); + async save(listing: any, listingsCategory: 'business' | 'professionals_brokers' | 'commercialProperty') { + if (listing.id) { + return await lastValueFrom(this.http.put(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}`, listing)); + } else { + return await lastValueFrom(this.http.post(`${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(`${this.apiBaseUrl}/bizmatch/listings/${listingsCategory}/${id}`)); } - async getPropertyImages(id:string):Promise{ + async getPropertyImages(id: string): Promise { return await lastValueFrom(this.http.get(`${this.apiBaseUrl}/bizmatch/image/${id}`)); } - async changeImageOrder(id:string, propertyImages: ImageProperty[]):Promise{ - return await lastValueFrom(this.http.put(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/imageOrder/${id}`,propertyImages)); + async changeImageOrder(id: string, propertyImages: ImageProperty[]): Promise { + return await lastValueFrom(this.http.put(`${this.apiBaseUrl}/bizmatch/listings/commercialProperty/imageOrder/${id}`, propertyImages)); } } diff --git a/bizmatch/src/app/services/user.service.ts b/bizmatch/src/app/services/user.service.ts index c07434e..a9b56dd 100644 --- a/bizmatch/src/app/services/user.service.ts +++ b/bizmatch/src/app/services/user.service.ts @@ -75,7 +75,17 @@ export class UserService { getUserObservable(): Observable { return this.user$; } + async getId(): Promise { + 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) { diff --git a/bizmatch/src/app/utils/utils.ts b/bizmatch/src/app/utils/utils.ts index 6333936..5683d0e 100644 --- a/bizmatch/src/app/utils/utils.ts +++ b/bizmatch/src/app/utils/utils.ts @@ -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 { - // Ein leeres Objekt vom Typ T erstellen - const ergebnis: Partial = {}; - - // 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; + // Ein leeres Objekt vom Typ T erstellen + const ergebnis: Partial = {}; + + // 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(); - 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 getCriteriaStateObject() { + const initialState = createGenericObject(); + const storedState = sessionStorage.getItem('criteria'); + return storedState ? JSON.parse(storedState) : initialState; +} + +export function getListingType(listing: BusinessListing | CommercialPropertyListing): 'business' | 'commercialProperty' { + return listing?.type < 100 ? 'business' : 'commercialProperty'; +} +export function isBusinessListing(listing: BusinessListing | CommercialPropertyListing): listing is BusinessListing { + return listing?.type < 100; +} +export function isCommercialPropertyListing(listing: BusinessListing | CommercialPropertyListing): listing is CommercialPropertyListing { + return listing?.type >= 100; +} +export function routeListingWithState(router: Router, value: string, data: any) { + if (value === 'business') { + router.navigate(['createBusinessListing'], { state: { data } }); + } else { + router.navigate(['createCommercialPropertyListing'], { state: { data } }); } - export function isBusinessListing(listing: BusinessListing | CommercialPropertyListing): listing is BusinessListing { - return listing?.type < 100; - } - export function isCommercialPropertyListing(listing: BusinessListing | CommercialPropertyListing): listing is CommercialPropertyListing { - return listing?.type >= 100; - } \ No newline at end of file +}