diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index a0aec0f..f635a38 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -277,3 +277,7 @@ export function emailToDirName(email: string): string { return normalizedEmail; } export const LISTINGS_PER_PAGE = 12; +export interface ValidationMessage { + field: string; + message: string; +} diff --git a/bizmatch/src/app/components/validated-input/validated-input.component.html b/bizmatch/src/app/components/validated-input/validated-input.component.html new file mode 100644 index 0000000..437d665 --- /dev/null +++ b/bizmatch/src/app/components/validated-input/validated-input.component.html @@ -0,0 +1 @@ +

validated-input works!

diff --git a/bizmatch/src/app/components/validated-input/validated-input.component.scss b/bizmatch/src/app/components/validated-input/validated-input.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/bizmatch/src/app/components/validated-input/validated-input.component.ts b/bizmatch/src/app/components/validated-input/validated-input.component.ts new file mode 100644 index 0000000..7113b5d --- /dev/null +++ b/bizmatch/src/app/components/validated-input/validated-input.component.ts @@ -0,0 +1,72 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'app-validated-input', + template: ` +
+ + +
+ `, + standalone: true, + imports: [CommonModule, FormsModule], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ValidatedInputComponent), + multi: true, + }, + ], +}) +export class ValidatedInputComponent implements ControlValueAccessor { + @Input() label: string = ''; + @Input() id: string = ''; + @Input() name: string = ''; + @Input() type: string = 'text'; + @Input() required: boolean = false; + @Input() validationMessage: string = ''; + + @Input() value: any = ''; + @Output() valueChange = new EventEmitter(); + + onChange: any = () => {}; + onTouched: any = () => {}; + + writeValue(value: any): void { + if (value !== undefined) { + this.value = value; + } + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + onInputChange(event: Event): void { + const value = (event.target as HTMLInputElement).value; + this.value = value; + this.onChange(value); + this.valueChange.emit(value); + } + + setDisabledState?(isDisabled: boolean): void { + // Implementieren Sie dies, wenn Sie die Deaktivierung des Inputs unterstützen möchten + } +} diff --git a/bizmatch/src/app/components/validated-select/validated-select.component.html b/bizmatch/src/app/components/validated-select/validated-select.component.html new file mode 100644 index 0000000..6f8bd7a --- /dev/null +++ b/bizmatch/src/app/components/validated-select/validated-select.component.html @@ -0,0 +1 @@ +

validated-select works!

diff --git a/bizmatch/src/app/components/validated-select/validated-select.component.scss b/bizmatch/src/app/components/validated-select/validated-select.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/bizmatch/src/app/components/validated-select/validated-select.component.ts b/bizmatch/src/app/components/validated-select/validated-select.component.ts new file mode 100644 index 0000000..0062a09 --- /dev/null +++ b/bizmatch/src/app/components/validated-select/validated-select.component.ts @@ -0,0 +1,77 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'app-validated-select', + template: ` +
+ + +
+ `, + standalone: true, + imports: [CommonModule, FormsModule], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ValidatedSelectComponent), + multi: true, + }, + ], +}) +export class ValidatedSelectComponent implements ControlValueAccessor { + @Input() label: string = ''; + @Input() id: string = ''; + @Input() name: string = ''; + @Input() required: boolean = false; + @Input() validationMessage: string = ''; + @Input() options: Array<{ value: any; label: string }> = []; + + @Input() value: any = ''; + @Output() valueChange = new EventEmitter(); + + onChange: any = () => {}; + onTouched: any = () => {}; + + writeValue(value: any): void { + if (value !== undefined) { + this.value = value; + } + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + onSelectChange(event: Event): void { + const value = (event.target as HTMLSelectElement).value; + this.value = value; + this.onChange(value); + this.valueChange.emit(value); + } + + setDisabledState?(isDisabled: boolean): void { + // Implementieren Sie dies, wenn Sie die Deaktivierung des Selects unterstützen möchten + } +} diff --git a/bizmatch/src/app/pages/subscription/account/account.component.html b/bizmatch/src/app/pages/subscription/account/account.component.html index 4be5e51..fbe8002 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.html +++ b/bizmatch/src/app/pages/subscription/account/account.component.html @@ -6,7 +6,7 @@
- +

You can only modify your email by contacting us at support@bizwatch.net

@if (isProfessional){ @@ -60,23 +60,26 @@
-
- - -
-
- - -
+ +
-
+ + @if (isProfessional){
@@ -223,7 +226,6 @@
-

Membership Level

@@ -264,270 +266,5 @@
}
- - - diff --git a/bizmatch/src/app/pages/subscription/account/account.component.ts b/bizmatch/src/app/pages/subscription/account/account.component.ts index 5436041..be871a5 100644 --- a/bizmatch/src/app/pages/subscription/account/account.component.ts +++ b/bizmatch/src/app/pages/subscription/account/account.component.ts @@ -1,3 +1,4 @@ +import { TitleCasePipe } from '@angular/common'; import { ChangeDetectorRef, Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; @@ -8,13 +9,15 @@ import { ImageCropperComponent } from 'ngx-image-cropper'; import { QuillModule } from 'ngx-quill'; import { lastValueFrom } from 'rxjs'; import { User } from '../../../../../../bizmatch-server/src/models/db.model'; -import { AutoCompleteCompleteEvent, Invoice, Subscription, UploadParams, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; +import { AutoCompleteCompleteEvent, Invoice, Subscription, UploadParams, ValidationMessage, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { environment } from '../../../../environments/environment'; import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component'; import { ConfirmationService } from '../../../components/confirmation/confirmation.service'; import { ImageCropAndUploadComponent, UploadReponse } from '../../../components/image-crop-and-upload/image-crop-and-upload.component'; import { MessageComponent } from '../../../components/message/message.component'; import { MessageService } from '../../../components/message/message.service'; +import { ValidatedInputComponent } from '../../../components/validated-input/validated-input.component'; +import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component'; import { GeoService } from '../../../services/geo.service'; import { ImageService } from '../../../services/image.service'; import { LoadingService } from '../../../services/loading.service'; @@ -28,8 +31,19 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults'; @Component({ selector: 'app-account', standalone: true, - imports: [SharedModule, QuillModule, NgxCurrencyDirective, NgSelectModule, ImageCropperComponent, ConfirmationComponent, ImageCropAndUploadComponent, MessageComponent], - providers: [], + imports: [ + SharedModule, + QuillModule, + NgxCurrencyDirective, + NgSelectModule, + ImageCropperComponent, + ConfirmationComponent, + ImageCropAndUploadComponent, + MessageComponent, + ValidatedInputComponent, + ValidatedSelectComponent, + ], + providers: [TitleCasePipe], templateUrl: './account.component.html', styleUrl: './account.component.scss', }) @@ -51,6 +65,9 @@ export class AccountComponent { toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']], }; uploadParams: UploadParams; + validationMessages: ValidationMessage[] = []; + customerTypeOptions: Array<{ value: string; label: string }> = []; + customerSubTypeOptions: Array<{ value: string; label: string }> = []; constructor( public userService: UserService, private subscriptionService: SubscriptionsService, @@ -65,6 +82,7 @@ export class AccountComponent { private confirmationService: ConfirmationService, private messageService: MessageService, private sharedService: SharedService, + private titleCasePipe: TitleCasePipe, ) {} async ngOnInit() { if (this.id) { @@ -95,6 +113,16 @@ export class AccountComponent { this.userSubscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions(this.user.id)); this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`; this.companyLogoUrl = this.user.hasCompanyLogo ? `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`; + + this.customerTypeOptions = this.customerTypes.map(type => ({ + value: type, + label: this.titleCasePipe.transform(type), + })); + + this.customerSubTypeOptions = this.customerSubTypes.map(type => ({ + value: type, + label: this.titleCasePipe.transform(type), + })); } printInvoice(invoice: Invoice) {} @@ -182,76 +210,8 @@ export class AccountComponent { }); } } - // 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 }; - // getImageDimensions(imageUrl).then(dimensions => { - // const dialogWidth = getDialogWidth(dimensions); - - // 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: dialogWidth, - // modal: true, - // closeOnEscape: true, - // keepInViewport: true, - // closable: false, - // }); - // this.dialogRef.onClose.subscribe(cropper => { - // if (cropper) { - // this.loadingService.startLoading('uploadImage'); - // cropper.getCroppedCanvas().toBlob(async blob => { - // this.imageUploadService.uploadImage(blob, type === 'company' ? 'uploadCompanyLogo' : 'uploadProfile', emailToDirName(this.user.email)).subscribe( - // async event => { - // if (event.type === HttpEventType.Response) { - // this.loadingService.stopLoading('uploadImage'); - // if (this.type === 'company') { - // this.user.hasCompanyLogo = true; // - // this.companyLogoUrl = `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}`; - // } else { - // this.user.hasProfile = true; - // this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}`; - // } - // await this.userService.save(this.user); - // } - // }, - // error => console.error('Fehler beim Upload:', error), - // ); - // }); - // } - // }); - // }); - // } - - // this.confirmationService.showConfirmation({ - // target: event.target as EventTarget, - // message: `Do you want to delete your ${type === 'logo' ? 'Logo' : 'Profile'} image`, - // 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 () => { - // if (type === 'profile') { - // this.user.hasProfile = false; - // await Promise.all([this.imageService.deleteProfileImagesById(this.user.email), this.userService.save(this.user)]); - // } else { - // this.user.hasCompanyLogo = false; - // await Promise.all([this.imageService.deleteLogoImagesById(this.user.email), this.userService.save(this.user)]); - // } - // this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' }); - // this.user = await this.userService.getById(this.user.id); - // }, - // reject: () => { - // console.log('deny'); - // }, - // }); + getValidationMessage(fieldName: string): string { + const message = this.validationMessages.find(msg => msg.field === fieldName); + return message ? message.message : ''; + } } 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 e90e868..1ccac73 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 @@ -74,7 +74,7 @@ export class EditBusinessListingComponent { } }); this.typesOfBusiness = selectOptions.typesOfBusiness.map(e => { - return { name: e.name, value: parseInt(e.value) }; + return { name: e.name, value: e.value }; }); } async ngOnInit() { 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 9756652..e78f076 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 @@ -113,7 +113,7 @@ export class EditCommercialPropertyListingComponent { } }); this.typesOfCommercialProperty = selectOptions.typesOfCommercialProperty.map(e => { - return { name: e.name, value: parseInt(e.value) }; + return { name: e.name, value: e.value }; }); } async ngOnInit() {