TD-372: Organization fixes; add BDT currency; update swag payments (#84)

This commit is contained in:
Rinat Arsaev 2022-09-06 15:56:35 +03:00 committed by GitHub
parent 418b418cad
commit 540d2706fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 139 additions and 124 deletions

3
.gitignore vendored
View File

@ -47,3 +47,6 @@ Thumbs.db
# Angular # Angular
.angular .angular
# Configs
src/*Config.json

12
.run/Dev.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dev" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="start" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -6,31 +6,53 @@
- [Angular Material](https://material.angular.io/) - [Angular Material](https://material.angular.io/)
- [Prettier](https://prettier.io/) - [Prettier](https://prettier.io/)
## Initialization ## Guides
### Install packages - [Using typography](https://material.angular.io/guide/typography)
- [Theming your components](https://material.angular.io/guide/theming-your-components)
## Installation
1. Add environment and configurations:
- `src/appConfig.json`:
```json
{
"apiEndpoint": "https://api.xample.com",
"urlShortenerEndpoint": "https://shrt.example.com",
"checkoutEndpoint": "https://checkout.example.com",
"docsEndpoints": {
"payments": "https://example.com/docs"
},
"theme": {
"name": "main"
},
"sentryDsn": "https://public@sentry.example.com/1",
"keycloakEndpoint": "https://auth.example.com",
"fileStorageEndpoint": "https://fs.example.com"
}
```
- `src/authConfig.json`:
```json
{
"realm": "external",
"auth-server-url": "https://auth.example.com/auth/",
"ssl-required": "external",
"resource": "koffing",
"public-client": true
}
```
2. Install packages
```sh ```sh
npm ci npm ci
``` ```
## Development server ## Development server
- API (Production API default) 1. `npm start`
- With mocks:
Change `./src/appConfig.json` API endpoints
1. Start
- Real Keycloak: `npm start`
- Stub Keycloak
1. Change `./src/authConfig.json` / `"auth-server-url"`:
1. `npm run stub`
1. Navigate to `http://localhost:8000/` 1. Navigate to `http://localhost:8000/`
@ -51,8 +73,3 @@ npm ci
npm run build -- --prod --stats-json --extraWebpackConfig webpack.extra.js npm run build -- --prod --stats-json --extraWebpackConfig webpack.extra.js
npx webpack-bundle-analyzer dist/stats.json npx webpack-bundle-analyzer dist/stats.json
``` ```
## Guides
- [Using typography](https://material.angular.io/guide/typography)
- [Theming your components](https://material.angular.io/guide/theming-your-components)

14
package-lock.json generated
View File

@ -36,7 +36,7 @@
"@vality/swag-dark-api": "0.1.1-a3f1678.0", "@vality/swag-dark-api": "0.1.1-a3f1678.0",
"@vality/swag-messages": "1.0.1-03e1014.0", "@vality/swag-messages": "1.0.1-03e1014.0",
"@vality/swag-organizations": "1.0.1-cd6cc10.0", "@vality/swag-organizations": "1.0.1-cd6cc10.0",
"@vality/swag-payments": "0.1.1-4550cf2.0", "@vality/swag-payments": "0.1.1-37e6f46.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0", "@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0",
"@vality/swag-url-shortener": "0.1.1-f780d07.0", "@vality/swag-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0", "@vality/swag-wallet": "0.1.1-9a236df.0",
@ -5040,9 +5040,9 @@
} }
}, },
"node_modules/@vality/swag-payments": { "node_modules/@vality/swag-payments": {
"version": "0.1.1-4550cf2.0", "version": "0.1.1-37e6f46.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-4550cf2.0.tgz", "resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-37e6f46.0.tgz",
"integrity": "sha512-UgWAkzaemcqMLJhSxwZGOeZ2v+UE7fonNTQDg/gh7FwS7kI7QWCIdIGyAEFXYEtnKRJDBWPbYXekmvLJP1kGmw==", "integrity": "sha512-mnINBuklAgRzkx/Q9RboScrdl2TZlXrHEywDZq4AaJFznoJLfR1yNrqjxh9g7SrNJfoTh+qbuJjxPgd+5oG7Tg==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -21330,9 +21330,9 @@
} }
}, },
"@vality/swag-payments": { "@vality/swag-payments": {
"version": "0.1.1-4550cf2.0", "version": "0.1.1-37e6f46.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-4550cf2.0.tgz", "resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-37e6f46.0.tgz",
"integrity": "sha512-UgWAkzaemcqMLJhSxwZGOeZ2v+UE7fonNTQDg/gh7FwS7kI7QWCIdIGyAEFXYEtnKRJDBWPbYXekmvLJP1kGmw==", "integrity": "sha512-mnINBuklAgRzkx/Q9RboScrdl2TZlXrHEywDZq4AaJFznoJLfR1yNrqjxh9g7SrNJfoTh+qbuJjxPgd+5oG7Tg==",
"requires": { "requires": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
} }

View File

@ -54,7 +54,7 @@
"@vality/swag-dark-api": "0.1.1-a3f1678.0", "@vality/swag-dark-api": "0.1.1-a3f1678.0",
"@vality/swag-messages": "1.0.1-03e1014.0", "@vality/swag-messages": "1.0.1-03e1014.0",
"@vality/swag-organizations": "1.0.1-cd6cc10.0", "@vality/swag-organizations": "1.0.1-cd6cc10.0",
"@vality/swag-payments": "0.1.1-4550cf2.0", "@vality/swag-payments": "0.1.1-37e6f46.0",
"@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0", "@vality/swag-questionary-aggr-proxy": "0.1.1-1dc5add.0",
"@vality/swag-url-shortener": "0.1.1-f780d07.0", "@vality/swag-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0", "@vality/swag-wallet": "0.1.1-9a236df.0",

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco'; import { TranslocoService } from '@ngneat/transloco';
import { StatusOffsetCount, InvoiceStatus, Report, RefundStatus } from '@vality/swag-anapi-v2'; import { InvoiceStatus, Report, RefundStatus, PaymentSearchResult } from '@vality/swag-anapi-v2';
import { BankCardPaymentSystem } from '@vality/swag-anapi-v2/lib/model/bankCardPaymentSystem'; import { BankCardPaymentSystem } from '@vality/swag-anapi-v2/lib/model/bankCardPaymentSystem';
import { BankCardTokenProvider } from '@vality/swag-anapi-v2/lib/model/bankCardTokenProvider'; import { BankCardTokenProvider } from '@vality/swag-anapi-v2/lib/model/bankCardTokenProvider';
@ -44,13 +44,14 @@ export class AnapiDictionaryService {
other: this.t.translate('anapi.errorCode.other', null, 'dictionary'), other: this.t.translate('anapi.errorCode.other', null, 'dictionary'),
})); }));
paymentStatus$ = this.dictionaryService.create<StatusOffsetCount.StatusEnum>(() => ({ paymentStatus$ = this.dictionaryService.create<PaymentSearchResult.StatusEnum>(() => ({
pending: this.t.translate('anapi.paymentStatus.pending', null, 'dictionary'), pending: this.t.translate('anapi.paymentStatus.pending', null, 'dictionary'),
processed: this.t.translate('anapi.paymentStatus.processed', null, 'dictionary'), processed: this.t.translate('anapi.paymentStatus.processed', null, 'dictionary'),
captured: this.t.translate('anapi.paymentStatus.captured', null, 'dictionary'), captured: this.t.translate('anapi.paymentStatus.captured', null, 'dictionary'),
cancelled: this.t.translate('anapi.paymentStatus.cancelled', null, 'dictionary'), cancelled: this.t.translate('anapi.paymentStatus.cancelled', null, 'dictionary'),
refunded: this.t.translate('anapi.paymentStatus.refunded', null, 'dictionary'), refunded: this.t.translate('anapi.paymentStatus.refunded', null, 'dictionary'),
failed: this.t.translate('anapi.paymentStatus.failed', null, 'dictionary'), failed: this.t.translate('anapi.paymentStatus.failed', null, 'dictionary'),
chargedback: this.t.translate('anapi.paymentStatus.chargedback', null, 'dictionary'),
})); }));
invoiceStatus$ = this.dictionaryService.create<InvoiceStatus.StatusEnum>(() => ({ invoiceStatus$ = this.dictionaryService.create<InvoiceStatus.StatusEnum>(() => ({

View File

@ -2,7 +2,7 @@ import { HttpResponse, HttpHeaders } from '@angular/common/http';
import { Injectable, Injector } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { Observable, combineLatest, isObservable, of } from 'rxjs'; import { Observable, combineLatest, isObservable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { RequiredKeys, UnionToIntersection, Overwrite } from 'utility-types'; import { RequiredKeys, UnionToIntersection, Overwrite, ReadonlyKeys } from 'utility-types';
import { ApiExtension } from './utils/api-extension'; import { ApiExtension } from './utils/api-extension';
import { getMethods } from './utils/get-methods'; import { getMethods } from './utils/get-methods';
@ -11,13 +11,19 @@ import { XrequestIdExtension } from './utils/x-request-id-extension';
const DEFAULT_HEADERS = new HttpHeaders({ 'Content-Type': 'application/json; charset=utf-8' }); const DEFAULT_HEADERS = new HttpHeaders({ 'Content-Type': 'application/json; charset=utf-8' });
// Exclude readonly keys
type DeepOnlyMutable<T> = T extends object
? {
[P in keyof T]: T[P] extends object ? Omit<T[P], ReadonlyKeys<T[P]>> : T[P];
}
: T;
type ApiArgs = [Injector]; type ApiArgs = [Injector];
type MethodParams<P extends Record<PropertyKey, any>, K extends PropertyKey> = RequiredKeys<Omit<P, K>> extends never type MethodParams<P extends Record<PropertyKey, any>, K extends PropertyKey> = RequiredKeys<Omit<P, K>> extends never
? void | Overwrite<P, { [N in K]?: P[N] }> ? void | Overwrite<P, { [N in K]?: P[N] }>
: Overwrite<P, { [N in K]?: P[N] }>; : Overwrite<P, { [N in K]?: P[N] }>;
type Method<M, P extends PropertyKey> = M extends (...args: unknown[]) => Observable<HttpResponse<infer R>> type Method<M, P extends PropertyKey> = M extends (...args: unknown[]) => Observable<HttpResponse<infer R>>
? (params: MethodParams<Parameters<M>[0], P>) => Observable<R> ? (params: DeepOnlyMutable<MethodParams<Parameters<M>[0], P>>) => Observable<R>
: never; : never;
/** /**

View File

@ -1,8 +1,6 @@
import { getBaseClass } from '@dsh/utils'; import { getBaseClass } from '@dsh/utils';
import type AppConfigJson from '../../appConfig.json'; export interface Config {
interface AppConfig {
apiEndpoint: string; apiEndpoint: string;
urlShortenerEndpoint: string; urlShortenerEndpoint: string;
checkoutEndpoint: string; checkoutEndpoint: string;
@ -16,6 +14,4 @@ interface AppConfig {
keycloakEndpoint: string; keycloakEndpoint: string;
fileStorageEndpoint: string; fileStorageEndpoint: string;
} }
export type Config = typeof AppConfigJson extends AppConfig ? AppConfig : never;
export const BASE_CONFIG = getBaseClass<Config>(); export const BASE_CONFIG = getBaseClass<Config>();

View File

@ -0,0 +1,23 @@
import { PaymentSearchResult } from '@vality/swag-anapi-v2';
import { StatusColor as Color } from '../theme-manager';
export const getPaymentStatusColor = (status: PaymentSearchResult.StatusEnum): Color => {
const statusEnum = PaymentSearchResult.StatusEnum;
switch (status) {
case statusEnum.Processed:
return Color.Success;
case statusEnum.Failed:
return Color.Warn;
case statusEnum.Refunded:
return Color.Neutral;
case statusEnum.Cancelled:
return Color.Warn;
case statusEnum.Captured:
return Color.Success;
case statusEnum.Pending:
return Color.Pending;
case statusEnum.Chargedback:
return Color.Neutral;
}
};

View File

@ -1,26 +0,0 @@
import { PaymentStatus } from '@vality/swag-payments';
import { StatusColor as Color } from '../theme-manager';
export interface PaymentStatusInfo {
color: Color;
status: string;
}
export const getPaymentStatusInfo = (status: PaymentStatus.StatusEnum): PaymentStatusInfo => {
const statusEnum = PaymentStatus.StatusEnum;
switch (status) {
case statusEnum.Processed:
return { color: Color.Success, status: 'processed' };
case statusEnum.Failed:
return { color: Color.Warn, status: 'failed' };
case statusEnum.Refunded:
return { color: Color.Neutral, status: 'refunded' };
case statusEnum.Cancelled:
return { color: Color.Warn, status: 'cancelled' };
case statusEnum.Captured:
return { color: Color.Success, status: 'captured' };
case statusEnum.Pending:
return { color: Color.Pending, status: 'pending' };
}
};

View File

@ -17,7 +17,12 @@
</div> </div>
</div> </div>
<ng-container dshBaseDialogActions> <ng-container dshBaseDialogActions>
<button dsh-button color="accent" [disabled]="emailControl.invalid || !selectedRoles.length" (click)="create()"> <button
dsh-button
color="accent"
[disabled]="emailControl.invalid || !selectedRoles.length || (inProgress$ | async)"
(click)="create()"
>
{{ t('send') }} {{ t('send') }}
</button> </button>
</ng-container> </ng-container>

View File

@ -16,6 +16,10 @@ const ORGANIZATION_SECTION_ROUTES: Routes = [
path: 'organizations/:orgId', path: 'organizations/:orgId',
loadChildren: () => import('./organization-details').then((m) => m.OrganizationDetailsModule), loadChildren: () => import('./organization-details').then((m) => m.OrganizationDetailsModule),
}, },
{
path: 'accept-invitation',
loadChildren: () => import('./accept-invitation').then((m) => m.AcceptInvitationModule),
},
{ {
path: '', path: '',
redirectTo: 'organizations', redirectTo: 'organizations',

View File

@ -2,10 +2,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { FormBuilder } from '@ngneat/reactive-forms'; import { FormBuilder } from '@ngneat/reactive-forms';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Organization } from '@vality/swag-organizations'; import { BehaviorSubject, switchMap } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import { OrgsService } from '@dsh/api/organizations'; import { OrgsService } from '@dsh/api/organizations';
import { KeycloakTokenInfoService } from '@dsh/app/shared';
import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog'; import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog';
import { ErrorService } from '@dsh/app/shared/services/error'; import { ErrorService } from '@dsh/app/shared/services/error';
import { NotificationService } from '@dsh/app/shared/services/notification'; import { NotificationService } from '@dsh/app/shared/services/notification';
@ -26,18 +26,24 @@ export class CreateOrganizationDialogComponent {
private organizationsService: OrgsService, private organizationsService: OrgsService,
private notificationService: NotificationService, private notificationService: NotificationService,
private errorService: ErrorService, private errorService: ErrorService,
private fb: FormBuilder private fb: FormBuilder,
private keycloakTokenInfoService: KeycloakTokenInfoService
) {} ) {}
@inProgressTo('inProgress$') @inProgressTo('inProgress$')
create() { create() {
return this.organizationsService return this.keycloakTokenInfoService.partyID$
.createOrg({ .pipe(
switchMap((owner) =>
this.organizationsService.createOrg({
organization: { organization: {
name: this.form.value.name, name: this.form.value.name,
} as Organization, owner,
},
}) })
.pipe(untilDestroyed(this)) ),
untilDestroyed(this)
)
.subscribe( .subscribe(
() => { () => {
this.notificationService.success(); this.notificationService.success();

View File

@ -4,19 +4,10 @@ import { RouterModule, Routes } from '@angular/router';
import { OrganizationsComponent } from './organizations.component'; import { OrganizationsComponent } from './organizations.component';
export const ROUTES: Routes = [ export const ROUTES: Routes = [
{
path: '',
children: [
{ {
path: '', path: '',
component: OrganizationsComponent, component: OrganizationsComponent,
}, },
{
path: 'accept-invitation',
loadChildren: () => import('./accept-invitation').then((m) => m.AcceptInvitationModule),
},
],
},
]; ];
@NgModule({ @NgModule({

View File

@ -1,7 +1,7 @@
<ng-container> <ng-container>
<ng-container *transloco="let t; scope: 'payment-section'; read: 'paymentSection.paymentDetails.statusDetailItem'"> <ng-container *transloco="let t; scope: 'payment-section'; read: 'paymentSection.paymentDetails.statusDetailItem'">
<dsh-details-item [title]="t('status')"> <dsh-details-item [title]="t('status')">
<dsh-status [color]="paymentColor">{{ (paymentStatusDict$ | async)?.[paymentStatus] }}</dsh-status> <dsh-status [color]="paymentColor">{{ (paymentStatusDict$ | async)?.[status] }}</dsh-status>
</dsh-details-item> </dsh-details-item>
</ng-container> </ng-container>
</ng-container> </ng-container>

View File

@ -1,13 +1,13 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { PaymentStatus } from '@vality/swag-payments'; import { PaymentSearchResult } from '@vality/swag-anapi-v2';
import isNil from 'lodash-es/isNil'; import isNil from 'lodash-es/isNil';
import isObject from 'lodash-es/isObject'; import isObject from 'lodash-es/isObject';
import { PaymentsDictionaryService } from '@dsh/api/payments'; import { AnapiDictionaryService } from '@dsh/api/anapi';
import { ComponentChange, ComponentChanges } from '@dsh/type-utils'; import { ComponentChange, ComponentChanges } from '@dsh/type-utils';
import { StatusColor } from '../../../../../../../../../theme-manager'; import { StatusColor } from '../../../../../../../../../theme-manager';
import { getPaymentStatusInfo } from '../../../../../../../../get-payment-status-info'; import { getPaymentStatusColor } from '../../../../../../../../get-payment-status-color';
@Component({ @Component({
selector: 'dsh-payment-status', selector: 'dsh-payment-status',
@ -15,13 +15,12 @@ import { getPaymentStatusInfo } from '../../../../../../../../get-payment-status
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class PaymentStatusComponent implements OnChanges { export class PaymentStatusComponent implements OnChanges {
@Input() status: PaymentStatus.StatusEnum; @Input() status: PaymentSearchResult.StatusEnum;
paymentColor: StatusColor; paymentColor: StatusColor;
paymentStatus: string; paymentStatusDict$ = this.anapiDictionaryService.paymentStatus$;
paymentStatusDict$ = this.paymentsDictionaryService.paymentStatus$;
constructor(private paymentsDictionaryService: PaymentsDictionaryService) {} constructor(private anapiDictionaryService: AnapiDictionaryService) {}
ngOnChanges(changes: ComponentChanges<PaymentStatusComponent>): void { ngOnChanges(changes: ComponentChanges<PaymentStatusComponent>): void {
if (isObject(changes.status)) { if (isObject(changes.status)) {
@ -33,8 +32,6 @@ export class PaymentStatusComponent implements OnChanges {
if (isNil(paymentStatus)) { if (isNil(paymentStatus)) {
return; return;
} }
const { status, color } = getPaymentStatusInfo(paymentStatus); this.paymentColor = getPaymentStatusColor(paymentStatus);
this.paymentColor = color;
this.paymentStatus = status;
} }
} }

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
import { provideValueAccessor } from '@s-libs/ng-core'; import { provideValueAccessor } from '@s-libs/ng-core';
import { Claim } from '@vality/swag-payments'; import { Claim } from '@vality/swag-claim-management';
import { FilterSuperclass } from '@dsh/components/filter'; import { FilterSuperclass } from '@dsh/components/filter';

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core'; import { provideValueAccessor, WrappedFormControlSuperclass } from '@s-libs/ng-core';
import { Claim } from '@vality/swag-payments'; import { Claim } from '@vality/swag-claim-management';
@Component({ @Component({
selector: 'dsh-claim-field', selector: 'dsh-claim-field',

View File

@ -14,7 +14,7 @@ export class CurrencyAutocompleteFieldComponent extends WrappedFormControlSuperc
@Input() label: string; @Input() label: string;
@Input() @coerceBoolean required = false; @Input() @coerceBoolean required = false;
options: Option<string>[] = ['RUB', 'USD', 'EUR', 'UAH', 'KZT', 'BYN', 'JPY', 'INR', 'AZN', 'BRL'] options: Option<string>[] = ['RUB', 'USD', 'EUR', 'UAH', 'KZT', 'BYN', 'JPY', 'INR', 'AZN', 'BRL', 'BDT']
.sort() .sort()
.map((currency) => ({ label: currency, value: currency })); .map((currency) => ({ label: currency, value: currency }));

View File

@ -1,14 +0,0 @@
{
"apiEndpoint": "https://api.rbk.money",
"urlShortenerEndpoint": "https://rbk.mn",
"checkoutEndpoint": "https://checkout.rbk.money",
"docsEndpoints": {
"payments": "https://developer.rbk.money/api"
},
"theme": {
"name": "main"
},
"sentryDsn": null,
"keycloakEndpoint": "https://auth.rbk.money",
"fileStorageEndpoint": "https://storage.rbk.money/files"
}

View File

@ -56,6 +56,7 @@
"paymentStatus": { "paymentStatus": {
"cancelled": "Отменен", "cancelled": "Отменен",
"captured": "Подтвержден", "captured": "Подтвержден",
"chargedback": "Chargedback",
"failed": "Неуспешен", "failed": "Неуспешен",
"pending": "Запущен", "pending": "Запущен",
"processed": "Обработан", "processed": "Обработан",

View File

@ -1,7 +0,0 @@
{
"realm": "external",
"auth-server-url": "https://auth.rbk.money/auth/",
"ssl-required": "external",
"resource": "koffing",
"public-client": true
}