mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
FR-699: Shop creation fixes (#538)
This commit is contained in:
parent
5f86ed6632
commit
407b46da20
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,7 +13,6 @@ chrome-profiler-events.json
|
||||
speed-measure-plugin.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
|
5
.idea/.gitignore
vendored
Normal file
5
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
7
.idea/CamelCaseConfigNew.xml
Normal file
7
.idea/CamelCaseConfigNew.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CamelCaseConfig">
|
||||
<option name="cb5State" value="false" />
|
||||
<option name="cb6State" value="false" />
|
||||
</component>
|
||||
</project>
|
50
.idea/codeStyles/Project.xml
Normal file
50
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_QUOTE_STYLE" value="Single" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
13
.idea/dashboard.iml
Normal file
13
.idea/dashboard.iml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/dist" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
.idea/inspectionProfiles/Project_Default.xml
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/jsLinters/eslint.xml
Normal file
6
.idea/jsLinters/eslint.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EslintConfiguration">
|
||||
<option name="fix-on-save" value="true" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/dashboard.iml" filepath="$PROJECT_DIR$/.idea/dashboard.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
7
.idea/prettier.xml
Normal file
7
.idea/prettier.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myRunOnSave" value="true" />
|
||||
<option name="myFilesPattern" value="{**/*,*}.{js,ts,jsx,tsx,md,json,html,svg}" />
|
||||
</component>
|
||||
</project>
|
18
.idea/vcs.xml
Normal file
18
.idea/vcs.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/build_utils" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/claim-management/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/dark-api/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/messages/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/organizations/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/questionary-aggr-proxy/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/questionary/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/sender/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/swag-analytics/v1" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/swag-wallets/v0" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/swag/v2" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/schemes/url-shortener/v0" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
32
package-lock.json
generated
32
package-lock.json
generated
@ -47,6 +47,7 @@
|
||||
"moment": "^2.24.0",
|
||||
"ng-apexcharts": "^1.3.0",
|
||||
"ng-yandex-metrika": "^4.0.0",
|
||||
"ngx-mat-select-search": "^3.3.0",
|
||||
"rxjs": "~6.6.7",
|
||||
"shelljs": "^0.8.4",
|
||||
"ts-keycode-enum": "^1.0.6",
|
||||
@ -14200,6 +14201,22 @@
|
||||
"rxjs": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-mat-select-search": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-3.3.0.tgz",
|
||||
"integrity": "sha512-OLLkNpWdFvb+AKtKEuq0G48gz9l5HA1WuVBuayOubekpq3UlMUHh+xr9TFh5DX4VY6NektTzG/XYfdAh5Sg9dg==",
|
||||
"dependencies": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/material": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-mat-select-search/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
@ -33376,6 +33393,21 @@
|
||||
"webpack-merge": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"ngx-mat-select-search": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-3.3.0.tgz",
|
||||
"integrity": "sha512-OLLkNpWdFvb+AKtKEuq0G48gz9l5HA1WuVBuayOubekpq3UlMUHh+xr9TFh5DX4VY6NektTzG/XYfdAh5Sg9dg==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
|
@ -9,7 +9,7 @@
|
||||
"build": "ng build --extra-webpack-config webpack.extra.js",
|
||||
"test": "ng test",
|
||||
"coverage": "npx http-server -c-1 -o -p 9875 ./coverage",
|
||||
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1801",
|
||||
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1765",
|
||||
"lint-cache-cmd": "npm run lint-cmd -- --cache",
|
||||
"lint": "npm run lint-cache-cmd",
|
||||
"lint-fix": "npm run lint-cache-cmd -- --fix",
|
||||
@ -68,6 +68,7 @@
|
||||
"moment": "^2.24.0",
|
||||
"ng-apexcharts": "^1.3.0",
|
||||
"ng-yandex-metrika": "^4.0.0",
|
||||
"ngx-mat-select-search": "^3.3.0",
|
||||
"rxjs": "~6.6.7",
|
||||
"shelljs": "^0.8.4",
|
||||
"ts-keycode-enum": "^1.0.6",
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { PartyModification, PayoutToolInfo, RussianBankAccount } from '@dsh/api-codegen/claim-management';
|
||||
|
||||
import { RussianShopCreateData } from '../../../../sections/payment-section/integrations/shops/shop-creation/create-russian-shop-entity/types/russian-shop-create-data';
|
||||
import { createContractPayoutToolCreationModification } from './create-contract-payout-tool-creation-modification';
|
||||
import { createContractPayoutToolCreationModification } from '@dsh/api/claims/claim-party-modification';
|
||||
|
||||
export function createRussianContractPayoutToolCreationModification(
|
||||
id: string,
|
||||
@ -18,16 +16,3 @@ export function createRussianContractPayoutToolCreationModification(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createTestRussianContractPayoutToolCreationModification(
|
||||
id: string,
|
||||
payoutToolID: string,
|
||||
{ bankAccount: { account, bankName, bankPostAccount, bankBik } }: RussianShopCreateData
|
||||
): PartyModification {
|
||||
return createRussianContractPayoutToolCreationModification(id, payoutToolID, {
|
||||
account,
|
||||
bankName,
|
||||
bankPostAccount,
|
||||
bankBik,
|
||||
});
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { IdGeneratorService } from '@rbkmoney/id-generator';
|
||||
import { defer, Observable, Subject } from 'rxjs';
|
||||
import { shareReplay, startWith, switchMapTo } from 'rxjs/operators';
|
||||
import { startWith, switchMapTo } from 'rxjs/operators';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { ShopsService } from '@dsh/api-codegen/capi/shops.service';
|
||||
import { SHARE_REPLAY_CONF } from '@dsh/operators';
|
||||
import { shareReplayRefCount } from '@dsh/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ApiShopsService {
|
||||
shops$: Observable<Shop[]> = defer(() => this.reloadShops$).pipe(
|
||||
startWith<void, null>(null),
|
||||
switchMapTo(this.getShops()),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
shareReplayRefCount()
|
||||
);
|
||||
|
||||
private reloadShops$ = new Subject<void>();
|
||||
|
@ -18,7 +18,7 @@ import * as Sentry from '@sentry/angular';
|
||||
import { ErrorModule, KeycloakTokenInfoModule, LoggerModule } from '@dsh/app/shared/services';
|
||||
import { QUERY_PARAMS_SERIALIZERS } from '@dsh/app/shared/services/query-params/utils/query-params-serializers';
|
||||
import { createDateRangeWithPresetSerializer } from '@dsh/components/filters/date-range-filter';
|
||||
import { AUTOCOMPLETE_FIELD_OPTIONS } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { SELECT_SEARCH_FIELD_OPTIONS } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { ENV, environment } from '../environments';
|
||||
import { OrganizationsModule } from './api';
|
||||
@ -122,7 +122,7 @@ import { YandexMetrikaConfigService, YandexMetrikaModule } from './yandex-metrik
|
||||
deps: [Router],
|
||||
},
|
||||
{
|
||||
provide: AUTOCOMPLETE_FIELD_OPTIONS,
|
||||
provide: SELECT_SEARCH_FIELD_OPTIONS,
|
||||
useValue: {
|
||||
svgIcon: 'cross',
|
||||
},
|
||||
|
@ -1,12 +1,21 @@
|
||||
<div class="container" fxLayout="column" fxLayoutGap="32px">
|
||||
<div fxLayout="column" fxLayoutGap="48px">
|
||||
<div class="mat-display-1" *transloco="let t; scope: 'claims'; read: 'claims'">
|
||||
<div *transloco="let t; scope: 'claims'; read: 'claims'" fxLayout="column" fxLayoutGap="48px">
|
||||
<div class="mat-display-1">
|
||||
{{ t('title') }}
|
||||
</div>
|
||||
<dsh-claims-search-filters
|
||||
[initParams]="params$ | async"
|
||||
(searchParamsChanges)="search($event)"
|
||||
></dsh-claims-search-filters>
|
||||
<div
|
||||
fxLayout="column-reverse"
|
||||
fxLayout.gt-sm="row"
|
||||
fxLayoutGap="16px"
|
||||
fxLayoutAlign="start start"
|
||||
fxLayoutAlign.gt-sm="space-between"
|
||||
>
|
||||
<dsh-claims-search-filters
|
||||
[initParams]="params$ | async"
|
||||
(searchParamsChanges)="search($event)"
|
||||
></dsh-claims-search-filters>
|
||||
<button (click)="createShop()" color="accent" dsh-button>{{ t('createClaim') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<dsh-claims-list
|
||||
[lastUpdated]="lastUpdated$ | async"
|
||||
|
@ -5,9 +5,10 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { Router } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { deepEqual, instance, mock, verify, when } from 'ts-mockito';
|
||||
import { deepEqual, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { Claim } from '@dsh/api-codegen/claim-management';
|
||||
import { ShopCreationService } from '@dsh/app/shared/components/shop-creation';
|
||||
import { QueryParamsService } from '@dsh/app/shared/services/query-params';
|
||||
import { provideMockService } from '@dsh/app/shared/tests';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
@ -51,11 +52,13 @@ describe('ClaimsComponent', () => {
|
||||
let mockFetchClaimsService: FetchClaimsService;
|
||||
let mockRouter: Router;
|
||||
let mockQueryParamsService: QueryParamsService<any>;
|
||||
let mockShopCreationService: ShopCreationService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRouter = mock(Router);
|
||||
mockFetchClaimsService = mock(FetchClaimsService);
|
||||
mockQueryParamsService = mock(QueryParamsService);
|
||||
mockShopCreationService = mock(ShopCreationService);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@ -76,15 +79,10 @@ describe('ClaimsComponent', () => {
|
||||
],
|
||||
declarations: [ClaimsComponent, MockClaimsSearchFiltersComponent, MockClaimsListComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: FetchClaimsService,
|
||||
useFactory: () => instance(mockFetchClaimsService),
|
||||
},
|
||||
{
|
||||
provide: Router,
|
||||
useFactory: () => instance(mockRouter),
|
||||
},
|
||||
provideMockService(FetchClaimsService, mockFetchClaimsService),
|
||||
provideMockService(Router, mockRouter),
|
||||
provideMockService(QueryParamsService, mockQueryParamsService),
|
||||
provideMockService(ShopCreationService, mockShopCreationService),
|
||||
],
|
||||
})
|
||||
.overrideComponent(ClaimsComponent, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ShopCreationService } from '@dsh/app/shared/components/shop-creation';
|
||||
import { SpinnerType } from '@dsh/components/indicators';
|
||||
|
||||
import { QueryParamsService } from '../../../shared/services/query-params';
|
||||
@ -25,7 +26,8 @@ export class ClaimsComponent {
|
||||
constructor(
|
||||
private fetchClaimsService: FetchClaimsService,
|
||||
private router: Router,
|
||||
private qp: QueryParamsService<Filters>
|
||||
private qp: QueryParamsService<Filters>,
|
||||
private shopCreationService: ShopCreationService
|
||||
) {}
|
||||
|
||||
search(filters: Filters): void {
|
||||
@ -44,4 +46,8 @@ export class ClaimsComponent {
|
||||
goToClaimDetails(id: number): void {
|
||||
void this.router.navigate(['claim-section', 'claims', id]);
|
||||
}
|
||||
|
||||
createShop(): void {
|
||||
this.shopCreationService.createShop();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { ClaimsService } from '@dsh/api/claims';
|
||||
import { ShopCreationModule } from '@dsh/app/shared/components/shop-creation';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { IndicatorsModule } from '@dsh/components/indicators';
|
||||
import { LayoutModule } from '@dsh/components/layout';
|
||||
@ -38,6 +39,7 @@ import { ClaimsComponent } from './claims.component';
|
||||
ButtonModule,
|
||||
ClaimsListModule,
|
||||
ClaimsSearchFiltersModule,
|
||||
ShopCreationModule,
|
||||
],
|
||||
declarations: [ClaimsComponent],
|
||||
exports: [ClaimsComponent],
|
||||
|
@ -3,10 +3,9 @@ import isNil from 'lodash-es/isNil';
|
||||
import { BehaviorSubject, combineLatest, Observable, ReplaySubject } from 'rxjs';
|
||||
import { map, mapTo, pluck, scan, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
import { Shop as ApiShop } from '@dsh/api-codegen/capi/swagger-codegen';
|
||||
import { PaymentInstitutionRealm } from '@dsh/api/model';
|
||||
import { PaymentInstitution, Shop as ApiShop } from '@dsh/api-codegen/capi/swagger-codegen';
|
||||
import { ApiShopsService } from '@dsh/api/shop';
|
||||
import { mapToTimestamp, SHARE_REPLAY_CONF } from '@dsh/operators';
|
||||
import { mapToTimestamp, shareReplayRefCount } from '@dsh/operators';
|
||||
|
||||
import { filterShopsByRealm } from '../../../../operations/operators';
|
||||
import { ShopBalance } from '../../types/shop-balance';
|
||||
@ -17,6 +16,8 @@ import { ShopsFiltersStoreService } from '../shops-filters-store/shops-filters-s
|
||||
import { ShopsFiltersService } from '../shops-filters/shops-filters.service';
|
||||
import { combineShopItem } from './combine-shop-item';
|
||||
|
||||
import RealmEnum = PaymentInstitution.RealmEnum;
|
||||
|
||||
const DEFAULT_LIST_PAGINATION_OFFSET = 20;
|
||||
|
||||
export const SHOPS_LIST_PAGINATION_OFFSET = new InjectionToken('shops-list-pagination-offset');
|
||||
@ -32,7 +33,7 @@ export class FetchShopsService {
|
||||
private selectedIndex$ = new ReplaySubject<number>(1);
|
||||
private listOffset$: Observable<number>;
|
||||
|
||||
private realmData$ = new ReplaySubject<PaymentInstitutionRealm>(1);
|
||||
private realmData$ = new ReplaySubject<RealmEnum>(1);
|
||||
|
||||
private showMore$ = new ReplaySubject<void>(1);
|
||||
private loader$ = new BehaviorSubject<boolean>(true);
|
||||
@ -57,7 +58,7 @@ export class FetchShopsService {
|
||||
this.initFiltersStore();
|
||||
}
|
||||
|
||||
initRealm(realm: PaymentInstitutionRealm): void {
|
||||
initRealm(realm: RealmEnum): void {
|
||||
this.realmData$.next(realm);
|
||||
}
|
||||
|
||||
@ -96,10 +97,7 @@ export class FetchShopsService {
|
||||
}
|
||||
|
||||
private initAllShopsFetching(): void {
|
||||
this.allShops$ = this.realmData$.pipe(
|
||||
filterShopsByRealm(this.apiShopsService.shops$),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
this.allShops$ = this.realmData$.pipe(filterShopsByRealm(this.apiShopsService.shops$), shareReplayRefCount());
|
||||
}
|
||||
|
||||
private initOffsetObservable(): void {
|
||||
@ -108,7 +106,7 @@ export class FetchShopsService {
|
||||
withLatestFrom(this.selectedIndex$),
|
||||
map(([curOffset]: [number, number]) => curOffset),
|
||||
scan((offset: number, limit: number) => offset + limit, 0),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
shareReplayRefCount()
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,7 +130,7 @@ export class FetchShopsService {
|
||||
.pipe(map((balances: ShopBalance[]) => combineShopItem(shops, balances)));
|
||||
}),
|
||||
tap(() => this.stopLoading()),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
shareReplayRefCount()
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,7 +143,7 @@ export class FetchShopsService {
|
||||
this.shownShops$.pipe(pluck('length')),
|
||||
]).pipe(
|
||||
map(([count, showedCount]: [number, number]) => count > showedCount),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
shareReplayRefCount()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ShopType } from '../../../types/shop-type';
|
||||
import { CreateShopDialogResponse } from '../../create-russian-shop-entity/types/create-shop-dialog-response';
|
||||
import { CreateShopDialogConfig } from '../../types/create-shop-dialog-config';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-create-shop-dialog',
|
||||
templateUrl: 'create-shop-dialog.component.html',
|
||||
styleUrls: ['create-shop-dialog.component.scss'],
|
||||
})
|
||||
export class CreateShopDialogComponent {
|
||||
selectedShopType: ShopType;
|
||||
|
||||
selectionConfirmed = false;
|
||||
|
||||
shopType = ShopType;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<CreateShopDialogComponent, CreateShopDialogResponse>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: CreateShopDialogConfig,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
onTypeChange(type: ShopType): void {
|
||||
this.selectedShopType = type;
|
||||
}
|
||||
|
||||
next(): void {
|
||||
if (this.selectedShopType === ShopType.New) {
|
||||
this.dialogRef.close();
|
||||
this.router.navigate(['claim-section', 'onboarding']);
|
||||
}
|
||||
this.selectionConfirmed = true;
|
||||
}
|
||||
|
||||
sendClaim(): void {
|
||||
this.dialogRef.close('send');
|
||||
}
|
||||
|
||||
cancelClaim(): void {
|
||||
this.dialogRef.close('canceled');
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
import { ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { of } from 'rxjs';
|
||||
import { instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { AutocompleteVirtualScrollModule } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll';
|
||||
import { BaseOption } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll/types/base-option';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
|
||||
import { generateMockShopsList } from '../../../../tests/generate-mock-shops-list';
|
||||
import { BANK_SHOP_ID_FIELD } from '../../consts';
|
||||
import { ShopOptionsSelectionService } from '../../services/shop-options-selection/shop-options-selection.service';
|
||||
import { ExistingBankAccountComponent } from './existing-bank-account.component';
|
||||
|
||||
describe('ExistingBankAccountComponent', () => {
|
||||
let component: ExistingBankAccountComponent;
|
||||
let fixture: ComponentFixture<ExistingBankAccountComponent>;
|
||||
let mockShopOptionsSelectionService: ShopOptionsSelectionService;
|
||||
const mockShopSelectionControl = new FormControl();
|
||||
|
||||
async function makeTestingModule() {
|
||||
TestBed.resetTestingModule();
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, ReactiveFormsModule, AutocompleteVirtualScrollModule, getTranslocoModule()],
|
||||
providers: [
|
||||
{
|
||||
provide: ShopOptionsSelectionService,
|
||||
useFactory: () => instance(mockShopOptionsSelectionService),
|
||||
},
|
||||
],
|
||||
declarations: [ExistingBankAccountComponent],
|
||||
})
|
||||
.overrideComponent(ExistingBankAccountComponent, {
|
||||
set: {
|
||||
providers: [],
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ExistingBankAccountComponent);
|
||||
component = fixture.componentInstance;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockShopOptionsSelectionService = mock(ShopOptionsSelectionService);
|
||||
|
||||
when(mockShopOptionsSelectionService.options$).thenReturn(of([]));
|
||||
when(mockShopOptionsSelectionService.selectedShop$).thenReturn(of());
|
||||
when(mockShopOptionsSelectionService.control).thenReturn(mockShopSelectionControl);
|
||||
});
|
||||
|
||||
describe('creation', () => {
|
||||
beforeEach(async () => {
|
||||
await makeTestingModule();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
when(mockShopOptionsSelectionService.selectedShop$).thenReturn(of());
|
||||
component.form = new FormGroup({
|
||||
[BANK_SHOP_ID_FIELD]: new FormControl(),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
let mockShopsList: Shop[];
|
||||
let mockOptionsList: BaseOption<string>[];
|
||||
|
||||
beforeEach(() => {
|
||||
mockShopsList = generateMockShopsList(15);
|
||||
mockOptionsList = mockShopsList.map(({ id, details: { name: label } }) => {
|
||||
return { id, label };
|
||||
});
|
||||
|
||||
when(mockShopOptionsSelectionService.options$).thenReturn(of(mockOptionsList));
|
||||
});
|
||||
|
||||
it('should init innerFormControl with form group value', async () => {
|
||||
await makeTestingModule();
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_SHOP_ID_FIELD]: new FormControl(mockOptionsList[5].id),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.innerShopControl.value).toEqual(mockOptionsList[5]);
|
||||
});
|
||||
|
||||
it('should update form control on every selected change', async () => {
|
||||
const mockFormControl = mock(FormControl);
|
||||
|
||||
when(mockFormControl.value).thenReturn(undefined);
|
||||
when(mockShopOptionsSelectionService.selectedShop$).thenReturn(of(mockShopsList[2], mockShopsList[5]));
|
||||
when(mockFormControl.setValue(mockShopsList[2].id)).thenReturn();
|
||||
when(mockFormControl.setValue(mockShopsList[5].id)).thenReturn();
|
||||
|
||||
await makeTestingModule();
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_SHOP_ID_FIELD]: instance(mockFormControl),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
verify(mockFormControl.setValue(mockShopsList[2].id)).once();
|
||||
verify(mockFormControl.setValue(mockShopsList[5].id)).once();
|
||||
expect().nothing();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,63 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map, take } from 'rxjs/operators';
|
||||
|
||||
import { PayoutTool, Shop } from '@dsh/api-codegen/capi';
|
||||
import { BaseOption } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll/types/base-option';
|
||||
|
||||
import { BANK_SHOP_ID_FIELD } from '../../consts';
|
||||
import { ShopOptionsSelectionService } from '../../services/shop-options-selection/shop-options-selection.service';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-existing-bank-account',
|
||||
templateUrl: 'existing-bank-account.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ShopOptionsSelectionService],
|
||||
})
|
||||
export class ExistingBankAccountComponent implements OnInit {
|
||||
@Input() form: FormGroup;
|
||||
@Input() payoutTool: PayoutTool;
|
||||
@Input() isLoading: boolean;
|
||||
@Input() hasError: boolean;
|
||||
@Input() contentWindow: HTMLElement;
|
||||
|
||||
shopsList$: Observable<BaseOption<string>[]> = this.shopOptionsService.options$;
|
||||
innerShopControl: FormControl = this.shopOptionsService.control;
|
||||
|
||||
private get shopControl(): FormControl {
|
||||
if (isNil(this.form) || isNil(this.form.get(BANK_SHOP_ID_FIELD))) {
|
||||
throw new Error(`Can't find shop control. FormGroup or FormControl doesn't exist`);
|
||||
}
|
||||
return this.form.get(BANK_SHOP_ID_FIELD) as FormControl;
|
||||
}
|
||||
|
||||
constructor(private shopOptionsService: ShopOptionsSelectionService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const formShopId = this.shopControl.value as string | undefined;
|
||||
this.shopsList$
|
||||
.pipe(
|
||||
map((shops: BaseOption<string>[]) => {
|
||||
return shops.find(({ id }: BaseOption<string>) => id === formShopId);
|
||||
}),
|
||||
take(1),
|
||||
filter(Boolean)
|
||||
)
|
||||
.subscribe((shop: BaseOption<string>) => {
|
||||
this.innerShopControl.setValue(shop);
|
||||
});
|
||||
|
||||
this.shopOptionsService.selectedShop$
|
||||
.pipe(
|
||||
map((shop: Shop | null) => (isNil(shop) ? '' : shop.id)),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe((shopID: string) => {
|
||||
this.shopControl.setValue(shopID);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { DaDataService as DaDataApiService } from '@dsh/api-codegen/aggr-proxy';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
|
||||
import { DaDataModule } from '../../../../../../../../dadata';
|
||||
import {
|
||||
NEW_BANK_ACCOUNT_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_BIK_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_NAME_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_SEARCH_FIELD,
|
||||
} from '../../consts';
|
||||
import { NewBankAccountComponent } from './new-bank-account.component';
|
||||
|
||||
describe('NewBankAccountComponent', () => {
|
||||
let component: NewBankAccountComponent;
|
||||
let fixture: ComponentFixture<NewBankAccountComponent>;
|
||||
let mockDaDataApiService: DaDataApiService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDaDataApiService = mock(DaDataApiService);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
DaDataModule,
|
||||
NoopAnimationsModule,
|
||||
getTranslocoModule(),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: DaDataApiService,
|
||||
useFactory: () => instance(mockDaDataApiService),
|
||||
},
|
||||
],
|
||||
declarations: [NewBankAccountComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NewBankAccountComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.form = new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_FIELD]: new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_SEARCH_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
}),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('bankAccountForm', () => {
|
||||
it('should return form group', () => {
|
||||
const testFormGroup = new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_FIELD]: new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_SEARCH_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
}),
|
||||
});
|
||||
|
||||
component.form = testFormGroup;
|
||||
|
||||
expect(component.bankAccountForm).toEqual(testFormGroup.get(NEW_BANK_ACCOUNT_FIELD) as FormGroup);
|
||||
});
|
||||
|
||||
it(`should throw an error if form wasn't provided`, () => {
|
||||
component.form = undefined;
|
||||
expect(() => component.bankAccountForm).toThrowError(`Form wasn't provided`);
|
||||
});
|
||||
|
||||
it(`should throw an error if group doesn't exist on form`, () => {
|
||||
component.form = new FormGroup({});
|
||||
expect(() => component.bankAccountForm).toThrowError(
|
||||
`Form doesn't contains "${NEW_BANK_ACCOUNT_FIELD}" control`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bankAccountNameControl', () => {
|
||||
it('should return form group', () => {
|
||||
const testFormControl = new FormControl('my value', [Validators.required]);
|
||||
|
||||
component.form = new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_FIELD]: new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_SEARCH_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: testFormControl,
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
}),
|
||||
});
|
||||
|
||||
expect(component.bankAccountNameControl).toEqual(testFormControl);
|
||||
});
|
||||
|
||||
it(`should throw an error if form wasn't provided`, () => {
|
||||
component.form = undefined;
|
||||
expect(() => component.bankAccountNameControl).toThrowError(`Form wasn't provided`);
|
||||
});
|
||||
|
||||
it(`should throw an error if control doesn't exist in group`, () => {
|
||||
component.form = new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_FIELD]: new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_SEARCH_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
[NEW_BANK_ACCOUNT_ACCOUNT_FIELD]: new FormControl('', [Validators.required]),
|
||||
}),
|
||||
});
|
||||
expect(() => component.bankAccountNameControl).toThrowError(
|
||||
`Form doesn't contains "${NEW_BANK_ACCOUNT_FIELD}.${NEW_BANK_ACCOUNT_BANK_NAME_FIELD}" control`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bankSelected', () => {
|
||||
it('should patch form value', () => {
|
||||
const spyOnPatchValue = spyOn(component.form, 'patchValue').and.callThrough();
|
||||
|
||||
component.bankSelected({
|
||||
correspondentAccount: 'account',
|
||||
bic: '0000000000000',
|
||||
value: 'my name',
|
||||
});
|
||||
|
||||
expect(spyOnPatchValue).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnPatchValue).toHaveBeenCalledWith(
|
||||
{
|
||||
[NEW_BANK_ACCOUNT_FIELD]: {
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: 'my name',
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: '0000000000000',
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: 'account',
|
||||
},
|
||||
},
|
||||
{ emitEvent: true }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,62 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
|
||||
import { BankContent } from '@dsh/api-codegen/aggr-proxy';
|
||||
|
||||
import {
|
||||
NEW_BANK_ACCOUNT_BANK_BIK_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_NAME_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_FIELD,
|
||||
} from '../../consts';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-new-bank-account',
|
||||
templateUrl: './new-bank-account.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NewBankAccountComponent {
|
||||
@Input() form: FormGroup;
|
||||
|
||||
get bankAccountForm(): FormGroup {
|
||||
this.chekFormProvided();
|
||||
if (isNil(this.form.get(NEW_BANK_ACCOUNT_FIELD))) {
|
||||
throw new Error(`Form doesn't contains "${NEW_BANK_ACCOUNT_FIELD}" control`);
|
||||
}
|
||||
return this.form.get(NEW_BANK_ACCOUNT_FIELD) as FormGroup;
|
||||
}
|
||||
|
||||
get bankAccountNameControl(): FormControl {
|
||||
this.chekFormProvided();
|
||||
if (isNil(this.form.get(`${NEW_BANK_ACCOUNT_FIELD}.${NEW_BANK_ACCOUNT_BANK_NAME_FIELD}`))) {
|
||||
throw new Error(
|
||||
`Form doesn't contains "${NEW_BANK_ACCOUNT_FIELD}.${NEW_BANK_ACCOUNT_BANK_NAME_FIELD}" control`
|
||||
);
|
||||
}
|
||||
|
||||
return this.form.get(`${NEW_BANK_ACCOUNT_FIELD}.${NEW_BANK_ACCOUNT_BANK_NAME_FIELD}`) as FormControl;
|
||||
}
|
||||
|
||||
bankSelected(bank: BankContent): void {
|
||||
if (bank)
|
||||
this.form.patchValue(
|
||||
{
|
||||
[NEW_BANK_ACCOUNT_FIELD]: {
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: bank.value,
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: bank.bic,
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: bank.correspondentAccount,
|
||||
},
|
||||
},
|
||||
{
|
||||
emitEvent: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected chekFormProvided(): void {
|
||||
if (isNil(this.form)) {
|
||||
throw new Error(`Form wasn't provided`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { of } from 'rxjs';
|
||||
import { instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { AutocompleteVirtualScrollModule } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
import { DetailsItemModule } from '@dsh/components/layout';
|
||||
|
||||
import { ShopContractDetailsService } from '../../../../services/shop-contract-details/shop-contract-details.service';
|
||||
import { generateMockShop } from '../../../../tests/generate-mock-shop';
|
||||
import { ShopOptionsSelectionService } from '../../services/shop-options-selection/shop-options-selection.service';
|
||||
import { ShopContractComponent } from './shop-contract.component';
|
||||
|
||||
describe('ShopContractComponent', () => {
|
||||
let component: ShopContractComponent;
|
||||
let fixture: ComponentFixture<ShopContractComponent>;
|
||||
let mockShopOptionsSelectionService: ShopOptionsSelectionService;
|
||||
let mockShopContractDetailsService: ShopContractDetailsService;
|
||||
|
||||
const mockShop = generateMockShop(15);
|
||||
|
||||
beforeEach(async () => {
|
||||
mockShopOptionsSelectionService = mock(ShopOptionsSelectionService);
|
||||
mockShopContractDetailsService = mock(ShopContractDetailsService);
|
||||
|
||||
when(mockShopContractDetailsService.shopContract$).thenReturn(of());
|
||||
when(mockShopContractDetailsService.isLoading$).thenReturn(of());
|
||||
when(mockShopContractDetailsService.errorOccurred$).thenReturn(of());
|
||||
|
||||
when(mockShopOptionsSelectionService.control).thenReturn(new FormControl());
|
||||
when(mockShopOptionsSelectionService.options$).thenReturn(of([]));
|
||||
when(mockShopOptionsSelectionService.selectedShop$).thenReturn(of(mockShop));
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [getTranslocoModule(), NoopAnimationsModule, AutocompleteVirtualScrollModule, DetailsItemModule],
|
||||
declarations: [ShopContractComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ShopOptionsSelectionService,
|
||||
useFactory: () => instance(mockShopOptionsSelectionService),
|
||||
},
|
||||
{
|
||||
provide: ShopContractDetailsService,
|
||||
useFactory: () => instance(mockShopContractDetailsService),
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideComponent(ShopContractComponent, {
|
||||
set: {
|
||||
providers: [],
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
fixture = TestBed.createComponent(ShopContractComponent);
|
||||
component = fixture.componentInstance;
|
||||
const componentControl = new FormControl();
|
||||
component.control = componentControl;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('creation', () => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should request new contract on every selected shop change', () => {
|
||||
verify(mockShopContractDetailsService.requestContract(mockShop.contractID)).once();
|
||||
expect().nothing();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,48 +0,0 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { BaseOption } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll/types/base-option';
|
||||
|
||||
import { ShopContractDetailsService } from '../../../../services/shop-contract-details/shop-contract-details.service';
|
||||
import { ShopOptionsSelectionService } from '../../services/shop-options-selection/shop-options-selection.service';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-shop-contract',
|
||||
templateUrl: 'shop-contract.component.html',
|
||||
providers: [ShopOptionsSelectionService, ShopContractDetailsService],
|
||||
})
|
||||
export class ShopContractComponent implements OnInit {
|
||||
@Input() control: FormControl;
|
||||
@Input() contentWindow: HTMLElement;
|
||||
|
||||
shopsList$: Observable<BaseOption<string>[]> = this.shopOptionsService.options$;
|
||||
shopControl: FormControl = this.shopOptionsService.control;
|
||||
contract$ = this.contractService.shopContract$;
|
||||
isLoading$ = this.contractService.isLoading$;
|
||||
hasError$ = this.contractService.errorOccurred$;
|
||||
|
||||
constructor(
|
||||
private shopOptionsService: ShopOptionsSelectionService,
|
||||
private contractService: ShopContractDetailsService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initContractRequests();
|
||||
this.initContractUpdater();
|
||||
}
|
||||
|
||||
private initContractRequests(): void {
|
||||
this.shopOptionsService.selectedShop$.pipe(untilDestroyed(this)).subscribe((shop) => {
|
||||
this.contractService.requestContract(shop?.contractID);
|
||||
});
|
||||
}
|
||||
|
||||
private initContractUpdater(): void {
|
||||
this.contract$.pipe(untilDestroyed(this)).subscribe((contract) => {
|
||||
this.control.setValue(contract);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { of } from 'rxjs';
|
||||
import { instance, mock, when } from 'ts-mockito';
|
||||
|
||||
import { AutocompleteVirtualScrollModule } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
import { DetailsItemModule } from '@dsh/components/layout';
|
||||
|
||||
import { FetchShopsService } from '../../../../services/fetch-shops/fetch-shops.service';
|
||||
import { ShopContractDetailsService } from '../../../../services/shop-contract-details/shop-contract-details.service';
|
||||
import {
|
||||
BANK_ACCOUNT_TYPE_FIELD,
|
||||
BANK_SHOP_ID_FIELD,
|
||||
CONTRACT_FORM_FIELD,
|
||||
NEW_BANK_ACCOUNT_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_BIK_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_NAME_FIELD,
|
||||
NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_FIELD,
|
||||
NEW_BANK_ACCOUNT_SEARCH_FIELD,
|
||||
} from '../../consts';
|
||||
import { ShopOptionsSelectionService } from '../../services/shop-options-selection/shop-options-selection.service';
|
||||
import { BankAccountType } from '../../types/bank-account-type';
|
||||
import { ShopFormComponent } from './shop-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-contract',
|
||||
template: '<p>Mock Shop Contract</p>',
|
||||
})
|
||||
class MockShopContractComponent {}
|
||||
|
||||
describe('ShopFormComponent', () => {
|
||||
let component: ShopFormComponent;
|
||||
let fixture: ComponentFixture<ShopFormComponent>;
|
||||
let mockFetchShopsService: FetchShopsService;
|
||||
let mockShopOptionsSelectionService: ShopOptionsSelectionService;
|
||||
let mockShopContractDetailsService: ShopContractDetailsService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetchShopsService = mock(FetchShopsService);
|
||||
mockShopOptionsSelectionService = mock(ShopOptionsSelectionService);
|
||||
mockShopContractDetailsService = mock(ShopContractDetailsService);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
when(mockFetchShopsService.allShops$).thenReturn(of([]));
|
||||
when(mockShopOptionsSelectionService.control).thenReturn(new FormControl());
|
||||
when(mockShopOptionsSelectionService.options$).thenReturn(of([]));
|
||||
when(mockShopOptionsSelectionService.selectedShop$).thenReturn(of());
|
||||
when(mockShopContractDetailsService.shopContract$).thenReturn(of());
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
getTranslocoModule(),
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatDividerModule,
|
||||
NoopAnimationsModule,
|
||||
AutocompleteVirtualScrollModule,
|
||||
DetailsItemModule,
|
||||
MatRadioModule,
|
||||
],
|
||||
declarations: [ShopFormComponent, MockShopContractComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: FetchShopsService,
|
||||
useFactory: () => instance(mockFetchShopsService),
|
||||
},
|
||||
{
|
||||
provide: ShopOptionsSelectionService,
|
||||
useFactory: () => instance(mockShopOptionsSelectionService),
|
||||
},
|
||||
{
|
||||
provide: ShopContractDetailsService,
|
||||
useFactory: () => instance(mockShopContractDetailsService),
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShopFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.form = new FormGroup({
|
||||
url: new FormControl(''),
|
||||
name: new FormControl(''),
|
||||
[CONTRACT_FORM_FIELD]: new FormControl(''),
|
||||
[BANK_ACCOUNT_TYPE_FIELD]: new FormControl(''),
|
||||
[BANK_SHOP_ID_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_FIELD]: new FormGroup({
|
||||
[NEW_BANK_ACCOUNT_SEARCH_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_NAME_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_BIK_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD]: new FormControl(''),
|
||||
[NEW_BANK_ACCOUNT_ACCOUNT_FIELD]: new FormControl(''),
|
||||
}),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('contractControl', () => {
|
||||
it('should return contract control', () => {
|
||||
const contractControl = new FormControl('');
|
||||
|
||||
component.form = new FormGroup({
|
||||
[CONTRACT_FORM_FIELD]: contractControl,
|
||||
});
|
||||
|
||||
expect(component.contractControl).toEqual(contractControl);
|
||||
});
|
||||
|
||||
it(`should throw an error if form doesn't exist`, () => {
|
||||
component.form = undefined;
|
||||
expect(() => component.contractControl).toThrowError(`Form wasn't provided`);
|
||||
});
|
||||
|
||||
it(`should throw an error if control doesn't exist on form`, () => {
|
||||
component.form = new FormGroup({});
|
||||
expect(() => component.contractControl).toThrowError(
|
||||
`Form doesn't contains ${CONTRACT_FORM_FIELD} control`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('accountType checks', () => {
|
||||
it(`should throw an error if form doesn't exist`, () => {
|
||||
component.form = undefined;
|
||||
expect(() => component.isNewBankAccount).toThrowError(`Form wasn't provided`);
|
||||
expect(() => component.isExistingBankAccount).toThrowError(`Form wasn't provided`);
|
||||
});
|
||||
|
||||
it(`should throw an error if control doesn't exist on form`, () => {
|
||||
component.form = new FormGroup({});
|
||||
expect(() => component.isNewBankAccount).toThrowError(
|
||||
`Form doesn't contains ${BANK_ACCOUNT_TYPE_FIELD} control`
|
||||
);
|
||||
expect(() => component.isExistingBankAccount).toThrowError(
|
||||
`Form doesn't contains ${BANK_ACCOUNT_TYPE_FIELD} control`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNewBankAccount', () => {
|
||||
it('should return true if account type is new', () => {
|
||||
const bankAccountTypeControl = new FormControl(BankAccountType.New);
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_ACCOUNT_TYPE_FIELD]: bankAccountTypeControl,
|
||||
});
|
||||
|
||||
expect(component.isNewBankAccount).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if account type is existing', () => {
|
||||
const bankAccountTypeControl = new FormControl(BankAccountType.Existing);
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_ACCOUNT_TYPE_FIELD]: bankAccountTypeControl,
|
||||
});
|
||||
|
||||
expect(component.isNewBankAccount).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isExistingBankAccount', () => {
|
||||
it('should return true if account type is existing', () => {
|
||||
const bankAccountTypeControl = new FormControl(BankAccountType.Existing);
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_ACCOUNT_TYPE_FIELD]: bankAccountTypeControl,
|
||||
});
|
||||
|
||||
expect(component.isExistingBankAccount).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if account type is new', () => {
|
||||
const bankAccountTypeControl = new FormControl(BankAccountType.New);
|
||||
|
||||
component.form = new FormGroup({
|
||||
[BANK_ACCOUNT_TYPE_FIELD]: bankAccountTypeControl,
|
||||
});
|
||||
|
||||
expect(component.isExistingBankAccount).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
let accountTypeControl: FormControl;
|
||||
let newBankAccountControl: FormGroup;
|
||||
let shopIdControl: FormControl;
|
||||
|
||||
beforeEach(() => {
|
||||
accountTypeControl = component.form.get(BANK_ACCOUNT_TYPE_FIELD) as FormControl;
|
||||
newBankAccountControl = component.form.get(NEW_BANK_ACCOUNT_FIELD) as FormGroup;
|
||||
shopIdControl = component.form.get(BANK_SHOP_ID_FIELD) as FormControl;
|
||||
});
|
||||
|
||||
it('should enable newBankAccount and disable bankShopIdControl if account type is new', () => {
|
||||
accountTypeControl.setValue(BankAccountType.New);
|
||||
|
||||
expect(newBankAccountControl.disabled).toBe(false);
|
||||
expect(shopIdControl.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should disable newBankAccount and enable bankShopIdControl if account type is existing', () => {
|
||||
accountTypeControl.setValue(BankAccountType.Existing);
|
||||
|
||||
expect(newBankAccountControl.disabled).toBe(true);
|
||||
expect(shopIdControl.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should disable newBankAccount and disable bankShopIdControl by default', () => {
|
||||
accountTypeControl.setValue('');
|
||||
|
||||
expect(newBankAccountControl.disabled).toBe(true);
|
||||
expect(shopIdControl.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
// TODO: add typed form control lib to remove all of this constants
|
||||
export const CONTRACT_FORM_FIELD = 'contract';
|
||||
export const BANK_SHOP_ID_FIELD = 'bankShopID';
|
||||
export const BANK_ACCOUNT_TYPE_FIELD = 'bankAccountType';
|
||||
export const NEW_BANK_ACCOUNT_FIELD = 'newBankAccount';
|
||||
export const NEW_BANK_ACCOUNT_SEARCH_FIELD = 'search';
|
||||
export const NEW_BANK_ACCOUNT_BANK_NAME_FIELD = 'bankName';
|
||||
export const NEW_BANK_ACCOUNT_BANK_BIK_FIELD = 'bankBik';
|
||||
export const NEW_BANK_ACCOUNT_BANK_POST_ACCOUNT_FIELD = 'bankPostAccount';
|
||||
export const NEW_BANK_ACCOUNT_ACCOUNT_FIELD = 'account';
|
@ -1,112 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { of } from 'rxjs';
|
||||
import { instance, mock, when } from 'ts-mockito';
|
||||
|
||||
import { FetchShopsService } from '../../../../services/fetch-shops/fetch-shops.service';
|
||||
import { generateMockShopsList } from '../../../../tests/generate-mock-shops-list';
|
||||
import { ShopOptionsSelectionService } from './shop-options-selection.service';
|
||||
|
||||
describe('ShopSelectorService', () => {
|
||||
let service: ShopOptionsSelectionService;
|
||||
let mockFetchShopsService: FetchShopsService;
|
||||
|
||||
const mainConfig = {
|
||||
providers: [
|
||||
ShopOptionsSelectionService,
|
||||
{
|
||||
provide: FetchShopsService,
|
||||
useFactory: () => instance(mockFetchShopsService),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function configureTestingModule() {
|
||||
TestBed.configureTestingModule(mainConfig);
|
||||
service = TestBed.inject(ShopOptionsSelectionService);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetchShopsService = mock(FetchShopsService);
|
||||
|
||||
when(mockFetchShopsService.allShops$).thenReturn(of([]));
|
||||
});
|
||||
|
||||
describe('creation', () => {
|
||||
beforeEach(() => {
|
||||
configureTestingModule();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('options$', () => {
|
||||
it('should map Shop objects in BaseOption', () => {
|
||||
when(mockFetchShopsService.allShops$).thenReturn(of(generateMockShopsList(3)));
|
||||
|
||||
configureTestingModule();
|
||||
|
||||
const expected$ = cold('(a|)', {
|
||||
a: [
|
||||
{
|
||||
id: 'mock_shop_1',
|
||||
label: 'my name',
|
||||
},
|
||||
{
|
||||
id: 'mock_shop_2',
|
||||
label: 'my name',
|
||||
},
|
||||
{
|
||||
id: 'mock_shop_3',
|
||||
label: 'my name',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(service.options$).toBeObservable(expected$);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectedShop$', () => {
|
||||
let expected$;
|
||||
|
||||
afterEach(() => {
|
||||
expect(service.selectedShop$).toBeObservable(expected$);
|
||||
});
|
||||
|
||||
it('should update selected value with found shop item', () => {
|
||||
const mockList = generateMockShopsList(5);
|
||||
when(mockFetchShopsService.allShops$).thenReturn(of(mockList));
|
||||
|
||||
configureTestingModule();
|
||||
|
||||
service.control.setValue({
|
||||
id: 'mock_shop_2',
|
||||
label: 'my name',
|
||||
});
|
||||
|
||||
expected$ = cold('a', {
|
||||
a: mockList[1],
|
||||
});
|
||||
});
|
||||
|
||||
it('should set selected value with nullable value if shop id was not found in shops list', () => {
|
||||
const mockList = generateMockShopsList(5);
|
||||
when(mockFetchShopsService.allShops$).thenReturn(of(mockList));
|
||||
|
||||
configureTestingModule();
|
||||
|
||||
service.control.setValue({
|
||||
id: 'mock_shop_12',
|
||||
label: 'my name',
|
||||
});
|
||||
|
||||
expected$ = cold('a', {
|
||||
a: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
//
|
||||
});
|
@ -1,61 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
import { Observable, ReplaySubject } from 'rxjs';
|
||||
import { map, shareReplay, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { BaseOption } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll/types/base-option';
|
||||
import { SHARE_REPLAY_CONF } from '@dsh/operators';
|
||||
|
||||
import { FetchShopsService } from '../../../../services/fetch-shops/fetch-shops.service';
|
||||
|
||||
@UntilDestroy()
|
||||
@Injectable()
|
||||
export class ShopOptionsSelectionService {
|
||||
control = new FormControl();
|
||||
options$: Observable<BaseOption<string>[]>;
|
||||
selectedShop$: Observable<Shop | null>;
|
||||
|
||||
private innerSelectedShop$ = new ReplaySubject<Shop>(1);
|
||||
|
||||
constructor(private shopsService: FetchShopsService) {
|
||||
this.initShopOptions();
|
||||
this.initSelectedShop();
|
||||
}
|
||||
|
||||
private initShopOptions(): void {
|
||||
this.options$ = this.shopsService.allShops$.pipe(
|
||||
map((shopsList: Shop[]) => {
|
||||
return shopsList.map((shop: Shop) => {
|
||||
return {
|
||||
id: shop.id,
|
||||
label: shop.details.name,
|
||||
};
|
||||
});
|
||||
}),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
);
|
||||
}
|
||||
|
||||
private initSelectedShop(): void {
|
||||
this.control.valueChanges
|
||||
.pipe(
|
||||
withLatestFrom(this.shopsService.allShops$),
|
||||
map(([selected, shopsList]: [BaseOption<string> | null, Shop[]]) => {
|
||||
if (isNil(selected)) {
|
||||
return null;
|
||||
}
|
||||
return shopsList.find((shop: Shop) => shop.id === selected.id);
|
||||
}),
|
||||
map((shop: Shop | undefined | null) => (isNil(shop) ? null : shop)),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe((selectedShop: Shop | null) => {
|
||||
this.innerSelectedShop$.next(selectedShop);
|
||||
});
|
||||
|
||||
this.selectedShop$ = this.innerSelectedShop$.asObservable();
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export type CreateShopDialogResponse = 'send' | 'canceled';
|
@ -1 +0,0 @@
|
||||
export * from './shop-creation.module';
|
@ -1,109 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { deepEqual, instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { PaymentInstitutionRealm } from '@dsh/api/model/payment-institution-realm';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
|
||||
import { CreateShopDialogComponent } from './components/create-shop-dialog/create-shop-dialog.component';
|
||||
import { CreateShopDialogResponse } from './create-russian-shop-entity/types/create-shop-dialog-response';
|
||||
import { ShopCreationService } from './shop-creation.service';
|
||||
|
||||
describe('CreateShopService', () => {
|
||||
let service: ShopCreationService;
|
||||
let mockMatDialog: MatDialog;
|
||||
let mockMatDialogRef: MatDialogRef<CreateShopDialogComponent>;
|
||||
let mockMatSnackBar: MatSnackBar;
|
||||
let transloco: TranslocoService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockMatDialog = mock(MatDialog);
|
||||
mockMatDialogRef = mock<MatDialogRef<CreateShopDialogComponent>>(MatDialogRef);
|
||||
mockMatSnackBar = mock(MatSnackBar);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [getTranslocoModule()],
|
||||
providers: [
|
||||
ShopCreationService,
|
||||
{
|
||||
provide: MatDialog,
|
||||
useFactory: () => instance(mockMatDialog),
|
||||
},
|
||||
{
|
||||
provide: MatSnackBar,
|
||||
useFactory: () => instance(mockMatSnackBar),
|
||||
},
|
||||
],
|
||||
});
|
||||
service = TestBed.inject(ShopCreationService);
|
||||
transloco = TestBed.inject(TranslocoService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('createShop', () => {
|
||||
beforeEach(() => {
|
||||
when(
|
||||
mockMatDialog.open(
|
||||
CreateShopDialogComponent,
|
||||
deepEqual({
|
||||
data: {
|
||||
realm: PaymentInstitutionRealm.Test,
|
||||
},
|
||||
})
|
||||
)
|
||||
).thenReturn(instance(mockMatDialogRef));
|
||||
when(mockMatDialogRef.afterClosed()).thenReturn(of('canceled') as Observable<CreateShopDialogResponse>);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect().nothing();
|
||||
});
|
||||
|
||||
it('should open dialog CreateShopDialogComponent with config', () => {
|
||||
service.createShop({ realm: PaymentInstitutionRealm.Test });
|
||||
|
||||
verify(
|
||||
mockMatDialog.open(
|
||||
CreateShopDialogComponent,
|
||||
deepEqual({
|
||||
data: {
|
||||
realm: PaymentInstitutionRealm.Test,
|
||||
},
|
||||
})
|
||||
)
|
||||
).once();
|
||||
});
|
||||
|
||||
it('should open snackbar on send response from dialog', () => {
|
||||
when(mockMatDialogRef.afterClosed()).thenReturn(of('send') as Observable<CreateShopDialogResponse>);
|
||||
when(
|
||||
mockMatSnackBar.open(transloco.translate('russianLegalEntity.created', null, 'create-shop'), 'OK')
|
||||
).thenReturn(null);
|
||||
|
||||
service.createShop({ realm: PaymentInstitutionRealm.Test });
|
||||
|
||||
verify(
|
||||
mockMatSnackBar.open(transloco.translate('russianLegalEntity.created', null, 'create-shop'), 'OK')
|
||||
).once();
|
||||
});
|
||||
|
||||
it('should do not open snackbar on canceled response from dialog', () => {
|
||||
when(
|
||||
mockMatSnackBar.open(transloco.translate('russianLegalEntity.created', null, 'create-shop'), 'OK')
|
||||
).thenReturn(null);
|
||||
|
||||
service.createShop({ realm: PaymentInstitutionRealm.Test });
|
||||
verify(
|
||||
mockMatSnackBar.open(transloco.translate('russianLegalEntity.created', null, 'create-shop'), 'OK')
|
||||
).never();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,27 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import { filter, take } from 'rxjs/operators';
|
||||
|
||||
import { CreateShopDialogComponent } from './components/create-shop-dialog/create-shop-dialog.component';
|
||||
import { CreateShopDialogResponse } from './create-russian-shop-entity/types/create-shop-dialog-response';
|
||||
import { CreateShopDialogConfig } from './types/create-shop-dialog-config';
|
||||
|
||||
@Injectable()
|
||||
export class ShopCreationService {
|
||||
constructor(private dialog: MatDialog, private transloco: TranslocoService, private snackBar: MatSnackBar) {}
|
||||
|
||||
createShop(config: CreateShopDialogConfig): void {
|
||||
this.dialog
|
||||
.open<CreateShopDialogComponent, CreateShopDialogConfig>(CreateShopDialogComponent, { data: config })
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
filter((response: CreateShopDialogResponse) => response === 'send')
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.snackBar.open(this.transloco.translate('russianLegalEntity.created', null, 'create-shop'), 'OK');
|
||||
});
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export interface CreateShopDialogConfig {
|
||||
realm: string;
|
||||
}
|
@ -3,9 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { ShopContractDetailsService } from '@dsh/app/shared/services/shop-contract-details';
|
||||
import { getTranslocoModule } from '@dsh/app/shared/tests/get-transloco-module';
|
||||
|
||||
import { ShopContractDetailsService } from '../../../../services/shop-contract-details/shop-contract-details.service';
|
||||
import { ShopContractDetailsComponent } from './shop-contract-details.component';
|
||||
|
||||
describe('ShopContractDetailsComponent', () => {
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
import { ShopContractDetailsService } from '../../../../services/shop-contract-details/shop-contract-details.service';
|
||||
import { ShopContractDetailsService } from '@dsh/app/shared/services/shop-contract-details';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-contract-details',
|
||||
templateUrl: 'shop-contract-details.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [ShopContractDetailsService],
|
||||
})
|
||||
export class ShopContractDetailsComponent {
|
||||
@Input()
|
||||
|
@ -11,10 +11,10 @@ import { instance, mock, when } from 'ts-mockito';
|
||||
import { CategoriesModule } from '@dsh/api/categories';
|
||||
import { ContractsModule } from '@dsh/api/contracts';
|
||||
import { ContractDetailsModule, PayoutToolModule } from '@dsh/app/shared/components';
|
||||
import { ShopContractDetailsService } from '@dsh/app/shared/services/shop-contract-details';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { DetailsItemModule } from '@dsh/components/layout';
|
||||
|
||||
import { ShopContractDetailsService } from '../../services/shop-contract-details/shop-contract-details.service';
|
||||
import { ShopPayoutToolDetailsService } from '../../services/shop-payout-tool-details/shop-payout-tool-details.service';
|
||||
import { generateMockShopItem } from '../../tests/generate-shop-item';
|
||||
import { ShopBalanceModule } from '../shop-balance';
|
||||
|
@ -10,6 +10,7 @@ import { TranslocoModule } from '@ngneat/transloco';
|
||||
import { CategoriesModule } from '@dsh/api/categories';
|
||||
import { ContractsModule } from '@dsh/api/contracts';
|
||||
import { ContractDetailsModule, PayoutToolModule } from '@dsh/app/shared/components';
|
||||
import { ShopContractDetailsModule } from '@dsh/app/shared/services/shop-contract-details';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
import { DetailsItemModule } from '@dsh/components/layout';
|
||||
|
||||
@ -38,6 +39,7 @@ import { ShopDetailsComponent } from './shop-details.component';
|
||||
ContractsModule,
|
||||
MatSnackBarModule,
|
||||
MatDialogModule,
|
||||
ShopContractDetailsModule,
|
||||
],
|
||||
declarations: [
|
||||
ShopDetailsComponent,
|
||||
|
@ -0,0 +1,4 @@
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
@ -2,25 +2,30 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { TranslocoTestingModule } from '@ngneat/transloco';
|
||||
import { of } from 'rxjs';
|
||||
import { deepEqual, instance, mock, verify, when } from 'ts-mockito';
|
||||
import { anything, instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { PaymentInstitutionRealm } from '@dsh/api/model';
|
||||
import { PaymentInstitution } from '@dsh/api-codegen/capi';
|
||||
import { ShopCreationService } from '@dsh/app/shared/components/shop-creation';
|
||||
import { provideMockService } from '@dsh/app/shared/tests';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
|
||||
import { PaymentInstitutionRealmService } from '../../services/payment-institution-realm/payment-institution-realm.service';
|
||||
import { RealmShopsService } from '../../services/realm-shops/realm-shops.service';
|
||||
import { FetchShopsService } from './services/fetch-shops/fetch-shops.service';
|
||||
import { ShopsBalanceService } from './services/shops-balance/shops-balance.service';
|
||||
import { ShopsFiltersStoreService } from './services/shops-filters-store/shops-filters-store.service';
|
||||
import { ShopsFiltersService } from './services/shops-filters/shops-filters.service';
|
||||
import { ShopCreationService } from './shop-creation/shop-creation.service';
|
||||
import { ShopFiltersModule } from './shop-filters';
|
||||
import { ShopsExpandedIdManagerService } from './shops-list/services/shops-expanded-id-manager/shops-expanded-id-manager.service';
|
||||
import { ShopListModule } from './shops-list/shop-list.module';
|
||||
import { ShopsComponent } from './shops.component';
|
||||
|
||||
import RealmEnum = PaymentInstitution.RealmEnum;
|
||||
|
||||
describe('ShopsComponent', () => {
|
||||
let component: ShopsComponent;
|
||||
let fixture: ComponentFixture<ShopsComponent>;
|
||||
@ -29,8 +34,9 @@ describe('ShopsComponent', () => {
|
||||
let mockShopsBalanceService: ShopsBalanceService;
|
||||
let mockShopsFiltersService: ShopsFiltersService;
|
||||
let mockShopsFiltersStoreService: ShopsFiltersStoreService;
|
||||
let mockActivatedRoute: ActivatedRoute;
|
||||
let mockShopCreationService: ShopCreationService;
|
||||
let mockRealmShopsService: RealmShopsService;
|
||||
let mockRealmService: PaymentInstitutionRealmService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetchShopsService = mock(FetchShopsService);
|
||||
@ -38,8 +44,9 @@ describe('ShopsComponent', () => {
|
||||
mockShopsBalanceService = mock(ShopsBalanceService);
|
||||
mockShopsFiltersService = mock(ShopsFiltersService);
|
||||
mockShopsFiltersStoreService = mock(ShopsFiltersStoreService);
|
||||
mockActivatedRoute = mock(ActivatedRoute);
|
||||
mockShopCreationService = mock(ShopCreationService);
|
||||
mockRealmShopsService = mock(RealmShopsService);
|
||||
mockRealmService = mock(PaymentInstitutionRealmService);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -47,6 +54,7 @@ describe('ShopsComponent', () => {
|
||||
when(mockFetchShopsService.shownShops$).thenReturn(of([]));
|
||||
when(mockFetchShopsService.lastUpdated$).thenReturn(of(''));
|
||||
when(mockFetchShopsService.hasMore$).thenReturn(of(false));
|
||||
when(mockRealmService.realm$).thenReturn(of(RealmEnum.Test));
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
@ -82,10 +90,6 @@ describe('ShopsComponent', () => {
|
||||
provide: ShopCreationService,
|
||||
useFactory: () => instance(mockShopCreationService),
|
||||
},
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useFactory: () => instance(mockActivatedRoute),
|
||||
},
|
||||
{
|
||||
provide: ShopsBalanceService,
|
||||
useFactory: () => instance(mockShopsBalanceService),
|
||||
@ -98,6 +102,8 @@ describe('ShopsComponent', () => {
|
||||
provide: ShopsFiltersStoreService,
|
||||
useFactory: () => instance(mockShopsFiltersStoreService),
|
||||
},
|
||||
provideMockService(RealmShopsService, mockRealmShopsService),
|
||||
provideMockService(PaymentInstitutionRealmService, mockRealmService),
|
||||
],
|
||||
})
|
||||
.overrideComponent(ShopsComponent, {
|
||||
@ -114,7 +120,6 @@ describe('ShopsComponent', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
when(mockActivatedRoute.params).thenReturn(of({ realm: PaymentInstitutionRealm.Test }));
|
||||
when(mockShopsExpandedIdManagerService.expandedId$).thenReturn(of(-1));
|
||||
when(mockShopsFiltersStoreService.data$).thenReturn(of({}));
|
||||
});
|
||||
@ -127,12 +132,12 @@ describe('ShopsComponent', () => {
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should use realm data to init FetchShopsService', () => {
|
||||
when(mockActivatedRoute.params).thenReturn(of({ realm: PaymentInstitutionRealm.Live }));
|
||||
when(mockFetchShopsService.initRealm(PaymentInstitutionRealm.Live)).thenReturn(null);
|
||||
when(mockRealmService.realm$).thenReturn(of(RealmEnum.Live));
|
||||
when(mockFetchShopsService.initRealm(RealmEnum.Live)).thenReturn(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
verify(mockFetchShopsService.initRealm(PaymentInstitutionRealm.Live)).once();
|
||||
verify(mockFetchShopsService.initRealm(RealmEnum.Live)).once();
|
||||
expect().nothing();
|
||||
});
|
||||
|
||||
@ -173,14 +178,9 @@ describe('ShopsComponent', () => {
|
||||
|
||||
describe('createShop', () => {
|
||||
it('should call create shop with activated route realm', () => {
|
||||
when(mockActivatedRoute.snapshot).thenReturn({ params: { realm: PaymentInstitutionRealm.Live } } as any);
|
||||
when(mockShopCreationService.createShop(deepEqual({ realm: PaymentInstitutionRealm.Live }))).thenReturn(
|
||||
null
|
||||
);
|
||||
|
||||
component.createShop();
|
||||
|
||||
verify(mockShopCreationService.createShop(deepEqual({ realm: PaymentInstitutionRealm.Live }))).once();
|
||||
verify(mockShopCreationService.createShop(anything())).once();
|
||||
expect().nothing();
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { pluck, take } from 'rxjs/operators';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { PaymentInstitutionRealm } from '@dsh/api/model';
|
||||
import { ShopCreationService } from '@dsh/app/shared/components/shop-creation';
|
||||
|
||||
import { PaymentInstitutionRealmService } from '../../services/payment-institution-realm/payment-institution-realm.service';
|
||||
import { RealmShopsService } from '../../services/realm-shops/realm-shops.service';
|
||||
import { FetchShopsService } from './services/fetch-shops/fetch-shops.service';
|
||||
import { ShopCreationService } from './shop-creation/shop-creation.service';
|
||||
import { ShopsExpandedIdManagerService } from './shops-list/services/shops-expanded-id-manager/shops-expanded-id-manager.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shops',
|
||||
templateUrl: 'shops.component.html',
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
],
|
||||
styleUrls: ['shops.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopsComponent implements OnInit {
|
||||
@ -31,11 +24,12 @@ export class ShopsComponent implements OnInit {
|
||||
private shopsService: FetchShopsService,
|
||||
private expandedIdManager: ShopsExpandedIdManagerService,
|
||||
private createShopService: ShopCreationService,
|
||||
private route: ActivatedRoute
|
||||
private realmShopsService: RealmShopsService,
|
||||
private realmService: PaymentInstitutionRealmService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params.pipe(take(1), pluck('realm')).subscribe((realm: PaymentInstitutionRealm) => {
|
||||
this.realmService.realm$.pipe(take(1)).subscribe((realm) => {
|
||||
this.shopsService.initRealm(realm);
|
||||
});
|
||||
this.expandedIdManager.expandedId$.pipe(take(1)).subscribe((offsetIndex: number) => {
|
||||
@ -44,9 +38,7 @@ export class ShopsComponent implements OnInit {
|
||||
}
|
||||
|
||||
createShop(): void {
|
||||
this.createShopService.createShop({
|
||||
realm: this.route.snapshot.params.realm,
|
||||
});
|
||||
this.createShopService.createShop({ shops$: this.realmShopsService.shops$ });
|
||||
}
|
||||
|
||||
refreshData(): void {
|
||||
|
@ -4,13 +4,13 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { ShopCreationModule } from '@dsh/app/shared/components/shop-creation';
|
||||
import { ButtonModule } from '@dsh/components/buttons';
|
||||
|
||||
import { FetchShopsService } from './services/fetch-shops/fetch-shops.service';
|
||||
import { ShopsBalanceService } from './services/shops-balance/shops-balance.service';
|
||||
import { ShopsFiltersStoreService } from './services/shops-filters-store/shops-filters-store.service';
|
||||
import { ShopsFiltersService } from './services/shops-filters/shops-filters.service';
|
||||
import { ShopCreationModule } from './shop-creation';
|
||||
import { ShopFiltersModule } from './shop-filters';
|
||||
import { ShopsExpandedIdManagerService } from './shops-list/services/shops-expanded-id-manager/shops-expanded-id-manager.service';
|
||||
import { ShopListModule } from './shops-list/shop-list.module';
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
import { PaymentInstitution } from '@dsh/api-codegen/capi';
|
||||
import { SHOPS } from '@dsh/app/shared/components/inputs/shop-field';
|
||||
|
||||
import { PaymentInstitutionRealmService } from './services/payment-institution-realm/payment-institution-realm.service';
|
||||
import { RealmShopsService } from './services/realm-shops/realm-shops.service';
|
||||
@ -12,23 +13,34 @@ import { RealmShopsService } from './services/realm-shops/realm-shops.service';
|
||||
@Component({
|
||||
templateUrl: 'payment-section.component.html',
|
||||
styleUrls: ['payment-section.scss'],
|
||||
providers: [PaymentInstitutionRealmService, RealmShopsService],
|
||||
providers: [
|
||||
PaymentInstitutionRealmService,
|
||||
RealmShopsService,
|
||||
{
|
||||
provide: SHOPS,
|
||||
deps: [RealmShopsService],
|
||||
useFactory: (realmShopsService: RealmShopsService) => realmShopsService.shops$,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class PaymentSectionComponent {
|
||||
export class PaymentSectionComponent implements OnInit {
|
||||
isTestRealm$ = this.realmService.realm$.pipe(map((realm) => realm === PaymentInstitution.RealmEnum.Test));
|
||||
|
||||
constructor(
|
||||
private realmService: PaymentInstitutionRealmService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.realmService.realm$
|
||||
.pipe(
|
||||
filter((realm) => !realm),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe(
|
||||
() => void this.router.navigate(['realm', PaymentInstitution.RealmEnum.Live], { relativeTo: route })
|
||||
() =>
|
||||
void this.router.navigate(['realm', PaymentInstitution.RealmEnum.Live], { relativeTo: this.route })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,16 @@ import { map } from 'rxjs/operators';
|
||||
|
||||
import { ApiShopsService } from '@dsh/api';
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { publishReplayRefCount } from '@dsh/operators';
|
||||
import { shareReplayRefCount } from '@dsh/operators';
|
||||
|
||||
import { getShopsByRealm } from '../../operations/operators';
|
||||
import { PaymentInstitutionRealmService } from '../payment-institution-realm/payment-institution-realm.service';
|
||||
|
||||
@Injectable()
|
||||
export class RealmShopsService {
|
||||
shops$: Observable<Shop[]> = combineLatest(this.realmService.realm$, this.shopsService.shops$).pipe(
|
||||
map(([realm, shops]) => getShopsByRealm(shops, realm)),
|
||||
publishReplayRefCount()
|
||||
shops$: Observable<Shop[]> = combineLatest(this.shopsService.shops$, this.realmService.realm$).pipe(
|
||||
map(([shops, realm]) => getShopsByRealm(shops, realm)),
|
||||
shareReplayRefCount()
|
||||
);
|
||||
|
||||
constructor(private shopsService: ApiShopsService, private realmService: PaymentInstitutionRealmService) {}
|
||||
|
@ -1,3 +1,2 @@
|
||||
export * from './filters';
|
||||
export * from './api-model-details';
|
||||
export * from './selects';
|
||||
|
@ -1,6 +1,6 @@
|
||||
<dsh-autocomplete-field
|
||||
<dsh-select-search-field
|
||||
[label]="label"
|
||||
[required]="required"
|
||||
[options]="options$ | async"
|
||||
[formControl]="formControl"
|
||||
></dsh-autocomplete-field>
|
||||
></dsh-select-search-field>
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component, Injector, Input } from '@angular/core';
|
||||
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, share } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { CategoriesService } from '@dsh/api';
|
||||
import { Category } from '@dsh/api-codegen/capi';
|
||||
import { Option } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
import { shareReplayRefCount } from '@dsh/operators';
|
||||
import { coerceBoolean } from '@dsh/utils';
|
||||
|
||||
@Component({
|
||||
@ -22,7 +23,7 @@ export class CategoryAutocompleteFieldComponent extends WrappedFormControlSuperc
|
||||
map((categories) =>
|
||||
categories.map((category) => ({ label: `${category.categoryID} - ${category.name}`, value: category }))
|
||||
),
|
||||
share()
|
||||
shareReplayRefCount()
|
||||
);
|
||||
|
||||
constructor(injector: Injector, private categoriesService: CategoriesService) {
|
||||
|
@ -4,12 +4,20 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { AutocompleteFieldModule } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { CategoriesModule } from '@dsh/api';
|
||||
import { SelectSearchFieldModule } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { CategoryAutocompleteFieldComponent } from './category-autocomplete-field.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MatInputModule, MatFormFieldModule, ReactiveFormsModule, AutocompleteFieldModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
SelectSearchFieldModule,
|
||||
CategoriesModule,
|
||||
],
|
||||
declarations: [CategoryAutocompleteFieldComponent],
|
||||
exports: [CategoryAutocompleteFieldComponent],
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
<dsh-autocomplete-field
|
||||
<dsh-select-search-field
|
||||
*ngIf="countries$ | async"
|
||||
[label]="label"
|
||||
[required]="required"
|
||||
[options]="options$ | async"
|
||||
[formControl]="formControl"
|
||||
></dsh-autocomplete-field>
|
||||
></dsh-select-search-field>
|
||||
|
@ -5,7 +5,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { CountriesModule } from '@dsh/api';
|
||||
import { AutocompleteFieldModule } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { SelectSearchFieldModule } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { CountryAutocompleteFieldComponent } from './countries-autocomplete-field.component';
|
||||
|
||||
@ -15,7 +15,7 @@ import { CountryAutocompleteFieldComponent } from './countries-autocomplete-fiel
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
AutocompleteFieldModule,
|
||||
SelectSearchFieldModule,
|
||||
CountriesModule,
|
||||
],
|
||||
declarations: [CountryAutocompleteFieldComponent],
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Country } from '@dsh/api-codegen/capi';
|
||||
import { Option } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
const countryToOption = (country: Country): Option<string> => ({
|
||||
label: `${country?.id} - ${country?.name}`,
|
||||
|
@ -1,6 +1,6 @@
|
||||
<dsh-autocomplete-field
|
||||
<dsh-select-search-field
|
||||
[label]="label"
|
||||
[required]="required"
|
||||
[options]="options$ | async"
|
||||
[formControl]="formControl"
|
||||
></dsh-autocomplete-field>
|
||||
></dsh-select-search-field>
|
||||
|
@ -5,7 +5,7 @@ import { map, share } from 'rxjs/operators';
|
||||
|
||||
import { PaymentInstitution } from '@dsh/api-codegen/capi';
|
||||
import { PaymentInstitutionsService } from '@dsh/api/payment-institutions';
|
||||
import { Option } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
import { coerceBoolean } from '@dsh/utils';
|
||||
|
||||
@Component({
|
||||
|
@ -4,12 +4,12 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { AutocompleteFieldModule } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { SelectSearchFieldModule } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { PaymentInstitutionAutocompleteFieldComponent } from './payment-institution-autocomplete-field.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MatInputModule, MatFormFieldModule, ReactiveFormsModule, AutocompleteFieldModule],
|
||||
imports: [CommonModule, MatInputModule, MatFormFieldModule, ReactiveFormsModule, SelectSearchFieldModule],
|
||||
declarations: [PaymentInstitutionAutocompleteFieldComponent],
|
||||
exports: [PaymentInstitutionAutocompleteFieldComponent],
|
||||
})
|
||||
|
@ -1,2 +0,0 @@
|
||||
export * from './shop-autocomplete-field.module';
|
||||
export * from './shop-autocomplete-field.component';
|
@ -1,27 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Injector, Input } from '@angular/core';
|
||||
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ApiShopsService } from '@dsh/api';
|
||||
import { coerceBoolean } from '@dsh/utils';
|
||||
|
||||
import { ShopId } from './types';
|
||||
import { shopsToOptions } from './utils';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-autocomplete-field',
|
||||
templateUrl: 'shop-autocomplete-field.component.html',
|
||||
providers: [provideValueAccessor(ShopAutocompleteFieldComponent)],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopAutocompleteFieldComponent extends WrappedFormControlSuperclass<ShopId> {
|
||||
@Input() label: string;
|
||||
@Input() @coerceBoolean required = false;
|
||||
|
||||
shops$ = this.apiShopsService.shops$;
|
||||
options$ = this.shops$.pipe(map(shopsToOptions));
|
||||
|
||||
constructor(injector: Injector, private apiShopsService: ApiShopsService) {
|
||||
super(injector);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
|
||||
export type ShopId = Shop['id'];
|
||||
export type ShopName = Shop['details']['name'];
|
||||
export type DisplayWithFn = (value: ShopId) => string;
|
@ -1 +0,0 @@
|
||||
export * from './shops-to-options';
|
@ -1,11 +0,0 @@
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { Option } from '@dsh/components/form-controls/autocomplete-field';
|
||||
|
||||
import { ShopId } from '../types';
|
||||
|
||||
const shopToOption = (shop: Shop): Option<ShopId> => ({
|
||||
label: shop?.details?.name,
|
||||
value: shop?.id,
|
||||
});
|
||||
|
||||
export const shopsToOptions = (shops: Shop[]): Option<ShopId>[] => shops.map(shopToOption);
|
3
src/app/shared/components/inputs/shop-field/index.ts
Normal file
3
src/app/shared/components/inputs/shop-field/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './shop-field.module';
|
||||
export * from './shop-field.component';
|
||||
export * from './shops-token';
|
@ -1,7 +1,6 @@
|
||||
<dsh-autocomplete-field
|
||||
*ngIf="shops$ | async"
|
||||
<dsh-select-search-field
|
||||
[label]="label"
|
||||
[required]="required"
|
||||
[options]="options$ | async"
|
||||
[formControl]="formControl"
|
||||
></dsh-autocomplete-field>
|
||||
></dsh-select-search-field>
|
@ -0,0 +1,41 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Injector, Input, Optional } from '@angular/core';
|
||||
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { defer, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ApiShopsService, toLiveShops } from '@dsh/api';
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { shopToOption } from '@dsh/app/shared/components/inputs/shop-field/utils/shops-to-options';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
import { shareReplayRefCount } from '@dsh/operators';
|
||||
import { coerceBoolean } from '@dsh/utils';
|
||||
|
||||
import { SHOPS } from './shops-token';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shop-field',
|
||||
templateUrl: 'shop-field.component.html',
|
||||
providers: [provideValueAccessor(ShopFieldComponent)],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopFieldComponent extends WrappedFormControlSuperclass<Shop> {
|
||||
@Input() label: string;
|
||||
@Input() @coerceBoolean required = false;
|
||||
|
||||
options$: Observable<Option<Shop>[]> = defer(
|
||||
() => this.shops$ || this.shopsService.shops$.pipe(map(toLiveShops))
|
||||
).pipe(
|
||||
map((shops) => shops.map(shopToOption)),
|
||||
shareReplayRefCount()
|
||||
);
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private shopsService: ApiShopsService,
|
||||
@Inject(SHOPS)
|
||||
@Optional()
|
||||
private shops$?: Observable<Shop[]>
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
}
|
@ -5,9 +5,9 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { ShopModule } from '@dsh/api';
|
||||
import { AutocompleteFieldModule } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { SelectSearchFieldModule } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { ShopAutocompleteFieldComponent } from './shop-autocomplete-field.component';
|
||||
import { ShopFieldComponent } from './shop-field.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -15,10 +15,10 @@ import { ShopAutocompleteFieldComponent } from './shop-autocomplete-field.compon
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
ReactiveFormsModule,
|
||||
AutocompleteFieldModule,
|
||||
SelectSearchFieldModule,
|
||||
ShopModule,
|
||||
],
|
||||
declarations: [ShopAutocompleteFieldComponent],
|
||||
exports: [ShopAutocompleteFieldComponent],
|
||||
declarations: [ShopFieldComponent],
|
||||
exports: [ShopFieldComponent],
|
||||
})
|
||||
export class ShopAutocompleteFieldModule {}
|
||||
export class ShopFieldModule {}
|
@ -0,0 +1,6 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
|
||||
export const SHOPS = new InjectionToken<Observable<Shop[]>>('Shops');
|
@ -0,0 +1,7 @@
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
export const shopToOption = (shop: Shop): Option<Shop> => ({
|
||||
label: shop?.details?.name,
|
||||
value: shop,
|
||||
});
|
@ -2,10 +2,10 @@ import { Component, Injector, Input, OnChanges } from '@angular/core';
|
||||
import { ComponentChanges } from '@rbkmoney/utils';
|
||||
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { defer, ReplaySubject } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { SHARE_REPLAY_CONF } from '@dsh/operators';
|
||||
import { shareReplayRefCount } from '@dsh/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-shops-field',
|
||||
@ -17,7 +17,7 @@ export class ShopsFieldComponent extends WrappedFormControlSuperclass<Shop['id']
|
||||
|
||||
options$ = defer(() => this.shops$).pipe(
|
||||
map((shops) => shops.map((shop) => ({ value: shop.id, label: shop.details.name }))),
|
||||
shareReplay(SHARE_REPLAY_CONF)
|
||||
shareReplayRefCount()
|
||||
);
|
||||
|
||||
private shops$ = new ReplaySubject<Shop[]>();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Wallet } from '@dsh/api-codegen/wallet-api';
|
||||
import { Option } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { Option } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
const walletToOption = (wallet: Wallet): Option<string> => ({
|
||||
label: `${wallet?.id} - ${wallet?.name}`,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<dsh-autocomplete-field
|
||||
<dsh-select-search-field
|
||||
*ngIf="wallets$ | async"
|
||||
[label]="label"
|
||||
[required]="required"
|
||||
[options]="options$ | async"
|
||||
[formControl]="formControl"
|
||||
></dsh-autocomplete-field>
|
||||
></dsh-select-search-field>
|
||||
|
@ -4,12 +4,12 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { AutocompleteFieldModule } from '@dsh/components/form-controls/autocomplete-field';
|
||||
import { SelectSearchFieldModule } from '@dsh/components/form-controls/select-search-field';
|
||||
|
||||
import { WalletAutocompleteFieldComponent } from './wallet-autocomplete-field.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MatInputModule, MatFormFieldModule, ReactiveFormsModule, AutocompleteFieldModule],
|
||||
imports: [CommonModule, MatInputModule, MatFormFieldModule, ReactiveFormsModule, SelectSearchFieldModule],
|
||||
declarations: [WalletAutocompleteFieldComponent],
|
||||
exports: [WalletAutocompleteFieldComponent],
|
||||
})
|
||||
|
@ -1,32 +0,0 @@
|
||||
<mat-form-field>
|
||||
<mat-label *ngIf="label">{{ label }}</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
[formControl]="searchControl"
|
||||
[placeholder]="placeholder"
|
||||
[matAutocomplete]="autocomplete"
|
||||
[required]="required"
|
||||
/>
|
||||
<button mat-button *ngIf="searchControl.value" matSuffix mat-icon-button aria-label="Clear" (click)="clearValue()">
|
||||
<mat-icon svgIcon="cross"></mat-icon>
|
||||
</button>
|
||||
<mat-autocomplete #autocomplete="matAutocomplete" (opened)="panelOpened()">
|
||||
<cdk-virtual-scroll-viewport
|
||||
[itemSize]="itemSize"
|
||||
[style.height.px]="listSize"
|
||||
[minBufferPx]="itemSize * (listMultiplier + 1)"
|
||||
[maxBufferPx]="itemSize * (listMultiplier * 2 + 1)"
|
||||
>
|
||||
<mat-option
|
||||
*cdkVirtualFor="let option of filteredOptions"
|
||||
[value]="option.label"
|
||||
(onSelectionChange)="selectionChanged(option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</mat-option>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</mat-autocomplete>
|
||||
<mat-hint *ngIf="hint">{{ hint }}</mat-hint>
|
||||
<mat-error *ngIf="control.invalid">{{ getErrorMessage() }}</mat-error>
|
||||
</mat-form-field>
|
@ -1,568 +0,0 @@
|
||||
import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatAutocomplete, MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { instance, mock, verify, when } from 'ts-mockito';
|
||||
|
||||
import { VIRTUAL_SCROLL_ITEM_SIZE, VIRTUAL_SCROLL_LIST_MULTIPLIER } from '@dsh/app/shared/components';
|
||||
|
||||
import { AutocompleteVirtualScrollComponent } from './autocomplete-virtual-scroll.component';
|
||||
|
||||
async function makeTestingModule(config?: TestModuleMetadata) {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
ScrollingModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
],
|
||||
declarations: [AutocompleteVirtualScrollComponent],
|
||||
...config,
|
||||
})
|
||||
.overrideComponent(AutocompleteVirtualScrollComponent, {
|
||||
set: {
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
},
|
||||
})
|
||||
.compileComponents();
|
||||
}
|
||||
|
||||
describe('AutocompleteVirtualScrollComponent', () => {
|
||||
let component: AutocompleteVirtualScrollComponent;
|
||||
let fixture: ComponentFixture<AutocompleteVirtualScrollComponent>;
|
||||
let mockCdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||
let mockMatAutocompleteTrigger: MatAutocompleteTrigger;
|
||||
let mockMatAutocomplete: MatAutocomplete;
|
||||
let mockHTMLElement: HTMLElement;
|
||||
|
||||
async function initDefaultComponent() {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = [];
|
||||
component.control = new FormControl(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockCdkVirtualScrollViewport = mock(CdkVirtualScrollViewport);
|
||||
mockMatAutocompleteTrigger = mock(MatAutocompleteTrigger);
|
||||
mockMatAutocomplete = mock(MatAutocomplete);
|
||||
mockHTMLElement = mock(HTMLElement);
|
||||
});
|
||||
|
||||
describe('creation', () => {
|
||||
beforeEach(async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
component.optionsList = [];
|
||||
component.control = new FormControl(null);
|
||||
fixture.detectChanges();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('itemSize', () => {
|
||||
it(`should use default value if item size wasn't provided`, async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
expect(component.itemSize).toBe(48);
|
||||
});
|
||||
|
||||
it('should use provided value if item size was provided', async () => {
|
||||
await makeTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_ITEM_SIZE,
|
||||
useValue: 20,
|
||||
},
|
||||
],
|
||||
});
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
expect(component.itemSize).toBe(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listMultiplier', () => {
|
||||
it(`should use default value if list multiplier wasn't provided`, async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
expect(component.listMultiplier).toBe(5);
|
||||
});
|
||||
|
||||
it('should use provided value if list multiplier was provided', async () => {
|
||||
await makeTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_LIST_MULTIPLIER,
|
||||
useValue: 10,
|
||||
},
|
||||
],
|
||||
});
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
expect(component.listMultiplier).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listSize', () => {
|
||||
beforeEach(async () => {
|
||||
await makeTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_ITEM_SIZE,
|
||||
useValue: 10,
|
||||
},
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_LIST_MULTIPLIER,
|
||||
useValue: 5,
|
||||
},
|
||||
],
|
||||
});
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it(`should return zero if list doesn't exist`, () => {
|
||||
component.filteredOptions = undefined;
|
||||
expect(component.listSize).toBe(0);
|
||||
});
|
||||
|
||||
it(`should return zero if list is empty`, () => {
|
||||
component.filteredOptions = [];
|
||||
expect(component.listSize).toBe(0);
|
||||
});
|
||||
|
||||
it(`should return list multiplier multiplied on items size if list bigger than list multiplier`, () => {
|
||||
component.filteredOptions = new Array(6).fill(null).map((_: null, index: number) => {
|
||||
return {
|
||||
id: index,
|
||||
label: `name_${index}`,
|
||||
};
|
||||
});
|
||||
expect(component.listSize).toBe(50);
|
||||
});
|
||||
|
||||
it(`should return list length multiplied on items size if list smaller or equal to list multiplier`, () => {
|
||||
component.filteredOptions = new Array(5).fill(null).map((_: null, index: number) => {
|
||||
return {
|
||||
id: index,
|
||||
label: `name_${index}`,
|
||||
};
|
||||
});
|
||||
expect(component.listSize).toBe(50);
|
||||
|
||||
component.filteredOptions = new Array(2).fill(null).map((_: null, index: number) => {
|
||||
return {
|
||||
id: index,
|
||||
label: `name_${index}`,
|
||||
};
|
||||
});
|
||||
expect(component.listSize).toBe(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should init search control value using external control label value', async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = [];
|
||||
component.control = new FormControl({
|
||||
id: 'id',
|
||||
label: 'MyLabel',
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.searchControl.value).toBe('MyLabel');
|
||||
});
|
||||
|
||||
it(`should init search control with empty string if external control doesn't contain base option value`, async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = [];
|
||||
component.control = new FormControl(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.searchControl.value).toBe('');
|
||||
});
|
||||
|
||||
it('should add search listener that filter optionsList using search by label and filter using more relevant value first', async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = [
|
||||
...new Array(5).fill(null).map((_: null, index: number) => {
|
||||
return {
|
||||
id: index,
|
||||
label: `name_${index}`,
|
||||
};
|
||||
}),
|
||||
{
|
||||
id: 5,
|
||||
label: 'test_1',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: 'JustTest_2',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: 'my_test',
|
||||
},
|
||||
];
|
||||
component.control = new FormControl(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
component.searchControl.setValue('test');
|
||||
|
||||
expect(component.filteredOptions).toEqual([
|
||||
{
|
||||
id: 5,
|
||||
label: 'test_1',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: 'my_test',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: 'JustTest_2',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should make filtered options equal to options if init search was empty string', async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = new Array(5).fill(null).map((_: null, index: number) => {
|
||||
return {
|
||||
id: index,
|
||||
label: `name_${index}`,
|
||||
};
|
||||
});
|
||||
component.control = new FormControl(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.filteredOptions).toEqual(component.optionsList);
|
||||
});
|
||||
|
||||
it('should make init filter using external control value', async () => {
|
||||
await makeTestingModule();
|
||||
fixture = TestBed.createComponent(AutocompleteVirtualScrollComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.optionsList = [
|
||||
{
|
||||
id: 5,
|
||||
label: 'test_1',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: 'JustTest_2',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: 'my_test',
|
||||
},
|
||||
];
|
||||
component.control = new FormControl({
|
||||
id: 6,
|
||||
label: 'JustTest_2',
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.filteredOptions).toEqual([
|
||||
{
|
||||
id: 6,
|
||||
label: 'JustTest_2',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnChanges', () => {
|
||||
const eventEmitter = new Subject<void>();
|
||||
const subscriptions = new Map<string, Subscription>();
|
||||
|
||||
beforeEach(async () => {
|
||||
await initDefaultComponent();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
when(mockHTMLElement.addEventListener).thenReturn((eventName, handler) => {
|
||||
if (subscriptions.has(eventName)) {
|
||||
subscriptions.get(eventName).unsubscribe();
|
||||
}
|
||||
subscriptions.set(
|
||||
eventName,
|
||||
eventEmitter.subscribe(() => {
|
||||
handler();
|
||||
})
|
||||
);
|
||||
});
|
||||
when(mockHTMLElement.removeEventListener).thenReturn((eventName) => {
|
||||
if (subscriptions.has(eventName)) {
|
||||
subscriptions.get(eventName).unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
when(mockMatAutocompleteTrigger.openPanel()).thenReturn();
|
||||
when(mockMatAutocompleteTrigger.closePanel()).thenReturn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect().nothing();
|
||||
});
|
||||
|
||||
it('should add observable that call close panel on any scroll', () => {
|
||||
when(mockMatAutocomplete.isOpen).thenReturn(true);
|
||||
|
||||
component.scrollableWindow = instance(mockHTMLElement);
|
||||
component.autocomplete = instance(mockMatAutocomplete);
|
||||
component.trigger = instance(mockMatAutocompleteTrigger);
|
||||
|
||||
// fixture detect changes doesn't call ngOnChanges hook
|
||||
component.ngOnChanges({
|
||||
scrollableWindow: {
|
||||
previousValue: undefined,
|
||||
currentValue: component.scrollableWindow,
|
||||
isFirstChange(): boolean {
|
||||
return false;
|
||||
},
|
||||
firstChange: false,
|
||||
},
|
||||
});
|
||||
|
||||
eventEmitter.next();
|
||||
|
||||
verify(mockMatAutocompleteTrigger.closePanel()).once();
|
||||
});
|
||||
|
||||
it('should not close panel if panel already closed', () => {
|
||||
when(mockMatAutocomplete.isOpen).thenReturn(false);
|
||||
|
||||
component.scrollableWindow = instance(mockHTMLElement);
|
||||
component.autocomplete = instance(mockMatAutocomplete);
|
||||
component.trigger = instance(mockMatAutocompleteTrigger);
|
||||
|
||||
// fixture detect changes doesn't call ngOnChanges hook
|
||||
component.ngOnChanges({
|
||||
scrollableWindow: {
|
||||
previousValue: undefined,
|
||||
currentValue: component.scrollableWindow,
|
||||
isFirstChange(): boolean {
|
||||
return false;
|
||||
},
|
||||
firstChange: false,
|
||||
},
|
||||
});
|
||||
|
||||
eventEmitter.next();
|
||||
|
||||
verify(mockMatAutocompleteTrigger.closePanel()).never();
|
||||
});
|
||||
});
|
||||
|
||||
describe('panelOpened', () => {
|
||||
beforeEach(async () => {
|
||||
await initDefaultComponent();
|
||||
|
||||
when(mockCdkVirtualScrollViewport.checkViewportSize()).thenReturn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect().nothing();
|
||||
});
|
||||
|
||||
it('should check viewport size on each panel opening', () => {
|
||||
component.viewport = instance(mockCdkVirtualScrollViewport);
|
||||
|
||||
component.panelOpened();
|
||||
|
||||
verify(mockCdkVirtualScrollViewport.checkViewportSize()).once();
|
||||
});
|
||||
|
||||
it(`shouldn't check viewport size if viewport wasn't provided`, () => {
|
||||
component.viewport = null;
|
||||
|
||||
component.panelOpened();
|
||||
|
||||
verify(mockCdkVirtualScrollViewport.checkViewportSize()).never();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getErrorMessage', () => {
|
||||
beforeEach(async () => {
|
||||
await initDefaultComponent();
|
||||
});
|
||||
|
||||
it('should return empty string if control errors is empty object or null', () => {
|
||||
component.control.setValidators(() => {
|
||||
return null;
|
||||
});
|
||||
component.control.updateValueAndValidity();
|
||||
|
||||
expect(component.getErrorMessage()).toBe('');
|
||||
});
|
||||
|
||||
it(`should use first truthy error from list and return it if it's an string`, () => {
|
||||
component.control.setValidators([
|
||||
() => {
|
||||
return { first: null };
|
||||
},
|
||||
() => {
|
||||
return { second: '' };
|
||||
},
|
||||
() => {
|
||||
return { third: 0 };
|
||||
},
|
||||
() => {
|
||||
return { fourth: false };
|
||||
},
|
||||
() => {
|
||||
return { fifth: 'my error' };
|
||||
},
|
||||
() => {
|
||||
return { sixth: 'another error' };
|
||||
},
|
||||
]);
|
||||
component.control.updateValueAndValidity();
|
||||
|
||||
expect(component.getErrorMessage()).toBe('my error');
|
||||
});
|
||||
|
||||
it(`should parse array error, using it's first element as an error message`, () => {
|
||||
component.control.setValidators([
|
||||
() => {
|
||||
return { myError: ['my error'] };
|
||||
},
|
||||
]);
|
||||
component.control.updateValueAndValidity();
|
||||
|
||||
expect(component.getErrorMessage()).toBe('my error');
|
||||
});
|
||||
|
||||
it('should return empty string if error has empty array as a value', () => {
|
||||
component.control.setValidators([
|
||||
() => {
|
||||
return { myError: [] };
|
||||
},
|
||||
]);
|
||||
component.control.updateValueAndValidity();
|
||||
|
||||
expect(component.getErrorMessage()).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectionChanged', () => {
|
||||
beforeEach(async () => {
|
||||
await initDefaultComponent();
|
||||
});
|
||||
|
||||
it('should update component control value', () => {
|
||||
expect(component.control.value).toBe(null);
|
||||
|
||||
component.selectionChanged({ id: 1, label: 'my option' });
|
||||
|
||||
expect(component.control.value).toEqual({ id: 1, label: 'my option' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearValue', () => {
|
||||
beforeEach(async () => {
|
||||
await initDefaultComponent();
|
||||
});
|
||||
|
||||
it('should clear control value', () => {
|
||||
component.control.setValue({ id: 1, label: 'my option' });
|
||||
|
||||
component.clearValue();
|
||||
|
||||
expect(component.control.value).toBe(null);
|
||||
});
|
||||
|
||||
it('should clear search control', () => {
|
||||
component.searchControl.setValue('my search');
|
||||
|
||||
component.clearValue();
|
||||
|
||||
expect(component.searchControl.value).toBe('');
|
||||
});
|
||||
|
||||
describe('panel open', () => {
|
||||
beforeEach(() => {
|
||||
when(mockMatAutocompleteTrigger.openPanel()).thenReturn();
|
||||
});
|
||||
|
||||
it('should open panel after tick', async () => {
|
||||
when(mockMatAutocomplete.isOpen).thenReturn(false);
|
||||
|
||||
component.trigger = instance(mockMatAutocompleteTrigger);
|
||||
component.autocomplete = instance(mockMatAutocomplete);
|
||||
component.clearValue();
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(null);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
verify(mockMatAutocompleteTrigger.openPanel()).once();
|
||||
expect().nothing();
|
||||
});
|
||||
|
||||
it('should not open panel after tick if panel was opened', async () => {
|
||||
when(mockMatAutocomplete.isOpen).thenReturn(true);
|
||||
|
||||
component.trigger = instance(mockMatAutocompleteTrigger);
|
||||
component.autocomplete = instance(mockMatAutocomplete);
|
||||
component.clearValue();
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(null);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
verify(mockMatAutocompleteTrigger.openPanel()).never();
|
||||
expect().nothing();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,200 +0,0 @@
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Inject,
|
||||
Input,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Optional,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import isEmpty from 'lodash-es/isEmpty';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
import isObject from 'lodash-es/isObject';
|
||||
import { fromEvent } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { BaseOption } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll/types/base-option';
|
||||
|
||||
import { ComponentChange, ComponentChanges } from '../../../../../type-utils';
|
||||
import { VIRTUAL_SCROLL_ITEM_SIZE, VIRTUAL_SCROLL_LIST_MULTIPLIER } from '../tokens';
|
||||
|
||||
const ITEM_SIZE = 48;
|
||||
const LIST_MULTIPLIER = 5;
|
||||
const DEFAULT_PLACEHOLDER = 'Search ...';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'dsh-autocomplete-virtual-scroll',
|
||||
templateUrl: './autocomplete-virtual-scroll.component.html',
|
||||
styleUrls: ['./autocomplete-virtual-scroll.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AutocompleteVirtualScrollComponent implements OnInit, OnChanges {
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: true }) viewport: CdkVirtualScrollViewport;
|
||||
@ViewChild(MatAutocompleteTrigger, { static: true }) trigger: MatAutocompleteTrigger;
|
||||
@ViewChild(MatAutocomplete, { static: true }) autocomplete: MatAutocomplete;
|
||||
|
||||
@Input() label: string;
|
||||
@Input() control: FormControl;
|
||||
@Input() optionsList: BaseOption[];
|
||||
@Input() hint: string;
|
||||
@Input() scrollableWindow: HTMLElement;
|
||||
@Input() required: boolean;
|
||||
|
||||
@Input()
|
||||
get placeholder(): string {
|
||||
return this.innerPlaceholder;
|
||||
}
|
||||
set placeholder(value: string | undefined) {
|
||||
if (isNil(value)) {
|
||||
return;
|
||||
}
|
||||
this.innerPlaceholder = value;
|
||||
}
|
||||
|
||||
filteredOptions: BaseOption[];
|
||||
searchControl = new FormControl();
|
||||
|
||||
get itemSize(): number {
|
||||
if (isNil(this.innerItemSize)) {
|
||||
return ITEM_SIZE;
|
||||
}
|
||||
return this.innerItemSize;
|
||||
}
|
||||
get listMultiplier(): number {
|
||||
if (isNil(this.innerListMultiplier)) {
|
||||
return LIST_MULTIPLIER;
|
||||
}
|
||||
return this.innerListMultiplier;
|
||||
}
|
||||
|
||||
get listSize(): number {
|
||||
if (isNil(this.filteredOptions)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const length = this.filteredOptions.length;
|
||||
|
||||
if (length >= this.listMultiplier) {
|
||||
return this.listMultiplier * this.itemSize;
|
||||
}
|
||||
|
||||
return length * this.itemSize;
|
||||
}
|
||||
|
||||
private scrollableElement: HTMLElement;
|
||||
private innerPlaceholder = DEFAULT_PLACEHOLDER;
|
||||
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
@Optional()
|
||||
@Inject(VIRTUAL_SCROLL_LIST_MULTIPLIER)
|
||||
private innerListMultiplier: number,
|
||||
@Optional()
|
||||
@Inject(VIRTUAL_SCROLL_ITEM_SIZE)
|
||||
private innerItemSize: number
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initControls();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: ComponentChanges<AutocompleteVirtualScrollComponent>): void {
|
||||
if (!isNil(changes.scrollableWindow)) {
|
||||
this.initScrollableClose(changes.scrollableWindow);
|
||||
}
|
||||
}
|
||||
|
||||
panelOpened(): void {
|
||||
if (!isNil(this.viewport)) {
|
||||
this.viewport.checkViewportSize();
|
||||
}
|
||||
}
|
||||
|
||||
getErrorMessage(): string {
|
||||
if (isEmpty(this.control.errors)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const [error] = Object.entries<string | string[] | null>(this.control.errors)
|
||||
.map(([, value]: [string, string | string[] | null]) => value)
|
||||
.filter(Boolean);
|
||||
const errorMessage = Array.isArray(error) ? error[0] : error;
|
||||
return isNil(errorMessage) ? '' : errorMessage;
|
||||
}
|
||||
|
||||
selectionChanged(option: BaseOption): void {
|
||||
this.control.setValue(option);
|
||||
}
|
||||
|
||||
clearValue(): void {
|
||||
this.control.setValue(null);
|
||||
this.searchControl.setValue('');
|
||||
// need to make update after cycle was completed once
|
||||
setTimeout(() => {
|
||||
this.openPanel();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private initControls(): void {
|
||||
this.searchControl.valueChanges
|
||||
.pipe(
|
||||
map((search: string) => search.toLowerCase()),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe((search: string) => {
|
||||
this.filterOptions(search);
|
||||
});
|
||||
|
||||
const initValue = isObject(this.control.value as BaseOption) ? this.control.value.label : '';
|
||||
this.searchControl.setValue(initValue);
|
||||
}
|
||||
|
||||
private initScrollableClose(change: ComponentChange<AutocompleteVirtualScrollComponent, 'scrollableWindow'>): void {
|
||||
if (isNil(change.currentValue) || !isNil(this.scrollableElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollableElement = change.currentValue;
|
||||
this.zone.runOutsideAngular(() => {
|
||||
fromEvent(this.scrollableWindow, 'scroll')
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => {
|
||||
this.closePanel();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private openPanel(): void {
|
||||
if (!this.autocomplete.isOpen) {
|
||||
this.trigger.openPanel();
|
||||
}
|
||||
}
|
||||
|
||||
private closePanel(): void {
|
||||
if (this.autocomplete.isOpen) {
|
||||
this.trigger.closePanel();
|
||||
}
|
||||
}
|
||||
|
||||
private filterOptions(search: string): void {
|
||||
this.filteredOptions = this.optionsList
|
||||
.filter(({ label }: BaseOption) => label.toLowerCase().includes(search))
|
||||
.map((option: BaseOption) => {
|
||||
return {
|
||||
...option,
|
||||
indexOf: option.label.toLowerCase().indexOf(search),
|
||||
};
|
||||
})
|
||||
.sort(({ indexOf: aIndex }, { indexOf: bIndex }) => {
|
||||
return aIndex >= bIndex ? 1 : -1;
|
||||
})
|
||||
.map(({ id, label }) => ({ id, label }));
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { AutocompleteVirtualScrollComponent } from './autocomplete-virtual-scroll.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
ScrollingModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
],
|
||||
declarations: [AutocompleteVirtualScrollComponent],
|
||||
exports: [AutocompleteVirtualScrollComponent],
|
||||
})
|
||||
export class AutocompleteVirtualScrollModule {}
|
@ -1 +0,0 @@
|
||||
export * from './autocomplete-virtual-scroll.module';
|
@ -1,4 +0,0 @@
|
||||
export interface BaseOption<Id = number | string> {
|
||||
id: Id;
|
||||
label: string;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export * from './tokens';
|
||||
export * from './selects.module';
|
||||
export * from './autocomplete-virtual-scroll';
|
@ -1,10 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AutocompleteVirtualScrollModule } from '@dsh/app/shared/components/selects/autocomplete-virtual-scroll';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, AutocompleteVirtualScrollModule],
|
||||
exports: [AutocompleteVirtualScrollModule],
|
||||
})
|
||||
export class SelectsModule {}
|
@ -1,4 +0,0 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const VIRTUAL_SCROLL_LIST_MULTIPLIER = new InjectionToken('VirtualScrollListMultiplier');
|
||||
export const VIRTUAL_SCROLL_ITEM_SIZE = new InjectionToken('VirtualScrollItemSize');
|
@ -1,14 +1,14 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { TranslocoTestingModule } from '@ngneat/transloco';
|
||||
import { instance, mock, verify } from 'ts-mockito';
|
||||
|
||||
import { ShopType } from '../../../types/shop-type';
|
||||
import { CreateShopDialogComponent } from './create-shop-dialog.component';
|
||||
import { ShopType } from './types/shop-type';
|
||||
|
||||
@Component({ template: '' })
|
||||
class MockOnBoardingComponent {}
|
||||
@ -51,12 +51,6 @@ describe('CreateShopDialogComponent', () => {
|
||||
provide: MatDialogRef,
|
||||
useFactory: () => instance(mockDialogRef),
|
||||
},
|
||||
{
|
||||
provide: MAT_DIALOG_DATA,
|
||||
useValue: {
|
||||
realm: 'my_realm',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideComponent(CreateShopDialogComponent, {
|
@ -0,0 +1,57 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Shop } from '@dsh/api-codegen/capi';
|
||||
import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog';
|
||||
import { SHOPS } from '@dsh/app/shared/components/inputs/shop-field';
|
||||
|
||||
import { ShopType } from './types/shop-type';
|
||||
|
||||
export interface CreateShopDialogData {
|
||||
shops$?: Observable<Shop[]>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-create-shop-dialog',
|
||||
templateUrl: 'create-shop-dialog.component.html',
|
||||
styleUrls: ['create-shop-dialog.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: SHOPS,
|
||||
deps: [MAT_DIALOG_DATA],
|
||||
useFactory: ({ shops$ }: CreateShopDialogData = {}) => shops$,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CreateShopDialogComponent {
|
||||
selectedShopType: ShopType;
|
||||
selectionConfirmed = false;
|
||||
shopType = ShopType;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<CreateShopDialogComponent, BaseDialogResponseStatus>,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
onTypeChange(type: ShopType): void {
|
||||
this.selectedShopType = type;
|
||||
}
|
||||
|
||||
next(): void {
|
||||
if (this.selectedShopType === ShopType.New) {
|
||||
this.dialogRef.close();
|
||||
void this.router.navigate(['claim-section', 'onboarding']);
|
||||
}
|
||||
this.selectionConfirmed = true;
|
||||
}
|
||||
|
||||
sendClaim(): void {
|
||||
this.dialogRef.close(BaseDialogResponseStatus.Success);
|
||||
}
|
||||
|
||||
cancelClaim(): void {
|
||||
this.dialogRef.close(BaseDialogResponseStatus.Cancelled);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user