Moving to table2 with lazy column (#405)

This commit is contained in:
Rinat Arsaev 2024-10-18 22:07:21 +09:00 committed by GitHub
parent 753ad0d727
commit 9e3b6d5ae8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 745 additions and 838 deletions

8
package-lock.json generated
View File

@ -26,7 +26,7 @@
"@vality/fistful-proto": "2.0.1-88e69a5.0",
"@vality/machinegun-proto": "1.0.1-3decc8f.0",
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
"@vality/ng-core": "18.4.1-pr-74-55d4acd.0",
"@vality/ng-core": "18.4.1-pr-78-9131f9a.0",
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
"@vality/repairer-proto": "2.0.2-07b73e9.0",
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",
@ -5969,9 +5969,9 @@
"integrity": "sha512-XWF7qM/CARRAey0scGVhfGU6jNq+UdlGE2mg3jn4eIFDuIWQJqsT+Bah300RBUrl+XgFsmj95C6HWRfeA5Q8kw=="
},
"node_modules/@vality/ng-core": {
"version": "18.4.1-pr-74-55d4acd.0",
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-18.4.1-pr-74-55d4acd.0.tgz",
"integrity": "sha512-EkGTPth5+HA0MLc+BYLIV5ie7xnwBrXftTxoVJw+5zlcWoLm5V1+10AGz6wmhX4VIunFFZR/p9XUNAhNqvluVw==",
"version": "18.4.1-pr-78-9131f9a.0",
"resolved": "https://registry.npmjs.org/@vality/ng-core/-/ng-core-18.4.1-pr-78-9131f9a.0.tgz",
"integrity": "sha512-qmTGnGe+pBWlIQ+cX64pt2aLWdoLM6M2Apc180dQQrLca4VBHB4Oh8yo7vvv/sxws1DLXEzWutgNzi9/bRdtZQ==",
"dependencies": {
"@angular/material-date-fns-adapter": "^18.2.2",
"@ng-matero/extensions": "^18.2.0",

View File

@ -35,7 +35,7 @@
"@vality/fistful-proto": "2.0.1-88e69a5.0",
"@vality/machinegun-proto": "1.0.1-3decc8f.0",
"@vality/magista-proto": "2.0.2-ec1bdb9.0",
"@vality/ng-core": "18.4.1-pr-74-55d4acd.0",
"@vality/ng-core": "18.4.1-pr-78-9131f9a.0",
"@vality/ng-thrift": "18.0.1-pr-13-bdb6d51.0",
"@vality/repairer-proto": "2.0.2-07b73e9.0",
"@vality/scrooge-proto": "0.1.1-9ce7fc6.0",

View File

@ -12,8 +12,8 @@ import { environment } from '../environments/environment';
import { ROUTING_CONFIG as CLAIMS_ROUTING_CONFIG } from './sections/claims/routing-config';
import { ROUTING_CONFIG as DEPOSITS_ROUTING_CONFIG } from './sections/deposits/routing-config';
import { ROUTING_CONFIG as DOMAIN_ROUTING_CONFIG } from './sections/domain/routing-config';
import { ROUTING_CONFIG as MACHINES_ROUTING_CONFIG } from './sections/machines/routing-config';
import { ROUTING_CONFIG as PAYMENTS_ROUTING_CONFIG } from './sections/payments/routing-config';
import { ROUTING_CONFIG as REPAIRING_ROUTING_CONFIG } from './sections/repairing/routing-config';
import { ROUTING_CONFIG as PARTIES_ROUTING_CONFIG } from './sections/search-parties/routing-config';
import { SHOPS_ROUTING_CONFIG } from './sections/shops';
import { ROUTING_CONFIG as SOURCES_ROUTING_CONFIG } from './sections/sources/routing-config';
@ -73,7 +73,7 @@ export class AppComponent {
{
label: 'Machines',
url: '/machines',
services: REPAIRING_ROUTING_CONFIG.services,
services: MACHINES_ROUTING_CONFIG.services,
},
{
label: 'Sources',

View File

@ -1,4 +1,4 @@
<cc-page-layout title="Chargebacks">
<cc-page-layout fullHeight title="Chargebacks">
<cc-page-layout-actions>
<v-more-filters-button [filters]="filters"></v-more-filters-button>
</cc-page-layout-actions>
@ -47,14 +47,6 @@
(more)="more()"
(update)="reload($event)"
>
<button
[disabled]="!selected.length"
color="primary"
mat-raised-button
(click)="changeStatuses()"
>
Change statuses
</button>
<button color="primary" mat-raised-button (click)="create()">Create by file</button>
</cc-chargebacks-table>
</cc-page-layout>

View File

@ -29,7 +29,6 @@ import {
CHARGEBACK_CATEGORIES,
} from '@cc/app/api/fistful-stat';
import { ChangeChargebacksStatusDialogComponent } from '../../shared/components/change-chargebacks-status-dialog';
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
import { CreateChargebacksByFileDialogComponent } from './components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component';
@ -46,7 +45,7 @@ type FormValue = {
@Component({
selector: 'cc-chargebacks',
templateUrl: './chargebacks.component.html',
styles: [],
providers: [FetchChargebacksService],
})
export class ChargebacksComponent implements OnInit {
filtersForm = new FormGroup(
@ -141,17 +140,4 @@ export class ChargebacksComponent implements OnInit {
});
});
}
changeStatuses() {
this.dialog
.open(ChangeChargebacksStatusDialogComponent, { chargebacks: this.selected })
.afterClosed()
.pipe(
filter((res) => res.status === DialogResponseStatus.Success),
takeUntilDestroyed(this.dr),
)
.subscribe(() => {
this.reload();
});
}
}

View File

@ -20,6 +20,8 @@ import {
} from '@vality/ng-core';
import { ThriftPipesModule } from '@vality/ng-thrift';
import { ChargebacksTableComponent } from '@cc/app/shared/components/chargebacks-table';
import { UploadCsvComponent } from '../../../components/upload-csv';
import { PageLayoutModule, ShopFieldModule } from '../../shared';
import { MerchantFieldModule } from '../../shared/components/merchant-field';
@ -31,15 +33,10 @@ import {
import { ChargebacksRoutingModule } from './chargebacks-routing.module';
import { ChargebacksComponent } from './chargebacks.component';
import { ChargebacksTableComponent } from './components/chargebacks-table/chargebacks-table.component';
import { CreateChargebacksByFileDialogComponent } from './components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component';
@NgModule({
declarations: [
ChargebacksComponent,
ChargebacksTableComponent,
CreateChargebacksByFileDialogComponent,
],
declarations: [ChargebacksComponent, CreateChargebacksByFileDialogComponent],
imports: [
CommonModule,
ChargebacksRoutingModule,
@ -67,6 +64,7 @@ import { CreateChargebacksByFileDialogComponent } from './components/create-char
MatInputModule,
MatCheckboxModule,
UploadCsvComponent,
ChargebacksTableComponent,
],
})
export class ChargebacksModule {}

View File

@ -1,144 +0,0 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { InvoicePaymentChargebackStatus } from '@vality/magista-proto/internal/proto/domain';
import { StatChargeback } from '@vality/magista-proto/magista';
import { LoadOptions, Column, TagColumn } from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import startCase from 'lodash-es/startCase';
import { AmountCurrencyService } from '../../../../shared/services';
@Component({
selector: 'cc-chargebacks-table',
templateUrl: './chargebacks-table.component.html',
styles: [],
})
export class ChargebacksTableComponent {
@Input() data!: StatChargeback[];
@Input() isLoading?: boolean | null;
@Input() hasMore?: boolean | null;
@Input() selected?: StatChargeback[];
@Output() selectedChange = new EventEmitter<StatChargeback[]>();
@Output() update = new EventEmitter<LoadOptions>();
@Output() more = new EventEmitter<void>();
columns: Column<StatChargeback>[] = [
{ field: 'chargeback_id', header: 'Id' },
{
field: 'chargeback_reason',
header: 'Reason',
formatter: (data) => startCase(getUnionKey(data.chargeback_reason.category)),
description: (data) => data.chargeback_reason.code,
},
{
field: 'chargeback_status',
type: 'tag',
header: 'Status',
formatter: (data) => getUnionKey(data.chargeback_status),
typeParameters: {
label: (data) => startCase(getUnionKey(data.chargeback_status)),
tags: {
pending: { color: 'pending' },
accepted: { color: 'success' },
rejected: { color: 'warn' },
cancelled: { color: 'neutral' },
},
},
} as TagColumn<StatChargeback, keyof InvoicePaymentChargebackStatus>,
{
field: 'amount',
type: 'currency',
formatter: (data) =>
this.amountCurrencyService.toMajor(data.amount, data.currency_code.symbolic_code),
typeParameters: {
currencyCode: (data) => data.currency_code.symbolic_code,
},
},
{
field: 'levy_amount',
type: 'currency',
formatter: (data) =>
this.amountCurrencyService.toMajor(
data.levy_amount,
data.levy_currency_code.symbolic_code,
),
typeParameters: {
currencyCode: (data) => data.levy_currency_code.symbolic_code,
},
},
{
field: 'fee',
type: 'currency',
formatter: (data) =>
this.amountCurrencyService.toMajor(data.fee, data.currency_code.symbolic_code),
typeParameters: {
currencyCode: (data) => data.currency_code.symbolic_code,
},
},
{
field: 'provider_fee',
type: 'currency',
formatter: (data) =>
this.amountCurrencyService.toMajor(
data.provider_fee,
data.currency_code.symbolic_code,
),
typeParameters: {
currencyCode: (data) => data.currency_code.symbolic_code,
},
},
{
field: 'external_fee',
type: 'currency',
formatter: (data) =>
this.amountCurrencyService.toMajor(
data.external_fee,
data.currency_code.symbolic_code,
),
typeParameters: {
currencyCode: (data) => data.currency_code.symbolic_code,
},
},
{
field: 'stage',
formatter: (data) => getUnionKey(data.stage),
},
{
field: 'invoice_id',
header: 'Invoice Id (Payment Id)',
description: 'payment_id',
link: (data) =>
`/party/${data.party_id}/invoice/${data.invoice_id}/payment/${data.payment_id}`,
},
{
field: 'created_at',
type: 'datetime',
},
{
field: 'party_id',
header: 'Party',
},
{
field: 'shop_id',
header: 'Shop',
},
{
field: 'external_id',
hide: true,
},
{
field: 'content.type',
description: 'content.data',
header: 'Content',
hide: true,
},
// createOperationColumn<StatChargeback>([
// {
// label: 'Change status',
// click: (data) => undefined,
// },
// ]),
];
constructor(private amountCurrencyService: AmountCurrencyService) {}
}

View File

@ -6,9 +6,7 @@ import { map, catchError } from 'rxjs/operators';
import { MerchantStatisticsService } from '../../api/magista';
@Injectable({
providedIn: 'root',
})
@Injectable()
export class FetchChargebacksService extends FetchSuperclass<
StatChargeback,
ChargebackSearchQuery

View File

@ -1,5 +1,5 @@
<v-table
[columns]="columns()"
<v-table2
[columns]="columns"
[data]="data"
[hasMore]="hasMore"
[progress]="isLoading"
@ -7,4 +7,4 @@
(update)="update.emit($event)"
>
<v-table-actions><ng-content></ng-content></v-table-actions>
</v-table>
</v-table2>

View File

@ -1,20 +1,11 @@
import {
Component,
Input,
Output,
EventEmitter,
booleanAttribute,
input,
computed,
} from '@angular/core';
import { Router } from '@angular/router';
import { Claim, ClaimStatus } from '@vality/domain-proto/claim_management';
import { Column, LoadOptions, TagColumn, createOperationColumn } from '@vality/ng-core';
import { Component, Input, Output, EventEmitter, booleanAttribute, input } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { Claim } from '@vality/domain-proto/claim_management';
import { Column2, LoadOptions, createMenuColumn } from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import isObject from 'lodash-es/isObject';
import startCase from 'lodash-es/startCase';
import { createPartyColumn } from '../../../shared';
import { createPartyColumn } from '@cc/app/shared/utils/table2';
@Component({
selector: 'cc-claims-table',
@ -30,48 +21,33 @@ export class ClaimsTableComponent {
@Output() update = new EventEmitter<LoadOptions>();
@Output() more = new EventEmitter<void>();
columns = computed<Column<Claim>[]>(() =>
this.sourceColumns.filter(
(c) => (isObject(c) && c?.field !== 'party_id') || !this.noParty(),
),
);
private sourceColumns: Column<Claim>[] = [
{ field: 'id', link: (d) => this.getClaimLink(d.party_id, d.id) },
createPartyColumn('party_id'),
columns: Column2<Claim>[] = [
{ field: 'id', cell: (d) => ({ link: () => `/party/${d.party_id}/claim/${d.id}` }) },
createPartyColumn((d) => ({ id: d.party_id }), { hidden: toObservable(this.noParty) }),
{
field: 'status',
type: 'tag',
formatter: (claim) => getUnionKey(claim.status),
typeParameters: {
label: (claim) => startCase(getUnionKey(claim.status)),
cell: (d) => ({
value: startCase(getUnionKey(d.status)),
tags: {
pending: { color: 'pending' },
review: { color: 'pending' },
pending_acceptance: { color: 'pending' },
accepted: { color: 'success' },
denied: { color: 'warn' },
revoked: { color: 'neutral' },
pending: 'pending',
review: 'pending',
pending_acceptance: 'pending',
accepted: 'success',
denied: 'warn',
revoked: 'neutral',
},
},
} as TagColumn<Claim, keyof ClaimStatus>,
'revision',
{ field: 'created_at', type: 'datetime' },
{ field: 'updated_at', type: 'datetime' },
createOperationColumn<Claim>([
{
label: 'Details',
click: (claim) => this.navigateToClaim(claim.party_id, claim.id),
},
]),
}),
},
{ field: 'revision' },
{ field: 'created_at', cell: { type: 'datetime' } },
{ field: 'updated_at', cell: { type: 'datetime' } },
createMenuColumn((d) => ({
items: [
{
label: 'Details',
link: () => `/party/${d.party_id}/claim/${d.id}`,
},
],
})),
];
constructor(private router: Router) {}
navigateToClaim(partyId: string, claimID: number) {
void this.router.navigate([this.getClaimLink(partyId, claimID)]);
}
private getClaimLink(partyId: string, claimID: number): string {
return `/party/${partyId}/claim/${claimID}`;
}
}

View File

@ -1,4 +1,4 @@
<cc-page-layout title="Claims">
<cc-page-layout fullHeight title="Claims">
<cc-page-layout-actions
><v-more-filters-button [filters]="filters"></v-more-filters-button
></cc-page-layout-actions>

View File

@ -1,6 +1,6 @@
<div style="display: flex; flex-direction: column; gap: 24px">
<h1 class="mat-headline-5">Reverts</h1>
<v-table
<v-table2
[columns]="columns"
[data]="reverts$ | async"
[hasMore]="hasMore$ | async"
@ -18,5 +18,5 @@
Create revert
</button>
</v-table-actions>
</v-table>
</v-table2>
</div>

View File

@ -1,11 +1,11 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { DepositStatus, StatDeposit, StatDepositRevert } from '@vality/fistful-proto/fistful_stat';
import { DialogService, Column, UpdateOptions } from '@vality/ng-core';
import { DialogService, UpdateOptions, Column2 } from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import startCase from 'lodash-es/startCase';
import { filter } from 'rxjs/operators';
import { createCurrencyColumn } from '@cc/app/shared/utils';
import { createCurrencyColumn } from '@cc/app/shared/utils/table2';
import { CreateRevertDialogComponent } from './create-revert-dialog/create-revert-dialog.component';
import { FetchRevertsService } from './services/fetch-reverts/fetch-reverts.service';
@ -23,27 +23,29 @@ export class RevertsComponent implements OnInit {
reverts$ = this.fetchRevertsService.result$;
hasMore$ = this.fetchRevertsService.hasMore$;
isLoading$ = this.fetchRevertsService.isLoading$;
columns: Column<StatDepositRevert>[] = [
columns: Column2<StatDepositRevert>[] = [
{ field: 'id' },
{
field: 'status',
type: 'tag',
formatter: (d) => getUnionKey(d.status),
typeParameters: {
label: (d) => startCase(getUnionKey(d.status)),
tags: {
pending: { color: 'pending' },
succeeded: { color: 'success' },
failed: { color: 'warn' },
},
},
cell: (d) => ({
value: startCase(getUnionKey(d.status)),
color: (
{
pending: 'pending',
succeeded: 'success',
failed: 'warn',
} as const
)[getUnionKey(d.status)],
}),
},
createCurrencyColumn(
'amount',
(d) => d.body.amount,
(d) => d.body.currency.symbolic_code,
(d) => ({
amount: d.body.amount,
code: d.body.currency.symbolic_code,
}),
{ header: 'Amount' },
),
{ field: 'created_at', type: 'datetime' },
{ field: 'created_at', cell: { type: 'datetime' } },
];
constructor(

View File

@ -1,4 +1,4 @@
<cc-page-layout title="Deposits">
<cc-page-layout fullHeight title="Deposits">
<cc-page-layout-actions>
<v-more-filters-button [filters]="filters"></v-more-filters-button>
</cc-page-layout-actions>
@ -22,7 +22,7 @@
<cc-merchant-field formControlName="party_id"></cc-merchant-field>
</ng-template>
</v-filters>
<v-table
<v-table2
[columns]="columns"
[data]="deposits$ | async"
[hasMore]="hasMore$ | async"
@ -31,10 +31,8 @@
(update)="reload($event)"
>
<v-table-actions>
<button color="primary" mat-raised-button (click)="createByFile()">
Create by file
</button>
<button mat-raised-button (click)="createByFile()">Create by file</button>
<button color="primary" mat-raised-button (click)="createDeposit()">Create</button>
</v-table-actions>
</v-table>
</v-table2>
</cc-page-layout>

View File

@ -2,10 +2,9 @@ import { ChangeDetectionStrategy, Component, OnInit, Inject, DestroyRef } from '
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NonNullableFormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { StatDeposit, RevertStatus } from '@vality/fistful-proto/fistful_stat';
import { fistful_stat } from '@vality/fistful-proto';
import { StatDeposit } from '@vality/fistful-proto/fistful_stat';
import {
Column,
createOperationColumn,
UpdateOptions,
createDateRangeToToday,
QueryParamsService,
@ -17,26 +16,24 @@ import {
debounceTimeWithFirst,
getValueChanges,
countChanged,
Column2,
getEnumKey,
createMenuColumn,
} from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import { endOfDay } from 'date-fns';
import startCase from 'lodash-es/startCase';
import { filter, map, shareReplay } from 'rxjs/operators';
import { createCurrencyColumn } from '@cc/app/shared/utils/table2';
import { QueryDsl } from '../../api/fistful-stat';
import { createCurrencyColumn } from '../../shared';
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
import { CreateDepositDialogComponent } from './components/create-deposit-dialog/create-deposit-dialog.component';
import { CreateDepositsByFileDialogComponent } from './components/create-deposits-by-file-dialog/create-deposits-by-file-dialog.component';
import { FetchDepositsService } from './services/fetch-deposits/fetch-deposits.service';
const REVERT_STATUS: { [N in RevertStatus]: string } = {
0: 'none',
1: 'partial',
2: 'full',
};
@Component({
templateUrl: 'deposits.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
@ -56,30 +53,31 @@ export class DepositsComponent implements OnInit {
deposits$ = this.fetchDepositsService.result$;
hasMore$ = this.fetchDepositsService.hasMore$;
isLoading$ = this.fetchDepositsService.isLoading$;
columns: Column<StatDeposit>[] = [
columns: Column2<StatDeposit>[] = [
{
field: 'id',
formatter: (d) => d.description || `#${d.id}`,
link: (d) => `/deposits/${d.id}`,
description: 'id',
maxWidth: 'max(300px, 30vw)',
cell: (d) => ({
value: d.description || `#${d.id}`,
link: () => `/deposits/${d.id}`,
description: d.id,
}),
},
{
field: 'status',
type: 'tag',
formatter: (d) => getUnionKey(d.status),
typeParameters: {
label: (d) => startCase(getUnionKey(d.status)),
tags: {
pending: { color: 'pending' },
succeeded: { color: 'success' },
failed: { color: 'warn' },
},
},
cell: (d) => ({
value: startCase(getUnionKey(d.status)),
color: (
{
pending: 'pending',
succeeded: 'success',
failed: 'warn',
} as const
)[getUnionKey(d.status)],
}),
},
{
field: 'created_at',
type: 'datetime',
cell: { type: 'datetime' },
},
{
field: 'destination_id',
@ -87,36 +85,33 @@ export class DepositsComponent implements OnInit {
{
field: 'identity_id',
},
createCurrencyColumn(
'amount',
(d) => d.amount,
(d) => d.currency_symbolic_code,
),
createCurrencyColumn(
'fee',
(d) => d.fee,
(d) => d.currency_symbolic_code,
{ hide: true },
),
{
field: 'source_id',
hide: true,
},
createCurrencyColumn((d) => ({ amount: d.amount, code: d.currency_symbolic_code }), {
header: 'Amount',
}),
createCurrencyColumn((d) => ({ amount: d.fee, code: d.currency_symbolic_code }), {
header: 'Fee',
}),
{
field: 'revert_status',
type: 'tag',
formatter: (d) => REVERT_STATUS[d.revert_status],
typeParameters: {
label: (d) => startCase(REVERT_STATUS[d.revert_status]),
tags: {},
},
cell: (d) => ({
value: startCase(getEnumKey(fistful_stat.RevertStatus, d.revert_status)),
color: (
{
none: 'neutral',
partial: 'pending',
full: 'success',
} as const
)[getEnumKey(fistful_stat.RevertStatus, d.revert_status)],
}),
},
createOperationColumn([
{
label: 'Details',
click: (d) => this.router.navigate([`/deposits/${d.id}`]),
},
]),
createMenuColumn((d) => ({
items: [
{
label: 'Details',
click: () => this.router.navigate([`/deposits/${d.id}`]),
},
],
})),
];
depositStatuses: QueryDsl['query']['deposits']['status'][] = ['Pending', 'Succeeded', 'Failed'];
active$ = getValueChanges(this.filtersForm).pipe(

View File

@ -13,7 +13,6 @@ import {
getValueChanges,
Column2,
createMenuColumn,
TABLE_WRAPPER_STYLE,
} from '@vality/ng-core';
import sortBy from 'lodash-es/sortBy';
import startCase from 'lodash-es/startCase';
@ -51,7 +50,6 @@ interface DomainObjectData {
ActionsModule,
MatButtonModule,
],
host: { style: TABLE_WRAPPER_STYLE },
})
export class DomainObjectsTableComponent implements OnInit {
@Output() selectedChange = new EventEmitter<string[]>();

View File

@ -0,0 +1,36 @@
import { Component, input } from '@angular/core';
import { Column2, getEnumKey, TableModule } from '@vality/ng-core';
import { repairer } from '@vality/repairer-proto';
import { StatusHistory } from '@vality/repairer-proto/repairer';
import { startCase } from 'lodash-es';
import { SidenavInfoModule } from '@cc/app/shared/components/sidenav-info';
@Component({
standalone: true,
template: `<cc-card title="Machine #{{ id() }} Status History"
><v-table2 [columns]="columns" [data]="history()"></v-table2
></cc-card>`,
imports: [TableModule, SidenavInfoModule],
})
export class MachineStatusHistoryCardComponent {
history = input<StatusHistory[]>([]);
id = input<string>('');
columns: Column2<StatusHistory>[] = [
{ field: 'changed_at', cell: { type: 'datetime' } },
{
field: 'status',
cell: (d) => ({
value: startCase(getEnumKey(repairer.RepairStatus, d.status)),
color: (
{
failed: 'warn',
in_progress: 'pending',
repaired: 'success',
} as const
)[getEnumKey(repairer.RepairStatus, d.status)],
}),
},
];
}

View File

@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import { AppAuthGuardService } from '../../shared/services';
import { RepairingComponent } from './repairing.component';
import { MachinesComponent } from './machines.component';
import { ROUTING_CONFIG } from './routing-config';
@NgModule({
@ -11,7 +11,7 @@ import { ROUTING_CONFIG } from './routing-config';
RouterModule.forChild([
{
path: '',
component: RepairingComponent,
component: MachinesComponent,
canActivate: [AppAuthGuardService],
data: ROUTING_CONFIG,
},
@ -19,4 +19,4 @@ import { ROUTING_CONFIG } from './routing-config';
],
exports: [RouterModule],
})
export class RepairingRoutingModule {}
export class MachinesRoutingModule {}

View File

@ -1,4 +1,4 @@
<cc-page-layout title="Machines">
<cc-page-layout fullHeight title="Machines">
<cc-page-layout-actions>
<v-more-filters-button [filters]="filters"></v-more-filters-button>
</cc-page-layout-actions>
@ -33,7 +33,7 @@
</ng-template>
</v-filters>
<v-table
<v-table2
[columns]="columns"
[data]="machines$ | async"
[hasMore]="hasMore$ | async"
@ -61,5 +61,5 @@
Simple repair
</button>
</v-table-actions>
</v-table>
</v-table2>
</cc-page-layout>

View File

@ -5,7 +5,6 @@ import {
DialogResponseStatus,
DialogService,
clean,
Column,
ConfirmDialogComponent,
QueryParamsService,
NotifyLogService,
@ -18,6 +17,7 @@ import {
debounceTimeWithFirst,
FetchOptions,
getEnumKey,
Column2,
} from '@vality/ng-core';
import { repairer } from '@vality/repairer-proto';
import { Namespace, ProviderID, RepairStatus, Machine } from '@vality/repairer-proto/repairer';
@ -27,10 +27,13 @@ import startCase from 'lodash-es/startCase';
import { BehaviorSubject } from 'rxjs';
import { filter, switchMap, map, shareReplay } from 'rxjs/operators';
import { SidenavInfoService } from '@cc/app/shared/components/sidenav-info';
import { createDomainObjectColumn } from '@cc/app/shared/utils/table2';
import { RepairManagementService } from '../../api/repairer';
import { createProviderColumn } from '../../shared/utils/table/create-provider-column';
import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS } from '../../tokens';
import { MachineStatusHistoryCardComponent } from './components/machine-status-history-card.component';
import { RepairByScenarioDialogComponent } from './components/repair-by-scenario-dialog/repair-by-scenario-dialog.component';
import { MachinesService } from './services/machines.service';
@ -44,11 +47,11 @@ interface Filters {
}
@Component({
selector: 'cc-repairing',
templateUrl: './repairing.component.html',
selector: 'cc-machines',
templateUrl: './machines.component.html',
providers: [MachinesService],
})
export class RepairingComponent implements OnInit {
export class MachinesComponent implements OnInit {
machines$ = this.machinesService.result$;
inProgress$ = this.machinesService.isLoading$;
hasMore$ = this.machinesService.hasMore$;
@ -62,28 +65,36 @@ export class RepairingComponent implements OnInit {
});
selected$ = new BehaviorSubject<Machine[]>([]);
status = repairer.RepairStatus;
columns: Column<Machine>[] = [
{ field: 'id' },
columns: Column2<Machine>[] = [
{ field: 'id', sticky: 'start' },
{ header: 'Namespace', field: 'ns' },
{ field: 'created_at', type: 'datetime' },
createProviderColumn((d) => Number(d.provider_id)),
{ field: 'created_at', cell: { type: 'datetime' } },
createDomainObjectColumn((d) => ({ ref: { terminal: { id: Number(d.provider_id) } } }), {
header: 'Terminal',
}),
{
field: 'status',
formatter: (d) => getEnumKey(repairer.RepairStatus, d.status),
type: 'tag',
typeParameters: {
label: (d) => startCase(getEnumKey(repairer.RepairStatus, d.status)),
tags: {
failed: { color: 'warn' },
in_progress: { color: 'pending' },
repaired: { color: 'success' },
},
},
cell: (d) => ({
value: startCase(getEnumKey(repairer.RepairStatus, d.status)),
color: (
{
failed: 'warn',
in_progress: 'pending',
repaired: 'success',
} as const
)[getEnumKey(repairer.RepairStatus, d.status)],
}),
},
{
field: 'history',
formatter: (data) => (data.history?.length ? String(data.history.length) : ''),
tooltip: 'history',
cell: (d) => ({
value: d.history?.length ? String(d.history.length) : '',
click: () => {
this.sidenavInfoService.toggle(MachineStatusHistoryCardComponent, {
history: d.history,
});
},
}),
},
{
field: 'error_message',
@ -106,6 +117,7 @@ export class RepairingComponent implements OnInit {
private destroyRef: DestroyRef,
@Inject(DATE_RANGE_DAYS) private dateRangeDays: number,
@Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number,
private sidenavInfoService: SidenavInfoService,
) {}
ngOnInit() {

View File

@ -28,13 +28,13 @@ import { ThriftFormModule } from '@cc/app/shared/components/metadata-form';
import { DomainObjectFieldComponent } from '@cc/app/shared/components/thrift-api-crud';
import { RepairByScenarioDialogComponent } from './components/repair-by-scenario-dialog/repair-by-scenario-dialog.component';
import { RepairingRoutingModule } from './repairing-routing.module';
import { RepairingComponent } from './repairing.component';
import { MachinesRoutingModule } from './machines-routing.module';
import { MachinesComponent } from './machines.component';
@NgModule({
imports: [
CommonModule,
RepairingRoutingModule,
MachinesRoutingModule,
TableModule,
MatCardModule,
ReactiveFormsModule,
@ -59,6 +59,6 @@ import { RepairingComponent } from './repairing.component';
ThriftPipesModule,
FiltersModule,
],
declarations: [RepairingComponent, RepairByScenarioDialogComponent],
declarations: [MachinesComponent, RepairByScenarioDialogComponent],
})
export class RepairingModule {}
export class MachinesModule {}

View File

@ -1,4 +1,4 @@
<cc-page-layout title="Shops">
<cc-page-layout fullHeight title="Shops">
<cc-shops-table
[progress]="progress$ | async"
[shops]="shopsParty$ | async"

View File

@ -1,3 +1,3 @@
<cc-sub-page-layout [links]="links" [tags]="tags$ | async"
><router-outlet></router-outlet
></cc-sub-page-layout>
<cc-sub-page-layout [links]="links">
<router-outlet></router-outlet>
</cc-sub-page-layout>

View File

@ -1,10 +1,12 @@
<cc-page-layout [progress]="isLoading$ | async" title="Chargebacks">
<cc-page-layout-actions>
<cc-page-layout [progress]="isLoading$ | async" fullHeight title="Chargebacks">
<cc-chargebacks-table
[data]="chargebacks$ | async"
[hasMore]="fetchChargebacksService.hasMore$ | async"
[isLoading]="isLoading$ | async"
onePayment
(more)="fetchChargebacksService.more()"
(update)="update()"
>
<button color="primary" mat-raised-button (click)="createChargeback()">Create</button>
</cc-page-layout-actions>
<cc-chargebacks
[chargebacks]="chargebacks$ | async"
[invoiceId]="(payment$ | async).invoice_id"
[paymentId]="(payment$ | async).id"
></cc-chargebacks>
</cc-chargebacks-table>
</cc-page-layout>

View File

@ -1,49 +1,42 @@
import { CommonModule } from '@angular/common';
import { Component, DestroyRef } from '@angular/core';
import { Component, DestroyRef, OnInit } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute } from '@angular/router';
import { DialogResponseStatus, DialogService } from '@vality/ng-core';
import { merge, defer, Subject } from 'rxjs';
import { map, switchMap, shareReplay } from 'rxjs/operators';
import { first } from 'rxjs';
import { FetchChargebacksService } from '@cc/app/sections/chargebacks/fetch-chargebacks.service';
import { ChargebacksTableComponent } from '@cc/app/shared/components/chargebacks-table';
import { InvoicingService } from '../../../../api/payment-processing';
import { PageLayoutModule } from '../../../../shared';
import { ChargebacksComponent } from '../../../../shared/components/chargebacks/chargebacks.component';
import { CreateChargebackDialogComponent } from '../../create-chargeback-dialog/create-chargeback-dialog.component';
import { PaymentDetailsService } from '../../payment-details.service';
@Component({
selector: 'cc-payment-chargebacks',
standalone: true,
imports: [CommonModule, PageLayoutModule, MatButton, ChargebacksComponent],
imports: [CommonModule, PageLayoutModule, MatButton, ChargebacksTableComponent],
templateUrl: './payment-chargebacks.component.html',
styles: ``,
providers: [FetchChargebacksService],
})
export class PaymentChargebacksComponent {
export class PaymentChargebacksComponent implements OnInit {
payment$ = this.paymentDetailsService.payment$;
isLoading$ = this.paymentDetailsService.isLoading$;
chargebacks$ = merge(
this.route.params,
defer(() => this.updateChargebacks$),
).pipe(
map(() => this.route.snapshot.params as Record<'invoiceID' | 'paymentID', string>),
switchMap(({ invoiceID, paymentID }) =>
this.invoicingService.GetPayment(invoiceID, paymentID),
),
map(({ chargebacks }) => chargebacks),
shareReplay({ refCount: true, bufferSize: 1 }),
);
private updateChargebacks$ = new Subject<void>();
chargebacks$ = this.fetchChargebacksService.result$;
constructor(
private route: ActivatedRoute,
private invoicingService: InvoicingService,
private dialogService: DialogService,
private dr: DestroyRef,
private paymentDetailsService: PaymentDetailsService,
protected fetchChargebacksService: FetchChargebacksService,
) {}
ngOnInit() {
this.update();
}
createChargeback() {
this.dialogService
.open(
@ -54,8 +47,21 @@ export class PaymentChargebacksComponent {
.pipe(takeUntilDestroyed(this.dr))
.subscribe(({ status }) => {
if (status === DialogResponseStatus.Success) {
this.updateChargebacks$.next();
this.update();
}
});
}
update() {
this.payment$.pipe(first(), takeUntilDestroyed(this.dr)).subscribe((p) => {
this.fetchChargebacksService.load({
common_search_query_params: {
from_time: new Date(0).toISOString(),
to_time: new Date().toISOString(),
},
payment_id: p.id,
invoice_ids: [p.invoice_id],
});
});
}
}

View File

@ -3,8 +3,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl } from '@angular/forms';
import { InvoicePaymentChargeback } from '@vality/domain-proto/domain';
import { InvoicePaymentChargebackParams } from '@vality/domain-proto/payment_processing';
import { DialogSuperclass, NotifyLogService } from '@vality/ng-core';
import { from } from 'rxjs';
import { DialogSuperclass, getImportValue, NotifyLogService } from '@vality/ng-core';
import { ThriftAstMetadata } from '@vality/ng-thrift';
import short from 'short-uuid';
import { InvoicingService } from '@cc/app/api/payment-processing';
@ -20,7 +20,7 @@ export class CreateChargebackDialogComponent extends DialogSuperclass<
InvoicePaymentChargeback
> {
form = new FormControl<Partial<InvoicePaymentChargebackParams>>({ id: short().generate() });
metadata$ = from(import('@vality/domain-proto/metadata.json').then((m) => m.default));
metadata$ = getImportValue<ThriftAstMetadata[]>(import('@vality/domain-proto/metadata.json'));
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
constructor(

View File

@ -5,7 +5,6 @@
{ label: 'Refunds', url: 'refunds' },
{ label: 'Chargebacks', url: 'chargebacks' }
]"
[tags]="tags$ | async"
id="{{
(payment$ | async) ? (payment$ | async)?.invoice_id + '.' + (payment$ | async)?.id : ''
}}"

View File

@ -12,7 +12,6 @@ import { StatusModule, PageLayoutModule, SubPageLayoutComponent } from '@cc/app/
import { DetailsItemModule } from '@cc/components/details-item';
import { HeadlineModule } from '@cc/components/headline';
import { ChargebacksComponent } from '../../shared/components/chargebacks/chargebacks.component';
import { JsonViewerModule } from '../../shared/components/json-viewer';
import { ThriftFormModule } from '../../shared/components/metadata-form';
import { MagistaThriftViewerComponent } from '../../shared/components/thrift-api-crud';
@ -34,7 +33,6 @@ import { RefundsTableModule } from './refunds-table';
MatProgressSpinnerModule,
MatButtonModule,
MatDialogModule,
ChargebacksComponent,
ActionsModule,
DialogModule,
ThriftFormModule,

View File

@ -1,7 +1,7 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Router } from '@angular/router';
import { StatPayment } from '@vality/magista-proto/magista';
import { LoadOptions, Column2, createMenuColumn, TABLE_WRAPPER_STYLE } from '@vality/ng-core';
import { LoadOptions, Column2, createMenuColumn } from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import startCase from 'lodash-es/startCase';
@ -18,7 +18,6 @@ import {
@Component({
selector: 'cc-payments-table',
templateUrl: './payments-table.component.html',
host: { style: TABLE_WRAPPER_STYLE },
})
export class PaymentsTableComponent {
@Input() data!: StatPayment[];

View File

@ -34,7 +34,7 @@ const ROUTES: Routes = [
},
{
path: 'machines',
loadChildren: () => import('./repairing/repairing.module').then((m) => m.RepairingModule),
loadChildren: () => import('./machines/machines.module').then((m) => m.MachinesModule),
},
{
path: 'payments',

View File

@ -1,9 +1,7 @@
<cc-page-layout title="Shops">
<cc-page-layout-actions></cc-page-layout-actions>
<cc-page-layout fullHeight title="Shops">
<cc-shops-table
[progress]="progress$ | async"
[shops]="shopsParty$ | async"
noSort
(filterChange)="filterChange$.next($event)"
(update)="update()"
></cc-shops-table>

View File

@ -2,7 +2,14 @@ import { Component } from '@angular/core';
import { SearchShopHit } from '@vality/deanonimus-proto/deanonimus';
import { Column, progressTo, NotifyLogService } from '@vality/ng-core';
import { BehaviorSubject, defer, of, combineLatest, Subject, Observable } from 'rxjs';
import { switchMap, shareReplay, catchError, map } from 'rxjs/operators';
import {
switchMap,
shareReplay,
catchError,
map,
debounceTime,
distinctUntilChanged,
} from 'rxjs/operators';
import { DeanonimusService } from '../../api/deanonimus';
import { ShopParty } from '../../shared/components/shops-table';
@ -14,7 +21,7 @@ import { ShopParty } from '../../shared/components/shops-table';
export class ShopsComponent {
filterChange$ = new Subject<string>();
shopsParty$: Observable<ShopParty[]> = combineLatest([
this.filterChange$,
this.filterChange$.pipe(distinctUntilChanged(), debounceTime(500)),
defer(() => this.updateShops$),
]).pipe(
switchMap(([search]) =>

View File

@ -1,13 +1,12 @@
<cc-page-layout title="Sources">
<cc-page-layout fullHeight title="Sources">
<cc-page-layout-actions>
<button color="primary" mat-raised-button (click)="create()">Create</button>
</cc-page-layout-actions>
<v-table
<v-table2
[(sort)]="sort"
[columns]="columns"
[data]="sources$ | async"
[progress]="!!(progress$ | async)"
[sort]="{ direction: 'asc', active: 'name' }"
noActions
sortOnFront
></v-table>
standaloneFilter
></v-table2>
</cc-page-layout>

View File

@ -1,6 +1,7 @@
import { Component } from '@angular/core';
import { Sort } from '@angular/material/sort';
import { Source } from '@vality/fistful-proto/internal/source';
import { DialogService, Column } from '@vality/ng-core';
import { DialogService, Column2 } from '@vality/ng-core';
import { CreateSourceComponent } from './create-source/create-source.component';
import { FetchSourcesService } from './fetch-sources.service';
@ -11,13 +12,14 @@ import { FetchSourcesService } from './fetch-sources.service';
export class SourcesComponent {
sources$ = this.fetchSourcesService.sources$;
progress$ = this.fetchSourcesService.progress$;
columns: Column<Source>[] = [
columns: Column2<Source>[] = [
{ field: 'id' },
{ field: 'name', sortable: true },
'identity',
{ field: 'currency_symbolic_code', sortable: true },
{ field: 'created_at', type: 'datetime' },
{ field: 'name' },
{ field: 'identity' },
{ field: 'currency_symbolic_code' },
{ field: 'created_at', cell: { type: 'datetime' } },
];
sort: Sort = { direction: 'asc', active: 'name' };
constructor(
private fetchSourcesService: FetchSourcesService,

View File

@ -1,53 +1,53 @@
<cc-page-layout title="Wallets">
<cc-page-layout fullHeight title="Wallets">
<cc-page-layout-actions>
<v-more-filters-button *ngIf="filters" [filters]="filters"></v-more-filters-button>
<v-switch-button
[formControl]="isFilterControl"
[states]="[
{ label: 'full-text search', icon: 'travel_explore' },
{ label: 'filters', icon: 'manage_search' }
]"
></v-switch-button>
@if (!(party$ | async)) {
<v-switch-button
[formControl]="isFilterControl"
[states]="[
{ label: 'full-text search', icon: 'travel_explore' },
{ label: 'filters', icon: 'manage_search' }
]"
></v-switch-button>
}
</cc-page-layout-actions>
<v-filters
*ngIf="isFilterControl.value"
[active]="active$ | async"
merge
(clear)="filtersForm.reset()"
>
<ng-template [formGroup]="filtersForm">
<cc-merchant-field
*ngIf="!(party$ | async)"
formControlName="party_id"
></cc-merchant-field>
<v-list-field formControlName="wallet_id" label="Wallet IDs"></v-list-field>
<mat-form-field>
<mat-label>Identity ID</mat-label>
<input formControlName="identity_id" matInput />
</mat-form-field>
<cc-currency-field formControlName="currency_code"></cc-currency-field>
</ng-template>
</v-filters>
@if (isFilterTable$ | async) {
<v-filters [active]="active$ | async" merge (clear)="filtersForm.reset()">
<ng-template [formGroup]="filtersForm">
<cc-merchant-field
*ngIf="!(party$ | async)"
formControlName="party_id"
></cc-merchant-field>
<v-list-field formControlName="wallet_id" label="Wallet IDs"></v-list-field>
<mat-form-field>
<mat-label>Identity ID</mat-label>
<input formControlName="identity_id" matInput />
</mat-form-field>
<cc-currency-field formControlName="currency_code"></cc-currency-field>
</ng-template>
</v-filters>
}
<v-table
*ngIf="isFilterControl.value"
[columns]="filterColumns$ | async"
[data]="filterWallets$ | async"
[hasMore]="filterHasMore$ | async"
[progress]="filtersLoading$ | async"
name="filterWallets"
(more)="filterMore()"
(update)="filterSearch($event)"
></v-table>
<v-table
*ngIf="!isFilterControl.value"
[columns]="fullTextSearchColumns"
[data]="fullTextSearchWallets$ | async"
[progress]="fullTextSearchLoading$ | async"
externalFilter
name="ftsWallets"
noActions
(filterChange)="fullTextSearch($event)"
(update)="fullTextSearchReload()"
></v-table>
@if (isFilterTable$ | async) {
<v-table2
[columns]="filterColumns"
[data]="filterWallets$ | async"
[hasMore]="filterHasMore$ | async"
[progress]="filtersLoading$ | async"
name="filterWallets"
(more)="filterMore()"
(update)="filterSearch($event)"
></v-table2>
} @else {
<v-table2
[columns]="fullTextSearchColumns"
[data]="fullTextSearchWallets$ | async"
[progress]="fullTextSearchLoading$ | async"
externalFilter
name="ftsWallets"
standaloneFilter
(filterChange)="fullTextSearch($event)"
(update)="fullTextSearchReload()"
></v-table2>
}
</cc-page-layout>

View File

@ -1,12 +1,4 @@
import {
Component,
OnInit,
Inject,
ViewChild,
DestroyRef,
Injector,
runInInjectionContext,
} from '@angular/core';
import { Component, OnInit, Inject, ViewChild, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, NonNullableFormBuilder } from '@angular/forms';
import { SearchWalletHit } from '@vality/deanonimus-proto/internal/deanonimus';
@ -15,17 +7,17 @@ import { AccountBalance } from '@vality/fistful-proto/internal/account';
import { StatWallet } from '@vality/fistful-proto/internal/fistful_stat';
import {
clean,
Column,
QueryParamsService,
NotifyLogService,
FiltersComponent,
UpdateOptions,
getValueChanges,
countChanged,
debounceTimeWithFirst,
Column2,
DebounceTime,
} from '@vality/ng-core';
import isNil from 'lodash-es/isNil';
import { of } from 'rxjs';
import { combineLatest, of } from 'rxjs';
import { map, shareReplay, catchError, take } from 'rxjs/operators';
import { MemoizeExpiring } from 'typescript-memoize';
@ -33,7 +25,7 @@ import { WalletParams } from '@cc/app/api/fistful-stat/query-dsl/types/wallet';
import { ManagementService } from '@cc/app/api/wallet';
import { IdentityManagementService } from '../../api/identity';
import { createCurrencyColumn, createPartyColumn } from '../../shared';
import { createCurrencyColumn, createPartyColumn } from '../../shared/utils/table2';
import { DEBOUNCE_TIME_MS } from '../../tokens';
import { PartyStoreService } from '../party';
@ -55,81 +47,81 @@ export class WalletsComponent implements OnInit {
fullTextSearchWallets$ = this.fetchWalletsTextService.result$;
fullTextSearchLoading$ = this.fetchWalletsTextService.isLoading$;
filterColumns$ = this.partyStoreService.party$.pipe(
map((party) =>
runInInjectionContext(this.injector, () => [
{ field: 'id' },
{ field: 'name' },
'currency_symbolic_code',
'identity_id',
{ field: 'created_at', type: 'datetime' },
createCurrencyColumn<StatWallet>(
'balance',
(d) => this.getBalance(d.id).pipe(map((b) => b.current)),
(d) => this.getBalance(d.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
filterColumns: Column2<StatWallet>[] = [
{ field: 'id' },
{ field: 'name' },
{ field: 'currency_symbolic_code' },
{ field: 'identity_id' },
{ field: 'created_at', cell: { type: 'datetime' } },
createCurrencyColumn(
(d) =>
this.getBalance(d.id).pipe(
map((b) => ({ amount: b.current, code: b.currency.symbolic_code })),
),
createCurrencyColumn<StatWallet>(
'hold',
(d) => this.getBalance(d.id).pipe(map((b) => b.current - b.expected_min)),
(d) => this.getBalance(d.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
),
createCurrencyColumn<StatWallet>(
'expected_min',
(d) => this.getBalance(d.id).pipe(map((b) => b.expected_min)),
(d) => this.getBalance(d.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
),
{
field: 'contract_id',
formatter: (d) =>
this.getIdentity(d.identity_id).pipe(
map((identity) => identity.contract_id),
),
lazy: true,
},
...(party
? []
: [
createPartyColumn<StatWallet>(
'party',
(d) =>
this.getIdentity(d.identity_id).pipe(
map((identity) => identity.party_id),
),
undefined,
{ lazy: true },
),
]),
]),
{ header: 'Balance', isLazyCell: true },
),
);
fullTextSearchColumns: Column<SearchWalletHit>[] = [
createCurrencyColumn(
(d) =>
this.getBalance(d.id).pipe(
map((b) => ({
amount: b.current - b.expected_min,
code: b.currency.symbolic_code,
})),
),
{ header: 'Hold', isLazyCell: true },
),
createCurrencyColumn(
(d) =>
this.getBalance(d.id).pipe(
map((b) => ({ amount: b.expected_min, code: b.currency.symbolic_code })),
),
{ header: 'Expected Min', isLazyCell: true },
),
{
field: 'contract_id',
lazyCell: (d) =>
this.getIdentity(d.identity_id).pipe(
map((identity) => ({ value: identity.contract_id })),
),
},
createPartyColumn(
(d) =>
this.getIdentity(d.identity_id).pipe(
map((identity) => ({ id: identity.party_id })),
),
{ hidden: this.partyStoreService.party$.pipe(map((p) => !p)) },
),
];
fullTextSearchColumns: Column2<SearchWalletHit>[] = [
{ field: 'wallet.id' },
{ field: 'wallet.name' },
createPartyColumn<SearchWalletHit>(
'party',
(d) => d.party.id,
(d) => d.party.email,
createPartyColumn((d) => ({
id: d.party.id,
partyName: d.party.email,
})),
createCurrencyColumn(
(d) =>
this.getBalance(d.wallet.id).pipe(
map((b) => ({ amount: b.current, code: b.currency.symbolic_code })),
),
{ header: 'Balance', isLazyCell: true },
),
createCurrencyColumn<SearchWalletHit>(
'balance',
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.current)),
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
createCurrencyColumn(
(d) =>
this.getBalance(d.wallet.id).pipe(
map((b) => ({
amount: b.current - b.expected_min,
code: b.currency.symbolic_code,
})),
),
{ header: 'Hold', isLazyCell: true },
),
createCurrencyColumn<SearchWalletHit>(
'hold',
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.current - b.expected_min)),
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
),
createCurrencyColumn<SearchWalletHit>(
'expected_min',
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.expected_min)),
(d) => this.getBalance(d.wallet.id).pipe(map((b) => b.currency.symbolic_code)),
{ lazy: true },
createCurrencyColumn(
(d) =>
this.getBalance(d.wallet.id).pipe(
map((b) => ({ amount: b.expected_min, code: b.currency.symbolic_code })),
),
{ header: 'Expected Min', isLazyCell: true },
),
];
filtersForm = this.fb.group({
@ -145,6 +137,10 @@ export class WalletsComponent implements OnInit {
@ViewChild(FiltersComponent) filters!: FiltersComponent;
typeQp = this.qp.createNamespace<{ isFilter: boolean }>('type');
party$ = this.partyStoreService.party$;
isFilterTable$ = combineLatest([getValueChanges(this.isFilterControl), this.party$]).pipe(
map(([isFilterControl, party]) => isFilterControl || !!party),
shareReplay({ refCount: true, bufferSize: 1 }),
);
private initFilters = this.filtersForm.value;
@ -154,12 +150,10 @@ export class WalletsComponent implements OnInit {
private qp: QueryParamsService<WalletParams>,
private fb: NonNullableFormBuilder,
private walletManagementService: ManagementService,
private log: NotifyLogService,
@Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number,
private destroyRef: DestroyRef,
private identityManagementService: IdentityManagementService,
private partyStoreService: PartyStoreService,
private injector: Injector,
) {}
ngOnInit() {
@ -198,6 +192,7 @@ export class WalletsComponent implements OnInit {
this.fetchWalletsService.more();
}
@DebounceTime()
fullTextSearch(text: string) {
this.fetchWalletsTextService.load(text);
}

View File

@ -13,8 +13,10 @@ import {
NotifyLogService,
EnumKeysPipe,
EnumKeyPipe,
getImportValue,
} from '@vality/ng-core';
import { from, BehaviorSubject } from 'rxjs';
import { ThriftAstMetadata } from '@vality/ng-thrift';
import { BehaviorSubject } from 'rxjs';
import { InvoicingService } from '@cc/app/api/payment-processing';
import { DomainMetadataFormExtensionsService } from '@cc/app/shared/services';
@ -57,7 +59,7 @@ export class ChangeChargebacksStatusDialogComponent
>
implements OnInit
{
metadata$ = from(import('@vality/domain-proto/metadata.json').then((m) => m.default));
metadata$ = getImportValue<ThriftAstMetadata[]>(import('@vality/domain-proto/metadata.json'));
extensions$ = this.domainMetadataFormExtensionsService.extensions$;
control = new FormControl();
actionControl = new FormControl<Action>(null, Validators.required);

View File

@ -1,15 +1,17 @@
<v-table
<v-table2
[(rowSelected)]="selected"
[columns]="columns"
[data]="data"
[hasMore]="hasMore"
[progress]="isLoading"
[rowSelected]="selected"
rowSelectable
(more)="more.emit()"
(rowSelectedChange)="selectedChange.emit($event)"
(update)="update.emit($event)"
>
<v-table-actions>
<button [disabled]="!selected().length" mat-raised-button (click)="changeStatuses()">
Change statuses
</button>
<ng-content></ng-content>
</v-table-actions>
</v-table>
</v-table2>

View File

@ -0,0 +1,155 @@
import { CommonModule } from '@angular/common';
import {
Component,
Input,
Output,
EventEmitter,
booleanAttribute,
input,
DestroyRef,
model,
} from '@angular/core';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button';
import { StatChargeback } from '@vality/magista-proto/magista';
import {
LoadOptions,
Column2,
TableModule,
DialogService,
createMenuColumn,
DialogResponseStatus,
} from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import startCase from 'lodash-es/startCase';
import { filter } from 'rxjs';
import { createCurrencyColumn, createPartyColumn, createShopColumn } from '../../utils/table2';
import { ChangeChargebacksStatusDialogComponent } from '../change-chargebacks-status-dialog';
@Component({
standalone: true,
selector: 'cc-chargebacks-table',
templateUrl: './chargebacks-table.component.html',
imports: [CommonModule, TableModule, MatButtonModule],
})
export class ChargebacksTableComponent {
@Input() data!: StatChargeback[];
@Input() isLoading?: boolean | null;
@Input() hasMore?: boolean | null;
selected = model<StatChargeback[]>([]);
onePayment = input(false, { transform: booleanAttribute });
@Output() selectedChange = new EventEmitter<StatChargeback[]>();
@Output() update = new EventEmitter<LoadOptions>();
@Output() more = new EventEmitter<void>();
columns: Column2<StatChargeback>[] = [
{ field: 'chargeback_id', header: 'Id' },
{
field: 'chargeback_reason',
header: 'Reason',
cell: (d) => ({
value: startCase(getUnionKey(d.chargeback_reason.category)),
description: d.chargeback_reason.code,
}),
},
{
field: 'chargeback_status',
header: 'Status',
cell: (d) => ({
value: startCase(getUnionKey(d.chargeback_status)),
color: (
{
pending: 'pending',
accepted: 'success',
rejected: 'warn',
cancelled: 'neutral',
} as const
)[getUnionKey(d.chargeback_status)],
}),
},
createCurrencyColumn((d) => ({ amount: d.amount, code: d.currency_code.symbolic_code }), {
header: 'Amount',
}),
createCurrencyColumn(
(d) => ({ amount: d.levy_amount, code: d.levy_currency_code.symbolic_code }),
{ header: 'Levy Amount' },
),
createCurrencyColumn((d) => ({ amount: d.fee, code: d.currency_code.symbolic_code }), {
header: 'Fee',
}),
createCurrencyColumn(
(d) => ({ amount: d.provider_fee, code: d.currency_code.symbolic_code }),
{ header: 'Provider Fee' },
),
createCurrencyColumn(
(d) => ({ amount: d.external_fee, code: d.currency_code.symbolic_code }),
{ header: 'External Fee' },
),
{
field: 'stage',
cell: (d) => ({ value: startCase(getUnionKey(d.stage)) }),
},
{
field: 'invoice_id',
header: {
value: 'Invoice Id',
description: 'Payment Id',
},
cell: (d) => ({
description: d.payment_id,
link: () => `/party/${d.party_id}/invoice/${d.invoice_id}/payment/${d.payment_id}`,
}),
hidden: toObservable(this.onePayment),
},
{
field: 'created_at',
cell: { type: 'datetime' },
},
createPartyColumn((d) => ({ id: d.party_id }), { hidden: toObservable(this.onePayment) }),
createShopColumn((d) => ({ shopId: d.shop_id, partyId: d.party_id }), {
hidden: toObservable(this.onePayment),
}),
createMenuColumn((d) => ({
items: [
{
label: 'Change status',
click: () => {
this.changeStatus(d);
},
},
],
})),
];
constructor(
private dialogService: DialogService,
private dr: DestroyRef,
) {}
changeStatus(chargeback: StatChargeback) {
this.dialogService.open(ChangeChargebacksStatusDialogComponent, {
chargebacks: [
{
payment_id: chargeback.payment_id,
invoice_id: chargeback.invoice_id,
chargeback_id: chargeback.chargeback_id,
},
],
});
}
changeStatuses() {
this.dialogService
.open(ChangeChargebacksStatusDialogComponent, { chargebacks: this.selected() })
.afterClosed()
.pipe(
filter((res) => res.status === DialogResponseStatus.Success),
takeUntilDestroyed(this.dr),
)
.subscribe(() => {
this.update.emit();
});
}
}

View File

@ -0,0 +1 @@
export * from './chargebacks-table.component';

View File

@ -1 +0,0 @@
<v-table [columns]="columns" [data]="chargebacks"></v-table>

View File

@ -1,84 +0,0 @@
import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import { InvoicePaymentChargeback } from '@vality/domain-proto/payment_processing';
import { DialogService, Column, TableModule, createOperationColumn } from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import startCase from 'lodash-es/startCase';
import { createCurrencyColumn } from '@cc/app/shared';
import { DetailsDialogComponent } from '@cc/app/shared/components/details-dialog/details-dialog.component';
import { ChangeChargebacksStatusDialogComponent } from '../change-chargebacks-status-dialog';
@Component({
standalone: true,
selector: 'cc-chargebacks',
templateUrl: './chargebacks.component.html',
imports: [CommonModule, TableModule],
})
export class ChargebacksComponent {
@Input() chargebacks: InvoicePaymentChargeback[];
@Input() paymentId: string;
@Input() invoiceId: string;
columns: Column<InvoicePaymentChargeback>[] = [
'chargeback.id',
{
field: 'status',
type: 'tag',
formatter: (d) => getUnionKey(d.chargeback.status),
typeParameters: {
label: (d) => startCase(getUnionKey(d.chargeback.status)),
tags: {
pending: { color: 'pending' },
accepted: { color: 'success' },
rejected: { color: 'warn' },
cancelled: { color: 'neutral' },
},
},
},
{ field: 'chargeback.created_at', type: 'datetime' },
createCurrencyColumn(
'body',
(d) => d.chargeback.body.amount,
(d) => d.chargeback.body.currency.symbolic_code,
),
createCurrencyColumn(
'levy',
(d) => d.chargeback.levy.amount,
(d) => d.chargeback.levy.currency.symbolic_code,
),
{ field: 'stage', formatter: (d) => getUnionKey(d.chargeback.stage) },
createOperationColumn([
{
label: 'Details',
click: (d) => this.showDetails(d),
},
{
label: 'Change status',
click: (d) => this.changeStatus(d.chargeback.id),
},
]),
];
constructor(private dialogService: DialogService) {}
changeStatus(id: string) {
this.dialogService.open(ChangeChargebacksStatusDialogComponent, {
chargebacks: [
{
payment_id: this.paymentId,
invoice_id: this.invoiceId,
chargeback_id: id,
},
],
});
}
showDetails(chargeback: InvoicePaymentChargeback) {
this.dialogService.open(DetailsDialogComponent, {
title: 'Chargeback details',
json: chargeback,
});
}
}

View File

@ -4,44 +4,44 @@
(extensionResult$ | async)?.type || (extensionResult$ | async)?.template;
else defaultFields
"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-extension-field>
<ng-template #defaultFields>
<cc-primitive-field
*ngSwitchCase="'primitive'"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-primitive-field>
<cc-complex-form
*ngSwitchCase="'complex'"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-complex-form>
<ng-container *ngSwitchCase="'object'" [ngSwitch]="data.objectType">
<cc-struct-form
*ngSwitchCase="'struct'"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-struct-form>
<cc-union-field
*ngSwitchCase="'union'"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-union-field>
<cc-enum-field
*ngSwitchCase="'enum'"
[data]="data"
[data]="$any(data)"
[formControl]="control"
></cc-enum-field>
<cc-typedef-form
*ngSwitchCase="'typedef'"
[data]="data"
[data]="$any(data)"
[extensions]="extensions"
[formControl]="control"
></cc-typedef-form>

View File

@ -16,4 +16,9 @@ $offset: 24px;
min-height: 48px;
gap: 8px;
}
::ng-deep & > *:last-child {
min-height: 0;
height: 100%;
}
}

View File

@ -9,7 +9,7 @@ import { CardComponent } from '../sidenav-info/components/card/card.component';
import { DomainThriftViewerComponent } from '../thrift-api-crud';
@Component({
selector: 'cc-contract-card',
selector: 'cc-shop-contract-card',
standalone: true,
imports: [CommonModule, CardComponent, DomainThriftViewerComponent],
templateUrl: './shop-contract-card.component.html',

View File

@ -18,6 +18,7 @@ import {
Option,
NotifyLogService,
progressTo,
SelectFieldComponent,
} from '@vality/ng-core';
import {
BehaviorSubject,
@ -47,8 +48,8 @@ export class ShopFieldComponent
{
@Input() label: string;
@Input({ transform: booleanAttribute }) required: boolean;
@Input() size?: string;
@Input() appearance?: string;
@Input() size?: SelectFieldComponent['size'];
@Input() appearance?: SelectFieldComponent['appearance'];
@Input() hint?: string;
@Input({ transform: booleanAttribute }) multiple = false;
partyId = input<PartyID>();

View File

@ -1,14 +1,11 @@
<v-table
<v-table2
[(sort)]="sort"
[columns]="columns$ | async"
[columns]="columns"
[data]="shops()"
[externalFilter]="filterChange.observed"
[progress]="progress"
[size]="100"
name="shops"
noActions
sortOnFront
standaloneFilter
(filterChange)="filterChange.emit($event)"
(update)="update.emit()"
></v-table>
></v-table2>

View File

@ -5,8 +5,8 @@ import {
EventEmitter,
Input,
booleanAttribute,
OnChanges,
input,
Injector,
} from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { MatCardModule } from '@angular/material/card';
@ -16,18 +16,17 @@ import { Shop, Party, PartyID, RoutingRulesetRef } from '@vality/domain-proto/do
import {
InputFieldModule,
TableModule,
Column,
createOperationColumn,
DialogService,
NotifyLogService,
ConfirmDialogComponent,
DialogResponseStatus,
ComponentChanges,
Column2,
createMenuColumn,
} from '@vality/ng-core';
import { getUnionKey } from '@vality/ng-thrift';
import isNil from 'lodash-es/isNil';
import { isNil } from 'lodash-es';
import startCase from 'lodash-es/startCase';
import { map, switchMap, Subject, defer, combineLatest, of } from 'rxjs';
import { map, switchMap, combineLatest, of } from 'rxjs';
import { filter, shareReplay, startWith, take, first } from 'rxjs/operators';
import { MemoizeExpiring } from 'typescript-memoize';
@ -38,6 +37,7 @@ import {
DelegateWithPaymentInstitution,
} from '../../../sections/routing-rules/party-delegate-rulesets';
import { RoutingRulesType } from '../../../sections/routing-rules/types/routing-rules-type';
import { createPartyColumn } from '../../utils/table2';
import { ShopCardComponent } from '../shop-card/shop-card.component';
import { ShopContractCardComponent } from '../shop-contract-card/shop-contract-card.component';
import { SidenavInfoService } from '../sidenav-info';
@ -68,172 +68,140 @@ export interface ShopParty {
templateUrl: './shops-table.component.html',
providers: [PartyDelegateRulesetsService],
})
export class ShopsTableComponent implements OnChanges {
export class ShopsTableComponent {
shops = input<ShopParty[]>([]);
@Input({ transform: booleanAttribute }) changed!: boolean;
@Input() progress: number | boolean = false;
@Output() update = new EventEmitter<void>();
@Output() filterChange = new EventEmitter<string>();
@Input({ transform: booleanAttribute }) noSort: boolean = false;
@Input({ transform: booleanAttribute }) noPartyColumn: boolean = false;
noPartyColumn = input(false, { transform: booleanAttribute });
columns$ = combineLatest([
toObservable(this.shops).pipe(
startWith(null),
map((shops) =>
shops?.length ? Array.from(new Set(shops.map((s) => s.party.id))) : [],
),
switchMap((parties) =>
parties?.length
? combineLatest(
parties.map((id) =>
this.partyDelegateRulesetsService.getDelegatesWithPaymentInstitution(
RoutingRulesType.Payment,
id,
),
),
).pipe(map((rules) => new Map(rules.map((r, idx) => [parties[idx], r]))))
: of(new Map<string, DelegateWithPaymentInstitution[]>()),
),
map((delegatesWithPaymentInstitutionByParty) => ({
delegatesWithPaymentInstitutionByParty,
rulesetIds: Array.from(
Array.from(delegatesWithPaymentInstitutionByParty.values()).reduce((acc, d) => {
d?.map((v) => v?.partyDelegate?.ruleset?.id).forEach((v) => acc.add(v));
return acc;
}, new Set<number>([])),
),
})),
),
defer(() => this.updateColumns$).pipe(startWith(null)),
]).pipe(
map(([delegatesByParty]): Column<ShopParty>[] => [
{
field: 'shop.id',
sortable: !this.noSort,
},
{
field: 'shop.details.name',
description: 'shop.details.description',
click: (d) => {
columns: Column2<ShopParty>[] = [
{
field: 'shop.id',
},
{
field: 'shop.details.name',
cell: (d) => ({
description: d.shop.details.description,
click: () => {
this.sidenavInfoService.toggle(ShopCardComponent, {
partyId: d.party.id,
id: d.shop.id,
});
},
sortable: !this.noSort,
},
...(this.noPartyColumn
? []
: [
{
field: 'party.email',
header: 'Party',
description: 'party.id',
link: (d) => `/party/${d.party.id}`,
},
]),
}),
},
createPartyColumn(
(d) => ({
id: d.party.id,
partyName: d.party.email,
}),
{
field: 'shop.contract_id',
header: 'Contract',
click: (d) => {
hidden: toObservable(this.noPartyColumn),
},
),
{
field: 'shop.contract_id',
header: 'Contract',
cell: (d) => ({
click: () => {
this.sidenavInfoService.toggle(ShopContractCardComponent, {
partyId: d.party.id,
id: d.shop.id,
});
},
},
{
field: 'terms',
formatter: (d) =>
this.getTerms(d.party.id, d.shop.id).pipe(
map((terms) => getDomainObjectDetails(terms).label),
),
description: (d) =>
this.getTerms(d.party.id, d.shop.id).pipe(
map((terms) => getDomainObjectDetails(terms).id),
),
lazy: true,
click: (d) => {
this.getTerms(d.party.id, d.shop.id).subscribe((terms) => {
this.sidenavInfoService.toggle(DomainObjectCardComponent, {
ref: { term_set_hierarchy: terms.term_set_hierarchy.ref },
});
});
},
},
{
field: 'shop.location.url',
},
{
field: 'shop.account.currency.symbolic_code',
header: 'Currency',
},
{
field: 'shop.blocking',
type: 'tag',
formatter: ({ shop }) => getUnionKey(shop.blocking),
typeParameters: {
label: ({ shop }) => startCase(getUnionKey(shop.blocking)),
tags: {
blocked: { color: 'warn' },
unblocked: { color: 'success' },
},
},
},
{
field: 'shop.suspension',
type: 'tag',
formatter: ({ shop }) => getUnionKey(shop.suspension),
typeParameters: {
label: ({ shop }) => startCase(getUnionKey(shop.suspension)),
tags: {
suspended: { color: 'warn' },
active: { color: 'success' },
},
},
},
createOperationColumn<ShopParty>([
...delegatesByParty.rulesetIds.map((id) => ({
label: `Routing rules #${id}`,
click: ({ shop, party }) =>
this.openRoutingRules(
delegatesByParty.delegatesWithPaymentInstitutionByParty
.get(party.id)
.find((d) => d?.partyDelegate?.ruleset?.id === id)?.partyDelegate
?.ruleset?.id,
shop.id,
party.id,
),
disabled: ({ party }) =>
isNil(
delegatesByParty.delegatesWithPaymentInstitutionByParty
.get(party.id)
.find((d) => d?.partyDelegate?.ruleset?.id === id)?.partyDelegate
?.ruleset?.id,
),
}),
},
{
field: 'terms',
lazyCell: (d) =>
this.getTerms(d.party.id, d.shop.id).pipe(
map((terms) => getDomainObjectDetails(terms)),
map((details) => ({
value: details.label,
description: details.description,
click: () => {
this.getTerms(d.party.id, d.shop.id).subscribe((terms) => {
this.sidenavInfoService.toggle(DomainObjectCardComponent, {
ref: {
term_set_hierarchy: terms.term_set_hierarchy.ref,
},
});
});
},
})),
),
},
{
field: 'shop.location.url',
},
{
field: 'shop.account.currency.symbolic_code',
header: 'Currency',
},
{
field: 'shop.blocking',
cell: (d) => ({
value: startCase(getUnionKey(d.shop.blocking)),
color: (
{
blocked: 'warn',
unblocked: 'success',
} as const
)[getUnionKey(d.shop.blocking)],
}),
},
{
field: 'shop.suspension',
cell: (d) => ({
value: startCase(getUnionKey(d.shop.suspension)),
color: (
{
suspended: 'warn',
active: 'success',
} as const
)[getUnionKey(d.shop.suspension)],
}),
},
createMenuColumn((d) =>
this.getDelegatesByParty().pipe(
map((delegatesByParty) => ({
items: [
...delegatesByParty.rulesetIds.map((id) => {
const rulesetId =
delegatesByParty.delegatesWithPaymentInstitutionByParty
?.get?.(d.party.id)
?.find?.((v) => v?.partyDelegate?.ruleset?.id === id)
?.partyDelegate?.ruleset?.id;
return {
label: `Routing rules #${id}`,
click: () =>
this.openRoutingRules(rulesetId, d.shop.id, d.party.id),
disabled: isNil(rulesetId),
};
}),
{
label:
getUnionKey(d.shop.suspension) === 'suspended'
? 'Activate'
: 'Suspend',
click: () => {
this.toggleSuspension(d);
},
},
{
label: getUnionKey(d.shop.blocking) === 'blocked' ? 'Unblock' : 'Block',
click: () => {
this.toggleBlocking(d);
},
},
],
})),
{
label: ({ shop }) =>
getUnionKey(shop.suspension) === 'suspended' ? 'Activate' : 'Suspend',
click: (d) => {
this.toggleSuspension(d);
},
},
{
label: ({ shop }) =>
getUnionKey(shop.blocking) === 'blocked' ? 'Unblock' : 'Block',
click: (d) => {
this.toggleBlocking(d);
},
},
]),
]),
shareReplay({ refCount: true, bufferSize: 1 }),
);
),
),
];
sort: Sort = { active: 'shop.details.name', direction: 'asc' };
private updateColumns$ = new Subject<void>();
constructor(
private sidenavInfoService: SidenavInfoService,
@ -243,17 +211,9 @@ export class ShopsTableComponent implements OnChanges {
private router: Router,
private partyDelegateRulesetsService: PartyDelegateRulesetsService,
private domainStoreService: DomainStoreService,
private injector: Injector,
) {}
ngOnChanges(changes: ComponentChanges<ShopsTableComponent>) {
if (changes.noSort || changes.noPartyColumn) {
if (this.noSort) {
this.sort = { active: '', direction: '' };
}
this.updateColumns$.next();
}
}
toggleBlocking({ party, shop }: ShopParty) {
this.dialogService
.open(ConfirmDialogComponent, {
@ -369,4 +329,34 @@ export class ShopsTableComponent implements OnChanges {
});
});
}
private getDelegatesByParty() {
return toObservable(this.shops, { injector: this.injector }).pipe(
startWith(null),
map((shops) =>
shops?.length ? Array.from(new Set(shops.map((s) => s.party.id))) : [],
),
switchMap((parties) =>
parties?.length
? combineLatest(
parties.map((id) =>
this.partyDelegateRulesetsService.getDelegatesWithPaymentInstitution(
RoutingRulesType.Payment,
id,
),
),
).pipe(map((rules) => new Map(rules.map((r, idx) => [parties[idx], r]))))
: of(new Map<string, DelegateWithPaymentInstitution[]>()),
),
map((delegatesWithPaymentInstitutionByParty) => ({
delegatesWithPaymentInstitutionByParty,
rulesetIds: Array.from(
Array.from(delegatesWithPaymentInstitutionByParty.values()).reduce((acc, d) => {
d?.map((v) => v?.partyDelegate?.ruleset?.id).forEach((v) => acc.add(v));
return acc;
}, new Set<number>([])),
),
})),
);
}
}

View File

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common';
import { Component, Input, booleanAttribute } from '@angular/core';
import { ThriftAstMetadata } from '@vality/domain-proto';
import { getImportValue } from '@vality/ng-core';
import { getImportValue, UnionEnum } from '@vality/ng-core';
import { ValueType } from '@vality/thrift-ts';
import { ThriftViewerModule, ViewerKind } from '../../../thrift-viewer';
@ -15,7 +15,7 @@ import { DomainMetadataViewExtensionsService } from './services/domain-metadata-
imports: [CommonModule, ThriftViewerModule],
})
export class DomainThriftViewerComponent<T> {
@Input() kind: ViewerKind = ViewerKind.Component;
@Input() kind: UnionEnum<ViewerKind> = ViewerKind.Component;
@Input() value: T;
@Input() compared?: T;
@Input() type: ValueType;

View File

@ -9,6 +9,7 @@ import {
FormControlSuperclass,
} from '@vality/ng-core';
import { toJson } from '@vality/ng-thrift';
import { ValueType } from '@vality/thrift-ts';
import { merge, defer, of, Subject } from 'rxjs';
import { map, filter, shareReplay } from 'rxjs/operators';
@ -32,7 +33,7 @@ export class ThriftEditorComponent<T> extends FormControlSuperclass<T> {
@Input() metadata: ThriftAstMetadata[];
@Input() namespace: string;
@Input() type: string;
@Input() type: ValueType;
@Input() extensions: MetadataFormExtension[];
@Input({ transform: booleanAttribute }) noChangeKind = false;
@Input({ transform: booleanAttribute }) noToolbar = false;

View File

@ -1,6 +1,6 @@
import { Component, Input, OnChanges, Output, EventEmitter, booleanAttribute } from '@angular/core';
import { ThriftAstMetadata } from '@vality/domain-proto';
import { ComponentChanges } from '@vality/ng-core';
import { ComponentChanges, UnionEnum } from '@vality/ng-core';
import { toJson } from '@vality/ng-thrift';
import { ValueType } from '@vality/thrift-ts';
import { DiffEditorModel } from 'ngx-monaco-editor-v2';
@ -19,7 +19,7 @@ export enum ViewerKind {
styleUrls: ['./thrift-viewer.component.scss'],
})
export class ThriftViewerComponent<T> implements OnChanges {
@Input() kind: ViewerKind = ViewerKind.Component;
@Input() kind: UnionEnum<ViewerKind> = ViewerKind.Component;
@Input() value: T;
@Input() compared?: T;
@Input({ transform: booleanAttribute }) progress: boolean = false;
@ -62,6 +62,6 @@ export class ThriftViewerComponent<T> implements OnChanges {
this.kind = ViewerKind.Editor;
break;
}
this.changeKind.emit(this.kind);
this.changeKind.emit(this.kind as ViewerKind);
}
}

View File

@ -15,6 +15,7 @@ import {
createControlProviders,
debounceTimeWithFirst,
progressTo,
SelectFieldComponent,
} from '@vality/ng-core';
import { BehaviorSubject, Observable, of, ReplaySubject, Subject, concat, forkJoin } from 'rxjs';
import { catchError, map, switchMap, tap, distinctUntilChanged } from 'rxjs/operators';
@ -34,8 +35,8 @@ export class WalletFieldComponent
{
@Input() label: string;
@Input({ transform: booleanAttribute }) required: boolean;
@Input() size?: string;
@Input() appearance?: string;
@Input() size?: SelectFieldComponent['size'];
@Input() appearance?: SelectFieldComponent['appearance'];
@Input() hint?: string;
@Input({ transform: booleanAttribute }) multiple = false;

View File

@ -1,12 +0,0 @@
import { Column, PossiblyAsync, getPossiblyAsyncObservable } from '@vality/ng-core';
import { map } from 'rxjs/operators';
import { createDomainObjectColumn } from './create-domain-object-column';
export function createProviderColumn<T extends object>(
selectTerminalId: (d: T) => PossiblyAsync<number>,
): Column<T> {
return createDomainObjectColumn('provider', (d) =>
getPossiblyAsyncObservable(selectTerminalId(d)).pipe(map((id) => ({ id }))),
);
}

View File

@ -1,5 +1,4 @@
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { createColumn } from '@vality/ng-core';
import { of } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
@ -16,9 +15,7 @@ export const createPartyColumn = createColumn(
.pipe(map((party) => party.contact_info.registration_email));
const partyCell = {
description: id,
link: () => {
void inject(Router).navigate([`/party/${id}`]);
},
link: () => `/party/${id}`,
};
return partyName$.pipe(
map((partyName) => ({