diff --git a/feature-libs/user/profile/assets/translations/en/address.json b/feature-libs/user/profile/assets/translations/en/address.json
index bbfad2191ca..b1b1d5394b0 100644
--- a/feature-libs/user/profile/assets/translations/en/address.json
+++ b/feature-libs/user/profile/assets/translations/en/address.json
@@ -19,6 +19,8 @@
"placeholder": "City"
},
"state": "State",
+ "province": "Province",
+ "district": "District",
"zipCode": {
"label": "Zip code",
"placeholder": "Postal Code/Zip"
@@ -36,6 +38,8 @@
"streetAddress": "Street Address",
"aptSuite": "Apartment Number",
"selectOne": "Select One...",
+ "selectProvinceFirst": "Please select a province first",
+ "selectCityFirst": "Please select a city first",
"setAsDefault": "Set as default delivery address",
"titleRequired": "Title is required.",
"userAddressAddSuccess": "New address was added successfully!",
diff --git a/feature-libs/user/profile/assets/translations/zh/address.json b/feature-libs/user/profile/assets/translations/zh/address.json
index 241ec7cdf8d..97ba0a4674e 100644
--- a/feature-libs/user/profile/assets/translations/zh/address.json
+++ b/feature-libs/user/profile/assets/translations/zh/address.json
@@ -19,6 +19,8 @@
"placeholder": "城市"
},
"state": "州/省",
+ "province": "省份",
+ "district": "区/县",
"zipCode": {
"label": "邮政编码",
"placeholder": "邮政编码"
@@ -36,6 +38,8 @@
"streetAddress": "街道地址",
"aptSuite": "公寓号",
"selectOne": "选择一个...",
+ "selectProvinceFirst": "请先选择省份",
+ "selectCityFirst": "请先选择城市",
"setAsDefault": "设置默认送货地址",
"titleRequired": "标题为必填项。",
"userAddressAddSuccess": "新地址添加成功!",
diff --git a/feature-libs/user/profile/assets/translations/zh_TW/address.json b/feature-libs/user/profile/assets/translations/zh_TW/address.json
index e562c9bbe47..ea59a33f22b 100644
--- a/feature-libs/user/profile/assets/translations/zh_TW/address.json
+++ b/feature-libs/user/profile/assets/translations/zh_TW/address.json
@@ -19,6 +19,8 @@
"placeholder": "城市"
},
"state": "州",
+ "province": "省份",
+ "district": "區/縣",
"zipCode": {
"label": "郵遞區號",
"placeholder": "郵遞區號/ZIP"
@@ -36,6 +38,8 @@
"streetAddress": "街道地址",
"aptSuite": "公寓號碼",
"selectOne": "選擇一個...",
+ "selectProvinceFirst": "請先選擇省份",
+ "selectCityFirst": "請先選擇城市",
"setAsDefault": "設定為預設交貨地址",
"titleRequired": "稱謂為必要。",
"userAddressAddSuccess": "已成功新增地址!",
diff --git a/feature-libs/user/profile/components/address-book/address-book.component.ts b/feature-libs/user/profile/components/address-book/address-book.component.ts
index 0dcf3ddc93c..74ff43deacf 100644
--- a/feature-libs/user/profile/components/address-book/address-book.component.ts
+++ b/feature-libs/user/profile/components/address-book/address-book.component.ts
@@ -110,11 +110,12 @@ export class AddressBookComponent implements OnInit {
textPhone,
textMobile,
]) => {
- let region = '';
-
- if (address.region && address.region.isocode) {
- region = address.region.isocode + ', ';
- }
+ const region = this.buildRegion(address);
+ const countryName =
+ address.country?.name || address.country?.isocode || '';
+ const townName = address.city?.name || address.town || '';
+ const districtName =
+ address.cityDistrict?.name || address.district || '';
const actions: { name: string; event: string }[] = [];
if (!address.defaultAddress) {
@@ -125,16 +126,21 @@ export class AddressBookComponent implements OnInit {
const numbers = getAddressNumbers(address, textPhone, textMobile);
+ const locationParts = [townName, region, countryName]
+ .filter(Boolean)
+ .join(', ');
+
return {
role: 'application',
textBold: address.firstName + ' ' + address.lastName,
text: [
address.line1,
address.line2,
- address.town + ', ' + region + address.country?.isocode,
+ locationParts,
+ districtName,
address.postalCode,
numbers,
- ],
+ ].filter(Boolean) as string[],
actions: actions,
header: address.defaultAddress ? `✓ ${defaultText}` : '',
deleteMsg: textVerifyDeleteMsg,
@@ -147,6 +153,10 @@ export class AddressBookComponent implements OnInit {
);
}
+ private buildRegion(address: Address): string {
+ return address.region?.name || address.region?.isocode || '';
+ }
+
setAddressAsDefault(address: Address): void {
this.service.setAddressAsDefault(address.id ?? '');
this.globalMessageService.add(
diff --git a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html
index 54fddcd11c1..3549dac0065 100644
--- a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html
+++ b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html
@@ -158,6 +158,45 @@
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
@@ -267,15 +329,23 @@
diff --git a/feature-libs/user/profile/components/address-book/address-form/address-form.component.spec.ts b/feature-libs/user/profile/components/address-book/address-form/address-form.component.spec.ts
index b491623987f..cd73c7d7193 100644
--- a/feature-libs/user/profile/components/address-book/address-form/address-form.component.spec.ts
+++ b/feature-libs/user/profile/components/address-book/address-form/address-form.component.spec.ts
@@ -14,6 +14,7 @@ import {
Country,
GlobalMessageService,
I18nTestingModule,
+ LanguageService,
Region,
Title,
UserAddressService,
@@ -70,6 +71,7 @@ const mockAddress: Address = {
line2: 'line2',
town: 'town',
region: { isocode: 'JP-27' },
+ district: '',
postalCode: 'zip',
country: { isocode: 'JP' },
phone: '123123123',
@@ -102,7 +104,20 @@ class MockUserAddressService {
verifyAddress(): Observable {
return of({});
}
+ getCities(): Observable<{ isocode?: string; name?: string }[]> {
+ return of([]);
+ }
+ getDistricts(): Observable<{ isocode?: string; name?: string }[]> {
+ return of([]);
+ }
}
+
+class MockLanguageService {
+ getActive() {
+ return of('en');
+ }
+}
+
const dialogClose$ = new BehaviorSubject('');
class MockLaunchDialogService implements Partial {
@@ -152,6 +167,10 @@ describe('AddressFormComponent', () => {
{ provide: UserAddressService, useClass: MockUserAddressService },
{ provide: GlobalMessageService, useValue: mockGlobalMessageService },
{ provide: UserProfileFacade, useClass: MockUserProfileFacade },
+ {
+ provide: LanguageService,
+ useClass: MockLanguageService,
+ },
],
})
.overrideComponent(AddressFormComponent, {
@@ -361,6 +380,81 @@ describe('AddressFormComponent', () => {
);
});
+ it('should set isChineseAddress and add validators when CN is selected', () => {
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.countrySelected({ isocode: 'CN' });
+ expect(component.isChineseAddress).toBe(true);
+ expect(component.addressForm.get('cellphone')?.validator).toBeTruthy();
+ expect(component.addressForm.get('district')?.validator).toBeTruthy();
+ });
+
+ it('should clear validators and reset state when switching away from CN', () => {
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.countrySelected({ isocode: 'CN' });
+ component.countrySelected({ isocode: 'US' });
+ expect(component.isChineseAddress).toBe(false);
+ expect(component.addressForm.get('cellphone')?.validator).toBeNull();
+ expect(component.addressForm.get('district')?.validator).toBeNull();
+ });
+
+ it('should reset town and district when region changes for CN address', () => {
+ component.isChineseAddress = true;
+ component.addressForm.get('town')?.setValue('old-town');
+ component.addressForm.get('district')?.setValue('old-district');
+ component.regionSelected({ isocode: 'CN-11' });
+ expect(component.addressForm.get('town')?.value).toBeNull();
+ expect(component.addressForm.get('district')?.value).toBeNull();
+ });
+
+ it('should not reset town and district when region changes for non-CN address', () => {
+ component.isChineseAddress = false;
+ component.addressForm.get('town')?.setValue('old-town');
+ component.regionSelected({ isocode: 'US-CA' });
+ expect(component.addressForm.get('town')?.value).toEqual('old-town');
+ });
+
+ it('should update selectedCity$ and reset district on citySelected', () => {
+ component.addressForm.get('district')?.setValue('old-district');
+ component.citySelected({ isocode: 'CN-11-1', name: 'Beijing' });
+ expect(component.addressForm.get('district')?.value).toBeNull();
+ });
+
+ it('should not update selectedCity$ when city is undefined', () => {
+ component.addressForm.get('district')?.setValue('old-district');
+ component.citySelected(undefined);
+ expect(component.addressForm.get('district')?.value).toEqual(
+ 'old-district'
+ );
+ });
+
+ it('should initialize cities as empty array', () => {
+ spyOn(userAddressService, 'getDeliveryCountries').and.returnValue(of([]));
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.ngOnInit();
+ expect(component.cities).toEqual([]);
+ });
+
+ it('should initialize districts as empty array', () => {
+ spyOn(userAddressService, 'getDeliveryCountries').and.returnValue(of([]));
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.ngOnInit();
+ expect(component.districts).toEqual([]);
+ });
+
+ it('should have empty cities when no region is selected', () => {
+ spyOn(userAddressService, 'getDeliveryCountries').and.returnValue(of([]));
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.ngOnInit();
+ expect(component.cities).toEqual([]);
+ });
+
+ it('should have empty districts when no city is selected', () => {
+ spyOn(userAddressService, 'getDeliveryCountries').and.returnValue(of([]));
+ spyOn(userAddressService, 'getRegions').and.returnValue(of([]));
+ component.ngOnInit();
+ expect(component.districts).toEqual([]);
+ });
+
it('should call verifyAddress', () => {
spyOn(component, 'verifyAddress').and.callThrough();
const mockCountryIsocode = 'test country isocode';
diff --git a/feature-libs/user/profile/components/address-book/address-form/address-form.component.ts b/feature-libs/user/profile/components/address-book/address-form/address-form.component.ts
index c5eadd3a32b..6ac6cad51f6 100644
--- a/feature-libs/user/profile/components/address-book/address-form/address-form.component.ts
+++ b/feature-libs/user/profile/components/address-book/address-form/address-form.component.ts
@@ -7,9 +7,11 @@
import { AsyncPipe, NgIf } from '@angular/common';
import {
ChangeDetectionStrategy,
+ ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
+ inject,
Input,
OnDestroy,
OnInit,
@@ -31,6 +33,7 @@ import {
ErrorModel,
GlobalMessageService,
GlobalMessageType,
+ LanguageService,
Region,
Title,
TranslatePipe,
@@ -47,7 +50,13 @@ import {
sortTitles,
} from '@spartacus/storefront';
import { UserProfileFacade } from '@spartacus/user/profile/root';
-import { BehaviorSubject, Observable, Subscription, combineLatest } from 'rxjs';
+import {
+ BehaviorSubject,
+ Observable,
+ Subscription,
+ combineLatest,
+ of,
+} from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
@Component({
@@ -68,10 +77,19 @@ import { filter, map, switchMap, take, tap } from 'rxjs/operators';
],
})
export class AddressFormComponent implements OnInit, OnDestroy {
+ protected languageService = inject(LanguageService);
+ protected cdr = inject(ChangeDetectorRef);
+ protected userProfileFacade = inject(UserProfileFacade);
+
countries$: Observable;
titles$: Observable;
regions$: Observable;
selectedCountry$: BehaviorSubject = new BehaviorSubject('');
+ selectedRegion$: BehaviorSubject = new BehaviorSubject('');
+ selectedCity$: BehaviorSubject = new BehaviorSubject('');
+ isChineseAddress = false;
+ cities: { isocode?: string; name?: string }[] = [];
+ districts: { isocode?: string; name?: string }[] = [];
addresses$: Observable;
@Input()
@@ -114,10 +132,11 @@ export class AddressFormComponent implements OnInit, OnDestroy {
lastName: ['', Validators.required],
line1: ['', Validators.required],
line2: [''],
- town: ['', Validators.required],
+ town: [null, Validators.required],
region: this.fb.group({
isocode: [null, Validators.required],
}),
+ district: [null],
postalCode: ['', Validators.required],
phone: '',
cellphone: '',
@@ -129,8 +148,7 @@ export class AddressFormComponent implements OnInit, OnDestroy {
protected userAddressService: UserAddressService,
protected globalMessageService: GlobalMessageService,
protected translation: TranslationService,
- protected launchDialogService: LaunchDialogService,
- protected userProfileFacade: UserProfileFacade
+ protected launchDialogService: LaunchDialogService
) {}
ngOnInit() {
@@ -171,6 +189,57 @@ export class AddressFormComponent implements OnInit, OnDestroy {
}
this.addresses$ = this.userAddressService.getAddresses();
+
+ this.subscription.add(
+ combineLatest([this.selectedRegion$, this.languageService.getActive()])
+ .pipe(
+ switchMap(([regionIsocode]) => {
+ if (!regionIsocode) {
+ return of([]);
+ }
+ return this.userAddressService.getCities(regionIsocode);
+ })
+ )
+ .subscribe((cities) => {
+ this.cities = cities;
+ if (this.addressData?.city?.isocode && !this.selectedCity$.value) {
+ this.selectedCity$.next(this.addressData.city.isocode);
+ this.addressForm
+ .get('town')
+ ?.setValue(this.addressData.city.isocode);
+ }
+ this.cdr.markForCheck();
+ })
+ );
+
+ this.subscription.add(
+ combineLatest([this.selectedCity$, this.languageService.getActive()])
+ .pipe(
+ switchMap(([cityIsocode]) => {
+ if (!cityIsocode) {
+ return of([]);
+ }
+ return this.userAddressService.getDistricts(cityIsocode);
+ })
+ )
+ .subscribe((districts) => {
+ this.districts = districts;
+ if (this.addressData?.cityDistrict?.isocode) {
+ this.addressForm
+ .get('district')
+ ?.setValue(this.addressData.cityDistrict.isocode);
+ }
+ this.cdr.markForCheck();
+ })
+ );
+
+ this.subscription.add(
+ this.languageService.getActive().subscribe(() => {
+ if (this.isChineseAddress) {
+ this.cdr.markForCheck();
+ }
+ })
+ );
}
getTitles(): Observable {
@@ -214,12 +283,48 @@ export class AddressFormComponent implements OnInit, OnDestroy {
countrySelected(country: Country | undefined): void {
this.addressForm.get('country')?.get('isocode')?.setValue(country?.isocode);
this.selectedCountry$.next(country?.isocode ?? '');
+ this.isChineseAddress = country?.isocode === 'CN';
+
+ const cellphoneControl = this.addressForm.get('cellphone');
+ const districtControl = this.addressForm.get('district');
+ const townControl = this.addressForm.get('town');
+ if (this.isChineseAddress) {
+ cellphoneControl?.setValidators([Validators.required]);
+ districtControl?.setValidators([Validators.required]);
+ townControl?.reset();
+ districtControl?.reset();
+ } else {
+ cellphoneControl?.clearValidators();
+ districtControl?.clearValidators();
+ districtControl?.reset();
+ townControl?.enable();
+ districtControl?.enable();
+ this.selectedRegion$.next('');
+ this.selectedCity$.next('');
+ }
+ cellphoneControl?.updateValueAndValidity();
+ districtControl?.updateValueAndValidity();
}
regionSelected(region: Region): void {
this.addressForm.get('region')?.get('isocode')?.setValue(region.isocode);
+ if (this.isChineseAddress) {
+ this.selectedRegion$.next(region.isocode ?? '');
+ this.addressForm.get('town')?.reset();
+ this.selectedCity$.next('');
+ this.addressForm.get('district')?.reset();
+ }
}
+ citySelected(city: { isocode?: string; name?: string } | undefined): void {
+ if (city?.isocode) {
+ this.selectedCity$.next(city.isocode);
+ this.addressForm.get('district')?.reset();
+ }
+ }
+
+ protected updateChinesePlaceholders(): void {}
+
toggleDefaultAddress(): void {
this.addressForm['controls'].defaultAddress.setValue(
this.addressForm.value.defaultAddress
@@ -251,13 +356,17 @@ export class AddressFormComponent implements OnInit, OnDestroy {
}
if (this.addressForm.dirty) {
- this.subscription.add(
- this.userAddressService
- .verifyAddress(this.addressForm.value)
- .subscribe((value) => {
- this.handleAddressVerificationResults(value);
- })
- );
+ if (this.isChineseAddress) {
+ this.submitAddress.emit(this.addressForm.value);
+ } else {
+ this.subscription.add(
+ this.userAddressService
+ .verifyAddress(this.addressForm.value)
+ .subscribe((value) => {
+ this.handleAddressVerificationResults(value);
+ })
+ );
+ }
} else {
// address form value not changed
// ignore duplicate address
diff --git a/feature-libs/user/profile/root/model/user-profile.model.ts b/feature-libs/user/profile/root/model/user-profile.model.ts
index 287115c406c..6e189be4caf 100644
--- a/feature-libs/user/profile/root/model/user-profile.model.ts
+++ b/feature-libs/user/profile/root/model/user-profile.model.ts
@@ -30,6 +30,16 @@ export interface Title {
name?: string;
}
+export interface ChineseCity {
+ name?: string;
+ isocode?: string;
+}
+
+export interface ChineseDistrict {
+ name?: string;
+ isocode?: string;
+}
+
export interface UserSignUp {
firstName?: string;
lastName?: string;
diff --git a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts
index dedeba545a2..f6edb7be1fa 100644
--- a/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts
+++ b/integration-libs/opf/checkout/components/opf-checkout-billing-address-form/opf-checkout-billing-address-form.component.spec.ts
@@ -13,6 +13,7 @@ import {
BaseSiteService,
Country,
MockTranslatePipe,
+ SiteAdapter,
TranslatePipe,
UserAddressAdapter,
} from '@spartacus/core';
@@ -73,6 +74,7 @@ describe('OpfCheckoutBillingAddressFormComponent', () => {
},
{ provide: Store, useValue: {} },
{ provide: UserAddressAdapter, useValue: {} },
+ { provide: SiteAdapter, useValue: {} },
{ provide: CheckoutStepService, useValue: {} },
{ provide: BaseSiteService, useValue: {} },
{ provide: ActivatedRoute, useValue: { params: of({}) } },
diff --git a/projects/core/src/model/address.model.ts b/projects/core/src/model/address.model.ts
index 693ab204bcc..c9bca07d6f9 100644
--- a/projects/core/src/model/address.model.ts
+++ b/projects/core/src/model/address.model.ts
@@ -38,8 +38,10 @@ export interface Address {
line2?: string;
postalCode?: string;
town?: string;
+ city?: { isocode?: string; name?: string };
region?: Region;
district?: string;
+ cityDistrict?: { isocode?: string; name?: string };
country?: Country;
cellphone?: string;
diff --git a/projects/core/src/occ/adapters/site-context/default-occ-site-context-config.ts b/projects/core/src/occ/adapters/site-context/default-occ-site-context-config.ts
index b307b0021fd..003c835a3d8 100644
--- a/projects/core/src/occ/adapters/site-context/default-occ-site-context-config.ts
+++ b/projects/core/src/occ/adapters/site-context/default-occ-site-context-config.ts
@@ -15,6 +15,8 @@ export const defaultOccSiteContextConfig: OccConfig = {
countries: 'countries',
regions:
'countries/${isoCode}/regions?fields=regions(name,isocode,isocodeShort)',
+ chineseAddressCities: 'regions/${regionId}/cities',
+ chineseAddressDistricts: 'cities/${cityId}/districts',
baseSites: 'basesites?fields=FULL',
},
},
diff --git a/projects/core/src/occ/adapters/site-context/occ-site.adapter.ts b/projects/core/src/occ/adapters/site-context/occ-site.adapter.ts
index 2cf18adcf66..556f85401c8 100644
--- a/projects/core/src/occ/adapters/site-context/occ-site.adapter.ts
+++ b/projects/core/src/occ/adapters/site-context/occ-site.adapter.ts
@@ -110,4 +110,30 @@ export class OccSiteAdapter implements SiteAdapter {
this.converterService.pipeableMany(BASE_SITE_NORMALIZER)
);
}
+
+ loadCities(
+ regionIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ const url = this.occEndpointsService.buildUrl('chineseAddressCities', {
+ urlParams: { regionId: regionIsocode },
+ });
+ return this.http
+ .get<{ cities: { isocode?: string; name?: string }[] }>(url, {
+ params: { fields: 'cities(name,isocode)' },
+ })
+ .pipe(map((res) => res.cities ?? []));
+ }
+
+ loadDistricts(
+ cityIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ const url = this.occEndpointsService.buildUrl('chineseAddressDistricts', {
+ urlParams: { cityId: cityIsocode },
+ });
+ return this.http
+ .get<{ districts: { isocode?: string; name?: string }[] }>(url, {
+ params: { fields: 'districts(name,isocode)' },
+ })
+ .pipe(map((res) => res.districts ?? []));
+ }
}
diff --git a/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts b/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts
index bd067359882..908f401b83a 100644
--- a/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts
+++ b/projects/core/src/occ/adapters/user/occ-user-address.adapter.ts
@@ -46,13 +46,18 @@ export class OccUserAddressAdapter implements UserAddressAdapter {
...CONTENT_TYPE_JSON_HEADER,
});
- return this.http.get(url, { headers }).pipe(
- catchError((error: any) => {
- throw tryNormalizeHttpError(error, this.logger);
- }),
- map((addressList) => addressList.addresses ?? []),
- this.converter.pipeableMany(ADDRESS_NORMALIZER)
- );
+ return this.http
+ .get(url, {
+ headers,
+ params: { fields: 'addresses(FULL)' },
+ })
+ .pipe(
+ catchError((error: any) => {
+ throw tryNormalizeHttpError(error, this.logger);
+ }),
+ map((addressList) => addressList.addresses ?? []),
+ this.converter.pipeableMany(ADDRESS_NORMALIZER)
+ );
}
add(userId: string, address: Address): Observable<{}> {
diff --git a/projects/core/src/occ/occ-models/occ-endpoints.model.ts b/projects/core/src/occ/occ-models/occ-endpoints.model.ts
index 5f744a4fc5c..6ead02c43a2 100644
--- a/projects/core/src/occ/occ-models/occ-endpoints.model.ts
+++ b/projects/core/src/occ/occ-models/occ-endpoints.model.ts
@@ -97,6 +97,14 @@ export interface OccEndpoints {
* @member {string}
*/
regions?: string | OccEndpoint;
+ /**
+ * Fetch cities for a Chinese address region
+ */
+ chineseAddressCities?: string | OccEndpoint;
+ /**
+ * Fetch districts for a Chinese address city
+ */
+ chineseAddressDistricts?: string | OccEndpoint;
/**
* Payment details root endpoint.
*
diff --git a/projects/core/src/site-context/connectors/site.adapter.ts b/projects/core/src/site-context/connectors/site.adapter.ts
index 4dad32880e5..3878187f608 100644
--- a/projects/core/src/site-context/connectors/site.adapter.ts
+++ b/projects/core/src/site-context/connectors/site.adapter.ts
@@ -38,4 +38,18 @@ export abstract class SiteAdapter {
* Abstract method used to get all base sites data.
*/
abstract loadBaseSites(): Observable;
+
+ /**
+ * Abstract method used to get cities for a region.
+ */
+ abstract loadCities(
+ regionIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]>;
+
+ /**
+ * Abstract method used to get districts for a city.
+ */
+ abstract loadDistricts(
+ cityIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]>;
}
diff --git a/projects/core/src/site-context/connectors/site.connector.spec.ts b/projects/core/src/site-context/connectors/site.connector.spec.ts
index e8a5bbee336..8efb8c063a8 100644
--- a/projects/core/src/site-context/connectors/site.connector.spec.ts
+++ b/projects/core/src/site-context/connectors/site.connector.spec.ts
@@ -24,7 +24,7 @@ class MockSiteAdapter implements SiteAdapter {
);
loadRegions = createSpy('SiteAdapter.loadRegions').and.callFake(
- (countryCode) => of(`loadRegions-${countryCode}`)
+ (countryCode: string) => of(`loadRegions-${countryCode}`)
);
loadBaseSite = createSpy('SiteAdapter.loadBaseSite').and.callFake(() =>
@@ -34,6 +34,12 @@ class MockSiteAdapter implements SiteAdapter {
loadBaseSites = createSpy('SiteAdapter.loadBaseSites').and.callFake(() =>
of(mockBaseSites)
);
+
+ loadCities = createSpy('SiteAdapter.loadCities').and.returnValue(of([]));
+
+ loadDistricts = createSpy('SiteAdapter.loadDistricts').and.returnValue(
+ of([])
+ );
}
describe('SiteConnector', () => {
diff --git a/projects/core/src/site-context/connectors/site.connector.ts b/projects/core/src/site-context/connectors/site.connector.ts
index 27d1a66c447..055127c4582 100644
--- a/projects/core/src/site-context/connectors/site.connector.ts
+++ b/projects/core/src/site-context/connectors/site.connector.ts
@@ -32,6 +32,18 @@ export class SiteConnector {
return this.adapter.loadRegions(countryIsoCode);
}
+ getCities(
+ regionIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ return this.adapter.loadCities(regionIsocode);
+ }
+
+ getDistricts(
+ cityIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ return this.adapter.loadDistricts(cityIsocode);
+ }
+
getBaseSite(siteUid?: string): Observable {
return this.adapter.loadBaseSite(siteUid);
}
diff --git a/projects/core/src/user/facade/user-address.service.spec.ts b/projects/core/src/user/facade/user-address.service.spec.ts
index 223e32e9c47..a28cdcd7aeb 100644
--- a/projects/core/src/user/facade/user-address.service.spec.ts
+++ b/projects/core/src/user/facade/user-address.service.spec.ts
@@ -12,6 +12,7 @@ import {
import { OCC_USER_ID_CURRENT } from '../../occ/utils/occ-constants';
import { PROCESS_FEATURE } from '../../process/store/process-state';
import * as fromProcessReducers from '../../process/store/reducers';
+import { SiteAdapter } from '../../site-context/connectors/site.adapter';
import { UserAddressConnector } from '../connectors/address/user-address.connector';
import { UserActions } from '../store/actions/index';
import * as fromStoreReducers from '../store/reducers/index';
@@ -67,6 +68,7 @@ describe('UserAddressService', () => {
UserAddressService,
{ provide: UserIdService, useClass: MockUserIdService },
{ provide: UserAddressConnector, useClass: MockUserAddressConnector },
+ { provide: SiteAdapter, useValue: {} },
],
});
diff --git a/projects/core/src/user/facade/user-address.service.ts b/projects/core/src/user/facade/user-address.service.ts
index fbe5b31a294..d90b60f4eea 100644
--- a/projects/core/src/user/facade/user-address.service.ts
+++ b/projects/core/src/user/facade/user-address.service.ts
@@ -19,6 +19,7 @@ import {
Command,
CommandService,
} from '../../util/command-query/command.service';
+import { SiteConnector } from '../../site-context/connectors/site.connector';
import { UserAddressConnector } from '../connectors/address/user-address.connector';
import { UserActions } from '../store/actions/index';
import { UsersSelectors } from '../store/selectors/index';
@@ -32,7 +33,8 @@ export class UserAddressService {
protected store: Store,
protected userIdService: UserIdService,
protected userAddressConnector: UserAddressConnector,
- protected command: CommandService
+ protected command: CommandService,
+ protected siteConnector: SiteConnector
) {}
/**
@@ -197,6 +199,19 @@ export class UserAddressService {
})
);
}
+
+ getCities(
+ regionIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ return this.siteConnector.getCities(regionIsocode);
+ }
+
+ getDistricts(
+ cityIsocode: string
+ ): Observable<{ isocode?: string; name?: string }[]> {
+ return this.siteConnector.getDistricts(cityIsocode);
+ }
+
/**
* Verifies the address
* @param address : the address to be verified