mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-32: New predicates in payment routing rules (#91)
This commit is contained in:
parent
53ac6091e8
commit
ed44c4c608
@ -1,6 +0,0 @@
|
||||
{
|
||||
"include": "node_modules/@rbkmoney/angular-templates/**",
|
||||
"template": {
|
||||
"prefix": "cc"
|
||||
}
|
||||
}
|
@ -34,7 +34,6 @@ import { PaymentAdjustmentModule } from './sections/payment-adjustment/payment-a
|
||||
import { PayoutsModule } from './sections/payouts';
|
||||
import { SearchClaimsModule } from './sections/search-claims/search-claims.module';
|
||||
import { SearchPartiesModule } from './sections/search-parties/search-parties.module';
|
||||
import { SettingsModule } from './settings';
|
||||
import { ThemeManager, ThemeManagerModule, ThemeName } from './theme-manager';
|
||||
import {
|
||||
DEFAULT_DIALOG_CONFIG,
|
||||
@ -69,7 +68,6 @@ moment.locale('en');
|
||||
DomainModule,
|
||||
RepairingModule,
|
||||
ThemeManagerModule,
|
||||
SettingsModule,
|
||||
PartyModule,
|
||||
SearchPartiesModule,
|
||||
SearchClaimsModule,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<cc-metadata-form
|
||||
[formControl]="control"
|
||||
[metadata]="metadata$ | async"
|
||||
[extensions]="extensions"
|
||||
[extensions]="extensions$ | async"
|
||||
namespace="claim_management"
|
||||
[type]="type"
|
||||
></cc-metadata-form>
|
||||
|
@ -3,19 +3,19 @@ import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { Claim } from '@vality/domain-proto/lib/claim_management';
|
||||
import { Party } from '@vality/domain-proto/lib/domain';
|
||||
import { from } from 'rxjs';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ComponentChanges, MetadataFormExtension } from '@cc/app/shared';
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services/domain-metadata-form-extensions';
|
||||
import { createControlProviders } from '@cc/utils';
|
||||
|
||||
import { DomainStoreService } from '../../../../thrift-services/damsel/domain-store.service';
|
||||
import { createDomainObjectMetadataFormExtension } from './utils/create-domain-object-metadata-form.extension';
|
||||
import { createPartyClaimMetadataFormExtensions } from './utils/create-party-claim-metadata-form-extensions';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-modification-form',
|
||||
templateUrl: './modification-form.component.html',
|
||||
providers: createValidatedAbstractControlProviders(ModificationFormComponent),
|
||||
providers: createControlProviders(ModificationFormComponent),
|
||||
})
|
||||
export class ModificationFormComponent
|
||||
extends WrappedFormControlSuperclass<unknown>
|
||||
@ -26,37 +26,28 @@ export class ModificationFormComponent
|
||||
@Input() type: string;
|
||||
|
||||
metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default));
|
||||
extensions: MetadataFormExtension[];
|
||||
extensions$: Observable<MetadataFormExtension[]>;
|
||||
|
||||
constructor(injector: Injector, private domainStoreService: DomainStoreService) {
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: ComponentChanges<ModificationFormComponent>) {
|
||||
super.ngOnChanges(changes);
|
||||
if (changes.party || changes.claim) {
|
||||
this.extensions = [
|
||||
...createPartyClaimMetadataFormExtensions(this.party, this.claim),
|
||||
...this.createDomainMetadataFormExtensions(),
|
||||
];
|
||||
this.extensions$ = this.domainMetadataFormExtensionsService.extensions$.pipe(
|
||||
map((e) => [
|
||||
...createPartyClaimMetadataFormExtensions(this.party, this.claim),
|
||||
...e,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
}
|
||||
|
||||
private createDomainMetadataFormExtensions(): MetadataFormExtension[] {
|
||||
return [
|
||||
createDomainObjectMetadataFormExtension('ContractTemplateRef', () =>
|
||||
this.domainStoreService.getObjects('contract_template')
|
||||
),
|
||||
createDomainObjectMetadataFormExtension('PaymentInstitutionRef', () =>
|
||||
this.domainStoreService.getObjects('payment_institution')
|
||||
),
|
||||
createDomainObjectMetadataFormExtension('CategoryRef', () =>
|
||||
this.domainStoreService.getObjects('category')
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Change Delegate Ruleset</div>
|
||||
|
||||
<cc-base-dialog title="Change Delegate Ruleset">
|
||||
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field>
|
||||
<mat-label>Delegate Ruleset</mat-label>
|
||||
@ -16,10 +14,9 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button mat-button color="primary" (click)="changeRuleset()" [disabled]="form.invalid">
|
||||
CHANGE RULESET
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { RoutingRulesService } from '../../../thrift-services';
|
||||
|
||||
@UntilDestroy()
|
||||
@ -12,7 +13,13 @@ import { RoutingRulesService } from '../../../thrift-services';
|
||||
templateUrl: 'change-delegate-ruleset-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ChangeDelegateRulesetDialogComponent implements OnInit {
|
||||
export class ChangeDelegateRulesetDialogComponent
|
||||
extends BaseDialogSuperclass<
|
||||
ChangeDelegateRulesetDialogComponent,
|
||||
{ mainRulesetRefID: number; delegateIdx: number }
|
||||
>
|
||||
implements OnInit
|
||||
{
|
||||
form = this.fb.group({
|
||||
rulesetRefId: [],
|
||||
description: '',
|
||||
@ -21,17 +28,18 @@ export class ChangeDelegateRulesetDialogComponent implements OnInit {
|
||||
rulesets$ = this.routingRulesService.rulesets$;
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private fb: FormBuilder,
|
||||
private dialogRef: MatDialogRef<ChangeDelegateRulesetDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { mainRulesetRefID: number; delegateIdx: number },
|
||||
private routingRulesService: RoutingRulesService
|
||||
) {}
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.routingRulesService
|
||||
.getRuleset(this.data.mainRulesetRefID)
|
||||
.getRuleset(this.dialogData.mainRulesetRefID)
|
||||
.pipe(
|
||||
map((r) => r?.data?.decisions?.delegates?.[this.data?.delegateIdx]),
|
||||
map((r) => r?.data?.decisions?.delegates?.[this.dialogData?.delegateIdx]),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe((delegate) => {
|
||||
@ -42,15 +50,11 @@ export class ChangeDelegateRulesetDialogComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
changeRuleset() {
|
||||
this.routingRulesService
|
||||
.changeDelegateRuleset({
|
||||
mainRulesetRefID: this.data.mainRulesetRefID,
|
||||
delegateIdx: this.data.delegateIdx,
|
||||
mainRulesetRefID: this.dialogData.mainRulesetRefID,
|
||||
delegateIdx: this.dialogData.delegateIdx,
|
||||
newDelegateRulesetRefID: this.form.value.rulesetRefId,
|
||||
description: this.form.value.description,
|
||||
})
|
||||
|
@ -6,6 +6,8 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
|
||||
import { ChangeDelegateRulesetDialogComponent } from './change-delegate-ruleset-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
@ -17,6 +19,7 @@ import { ChangeDelegateRulesetDialogComponent } from './change-delegate-ruleset-
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatSelectModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: [ChangeDelegateRulesetDialogComponent],
|
||||
exports: [ChangeDelegateRulesetDialogComponent],
|
||||
|
@ -1,14 +1,11 @@
|
||||
<div fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Change main ruleset</div>
|
||||
|
||||
<cc-base-dialog title="Change main ruleset">
|
||||
<cc-target-ruleset-form
|
||||
(valueChanges)="targetRuleset$.next($event)"
|
||||
[value]="initValue"
|
||||
(valid)="targetRulesetValid$.next($event)"
|
||||
></cc-target-ruleset-form>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button
|
||||
mat-button
|
||||
color="primary"
|
||||
@ -17,5 +14,5 @@
|
||||
>
|
||||
CHANGE TARGET
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { ErrorService } from '../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../thrift-services';
|
||||
import { TargetRuleset } from '../target-ruleset-form';
|
||||
@ -12,32 +13,36 @@ import { TargetRuleset } from '../target-ruleset-form';
|
||||
templateUrl: 'change-target-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ChangeTargetDialogComponent {
|
||||
export class ChangeTargetDialogComponent extends BaseDialogSuperclass<
|
||||
ChangeTargetDialogComponent,
|
||||
{ mainRulesetRefID: number; delegateIdx: number }
|
||||
> {
|
||||
targetRuleset$ = new BehaviorSubject<TargetRuleset>(undefined);
|
||||
targetRulesetValid$ = new BehaviorSubject<boolean>(undefined);
|
||||
initValue: Partial<TargetRuleset> = {};
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<ChangeTargetDialogComponent>,
|
||||
injector: Injector,
|
||||
private routingRulesService: RoutingRulesService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { mainRulesetRefID: number; delegateIdx: number },
|
||||
private errorService: ErrorService
|
||||
) {
|
||||
super(injector);
|
||||
this.routingRulesService
|
||||
.getRuleset(data?.mainRulesetRefID)
|
||||
.getRuleset(this.dialogData?.mainRulesetRefID)
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe((ruleset) => {
|
||||
this.initValue = {
|
||||
mainRulesetRefID: ruleset.ref.id,
|
||||
mainDelegateDescription:
|
||||
ruleset?.data?.decisions?.delegates?.[data?.delegateIdx]?.description,
|
||||
ruleset?.data?.decisions?.delegates?.[this.dialogData?.delegateIdx]
|
||||
?.description,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
changeTarget() {
|
||||
const { mainRulesetRefID, mainDelegateDescription } = this.targetRuleset$.value;
|
||||
const { mainRulesetRefID: previousMainRulesetRefID, delegateIdx } = this.data;
|
||||
const { mainRulesetRefID: previousMainRulesetRefID, delegateIdx } = this.dialogData;
|
||||
this.routingRulesService
|
||||
.changeMainRuleset({
|
||||
previousMainRulesetRefID,
|
||||
@ -48,8 +53,4 @@ export class ChangeTargetDialogComponent {
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => this.dialogRef.close(), this.errorService.error);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,19 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
|
||||
import { ErrorModule } from '../../../shared/services/error';
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
|
||||
import { TargetRulesetFormModule } from '../target-ruleset-form';
|
||||
import { ChangeTargetDialogComponent } from './change-target-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ErrorModule,
|
||||
TargetRulesetFormModule,
|
||||
FlexLayoutModule,
|
||||
MatDialogModule,
|
||||
MatButtonModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: [ChangeTargetDialogComponent],
|
||||
exports: [ChangeTargetDialogComponent],
|
||||
|
@ -1,7 +1,5 @@
|
||||
<form [formGroup]="form" fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Attach party delegate ruleset</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-base-dialog title="Attach party delegate ruleset">
|
||||
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-target-ruleset-form
|
||||
(valueChanges)="targetRuleset$.next($event)"
|
||||
(valid)="targetRulesetValid$.next($event)"
|
||||
@ -25,8 +23,7 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button
|
||||
mat-button
|
||||
color="primary"
|
||||
@ -35,5 +32,5 @@
|
||||
>
|
||||
ATTACH
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { ErrorService } from '../../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../../thrift-services';
|
||||
import { TargetRuleset } from '../../target-ruleset-form';
|
||||
@ -13,7 +14,10 @@ import { TargetRuleset } from '../../target-ruleset-form';
|
||||
templateUrl: 'attach-new-ruleset-dialog.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AttachNewRulesetDialogComponent {
|
||||
export class AttachNewRulesetDialogComponent extends BaseDialogSuperclass<
|
||||
AttachNewRulesetDialogComponent,
|
||||
{ partyID: string }
|
||||
> {
|
||||
form = this.fb.group({
|
||||
ruleset: this.fb.group({
|
||||
name: 'submain ruleset[by shop id]',
|
||||
@ -25,27 +29,27 @@ export class AttachNewRulesetDialogComponent {
|
||||
targetRulesetValid$ = new BehaviorSubject<boolean>(undefined);
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private fb: FormBuilder,
|
||||
private dialogRef: MatDialogRef<AttachNewRulesetDialogComponent>,
|
||||
private paymentRoutingRulesService: RoutingRulesService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { partyID: string },
|
||||
private errorService: ErrorService
|
||||
) {}
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
attach() {
|
||||
const { mainRulesetRefID, mainDelegateDescription } = this.targetRuleset$.value;
|
||||
this.paymentRoutingRulesService
|
||||
.attachPartyDelegateRuleset({
|
||||
partyID: this.data.partyID,
|
||||
partyID: this.dialogData.partyID,
|
||||
mainRulesetRefID,
|
||||
mainDelegateDescription,
|
||||
ruleset: this.form.value.ruleset,
|
||||
})
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => this.dialogRef.close(), this.errorService.error);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
.subscribe({
|
||||
next: () => this.dialogRef.close(),
|
||||
error: (err) => this.errorService.error(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { first, map, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
import { BaseDialogService } from '@cc/components/base-dialog/services/base-dialog.service';
|
||||
|
||||
import { handleError } from '../../../../utils/operators/handle-error';
|
||||
import { ErrorService } from '../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../thrift-services';
|
||||
import { DomainStoreService } from '../../../thrift-services/damsel/domain-store.service';
|
||||
import { DialogConfig, DIALOG_CONFIG } from '../../../tokens';
|
||||
import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog';
|
||||
import { PartyDelegateRulesetsService } from './party-delegate-rulesets.service';
|
||||
|
||||
@ -54,10 +54,9 @@ export class PartyDelegateRulesetsComponent {
|
||||
private partyDelegateRulesetsService: PartyDelegateRulesetsService,
|
||||
private paymentRoutingRulesService: RoutingRulesService,
|
||||
private router: Router,
|
||||
private dialog: MatDialog,
|
||||
private baseDialogService: BaseDialogService,
|
||||
private domainStoreService: DomainStoreService,
|
||||
private errorService: ErrorService,
|
||||
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
|
||||
private errorService: ErrorService
|
||||
) {}
|
||||
|
||||
attachNewRuleset() {
|
||||
@ -65,11 +64,8 @@ export class PartyDelegateRulesetsComponent {
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap((partyID) =>
|
||||
this.dialog
|
||||
.open(AttachNewRulesetDialogComponent, {
|
||||
...this.dialogConfig.medium,
|
||||
data: { partyID },
|
||||
})
|
||||
this.baseDialogService
|
||||
.open(AttachNewRulesetDialogComponent, { partyID })
|
||||
.afterClosed()
|
||||
),
|
||||
handleError(this.errorService.error),
|
||||
|
@ -15,9 +15,9 @@ import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
import { DetailsItemModule } from '@cc/components/details-item';
|
||||
|
||||
import { ErrorModule } from '../../../shared/services/error';
|
||||
import { ChangeTargetDialogModule } from '../change-target-dialog';
|
||||
import { PaymentRoutingRulesetHeaderModule } from '../payment-routing-ruleset-header';
|
||||
import { RoutingRulesListModule } from '../routing-rules-list';
|
||||
@ -48,10 +48,10 @@ const EXPORTED_DECLARATIONS = [PartyDelegateRulesetsComponent, AttachNewRulesetD
|
||||
DetailsItemModule,
|
||||
MatInputModule,
|
||||
MatProgressBarModule,
|
||||
ErrorModule,
|
||||
ChangeTargetDialogModule,
|
||||
TargetRulesetFormModule,
|
||||
RoutingRulesListModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: EXPORTED_DECLARATIONS,
|
||||
exports: EXPORTED_DECLARATIONS,
|
||||
|
@ -1,11 +1,9 @@
|
||||
<form [formGroup]="form" fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Party payment routing rule params</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-base-dialog title="Party payment routing rule params">
|
||||
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field>
|
||||
<mat-label>Shop</mat-label>
|
||||
<mat-select formControlName="shopID" required>
|
||||
<mat-option *ngFor="let shop of data.shops" [value]="shop.id">
|
||||
<mat-option *ngFor="let shop of dialogData.shops" [value]="shop.id">
|
||||
{{ shop.details.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
@ -26,8 +24,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button mat-button color="primary" (click)="add()" [disabled]="form.invalid">ADD</button>
|
||||
</div>
|
||||
</form>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { Shop } from '@vality/domain-proto/lib/domain';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { ErrorService } from '../../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../../thrift-services';
|
||||
|
||||
@ -11,7 +12,10 @@ import { RoutingRulesService } from '../../../../thrift-services';
|
||||
@Component({
|
||||
templateUrl: 'add-party-payment-routing-rule-dialog.component.html',
|
||||
})
|
||||
export class AddPartyPaymentRoutingRuleDialogComponent {
|
||||
export class AddPartyPaymentRoutingRuleDialogComponent extends BaseDialogSuperclass<
|
||||
AddPartyPaymentRoutingRuleDialogComponent,
|
||||
{ refID: number; partyID: string; shops: Shop[] }
|
||||
> {
|
||||
form = this.fb.group({
|
||||
shopID: '',
|
||||
name: 'Ruleset[candidates]',
|
||||
@ -19,13 +23,13 @@ export class AddPartyPaymentRoutingRuleDialogComponent {
|
||||
});
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private fb: FormBuilder,
|
||||
private dialogRef: MatDialogRef<AddPartyPaymentRoutingRuleDialogComponent>,
|
||||
private paymentRoutingRulesService: RoutingRulesService,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: { refID: number; partyID: string; shops: Shop[] },
|
||||
private errorService: ErrorService
|
||||
) {}
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
add() {
|
||||
const { shopID, name, description } = this.form.value;
|
||||
@ -33,15 +37,11 @@ export class AddPartyPaymentRoutingRuleDialogComponent {
|
||||
.addShopRuleset({
|
||||
name,
|
||||
description,
|
||||
partyRulesetRefID: this.data.refID,
|
||||
partyID: this.data.partyID,
|
||||
partyRulesetRefID: this.dialogData.refID,
|
||||
partyID: this.dialogData.partyID,
|
||||
shopID,
|
||||
})
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => this.dialogRef.close(), this.errorService.error);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { ErrorModule } from '../../../../shared/services/error';
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
|
||||
import { AddPartyPaymentRoutingRuleDialogComponent } from './add-party-payment-routing-rule-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
@ -29,7 +30,7 @@ import { AddPartyPaymentRoutingRuleDialogComponent } from './add-party-payment-r
|
||||
MatSelectModule,
|
||||
MatRadioModule,
|
||||
MatAutocompleteModule,
|
||||
ErrorModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: [AddPartyPaymentRoutingRuleDialogComponent],
|
||||
exports: [AddPartyPaymentRoutingRuleDialogComponent],
|
||||
|
@ -1,7 +1,5 @@
|
||||
<form [formGroup]="form" fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Payment rules init params</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-base-dialog title="Payment rules init params">
|
||||
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
@ -25,8 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button mat-button color="primary" (click)="init()" [disabled]="form.invalid">INIT</button>
|
||||
</div>
|
||||
</form>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { ErrorService } from '../../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../../thrift-services';
|
||||
|
||||
@ -11,7 +12,10 @@ import { RoutingRulesService } from '../../../../thrift-services';
|
||||
selector: 'cc-initialize-payment-routing-rules-dialog',
|
||||
templateUrl: 'initialize-payment-routing-rules-dialog.component.html',
|
||||
})
|
||||
export class InitializePaymentRoutingRulesDialogComponent {
|
||||
export class InitializePaymentRoutingRulesDialogComponent extends BaseDialogSuperclass<
|
||||
InitializePaymentRoutingRulesDialogComponent,
|
||||
{ partyID: string; refID: number }
|
||||
> {
|
||||
form = this.fb.group({
|
||||
delegateDescription: 'Main delegate[party]',
|
||||
name: 'submain ruleset[by shop id]',
|
||||
@ -19,28 +23,25 @@ export class InitializePaymentRoutingRulesDialogComponent {
|
||||
});
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private fb: FormBuilder,
|
||||
private dialogRef: MatDialogRef<InitializePaymentRoutingRulesDialogComponent>,
|
||||
private paymentRoutingRulesService: RoutingRulesService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { partyID: string; refID: number },
|
||||
private errorService: ErrorService
|
||||
) {}
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
init() {
|
||||
const { delegateDescription, name, description } = this.form.value;
|
||||
this.paymentRoutingRulesService
|
||||
.addPartyRuleset({
|
||||
name,
|
||||
partyID: this.data.partyID,
|
||||
mainRulesetRefID: this.data.refID,
|
||||
partyID: this.dialogData.partyID,
|
||||
mainRulesetRefID: this.dialogData.refID,
|
||||
description,
|
||||
delegateDescription,
|
||||
})
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => this.dialogRef.close(), this.errorService.error);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { ErrorModule } from '../../../../shared/services/error';
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
|
||||
import { InitializePaymentRoutingRulesDialogComponent } from './initialize-payment-routing-rules-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
@ -29,7 +30,7 @@ import { InitializePaymentRoutingRulesDialogComponent } from './initialize-payme
|
||||
MatSelectModule,
|
||||
MatRadioModule,
|
||||
MatAutocompleteModule,
|
||||
ErrorModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: [InitializePaymentRoutingRulesDialogComponent],
|
||||
exports: [InitializePaymentRoutingRulesDialogComponent],
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { filter, map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
import { BaseDialogService } from '@cc/components/base-dialog/services/base-dialog.service';
|
||||
|
||||
import { DomainStoreService } from '../../../thrift-services/damsel/domain-store.service';
|
||||
import { DialogConfig, DIALOG_CONFIG } from '../../../tokens';
|
||||
import { AddPartyPaymentRoutingRuleDialogComponent } from './add-party-payment-routing-rule-dialog';
|
||||
import { InitializePaymentRoutingRulesDialogComponent } from './initialize-payment-routing-rules-dialog';
|
||||
import { PartyPaymentRoutingRulesetService } from './party-payment-routing-ruleset.service';
|
||||
@ -54,11 +54,10 @@ export class PaymentRoutingRulesComponent {
|
||||
);
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private baseDialogService: BaseDialogService,
|
||||
private partyPaymentRoutingRulesetService: PartyPaymentRoutingRulesetService,
|
||||
private router: Router,
|
||||
private domainStoreService: DomainStoreService,
|
||||
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
|
||||
private domainStoreService: DomainStoreService
|
||||
) {}
|
||||
|
||||
initialize() {
|
||||
@ -69,11 +68,8 @@ export class PaymentRoutingRulesComponent {
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap(([partyID, refID]) =>
|
||||
this.dialog
|
||||
.open(InitializePaymentRoutingRulesDialogComponent, {
|
||||
...this.dialogConfig.medium,
|
||||
data: { partyID, refID },
|
||||
})
|
||||
this.baseDialogService
|
||||
.open(InitializePaymentRoutingRulesDialogComponent, { partyID, refID })
|
||||
.afterClosed()
|
||||
),
|
||||
untilDestroyed(this)
|
||||
@ -90,11 +86,8 @@ export class PaymentRoutingRulesComponent {
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap(([refID, shops, partyID]) =>
|
||||
this.dialog
|
||||
.open(AddPartyPaymentRoutingRuleDialogComponent, {
|
||||
...this.dialogConfig.medium,
|
||||
data: { refID, shops, partyID },
|
||||
})
|
||||
this.baseDialogService
|
||||
.open(AddPartyPaymentRoutingRuleDialogComponent, { refID, shops, partyID })
|
||||
.afterClosed()
|
||||
),
|
||||
untilDestroyed(this)
|
||||
|
@ -18,7 +18,6 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { ErrorModule } from '../../../shared/services/error';
|
||||
import { DamselModule } from '../../../thrift-services';
|
||||
import { ChangeTargetDialogModule } from '../change-target-dialog';
|
||||
import { PaymentRoutingRulesetHeaderModule } from '../payment-routing-ruleset-header';
|
||||
@ -53,7 +52,6 @@ import { PaymentRoutingRulesComponent } from './party-payment-routing-ruleset.co
|
||||
AddPartyPaymentRoutingRuleDialogModule,
|
||||
InitializePaymentRoutingRulesDialogModule,
|
||||
MatProgressBarModule,
|
||||
ErrorModule,
|
||||
ChangeTargetDialogModule,
|
||||
RoutingRulesListModule,
|
||||
],
|
||||
|
@ -2,12 +2,10 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
Input,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
@ -21,7 +19,6 @@ import { ConfirmActionDialogComponent } from '../../../../components/confirm-act
|
||||
import { handleError } from '../../../../utils/operators/handle-error';
|
||||
import { ErrorService } from '../../../shared/services/error';
|
||||
import { RoutingRulesService } from '../../../thrift-services';
|
||||
import { DIALOG_CONFIG, DialogConfig } from '../../../tokens';
|
||||
import { ChangeDelegateRulesetDialogComponent } from '../change-delegate-ruleset-dialog';
|
||||
import { ChangeTargetDialogComponent } from '../change-target-dialog';
|
||||
|
||||
@ -54,10 +51,7 @@ export class RoutingRulesListComponent<T extends { [N in PropertyKey]: any } & D
|
||||
private paginator$ = new ReplaySubject<MatPaginator>(1);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/member-ordering
|
||||
dataSource$ = combineLatest([
|
||||
this.data$,
|
||||
this.paginator$.pipe(startWith<any, null>(null)),
|
||||
]).pipe(
|
||||
dataSource$ = combineLatest([this.data$, this.paginator$.pipe(startWith(null))]).pipe(
|
||||
map(([d, paginator]) => {
|
||||
const data = new MatTableDataSource(d);
|
||||
data.paginator = paginator;
|
||||
@ -81,25 +75,16 @@ export class RoutingRulesListComponent<T extends { [N in PropertyKey]: any } & D
|
||||
}
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private baseDialogService: BaseDialogService,
|
||||
private errorService: ErrorService,
|
||||
private routingRulesService: RoutingRulesService,
|
||||
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig
|
||||
private routingRulesService: RoutingRulesService
|
||||
) {}
|
||||
|
||||
getColumnsKeys(col) {
|
||||
return col.key;
|
||||
}
|
||||
|
||||
changeDelegateRuleset(delegateId: DelegateId) {
|
||||
this.dialog
|
||||
this.baseDialogService
|
||||
.open(ChangeDelegateRulesetDialogComponent, {
|
||||
...this.dialogConfig.medium,
|
||||
data: {
|
||||
mainRulesetRefID: delegateId.parentRefId,
|
||||
delegateIdx: delegateId.delegateIdx,
|
||||
},
|
||||
mainRulesetRefID: delegateId.parentRefId,
|
||||
delegateIdx: delegateId.delegateIdx,
|
||||
})
|
||||
.afterClosed()
|
||||
.pipe(handleError(this.errorService.error), untilDestroyed(this))
|
||||
@ -107,13 +92,10 @@ export class RoutingRulesListComponent<T extends { [N in PropertyKey]: any } & D
|
||||
}
|
||||
|
||||
changeTarget(delegateId: DelegateId) {
|
||||
this.dialog
|
||||
this.baseDialogService
|
||||
.open(ChangeTargetDialogComponent, {
|
||||
...this.dialogConfig.medium,
|
||||
data: {
|
||||
mainRulesetRefID: delegateId.parentRefId,
|
||||
delegateIdx: delegateId.delegateIdx,
|
||||
},
|
||||
mainRulesetRefID: delegateId.parentRefId,
|
||||
delegateIdx: delegateId.delegateIdx,
|
||||
})
|
||||
.afterClosed()
|
||||
.pipe(untilDestroyed(this))
|
||||
|
@ -1,7 +1,5 @@
|
||||
<form [formGroup]="form" fxLayout="column" fxLayoutGap="32px">
|
||||
<div class="cc-headline">Shop payment routing rule params</div>
|
||||
|
||||
<div fxLayout="column" fxLayoutGap="24px">
|
||||
<cc-base-dialog title="Shop payment routing rule params">
|
||||
<div [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
|
||||
<div fxLayout="column" fxLayoutGap="16px">
|
||||
<mat-form-field>
|
||||
<input
|
||||
@ -32,10 +30,7 @@
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="cc-title">Predicate</div>
|
||||
<cc-predicate
|
||||
(validationChange)="predicateValid = $event"
|
||||
(predicateChange)="predicate = $event"
|
||||
></cc-predicate>
|
||||
<cc-predicate [formControl]="predicateControl"></cc-predicate>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
@ -115,15 +110,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout fxLayoutAlign="space-between">
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<cc-base-dialog-actions>
|
||||
<button
|
||||
mat-button
|
||||
color="primary"
|
||||
(click)="add()"
|
||||
[disabled]="form.invalid || !predicateValid"
|
||||
[disabled]="form.invalid || predicateControl.invalid"
|
||||
>
|
||||
ADD
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</cc-base-dialog-actions>
|
||||
</cc-base-dialog>
|
||||
|
@ -1,43 +1,50 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { FormBuilder } from '@ngneat/reactive-forms';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { Predicate, RiskScore } from '@vality/domain-proto/lib/domain';
|
||||
|
||||
import { BaseDialogSuperclass } from '@cc/components/base-dialog';
|
||||
|
||||
import { DomainStoreService } from '../../../../thrift-services/damsel/domain-store.service';
|
||||
import {
|
||||
AddShopPaymentRoutingRuleDialogService,
|
||||
TerminalType,
|
||||
} from './add-shop-payment-routing-rule-dialog.service';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'cc-add-shop-payment-routing-rule-dialog',
|
||||
templateUrl: 'add-shop-payment-routing-rule-dialog.component.html',
|
||||
styleUrls: ['add-shop-payment-routing-rule-dialog.component.scss'],
|
||||
providers: [AddShopPaymentRoutingRuleDialogService],
|
||||
})
|
||||
export class AddShopPaymentRoutingRuleDialogComponent {
|
||||
export class AddShopPaymentRoutingRuleDialogComponent extends BaseDialogSuperclass<
|
||||
AddShopPaymentRoutingRuleDialogComponent,
|
||||
{ refID: number }
|
||||
> {
|
||||
form = this.addShopPaymentRoutingRuleDialogService.form;
|
||||
newTerminalOptionsForm = this.addShopPaymentRoutingRuleDialogService.newTerminalOptionsForm;
|
||||
predicateControl = this.fb.control<Predicate>(null, Validators.required);
|
||||
|
||||
terminalType = TerminalType;
|
||||
riskScore = RiskScore;
|
||||
terminals$ = this.domainStoreService.getObjects('terminal');
|
||||
|
||||
predicate: Predicate;
|
||||
predicateValid: boolean;
|
||||
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private addShopPaymentRoutingRuleDialogService: AddShopPaymentRoutingRuleDialogService,
|
||||
private dialogRef: MatDialogRef<AddShopPaymentRoutingRuleDialogComponent>,
|
||||
private domainStoreService: DomainStoreService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { partyID: string; refID: number }
|
||||
) {}
|
||||
|
||||
add() {
|
||||
this.addShopPaymentRoutingRuleDialogService.add(this.predicate);
|
||||
private fb: FormBuilder
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
add() {
|
||||
this.addShopPaymentRoutingRuleDialogService.add(
|
||||
this.predicateControl.value,
|
||||
this.dialogData.refID
|
||||
);
|
||||
}
|
||||
|
||||
addOption() {
|
||||
|
@ -12,6 +12,9 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { MetadataFormModule } from '@cc/app/shared';
|
||||
import { BaseDialogModule } from '@cc/components/base-dialog';
|
||||
|
||||
import { AddShopPaymentRoutingRuleDialogComponent } from './add-shop-payment-routing-rule-dialog.component';
|
||||
import { ExpanderComponent } from './expander';
|
||||
import { PredicateComponent } from './predicate';
|
||||
@ -30,6 +33,8 @@ import { PredicateComponent } from './predicate';
|
||||
MatSelectModule,
|
||||
MatRadioModule,
|
||||
MatAutocompleteModule,
|
||||
MetadataFormModule,
|
||||
BaseDialogModule,
|
||||
],
|
||||
declarations: [AddShopPaymentRoutingRuleDialogComponent, PredicateComponent, ExpanderComponent],
|
||||
exports: [AddShopPaymentRoutingRuleDialogComponent],
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FormArray, FormBuilder, Validators } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { Predicate } from '@vality/domain-proto/lib/domain';
|
||||
import { of } from 'rxjs';
|
||||
import { startWith, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
import { BaseDialogResponseStatus } from '@cc/components/base-dialog';
|
||||
|
||||
import { RoutingRulesService, TerminalService } from '../../../../thrift-services';
|
||||
import { AddShopPaymentRoutingRuleDialogComponent } from './add-shop-payment-routing-rule-dialog.component';
|
||||
|
||||
@ -37,8 +39,7 @@ export class AddShopPaymentRoutingRuleDialogService {
|
||||
private fb: FormBuilder,
|
||||
private dialogRef: MatDialogRef<AddShopPaymentRoutingRuleDialogComponent>,
|
||||
private paymentRoutingRulesService: RoutingRulesService,
|
||||
private terminalService: TerminalService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { partyID: string; refID: number }
|
||||
private terminalService: TerminalService
|
||||
) {
|
||||
this.form
|
||||
.get('terminalType')
|
||||
@ -62,7 +63,7 @@ export class AddShopPaymentRoutingRuleDialogService {
|
||||
});
|
||||
}
|
||||
|
||||
add(predicate: Predicate) {
|
||||
add(predicate: Predicate, refID: number) {
|
||||
const { description, weight, priority, terminalType, existentTerminalID, newTerminal } =
|
||||
this.form.value;
|
||||
(terminalType === TerminalType.New
|
||||
@ -82,12 +83,12 @@ export class AddShopPaymentRoutingRuleDialogService {
|
||||
weight,
|
||||
priority,
|
||||
terminalID,
|
||||
refID: this.data.refID,
|
||||
refID,
|
||||
predicate,
|
||||
})
|
||||
)
|
||||
)
|
||||
.subscribe(() => this.dialogRef.close());
|
||||
.subscribe(() => this.dialogRef.close({ status: BaseDialogResponseStatus.Success }));
|
||||
}
|
||||
|
||||
addOption() {
|
||||
|
@ -1,203 +1,7 @@
|
||||
<div fxLayout="column" fxLayoutGap="24px" [formGroup]="form">
|
||||
<mat-radio-group formControlName="type" fxLayout="column" fxLayoutGap="16px">
|
||||
<mat-radio-button [value]="predicateType.condition">Condition</mat-radio-button>
|
||||
<div fxLayout>
|
||||
<mat-radio-button [value]="predicateType.constant" fxFlex>Constant</mat-radio-button>
|
||||
<mat-radio-button [value]="predicateType.allOf" fxFlex>All of</mat-radio-button>
|
||||
</div>
|
||||
<div fxLayout>
|
||||
<mat-radio-button [value]="predicateType.anyOf" fxFlex>Any of</mat-radio-button>
|
||||
<mat-radio-button [value]="predicateType.isNot" fxFlex>Is not</mat-radio-button>
|
||||
</div>
|
||||
</mat-radio-group>
|
||||
|
||||
<div
|
||||
*ngIf="
|
||||
[predicateType.anyOf, predicateType.allOf, predicateType.isNot].includes(
|
||||
form.value.type
|
||||
) && childrenForm?.controls?.length
|
||||
"
|
||||
fxLayout
|
||||
fxLayoutGap="24px"
|
||||
>
|
||||
<mat-divider vertical></mat-divider>
|
||||
<div fxLayout="column" fxLayoutGap="24px" fxFlex>
|
||||
<cc-expander
|
||||
*ngIf="form.value.type === predicateType.isNot; else allNAnyOf"
|
||||
title="Is not predicate"
|
||||
(remove)="removeAll()"
|
||||
>
|
||||
<cc-predicate [form]="childrenForm.controls[0]"></cc-predicate>
|
||||
</cc-expander>
|
||||
<ng-template #allNAnyOf>
|
||||
<cc-expander
|
||||
*ngFor="let childForm of childrenForm.controls; let idx = index"
|
||||
[title]="
|
||||
(form.value.type === predicateType.anyOf ? 'Any' : 'All') +
|
||||
' of predicate #' +
|
||||
(idx + 1)
|
||||
"
|
||||
(remove)="removeChild(idx)"
|
||||
>
|
||||
<cc-predicate [form]="childForm"></cc-predicate>
|
||||
</cc-expander>
|
||||
<mat-icon class="action" fxFlexAlign="end" (click)="addChild()">add</mat-icon>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="form.value.type === predicateType.constant">
|
||||
<div class="cc-subheading-2">Constant</div>
|
||||
<mat-radio-group formControlName="constant" fxLayout>
|
||||
<mat-radio-button [value]="true" fxFlex>True</mat-radio-button>
|
||||
<mat-radio-button [value]="false" fxFlex>False</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="form.value.type === predicateType.condition" formGroupName="condition">
|
||||
<div class="cc-subheading-2">Condition</div>
|
||||
<mat-radio-group formControlName="type" fxLayout="column" fxLayoutGap="16px">
|
||||
<mat-radio-button [value]="conditionType.paymentTool">Payment tool</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<ng-container
|
||||
formGroupName="paymentTool"
|
||||
*ngIf="form.value.condition.type === conditionType.paymentTool"
|
||||
>
|
||||
<div class="cc-subheading-2">Payment tool condition</div>
|
||||
<mat-radio-group formControlName="type" fxLayout="column" fxLayoutGap="16px">
|
||||
<mat-radio-button [value]="paymentToolType.bankCard">Bank Card</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<ng-container
|
||||
formGroupName="bankCard"
|
||||
*ngIf="form.value.condition.paymentTool.type === paymentToolType.bankCard"
|
||||
>
|
||||
<div class="cc-subheading-2">Bank card condition</div>
|
||||
<mat-radio-group formControlName="type" fxLayout="column" fxLayoutGap="16px">
|
||||
<div fxLayout>
|
||||
<mat-radio-button [value]="bankCardType.issuerCountryIs" fxFlex
|
||||
>Issuer country is</mat-radio-button
|
||||
>
|
||||
<mat-radio-button [value]="bankCardType.paymentSystem" fxFlex
|
||||
>Payment system</mat-radio-button
|
||||
>
|
||||
</div>
|
||||
<mat-radio-button [value]="bankCardType.paymentSystemIs"
|
||||
>Payment system is (deprecated)</mat-radio-button
|
||||
>
|
||||
</mat-radio-group>
|
||||
<div fxLayout="column">
|
||||
<mat-form-field
|
||||
*ngIf="
|
||||
form.value.condition.paymentTool.bankCard.type ===
|
||||
bankCardType.issuerCountryIs
|
||||
"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Residence"
|
||||
formControlName="residence"
|
||||
[matAutocomplete]="residenceAuto"
|
||||
required
|
||||
/>
|
||||
<mat-autocomplete autoActiveFirstOption #residenceAuto="matAutocomplete">
|
||||
<mat-option *ngFor="let option of residences$ | async" [value]="option">
|
||||
{{ option }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
<mat-hint>ISO_3166-1 Alpha-3 Code</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field
|
||||
*ngIf="
|
||||
form.value.condition.paymentTool.bankCard.type ===
|
||||
bankCardType.paymentSystemIs
|
||||
"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Payment system"
|
||||
formControlName="paymentSystemIs"
|
||||
[matAutocomplete]="paymentSystemAuto"
|
||||
required
|
||||
/>
|
||||
<mat-autocomplete
|
||||
autoActiveFirstOption
|
||||
#paymentSystemAuto="matAutocomplete"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let option of deprecatedPaymentSystems$ | async"
|
||||
[value]="option"
|
||||
>
|
||||
{{ option }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
form.value.condition.paymentTool.bankCard.type ===
|
||||
bankCardType.paymentSystem
|
||||
"
|
||||
>
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Payment system"
|
||||
formControlName="paymentSystem"
|
||||
[matAutocomplete]="paymentSystemAuto"
|
||||
required
|
||||
/>
|
||||
<mat-autocomplete
|
||||
autoActiveFirstOption
|
||||
#paymentSystemAuto="matAutocomplete"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let option of paymentSystems$ | async"
|
||||
[value]="option.ref.id"
|
||||
>
|
||||
{{ option.data?.name }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Token provider"
|
||||
formControlName="tokenProvider"
|
||||
[matAutocomplete]="tokenProviderAuto"
|
||||
/>
|
||||
<mat-autocomplete
|
||||
autoActiveFirstOption
|
||||
#tokenProviderAuto="matAutocomplete"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let option of tokenProviders$ | async"
|
||||
[value]="option"
|
||||
>
|
||||
{{ option }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
placeholder="Tokenization method"
|
||||
formControlName="tokenizationMethod"
|
||||
[matAutocomplete]="tokenizationMethodAuto"
|
||||
/>
|
||||
<mat-autocomplete
|
||||
autoActiveFirstOption
|
||||
#tokenizationMethodAuto="matAutocomplete"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let option of tokenizationMethods$ | async"
|
||||
[value]="option"
|
||||
>
|
||||
{{ option }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<cc-metadata-form
|
||||
[formControl]="control"
|
||||
[metadata]="metadata$ | async"
|
||||
[extensions]="extensions$ | async"
|
||||
namespace="domain"
|
||||
type="Predicate"
|
||||
></cc-metadata-form>
|
||||
|
@ -1,3 +0,0 @@
|
||||
.action {
|
||||
cursor: pointer;
|
||||
}
|
@ -1,302 +1,26 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
FormArray,
|
||||
FormBuilder,
|
||||
FormGroup,
|
||||
ValidatorFn,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import {
|
||||
BankCardConditionDefinition,
|
||||
LegacyBankCardPaymentSystem,
|
||||
LegacyBankCardTokenProvider,
|
||||
Predicate,
|
||||
CountryCode,
|
||||
TokenizationMethod,
|
||||
} from '@vality/domain-proto/lib/domain';
|
||||
import identity from 'lodash-es/identity';
|
||||
import pickBy from 'lodash-es/pickBy';
|
||||
import { merge, Observable, Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged, map, shareReplay, startWith, tap } from 'rxjs/operators';
|
||||
import { Component, Injector, OnChanges } from '@angular/core';
|
||||
import { Predicate } from '@vality/domain-proto/lib/domain';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
import { ComponentChanges } from '@cc/app/shared/utils';
|
||||
|
||||
import { DomainStoreService } from '../../../../../thrift-services/damsel/domain-store.service';
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
enum PredicateType {
|
||||
constant = 'constant',
|
||||
condition = 'condition',
|
||||
anyOf = 'anyOf',
|
||||
allOf = 'allOf',
|
||||
isNot = 'isNot',
|
||||
}
|
||||
|
||||
enum ConditionType {
|
||||
paymentTool = 'paymentTool',
|
||||
}
|
||||
|
||||
enum PaymentToolType {
|
||||
bankCard = 'bankCard',
|
||||
}
|
||||
|
||||
enum BankCardType {
|
||||
issuerCountryIs = 'issuerCountryIs',
|
||||
paymentSystem = 'paymentSystem',
|
||||
paymentSystemIs = 'paymentSystemIs',
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-predicate',
|
||||
templateUrl: 'predicate.component.html',
|
||||
styleUrls: ['predicate.component.scss'],
|
||||
providers: createControlProviders(PredicateComponent),
|
||||
})
|
||||
export class PredicateComponent implements OnChanges {
|
||||
@Input() form = this.createForm();
|
||||
@Output() validationChange = new EventEmitter<boolean>();
|
||||
@Output() predicateChange = new EventEmitter<Predicate>();
|
||||
export class PredicateComponent
|
||||
extends ValidatedFormControlSuperclass<Predicate>
|
||||
implements OnChanges
|
||||
{
|
||||
metadata$ = from(import('@vality/domain-proto/lib/metadata.json').then((m) => m.default));
|
||||
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
|
||||
|
||||
predicateType = PredicateType;
|
||||
conditionType = ConditionType;
|
||||
paymentToolType = PaymentToolType;
|
||||
bankCardType = BankCardType;
|
||||
|
||||
deprecatedPaymentSystems$: Observable<string[]>;
|
||||
paymentSystems$ = this.domainStoreService.getObjects('payment_system');
|
||||
tokenProviders$: Observable<string[]>;
|
||||
tokenizationMethods$: Observable<string[]>;
|
||||
residences$: Observable<string[]>;
|
||||
|
||||
private outputSub: Subscription;
|
||||
|
||||
get childrenForm() {
|
||||
return this.form?.controls?.children as FormArray;
|
||||
}
|
||||
|
||||
constructor(private fb: FormBuilder, private domainStoreService: DomainStoreService) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
ngOnChanges({ form }: ComponentChanges<PredicateComponent>): void {
|
||||
if (form) {
|
||||
this.init(true);
|
||||
}
|
||||
}
|
||||
|
||||
addChild() {
|
||||
this.childrenForm.push(this.createForm());
|
||||
}
|
||||
|
||||
removeChild(idx: number) {
|
||||
this.childrenForm.removeAt(idx);
|
||||
if (!this.childrenForm.controls.length) {
|
||||
this.addChild();
|
||||
}
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
this.childrenForm.clear();
|
||||
this.addChild();
|
||||
}
|
||||
|
||||
private init(isInternal = false) {
|
||||
if (this.childrenForm && !this.childrenForm.controls.length) {
|
||||
this.addChild();
|
||||
}
|
||||
const { condition, constant, type } = this.form.controls;
|
||||
type.valueChanges.pipe(startWith(type.value), distinctUntilChanged()).subscribe((t) => {
|
||||
switch (t) {
|
||||
case PredicateType.allOf:
|
||||
case PredicateType.anyOf:
|
||||
case PredicateType.isNot:
|
||||
this.childrenForm.enable();
|
||||
constant.disable();
|
||||
condition.disable();
|
||||
break;
|
||||
case PredicateType.constant:
|
||||
this.childrenForm.disable();
|
||||
constant.enable();
|
||||
condition.disable();
|
||||
break;
|
||||
case PredicateType.condition:
|
||||
this.childrenForm.disable();
|
||||
constant.disable();
|
||||
condition.enable();
|
||||
break;
|
||||
default:
|
||||
this.childrenForm.disable();
|
||||
constant.disable();
|
||||
condition.disable();
|
||||
break;
|
||||
}
|
||||
});
|
||||
const {
|
||||
residence,
|
||||
paymentSystemIs,
|
||||
paymentSystem,
|
||||
type: bankCardType,
|
||||
tokenProvider,
|
||||
tokenizationMethod,
|
||||
} = (this.form.get('condition.paymentTool.bankCard') as FormGroup).controls;
|
||||
bankCardType.valueChanges
|
||||
.pipe(startWith(bankCardType.value), distinctUntilChanged())
|
||||
.subscribe((t) => {
|
||||
switch (t) {
|
||||
case BankCardType.issuerCountryIs:
|
||||
paymentSystem.disable();
|
||||
paymentSystemIs.disable();
|
||||
residence.enable();
|
||||
break;
|
||||
case BankCardType.paymentSystem:
|
||||
residence.disable();
|
||||
paymentSystemIs.disable();
|
||||
paymentSystem.enable();
|
||||
break;
|
||||
case BankCardType.paymentSystemIs:
|
||||
residence.disable();
|
||||
paymentSystem.disable();
|
||||
paymentSystemIs.enable();
|
||||
break;
|
||||
default:
|
||||
residence.disable();
|
||||
paymentSystem.disable();
|
||||
paymentSystemIs.disable();
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (this.outputSub) {
|
||||
this.outputSub.unsubscribe();
|
||||
delete this.outputSub;
|
||||
}
|
||||
if (!isInternal) {
|
||||
this.outputSub = merge(
|
||||
this.form.valueChanges.pipe(
|
||||
startWith(this.form.value),
|
||||
map(() => this.valueToPredicate()),
|
||||
distinctUntilChanged(),
|
||||
tap((v) => this.predicateChange.next(v))
|
||||
),
|
||||
this.form.statusChanges.pipe(
|
||||
startWith(this.form.status),
|
||||
map(() => this.form.valid),
|
||||
distinctUntilChanged(),
|
||||
tap((s) => this.validationChange.next(s))
|
||||
)
|
||||
).subscribe();
|
||||
}
|
||||
this.deprecatedPaymentSystems$ = this.getFilteredKeys(
|
||||
paymentSystemIs,
|
||||
LegacyBankCardPaymentSystem
|
||||
);
|
||||
this.tokenProviders$ = this.getFilteredKeys(tokenProvider, LegacyBankCardTokenProvider);
|
||||
this.tokenizationMethods$ = this.getFilteredKeys(tokenizationMethod, TokenizationMethod);
|
||||
this.residences$ = this.getFilteredKeys(residence, CountryCode);
|
||||
}
|
||||
|
||||
private createForm() {
|
||||
return this.fb.group({
|
||||
type: ['', Validators.required],
|
||||
condition: this.fb.group({
|
||||
type: [ConditionType.paymentTool, Validators.required],
|
||||
paymentTool: this.fb.group({
|
||||
type: [PaymentToolType.bankCard, Validators.required],
|
||||
bankCard: this.fb.group({
|
||||
type: ['', Validators.required],
|
||||
residence: ['', [Validators.required, this.enumValidator(CountryCode)]],
|
||||
paymentSystemIs: [
|
||||
'',
|
||||
[Validators.required, this.enumValidator(LegacyBankCardPaymentSystem)],
|
||||
],
|
||||
paymentSystem: ['', [Validators.required]],
|
||||
tokenProvider: ['', this.enumValidator(LegacyBankCardTokenProvider)],
|
||||
tokenizationMethod: ['', this.enumValidator(TokenizationMethod)],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
constant: ['', Validators.required],
|
||||
children: this.fb.array([]),
|
||||
});
|
||||
}
|
||||
|
||||
private valueToPredicate(value = this.form.value): Predicate {
|
||||
if (this.form.invalid) {
|
||||
return null;
|
||||
}
|
||||
switch (value.type) {
|
||||
case PredicateType.allOf:
|
||||
return { all_of: value.children.map((c) => this.valueToPredicate(c)) };
|
||||
case PredicateType.anyOf:
|
||||
return { any_of: value.children.map((c) => this.valueToPredicate(c)) };
|
||||
case PredicateType.isNot:
|
||||
return { is_not: this.valueToPredicate(value.children[0]) };
|
||||
case PredicateType.constant:
|
||||
return { constant: value.constant };
|
||||
case PredicateType.condition:
|
||||
if (!value.condition) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
condition: {
|
||||
payment_tool: {
|
||||
bank_card: {
|
||||
definition: this.bankCardValueToBankCardConditionDefinition(
|
||||
value.condition.paymentTool.bankCard
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private bankCardValueToBankCardConditionDefinition(value: any): BankCardConditionDefinition {
|
||||
switch (value.type) {
|
||||
case BankCardType.issuerCountryIs:
|
||||
return { issuer_country_is: CountryCode[value.residence as string] };
|
||||
case BankCardType.paymentSystem:
|
||||
return {
|
||||
payment_system: pickBy(
|
||||
{
|
||||
tokenization_method_is:
|
||||
TokenizationMethod[value.tokenizationMethod as string],
|
||||
payment_system_is: { id: value.paymentSystem },
|
||||
token_provider_is_deprecated:
|
||||
LegacyBankCardTokenProvider[value.tokenProvider as string],
|
||||
},
|
||||
identity
|
||||
),
|
||||
};
|
||||
case BankCardType.paymentSystemIs:
|
||||
return {
|
||||
payment_system_is:
|
||||
LegacyBankCardPaymentSystem[
|
||||
value.paymentSystemIs as keyof LegacyBankCardPaymentSystem
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private getFilteredKeys(control: AbstractControl, enumObj: any) {
|
||||
return control.valueChanges.pipe(
|
||||
startWith(control.value),
|
||||
map((v) => v.trim().toLowerCase()),
|
||||
map((v) =>
|
||||
this.getKeys(enumObj).filter((option) => option.toLowerCase().indexOf(v) === 0)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
private getKeys(enumObj: any) {
|
||||
return Object.keys(enumObj).filter((k) => isNaN(+k));
|
||||
}
|
||||
|
||||
private enumValidator(enumObj: any): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: any } | null =>
|
||||
!control.value || Object.keys(enumObj).includes(control.value)
|
||||
? null
|
||||
: { enumNotIncludeKey: { value: control.value } };
|
||||
constructor(
|
||||
injector: Injector,
|
||||
private domainMetadataFormExtensionsService: DomainMetadataFormExtensionsService
|
||||
) {
|
||||
super(injector);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,19 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { Predicate, TerminalObject } from '@vality/domain-proto/lib/domain';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { first, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { objectToJSON } from '@cc/app/api/utils';
|
||||
import { NotificationService } from '@cc/app/shared/services/notification';
|
||||
import { BaseDialogResponseStatus } from '@cc/components/base-dialog';
|
||||
import { BaseDialogService } from '@cc/components/base-dialog/services/base-dialog.service';
|
||||
|
||||
import { handleError } from '../../../../utils/operators/handle-error';
|
||||
import { ErrorService } from '../../../shared/services/error';
|
||||
import { damselInstanceToObject } from '../../../thrift-services';
|
||||
import { DomainStoreService } from '../../../thrift-services/damsel/domain-store.service';
|
||||
import { AddShopPaymentRoutingRuleDialogComponent } from './add-shop-payment-routing-rule-dialog';
|
||||
import { ShopPaymentRoutingRulesetService } from './shop-payment-routing-ruleset.service';
|
||||
|
||||
const DIALOG_WIDTH = '548px';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'cc-shop-payment-routing-ruleset',
|
||||
@ -44,30 +42,40 @@ export class ShopPaymentRoutingRulesetComponent {
|
||||
isLoading$ = this.domainStoreService.isLoading$;
|
||||
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private baseDialogService: BaseDialogService,
|
||||
private shopPaymentRoutingRulesetService: ShopPaymentRoutingRulesetService,
|
||||
private domainStoreService: DomainStoreService,
|
||||
private errorService: ErrorService
|
||||
private errorService: ErrorService,
|
||||
private notificationService: NotificationService
|
||||
) {}
|
||||
|
||||
addShopRule() {
|
||||
combineLatest([this.partyID$, this.shopPaymentRoutingRulesetService.refID$])
|
||||
this.shopPaymentRoutingRulesetService.refID$
|
||||
.pipe(
|
||||
take(1),
|
||||
switchMap(([partyID, refID]) =>
|
||||
this.dialog
|
||||
.open(AddShopPaymentRoutingRuleDialogComponent, {
|
||||
disableClose: true,
|
||||
width: DIALOG_WIDTH,
|
||||
maxHeight: '90vh',
|
||||
data: { partyID, refID },
|
||||
})
|
||||
first(),
|
||||
switchMap((refID) =>
|
||||
this.baseDialogService
|
||||
.open(AddShopPaymentRoutingRuleDialogComponent, { refID })
|
||||
.afterClosed()
|
||||
),
|
||||
handleError(this.errorService.error),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
)
|
||||
.subscribe();
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
if (res.status === BaseDialogResponseStatus.Success) {
|
||||
this.domainStoreService.forceReload();
|
||||
this.notificationService.success(
|
||||
'Shop payment routing ruleset successfully added'
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.errorService.error(err);
|
||||
this.notificationService.success(
|
||||
'Error while adding shop payment routing ruleset'
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
removeShopRule(idx: number) {
|
||||
|
@ -21,7 +21,6 @@ import { RouterModule } from '@angular/router';
|
||||
|
||||
import { PrettyJsonModule } from '@cc/components/pretty-json';
|
||||
|
||||
import { ErrorModule } from '../../../shared/services/error';
|
||||
import { DamselModule } from '../../../thrift-services';
|
||||
import { PaymentRoutingRulesetHeaderModule } from '../payment-routing-ruleset-header';
|
||||
import { AddShopPaymentRoutingRuleDialogModule } from './add-shop-payment-routing-rule-dialog';
|
||||
@ -54,7 +53,6 @@ import { ShopPaymentRoutingRulesetComponent } from './shop-payment-routing-rules
|
||||
AddShopPaymentRoutingRuleDialogModule,
|
||||
PrettyJsonModule,
|
||||
MatProgressBarModule,
|
||||
ErrorModule,
|
||||
],
|
||||
declarations: [ShopPaymentRoutingRulesetComponent],
|
||||
})
|
||||
|
@ -10,13 +10,11 @@ import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { DetailsItemModule } from '../../../../components/details-item';
|
||||
import { ErrorModule } from '../../../shared/services/error';
|
||||
import { TargetRulesetFormComponent } from './target-ruleset-form.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ErrorModule,
|
||||
FlexLayoutModule,
|
||||
ReactiveFormsModule,
|
||||
MatRadioModule,
|
||||
|
@ -6,10 +6,7 @@ import { Party, Shop } from '@vality/magista-proto/lib/domain';
|
||||
import { Moment } from 'moment';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import {
|
||||
createValidatedAbstractControlProviders,
|
||||
ValidatedWrappedAbstractControlSuperclass,
|
||||
} from '@cc/utils/forms';
|
||||
import { createControlProviders, ValidatedControlSuperclass } from '@cc/utils/forms';
|
||||
import { getEnumKeys } from '@cc/utils/get-enum-keys';
|
||||
|
||||
export interface PayoutsSearchForm {
|
||||
@ -25,9 +22,9 @@ export interface PayoutsSearchForm {
|
||||
@Component({
|
||||
selector: 'cc-payouts-search-form',
|
||||
templateUrl: './payouts-search-form.component.html',
|
||||
providers: createValidatedAbstractControlProviders(PayoutsSearchFormComponent),
|
||||
providers: createControlProviders(PayoutsSearchFormComponent),
|
||||
})
|
||||
export class PayoutsSearchFormComponent extends ValidatedWrappedAbstractControlSuperclass<PayoutsSearchForm> {
|
||||
export class PayoutsSearchFormComponent extends ValidatedControlSuperclass<PayoutsSearchForm> {
|
||||
control = this.fb.group<PayoutsSearchForm>({
|
||||
payoutId: null,
|
||||
partyId: null,
|
||||
|
@ -1,2 +0,0 @@
|
||||
export * from './settings.module';
|
||||
export * from './settings.service';
|
@ -1,8 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { SettingsService } from './settings.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [SettingsService],
|
||||
})
|
||||
export class SettingsModule {}
|
@ -1,22 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class SettingsService {
|
||||
set(key: string, value: string) {
|
||||
localStorage.setItem(this.getKeyName(key), value);
|
||||
}
|
||||
|
||||
setAll(keyValue: { [name: string]: string }) {
|
||||
for (const [k, v] of Object.entries(keyValue)) {
|
||||
this.set(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
get(key: string): string {
|
||||
return localStorage.getItem(this.getKeyName(key));
|
||||
}
|
||||
|
||||
private getKeyName(name: string) {
|
||||
return `cc-${name}`;
|
||||
}
|
||||
}
|
@ -1,21 +1,17 @@
|
||||
<div gdColumns="1fr" gdGap="16px">
|
||||
<span class="cc-body-1">
|
||||
<span class="cc-body-1" *ngIf="hasLabel">
|
||||
<cc-field-label [field]="data.field" [type]="data.type"></cc-field-label>
|
||||
({{ data.type.name | titlecase }})
|
||||
</span>
|
||||
<mat-accordion>
|
||||
<mat-accordion [ngStyle]="{ 'padding-left': hasLabel && '16px' }">
|
||||
<mat-expansion-panel
|
||||
class="mat-elevation-z0"
|
||||
*ngFor="let control of controls.controls; let i = index"
|
||||
*ngFor="let valueControl of valueControls.controls; let i = index"
|
||||
>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title fxLayoutAlign=" center">
|
||||
{{ i + 1 }}.
|
||||
{{
|
||||
data.type.name === 'map'
|
||||
? (data.type.keyType | valueTypeTitle | titlecase) + ' - '
|
||||
: ''
|
||||
}}
|
||||
{{ hasKeys ? (keyType | valueTypeTitle | titlecase) + ' - ' : '' }}
|
||||
{{ data.type.valueType | valueTypeTitle | titlecase }}
|
||||
</mat-panel-title>
|
||||
<mat-panel-description fxLayoutAlign="end">
|
||||
@ -25,18 +21,20 @@
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<div gdColumns="1fr" gdGap="16px">
|
||||
<ng-container *ngIf="data.type.name === 'map'">
|
||||
<ng-container *ngIf="hasKeys">
|
||||
<span class="cc-body-2">Key</span>
|
||||
<cc-metadata-form
|
||||
[formControl]="keyControls.controls[i]"
|
||||
[metadata]="data.metadata"
|
||||
[namespace]="data.namespace"
|
||||
[type]="data.type.keyType"
|
||||
[type]="keyType"
|
||||
[parent]="data"
|
||||
[extensions]="data.extensions"
|
||||
></cc-metadata-form>
|
||||
</ng-container>
|
||||
<span class="cc-body-2" *ngIf="data.type.name === 'map'">Value</span>
|
||||
<cc-metadata-form
|
||||
[formControl]="valueControl"
|
||||
[metadata]="data.metadata"
|
||||
[namespace]="data.namespace"
|
||||
[type]="data.type.valueType"
|
||||
|
@ -1,5 +1,5 @@
|
||||
mat-expansion-panel-body {
|
||||
padding: 0;
|
||||
::ng-deep .mat-expansion-panel-body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
mat-expansion-panel-header {
|
||||
|
@ -1,38 +1,75 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { FormArray, FormControl } from '@ngneat/reactive-forms';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { FormComponentSuperclass } from '@s-libs/ng-core';
|
||||
import { MapType, SetType, ListType } from '@vality/thrift-ts';
|
||||
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, getErrorsTree } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'cc-complex-form',
|
||||
templateUrl: './complex-form.component.html',
|
||||
styleUrls: ['complex-form.component.scss'],
|
||||
providers: createValidatedAbstractControlProviders(ComplexFormComponent),
|
||||
providers: createControlProviders(ComplexFormComponent),
|
||||
})
|
||||
export class ComplexFormComponent
|
||||
extends WrappedFormControlSuperclass<unknown>
|
||||
implements Validator
|
||||
export class ComplexFormComponent<T extends unknown[] | Map<unknown, unknown> | Set<unknown>>
|
||||
extends FormComponentSuperclass<T>
|
||||
implements OnInit, Validator
|
||||
{
|
||||
@Input() data: MetadataFormData<SetType | MapType | ListType>;
|
||||
|
||||
controls = new FormArray([]);
|
||||
valueControls = new FormArray([]);
|
||||
keyControls = new FormArray([]);
|
||||
|
||||
add() {
|
||||
this.controls.push(new FormControl());
|
||||
get hasLabel() {
|
||||
return !!this.data.trueParent;
|
||||
}
|
||||
|
||||
delete(idx: number) {
|
||||
this.controls.removeAt(idx);
|
||||
get hasKeys() {
|
||||
return this.data.type.name === 'map';
|
||||
}
|
||||
|
||||
get keyType() {
|
||||
if ('keyType' in this.data.type) return this.data.type.keyType;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.valueControls.valueChanges.pipe(untilDestroyed(this)).subscribe((value) => {
|
||||
switch (this.data.type.name) {
|
||||
case 'list':
|
||||
this.emitOutgoingValue(value as never);
|
||||
break;
|
||||
case 'map':
|
||||
this.emitOutgoingValue(
|
||||
new Map(value.map((v, idx) => [this.keyControls.value[idx], v])) as never
|
||||
);
|
||||
break;
|
||||
case 'set':
|
||||
this.emitOutgoingValue(new Set(value) as never);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleIncomingValue(value: T) {
|
||||
this.valueControls.patchValue(value as never, { emitEvent: false });
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.invalid || this.controls.invalid
|
||||
? { [this.data.type.name + 'Invalid']: true }
|
||||
: null;
|
||||
return getErrorsTree(this.keyControls) || getErrorsTree(this.valueControls);
|
||||
}
|
||||
|
||||
add() {
|
||||
this.valueControls.push(new FormControl());
|
||||
if (this.hasKeys) this.keyControls.push(new FormControl());
|
||||
}
|
||||
|
||||
delete(idx: number) {
|
||||
this.valueControls.removeAt(idx);
|
||||
if (this.hasKeys) this.keyControls.removeAt(idx);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
<cc-field-label [field]="data.field" [type]="data.type"></cc-field-label>
|
||||
</mat-label>
|
||||
<mat-select [formControl]="control" [required]="data.isRequired">
|
||||
<mat-option *ngFor="let item of data.ast.items" [value]="item.value">{{
|
||||
item.name
|
||||
}}</mat-option>
|
||||
<mat-option
|
||||
*ngFor="let item of data.ast.items; let idx = index"
|
||||
[value]="item.value ?? idx"
|
||||
>
|
||||
{{ item.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<button
|
||||
matSuffix
|
||||
|
@ -1,21 +1,15 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { Enums } from '@vality/thrift-ts/src/thrift-parser';
|
||||
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-enum-field',
|
||||
templateUrl: './enum-field.component.html',
|
||||
providers: createValidatedAbstractControlProviders(EnumFieldComponent),
|
||||
providers: createControlProviders(EnumFieldComponent),
|
||||
})
|
||||
export class EnumFieldComponent extends WrappedFormControlSuperclass<unknown> implements Validator {
|
||||
export class EnumFieldComponent<T> extends ValidatedFormControlSuperclass<T> {
|
||||
@Input() data: MetadataFormData<string, Enums[string]>;
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,87 @@
|
||||
<div gdColumns="1fr" gdGap="16px">
|
||||
<div fxLayoutGap="4px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>
|
||||
<cc-field-label [field]="data.field" [type]="data.type"></cc-field-label>
|
||||
</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matAutocomplete]="auto"
|
||||
#trigger="matAutocompleteTrigger"
|
||||
[formControl]="control"
|
||||
[required]="data.isRequired"
|
||||
[type]="inputType"
|
||||
[ngClass]="{ 'cc-code': (data.extensionResult$ | async)?.isIdentifier }"
|
||||
/>
|
||||
<div matSuffix fxLayoutGap="4px">
|
||||
<button mat-icon-button *ngIf="control.value" (click)="clear($event)">
|
||||
<ng-container *ngIf="data.type === 'bool'; else input">
|
||||
<div gdColumns="1fr" gdGap="16px">
|
||||
<cc-field-label
|
||||
class="cc-body-1"
|
||||
[field]="data.field"
|
||||
[type]="data.type"
|
||||
></cc-field-label>
|
||||
<div fxLayoutGap="4px" fxLayoutAlign=" center">
|
||||
<mat-radio-group fxFlex gdColumns="1fr 1fr" gdGap="8px" [formControl]="control">
|
||||
<mat-radio-button [value]="false">False</mat-radio-button>
|
||||
<mat-radio-button [value]="true">True</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngIf="
|
||||
!data.isRequired && control.value !== null && control.value !== undefined
|
||||
"
|
||||
(click)="clear($event)"
|
||||
>
|
||||
<mat-icon>clear</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="(extensionResult$ | async)?.options?.length"
|
||||
mat-icon-button
|
||||
(click)="
|
||||
auto.isOpen ? trigger.closePanel() : trigger.openPanel();
|
||||
$event.stopPropagation()
|
||||
"
|
||||
>
|
||||
<mat-icon>{{ auto.isOpen ? 'expand_less' : 'expand_more' }}</mat-icon>
|
||||
<button *ngIf="generate$ | async" mat-icon-button (click)="generate($event)">
|
||||
<mat-icon>loop</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<mat-hint>{{ aliases }}</mat-hint>
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<ng-container *ngIf="data.extensionResult$ | async as extensionResult">
|
||||
<mat-option
|
||||
*ngFor="let option of filteredOptions$ | async"
|
||||
[value]="option.value"
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #input>
|
||||
<div fxLayoutGap="4px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>
|
||||
<cc-field-label [field]="data.field" [type]="data.type"></cc-field-label>
|
||||
</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matAutocomplete]="auto"
|
||||
#trigger="matAutocompleteTrigger"
|
||||
[formControl]="control"
|
||||
[required]="data.isRequired"
|
||||
[type]="inputType"
|
||||
[ngClass]="{ 'cc-code': (data.extensionResult$ | async)?.isIdentifier }"
|
||||
/>
|
||||
<div matSuffix fxLayoutGap="4px">
|
||||
<button
|
||||
mat-icon-button
|
||||
*ngIf="!data.isRequired && control.value"
|
||||
(click)="clear($event)"
|
||||
>
|
||||
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign=" center">
|
||||
<div [ngClass]="{ 'cc-code': extensionResult.isIdentifier }">
|
||||
{{ option.value }}
|
||||
<mat-icon>clear</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="(extensionResult$ | async)?.options?.length"
|
||||
mat-icon-button
|
||||
(click)="
|
||||
auto.isOpen ? trigger.closePanel() : trigger.openPanel();
|
||||
$event.stopPropagation()
|
||||
"
|
||||
>
|
||||
<mat-icon>{{ auto.isOpen ? 'expand_less' : 'expand_more' }}</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<mat-hint>{{ aliases }}</mat-hint>
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<ng-container *ngIf="data.extensionResult$ | async as extensionResult">
|
||||
<mat-option
|
||||
*ngFor="let option of filteredOptions$ | async"
|
||||
[value]="option.value"
|
||||
>
|
||||
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign=" center">
|
||||
<div [ngClass]="{ 'cc-code': extensionResult.isIdentifier }">
|
||||
{{ option.value }}
|
||||
</div>
|
||||
<cc-label [label]="option.label" [color]="option.color"></cc-label>
|
||||
</div>
|
||||
<cc-label [label]="option.label" [color]="option.color"></cc-label>
|
||||
</div>
|
||||
</mat-option>
|
||||
</ng-container>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<button *ngIf="generate$ | async" mat-icon-button (click)="generate($event)">
|
||||
<mat-icon>loop</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-option>
|
||||
</ng-container>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<button *ngIf="generate$ | async" mat-icon-button (click)="generate($event)">
|
||||
<mat-icon>loop</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="selected$ | async as selected">
|
||||
<mat-expansion-panel *ngIf="selected.details">
|
||||
<mat-expansion-panel-header>
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { ThriftType } from '@vality/thrift-ts';
|
||||
import { combineLatest, defer, ReplaySubject, switchMap } from 'rxjs';
|
||||
import { map, pluck, shareReplay, startWith } from 'rxjs/operators';
|
||||
|
||||
import { ComponentChanges, getAliases, getValueTypeTitle } from '@cc/app/shared';
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
|
||||
@ -15,11 +13,11 @@ import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
@Component({
|
||||
selector: 'cc-primitive-field',
|
||||
templateUrl: './primitive-field.component.html',
|
||||
providers: createValidatedAbstractControlProviders(PrimitiveFieldComponent),
|
||||
providers: createControlProviders(PrimitiveFieldComponent),
|
||||
})
|
||||
export class PrimitiveFieldComponent
|
||||
extends WrappedFormControlSuperclass<unknown>
|
||||
implements OnChanges, Validator
|
||||
export class PrimitiveFieldComponent<T>
|
||||
extends ValidatedFormControlSuperclass<T>
|
||||
implements OnChanges
|
||||
{
|
||||
@Input() data: MetadataFormData<ThriftType>;
|
||||
|
||||
@ -71,22 +69,18 @@ export class PrimitiveFieldComponent
|
||||
|
||||
private data$ = new ReplaySubject<MetadataFormData<ThriftType>>(1);
|
||||
|
||||
ngOnChanges(changes: ComponentChanges<PrimitiveFieldComponent>) {
|
||||
ngOnChanges(changes: ComponentChanges<PrimitiveFieldComponent<T>>) {
|
||||
super.ngOnChanges(changes);
|
||||
if (changes.data) this.data$.next(this.data);
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
}
|
||||
|
||||
generate(event: MouseEvent) {
|
||||
this.generate$
|
||||
.pipe(
|
||||
switchMap((generate) => generate()),
|
||||
untilDestroyed(this)
|
||||
)
|
||||
.subscribe((value) => this.control.setValue(value));
|
||||
.subscribe((value) => this.control.setValue(value as T));
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div gdColumns="1fr" gdGap="16px">
|
||||
<ng-container *ngIf="data.trueParent?.objectType !== 'union'">
|
||||
<ng-container *ngIf="hasLabel">
|
||||
<mat-checkbox *ngIf="!labelControl.disabled; else label" [formControl]="labelControl">
|
||||
<ng-container [ngTemplateOutlet]="label"></ng-container>
|
||||
</mat-checkbox>
|
||||
@ -14,6 +14,7 @@
|
||||
<ng-container *ngIf="labelControl.value">
|
||||
<cc-metadata-form
|
||||
*ngFor="let field of data.ast"
|
||||
[ngStyle]="{ 'padding-left': hasLabel && '16px' }"
|
||||
[formControl]="control.get(field.name)"
|
||||
[metadata]="data.metadata"
|
||||
[namespace]="data.namespace"
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Component, Injector, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { ValidationErrors, Validator, Validators } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { FormBuilder } from '@ngneat/reactive-forms';
|
||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||
import { FormComponentSuperclass } from '@s-libs/ng-core';
|
||||
import { Field } from '@vality/thrift-ts';
|
||||
import isNil from 'lodash-es/isNil';
|
||||
import omitBy from 'lodash-es/omitBy';
|
||||
import { merge } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, ValidatedControlSuperclass } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
|
||||
@ -17,17 +16,25 @@ import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
@Component({
|
||||
selector: 'cc-struct-form',
|
||||
templateUrl: './struct-form.component.html',
|
||||
providers: createValidatedAbstractControlProviders(StructFormComponent),
|
||||
providers: createControlProviders(StructFormComponent),
|
||||
})
|
||||
export class StructFormComponent
|
||||
extends FormComponentSuperclass<{ [N in string]: unknown }>
|
||||
implements OnChanges, Validator, OnInit
|
||||
export class StructFormComponent<T extends { [N in string]: unknown }>
|
||||
extends ValidatedControlSuperclass<T>
|
||||
implements OnChanges, OnInit
|
||||
{
|
||||
@Input() data: MetadataFormData<string, Field[]>;
|
||||
|
||||
control = this.fb.group<{ [N in string]: unknown }>({});
|
||||
control = this.fb.group<T>({} as T);
|
||||
labelControl = this.fb.control(false);
|
||||
|
||||
get hasLabel() {
|
||||
return (
|
||||
!!this.data.trueParent &&
|
||||
this.data.trueParent.objectType !== 'union' &&
|
||||
this.data.trueParent.typeGroup !== 'complex'
|
||||
);
|
||||
}
|
||||
|
||||
constructor(injector: Injector, private fb: FormBuilder) {
|
||||
super(injector);
|
||||
}
|
||||
@ -38,52 +45,46 @@ export class StructFormComponent
|
||||
.subscribe(() => {
|
||||
this.emitOutgoingValue(
|
||||
this.control.value && this.labelControl.value
|
||||
? omitBy(this.control.value, isNil)
|
||||
? (omitBy(this.control.value, isNil) as T)
|
||||
: null
|
||||
);
|
||||
});
|
||||
return super.ngOnInit();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const newControlsNames = new Set(this.data.ast.map(({ name }) => name));
|
||||
Object.keys(this.control.controls).forEach((name) => {
|
||||
if (newControlsNames.has(name)) newControlsNames.delete(name);
|
||||
else this.control.removeControl(name);
|
||||
else this.control.removeControl(name as never);
|
||||
});
|
||||
newControlsNames.forEach((name) =>
|
||||
this.control.addControl(
|
||||
name,
|
||||
name as never,
|
||||
this.fb.control(null, {
|
||||
validators:
|
||||
this.data.ast.find((f) => f.name === name)?.option === 'required'
|
||||
? [Validators.required]
|
||||
: [],
|
||||
})
|
||||
}) as never
|
||||
)
|
||||
);
|
||||
|
||||
if (this.data.isRequired) {
|
||||
this.labelControl.setValue(true);
|
||||
this.labelControl.disable();
|
||||
} else {
|
||||
this.labelControl.setValue(false);
|
||||
this.labelControl.enable();
|
||||
}
|
||||
|
||||
this.setLabelControl();
|
||||
super.ngOnChanges(changes);
|
||||
}
|
||||
|
||||
handleIncomingValue(value: { [N in string]: unknown }) {
|
||||
this.control.patchValue(value, { emitEvent: false });
|
||||
const newValue = this.labelControl.disabled || !!(value && Object.keys(value).length);
|
||||
if (this.labelControl.value !== newValue) {
|
||||
this.labelControl.setValue(newValue);
|
||||
}
|
||||
handleIncomingValue(value: T) {
|
||||
this.control.patchValue(value as never, { emitEvent: false });
|
||||
this.setLabelControl(!!(value && Object.keys(value).length));
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.labelControl.value && this.control.invalid
|
||||
? this.control.errors || { structInvalid: true }
|
||||
: null;
|
||||
private setLabelControl(value: boolean = false) {
|
||||
if (!this.hasLabel || this.data.isRequired) {
|
||||
if (!this.labelControl.value) this.labelControl.setValue(true);
|
||||
if (this.labelControl.enabled) this.labelControl.disable();
|
||||
} else {
|
||||
if (this.labelControl.value !== value) this.labelControl.setValue(value);
|
||||
if (this.labelControl.disabled) this.labelControl.enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,15 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { TypeDefs } from '@vality/thrift-ts';
|
||||
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-typedef-form',
|
||||
templateUrl: './typedef-form.component.html',
|
||||
providers: createValidatedAbstractControlProviders(TypedefFormComponent),
|
||||
providers: createControlProviders(TypedefFormComponent),
|
||||
})
|
||||
export class TypedefFormComponent
|
||||
extends WrappedFormControlSuperclass<unknown>
|
||||
implements Validator
|
||||
{
|
||||
export class TypedefFormComponent<T> extends ValidatedFormControlSuperclass<T> {
|
||||
@Input() data: MetadataFormData<string, TypeDefs[string]>;
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { Field } from '@vality/thrift-ts';
|
||||
import { merge } from 'rxjs';
|
||||
import { delay, distinctUntilChanged, map } from 'rxjs/operators';
|
||||
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, getErrorsTree } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from '../../types/metadata-form-data';
|
||||
import { getDefaultValue } from '../../utils/get-default-value';
|
||||
@ -16,23 +16,23 @@ import { getDefaultValue } from '../../utils/get-default-value';
|
||||
@Component({
|
||||
selector: 'cc-union-field',
|
||||
templateUrl: './union-field.component.html',
|
||||
providers: createValidatedAbstractControlProviders(UnionFieldComponent),
|
||||
providers: createControlProviders(UnionFieldComponent),
|
||||
})
|
||||
export class UnionFieldComponent
|
||||
extends FormComponentSuperclass<{ [N in string]: unknown }>
|
||||
export class UnionFieldComponent<T extends { [N in string]: unknown }>
|
||||
extends FormComponentSuperclass<T>
|
||||
implements OnInit, Validator
|
||||
{
|
||||
@Input() data: MetadataFormData<string, Field[]>;
|
||||
|
||||
fieldControl = new FormControl<Field>();
|
||||
internalControl = new FormControl<unknown>();
|
||||
internalControl = new FormControl<T[keyof T]>();
|
||||
|
||||
ngOnInit() {
|
||||
merge(this.fieldControl.valueChanges, this.internalControl.valueChanges)
|
||||
.pipe(
|
||||
map(() => {
|
||||
const field = this.fieldControl.value;
|
||||
return field ? { [field.name]: this.internalControl.value } : null;
|
||||
return field ? ({ [field.name]: this.internalControl.value } as T) : null;
|
||||
}),
|
||||
distinctUntilChanged(),
|
||||
delay(0),
|
||||
@ -44,14 +44,14 @@ export class UnionFieldComponent
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.fieldControl.invalid || this.internalControl.invalid
|
||||
? { unionInvalid: true }
|
||||
: null;
|
||||
return (
|
||||
(this.fieldControl.errors as ValidationErrors) || getErrorsTree(this.internalControl)
|
||||
);
|
||||
}
|
||||
|
||||
handleIncomingValue(value: { [N in string]: unknown }) {
|
||||
handleIncomingValue(value: T) {
|
||||
if (value) {
|
||||
const name = Object.keys(value)[0];
|
||||
const name: keyof T = Object.keys(value)[0];
|
||||
this.fieldControl.setValue(
|
||||
this.data.ast.find((f) => f.name === name),
|
||||
{ emitEvent: false }
|
||||
@ -66,11 +66,11 @@ export class UnionFieldComponent
|
||||
cleanInternal() {
|
||||
this.internalControl.reset(
|
||||
this.fieldControl.value
|
||||
? getDefaultValue(
|
||||
? (getDefaultValue(
|
||||
this.data.metadata,
|
||||
this.data.namespace,
|
||||
this.fieldControl.value.type
|
||||
)
|
||||
) as T[keyof T])
|
||||
: null,
|
||||
{ emitEvent: false }
|
||||
);
|
||||
|
@ -1,7 +1,4 @@
|
||||
<div
|
||||
[ngSwitch]="data?.typeGroup"
|
||||
[style]="data?.parent?.objectType === 'struct' ? 'padding-left: 16px' : ''"
|
||||
>
|
||||
<div [ngSwitch]="data?.typeGroup">
|
||||
<cc-primitive-field
|
||||
*ngSwitchCase="'primitive'"
|
||||
[formControl]="control"
|
||||
|
@ -1,21 +1,20 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { WrappedFormControlSuperclass } from '@s-libs/ng-core';
|
||||
import { Validator } from '@angular/forms';
|
||||
import { Field, ValueType } from '@vality/thrift-ts';
|
||||
|
||||
import { ThriftAstMetadata } from '@cc/app/api/utils';
|
||||
import { MetadataFormExtension } from '@cc/app/shared/components/metadata-form/types/metadata-form-extension';
|
||||
import { createValidatedAbstractControlProviders } from '@cc/utils';
|
||||
import { createControlProviders, ValidatedFormControlSuperclass } from '@cc/utils';
|
||||
|
||||
import { MetadataFormData } from './types/metadata-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-metadata-form',
|
||||
templateUrl: './metadata-form.component.html',
|
||||
providers: createValidatedAbstractControlProviders(MetadataFormComponent),
|
||||
providers: createControlProviders(MetadataFormComponent),
|
||||
})
|
||||
export class MetadataFormComponent
|
||||
extends WrappedFormControlSuperclass<unknown>
|
||||
export class MetadataFormComponent<T>
|
||||
extends ValidatedFormControlSuperclass<T>
|
||||
implements OnChanges, Validator
|
||||
{
|
||||
@Input() metadata: ThriftAstMetadata[];
|
||||
@ -39,8 +38,4 @@ export class MetadataFormComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
@ -47,6 +48,7 @@ import { MetadataFormComponent } from './metadata-form.component';
|
||||
ValueTypeTitleModule,
|
||||
MatCheckboxModule,
|
||||
MatChipsModule,
|
||||
MatRadioModule,
|
||||
],
|
||||
declarations: [
|
||||
MetadataFormComponent,
|
||||
|
@ -9,20 +9,17 @@ import { catchError, map, pluck, shareReplay, startWith } from 'rxjs/operators';
|
||||
import { PartyManagementWithUserService } from '@cc/app/api/payment-processing';
|
||||
import { NotificationService } from '@cc/app/shared/services/notification';
|
||||
import { Option } from '@cc/components/select-search-field';
|
||||
import {
|
||||
createValidatedAbstractControlProviders,
|
||||
ValidatedWrappedAbstractControlSuperclass,
|
||||
} from '@cc/utils/forms';
|
||||
import { createControlProviders, ValidatedControlSuperclass } from '@cc/utils/forms';
|
||||
|
||||
@UntilDestroy()
|
||||
@Component({
|
||||
selector: 'cc-payout-tool-field',
|
||||
templateUrl: 'payout-tool-field.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: createValidatedAbstractControlProviders(PayoutToolFieldComponent),
|
||||
providers: createControlProviders(PayoutToolFieldComponent),
|
||||
})
|
||||
export class PayoutToolFieldComponent
|
||||
extends ValidatedWrappedAbstractControlSuperclass<PartyID>
|
||||
extends ValidatedControlSuperclass<PartyID>
|
||||
implements OnInit
|
||||
{
|
||||
@Input() label: string;
|
||||
|
@ -15,10 +15,7 @@ import { filter, map, share, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { PartyManagementWithUserService } from '@cc/app/api/payment-processing';
|
||||
import { ComponentChanges } from '@cc/app/shared/utils';
|
||||
import {
|
||||
createValidatedAbstractControlProviders,
|
||||
ValidatedWrappedAbstractControlSuperclass,
|
||||
} from '@cc/utils/forms';
|
||||
import { createControlProviders, ValidatedControlSuperclass } from '@cc/utils/forms';
|
||||
import { RequiredSuper } from '@cc/utils/required-super';
|
||||
|
||||
@UntilDestroy()
|
||||
@ -26,11 +23,11 @@ import { RequiredSuper } from '@cc/utils/required-super';
|
||||
selector: 'cc-shop-field',
|
||||
templateUrl: './shop-field.component.html',
|
||||
styleUrls: ['./shop-field.component.scss'],
|
||||
providers: createValidatedAbstractControlProviders(ShopFieldComponent),
|
||||
providers: createControlProviders(ShopFieldComponent),
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShopFieldComponent<M extends boolean = boolean>
|
||||
extends ValidatedWrappedAbstractControlSuperclass<
|
||||
extends ValidatedControlSuperclass<
|
||||
M extends true ? Shop[] : Shop,
|
||||
M extends true ? ShopID[] : ShopID
|
||||
>
|
||||
|
@ -0,0 +1,68 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainObject } from '@vality/domain-proto/lib/domain';
|
||||
import { Field } from '@vality/thrift-ts';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { ThriftAstMetadata } from '@cc/app/api/utils';
|
||||
|
||||
import { DomainStoreService } from '../../../thrift-services/damsel/domain-store.service';
|
||||
import { MetadataFormData, MetadataFormExtension } from '../../components/metadata-form';
|
||||
import { createDomainObjectExtension } from './utils/create-domain-object-extension';
|
||||
import {
|
||||
defaultDomainObjectToOption,
|
||||
DOMAIN_OBJECTS_TO_OPTIONS,
|
||||
OtherDomainObjects,
|
||||
} from './utils/domains-objects-to-options';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DomainMetadataFormExtensionsService {
|
||||
extensions$: Observable<MetadataFormExtension[]> = from(
|
||||
import('@vality/domain-proto/lib/metadata.json').then(
|
||||
(m) => m.default as never as ThriftAstMetadata[]
|
||||
)
|
||||
).pipe(
|
||||
map((metadata) => this.createDomainObjectsOptions(metadata)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private domainStoreService: DomainStoreService) {}
|
||||
|
||||
private createDomainObjectsOptions(metadata: ThriftAstMetadata[]): MetadataFormExtension[] {
|
||||
const domainFields = new MetadataFormData<string, Field[]>(
|
||||
metadata,
|
||||
'domain',
|
||||
'DomainObject'
|
||||
).ast;
|
||||
return domainFields
|
||||
.filter(
|
||||
(f) => !(f.name in DOMAIN_OBJECTS_TO_OPTIONS) || DOMAIN_OBJECTS_TO_OPTIONS[f.name]
|
||||
)
|
||||
.map((f) =>
|
||||
this.createFieldOptions(metadata, f.type as string, f.name as keyof DomainObject)
|
||||
);
|
||||
}
|
||||
|
||||
private createFieldOptions(
|
||||
metadata: ThriftAstMetadata[],
|
||||
objectType: string,
|
||||
objectKey: keyof DomainObject
|
||||
): MetadataFormExtension {
|
||||
const objectFields = new MetadataFormData<string, Field[]>(metadata, 'domain', objectType)
|
||||
.ast;
|
||||
const refType = objectFields.find((n) => n.name === 'ref').type as string;
|
||||
return createDomainObjectExtension(refType, () =>
|
||||
this.domainStoreService.getObjects(objectKey).pipe(
|
||||
map((objects) => {
|
||||
const domainObjectToOption =
|
||||
objectKey in DOMAIN_OBJECTS_TO_OPTIONS
|
||||
? DOMAIN_OBJECTS_TO_OPTIONS[objectKey as keyof OtherDomainObjects]
|
||||
: defaultDomainObjectToOption;
|
||||
return objects.map(domainObjectToOption);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './domain-metadata-form-extensions.service';
|
@ -1,11 +1,15 @@
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { isTypeWithAliases, MetadataFormExtension } from '@cc/app/shared';
|
||||
import {
|
||||
isTypeWithAliases,
|
||||
MetadataFormExtension,
|
||||
MetadataFormExtensionOption,
|
||||
} from '../../../components';
|
||||
|
||||
export function createDomainObjectMetadataFormExtension(
|
||||
export function createDomainObjectExtension(
|
||||
refType: string,
|
||||
getObjects: () => Observable<{ ref: { id: number }; data: { name?: string } }[]>
|
||||
options: () => Observable<MetadataFormExtensionOption[]>
|
||||
): MetadataFormExtension {
|
||||
return {
|
||||
determinant: (data) =>
|
||||
@ -14,14 +18,17 @@ export function createDomainObjectMetadataFormExtension(
|
||||
isTypeWithAliases(data, 'ObjectID', 'domain')
|
||||
),
|
||||
extension: () =>
|
||||
getObjects().pipe(
|
||||
options().pipe(
|
||||
map((objects) => ({
|
||||
options: objects
|
||||
.sort((a, b) => a.ref.id - b.ref.id)
|
||||
.sort((a, b) =>
|
||||
typeof a.value === 'number' && typeof b.value === 'number'
|
||||
? a.value - b.value
|
||||
: 0
|
||||
)
|
||||
.map((o) => ({
|
||||
label: o.data.name,
|
||||
value: o.ref.id,
|
||||
details: o,
|
||||
...o,
|
||||
})),
|
||||
isIdentifier: true,
|
||||
}))
|
@ -0,0 +1,31 @@
|
||||
import { DomainObject } from '@vality/domain-proto';
|
||||
import { PickByValue } from 'utility-types';
|
||||
|
||||
import { MetadataFormExtensionOption } from '../../../components';
|
||||
|
||||
type DomainRefDataObjects = PickByValue<
|
||||
DomainObject,
|
||||
{
|
||||
ref: { id: number | string };
|
||||
data: { name?: string; id?: string };
|
||||
}
|
||||
>;
|
||||
export type OtherDomainObjects = Omit<DomainObject, keyof DomainRefDataObjects>;
|
||||
export const DOMAIN_OBJECTS_TO_OPTIONS: {
|
||||
[N in keyof OtherDomainObjects]-?: (o: OtherDomainObjects[N]) => MetadataFormExtensionOption;
|
||||
} = {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
currency: (o) => ({ value: o.ref.symbolic_code, label: o.data.name }),
|
||||
payment_method: null,
|
||||
globals: null,
|
||||
identity_provider: (o) => ({ value: o.ref.id }),
|
||||
dummy_link: (o) => ({ value: o.ref.id, label: o.data.link.id }),
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
};
|
||||
|
||||
export function defaultDomainObjectToOption(o: DomainRefDataObjects[keyof DomainRefDataObjects]) {
|
||||
let label: string;
|
||||
if ('name' in o.data) label = o.data.name;
|
||||
if ('id' in o.data && !label) label = o.data.id;
|
||||
return { value: o.ref.id, label };
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { ErrorService } from './error.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [ErrorService],
|
||||
})
|
||||
export class ErrorModule {}
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { NotificationService } from '../notification';
|
||||
|
||||
// TODO: collect error information
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ErrorService {
|
||||
constructor(private notificationService: NotificationService) {}
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './error.module';
|
||||
export * from './error.service';
|
||||
|
@ -6,3 +6,4 @@ export * from './user-info-based-id-generator';
|
||||
export * from './partial-fetcher';
|
||||
export * from './query-params';
|
||||
export * from './moment-utc-date-adapter';
|
||||
export * from './domain-metadata-form-extensions';
|
||||
|
@ -16,8 +16,14 @@ export class BaseDialogService {
|
||||
|
||||
open<C, D, R, S>(
|
||||
dialogComponent: ComponentType<BaseDialogSuperclass<C, D, R, S>>,
|
||||
data: D = null,
|
||||
configOrConfigName: Omit<MatDialogConfig<D>, 'data'> | keyof DialogConfig = {}
|
||||
/**
|
||||
* Workaround when both conditions for the 'data' argument must be true:
|
||||
* - typing did not require passing when it is optional (for example: {param: number} | void)
|
||||
* - typing required to pass when it is required (for example: {param: number})
|
||||
*/
|
||||
...[data, configOrConfigName]: D extends void
|
||||
? []
|
||||
: [data: D, configOrConfigName?: Omit<MatDialogConfig<D>, 'data'> | keyof DialogConfig]
|
||||
): MatDialogRef<C, BaseDialogResponse<R, S>> {
|
||||
return this.dialog.open(dialogComponent as never, {
|
||||
data,
|
||||
|
@ -1,4 +1,4 @@
|
||||
<cc-base-dialog [title]="dialogData?.title || 'Confirm this action'" noContent>
|
||||
<cc-base-dialog [title]="title || 'Confirm this action'" noContent>
|
||||
<cc-base-dialog-actions>
|
||||
<button mat-button (click)="cancel()">CANCEL</button>
|
||||
<button mat-raised-button color="primary" (click)="confirm()">CONFIRM</button>
|
||||
|
@ -9,8 +9,12 @@ import { BaseDialogResponseStatus, BaseDialogSuperclass } from '@cc/components/b
|
||||
})
|
||||
export class ConfirmActionDialogComponent extends BaseDialogSuperclass<
|
||||
ConfirmActionDialogComponent,
|
||||
{ title?: string }
|
||||
{ title?: string } | void
|
||||
> {
|
||||
get title() {
|
||||
return typeof this.dialogData === 'object' ? this.dialogData.title : '';
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close({ status: BaseDialogResponseStatus.Cancelled });
|
||||
}
|
||||
|
@ -1,25 +1,22 @@
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { FormGroup, FormArray } from '@ngneat/reactive-forms';
|
||||
import { ControlsValue } from '@ngneat/reactive-forms/lib/types';
|
||||
|
||||
function hasControls<T>(control: AbstractControl): control is FormGroup<T> | FormArray<T> {
|
||||
return !!(control as any)?.controls;
|
||||
}
|
||||
import { hasControls } from './has-controls';
|
||||
|
||||
export function getValue<T extends AbstractControl>(control: T): T['value'] {
|
||||
if (!hasControls(control)) {
|
||||
return control.value;
|
||||
return control.value as never;
|
||||
}
|
||||
if (Array.isArray(control.controls)) {
|
||||
const result: ControlsValue<T>[] = [];
|
||||
for (const v of control.controls) {
|
||||
result.push(getValue(v as any));
|
||||
result.push(getValue(v) as ControlsValue<T>);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const result: Partial<ControlsValue<T>> = {};
|
||||
for (const [k, v] of Object.entries(control.controls)) {
|
||||
result[k] = getValue(v as any);
|
||||
result[k] = getValue(v as AbstractControl) as ControlsValue<T>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
6
src/utils/forms/has-controls.ts
Normal file
6
src/utils/forms/has-controls.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { FormArray, FormGroup } from '@ngneat/reactive-forms';
|
||||
|
||||
export function hasControls<T>(control: AbstractControl): control is FormGroup<T> | FormArray<T> {
|
||||
return 'controls' in control;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
export * from './set-form-array-value';
|
||||
export * from './get-form-value-changes';
|
||||
export * from './get-form-validation-changes';
|
||||
export * from './validated-wrapped-abstract-control-superclass';
|
||||
export * from './validated-control-superclass';
|
||||
export * from './switch-control';
|
||||
|
@ -4,6 +4,7 @@ import { provideValueAccessor } from '@s-libs/ng-core';
|
||||
|
||||
import { provideValidator } from './provide-validator';
|
||||
|
||||
export const createValidatedAbstractControlProviders = (
|
||||
component: ComponentType<unknown>
|
||||
): Provider[] => [provideValueAccessor(component), provideValidator(component)];
|
||||
export const createControlProviders = (component: ComponentType<unknown>): Provider[] => [
|
||||
provideValueAccessor(component),
|
||||
provideValidator(component),
|
||||
];
|
4
src/utils/forms/validated-control-superclass/index.ts
Normal file
4
src/utils/forms/validated-control-superclass/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './validated-control-superclass.directive';
|
||||
export * from './provide-validator';
|
||||
export * from './create-control-providers';
|
||||
export { getErrorsTree } from '@cc/utils/forms/validated-control-superclass/utils/get-errors-tree';
|
@ -0,0 +1,27 @@
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
|
||||
import { hasControls } from '../../has-controls';
|
||||
|
||||
/**
|
||||
* FormGroup/FormArray don't return internal control errors,
|
||||
* so you need to get internal errors manually
|
||||
*/
|
||||
export function getErrorsTree(control: AbstractControl): ValidationErrors | null {
|
||||
if (control.valid) {
|
||||
return null;
|
||||
}
|
||||
const errors: ValidationErrors = Object.assign({}, control.errors);
|
||||
if (hasControls(control)) {
|
||||
if (Array.isArray(control.controls)) {
|
||||
errors.formArrayErrors = control.controls.map((c) => getErrorsTree(c));
|
||||
} else {
|
||||
errors.formGroupErrors = Object.fromEntries(
|
||||
Array.from(Object.entries(control.controls)).map(([k, c]) => [
|
||||
k,
|
||||
getErrorsTree(c as AbstractControl),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
import { Directive, OnInit } from '@angular/core';
|
||||
import { ValidationErrors, Validator } from '@angular/forms';
|
||||
import { FormControl } from '@ngneat/reactive-forms';
|
||||
import { WrappedControlSuperclass } from '@s-libs/ng-core';
|
||||
|
||||
import { RequiredSuper, REQUIRED_SUPER } from '../../required-super';
|
||||
import { REQUIRED_SUPER, RequiredSuper } from '../../required-super';
|
||||
import { getValue } from '../get-value';
|
||||
import { getErrorsTree } from './utils/get-errors-tree';
|
||||
|
||||
@Directive()
|
||||
export abstract class ValidatedWrappedAbstractControlSuperclass<OuterType, InnerType = OuterType>
|
||||
export abstract class ValidatedControlSuperclass<OuterType, InnerType = OuterType>
|
||||
extends WrappedControlSuperclass<OuterType, InnerType>
|
||||
implements OnInit, Validator
|
||||
{
|
||||
@ -19,14 +21,21 @@ export abstract class ValidatedWrappedAbstractControlSuperclass<OuterType, Inner
|
||||
}
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.control.errors;
|
||||
return getErrorsTree(this.control);
|
||||
}
|
||||
|
||||
protected outerToInner(outer: OuterType): InnerType {
|
||||
if (typeof this.emptyValue === 'object') {
|
||||
if (!outer) return this.emptyValue;
|
||||
return { ...this.emptyValue, ...outer };
|
||||
if (!outer && 'controls' in this.control) {
|
||||
return this.emptyValue;
|
||||
}
|
||||
return outer as unknown as InnerType;
|
||||
return outer as never;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export class ValidatedFormControlSuperclass<
|
||||
OuterType,
|
||||
InnerType = OuterType
|
||||
> extends ValidatedControlSuperclass<OuterType, InnerType> {
|
||||
control = new FormControl<InnerType>();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export * from './validated-wrapped-abstract-control-superclass';
|
||||
export * from './provide-validator';
|
||||
export * from './create-validated-abstract-control-providers';
|
Loading…
Reference in New Issue
Block a user