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
# 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/)
- [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
npm ci
```
## Development server
- API (Production API default)
- 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. `npm start`
1. Navigate to `http://localhost:8000/`
@ -51,8 +73,3 @@ npm ci
npm run build -- --prod --stats-json --extraWebpackConfig webpack.extra.js
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-messages": "1.0.1-03e1014.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-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0",
@ -5040,9 +5040,9 @@
}
},
"node_modules/@vality/swag-payments": {
"version": "0.1.1-4550cf2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-4550cf2.0.tgz",
"integrity": "sha512-UgWAkzaemcqMLJhSxwZGOeZ2v+UE7fonNTQDg/gh7FwS7kI7QWCIdIGyAEFXYEtnKRJDBWPbYXekmvLJP1kGmw==",
"version": "0.1.1-37e6f46.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-37e6f46.0.tgz",
"integrity": "sha512-mnINBuklAgRzkx/Q9RboScrdl2TZlXrHEywDZq4AaJFznoJLfR1yNrqjxh9g7SrNJfoTh+qbuJjxPgd+5oG7Tg==",
"dependencies": {
"tslib": "^2.3.0"
},
@ -21330,9 +21330,9 @@
}
},
"@vality/swag-payments": {
"version": "0.1.1-4550cf2.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-4550cf2.0.tgz",
"integrity": "sha512-UgWAkzaemcqMLJhSxwZGOeZ2v+UE7fonNTQDg/gh7FwS7kI7QWCIdIGyAEFXYEtnKRJDBWPbYXekmvLJP1kGmw==",
"version": "0.1.1-37e6f46.0",
"resolved": "https://registry.npmjs.org/@vality/swag-payments/-/swag-payments-0.1.1-37e6f46.0.tgz",
"integrity": "sha512-mnINBuklAgRzkx/Q9RboScrdl2TZlXrHEywDZq4AaJFznoJLfR1yNrqjxh9g7SrNJfoTh+qbuJjxPgd+5oG7Tg==",
"requires": {
"tslib": "^2.3.0"
}

View File

@ -54,7 +54,7 @@
"@vality/swag-dark-api": "0.1.1-a3f1678.0",
"@vality/swag-messages": "1.0.1-03e1014.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-url-shortener": "0.1.1-f780d07.0",
"@vality/swag-wallet": "0.1.1-9a236df.0",

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
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 { 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'),
}));
paymentStatus$ = this.dictionaryService.create<StatusOffsetCount.StatusEnum>(() => ({
paymentStatus$ = this.dictionaryService.create<PaymentSearchResult.StatusEnum>(() => ({
pending: this.t.translate('anapi.paymentStatus.pending', null, 'dictionary'),
processed: this.t.translate('anapi.paymentStatus.processed', null, 'dictionary'),
captured: this.t.translate('anapi.paymentStatus.captured', null, 'dictionary'),
cancelled: this.t.translate('anapi.paymentStatus.cancelled', null, 'dictionary'),
refunded: this.t.translate('anapi.paymentStatus.refunded', 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>(() => ({

View File

@ -2,7 +2,7 @@ import { HttpResponse, HttpHeaders } from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { Observable, combineLatest, isObservable, of } from 'rxjs';
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 { 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' });
// 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 MethodParams<P extends Record<PropertyKey, any>, K extends PropertyKey> = RequiredKeys<Omit<P, K>> extends never
? void | 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>>
? (params: MethodParams<Parameters<M>[0], P>) => Observable<R>
? (params: DeepOnlyMutable<MethodParams<Parameters<M>[0], P>>) => Observable<R>
: never;
/**

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<ng-container>
<ng-container *transloco="let t; scope: 'payment-section'; read: 'paymentSection.paymentDetails.statusDetailItem'">
<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>
</ng-container>
</ng-container>

View File

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

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Injector } from '@angular/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';

View File

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

View File

@ -14,7 +14,7 @@ export class CurrencyAutocompleteFieldComponent extends WrappedFormControlSuperc
@Input() label: string;
@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()
.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": {
"cancelled": "Отменен",
"captured": "Подтвержден",
"chargedback": "Chargedback",
"failed": "Неуспешен",
"pending": "Запущен",
"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
}