OPS-235, OPS-237: Support API's partyID, fix first entrance (#109)

This commit is contained in:
Rinat Arsaev 2023-02-16 17:34:28 +06:00 committed by GitHub
parent a56d987843
commit 12cbb6bbe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 406 additions and 589 deletions

95
package-lock.json generated
View File

@ -31,14 +31,13 @@
"@sentry/angular": "7.7.0",
"@sentry/integrations": "7.7.0",
"@sentry/tracing": "7.7.0",
"@vality/swag-anapi-v2": "2.0.1-0405be2.0",
"@vality/swag-anapi-v2": "2.0.1-38f360b.0",
"@vality/swag-claim-management": "0.1.1-bfc2e6c.0",
"@vality/swag-dark-api": "0.1.1-a3f1678.0",
"@vality/swag-organizations": "1.0.1-cd6cc10.0",
"@vality/swag-payments": "0.1.1-d71aebc.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-ed41741.0",
"@vality/swag-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0",
"@vality/swag-wallet": "0.1.2-21b69d2.0",
"angular-epic-spinners": "2.0.0",
"angular-file": "^3.0.1",
"angular2-text-mask": "^9.0.0",
@ -85,6 +84,7 @@
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"concurrently": "7.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.0.3",
"eslint": "8.20.0",
"eslint-config-prettier": "8.5.0",
@ -4980,15 +4980,15 @@
}
},
"node_modules/@vality/swag-anapi-v2": {
"version": "2.0.1-0405be2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-0405be2.0.tgz",
"integrity": "sha512-YHSMYjTByWVTiTyO1U+I6Rfwm6N1KUz3F4rPiG4WJRcjVut73nvoo1A1xCp5KC38qhQ7li+uTpGQ/kx8VaL5oQ==",
"version": "2.0.1-38f360b.0",
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-38f360b.0.tgz",
"integrity": "sha512-6RCpXFDvD+BlKsQxrA2pDH6B9fyzp+X69zapNpTgmTsxeS5acR7Jf9MS+a0kL2Rz/B9HS86jt+VYk3pvQco/PQ==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^13.0.0",
"@angular/core": "^13.0.0"
"@angular/common": ">=14.0.0",
"@angular/core": ">=14.0.0"
}
},
"node_modules/@vality/swag-claim-management": {
@ -5002,18 +5002,6 @@
"@angular/core": "^13.0.0"
}
},
"node_modules/@vality/swag-dark-api": {
"version": "0.1.1-a3f1678.0",
"resolved": "https://registry.npmjs.org/@vality/swag-dark-api/-/swag-dark-api-0.1.1-a3f1678.0.tgz",
"integrity": "sha512-2mYcmHKn9YOWjoUU9h/7Pg/hUPUS8ODckjtzmFGNFwtQ2tStLtVkODBe56cthxcQHbnhrQs24Hekatyv9wi69w==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^13.0.0",
"@angular/core": "^13.0.0"
}
},
"node_modules/@vality/swag-organizations": {
"version": "1.0.1-cd6cc10.0",
"resolved": "https://registry.npmjs.org/@vality/swag-organizations/-/swag-organizations-1.0.1-cd6cc10.0.tgz",
@ -5039,9 +5027,9 @@
}
},
"node_modules/@vality/swag-questionary-aggr-proxy": {
"version": "0.1.1-1dc5add.0",
"resolved": "https://registry.npmjs.org/@vality/swag-questionary-aggr-proxy/-/swag-questionary-aggr-proxy-0.1.1-1dc5add.0.tgz",
"integrity": "sha512-SHuDYPoqsfmHnqRtiwryxzSqvsllNSN9dvOLwUpGPN1lTsGWPDr/xsZDqgeJiTCnD/Tlf19repnWTtah1Mz/5Q==",
"version": "0.1.1-ed41741.0",
"resolved": "https://registry.npmjs.org/@vality/swag-questionary-aggr-proxy/-/swag-questionary-aggr-proxy-0.1.1-ed41741.0.tgz",
"integrity": "sha512-Zm2DINHuG4xP+rmxWo2yvRHpkf84hNBS7giuE5AHw/zYPXiKG+9FiQxKYpU6g3OB4geZZWkt4lWLYgVwfcFFfQ==",
"dependencies": {
"tslib": "^2.3.0"
},
@ -5063,9 +5051,9 @@
}
},
"node_modules/@vality/swag-wallet": {
"version": "0.1.1-9a236df.0",
"resolved": "https://registry.npmjs.org/@vality/swag-wallet/-/swag-wallet-0.1.1-9a236df.0.tgz",
"integrity": "sha512-equiIidtj1xblTFDk4z7Y4MsJxdlM2eaimMyWLHVYJrMkWPkQkYldC9V5bFWj5c16AY8d5zhXnOq6vaVBMp0ww==",
"version": "0.1.2-21b69d2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-wallet/-/swag-wallet-0.1.2-21b69d2.0.tgz",
"integrity": "sha512-zbyoiJ4ZJAvCtXpTRad5n7xwTwavl40UWLaU2DWSdp/cgjKd9yhUoEUU89+HLE6rZpqQL6fdSuqZ5CyFEfh9Yg==",
"dependencies": {
"tslib": "^2.3.0"
},
@ -7240,6 +7228,24 @@
"node": ">=8"
}
},
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.1"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=10.14",
"npm": ">=6",
"yarn": ">=1"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"dev": true,
@ -21284,9 +21290,9 @@
}
},
"@vality/swag-anapi-v2": {
"version": "2.0.1-0405be2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-0405be2.0.tgz",
"integrity": "sha512-YHSMYjTByWVTiTyO1U+I6Rfwm6N1KUz3F4rPiG4WJRcjVut73nvoo1A1xCp5KC38qhQ7li+uTpGQ/kx8VaL5oQ==",
"version": "2.0.1-38f360b.0",
"resolved": "https://registry.npmjs.org/@vality/swag-anapi-v2/-/swag-anapi-v2-2.0.1-38f360b.0.tgz",
"integrity": "sha512-6RCpXFDvD+BlKsQxrA2pDH6B9fyzp+X69zapNpTgmTsxeS5acR7Jf9MS+a0kL2Rz/B9HS86jt+VYk3pvQco/PQ==",
"requires": {
"tslib": "^2.3.0"
}
@ -21297,14 +21303,6 @@
"tslib": "^2.3.0"
}
},
"@vality/swag-dark-api": {
"version": "0.1.1-a3f1678.0",
"resolved": "https://registry.npmjs.org/@vality/swag-dark-api/-/swag-dark-api-0.1.1-a3f1678.0.tgz",
"integrity": "sha512-2mYcmHKn9YOWjoUU9h/7Pg/hUPUS8ODckjtzmFGNFwtQ2tStLtVkODBe56cthxcQHbnhrQs24Hekatyv9wi69w==",
"requires": {
"tslib": "^2.3.0"
}
},
"@vality/swag-organizations": {
"version": "1.0.1-cd6cc10.0",
"resolved": "https://registry.npmjs.org/@vality/swag-organizations/-/swag-organizations-1.0.1-cd6cc10.0.tgz",
@ -21322,9 +21320,9 @@
}
},
"@vality/swag-questionary-aggr-proxy": {
"version": "0.1.1-1dc5add.0",
"resolved": "https://registry.npmjs.org/@vality/swag-questionary-aggr-proxy/-/swag-questionary-aggr-proxy-0.1.1-1dc5add.0.tgz",
"integrity": "sha512-SHuDYPoqsfmHnqRtiwryxzSqvsllNSN9dvOLwUpGPN1lTsGWPDr/xsZDqgeJiTCnD/Tlf19repnWTtah1Mz/5Q==",
"version": "0.1.1-ed41741.0",
"resolved": "https://registry.npmjs.org/@vality/swag-questionary-aggr-proxy/-/swag-questionary-aggr-proxy-0.1.1-ed41741.0.tgz",
"integrity": "sha512-Zm2DINHuG4xP+rmxWo2yvRHpkf84hNBS7giuE5AHw/zYPXiKG+9FiQxKYpU6g3OB4geZZWkt4lWLYgVwfcFFfQ==",
"requires": {
"tslib": "^2.3.0"
}
@ -21338,9 +21336,9 @@
}
},
"@vality/swag-wallet": {
"version": "0.1.1-9a236df.0",
"resolved": "https://registry.npmjs.org/@vality/swag-wallet/-/swag-wallet-0.1.1-9a236df.0.tgz",
"integrity": "sha512-equiIidtj1xblTFDk4z7Y4MsJxdlM2eaimMyWLHVYJrMkWPkQkYldC9V5bFWj5c16AY8d5zhXnOq6vaVBMp0ww==",
"version": "0.1.2-21b69d2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-wallet/-/swag-wallet-0.1.2-21b69d2.0.tgz",
"integrity": "sha512-zbyoiJ4ZJAvCtXpTRad5n7xwTwavl40UWLaU2DWSdp/cgjKd9yhUoEUU89+HLE6rZpqQL6fdSuqZ5CyFEfh9Yg==",
"requires": {
"tslib": "^2.3.0"
}
@ -22921,6 +22919,15 @@
}
}
},
"cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.1"
}
},
"cross-spawn": {
"version": "7.0.3",
"dev": true,

View File

@ -5,7 +5,7 @@
"scripts": {
"postinstall": "ngcc",
"start": "ng serve --proxy-config proxy.conf.js --port 8000",
"stage": "ng serve --proxy-config proxy.conf.js --port 8001 --configuration=stage",
"stage": "cross-env NODE_ENV=stage ng serve --proxy-config proxy.conf.js --port 8001 --configuration=stage",
"fix": "npm run lint-fix && npm run prettier-fix",
"build": "ng build --extra-webpack-config webpack.extra.js && npm run transloco:optimize",
"test": "ng test",
@ -13,7 +13,7 @@
"i18n:extract": "transloco-keys-manager extract",
"i18n:check": "transloco-keys-manager find --emit-error-on-extra-keys",
"coverage": "npx http-server -c-1 -o -p 9875 ./coverage",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1067",
"lint-cmd": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 1073",
"lint": "npm run lint-cmd",
"lint-fix": "npm run lint-cmd -- --fix",
"lint-errors": "npm run lint-cmd -- --quiet",
@ -49,14 +49,13 @@
"@sentry/angular": "7.7.0",
"@sentry/integrations": "7.7.0",
"@sentry/tracing": "7.7.0",
"@vality/swag-anapi-v2": "2.0.1-0405be2.0",
"@vality/swag-anapi-v2": "2.0.1-38f360b.0",
"@vality/swag-claim-management": "0.1.1-bfc2e6c.0",
"@vality/swag-dark-api": "0.1.1-a3f1678.0",
"@vality/swag-organizations": "1.0.1-cd6cc10.0",
"@vality/swag-payments": "0.1.1-d71aebc.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-ed41741.0",
"@vality/swag-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0",
"@vality/swag-wallet": "0.1.2-21b69d2.0",
"angular-epic-spinners": "2.0.0",
"angular-file": "^3.0.1",
"angular2-text-mask": "^9.0.0",
@ -103,6 +102,7 @@
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"concurrently": "7.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.0.3",
"eslint": "8.20.0",
"eslint-config-prettier": "8.5.0",
@ -127,4 +127,4 @@
"ts-node": "10.9.1",
"typescript": "4.7.4"
}
}
}

View File

@ -26,6 +26,9 @@ function createSubdomainByPathProxy(target, subdomainPathPrefix = '__') {
secure: false,
logLevel: 'debug',
changeOrigin: true,
onProxyReq: (req) => {
req.setHeader('origin', `https://dashboard.${host}${port ? `:${port}` : ''}`);
},
};
}

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { InvoiceStatus, Report, RefundStatus, PaymentSearchResult } from '@vality/swag-anapi-v2';
import { BankCardPaymentSystem } from '@vality/swag-anapi-v2/lib/model/bankCardPaymentSystem';
import { BankCardTokenProvider } from '@vality/swag-anapi-v2/lib/model/bankCardTokenProvider';
import { PaymentSystem, TokenProvider } from '@dsh/api/payments';
import { DictionaryService } from '../utils';
@ -73,7 +73,7 @@ export class AnapiDictionaryService {
pending: this.t.translate('anapi.reportStatus.pending', null, 'dictionary'),
}));
bankCardPaymentSystem$ = this.dictionaryService.create<BankCardPaymentSystem>(() => ({
bankCardPaymentSystem$ = this.dictionaryService.create<PaymentSystem>(() => ({
amex: this.t.translate('anapi.bankCardPaymentSystem.amex', null, 'dictionary'),
dankort: this.t.translate('anapi.bankCardPaymentSystem.dankort', null, 'dictionary'),
dinersclub: this.t.translate('anapi.bankCardPaymentSystem.dinersclub', null, 'dictionary'),
@ -92,7 +92,7 @@ export class AnapiDictionaryService {
uzcard: this.t.translate('anapi.bankCardPaymentSystem.uzcard', null, 'dictionary'),
}));
bankCardTokenProvider$ = this.dictionaryService.create<BankCardTokenProvider>(() => ({
bankCardTokenProvider$ = this.dictionaryService.create<TokenProvider>(() => ({
applepay: this.t.translate('anapi.bankCardTokenProvider.applepay', null, 'dictionary'),
googlepay: this.t.translate('anapi.bankCardTokenProvider.googlepay', null, 'dictionary'),
samsungpay: this.t.translate('anapi.bankCardTokenProvider.samsungpay', null, 'dictionary'),

View File

@ -1,18 +0,0 @@
import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { RefundStatus } from '@vality/swag-dark-api';
import { DictionaryService } from '../utils';
@Injectable({
providedIn: 'root',
})
export class DarkApiDictionaryService {
refundStatus$ = this.dictionaryService.create<RefundStatus.StatusEnum>(() => ({
pending: this.t.translate('darkApi.refundStatus.pending', null, 'dictionary'),
succeeded: this.t.translate('darkApi.refundStatus.succeeded', null, 'dictionary'),
failed: this.t.translate('darkApi.refundStatus.failed', null, 'dictionary'),
}));
constructor(private t: TranslocoService, private dictionaryService: DictionaryService) {}
}

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { Configuration } from '@vality/swag-dark-api';
import { ConfigService } from '../../config';
@NgModule({
providers: [
{
provide: Configuration,
deps: [ConfigService],
useFactory: (configService: ConfigService) =>
new Configuration({ basePath: `${configService.apiEndpoint}/dark-api/v1` }),
},
],
})
export class DarkApiModule {}

View File

@ -1,53 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { FilesService as ApiFilesService, FileUploadData } from '@vality/swag-dark-api';
import { forkJoin, Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { createApi, ApiMethodParams } from '../utils';
@Injectable({
providedIn: 'root',
})
export class FilesService extends createApi(ApiFilesService) {
constructor(injector: Injector, private http: HttpClient) {
super(injector);
}
uploadFiles(files: File[]) {
return forkJoin(
files.map((file) =>
this.getUploadLink().pipe(
switchMap((uploadData) =>
forkJoin([of(uploadData.fileId), this.uploadFileToUrl(file, uploadData.url)])
),
map(([fileId]) => fileId)
)
)
);
}
getDecodedFileInfo = (params: ApiMethodParams<ApiFilesService['getFileInfo'], 'xRequestID'>) => {
return this.getFileInfo(params).pipe(
map((file) => ({
...file,
fileName: decodeURI(file.fileName),
}))
);
};
private uploadFileToUrl(file: File, url: string): Observable<any> {
return this.http.put(url, file, {
headers: {
/* eslint-disable @typescript-eslint/naming-convention */
'Content-Disposition': `attachment;filename=${encodeURI(file.name)}`,
'Content-Type': '',
/* eslint-enable @typescript-eslint/naming-convention */
},
});
}
private getUploadLink(): Observable<FileUploadData> {
return this.uploadFile({ uploadFileRequest: { metadata: {} } });
}
}

View File

@ -1,2 +0,0 @@
export * from './dark-api.module';
export * from './files.service';

View File

@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { ContractsService as ApiContractsService } from '@vality/swag-payments';
import { PartyIdExtension } from '@dsh/api/utils/extensions';
import { createApi } from '../utils';
@Injectable({
providedIn: 'root',
})
export class ContractsService extends createApi(ApiContractsService) {}
export class ContractsService extends createApi(ApiContractsService, [PartyIdExtension]) {}

View File

@ -1,9 +1,19 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector } from '@angular/core';
import { InvoiceTemplatesService as ApiInvoiceTemplatesService } from '@vality/swag-payments';
import { PartyIdPatchMethodService } from '@dsh/api/utils/extensions';
import { createApi } from '../utils';
@Injectable({
providedIn: 'root',
})
export class InvoiceTemplatesService extends createApi(ApiInvoiceTemplatesService) {}
export class InvoiceTemplatesService extends createApi(ApiInvoiceTemplatesService) {
constructor(injector: Injector, private partyIdPatchMethodService: PartyIdPatchMethodService) {
super(injector);
this.createInvoiceTemplate = this.partyIdPatchMethodService.patch(
this.createInvoiceTemplate,
(params, partyId) => (params.invoiceTemplateCreateParams.partyID = partyId)
);
}
}

View File

@ -1,9 +1,19 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector } from '@angular/core';
import { InvoicesService as ApiInvoicesService } from '@vality/swag-payments';
import { PartyIdPatchMethodService } from '@dsh/api/utils/extensions';
import { createApi } from '../utils';
@Injectable({
providedIn: 'root',
})
export class InvoicesService extends createApi(ApiInvoicesService) {}
export class InvoicesService extends createApi(ApiInvoicesService) {
constructor(injector: Injector, partyIdPatchMethodService: PartyIdPatchMethodService) {
super(injector);
this.createInvoice = partyIdPatchMethodService.patch(
this.createInvoice,
(p, id) => (p.invoiceParams.partyID = id)
);
}
}

View File

@ -1,9 +1,19 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector } from '@angular/core';
import { PayoutsService as ApiPayoutsService } from '@vality/swag-payments';
import { PartyIdExtension, PartyIdPatchMethodService } from '@dsh/api/utils/extensions';
import { createApi } from '../utils';
@Injectable({
providedIn: 'root',
})
export class PayoutsService extends createApi(ApiPayoutsService) {}
export class PayoutsService extends createApi(ApiPayoutsService, [PartyIdExtension]) {
constructor(injector: Injector, private partyIdPatchMethodService: PartyIdPatchMethodService) {
super(injector);
this.createPayout = partyIdPatchMethodService.patch(
this.createPayout,
(params, partyId) => (params.payoutParams.partyID = partyId)
);
}
}

View File

@ -1,32 +1,13 @@
import { Injectable, Injector } from '@angular/core';
import { ShopsService as ApiShopsService, Shop } from '@vality/swag-payments';
import { defer, Observable, Subject, combineLatest } from 'rxjs';
import { startWith, switchMap } from 'rxjs/operators';
import { ContextService } from '@dsh/app/shared';
import { shareReplayRefCount } from '@dsh/operators';
import { Injectable } from '@angular/core';
import { ShopsService as ApiShopsService } from '@vality/swag-payments';
import { createApi } from '../utils';
import { PartyIdExtension } from '../utils/extensions';
/**
* Use only "SomeForParty" methods
*/
@Injectable({
providedIn: 'root',
})
export class ShopsService extends createApi(ApiShopsService) {
shops$: Observable<Shop[]> = combineLatest([
this.contextService.organization$,
defer(() => this.reloadShops$).pipe(startWith(null)),
]).pipe(
switchMap(([{ party }]) => this.getShopsForParty({ partyID: party })),
shareReplayRefCount()
);
private reloadShops$ = new Subject<void>();
constructor(injector: Injector, private contextService: ContextService) {
super(injector);
}
reloadShops(): void {
this.reloadShops$.next();
}
}
export class ShopsService extends createApi(ApiShopsService, [PartyIdExtension]) {}

View File

@ -1,9 +1,19 @@
import { Injectable } from '@angular/core';
import { Injectable, Injector } from '@angular/core';
import { WebhooksService as ApiWebhooksService } from '@vality/swag-payments';
import { PartyIdPatchMethodService } from '@dsh/api/utils/extensions';
import { createApi } from '../utils';
@Injectable({
providedIn: 'root',
})
export class WebhooksService extends createApi(ApiWebhooksService) {}
export class WebhooksService extends createApi(ApiWebhooksService) {
constructor(injector: Injector, partyIdPatchMethodService: PartyIdPatchMethodService) {
super(injector);
this.createWebhook = partyIdPatchMethodService.patch(
this.createWebhook,
(params, partyID) => (params.webhookParams.partyID = partyID)
);
}
}

View File

@ -1 +1,2 @@
export * from './party-id-extension';
export * from './party-id-patch-method.service';

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { first, map } from 'rxjs/operators';
import { ContextService } from '@dsh/app/shared';
import { ContextOrganizationService } from '@dsh/app/shared/services/context-organization';
import { ApiExtension } from '../create-api';
@ -9,10 +9,10 @@ import { ApiExtension } from '../create-api';
providedIn: 'root',
})
export class PartyIdExtension implements ApiExtension {
constructor(private contextService: ContextService) {}
constructor(private contextOrganizationService: ContextOrganizationService) {}
selector() {
return this.contextService.organization$.pipe(
return this.contextOrganizationService.organization$.pipe(
first(),
map(({ party }) => ({ partyID: party }))
);

View File

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import cloneDeep from 'lodash-es/cloneDeep';
import { Observable, switchMap } from 'rxjs';
import { DeepPartial } from 'utility-types';
import { PartyIdExtension } from './party-id-extension';
@Injectable({
providedIn: 'root',
})
export class PartyIdPatchMethodService extends PartyIdExtension {
patch<P extends object, R, E extends DeepPartial<P> | void>(
method: (params: P) => Observable<R>,
patch: (params: P, partyId: string) => unknown
): (params: E) => Observable<R> {
return (params) =>
this.selector().pipe(
switchMap(({ partyID }) => {
const newParams = cloneDeep(params);
patch(newParams as P, partyID);
return method(newParams as P);
})
);
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { IdentitiesService as ApiIdentitiesService } from '@vality/swag-wallet';
import { Subject, defer } from 'rxjs';
import { switchMapTo, pluck, startWith } from 'rxjs/operators';
import { IdentitiesService as ApiIdentitiesService, Identity } from '@vality/swag-wallet';
import { Subject, defer, switchMap } from 'rxjs';
import { startWith, map } from 'rxjs/operators';
import { shareReplayRefCount } from '@dsh/operators';
@ -13,8 +13,8 @@ import { createApi } from '../utils';
export class IdentitiesService extends createApi(ApiIdentitiesService) {
identities$ = defer(() => this.reloadIdentities$).pipe(
startWith<void>(undefined),
switchMapTo(this.listIdentities()),
pluck('result'),
switchMap(() => this.listIdentities()),
map((r) => r.result as Identity[]),
shareReplayRefCount()
);

View File

@ -1,10 +1,6 @@
import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { DepositRevert } from '@vality/swag-wallet';
import { Deposit } from '@vality/swag-wallet/lib/model/deposit';
import { DestinationsTopic } from '@vality/swag-wallet/lib/model/destinationsTopic';
import { Withdrawal } from '@vality/swag-wallet/lib/model/withdrawal';
import { WithdrawalsTopic } from '@vality/swag-wallet/lib/model/withdrawalsTopic';
import { DepositRevert, WithdrawalsTopic, DestinationsTopic, Deposit, Withdrawal } from '@vality/swag-wallet';
import { DictionaryService } from '../utils';

View File

@ -1,3 +1,8 @@
<dsh-home>
<dsh-sections *ngIf="bootstrapped$ | async"></dsh-sections>
<dsh-sections *ngIf="bootstrapped$ | async; else spinner"></dsh-sections>
<ng-template #spinner>
<div fxLayout="row" fxFlexAlign="center">
<dsh-spinner style="margin: auto"></dsh-spinner>
</div>
</ng-template>
</dsh-home>

View File

@ -1,13 +1,10 @@
import { Component, Inject, OnInit } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Component, OnInit } from '@angular/core';
import * as Sentry from '@sentry/angular';
import { first, map } from 'rxjs/operators';
import { first } from 'rxjs/operators';
import { ENV, Env } from '../environments';
import { BootstrapService } from './bootstrap.service';
import { ContextService } from './shared';
import { ContextOrganizationService } from './shared';
@UntilDestroy()
@Component({
selector: 'dsh-root',
templateUrl: 'app.component.html',
@ -18,15 +15,12 @@ export class AppComponent implements OnInit {
constructor(
private bootstrapService: BootstrapService,
@Inject(ENV) public env: Env,
private contextService: ContextService
private contextOrganizationService: ContextOrganizationService
) {}
ngOnInit(): void {
this.bootstrapService.bootstrap();
this.contextService.organization$
.pipe(map(({ party }) => party))
.pipe(first(), untilDestroyed(this))
.subscribe((partyID) => Sentry.setUser({ id: partyID }));
this.contextOrganizationService.organization$
.pipe(first())
.subscribe(({ party }) => Sentry.setUser({ id: party }));
}
}

View File

@ -18,7 +18,6 @@ import * as Sentry from '@sentry/angular';
import { AnapiModule } from '@dsh/api/anapi';
import { ClaimManagementModule } from '@dsh/api/claim-management';
import { DarkApiModule } from '@dsh/api/dark-api';
import { PaymentsModule } from '@dsh/api/payments';
import { QuestionaryAggrProxyModule } from '@dsh/api/questionary-aggr-proxy';
import { UrlShortenerModule } from '@dsh/api/url-shortener';
@ -26,6 +25,7 @@ import { WalletModule } from '@dsh/api/wallet';
import { ErrorModule } 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/date-range-filter';
import { SpinnerModule } from '@dsh/components/indicators';
import { ENV, environment } from '../environments';
import { OrganizationsModule } from './api/organizations';
@ -64,8 +64,8 @@ import { TranslocoHttpLoaderService } from './transloco-http-loader.service';
OrganizationsModule,
UrlShortenerModule,
QuestionaryAggrProxyModule,
DarkApiModule,
WalletModule,
SpinnerModule,
],
providers: [
LanguageService,

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import { ContextService } from '@dsh/app/shared';
import { ContextOrganizationService } from '@dsh/app/shared';
import { ROLE_ACCESS_GROUPS } from './role-access-groups';
import { RoleAccess } from './types/role-access';
@ -16,12 +16,11 @@ const ROLE_ACCESSES_OBJECT = Object.fromEntries(
providedIn: 'root',
})
export class RoleAccessService {
constructor(private contextService: ContextService) {}
constructor(private contextOrganizationService: ContextOrganizationService) {}
isAccessAllowed(roleAccessNames: RoleAccessName[], type: 'every' | 'some' = 'every'): Observable<boolean> {
if (!roleAccessNames.length) return of(true);
return this.contextService.member$.pipe(
first(),
return this.contextOrganizationService.member$.pipe(
map((member) => {
const memberRoles = member.roles.map((r) => r.roleId);
return roleAccessNames[type]((access) =>

View File

@ -1,87 +1,26 @@
import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Organization } from '@vality/swag-organizations';
import { concat, defer, Observable, of, ReplaySubject, throwError } from 'rxjs';
import { catchError, first, mapTo, shareReplay, switchMap, takeLast, tap, map } from 'rxjs/operators';
import { Observable, throwError, switchMap } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';
import { ClaimsService, createTestShopClaimChangeset } from '@dsh/api/claim-management';
import { DEFAULT_ORGANIZATION_NAME, OrgsService } from '@dsh/api/organizations';
import { ShopsService } from '@dsh/api/payments';
import { CommonError, ErrorService, IdGeneratorService } from '@dsh/app/shared';
import { ShopsDataService, ErrorService, CommonError } from '@dsh/app/shared';
@UntilDestroy()
@Injectable()
export class BootstrapService {
bootstrapped$: Observable<boolean> = defer(() => this.bootstrap$).pipe(
switchMap(() => this.getBootstrapped()),
untilDestroyed(this),
shareReplay(1)
bootstrapped$: Observable<boolean> = this.shopsDataService.shops$.pipe(
catchError((err) =>
this.transloco.selectTranslate<string>('app.errors.bootstrapAppFailed', null, 'components').pipe(
tap((msg) => this.errorService.error(new CommonError(msg))),
switchMap(() => throwError(err))
)
),
map(() => true)
);
private bootstrap$ = new ReplaySubject<void>(1);
constructor(
private shopService: ShopsService,
private claimsService: ClaimsService,
private errorService: ErrorService,
private organizationsService: OrgsService,
private shopsDataService: ShopsDataService,
private transloco: TranslocoService,
private idGenerator: IdGeneratorService
private errorService: ErrorService
) {}
bootstrap(): void {
this.bootstrap$.next();
}
private getBootstrapped(): Observable<boolean> {
return concat(this.initOrganization(), this.initShop()).pipe(
takeLast(1),
catchError((err) =>
this.transloco.selectTranslate<string>('app.errors.bootstrapAppFailed', null, 'components').pipe(
tap((msg) => this.errorService.error(new CommonError(msg))),
switchMap(() => throwError(err))
)
)
);
}
private initOrganization(): Observable<boolean> {
return this.organizationsService.listOrgMembership({ limit: 1 }).pipe(
first(),
switchMap((orgs) => (orgs.result.length ? of(true) : this.createOrganization())),
catchError((err) => {
this.errorService.error(err, false);
return of(true);
})
);
}
private createOrganization(): Observable<boolean> {
return this.organizationsService
.createOrg({ organization: { name: DEFAULT_ORGANIZATION_NAME } as Organization })
.pipe(mapTo(true));
}
private initShop(): Observable<boolean> {
return this.shopService.shops$.pipe(
first(),
switchMap((shops) =>
shops.length ? of(true) : this.createTestShop().pipe(tap(() => this.shopService.reloadShops()))
)
);
}
private createTestShop(): Observable<boolean> {
return this.claimsService
.createClaim({
changeset: createTestShopClaimChangeset(
this.idGenerator.uuid(),
this.idGenerator.uuid(),
this.idGenerator.uuid(),
this.idGenerator.uuid()
),
})
.pipe(map(() => true));
}
}

View File

@ -8,7 +8,7 @@ import { first, map } from 'rxjs/operators';
import { SEARCH_LIMIT } from '@dsh/app/sections/tokens';
import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog';
import { ContextService } from '@dsh/app/shared/services/context';
import { ContextOrganizationService } from '@dsh/app/shared/services';
import { FetchOrganizationsService } from '@dsh/app/shared/services/fetch-organizations';
const DISPLAYED_COUNT = 5;
@ -25,7 +25,7 @@ export class SelectActiveOrganizationDialogComponent implements OnInit {
hasMore$ = this.fetchOrganizationsService.hasMore$;
selectedOrganization: Organization;
isLoading$ = this.fetchOrganizationsService.doAction$;
contextOrganization$ = this.contextService.organization$;
contextOrganization$ = this.contextOrganizationService.organization$;
constructor(
private dialogRef: MatDialogRef<
@ -34,12 +34,12 @@ export class SelectActiveOrganizationDialogComponent implements OnInit {
>,
private fetchOrganizationsService: FetchOrganizationsService,
private router: Router,
private contextService: ContextService
private contextOrganizationService: ContextOrganizationService
) {}
ngOnInit(): void {
this.fetchOrganizationsService.search();
combineLatest([this.organizations$, this.contextService.organization$])
combineLatest([this.organizations$, this.contextOrganizationService.organization$])
.pipe(
first(),
map(([orgs, activeOrg]) => orgs.find((org) => org.id === activeOrg.id)),

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { map } from 'rxjs/operators';
import { ContextService } from '@dsh/app/shared/services/context';
import { ContextOrganizationService } from '@dsh/app/shared/services';
import { coerceBoolean } from '@dsh/utils';
import { KeycloakService } from '../../../../auth';
@ -18,7 +18,10 @@ export class UserDropdownComponent {
@Input() @coerceBoolean expanded = false;
username = this.keycloakService.getUsername();
orgName$ = this.contextService.organization$.pipe(map(({ name }) => name));
orgName$ = this.contextOrganizationService.organization$.pipe(map(({ name }) => name));
constructor(private contextService: ContextService, private keycloakService: KeycloakService) {}
constructor(
private contextOrganizationService: ContextOrganizationService,
private keycloakService: KeycloakService
) {}
}

View File

@ -6,7 +6,7 @@ import { Organization } from '@vality/swag-organizations';
import { map, filter } from 'rxjs/operators';
import { DIALOG_CONFIG, DialogConfig } from '@dsh/app/sections/tokens';
import { ContextService } from '@dsh/app/shared';
import { ContextOrganizationService } from '@dsh/app/shared';
import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog';
import { KeycloakService } from '../../../../auth';
@ -22,7 +22,7 @@ export class UserComponent {
@Output() selected = new EventEmitter<void>();
username = this.keycloakService.getUsername();
activeOrg$ = this.contextService.organization$;
activeOrg$ = this.contextOrganizationService.organization$;
keycloakAccountEndpoint = `${this.config.keycloakEndpoint}/auth/realms/external/account/`;
userLinksConfig$ = this.transloco.selectTranslation('components').pipe(
map(() => [
@ -44,7 +44,7 @@ export class UserComponent {
constructor(
private keycloakService: KeycloakService,
private config: ConfigService,
private contextService: ContextService,
private contextOrganizationService: ContextOrganizationService,
private router: Router,
private dialog: MatDialog,
private transloco: TranslocoService,
@ -69,7 +69,7 @@ export class UserComponent {
.afterClosed()
.pipe(filter((res) => !Object.values(BaseDialogResponseStatus).includes(res as BaseDialogResponseStatus)))
.subscribe((org: Organization) => {
this.contextService.switchOrganization(org.id);
this.contextOrganizationService.switchOrganization(org.id);
});
}

View File

@ -7,8 +7,8 @@ import { BehaviorSubject, combineLatest, EMPTY, Observable, of } from 'rxjs';
import { first, map, switchMap, tap } from 'rxjs/operators';
import { OrganizationsDictionaryService } from '@dsh/api/organizations';
import { ShopsService } from '@dsh/api/payments';
import { DialogConfig, DIALOG_CONFIG } from '@dsh/app/sections/tokens';
import { ShopsDataService } from '@dsh/app/shared';
import { sortRoleIds } from '@dsh/app/shared/components/organization-roles/utils/sort-role-ids';
import { PartialReadonly } from '@dsh/type-utils';
import { coerceBoolean } from '@dsh/utils';
@ -49,7 +49,7 @@ export class ChangeRolesTableComponent implements OnInit {
@Output() removedRoles = new EventEmitter<PartialReadonly<MemberRole>[]>();
roleIds: RoleId[] = [];
shops$ = this.shopsService.shops$;
shops$ = this.shopsDataService.shops$;
roleIdDict$ = this.organizationsDictionaryService.roleId$;
get availableRoles(): RoleId[] {
@ -71,7 +71,7 @@ export class ChangeRolesTableComponent implements OnInit {
}
constructor(
private shopsService: ShopsService,
private shopsDataService: ShopsDataService,
private dialog: MatDialog,
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig,
private cdr: ChangeDetectorRef,

View File

@ -7,7 +7,7 @@ import { filter, pluck, switchMap } from 'rxjs/operators';
import { OrgsService } from '@dsh/api/organizations';
import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog';
import { ErrorService, NotificationService, ContextService } from '@dsh/app/shared/services';
import { ErrorService, NotificationService, ContextOrganizationService } from '@dsh/app/shared/services';
import { FetchOrganizationsService } from '@dsh/app/shared/services/fetch-organizations';
import { OrganizationManagementService } from '@dsh/app/shared/services/organization-management/organization-management.service';
import { ConfirmActionDialogComponent, ConfirmActionDialogResult } from '@dsh/components/popups';
@ -29,7 +29,7 @@ export class OrganizationComponent implements OnChanges {
@Output() changed = new EventEmitter<void>();
member$ = this.contextService.member$;
member$ = this.contextOrganizationService.member$;
membersCount$ = this.organizationManagementService.members$.pipe(pluck('length'));
hasAdminAccess$ = this.organizationManagementService.hasAdminAccess$;
isOwner$ = this.organizationManagementService.isOrganizationOwner$;
@ -41,7 +41,7 @@ export class OrganizationComponent implements OnChanges {
private notificationService: NotificationService,
private errorService: ErrorService,
private fetchOrganizationsService: FetchOrganizationsService,
private contextService: ContextService
private contextOrganizationService: ContextOrganizationService
) {}
ngOnChanges({ organization }: ComponentChanges<OrganizationComponent>) {

View File

@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router';
import { FormControl } from '@ngneat/reactive-forms';
import { pluck, shareReplay } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { FormData } from '@dsh/app/shared/components/create-invoice-form';
import { SHARE_REPLAY_CONF } from '@dsh/operators';
@ -17,9 +17,13 @@ export class CreateInvoiceOrInvoiceTemplateService {
shops$ = this.route.params.pipe(
pluck('realm'),
filterShopsByRealm(this.shopService.shops$),
filterShopsByRealm(this.shopsDataService.shops$),
shareReplay(SHARE_REPLAY_CONF)
);
constructor(private fb: UntypedFormBuilder, private route: ActivatedRoute, private shopService: ShopsService) {}
constructor(
private fb: UntypedFormBuilder,
private route: ActivatedRoute,
private shopsDataService: ShopsDataService
) {}
}

View File

@ -4,7 +4,8 @@ import { WebhookScope } from '@vality/swag-payments';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { ShopsService, PaymentsDictionaryService } from '@dsh/api/payments';
import { PaymentsDictionaryService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { oneMustBeSelected } from '@dsh/components/form-controls';
import { getEventsByTopic } from '../get-events-by-topic';
@ -19,7 +20,7 @@ export class CreateWebhookFormComponent implements OnInit {
@Input()
form: UntypedFormGroup;
shops$ = this.shopService.shops$;
shops$ = this.shopsDataService.shops$;
activeTopic$ = new BehaviorSubject<TopicEnum>('InvoicesTopic');
@ -29,7 +30,7 @@ export class CreateWebhookFormComponent implements OnInit {
]).pipe(map(([i, c]) => ({ ...i, ...c })));
constructor(
private shopService: ShopsService,
private shopsDataService: ShopsDataService,
private fb: UntypedFormBuilder,
private paymentsDictionaryService: PaymentsDictionaryService
) {}

View File

@ -6,7 +6,7 @@ import { PaymentInstitution, Shop } from '@vality/swag-payments';
import { Observable, of, ReplaySubject } from 'rxjs';
import { filter, pluck, switchMap, take } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { filterShopsByRealm } from '../../operators';
import { CreateInvoiceDialogComponent } from './components/create-invoice-dialog/create-invoice-dialog.component';
@ -16,7 +16,7 @@ import RealmEnum = PaymentInstitution.RealmEnum;
@Injectable()
export class CreateInvoiceService {
constructor(
private apiShopsService: ShopsService,
private shopsDataService: ShopsDataService,
private dialog: MatDialog,
private transloco: TranslocoService,
private snackBar: MatSnackBar
@ -26,7 +26,7 @@ export class CreateInvoiceService {
const invoiceCreated$ = new ReplaySubject<string>(1);
of(realm)
.pipe(
filterShopsByRealm(this.apiShopsService.shops$),
filterShopsByRealm(this.shopsDataService.shops$),
switchMap((shops) =>
this.dialog
.open<CreateInvoiceDialogComponent, Shop[]>(CreateInvoiceDialogComponent, {

View File

@ -2,7 +2,7 @@ import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { FormBuilder } from '@ngneat/reactive-forms';
import { WrappedFormControlSuperclass, provideValueAccessor } from '@s-libs/ng-core';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
@Component({
selector: 'dsh-shops-filter',
@ -11,9 +11,9 @@ import { ShopsService } from '@dsh/api/payments';
providers: [provideValueAccessor(ShopsFilterComponent)],
})
export class ShopsFilterComponent extends WrappedFormControlSuperclass<string[]> {
shops$ = this.shopsService.shops$;
shops$ = this.shopsDataService.shops$;
constructor(injector: Injector, private fb: FormBuilder, private shopsService: ShopsService) {
constructor(injector: Injector, private fb: FormBuilder, private shopsDataService: ShopsDataService) {
super(injector);
}
}

View File

@ -12,7 +12,7 @@ import pick from 'lodash-es/pick';
import { defer, ReplaySubject, BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { createDateRangeWithPreset, Preset, DateRangeWithPreset } from '@dsh/components/date-range-filter';
import { shareReplayRefCount } from '@dsh/operators';
import { ComponentChanges } from '@dsh/type-utils';
@ -49,7 +49,7 @@ export class InvoicesSearchFiltersComponent implements OnChanges, OnInit {
shopIDs: null,
invoiceStatus: null,
});
shops$ = defer(() => this.realm$).pipe(filterShopsByRealm(this.shopService.shops$), shareReplayRefCount());
shops$ = defer(() => this.realm$).pipe(filterShopsByRealm(this.shopsDataService.shops$), shareReplayRefCount());
isAdditionalFilterApplied$ = defer(() => this.additionalFilters$).pipe(map(negate(isEmpty)));
get keys(): string[] {
@ -61,7 +61,7 @@ export class InvoicesSearchFiltersComponent implements OnChanges, OnInit {
constructor(
private fb: FormBuilder,
private shopService: ShopsService,
private shopsDataService: ShopsDataService,
private dialog: MatDialog,
private mediaObserver: MediaObserver
) {}

View File

@ -1,8 +1,9 @@
import { Component, Injector } from '@angular/core';
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { BankCardPaymentSystem } from '@vality/swag-anapi-v2';
import { SearchPaymentsRequestParams } from '@vality/swag-anapi-v2';
import { AnapiDictionaryService } from '@dsh/api/anapi';
import { PaymentSystem } from '@dsh/api/payments';
@Component({
selector: 'dsh-payment-system-filter',
@ -10,8 +11,10 @@ import { AnapiDictionaryService } from '@dsh/api/anapi';
styleUrls: ['./payment-system-filter.component.scss'],
providers: [provideValueAccessor(PaymentSystemFilterComponent)],
})
export class PaymentSystemFilterComponent extends WrappedFormControlSuperclass<BankCardPaymentSystem> {
paymentSystems = Object.values(BankCardPaymentSystem);
export class PaymentSystemFilterComponent extends WrappedFormControlSuperclass<
SearchPaymentsRequestParams['bankCardPaymentSystem']
> {
paymentSystems = Object.values(PaymentSystem);
bankCardPaymentSystemDict$ = this.anapiDictionaryService.bankCardPaymentSystem$;
constructor(injector: Injector, private anapiDictionaryService: AnapiDictionaryService) {

View File

@ -1,7 +1,7 @@
import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { FormBuilder } from '@ngneat/reactive-forms';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { ValidatedControlSuperclass, createControlProviders } from '@dsh/utils';
import { ShopsFilterForm } from './types';
@ -17,9 +17,9 @@ export class ShopsFilterComponent extends ValidatedControlSuperclass<ShopsFilter
shopIDs: null,
});
shops$ = this.shopsService.shops$;
shops$ = this.shopsDataService.shops$;
constructor(injector: Injector, private fb: FormBuilder, private shopsService: ShopsService) {
constructor(injector: Injector, private fb: FormBuilder, private shopsDataService: ShopsDataService) {
super(injector);
}
}

View File

@ -1,8 +1,9 @@
import { Component, Injector } from '@angular/core';
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { BankCardTokenProvider } from '@vality/swag-anapi-v2';
import { SearchPaymentsRequestParams } from '@vality/swag-anapi-v2';
import { AnapiDictionaryService } from '@dsh/api/anapi';
import { TokenProvider } from '@dsh/api/payments';
@Component({
selector: 'dsh-token-provider-filter',
@ -10,8 +11,10 @@ import { AnapiDictionaryService } from '@dsh/api/anapi';
styleUrls: ['./token-provider-filter.component.scss'],
providers: [provideValueAccessor(TokenProviderFilterComponent)],
})
export class TokenProviderFilterComponent extends WrappedFormControlSuperclass<BankCardTokenProvider> {
providers = Object.values(BankCardTokenProvider);
export class TokenProviderFilterComponent extends WrappedFormControlSuperclass<
SearchPaymentsRequestParams['bankCardTokenProvider']
> {
providers: TokenProvider[] = Object.values(TokenProvider);
bankCardTokenProviderDict$ = this.anapiDictionaryService.bankCardTokenProvider$;
constructor(injector: Injector, private anapiDictionaryService: AnapiDictionaryService) {

View File

@ -1,4 +1,4 @@
import { BankCardTokenProvider, BankCardPaymentSystem, PaymentStatus } from '@vality/swag-anapi-v2';
import { PaymentStatus, SearchPaymentsRequestParams } from '@vality/swag-anapi-v2';
import { CardFilterForm } from '../card-filter';
import { InvoicesFilterForm } from '../invoices-filter';
@ -10,8 +10,8 @@ export interface AdditionalFiltersForm {
main: MainFiltersForm;
paymentStatus: PaymentStatus.StatusEnum;
paymentSum: PaymentSumFilterForm;
tokenProvider: BankCardTokenProvider;
paymentSystem: BankCardPaymentSystem;
tokenProvider: SearchPaymentsRequestParams['bankCardTokenProvider'];
paymentSystem: SearchPaymentsRequestParams['bankCardPaymentSystem'];
invoices: InvoicesFilterForm;
shops: ShopsFilterForm;
binPan: CardFilterForm;

View File

@ -1,4 +1,4 @@
import { PaymentStatus, BankCardPaymentSystem, BankCardTokenProvider } from '@vality/swag-anapi-v2';
import { PaymentStatus, SearchPaymentsRequestParams } from '@vality/swag-anapi-v2';
import { CardFilterForm } from '../card-filter';
import { InvoicesFilterForm } from '../invoices-filter';
@ -13,6 +13,6 @@ export type AdditionalFilters = Partial<MainFiltersForm> &
Partial<CardFilterForm> & {
binPan?: CardFilterForm;
paymentStatus?: PaymentStatus.StatusEnum;
tokenProvider?: BankCardTokenProvider;
paymentSystem?: BankCardPaymentSystem;
tokenProvider?: SearchPaymentsRequestParams['bankCardTokenProvider'];
paymentSystem?: SearchPaymentsRequestParams['bankCardPaymentSystem'];
};

View File

@ -12,7 +12,7 @@ import pick from 'lodash-es/pick';
import { defer, ReplaySubject, BehaviorSubject, combineLatest } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { DateRange, Preset, createDateRangeWithPreset } from '@dsh/components/date-range-filter';
import { ComponentChanges } from '@dsh/type-utils';
import { getFormValueChanges } from '@dsh/utils';
@ -40,7 +40,7 @@ export class PaymentsFiltersComponent implements OnInit, OnChanges {
@Input() initParams: Filters;
@Output() filtersChanged = new EventEmitter<MainFilters>();
shops$ = defer(() => this.realm$).pipe(filterShopsByRealm(this.shopService.shops$), shareReplay(1));
shops$ = defer(() => this.realm$).pipe(filterShopsByRealm(this.shopsDataService.shops$), shareReplay(1));
isAdditionalFilterApplied$ = defer(() => this.additionalFilters$).pipe(map(negate(isEmpty)));
defaultDateRange = createDateRangeWithPreset(Preset.Last90days);
form = this.fb.group<MainFilters>({
@ -58,7 +58,7 @@ export class PaymentsFiltersComponent implements OnInit, OnChanges {
private realm$ = new ReplaySubject<RealmEnum>(1);
constructor(
private shopService: ShopsService,
private shopsDataService: ShopsDataService,
private fb: FormBuilder,
private dialog: MatDialog,
private mediaObserver: MediaObserver

View File

@ -1,22 +1,22 @@
import {
BankCardPaymentSystem,
BankCardTokenProvider,
PaymentTerminalProvider,
SearchPaymentsRequestParams,
} from '@vality/swag-anapi-v2';
import { SearchPaymentsRequestParams } from '@vality/swag-anapi-v2';
import { PaymentInstitution } from '@vality/swag-payments';
import RealmEnum = PaymentInstitution.RealmEnum;
export interface PaymentSearchFormValue {
export interface PaymentSearchFormValue
extends Pick<
SearchPaymentsRequestParams,
| 'paymentStatus'
| 'paymentTerminalProvider'
| 'bankCardTokenProvider'
| 'bankCardPaymentSystem'
| 'paymentMethod'
| 'paymentFlow'
> {
realm: RealmEnum;
fromTime: string;
toTime: string;
shopIDs?: string[];
paymentStatus?: SearchPaymentsRequestParams['paymentStatus'];
paymentFlow?: 'hold' | 'instant';
paymentMethod?: 'bankCard' | 'paymentTerminal';
paymentTerminalProvider?: PaymentTerminalProvider;
invoiceIDs?: string[];
invoiceID?: string;
paymentID?: string;
@ -26,8 +26,6 @@ export interface PaymentSearchFormValue {
customerID?: string;
first6?: string;
last4?: string;
bankCardTokenProvider?: BankCardTokenProvider;
bankCardPaymentSystem?: BankCardPaymentSystem;
paymentAmount?: number;
rrn?: string;
paymentAmountFrom?: number;

View File

@ -2,7 +2,7 @@ import { Component, Injector, ChangeDetectionStrategy } from '@angular/core';
import { FormBuilder } from '@ngneat/reactive-forms';
import { WrappedFormControlSuperclass, provideValueAccessor } from '@s-libs/ng-core';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
@Component({
selector: 'dsh-shops-filter',
@ -11,9 +11,9 @@ import { ShopsService } from '@dsh/api/payments';
providers: [provideValueAccessor(ShopsFilterComponent)],
})
export class ShopsFilterComponent extends WrappedFormControlSuperclass<string[]> {
shops$ = this.shopsService.shops$;
shops$ = this.shopsDataService.shops$;
constructor(injector: Injector, private fb: FormBuilder, private shopsService: ShopsService) {
constructor(injector: Injector, private fb: FormBuilder, private shopsDataService: ShopsDataService) {
super(injector);
}
}

View File

@ -5,7 +5,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslocoService } from '@ngneat/transloco';
import { of } from 'rxjs';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { amountValidator } from '@dsh/components/form-controls';
import { filterShopsByRealm, mapToShopInfo } from '../../operations/operators';
@ -25,7 +25,7 @@ export class CreatePayoutDialogComponent implements OnInit {
currentPayoutToolCurrency: string;
shopsInfo$ = of(this.data.realm).pipe(filterShopsByRealm(this.shopsService.shops$), mapToShopInfo);
shopsInfo$ = of(this.data.realm).pipe(filterShopsByRealm(this.shopsDataService.shops$), mapToShopInfo);
isPayoutToolsLoading$ = this.createPayoutDialogService.isLoading$;
payoutTools$ = this.createPayoutDialogService.payoutTools$;
@ -37,7 +37,7 @@ export class CreatePayoutDialogComponent implements OnInit {
private createPayoutDialogService: CreatePayoutDialogService,
private snackBar: MatSnackBar,
private transloco: TranslocoService,
private shopsService: ShopsService,
private shopsDataService: ShopsDataService,
@Inject(MAT_DIALOG_DATA) private data: { realm: string }
) {}

View File

@ -2,7 +2,8 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject, forkJoin, merge, of, Subject } from 'rxjs';
import { catchError, filter, map, shareReplay, switchMap, take, tap } from 'rxjs/operators';
import { ShopsService, PayoutsService } from '@dsh/api/payments';
import { PayoutsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { toPayoutParams } from './to-payout-params';
@ -24,8 +25,8 @@ export class CreatePayoutDialogService {
// eslint-disable-next-line @typescript-eslint/member-ordering
payoutTools$ = this.currentShopID$.pipe(
switchMap((shopID) => this.shopsService.shops$.pipe(map((shops) => shops.find(({ id }) => id === shopID)))),
switchMap(({ contractID }) => this.payoutsService.getPayoutTools({ contractID })),
switchMap((shopID) => this.shopsDataService.shops$.pipe(map((shops) => shops.find(({ id }) => id === shopID)))),
switchMap(({ contractID }) => this.payoutsService.getPayoutToolsForParty({ contractID })),
map((tools) => tools.filter((tool) => tool.details.detailsType === 'PayoutToolDetailsWalletInfo')),
shareReplay(1)
);
@ -36,7 +37,7 @@ export class CreatePayoutDialogService {
shareReplay(1)
);
constructor(private shopsService: ShopsService, private payoutsService: PayoutsService) {
constructor(private shopsDataService: ShopsDataService, private payoutsService: PayoutsService) {
merge(this.payoutTools$, this.hasPayoutTools$).subscribe();
this.create$
.pipe(
@ -47,7 +48,7 @@ export class CreatePayoutDialogService {
switchMap((params) =>
forkJoin([
of(params),
this.shopsService.shops$.pipe(
this.shopsDataService.shops$.pipe(
take(1),
map((shops) => shops.find(({ id }) => id === params.shopID)?.currency)
),

View File

@ -6,7 +6,7 @@ import { TranslocoService } from '@ngneat/transloco';
import moment from 'moment';
import { of } from 'rxjs';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { filterShopsByRealm, mapToShopInfo } from '../../operations/operators';
import { CreateReportDialogService } from './create-report-dialog.service';
@ -21,7 +21,7 @@ const TIME_PATTERN = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/;
})
export class CreateReportDialogComponent implements OnInit {
isLoading$ = this.createReportDialogService.isLoading$;
shopsInfo$ = of(this.data.realm).pipe(filterShopsByRealm(this.shopService.shops$), mapToShopInfo);
shopsInfo$ = of(this.data.realm).pipe(filterShopsByRealm(this.shopsDataService.shops$), mapToShopInfo);
form = this.fb.group({
fromDate: [moment().startOf('month').format(), Validators.required],
fromTime: ['00:00:00', Validators.pattern(TIME_PATTERN)],
@ -32,7 +32,7 @@ export class CreateReportDialogComponent implements OnInit {
constructor(
private dialogRef: MatDialogRef<CreateReportDialogComponent, 'cancel' | 'created'>,
private shopService: ShopsService,
private shopsDataService: ShopsDataService,
private fb: UntypedFormBuilder,
private createReportDialogService: CreateReportDialogService,
private transloco: TranslocoService,

View File

@ -3,7 +3,7 @@ import { Shop } from '@vality/swag-payments';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { shareReplayRefCount } from '@dsh/operators';
import { getShopsByRealm } from '../operations/operators';
@ -11,10 +11,10 @@ import { PaymentInstitutionRealmService } from './payment-institution-realm.serv
@Injectable()
export class RealmShopsService {
shops$: Observable<Shop[]> = combineLatest([this.shopsService.shops$, this.realmService.realm$]).pipe(
shops$: Observable<Shop[]> = combineLatest([this.shopsDataService.shops$, this.realmService.realm$]).pipe(
map(([shops, realm]) => (realm ? getShopsByRealm(shops, realm) : [])),
shareReplayRefCount()
);
constructor(private shopsService: ShopsService, private realmService: PaymentInstitutionRealmService) {}
constructor(private shopsDataService: ShopsDataService, private realmService: PaymentInstitutionRealmService) {}
}

View File

@ -4,7 +4,7 @@ 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 { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { mapToTimestamp, shareReplayRefCount } from '@dsh/operators';
import { filterShopsByRealm } from '../../../operations/operators';
@ -41,7 +41,7 @@ export class FetchShopsService {
private filteredShops$: Observable<ShopItem[]>;
constructor(
private shopsService: ShopsService,
private shopsDataService: ShopsDataService,
private shopsBalance: ShopsBalanceService,
private filtersStore: ShopsFiltersStoreService,
private filtersService: ShopsFiltersService,
@ -69,7 +69,7 @@ export class FetchShopsService {
refreshData(): void {
this.startLoading();
this.shopsService.reloadShops();
this.shopsDataService.reloadShops();
}
showMore(): void {
@ -97,7 +97,7 @@ export class FetchShopsService {
}
private initAllShopsFetching(): void {
this.allShops$ = this.realmData$.pipe(filterShopsByRealm(this.shopsService.shops$), shareReplayRefCount());
this.allShops$ = this.realmData$.pipe(filterShopsByRealm(this.shopsDataService.shops$), shareReplayRefCount());
}
private initOffsetObservable(): void {

View File

@ -32,7 +32,7 @@ export class ShopPayoutToolDetailsService {
switchMap((payoutToolParams) =>
payoutToolParams
? this.payoutsService
.getPayoutToolByID({
.getPayoutToolByIDForParty({
contractID: payoutToolParams.contractID,
payoutToolID: payoutToolParams.payoutToolID,
})

View File

@ -7,7 +7,7 @@ import { Shop } from '@vality/swag-payments';
import cloneDeep from 'lodash-es/cloneDeep';
import { of } from 'rxjs';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { generateMockShopItem } from '../../../../tests/generate-shop-item';
import { ShopActionsService } from '../../services/shop-actions/shop-actions.service';
@ -17,7 +17,7 @@ import { ShopActionsComponent } from './shop-actions.component';
class MockShopsService {}
@Injectable()
class MockApiShopsService extends ShopsService {
class MockApiShopsService extends ShopsDataService {
set mockShops(shops: Shop[]) {
this._shops = shops;
}
@ -83,11 +83,11 @@ describe('ShopActionsComponent', () => {
providers: [
ShopActionsService,
{
provide: ShopsService,
provide: ShopsDataService,
useClass: MockApiShopsService,
},
{
provide: ShopsService,
provide: ShopsDataService,
useClass: MockShopsService,
},
{

View File

@ -13,7 +13,7 @@ import { ShopActionResult } from '../../types/shop-action-result';
@Injectable()
export class ShopActionsService {
constructor(
private shopService: ShopsService,
private shopsService: ShopsService,
private dialog: MatDialog,
private snackBar: MatSnackBar,
private transloco: TranslocoService
@ -25,7 +25,7 @@ export class ShopActionsService {
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
switchMap(() => this.shopService.suspendShop({ shopID })),
switchMap(() => this.shopsService.suspendShopForParty({ shopID })),
map(() => {
this.snackBar.open(
this.transloco.translate('shops.suspend.success', null, 'payment-section'),
@ -49,7 +49,7 @@ export class ShopActionsService {
.afterClosed()
.pipe(
filter((r) => r === 'confirm'),
switchMap(() => this.shopService.activateShop({ shopID })),
switchMap(() => this.shopsService.activateShopForParty({ shopID })),
map(() => {
this.snackBar.open(
this.transloco.translate('shops.activate.success', null, 'payment-section'),

View File

@ -45,7 +45,7 @@ export class ReceiveWebhooksService {
catchError((err) => {
console.error(err);
this.snackBar.open(this.transloco.translate('shared.httpError', null, 'components'), 'OK');
return of([]);
return of<Webhook[]>([]);
})
)
),

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
import { provideValueAccessor } from '@s-libs/ng-core';
import { RefundStatus } from '@vality/swag-dark-api';
import { RefundStatus } from '@vality/swag-anapi-v2';
import { FilterSuperclass } from '@dsh/components/filter';

View File

@ -4,7 +4,7 @@ import { Shop } from '@vality/swag-payments';
import { combineLatest } from 'rxjs';
import { map, share } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
import { FilterSuperclass } from '@dsh/components/filter';
@Component({
@ -16,14 +16,14 @@ import { FilterSuperclass } from '@dsh/components/filter';
export class ShopsFilterComponent extends FilterSuperclass<Shop['id'][]> {
@Input() shops: Shop[];
labels$ = combineLatest([this.savedValue$, this.shopsService.shops$]).pipe(
labels$ = combineLatest([this.savedValue$, this.shopsDataService.shops$]).pipe(
map(([selectedShopIds, shops]) =>
(selectedShopIds || []).map((id) => shops.find((s) => s.id === id)?.details?.name || id)
),
share()
);
constructor(injector: Injector, private shopsService: ShopsService) {
constructor(injector: Injector, private shopsDataService: ShopsDataService) {
super(injector);
}
}

View File

@ -1,16 +1,16 @@
import { Pipe, PipeTransform } from '@angular/core';
import { RefundStatus } from '@vality/swag-dark-api';
import { RefundStatus } from '@vality/swag-anapi-v2';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { DarkApiDictionaryService } from '@dsh/api/dark-api/dark-api-dictionary.service';
import { AnapiDictionaryService } from '@dsh/api/anapi';
@Pipe({ name: 'refundStatusLabel' })
export class RefundStatusLabelPipe implements PipeTransform {
constructor(private darkApiDictionaryService: DarkApiDictionaryService) {}
constructor(private anapiDictionaryService: AnapiDictionaryService) {}
transform(value: RefundStatus.StatusEnum): Observable<string> {
if (!value) return of('');
return this.darkApiDictionaryService.refundStatus$.pipe(map((d) => d[value]));
return this.anapiDictionaryService.refundStatus$.pipe(map((d) => d[value]));
}
}

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { RefundStatus } from '@vality/swag-dark-api';
import { RefundStatus } from '@vality/swag-anapi-v2';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

View File

@ -1,4 +1,4 @@
import { RefundStatus } from '@vality/swag-dark-api';
import { RefundStatus } from '@vality/swag-anapi-v2';
export const OPTION_LABELS: { [N in RefundStatus.StatusEnum]: string } = {
succeeded: 'succeeded',

View File

@ -4,7 +4,8 @@ import { Shop } from '@vality/swag-payments';
import { defer, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ShopsService, toLiveShops } from '@dsh/api/payments';
import { toLiveShops } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
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';
@ -23,7 +24,7 @@ export class ShopFieldComponent extends WrappedFormControlSuperclass<Shop> {
@Input() @coerceBoolean required = false;
options$: Observable<Option<Shop>[]> = defer(
() => this.shops$ || this.shopsService.shops$.pipe(map(toLiveShops))
() => this.shops$ || this.shopsDataService.shops$.pipe(map(toLiveShops))
).pipe(
map((shops) => shops.map(shopToOption)),
shareReplayRefCount()
@ -31,7 +32,7 @@ export class ShopFieldComponent extends WrappedFormControlSuperclass<Shop> {
constructor(
injector: Injector,
private shopsService: ShopsService,
private shopsDataService: ShopsDataService,
@Inject(SHOPS)
@Optional()
private shops$?: Observable<Shop[]>

View File

@ -71,7 +71,7 @@ export class ExistingBankAccountComponent extends ValidatedControlSuperclass<Exi
private getPayoutToolByShop(shop: Shop): Observable<PayoutTool> {
return this.payoutsService
.getPayoutToolByID({ contractID: shop.contractID, payoutToolID: shop.payoutToolID })
.getPayoutToolByIDForParty({ contractID: shop.contractID, payoutToolID: shop.payoutToolID })
.pipe(
switchMap((payoutTool) => {
if (payoutTool.details.detailsType !== this.bankAccountType)

View File

@ -61,7 +61,7 @@ export class ExistingContractFormComponent extends ValidatedControlSuperclass<Ex
}
private getContract(contractID: Contract['id']): Observable<ExistingContractForm> {
return this.contractsService.getContractByID({ contractID }).pipe(
return this.contractsService.getContractByIDForParty({ contractID }).pipe(
switchMap((contract: ExistingContractForm) => {
if (contract.contractor.entityType !== this.entityType)
return (

View File

@ -2,7 +2,7 @@ import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
import { ShopsService } from '@dsh/api/payments';
import { ShopsDataService } from '@dsh/app/shared';
@Pipe({
name: 'shopDetails',
@ -13,8 +13,8 @@ export class ShopDetailsPipe implements PipeTransform, OnDestroy {
private shopIDChange$: Subject<string> = new Subject();
private destroy$: Subject<void> = new Subject();
constructor(private shopService: ShopsService, private ref: ChangeDetectorRef) {
combineLatest([this.shopService.shops$, this.shopIDChange$.pipe(distinctUntilChanged())])
constructor(private shopsDataService: ShopsDataService, private ref: ChangeDetectorRef) {
combineLatest([this.shopsDataService.shops$, this.shopIDChange$.pipe(distinctUntilChanged())])
.pipe(
takeUntil(this.destroy$),
map(([shops, shopID]) => shops.find((s) => s.id === shopID)?.details?.name || shopID)

View File

@ -2,27 +2,28 @@ import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Organization, Member, RoleId } from '@vality/swag-organizations';
import isNil from 'lodash-es/isNil';
import { Observable, ReplaySubject, EMPTY, concat, defer, combineLatest, of, throwError } from 'rxjs';
import { switchMap, shareReplay, catchError, map, tap } from 'rxjs/operators';
import { switchMap, shareReplay, catchError, map, tap, filter } from 'rxjs/operators';
import { OrgsService, MembersService } from '@dsh/api/organizations';
import { OrgsService, MembersService, DEFAULT_ORGANIZATION_NAME } from '@dsh/api/organizations';
import { KeycloakTokenInfoService } from '@dsh/app/shared/services/keycloak-token-info';
import { ErrorService } from '../error';
import { KeycloakTokenInfoService } from '../keycloak-token-info';
@UntilDestroy()
@Injectable({
providedIn: 'root',
})
export class ContextService {
export class ContextOrganizationService {
organization$: Observable<Organization> = concat(
this.organizationsService.getContext().pipe(
map(({ organizationId }) => organizationId),
catchError((err) => {
if (err instanceof HttpErrorResponse && err.status === 404)
return this.organizationsService.listOrgMembership({ limit: 1 }).pipe(
map(({ result }) => result[0].id),
tap((id) => this.switchOrganization(id)),
switchMap(({ result }) => (result[0] ? of(result[0]) : this.createOrganization())),
tap(({ id }) => this.switchOrganization(id)),
switchMap(() => EMPTY)
);
console.error(err);
@ -42,6 +43,7 @@ export class ContextService {
shareReplay(1)
);
member$ = combineLatest([this.organization$, this.keycloakTokenInfoService.userID$]).pipe(
filter(([org, userId]) => !isNil(org) && !isNil(userId)),
switchMap(([{ id: orgId }, userId]) =>
this.membersService.getOrgMember({ orgId, userId }).pipe(
catchError((error) => {
@ -73,4 +75,10 @@ export class ContextService {
switchOrganization(organizationId: string): void {
this.switchOrganization$.next(organizationId);
}
private createOrganization(): Observable<Organization> {
return this.organizationsService.createOrg({
organization: { name: DEFAULT_ORGANIZATION_NAME } as Organization,
});
}
}

View File

@ -0,0 +1 @@
export * from './context-organization.service';

View File

@ -1 +0,0 @@
export * from './context.service';

View File

@ -9,4 +9,5 @@ export * from './sections-links';
export * from './query-params';
export * from './id-generator';
export * from './partial-fetcher';
export * from './context';
export * from './context-organization';
export * from './shops-data';

View File

@ -4,7 +4,7 @@ import { combineLatest, defer, Observable, ReplaySubject } from 'rxjs';
import { map, pluck, shareReplay, switchMap } from 'rxjs/operators';
import { MembersService } from '@dsh/api/organizations';
import { ContextService } from '@dsh/app/shared';
import { ContextOrganizationService } from '@dsh/app/shared';
import { Initializable } from '@dsh/app/shared/types';
import { SHARE_REPLAY_CONF } from '@dsh/operators';
@ -16,12 +16,12 @@ export class OrganizationManagementService implements Initializable {
shareReplay(SHARE_REPLAY_CONF)
);
isOrganizationOwner$: Observable<boolean> = defer(() =>
combineLatest([this.organization$, this.contextService.organization$.pipe(pluck('party'))])
combineLatest([this.organization$, this.contextOrganizationService.organization$.pipe(pluck('party'))])
).pipe(
map(([{ owner }, id]) => owner === id),
shareReplay(SHARE_REPLAY_CONF)
);
isOrganizationAdmin$: Observable<boolean> = this.contextService.member$.pipe(
isOrganizationAdmin$: Observable<boolean> = this.contextOrganizationService.member$.pipe(
map((member) => member.roles.findIndex((r) => r.roleId === RoleId.Administrator) !== -1),
shareReplay(SHARE_REPLAY_CONF)
);
@ -34,7 +34,10 @@ export class OrganizationManagementService implements Initializable {
private organization$ = new ReplaySubject<Organization>();
constructor(private membersService: MembersService, private contextService: ContextService) {}
constructor(
private membersService: MembersService,
private contextOrganizationService: ContextOrganizationService
) {}
init(organization: Organization) {
this.organization$.next(organization);

View File

@ -31,7 +31,7 @@ export class ShopContractDetailsService {
tap(() => this._isLoading$.next(true)),
switchMap((contractID) =>
contractID
? this.contractsService.getContractByID({ contractID }).pipe(
? this.contractsService.getContractByIDForParty({ contractID }).pipe(
catchError((e) => {
console.error(e);
this.error$.next(true);

View File

@ -0,0 +1 @@
export * from './shops-data.service';

View File

@ -0,0 +1,63 @@
import { Injectable } from '@angular/core';
import { Shop } from '@vality/swag-payments';
import { Observable, Subject, of, repeat } from 'rxjs';
import { startWith, switchMap, takeWhile } from 'rxjs/operators';
import { createTestShopClaimChangeset, ClaimsService } from '@dsh/api/claim-management';
import { ShopsService } from '@dsh/api/payments';
import { ContextOrganizationService } from '@dsh/app/shared';
import { shareReplayRefCount } from '@dsh/operators';
import { IdGeneratorService } from '../id-generator';
@Injectable({
providedIn: 'root',
})
export class ShopsDataService {
shops$: Observable<Shop[]> = this.contextOrganizationService.organization$.pipe(
switchMap(() => this.reloadShops$),
startWith(this.shopsService.getShopsForParty()),
switchMap((shops$) => shops$),
switchMap((shops) => (shops.length ? of(shops) : this.createTestShop())),
shareReplayRefCount()
);
private reloadShops$ = new Subject<Observable<Shop[]>>();
constructor(
private shopsService: ShopsService,
private contextOrganizationService: ContextOrganizationService,
private idGenerator: IdGeneratorService,
private claimsService: ClaimsService
) {}
reloadShops(): Observable<Shop[]> {
const shop$ = this.shopsService.getShopsForParty();
this.reloadShops$.next(shop$);
return shop$;
}
private createTestShop(): Observable<Shop[]> {
return this.claimsService
.searchClaims({ limit: 1 })
.pipe(
switchMap((claims) =>
claims.result.length
? of(claims.result[0])
: this.claimsService.createClaim({
changeset: createTestShopClaimChangeset(
this.idGenerator.uuid(),
this.idGenerator.uuid(),
this.idGenerator.uuid(),
this.idGenerator.uuid()
),
})
)
)
.pipe(
switchMap(() => this.shopsService.getShopsForParty()),
repeat({ count: 2, delay: 1000 }),
takeWhile((shops) => !shops.length)
);
}
}

View File

@ -33,11 +33,6 @@
"emptySearchResult": {
"empty": "No data available for this period"
},
"fileUploader": {
"choose": "select",
"forUpload": "To upload, move the files to the area, or",
"uploading": "Loading..."
},
"humanizeDuration": {
"ago": "ago",
"justNow": "just now",

View File

@ -33,11 +33,6 @@
"emptySearchResult": {
"empty": "Данные за указанный период отсутствуют"
},
"fileUploader": {
"choose": "выберите",
"forUpload": "Для загрузки переместите файлы в область, или",
"uploading": "Загрузка..."
},
"humanizeDuration": {
"ago": "назад",
"justNow": "только что",

View File

@ -93,13 +93,6 @@
"revoked": "Revoked"
}
},
"darkApi": {
"refundStatus": {
"failed": "Failed",
"pending": "Pending",
"succeeded": "Succeeded"
}
},
"organizations": {
"resourceScopeId": {
"Shop": "Shops"

View File

@ -93,13 +93,6 @@
"revoked": "Отозвана"
}
},
"darkApi": {
"refundStatus": {
"failed": "Неуспешен",
"pending": "Запущен",
"succeeded": "Успешен"
}
},
"organizations": {
"resourceScopeId": {
"Shop": "Магазины"

View File

@ -1,21 +0,0 @@
@use '@angular/material' as mat;
@mixin dsh-file-uploader-theme($theme) {
$primary: map-get($theme, primary);
$foreground: map-get($theme, foreground);
.dsh-file-uploader {
&-container {
border-color: map-get($foreground, border);
&-dragover {
border-color: mat.get-color-from-palette($primary, default);
transition: ease 1s;
}
}
&-choose-files {
color: mat.get-color-from-palette($primary, default);
}
}
}

View File

@ -1,21 +0,0 @@
<div
ngfDrop
(dragenter)="setDragover(true)"
(dragleave)="setDragover(false)"
(filesChange)="startUploading($event)"
class="dsh-file-uploader-container"
[class.dsh-file-uploader-container-dragover]="isDragover"
fxFlex
fxLayoutAlign="center"
>
<div *transloco="let t; scope: 'core-components'; read: 'coreComponents.fileUploader'">
<div *ngIf="!(isUploading$ | async); else uploading">
{{ t('forUpload') }}
<span ngfSelect (filesChange)="startUploading($event)" class="dsh-file-uploader-choose-files">{{
t('choose')
}}</span
>.
</div>
<ng-template #uploading>{{ t('uploading') }}</ng-template>
</div>
</div>

View File

@ -1,17 +0,0 @@
.dsh-file-uploader {
&-container {
border: 2px dashed;
padding: 15px 0;
box-sizing: border-box;
border-radius: 4px;
&-dragover {
border: 2px solid;
transition: ease 1s;
}
}
&-choose-files {
cursor: pointer;
}
}

View File

@ -1,31 +0,0 @@
import { Component, EventEmitter, HostBinding, Output } from '@angular/core';
import { FileUploaderService } from './file-uploader.service';
@Component({
selector: 'dsh-file-uploader',
templateUrl: 'file-uploader.component.html',
styleUrls: ['file-uploader.component.scss'],
})
export class FileUploaderComponent {
@Output()
filesUploaded = new EventEmitter<string[]>();
@HostBinding('class.dsh-file-uploader-container')
isDragover = false;
startUploading$ = this.fileUploaderService.startUploading$;
isUploading$ = this.fileUploaderService.isUploading$;
constructor(private fileUploaderService: FileUploaderService) {
this.fileUploaderService.filesUploaded$.subscribe((value) => this.filesUploaded.emit(value));
}
setDragover(value: boolean) {
this.isDragover = value;
}
startUploading(files: File[]) {
this.startUploading$.next(files);
}
}

View File

@ -1,16 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexModule } from '@angular/flex-layout';
import { TranslocoModule } from '@ngneat/transloco';
import { ngfModule } from 'angular-file';
import { FileUploaderComponent } from './file-uploader.component';
import { FileUploaderService } from './file-uploader.service';
@NgModule({
imports: [TranslocoModule, FlexModule, ngfModule, CommonModule],
exports: [FileUploaderComponent],
declarations: [FileUploaderComponent],
providers: [FileUploaderService],
})
export class FileUploaderModule {}

View File

@ -1,40 +0,0 @@
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslocoService } from '@ngneat/transloco';
import { merge, of, Subject } from 'rxjs';
import { catchError, filter, shareReplay, switchMap } from 'rxjs/operators';
import { FilesService } from '@dsh/api/dark-api';
import { progress } from '@dsh/operators';
@Injectable()
export class FileUploaderService {
startUploading$ = new Subject<File[]>();
filesUploadingError$ = new Subject<null>();
filesUploaded$ = this.startUploading$.pipe(
switchMap((files) =>
this.filesService.uploadFiles(files).pipe(
catchError(() => {
this.filesUploadingError$.next(null);
return of([]);
})
)
),
filter((v) => !!v.length),
shareReplay(1)
);
isUploading$ = progress(this.startUploading$, merge(this.filesUploaded$, this.filesUploadingError$));
constructor(
private filesService: FilesService,
private snackBar: MatSnackBar,
private transloco: TranslocoService
) {
this.filesUploadingError$.subscribe(() =>
this.snackBar.open(this.transloco.translate('shared.commonError', null, 'components'), 'OK')
);
}
}

View File

@ -1,2 +0,0 @@
export * from './file-uploader.module';
export * from './file-uploader.component';

View File

@ -1,10 +1,9 @@
import { NgModule } from '@angular/core';
import { FileUploaderModule } from './file-uploader';
import { FormatInputModule } from './format-input';
import { SelectSearchFieldModule } from './select-search-field';
const EXPORTED_DECLARATIONS = [FormatInputModule, FileUploaderModule, SelectSearchFieldModule];
const EXPORTED_DECLARATIONS = [FormatInputModule, SelectSearchFieldModule];
@NgModule({
imports: EXPORTED_DECLARATIONS,

View File

@ -3,4 +3,3 @@ export * from './masks';
export * from './validators';
export * from './format-input';
export * from './utils';
export * from './file-uploader';

View File

@ -1,4 +1,3 @@
@import '../../components/form-controls/file-uploader/file-uploader-theme';
@import '../../components/buttons/button/button-theme';
@import '../../components/buttons/button-toggle/button-toggle-theme';
@import '../../components/layout/card/card-theme';
@ -44,7 +43,6 @@
@include dsh-status-theme($theme);
@include dsh-dadata-autocomplete-theme($theme);
@include dsh-details-item-theme($theme);
@include dsh-file-uploader-theme($theme);
@include dsh-panel-theme($theme);
@include dsh-bar-chart-theme($theme);
@include dsh-percent-difference-theme($theme);