Skip to content

Commit f3d4674

Browse files
authored
Bugfix/house validator (#3943)
* added house validator for address * fixed placeID for house entrance and corpus * test fix * rabbit fixes * district fix * district dto fix * dirty form fix * artifact code remove tests
1 parent 01fd71b commit f3d4674

13 files changed

Lines changed: 162 additions & 73 deletions

File tree

src/app/chat/service/chats/telegram-socket.service.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { TestBed } from '@angular/core/testing';
22
import { NgZone } from '@angular/core';
33
import { TelegramSocketService } from './telegram-socket.service';
4-
import { IMessage } from '@stomp/stompjs';
4+
import { Client, IFrame, IMessage } from '@stomp/stompjs';
55
import { Subject } from 'rxjs';
6-
import { Client, IFrame } from '@stomp/stompjs';
76

87
class FakeStompClient {
98
onConnect?: (frame: any) => void;

src/app/ubs/shared/components/address-input/address-input.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@
108108

109109
<mat-select
110110
*ngIf="allowDistrictEdit"
111-
formControlName="district"
112111
class="shadow-none form-control"
113112
(selectionChange)="onDistrictChange($event.value.nameUk, $event.value.nameEn)"
114113
[placeholder]="district.value ? district.value + ', ' + ('personal-info.info-district-placeholder' | translate) : ''"

src/app/ubs/shared/components/address-input/address-input.component.spec.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { AddressInputComponent } from './address-input.component';
33
import { HttpClientTestingModule } from '@angular/common/http/testing';
44
import { TranslateModule } from '@ngx-translate/core';
55
import { MatAutocompleteModule } from '@angular/material/autocomplete';
6-
import { ReactiveFormsModule, FormBuilder } from '@angular/forms';
6+
import { ReactiveFormsModule, FormBuilder, AbstractControl } from '@angular/forms';
77
import { NO_ERRORS_SCHEMA } from '@angular/core';
88
import { BehaviorSubject, of } from 'rxjs';
99
import { LanguageService } from 'src/app/shared/i18n/language.service';
@@ -580,11 +580,13 @@ describe('AddressInputComponent', () => {
580580
});
581581

582582
it('should update and validate form on value change in addressData when address data changes', fakeAsync(() => {
583+
spyOn(component.addressData, 'getPlaceIdByAddress').and.returnValue(Promise.resolve(['testPlaceId', ['street_address']]));
584+
component.districtsForKyiv = [{ nameEn: 'Shevchenkivskyi' } as DistrictsDtos];
583585
spyOn(component, 'onChange');
584586
spyOn(component.addressData, 'getValues').and.returnValue({ houseNumber: '1' } as any);
585587
component['initListeners']();
586588
fixture.detectChanges();
587-
tick();
589+
tick(1000);
588590

589591
const mockAddressData = {
590592
regionUk: 'Київська область',
@@ -600,7 +602,7 @@ describe('AddressInputComponent', () => {
600602
};
601603

602604
component.addressData['addressChange'].next(mockAddressData as any);
603-
tick();
605+
tick(1000);
604606
fixture.detectChanges();
605607

606608
expect(component.blockAutoComplete).toBeTrue();
@@ -799,7 +801,6 @@ describe('AddressInputComponent', () => {
799801
expect(component.addressData.setStreet).toHaveBeenCalledWith({ placeEn: mockGeocoderResult, placeUk: mockGeocoderResult });
800802
expect(component.district.enabled).toBeTrue();
801803
expect(component['delayAutocomplete']).toHaveBeenCalled();
802-
expect(component.placeId.value).toBe(mockStreet.place_id);
803804
}));
804805

805806
it('should disable district and reset data if street selection is null', () => {
@@ -843,4 +844,59 @@ describe('AddressInputComponent', () => {
843844

844845
expect(component.blockAutoComplete).toBeFalse();
845846
}));
847+
848+
describe('AddressValidator.validate', () => {
849+
let control: AbstractControl;
850+
851+
beforeEach(() => {
852+
control = {} as AbstractControl;
853+
});
854+
855+
it('should return null if form is pristine', () => {
856+
component.addressForm = { pristine: true } as any;
857+
858+
const result = component.validate(control);
859+
860+
expect(result).toBeNull();
861+
});
862+
863+
it('should return disableSubmit error while validating', () => {
864+
component.addressForm = { pristine: false } as any;
865+
(component as any).isValidating = true;
866+
867+
const result = component.validate(control);
868+
869+
expect(result).toEqual({ disableSubmit: true });
870+
});
871+
872+
it('should return null if form and address data are valid', () => {
873+
component.addressForm = {
874+
pristine: false,
875+
valid: true
876+
} as any;
877+
878+
component.addressData = {
879+
isValid: () => true
880+
} as any;
881+
882+
const result = component.validate(control);
883+
884+
expect(result).toBeNull();
885+
});
886+
887+
it('should return incorrectAddress error if form or address data is invalid', () => {
888+
component.addressForm = {
889+
pristine: false,
890+
valid: false
891+
} as any;
892+
893+
component.addressData = {
894+
isValid: () => false
895+
} as any;
896+
897+
const result = component.validate(control);
898+
899+
expect(result).toEqual({ incorrectAddress: true });
900+
});
901+
});
846902
});

src/app/ubs/shared/components/address-input/address-input.component.ts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { LocalStorageService } from 'src/app/shared/services/localstorage/local-
1515
import { Coordinates } from 'src/app/greencity/modules/user/models/edit-profile.model';
1616
import { select, Store } from '@ngrx/store';
1717
import { BehaviorSubject, combineLatest, filter, from, Subject } from 'rxjs';
18-
import { debounceTime, switchMap, take, takeUntil } from 'rxjs/operators';
18+
import { debounceTime, switchMap, take, takeUntil, tap } from 'rxjs/operators';
1919
import { LanguageService } from 'src/app/shared/i18n/language.service';
2020
import { emptyOrValid } from '@ubs/shared/validators/empthy-or-valid.validator';
2121
import { addressesSelector } from 'src/app/store/selectors/order.selectors';
@@ -82,6 +82,7 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
8282
private readonly numericPattern = Patterns.numeric;
8383
private readonly $destroy: Subject<void> = new Subject();
8484
private viewInitialized = false;
85+
private isValidating = false;
8586
private googlePlacesService: google.maps.places.PlacesService;
8687
private readonly showMapSelected$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
8788

@@ -139,6 +140,7 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
139140
return this.addressForm.get('placeId') as FormControl;
140141
}
141142

143+
private onValidatorChange?: () => void;
142144
onChange = (address) => {};
143145
onTouched = () => {};
144146

@@ -160,6 +162,9 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
160162
this.showMapSelected$.next(this.isShowMap);
161163
this.initForm();
162164
this.initListeners();
165+
this.addressForm.statusChanges.pipe(takeUntil(this.$destroy)).subscribe(() => {
166+
this.onValidatorChange?.();
167+
});
163168
}
164169

165170
ngAfterViewInit(): void {
@@ -185,7 +190,23 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
185190
}
186191

187192
validate(control: AbstractControl): ValidationErrors {
188-
return (this.addressForm.valid && this.addressData.isValid()) || this.addressForm.pristine ? null : { incorrectAddress: true };
193+
if (this.addressForm.pristine) {
194+
return null;
195+
}
196+
197+
if (this.isValidating) {
198+
return { disableSubmit: true };
199+
}
200+
201+
if (this.addressForm.valid && this.addressData.isValid()) {
202+
return null;
203+
}
204+
205+
return { incorrectAddress: true };
206+
}
207+
208+
registerOnValidatorChange(fn: () => void) {
209+
this.onValidatorChange = fn;
189210
}
190211

191212
writeValue(obj: any): void {}
@@ -255,8 +276,15 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
255276

256277
this.addressData
257278
.getAddressChange()
258-
.pipe(takeUntil(this.$destroy))
259-
.subscribe((addressData) => {
279+
.pipe(
280+
tap(() => {
281+
this.isValidating = true;
282+
this.onValidatorChange?.();
283+
}),
284+
debounceTime(1000),
285+
takeUntil(this.$destroy)
286+
)
287+
.subscribe(async (addressData) => {
260288
this.blockAutoComplete = true;
261289

262290
const region = this.currentLanguage === 'uk' ? addressData.regionUk : addressData.regionEn;
@@ -271,8 +299,18 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
271299
}
272300
this.houseNumber.setValue(addressData.houseNumber);
273301

274-
this.onChange(this.addressData.getValues());
275-
this.cdr.detectChanges();
302+
if (this.addressForm.valid) {
303+
const [placeId, types] = await this.addressData.getPlaceIdByAddress();
304+
if (types.includes('street_address')) {
305+
this.placeId.setValue(placeId);
306+
this.onChange(this.addressData.getValues());
307+
} else {
308+
this.houseNumber.setErrors({ invalidHouseNumber: true });
309+
}
310+
this.isValidating = false;
311+
this.onValidatorChange?.();
312+
this.cdr.markForCheck();
313+
}
276314

277315
this.delayAutocomplete();
278316
});
@@ -319,7 +357,7 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
319357
});
320358
}
321359
},
322-
error: (error) => {
360+
error: () => {
323361
this.ngZone.run(() => {
324362
this.isMapLoaded$.next(false);
325363
this.cleanupGoogleMapUtilities();
@@ -489,7 +527,6 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
489527
await this.addressData.setCity({ placeUk, placeEn });
490528
await this.addressData.setStreet({ placeUk, placeEn });
491529

492-
this.placeId.setValue(street.place_id);
493530
this.allowDistrictEdit && this.district.enable();
494531
this.district.markAsTouched();
495532
} else {
@@ -537,9 +574,8 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
537574
}
538575

539576
onDistrictChange(district: string, districtEn: string): void {
540-
this.allowDistrictEdit && this.addressData.setCustomDistrict(district, districtEn);
541-
542577
this.OnChangeAndTouched();
578+
this.allowDistrictEdit && this.addressData.setCustomDistrict(district, districtEn);
543579
}
544580

545581
onHouseCorpusChange(): void {
@@ -566,7 +602,6 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
566602
if (typeof window?.google?.maps === 'undefined' || !this.isMapLoaded$.value || !this.map?.googleMap) {
567603
return;
568604
}
569-
570605
this.addressCoords = $event.latLng.toJSON();
571606

572607
this.addressData.setCoordinates(this.addressCoords, { fetch: true });
@@ -673,9 +708,10 @@ export class AddressInputComponent implements OnInit, AfterViewInit, OnDestroy,
673708
}
674709
this.onChange(this.addressData.getValues());
675710
this.markAsTouched();
711+
this.addressForm.markAsDirty();
676712
}
677713

678-
//Set users current location
714+
// Set users current location
679715
private setCurrentLocation(): void {
680716
navigator.geolocation.getCurrentPosition(
681717
(position) => this.handleGeolocationSuccess(position),

src/app/ubs/shared/components/ubs-add-address-pop-up/ubs-add-address-pop-up.component.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<form [formGroup]="addAddressForm" class="row adress">
1+
<form id="address-form" [formGroup]="addAddressForm" class="row adress">
22
<div class="w-100">
33
<h2 *ngIf="!data.edit" class="personal-info-pop-up-title">{{ 'personal-info.pop-up-title' | translate }}</h2>
44
<h2 *ngIf="data.edit" class="personal-info-pop-up-title">{{ 'personal-info.pop-up-title-edit' | translate }}</h2>
@@ -18,7 +18,13 @@ <h2 *ngIf="data.edit" class="personal-info-pop-up-title">{{ 'personal-info.pop-u
1818
<button class="ubs-secondary-global-button m-btn" (click)="onNoClick()">
1919
{{ 'personal-info.pop-up-cancel' | translate }}
2020
</button>
21-
<button class="ubs-primary-global-button m-btn" (click)="chooseActions()" [disabled]="addAddressForm.invalid || !addAddressForm.touched">
21+
<button
22+
class="ubs-primary-global-button m-btn"
23+
type="submit"
24+
form="address-form"
25+
(click)="chooseActions()"
26+
[disabled]="addAddressForm.invalid || !addAddressForm.touched"
27+
>
2228
{{ (data.edit ? 'personal-info.pop-up-save-changes' : 'personal-info.pop-up-add-address') | translate }}
2329
</button>
2430
</div>

src/app/ubs/shared/components/ubs-add-address-pop-up/ubs-add-address-pop-up.component.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
22
import { Component, Inject, OnInit } from '@angular/core';
3-
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
4-
import { Address, AddressData, CourierLocations, DistrictsDtos } from 'src/app/ubs/ubs/models/ubs.interface';
3+
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
4+
import { Address, AddressData, CourierLocations } from 'src/app/ubs/ubs/models/ubs.interface';
55
import { Store } from '@ngrx/store';
66
import { CreateAddress, DeleteAddress, UpdateAddress } from 'src/app/store/actions/order.actions';
7-
import { CAddressData } from 'src/app/ubs/ubs/models/ubs.model';
87
import { UpdateOrderAddress } from 'src/app/store/actions/bigOrderTable.actions';
98
import { SetCursorWaite } from 'src/app/store/actions/ubs-admin.actions';
109

@@ -17,26 +16,6 @@ export class UBSAddAddressPopUpComponent implements OnInit {
1716
addAddressForm: FormGroup;
1817
currentLanguage: string;
1918
locations: CourierLocations;
20-
districtList: DistrictsDtos[];
21-
addressData: CAddressData;
22-
23-
autocompleteRegionRequest = {
24-
input: '',
25-
types: ['administrative_area_level_1'],
26-
componentRestrictions: { country: 'uk' }
27-
};
28-
29-
autocompleteCityRequest = {
30-
input: '',
31-
types: ['(cities)'],
32-
componentRestrictions: { country: 'uk' }
33-
};
34-
35-
autocompleteStreetRequest = {
36-
input: '',
37-
types: ['address'],
38-
componentRestrictions: { country: 'uk' }
39-
};
4019

4120
constructor(
4221
private fb: FormBuilder,

src/app/ubs/shared/components/ubs-input-error/ubs-input-error.component.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,9 @@ import { Patterns } from 'src/assets/patterns/patterns';
44
import { inputsName } from 'src/app/greencity/modules/user/models/error-type.model';
55

66
enum errorType {
7-
email = 'email',
8-
pattern = 'pattern',
9-
wrongNumber = 'wrongNumber',
10-
minlength = 'minlength',
11-
maxlength = 'maxlength',
127
required = 'required',
13-
newPasswordMatchesOld = 'newPasswordMatchesOld',
14-
confirmPasswordMistmatch = 'confirmPasswordMistmatch',
15-
requiredFromDropdown = 'requiredFromDropdown',
16-
emailExist = 'emailExist'
8+
pattern = 'pattern',
9+
maxlength = 'maxlength'
1710
}
1811

1912
@Component({
@@ -32,6 +25,7 @@ export class UBSInputErrorComponent implements OnInit {
3225
emailEmployee: 'input-error.email-required-employee',
3326
phoneEmployee: 'input-error.phone-required-employee',
3427
houseNumber: 'input-error.house-number',
28+
invalidHouseNumber: 'input-error.invalid-house-number',
3529
minlength: 'input-error.minlength-short',
3630
maxlength: 'input-error.max-length',
3731
maxlengthEmail: 'input-error.max-length-email',
@@ -64,8 +58,8 @@ export class UBSInputErrorComponent implements OnInit {
6458
}
6559

6660
getType() {
67-
Object.values(errorType).forEach((err) => {
68-
if (this.formElement.errors?.[err]) {
61+
if (this.formElement.errors) {
62+
Object.keys(this.formElement.errors).forEach((err) => {
6963
switch (err) {
7064
case errorType.required:
7165
this.errorMessage = this.getRequiredErrorMessage(this.formElement.errors.required, this.inputName);
@@ -79,8 +73,8 @@ export class UBSInputErrorComponent implements OnInit {
7973
default:
8074
this.errorMessage = this.validationErrors[err];
8175
}
82-
}
83-
});
76+
});
77+
}
8478
}
8579

8680
getRequiredErrorMessage(required: boolean, inputName: string): string {

src/app/ubs/ubs/components/ubs-personal-information/ubs-order-address/ubs-order-address.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export class UbsOrderAddressComponent implements OnInit, OnDestroy {
135135
switchMap((clonedAddress) => {
136136
if (!clonedAddress.placeId) {
137137
const latLng = { lat: clonedAddress.coordinates.latitude, lng: clonedAddress.coordinates.longitude };
138-
return from(this.addressData.getAddressPlaceId(latLng)).pipe(
138+
return from(this.addressData.getPlaceIdByCoordinates(latLng)).pipe(
139139
switchMap((placeId: string) => {
140140
clonedAddress.placeId = placeId;
141141
return of(clonedAddress);

0 commit comments

Comments
 (0)