image-cropper component, drag & drop bilder
This commit is contained in:
parent
840d7a63b1
commit
89bb85a512
|
|
@ -30,8 +30,6 @@ export class FileService {
|
|||
return this.subscriptions
|
||||
}
|
||||
async storeProfilePicture(file: Express.Multer.File, userId: string) {
|
||||
// const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
// await fs.outputFile(`./pictures/profile/${userId}`, file.buffer);
|
||||
let quality = 50;
|
||||
const output = await sharp(file.buffer)
|
||||
.resize({ width: 300 })
|
||||
|
|
@ -45,7 +43,6 @@ export class FileService {
|
|||
}
|
||||
|
||||
async storeCompanyLogo(file: Express.Multer.File, userId: string) {
|
||||
// const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
let quality = 50;
|
||||
const output = await sharp(file.buffer)
|
||||
.resize({ width: 300 })
|
||||
|
|
@ -110,17 +107,11 @@ export class FileService {
|
|||
let quality = 50; // AVIF kann mit niedrigeren Qualitätsstufen gute Ergebnisse erzielen
|
||||
let output;
|
||||
let start = Date.now();
|
||||
// do {
|
||||
output = await sharp(buffer)
|
||||
.resize({ width: 1500 })
|
||||
.avif({ quality }) // Verwende AVIF
|
||||
//.webp({ quality }) // Verwende Webp
|
||||
.toBuffer();
|
||||
|
||||
// if (output.byteLength > maxSize) {
|
||||
// quality -= 5; // Justiere Qualität in feineren Schritten
|
||||
// }
|
||||
// } while (output.byteLength > maxSize && quality > 0);
|
||||
await sharp(output).toFile(`${directory}/${imageName}.avif`); // Ersetze Dateierweiterung
|
||||
let timeTaken = Date.now() - start;
|
||||
this.logger.info(`Quality: ${quality} - Time: ${timeTaken} milliseconds`)
|
||||
|
|
@ -141,4 +132,8 @@ export class FileService {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
deleteImage(path:string){
|
||||
fs.unlinkSync(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,24 +3,22 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
|||
import { Logger } from 'winston';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
import { SelectOptionsService } from '../select-options/select-options.service.js';
|
||||
|
||||
@Controller('image')
|
||||
export class ImageController {
|
||||
|
||||
constructor(private fileService:FileService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
||||
constructor(private fileService:FileService,
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
private selectOptions:SelectOptionsService) {
|
||||
}
|
||||
|
||||
@Post('uploadPropertyPicture/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
async uploadFile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
async uploadPropertyPicture(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
await this.fileService.storePropertyPicture(file,id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async getPropertyImagesById(@Param('id') id:string): Promise<any> {
|
||||
return await this.fileService.getPropertyImages(id);
|
||||
}
|
||||
|
||||
@Post('uploadProfile/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
async uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
|
|
@ -33,6 +31,10 @@ export class ImageController {
|
|||
await this.fileService.storeCompanyLogo(file,id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async getPropertyImagesById(@Param('id') id:string): Promise<any> {
|
||||
return await this.fileService.getPropertyImages(id);
|
||||
}
|
||||
@Get('profileImages/:userids')
|
||||
async getProfileImagesForUsers(@Param('userids') userids:string): Promise<any> {
|
||||
return await this.fileService.getProfileImagesForUsers(userids);
|
||||
|
|
@ -41,4 +43,16 @@ export class ImageController {
|
|||
async getCompanyLogosForUsers(@Param('userids') userids:string): Promise<any> {
|
||||
return await this.fileService.getCompanyLogosForUsers(userids);
|
||||
}
|
||||
@Delete('propertyPicture/:listingid/:imagename')
|
||||
async deletePropertyImagesById(@Param('listingid') listingid:string,@Param('imagename') imagename:string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/property/${listingid}/${imagename}`)
|
||||
}
|
||||
@Delete('logo/:userid/')
|
||||
async deleteLogoImagesById(@Param('id') id:string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/property//${id}`)
|
||||
}
|
||||
@Delete('profile/:userid/')
|
||||
async deleteProfileImagesById(@Param('id') id:string): Promise<any> {
|
||||
this.fileService.deleteImage(`pictures/property//${id}`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
|||
import { ImageController } from './image.controller.js';
|
||||
import { ImageService } from './image.service.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
import { SelectOptionsService } from '../select-options/select-options.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [ImageController],
|
||||
providers: [ImageService,FileService]
|
||||
providers: [ImageService,FileService,SelectOptionsService]
|
||||
})
|
||||
export class ImageModule {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { KeyValue, KeyValueStyle } from '../models/main.model.js';
|
||||
import { ImageType, KeyValue, KeyValueStyle } from '../models/main.model.js';
|
||||
|
||||
@Injectable()
|
||||
export class SelectOptionsService {
|
||||
|
|
@ -45,6 +45,11 @@ export class SelectOptionsService {
|
|||
{ name: 'Broker', value: 'broker', icon:'pi-image',bgColorClass:'bg-green-100',textColorClass:'text-green-600' },
|
||||
{ name: 'Professional', value: 'professional', icon:'pi-globe',bgColorClass:'bg-yellow-100',textColorClass:'text-yellow-600' },
|
||||
]
|
||||
public imageTypes:ImageType[] = [
|
||||
{name:'propertyPicture',upload:'uploadPropertyPicture',delete:'propertyPicture'},
|
||||
{name:'companyLogo',upload:'uploadCompanyLogo',delete:'logo'},
|
||||
{name:'profile',upload:'uploadProfile',delete:'profile'},
|
||||
]
|
||||
private usStates = [
|
||||
{ name: 'ALABAMA', abbreviation: 'AL'},
|
||||
{ name: 'ALASKA', abbreviation: 'AK'},
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.2.2",
|
||||
"@angular/cdk": "^17.3.2",
|
||||
"@angular/common": "^17.2.2",
|
||||
"@angular/compiler": "^17.2.2",
|
||||
"@angular/core": "^17.2.2",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<angular-cropper #cropper [imageUrl]="imageUrl" [cropperOptions]="cropperConfig"></angular-cropper>
|
||||
<div class="flex justify-content-between mt-3">
|
||||
@if(ratioVariable){
|
||||
<div>
|
||||
<p-selectButton [options]="stateOptions" [ngModel]="value" (ngModelChange)="changeAspectRation($event)"
|
||||
optionLabel="label" optionValue="value"></p-selectButton>
|
||||
</div>
|
||||
} @else {
|
||||
<div></div>
|
||||
}
|
||||
<div>
|
||||
<p-button icon="pi" (click)="cancelUpload()" label="Cancel" [outlined]="true"></p-button>
|
||||
<p-button icon="pi pi-check" (click)="sendImage()" label="Finish" pAutoFocus [autofocus]="true"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import { Component, ViewChild } from '@angular/core';
|
||||
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
||||
import { LoadingService } from '../../services/loading.service';
|
||||
import { ImageService } from '../../services/image.service';
|
||||
import { HttpEventType } from '@angular/common/http';
|
||||
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { KeyValueRatio, User } from '../../../../../common-models/src/main.model';
|
||||
import { SharedModule } from '../../shared/shared/shared.module';
|
||||
import { SelectButtonModule } from 'primeng/selectbutton';
|
||||
export const stateOptions:KeyValueRatio[]=[
|
||||
{label:'16/9',value:16/9},
|
||||
{label:'1/1',value:1},
|
||||
{label:'2/3',value:2/3},
|
||||
]
|
||||
@Component({
|
||||
selector: 'app-image-cropper',
|
||||
standalone: true,
|
||||
imports: [SharedModule,FileUploadModule,AngularCropperjsModule,SelectButtonModule],
|
||||
templateUrl: './image-cropper.component.html',
|
||||
styleUrl: './image-cropper.component.scss'
|
||||
})
|
||||
export class ImageCropperComponent {
|
||||
@ViewChild(CropperComponent) public angularCropper: CropperComponent;
|
||||
imageUrl:string; //wird im Template verwendet
|
||||
fileUpload:FileUpload
|
||||
value:number = stateOptions[0].value;
|
||||
cropperConfig={aspectRatio: this.value}
|
||||
ratioVariable:boolean
|
||||
stateOptions=stateOptions
|
||||
constructor(
|
||||
private loadingService:LoadingService,
|
||||
private imageUploadService: ImageService,
|
||||
public config: DynamicDialogConfig,
|
||||
public ref: DynamicDialogRef
|
||||
){}
|
||||
ngOnInit(): void {
|
||||
if (this.config.data) {
|
||||
this.imageUrl = this.config.data.imageUrl;
|
||||
this.fileUpload = this.config.data.fileUpload;
|
||||
this.cropperConfig = this.config.data.config ? this.config.data.config: this.cropperConfig;
|
||||
this.ratioVariable = this.config.data.ratioVariable;
|
||||
}
|
||||
}
|
||||
sendImage(){
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
setTimeout(()=>{
|
||||
this.angularCropper.cropper.getCroppedCanvas().toBlob(async(blob) => {
|
||||
this.fileUpload.clear()
|
||||
this.ref.close(blob);
|
||||
}, 'image/png');
|
||||
})
|
||||
}
|
||||
|
||||
cancelUpload(){
|
||||
this.fileUpload.clear();
|
||||
this.ref.close();
|
||||
}
|
||||
changeAspectRation(ratio:number){
|
||||
this.cropperConfig={aspectRatio: ratio}
|
||||
this.angularCropper.cropper.setAspectRatio(ratio);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Description</div>
|
||||
<div class="text-900 w-full md:w-10 line-height-3">{{listing?.description}}</div>
|
||||
<div class="text-900 w-full md:w-10 line-height-3" [innerHTML]="description"></div>
|
||||
</li>
|
||||
@if (listing && (listing.listingsCategory==='business')){
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { MessageService } from 'primeng/api';
|
|||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { GalleriaModule } from 'primeng/galleria';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
@Component({
|
||||
selector: 'app-details-listing',
|
||||
standalone: true,
|
||||
|
|
@ -59,13 +60,15 @@ export class DetailsListingComponent {
|
|||
propertyImages: ImageProperty[] = []
|
||||
environment = environment;
|
||||
user:User
|
||||
description:SafeHtml;
|
||||
constructor(private activatedRoute: ActivatedRoute,
|
||||
private listingsService: ListingsService,
|
||||
private router: Router,
|
||||
private userService: UserService,
|
||||
public selectOptions: SelectOptionsService,
|
||||
private mailService: MailService,
|
||||
private messageService: MessageService) {
|
||||
private messageService: MessageService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
this.userService.getUserObservable().subscribe(user => {
|
||||
this.user = user
|
||||
});
|
||||
|
|
@ -76,6 +79,7 @@ export class DetailsListingComponent {
|
|||
async ngOnInit() {
|
||||
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, this.type));
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
||||
this.description=this.sanitizer.bypassSecurityTrustHtml(this.listing.description);
|
||||
}
|
||||
back() {
|
||||
this.router.navigate(['listings', this.criteria.listingsCategory])
|
||||
|
|
|
|||
|
|
@ -56,34 +56,14 @@
|
|||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="companyOverview" class="block font-medium text-900 mb-2">Company Overview</label>
|
||||
<p-editor [(ngModel)]="user.companyOverview" [style]="{ height: '320px' }">
|
||||
<ng-template pTemplate="header">
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="ql-bold" aria-label="Bold"></button>
|
||||
<button type="button" class="ql-italic" aria-label="Italic"></button>
|
||||
<button type="button" class="ql-underline" aria-label="Underline"></button>
|
||||
<button value="ordered" aria-label="Ordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
<button value="bullet" aria-label="Unordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
</span>
|
||||
</ng-template>
|
||||
<p-editor [(ngModel)]="user.companyOverview" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="companyOverview" class="block font-medium text-900 mb-2">Services We offer</label>
|
||||
<p-editor [(ngModel)]="user.offeredServices" [style]="{ height: '320px' }">
|
||||
<ng-template pTemplate="header">
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="ql-bold" aria-label="Bold"></button>
|
||||
<button type="button" class="ql-italic" aria-label="Italic"></button>
|
||||
<button type="button" class="ql-underline" aria-label="Underline"></button>
|
||||
<button value="ordered" aria-label="Ordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
<button value="bullet" aria-label="Unordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
</span>
|
||||
</ng-template>
|
||||
<p-editor [(ngModel)]="user.offeredServices" [style]="{ height: '320px' }" [modules]="editorModules">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
|
||||
|
|
@ -126,7 +106,7 @@
|
|||
<span class="font-medium text-900 mb-2">Company Logo</span>
|
||||
<span class="font-medium text-xs mb-2">(is shown in every offer)</span>
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="{{environment.apiBaseUrl}}/logo/{{user.id}}.avif" class="rounded-profile" />
|
||||
<img src="{{companyLogoUrl}}" class="rounded-profile" />
|
||||
} @else {
|
||||
<img src="assets/images/placeholder.png" class="rounded-profile" />
|
||||
}
|
||||
|
|
@ -138,7 +118,7 @@
|
|||
<div class="flex flex-column align-items-center flex-or">
|
||||
<span class="font-medium text-900 mb-2">Your Profile Picture</span>
|
||||
@if(user.hasProfile){
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}.avif" class="rounded-profile" />
|
||||
<img src="{{profileUrl}}" class="rounded-profile" />
|
||||
} @else {
|
||||
<img src="assets/images/person_placeholder.jpg" class="rounded-profile" />
|
||||
}
|
||||
|
|
@ -212,8 +192,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-dialog header="Edit Image" [visible]="imageUrl" [modal]="true" [style]="{ width: '50vw' }" [draggable]="false" [resizable]="false">
|
||||
<!-- <app-cropper #cropper [imageUrl]="imageUrl"></app-cropper> -->
|
||||
<!-- <p-dialog header="Edit Image" [visible]="imageUrl" [modal]="true" [style]="{ width: '50vw' }" [draggable]="false" [resizable]="false">
|
||||
<angular-cropper #cropper [imageUrl]="imageUrl" [cropperOptions]="config"></angular-cropper>
|
||||
<ng-template pTemplate="footer" let-config="config">
|
||||
<div class="flex justify-content-between">
|
||||
|
|
@ -230,4 +209,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-dialog>
|
||||
</p-dialog> -->
|
||||
|
|
@ -32,69 +32,64 @@ import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
|||
import { ImageService } from '../../../services/image.service';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { SelectButtonModule } from 'primeng/selectbutton';
|
||||
import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { ImageCropperComponent, stateOptions } from '../../../components/image-cropper/image-cropper.component';
|
||||
import Quill from 'quill'
|
||||
import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
standalone: true,
|
||||
imports: [SharedModule,FileUploadModule,EditorModule,AngularCropperjsModule,DialogModule,SelectButtonModule],
|
||||
providers:[MessageService],
|
||||
imports: [SharedModule, FileUploadModule, EditorModule, AngularCropperjsModule, DialogModule, SelectButtonModule, DynamicDialogModule],
|
||||
providers: [MessageService, DialogService],
|
||||
templateUrl: './account.component.html',
|
||||
styleUrl: './account.component.scss'
|
||||
})
|
||||
export class AccountComponent {
|
||||
@ViewChild(CropperComponent) public angularCropper: CropperComponent;
|
||||
@ViewChild('companyUpload') public companyUpload: FileUpload;
|
||||
@ViewChild('profileUpload') public profileUpload: FileUpload;
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user:User;
|
||||
subscriptions:Array<Subscription>;
|
||||
userSubscriptions:Array<Subscription>=[];
|
||||
maxFileSize=1000000;
|
||||
companyLogoUrl:string;
|
||||
profileUrl:string;
|
||||
imageUrl;
|
||||
type:'company'|'profile'
|
||||
stateOptions:KeyValueRatio[]=[
|
||||
{label:'16/9',value:16/9},
|
||||
{label:'1/1',value:1},
|
||||
{label:'2/3',value:2/3},
|
||||
]
|
||||
value:number = this.stateOptions[0].value;
|
||||
config={aspectRatio: this.value}
|
||||
environment=environment
|
||||
user: User;
|
||||
subscriptions: Array<Subscription>;
|
||||
userSubscriptions: Array<Subscription> = [];
|
||||
maxFileSize = 1000000;
|
||||
companyLogoUrl: string;
|
||||
profileUrl: string;
|
||||
type: 'company' | 'profile'
|
||||
dialogRef: DynamicDialogRef | undefined;
|
||||
environment = environment
|
||||
editorModules = TOOLBAR_OPTIONS
|
||||
constructor(public userService: UserService,
|
||||
private subscriptionService: SubscriptionsService,
|
||||
private messageService: MessageService,
|
||||
private geoService:GeoService,
|
||||
public selectOptions:SelectOptionsService,
|
||||
private cdref:ChangeDetectorRef,
|
||||
private geoService: GeoService,
|
||||
public selectOptions: SelectOptionsService,
|
||||
private cdref: ChangeDetectorRef,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private loadingService:LoadingService,
|
||||
private imageUploadService: ImageService) {
|
||||
private loadingService: LoadingService,
|
||||
private imageUploadService: ImageService,
|
||||
public dialogService: DialogService) {}
|
||||
async ngOnInit() {
|
||||
this.user = await this.userService.getById(this.id);
|
||||
this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||
if (!this.user.licensedIn || this.user.licensedIn?.length === 0) {
|
||||
this.user.licensedIn = [{ name: '', value: '' }]
|
||||
}
|
||||
this.user = await this.userService.getById(this.user.id);
|
||||
this.profileUrl = this.user.hasProfile ? `${environment.apiBaseUrl}/profile/${this.user.id}.avif` : `/assets/images/placeholder.png`
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo ? `${environment.apiBaseUrl}/logo/${this.user.id}.avif` : `/assets/images/placeholder.png`
|
||||
}
|
||||
printInvoice(invoice: Invoice) { }
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
this.user=await this.userService.getById(this.id);
|
||||
this.userSubscriptions=await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||
if (!this.user.licensedIn || this.user.licensedIn?.length===0){
|
||||
this.user.licensedIn = [{name:'',value:''}]
|
||||
}
|
||||
this.user=await this.userService.getById(this.user.id);
|
||||
this.profileUrl = this.user.hasProfile?`${environment.apiBaseUrl}/profile/${this.user.id}.avif`:`/assets/images/placeholder.png`
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo?`${environment.apiBaseUrl}/logo/${this.user.id}.avif`:`/assets/images/placeholder.png`
|
||||
}
|
||||
printInvoice(invoice:Invoice){}
|
||||
|
||||
async updateProfile(user:User){
|
||||
//this.messageService.add({ severity: 'warn', summary: 'Information', detail: 'This function is not yet available, please send an email to info@bizmatch.net for changes to your customer data', life: 15000 });
|
||||
async updateProfile(user: User) {
|
||||
await this.userService.save(this.user);
|
||||
}
|
||||
|
||||
|
||||
onUploadCompanyLogo(event:any){
|
||||
onUploadCompanyLogo(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.companyLogoUrl = `${environment.apiBaseUrl}/logo/${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
||||
}
|
||||
onUploadProfilePicture(event:any){
|
||||
onUploadProfilePicture(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.profileUrl = `${environment.apiBaseUrl}/profile/${this.user.id}${uniqueSuffix}` //`http://IhrServer:Port/${newImagePath}${uniqueSuffix}`;
|
||||
}
|
||||
|
|
@ -107,68 +102,58 @@ export class AccountComponent {
|
|||
|
||||
async search(event: AutoCompleteCompleteEvent) {
|
||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query))
|
||||
this.suggestions = result.map(r=>`${r.city} - ${r.state_code}`).slice(0,5);
|
||||
this.suggestions = result.map(r => `${r.city} - ${r.state_code}`).slice(0, 5);
|
||||
}
|
||||
addLicence(){
|
||||
this.user.licensedIn.push({name:'',value:''});
|
||||
addLicence() {
|
||||
this.user.licensedIn.push({ name: '', value: '' });
|
||||
}
|
||||
removeLicence(){
|
||||
this.user.licensedIn.splice(this.user.licensedIn.length-2,1);
|
||||
removeLicence() {
|
||||
this.user.licensedIn.splice(this.user.licensedIn.length - 2, 1);
|
||||
}
|
||||
|
||||
select(event:any,type:'company'|'profile'){
|
||||
this.imageUrl = URL.createObjectURL(event.files[0]);
|
||||
this.type=type
|
||||
this.config={aspectRatio: type==='company'?this.stateOptions[0].value:this.stateOptions[2].value}
|
||||
}
|
||||
sendImage(){
|
||||
this.imageUrl=null
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
this.angularCropper.cropper.getCroppedCanvas().toBlob(async(blob) => {
|
||||
select(event: any, type: 'company' | 'profile') {
|
||||
const imageUrl = URL.createObjectURL(event.files[0]);
|
||||
this.type = type
|
||||
const config = { aspectRatio: type === 'company' ? stateOptions[0].value : stateOptions[2].value }
|
||||
this.dialogRef = this.dialogService.open(ImageCropperComponent, {
|
||||
data: {
|
||||
imageUrl: imageUrl,
|
||||
fileUpload: type === 'company' ? this.companyUpload : this.profileUpload,
|
||||
config: config,
|
||||
ratioVariable: type === 'company' ? true : false
|
||||
},
|
||||
header: 'Edit Image',
|
||||
width: '50vw',
|
||||
modal: true,
|
||||
closeOnEscape: true,
|
||||
keepInViewport: true,
|
||||
closable: false,
|
||||
breakpoints: {
|
||||
'960px': '75vw',
|
||||
'640px': '90vw'
|
||||
},
|
||||
});
|
||||
this.dialogRef.onClose.subscribe(blob => {
|
||||
if (blob) {
|
||||
this.imageUploadService.uploadImage(blob, type==='company'?'uploadCompanyLogo':'uploadProfile',this.user.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
if (this.type==='company'){
|
||||
this.imageUploadService.uploadCompanyLogo(blob,this.user.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
// Hier könntest du beispielsweise eine Fortschrittsanzeige aktualisieren
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
this.companyUpload.clear();
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
this.user.hasCompanyLogo=true;
|
||||
this.companyLogoUrl=`${environment.apiBaseUrl}/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`
|
||||
}
|
||||
}, error => console.error('Fehler beim Upload:', error));
|
||||
} else {
|
||||
this.imageUploadService.uploadProfileImage(blob,this.user.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
// Hier könntest du beispielsweise eine Fortschrittsanzeige aktualisieren
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
this.profileUpload.clear();
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
this.user.hasProfile=true;
|
||||
this.profileUrl=`${environment.apiBaseUrl}/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`
|
||||
}
|
||||
}
|
||||
}, error => console.error('Fehler beim Upload:', error));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// this.fileUpload.upload();
|
||||
}, 'image/png');
|
||||
}
|
||||
cancelUpload(){
|
||||
this.imageUrl=null
|
||||
if (this.type==='company'){
|
||||
this.companyUpload.clear();
|
||||
} else {
|
||||
this.profileUpload.clear();
|
||||
}
|
||||
}
|
||||
changeAspectRation(ratio:number){
|
||||
this.config={aspectRatio: ratio}
|
||||
this.angularCropper.cropper.setAspectRatio(ratio);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@
|
|||
<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>
|
||||
<!-- <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>
|
||||
@if (listing.listingsCategory==='business'){
|
||||
|
|
@ -77,7 +80,7 @@
|
|||
</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 Picture</span>
|
||||
<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"
|
||||
|
|
@ -91,7 +94,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-carousel [value]="propertyImages" [numVisible]="3" [numScroll]="3" [circular]="false" [responsiveOptions]="responsiveOptions">
|
||||
<!-- <p-carousel [value]="propertyImages" [numVisible]="3" [numScroll]="3" [circular]="false" [responsiveOptions]="responsiveOptions">
|
||||
<ng-template let-image pTemplate="item">
|
||||
<div class="border-1 surface-border border-round m-2 text-center py-5 px-3">
|
||||
<div class="mb-3">
|
||||
|
|
@ -100,12 +103,22 @@
|
|||
<div>
|
||||
<div class="mt-5 flex align-items-center justify-content-center gap-2">
|
||||
<p-button icon="pi pi-file-edit" [rounded]="true" />
|
||||
<p-button icon="fa-solid fa-trash-can" [rounded]="true" severity="danger"></p-button>
|
||||
<p-button icon="fa-solid fa-trash-can" [rounded]="true" severity="danger" (click)="deleteConfirm(image.name)"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-carousel>
|
||||
</p-carousel> -->
|
||||
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropList (cdkDropListDropped)="onDrop($event)"
|
||||
cdkDropListOrientation="horizontal">
|
||||
<ul >
|
||||
<li *ngFor="let image of propertyImages" class="p-2 border-round shadow-1" >
|
||||
<!-- <div class="image-container"> -->
|
||||
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{image.name}}" [alt]="image.name" class="shadow-2" cdkDrag/>
|
||||
<!-- </div> -->
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
@if (listing.listingsCategory==='business'){
|
||||
<div class="grid">
|
||||
|
|
@ -195,12 +208,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-dialog header="Edit Image" [visible]="imageUrl" [modal]="true" [style]="{ width: '50vw' }" [draggable]="false" [resizable]="false">
|
||||
<!-- <app-cropper #cropper [imageUrl]="imageUrl"></app-cropper> -->
|
||||
<angular-cropper #cropper [imageUrl]="imageUrl" [cropperOptions]="config"></angular-cropper>
|
||||
<ng-template pTemplate="footer">
|
||||
<p-button icon="pi" (click)="imageUrl = null" label="Cancel" [outlined]="true"></p-button>
|
||||
<p-button icon="pi pi-check" (click)="sendImage()" label="Finish" pAutoFocus [autofocus]="true"></p-button>
|
||||
</ng-template>
|
||||
</p-dialog>
|
||||
<p-toast></p-toast>
|
||||
<p-confirmDialog></p-confirmDialog>
|
||||
|
|
@ -1,10 +1,87 @@
|
|||
.translate-y-5{
|
||||
.translate-y-5 {
|
||||
transform: translateY(5px);
|
||||
}
|
||||
.image {
|
||||
width: 120px;
|
||||
height: 30px;
|
||||
border: 1px solid #6b7280;
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
// .image {
|
||||
// width: 120px;
|
||||
// height: 30px;
|
||||
// border: 1px solid #6b7280;
|
||||
// padding: 1px 1px;
|
||||
// object-fit: contain;
|
||||
// }
|
||||
|
||||
// .image-container img {
|
||||
// width: 200px;
|
||||
// box-shadow: 0 3px 6px #00000029, 0 3px 6px #0000003b;
|
||||
// margin-right: 1rem;
|
||||
// }
|
||||
|
||||
// .container {
|
||||
// width: 100%;
|
||||
// min-height: 200px;
|
||||
// border: 1px solid #ccc;
|
||||
// display: flex;
|
||||
// flex-wrap: wrap;
|
||||
// }
|
||||
.image-container {
|
||||
width: 100%;
|
||||
/* oder eine spezifische Breite */
|
||||
overflow-x: auto;
|
||||
/* Ermöglicht das Scrollen, wenn die Bilder zu breit sind */
|
||||
}
|
||||
|
||||
.image-container ul {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
/* Entfernt den Standard-Abstand des ul-Elements */
|
||||
margin: 0;
|
||||
/* Entfernt den Standard-Außenabstand des ul-Elements */
|
||||
list-style-type: none;
|
||||
/* Entfernt die Listenpunkte */
|
||||
}
|
||||
|
||||
.image-container li {
|
||||
flex: 1 1 auto;
|
||||
/* Erlaubt den li-Elementen, zu wachsen und zu schrumpfen, aber füllt den Raum gleichmäßig */
|
||||
/* Optional: Füge hier Abstände zwischen den li-Elementen hinzu */
|
||||
}
|
||||
|
||||
.image-container img {
|
||||
max-width: 100%;
|
||||
/* Stellt sicher, dass die Bilder nicht über ihre natürliche Größe hinaus wachsen */
|
||||
height: auto;
|
||||
/* Behält das Seitenverhältnis bei */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.draggable-image {
|
||||
margin: 8px;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.draggable-image:active {
|
||||
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
|
||||
0 8px 10px 1px rgba(0, 0, 0, 0.14),
|
||||
0 3px 14px 2px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
// .cdk-drag-preview {
|
||||
// box-shadow: 0 5px 5px rgba(0, 0, 0, 0.2);
|
||||
// }
|
||||
|
||||
// .cdk-drag-placeholder {
|
||||
// background-color: #ccc;
|
||||
// }
|
||||
|
||||
.drop-area {
|
||||
border: 2px dashed #ccc;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
/* CSS-Klasse für den Drop-Bereich, wenn ein Element darüber gezogen wird */
|
||||
.drop-area-active {
|
||||
border-color: #2196F3;
|
||||
background-color: #E3F2FD;
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import { lastValueFrom } from 'rxjs';
|
|||
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
import { AutoCompleteCompleteEvent, BusinessListing, CommercialPropertyListing, ImageProperty, ListingType, User } from '../../../../../../common-models/src/main.model';
|
||||
import { GeoResult, GeoService } from '../../../services/geo.service';
|
||||
import { InputNumberComponent, InputNumberModule } from '../../../components/inputnumber/inputnumber.component';
|
||||
|
|
@ -35,31 +35,36 @@ 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 { 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';
|
||||
@Component({
|
||||
selector: 'create-listing',
|
||||
standalone: true,
|
||||
imports: [SharedModule,ArrayToStringPipe, InputNumberModule,CarouselModule,DialogModule,AngularCropperjsModule,FileUploadModule],
|
||||
providers:[MessageService],
|
||||
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule, DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, ConfirmDialogModule,DragDropModule],
|
||||
providers: [MessageService, DialogService, ConfirmationService],
|
||||
templateUrl: './edit-listing.component.html',
|
||||
styleUrl: './edit-listing.component.scss'
|
||||
})
|
||||
export class EditListingComponent {
|
||||
@ViewChild(CropperComponent) public angularCropper: CropperComponent;
|
||||
@ViewChild(FileUpload) public fileUpload: FileUpload;
|
||||
listingCategory:'Business'|'Commercial Property';
|
||||
category:string;
|
||||
location:string;
|
||||
mode:'edit'|'create';
|
||||
separator:'\n\n'
|
||||
listing:ListingType
|
||||
listingCategory: 'Business' | 'Commercial Property';
|
||||
category: string;
|
||||
location: string;
|
||||
mode: 'edit' | 'create';
|
||||
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[]
|
||||
user: User;
|
||||
maxFileSize = 3000000;
|
||||
uploadUrl: string;
|
||||
environment = environment;
|
||||
propertyImages: ImageProperty[]
|
||||
responsiveOptions = [
|
||||
{
|
||||
breakpoint: '1199px',
|
||||
|
|
@ -77,79 +82,130 @@ export class EditListingComponent {
|
|||
numScroll: 1
|
||||
}
|
||||
];
|
||||
imageUrl
|
||||
config={aspectRatio: 16 / 9}
|
||||
constructor(public selectOptions:SelectOptionsService,
|
||||
config = { aspectRatio: 16 / 9 }
|
||||
editorModules = TOOLBAR_OPTIONS
|
||||
dialogRef: DynamicDialogRef | undefined;
|
||||
draggedImage:ImageProperty
|
||||
dropAreaActive = false;
|
||||
constructor(public selectOptions: SelectOptionsService,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private listingsService:ListingsService,
|
||||
private listingsService: ListingsService,
|
||||
public userService: UserService,
|
||||
private messageService: MessageService,
|
||||
private geoService:GeoService,
|
||||
private imageUploadService: ImageService,
|
||||
private loadingService:LoadingService){
|
||||
this.user=this.userService.getUser();
|
||||
private geoService: GeoService,
|
||||
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
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.mode = event.url==='/createListing'?'create':'edit';
|
||||
this.mode = event.url === '/createListing' ? 'create' : 'edit';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
if (this.mode==='edit'){
|
||||
this.listing=await lastValueFrom(this.listingsService.getListingById(this.id));
|
||||
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.temporary=true;
|
||||
this.listing.userId=this.user.id
|
||||
this.listing.listingsCategory='business';
|
||||
const uuid = sessionStorage.getItem('uuid') ? sessionStorage.getItem('uuid') : uuidv4();
|
||||
sessionStorage.setItem('uuid', uuid);
|
||||
this.listing = createGenericObject<BusinessListing>();
|
||||
this.listing.id = uuid
|
||||
this.listing.temporary = true;
|
||||
this.listing.userId = this.user.id
|
||||
this.listing.listingsCategory = 'business';
|
||||
}
|
||||
this.uploadUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${this.listing.id}`;
|
||||
this.propertyImages=await this.listingsService.getPropertyImages(this.listing.id)
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
||||
}
|
||||
|
||||
async save(){
|
||||
async save() {
|
||||
sessionStorage.removeItem('uuid')
|
||||
await this.listingsService.save(this.listing,this.listing.listingsCategory);
|
||||
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))
|
||||
this.suggestions = result.map(r=>r.city).slice(0,5);
|
||||
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){
|
||||
this.imageUrl = URL.createObjectURL(event.files[0]);
|
||||
}
|
||||
sendImage(){
|
||||
this.imageUrl=null
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
this.angularCropper.cropper.getCroppedCanvas().toBlob(async(blob) => {
|
||||
|
||||
this.imageUploadService.uploadPropertyImage(blob,this.listing.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
// Hier könntest du beispielsweise eine Fortschrittsanzeige aktualisieren
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
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(blob => {
|
||||
if (blob) {
|
||||
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
|
||||
if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
// Hier könntest du die Ladeanimation ausblenden
|
||||
this.propertyImages=await this.listingsService.getPropertyImages(this.listing.id)
|
||||
this.fileUpload.clear();
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
|
||||
}
|
||||
}, error => console.error('Fehler beim Upload:', error));
|
||||
|
||||
// this.fileUpload.upload();
|
||||
}, 'image/png');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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: CdkDragDrop<string[]>) {
|
||||
this.dropAreaActive = false;
|
||||
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
|
||||
//console.log(event.previousIndex, event.currentIndex);
|
||||
}
|
||||
|
||||
|
||||
onDragEnter(event: CdkDragEnter<any,any>) {
|
||||
this.dropAreaActive = true;
|
||||
}
|
||||
|
||||
onDragExit(event: CdkDragExit<any,any>) {
|
||||
this.dropAreaActive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
export const TOOLBAR_OPTIONS = {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline'], // Einige Standardoptionen
|
||||
[{'header': [1, 2, 3, false]}], // Benutzerdefinierte Header
|
||||
[{'list': 'ordered'}, {'list': 'bullet'}],
|
||||
[{'color': []}], // Dropdown mit Standardfarben
|
||||
['clean'] // Entfernt Formatierungen
|
||||
]
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http';
|
|||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ImageType } from '../../../../common-models/src/main.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -12,19 +13,8 @@ export class ImageService {
|
|||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
uploadPropertyImage(imageBlob: Blob,listingId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${listingId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadCompanyLogo(imageBlob: Blob,userId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadCompanyLogo/${userId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadProfileImage(imageBlob: Blob,userId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadProfile/${userId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadImage(imageBlob: Blob,uploadUrl:string) {
|
||||
uploadImage(imageBlob: Blob,type:'uploadPropertyPicture'|'uploadCompanyLogo'|'uploadProfile',id:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/${type}/${id}`;
|
||||
const formData = new FormData();
|
||||
formData.append('file', imageBlob, 'image.png');
|
||||
|
||||
|
|
@ -34,7 +24,12 @@ export class ImageService {
|
|||
observe: 'events',
|
||||
});
|
||||
}
|
||||
|
||||
async deleteUserImage(userid:string,type:ImageType,name?:string){
|
||||
return await lastValueFrom(this.http.delete<[]>(`${this.apiBaseUrl}/bizmatch/image/${type.delete}${userid}`));
|
||||
}
|
||||
async deleteListingImage(listingid:string,name?:string){
|
||||
return await lastValueFrom(this.http.delete<[]>(`${this.apiBaseUrl}/bizmatch/image/propertyPicture/${listingid}/${name}`));
|
||||
}
|
||||
async getProfileImagesForUsers(userids:string[]){
|
||||
return await lastValueFrom(this.http.get<[]>(`${this.apiBaseUrl}/bizmatch/image/profileImages/${userids.join(',')}`));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ export class LoadingService {
|
|||
public startLoading(type: string,request?:string): void {
|
||||
if (!this.loading$.value.includes(type)) {
|
||||
this.loading$.next(this.loading$.value.concat(type));
|
||||
if (type==='uploadImage' || request?.includes('uploadPropertyPicture')|| request?.includes('uploadProfile')|| request?.includes('uploadCompanyLogo')) {
|
||||
if (type==='uploadImage'
|
||||
|| request?.includes('uploadPropertyPicture')
|
||||
|| request?.includes('uploadProfile')
|
||||
|| request?.includes('uploadCompanyLogo')) {
|
||||
this.loadingTextSubject.next("Please wait - we're processing your image...");
|
||||
} else {
|
||||
this.loadingTextSubject.next(null);
|
||||
|
|
|
|||
|
|
@ -71,3 +71,8 @@ p-menubarsub ul {
|
|||
height: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.p-editor-container .ql-toolbar{
|
||||
background: #f9fafb;
|
||||
border-top-right-radius: 6px;
|
||||
border-top-left-radius: 6px;
|
||||
}
|
||||
|
|
@ -17,12 +17,15 @@ export type SelectOption<T = number> = {
|
|||
value: T;
|
||||
label: string;
|
||||
};
|
||||
export type ImageType = {
|
||||
name:'propertyPicture'|'companyLogo'|'profile',upload:string,delete:string,
|
||||
}
|
||||
export interface Listing {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: string; //enum
|
||||
title: string;
|
||||
description: Array<string>;
|
||||
description: string;
|
||||
country: string;
|
||||
city: string,
|
||||
state: string;//enum
|
||||
|
|
|
|||
Loading…
Reference in New Issue