diff --git a/.vscode/settings.json b/.vscode/settings.json index f36565fad..5802ee147 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,6 @@ "prettier.trailingComma": "es5", "prettier.tabWidth": 4, "prettier.printWidth": 140, - "workbench.colorCustomizations": {} + "workbench.colorCustomizations": {}, + "typescriptHero.imports.insertSemicolons": false } diff --git a/apps/sales/src/app/core/services/customer.service.ts b/apps/sales/src/app/core/services/customer.service.ts index a5f260637..64363949d 100644 --- a/apps/sales/src/app/core/services/customer.service.ts +++ b/apps/sales/src/app/core/services/customer.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { CustomerService as CustomerApiService, QueryTokenDTO, PayerService } from 'swagger'; -import { map, switchMap, tap, catchError } from 'rxjs/operators'; +import { map, switchMap, tap, catchError, share } from 'rxjs/operators'; import { Observable, of, combineLatest } from 'rxjs'; import { CustomerMapping } from '../mappings/customer.mapping'; import { CustomerSearchResponse } from '../models/customer-search-response.model'; @@ -427,4 +427,19 @@ export class CustomerService { }) ); } + + emailExists(email: string) { + return this.customerService.CustomerEmailExists(email).pipe( + map(response => { + if (response && response.error) { + throw new Error(response.message); + } + return response && response.result; + }), + catchError(error => { + console.log(error); + return of(undefined); + }) + ); + } } diff --git a/apps/sales/src/app/core/utils/app.prototypes.ts b/apps/sales/src/app/core/utils/app.prototypes.ts index 4581468c1..da8c88437 100644 --- a/apps/sales/src/app/core/utils/app.prototypes.ts +++ b/apps/sales/src/app/core/utils/app.prototypes.ts @@ -7,7 +7,8 @@ declare global { } interface Boolean { - ifTrue(callback); + ifTrue(callback): boolean; + ifFalse(callback): boolean; } } @@ -22,5 +23,13 @@ Array.prototype.hasItems = function() { Boolean.prototype.ifTrue = function(callback) { if (this && this.valueOf() === true) { callback(); + return false; + } +}; + +Boolean.prototype.ifFalse = function(callback) { + if (this && this.valueOf() === false) { + callback(); + return true; } }; diff --git a/apps/sales/src/app/core/utils/http-interceptor-whitelist.utils.ts b/apps/sales/src/app/core/utils/http-interceptor-whitelist.utils.ts index aa96b2280..223e4416f 100644 --- a/apps/sales/src/app/core/utils/http-interceptor-whitelist.utils.ts +++ b/apps/sales/src/app/core/utils/http-interceptor-whitelist.utils.ts @@ -6,6 +6,7 @@ const errorWhiteList: { url: string; codes: number[] }[] = [ { url: '/customer/', codes: [409] }, { url: '/bonuscard', codes: [404] }, { url: '/order/', codes: [400, 409, 500] }, + { url: '/store/customer/emailexists', codes: [400, 409, 500] }, { url: '/receipt/order/', codes: [401, 403, 404, 406, 407, 410, 412, 416, 418, 451, 500, 501, 502, 503, 504, 0, -1] }, { url: '/5d1e00a230000080a4d72461', codes: [503] }, // test call ]; diff --git a/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.html b/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.html index 726949be3..9a109e021 100644 --- a/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.html +++ b/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.html @@ -98,6 +98,7 @@ [label]="'Straße'" [error]="submitted && f.address.errors" [errorMessage]="'Straße wird benötigt'" + [displayErrorUnder]="true" [value]="f.address.value" (valueChanges)="valueChanges(f.address, $event)" > @@ -111,6 +112,7 @@ [label]="'Hausnummer'" [error]="submitted && f.streetNo.errors" [errorMessage]="'Hausnummer wird benötigt'" + [displayErrorUnder]="true" [value]="f.streetNo.value" (valueChanges)="valueChanges(f.streetNo, $event)" > @@ -169,7 +171,7 @@ [id]="'email'" [label]="'E-Mail-Adresse'" [error]="submitted && f.email.errors" - [errorMessage]="'E-Mail-Adresse ungültig'" + [errorMessage]="f.email.errors && f.email.errors.emailExists ? 'E-Mail-Adresse existiert' : 'E-Mail-Adresse ungültig'" (valueChanges)="valueChanges(f.email, $event)" [value]="f.email.value" > diff --git a/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.ts b/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.ts index d32155976..18439c25d 100644 --- a/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.ts +++ b/apps/sales/src/app/modules/customer/components/create-customer-card/create-customer-card.component.ts @@ -6,10 +6,10 @@ import { User, Address } from '../../../../core/models/user.model'; import { Router } from '@angular/router'; import { Process, CustomerFormState } from '../../../../core/models/process.model'; import { Breadcrumb } from '../../../../core/models/breadcrumb.model'; -import { Observable, Subject } from 'rxjs'; +import { Observable, Subject, of } from 'rxjs'; import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions'; import { AddUser, SetUserDetails } from '../../../../core/store/actions/customer.actions'; -import { takeUntil, distinctUntilChanged, switchMap, map, filter, tap } from 'rxjs/operators'; +import { takeUntil, distinctUntilChanged, switchMap, map, filter, tap, share, debounceTime } from 'rxjs/operators'; import { CustomValidators } from '../../../../shared/validation/custom-validation'; import { GENDERS, TIITLES } from '../../dropdown-values'; import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors'; @@ -20,6 +20,7 @@ import { Country } from '../../../../core/models/country.model'; import { isoDateFromString } from '../../../../core/utils/app.utils'; import { isNullOrUndefined } from 'util'; import { DeliveryOption } from 'apps/sales/src/app/core/models/delivery-option.model'; +import { CustomerService } from 'apps/sales/src/app/core/services/customer.service'; @Component({ selector: 'app-create-customer-card', @@ -54,6 +55,7 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { guestChecked = false; onlineChecked = false; customerCardChecked = false; + timer: any; get countries() { return this.countries$.pipe( @@ -64,7 +66,13 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { ); } - constructor(private fb: FormBuilder, private store: Store, private router: Router, private renderer: Renderer) {} + constructor( + private fb: FormBuilder, + private store: Store, + private router: Router, + private renderer: Renderer, + private customerService: CustomerService + ) {} ngOnInit() { this.initialize(); @@ -98,7 +106,6 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { this.cartHasItemsForDelivery.ifTrue(() => { this.guestChecked = true; }); - // tslint:disable-next-line: semicolon }; initializationSubscriptionHandler = (process: Process) => { @@ -115,19 +122,66 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { this.userForm = this.buildCreateForm(this.fb); } + this.setValidations(); this.setCompanyValidators(); this.loaded = true; } - // tslint:disable-next-line: semicolon }; - @HostListener('window:scroll', ['$event']) - scrollHandler(event) { - if (event.target.id === 'customer-form') { - this.renderer.invokeElementMethod(document.activeElement, 'blur'); - } + setValidations() { + const deliveryAndGuestOrOnline = this.cartHasItemsForDelivery && (this.guestChecked || this.onlineChecked); + deliveryAndGuestOrOnline.ifTrue(this.deliveryAndGuestValidations); } + deliveryAndGuestValidations = () => { + this.userForm.get('title').setValidators([Validators.required]); + this.userForm.get('title').updateValueAndValidity(); + this.userForm.get('email').setValidators([Validators.required, CustomValidators.validateEmail]); + this.userForm.get('email').updateValueAndValidity(); + this.userForm.get('address').setValidators([Validators.required]); + this.userForm.get('address').updateValueAndValidity(); + this.userForm.get('streetNo').setValidators([Validators.required]); + this.userForm.get('streetNo').updateValueAndValidity(); + this.userForm.get('zipCode').setValidators([Validators.required]); + this.userForm.get('zipCode').updateValueAndValidity(); + this.userForm.get('city').setValidators([Validators.required]); + this.userForm.get('city').updateValueAndValidity(); + this.userForm.get('country').setValidators([Validators.required]); + this.userForm.get('country').updateValueAndValidity(); + }; + + resetValidations = () => { + this.userForm.get('title').clearValidators(); + this.userForm.get('title').updateValueAndValidity(); + this.userForm.get('email').setValidators([CustomValidators.validateEmail]); + this.userForm.get('email').updateValueAndValidity(); + this.userForm.get('address').clearValidators(); + this.userForm.get('address').updateValueAndValidity(); + this.userForm.get('streetNo').clearValidators(); + this.userForm.get('streetNo').updateValueAndValidity(); + this.userForm.get('zipCode').clearValidators(); + this.userForm.get('zipCode').updateValueAndValidity(); + this.userForm.get('city').clearValidators(); + this.userForm.get('city').updateValueAndValidity(); + this.userForm.get('country').clearValidators(); + this.userForm.get('country').updateValueAndValidity(); + }; + + validateIfEmailExists(control: AbstractControl) { + return control.value ? this.emailValidation$(control.value) : of(null); + } + + emailValidation$ = (value: string) => + this.customerService.emailExists(value).pipe( + map((exists: Boolean) => { + if (isNullOrUndefined(exists)) { + return { invalidEmail: true }; + } + return exists ? { emailExists: true } : null; + }), + share() + ); + createUser() { if (this.userForm.valid) { this.createProcessIfDosntExists(); @@ -322,7 +376,7 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { return fb.group({ firstName: ['', Validators.required], lastName: ['', Validators.required], - email: ['', CustomValidators.validateEmail], + email: ['', CustomValidators.validateEmail, this.validateIfEmailExists.bind(this)], address: [''], streetNo: [''], zipCode: [''], @@ -356,5 +410,18 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy { toggle(checkbox: string) { this[checkbox] = !this[checkbox]; + const type = this[checkbox] as boolean; + if (type) { + this.setValidations(); + } else { + this.resetValidations(); + } + } + + @HostListener('window:scroll', ['$event']) + scrollHandler(event) { + if (event.target.id === 'customer-form') { + this.renderer.invokeElementMethod(document.activeElement, 'blur'); + } } } diff --git a/apps/sales/src/app/shared/validation/custom-validation.ts b/apps/sales/src/app/shared/validation/custom-validation.ts index 8403baaec..cf609c4c6 100644 --- a/apps/sales/src/app/shared/validation/custom-validation.ts +++ b/apps/sales/src/app/shared/validation/custom-validation.ts @@ -33,6 +33,7 @@ export class CustomValidators extends Validators { static validateEmail(control: FormControl) { if (control.value && control.value.length > 0) { if (emaiValidation.test(control.value)) { + console.log('passed email validation'); return null; } diff --git a/tslint.json b/tslint.json index c740a7b2a..0624837e7 100644 --- a/tslint.json +++ b/tslint.json @@ -1,68 +1,39 @@ { - "rulesDirectory": [ - "codelyzer" - ], + "rulesDirectory": ["codelyzer"], "rules": { "arrow-return-shorthand": true, "callable-types": true, "class-name": true, - "comment-format": [ - true, - "check-space" - ], + "comment-format": [true, "check-space"], "curly": true, "deprecation": { "severity": "warn" }, "eofline": true, "forin": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], + "import-blacklist": [true, "rxjs/Rx"], "import-spacing": true, - "indent": [ - true, - "spaces" - ], + "indent": [true, "spaces"], "interface-over-type-literal": true, "label-position": true, - "max-line-length": [ - true, - 140 - ], + "max-line-length": [true, 140], "member-access": false, "member-ordering": [ true, { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] + "order": ["static-field", "instance-field", "static-method", "instance-method"] } ], "no-arg": true, "no-bitwise": true, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], + "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], "no-construct": true, "no-debugger": true, "no-duplicate-super": true, "no-empty": false, "no-empty-interface": true, "no-eval": true, - "no-inferrable-types": [ - true, - "ignore-params" - ], + "no-inferrable-types": [true, "ignore-params"], "no-misused-new": true, "no-non-null-assertion": true, "no-redundant-jsdoc": true, @@ -76,27 +47,12 @@ "no-use-before-declare": true, "no-var-keyword": true, "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], + "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], "prefer-const": true, - "quotemark": [ - true, - "single" - ], + "quotemark": [true, "single"], "radix": true, - "semicolon": [ - true, - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], + "semicolon": [false, "always"], + "triple-equals": [true, "allow-null-check"], "typedef-whitespace": [ true, { @@ -109,14 +65,7 @@ ], "unified-signatures": true, "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], + "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"], "no-output-on-prefix": true, "use-input-property-decorator": true, "use-output-property-decorator": true,