mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-200: Add invoice events page (#379)
This commit is contained in:
parent
335100b3ce
commit
c682179598
@ -127,4 +127,9 @@ export class InvoicingService {
|
||||
switchMap((c) => c.CancelChargeback(id, paymentId, chargebackId, params)),
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
GetEvents(...args: Parameters<payment_processing_InvoicingCodegenClient['GetEvents']>) {
|
||||
return this.client$.pipe(switchMap((c) => c.GetEvents(...args)));
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import { PartyManagementService } from '../../api/payment-processing';
|
||||
|
||||
@Injectable()
|
||||
export class PartyStoreService {
|
||||
party$: Observable<Party | Pick<Party, 'id'> | null> = this.route.params.pipe(
|
||||
party$: Observable<Party | Partial<Party> | null> = this.route.params.pipe(
|
||||
startWith(this.route.snapshot.params),
|
||||
switchMap(({ partyID }) =>
|
||||
partyID
|
||||
|
@ -1,35 +1,3 @@
|
||||
<mat-toolbar *ngIf="party$ | async as party" style="height: 48px; padding: 0 24px">
|
||||
<div style="display: flex; gap: 8px; align-items: center">
|
||||
<!-- <div class="mat-body-2 mat-no-margin">-->
|
||||
<!-- {{ party?.contact_info?.email }}-->
|
||||
<!-- </div>-->
|
||||
<v-tag
|
||||
*ngIf="party?.blocking"
|
||||
[color]="(party?.blocking | ngtUnionKey) === 'blocked' ? 'warn' : 'success'"
|
||||
style="margin-top: 8px"
|
||||
>{{ party?.blocking | ngtUnionKey | titlecase }}</v-tag
|
||||
>
|
||||
<v-tag
|
||||
*ngIf="party?.suspension"
|
||||
[color]="(party?.suspension | ngtUnionKey) === 'suspended' ? 'warn' : 'success'"
|
||||
style="margin-top: 8px"
|
||||
>{{ party?.suspension | ngtUnionKey | titlecase }}</v-tag
|
||||
>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-sidenav-container autosize>
|
||||
<mat-sidenav-content style="overflow: unset"
|
||||
><router-outlet></router-outlet
|
||||
></mat-sidenav-content>
|
||||
<mat-sidenav
|
||||
[fixedTopGap]="64 + 48"
|
||||
[opened]="!(sidenavInfoService.opened$ | async)"
|
||||
fixedInViewport="true"
|
||||
mode="side"
|
||||
position="end"
|
||||
style="background: transparent; border: none; padding: 24px 0 24px 0"
|
||||
>
|
||||
<v-nav [links]="links" type="secondary"></v-nav
|
||||
></mat-sidenav>
|
||||
</mat-sidenav-container>
|
||||
<cc-sub-page-layout [links]="links" [tags]="tags$ | async"
|
||||
><router-outlet></router-outlet
|
||||
></cc-sub-page-layout>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Link } from '@vality/ng-core';
|
||||
import { getUnionKey } from '@vality/ng-thrift';
|
||||
import startCase from 'lodash-es/startCase';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { AppAuthGuardService, Services } from '@cc/app/shared/services';
|
||||
|
||||
@ -48,6 +51,26 @@ export class PartyComponent {
|
||||
},
|
||||
].filter((item) => this.appAuthGuardService.userHasSomeServiceMethods(item.services));
|
||||
party$ = this.partyStoreService.party$;
|
||||
tags$ = this.party$.pipe(
|
||||
map((party) => [
|
||||
...(party?.blocking
|
||||
? [
|
||||
{
|
||||
value: startCase(getUnionKey(party.blocking)),
|
||||
color: getUnionKey(party.blocking) === 'blocked' ? 'warn' : 'success',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(party?.suspension
|
||||
? [
|
||||
{
|
||||
value: startCase(getUnionKey(party.suspension)),
|
||||
color: getUnionKey(party.suspension) === 'suspended' ? 'warn' : 'success',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private appAuthGuardService: AppAuthGuardService,
|
||||
|
@ -8,6 +8,7 @@ import { NavComponent, TagModule } from '@vality/ng-core';
|
||||
import { ThriftPipesModule } from '@vality/ng-thrift';
|
||||
|
||||
import { PageLayoutModule } from '../../shared';
|
||||
import { SubPageLayoutComponent } from '../../shared/components/page-layout/components/sub-page-layout/sub-page-layout.component';
|
||||
|
||||
import { PartyRouting } from './party-routing.module';
|
||||
import { PartyComponent } from './party.component';
|
||||
@ -24,6 +25,7 @@ import { PartyComponent } from './party.component';
|
||||
MatToolbar,
|
||||
TagModule,
|
||||
ThriftPipesModule,
|
||||
SubPageLayoutComponent,
|
||||
],
|
||||
declarations: [PartyComponent],
|
||||
})
|
||||
|
@ -0,0 +1,10 @@
|
||||
<cc-page-layout [progress]="isLoading$ | async" title="Chargebacks">
|
||||
<cc-page-layout-actions>
|
||||
<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-page-layout>
|
@ -0,0 +1,61 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, DestroyRef } 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 { 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],
|
||||
templateUrl: './payment-chargebacks.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class PaymentChargebacksComponent {
|
||||
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>();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private invoicingService: InvoicingService,
|
||||
private dialogService: DialogService,
|
||||
private dr: DestroyRef,
|
||||
private paymentDetailsService: PaymentDetailsService,
|
||||
) {}
|
||||
|
||||
createChargeback() {
|
||||
this.dialogService
|
||||
.open(
|
||||
CreateChargebackDialogComponent,
|
||||
this.route.snapshot.params as Record<'invoiceID' | 'paymentID', string>,
|
||||
)
|
||||
.afterClosed()
|
||||
.pipe(takeUntilDestroyed(this.dr))
|
||||
.subscribe(({ status }) => {
|
||||
if (status === DialogResponseStatus.Success) {
|
||||
this.updateChargebacks$.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<cc-page-layout
|
||||
[progress]="isLoading$ | async"
|
||||
id="{{
|
||||
(payment$ | async) ? (payment$ | async)?.invoice_id + '.' + (payment$ | async)?.id : ''
|
||||
}}"
|
||||
title="Payment"
|
||||
>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<cc-magista-thrift-viewer
|
||||
[extensions]="extensions$ | async"
|
||||
[value]="payment$ | async"
|
||||
type="StatPayment"
|
||||
></cc-magista-thrift-viewer>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</cc-page-layout>
|
@ -0,0 +1,75 @@
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { Component, Inject, LOCALE_ID } from '@angular/core';
|
||||
import { MatCard, MatCardContent } from '@angular/material/card';
|
||||
import { ThriftAstMetadata } from '@vality/domain-proto';
|
||||
import { getImportValue, formatCurrency } from '@vality/ng-core';
|
||||
import { isTypeWithAliases, getUnionValue } from '@vality/ng-thrift';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { PageLayoutModule } from '../../../../shared';
|
||||
import { MetadataViewExtension } from '../../../../shared/components/json-viewer';
|
||||
import { MagistaThriftViewerComponent } from '../../../../shared/components/thrift-api-crud';
|
||||
import { DomainMetadataViewExtensionsService } from '../../../../shared/components/thrift-api-crud/domain/domain-thrift-viewer/services/domain-metadata-view-extensions';
|
||||
import { AmountCurrencyService } from '../../../../shared/services';
|
||||
import { PaymentDetailsService } from '../../payment-details.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-payment-details',
|
||||
standalone: true,
|
||||
imports: [AsyncPipe, MagistaThriftViewerComponent, MatCard, MatCardContent, PageLayoutModule],
|
||||
templateUrl: './payment-details.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class PaymentDetailsComponent {
|
||||
payment$ = this.paymentDetailsService.payment$;
|
||||
isLoading$ = this.paymentDetailsService.isLoading$;
|
||||
metadata$ = getImportValue<ThriftAstMetadata[]>(import('@vality/magista-proto/metadata.json'));
|
||||
extensions$: Observable<MetadataViewExtension[]> = this.payment$.pipe(
|
||||
map((payment): MetadataViewExtension[] => [
|
||||
this.domainMetadataViewExtensionsService.createShopExtension(payment.owner_id),
|
||||
{
|
||||
determinant: (d) => of(isTypeWithAliases(d, 'Amount', 'domain')),
|
||||
extension: (_, amount: number) =>
|
||||
this.amountCurrencyService.getCurrency(payment.currency_symbolic_code).pipe(
|
||||
map((c) => ({
|
||||
value: formatCurrency(
|
||||
amount,
|
||||
c.data.symbolic_code,
|
||||
'long',
|
||||
this._locale,
|
||||
c.data.exponent,
|
||||
),
|
||||
})),
|
||||
),
|
||||
},
|
||||
{
|
||||
determinant: (d) =>
|
||||
of(
|
||||
isTypeWithAliases(d, 'InvoicePaymentStatus', 'domain') ||
|
||||
isTypeWithAliases(d, 'InvoicePaymentFlow', 'magista'),
|
||||
),
|
||||
extension: (_, v) => of({ hidden: !Object.keys(getUnionValue(v)).length }),
|
||||
},
|
||||
{
|
||||
determinant: (d) =>
|
||||
of(
|
||||
isTypeWithAliases(d?.trueParent, 'StatPayment', 'magista') &&
|
||||
(d.field.name === 'make_recurrent' ||
|
||||
d.field.name === 'currency_symbolic_code' ||
|
||||
isTypeWithAliases(d, 'InvoiceID', 'domain') ||
|
||||
isTypeWithAliases(d, 'InvoicePaymentID', 'domain')),
|
||||
),
|
||||
extension: () => of({ hidden: true }),
|
||||
},
|
||||
]),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private paymentDetailsService: PaymentDetailsService,
|
||||
private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService,
|
||||
private amountCurrencyService: AmountCurrencyService,
|
||||
@Inject(LOCALE_ID) private _locale: string,
|
||||
) {}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{{ text }}
|
||||
@if (date) {
|
||||
at {{ date | date: 'dd.MM.yyyy HH:mm:ss.SSS' }} ({{
|
||||
date | humanizedDuration: { hasAgoEnding: true }
|
||||
}})
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { HumanizedDurationPipe } from '@vality/ng-core';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-timeline-item-header',
|
||||
standalone: true,
|
||||
templateUrl: 'timeline-item-header.component.html',
|
||||
imports: [HumanizedDurationPipe, DatePipe],
|
||||
})
|
||||
export class TimelineItemHeaderComponent {
|
||||
@Input() date: string;
|
||||
@Input() text: string;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<cc-page-layout [progress]="isLoading$ | async" title="Invoice Events">
|
||||
<cc-timeline>
|
||||
@for (e of events$ | async; track e) {
|
||||
<cc-timeline-item>
|
||||
<cc-timeline-item-badge [color]="e.color">
|
||||
<mat-icon>{{ e.icon }}</mat-icon>
|
||||
</cc-timeline-item-badge>
|
||||
<cc-timeline-item-title>
|
||||
<cc-timeline-item-header
|
||||
[date]="e.date"
|
||||
[text]="e.title"
|
||||
></cc-timeline-item-header>
|
||||
</cc-timeline-item-title>
|
||||
@if (e.change) {
|
||||
<cc-timeline-item-content>
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{ e.expansionTitle }}
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<ng-template matExpansionPanelContent>
|
||||
<cc-domain-thrift-viewer
|
||||
[namespace]="e.namespace"
|
||||
[type]="e.type"
|
||||
[value]="e.change"
|
||||
></cc-domain-thrift-viewer>
|
||||
</ng-template>
|
||||
</mat-expansion-panel>
|
||||
</cc-timeline-item-content>
|
||||
}
|
||||
</cc-timeline-item>
|
||||
}
|
||||
</cc-timeline>
|
||||
</cc-page-layout>
|
@ -0,0 +1,53 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { HumanizedDurationPipe } from '@vality/ng-core';
|
||||
import { ThriftPipesModule } from '@vality/ng-thrift';
|
||||
import { switchMap } from 'rxjs';
|
||||
import { shareReplay, map } from 'rxjs/operators';
|
||||
|
||||
import { TimelineModule } from '../../../../../components/timeline';
|
||||
import { InvoicingService } from '../../../../api/payment-processing';
|
||||
import { PageLayoutModule } from '../../../../shared';
|
||||
import { DomainThriftViewerComponent } from '../../../../shared/components/thrift-api-crud';
|
||||
import { PaymentDetailsService } from '../../payment-details.service';
|
||||
|
||||
import { TimelineItemHeaderComponent } from './components/timeline-item-header/timeline-item-header.component';
|
||||
import { getInvoiceChangeInfo } from './utils/get-invoice-change-info';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-payment-events',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
PageLayoutModule,
|
||||
TimelineModule,
|
||||
MatIcon,
|
||||
HumanizedDurationPipe,
|
||||
ThriftPipesModule,
|
||||
DomainThriftViewerComponent,
|
||||
MatExpansionModule,
|
||||
TimelineItemHeaderComponent,
|
||||
],
|
||||
templateUrl: './payment-events.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class PaymentEventsComponent {
|
||||
payment$ = this.paymentDetailsService.payment$;
|
||||
isLoading$ = this.paymentDetailsService.isLoading$;
|
||||
events$ = this.payment$.pipe(
|
||||
switchMap((payment) => this.invoicingService.GetEvents(payment.invoice_id, {})),
|
||||
map((events) =>
|
||||
events.flatMap((e) =>
|
||||
(e.payload.invoice_changes || []).map((change) => getInvoiceChangeInfo(e, change)),
|
||||
),
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private paymentDetailsService: PaymentDetailsService,
|
||||
private invoicingService: InvoicingService,
|
||||
) {}
|
||||
}
|
@ -0,0 +1,416 @@
|
||||
import { InvoiceChange, Event } from '@vality/domain-proto/internal/payment_processing';
|
||||
import { getUnionKey, getUnionValue } from '@vality/ng-thrift';
|
||||
import { upperFirst } from 'lodash-es';
|
||||
import isEmpty from 'lodash-es/isEmpty';
|
||||
import startCase from 'lodash-es/startCase';
|
||||
|
||||
import { StatusColor } from '../../../../../styles';
|
||||
|
||||
function getKeyTitle(v: unknown) {
|
||||
return String(v).replaceAll('_', ' ');
|
||||
}
|
||||
|
||||
const STATUS_ICONS = {
|
||||
warn: 'priority_high',
|
||||
neutral: 'block',
|
||||
success: 'check',
|
||||
pending: 'pending',
|
||||
};
|
||||
|
||||
export function getInvoiceChangeInfo(e: Event, change: InvoiceChange) {
|
||||
switch (getUnionKey(change)) {
|
||||
case 'invoice_created': {
|
||||
return {
|
||||
change: change.invoice_created.invoice,
|
||||
type: `Invoice ${change.invoice_created.invoice.id}`,
|
||||
namespace: 'domain',
|
||||
title: 'Invoice created',
|
||||
expansionTitle: 'Invoice',
|
||||
date: e.created_at,
|
||||
icon: 'request_quote',
|
||||
};
|
||||
}
|
||||
case 'invoice_status_changed': {
|
||||
const status = change.invoice_status_changed.status;
|
||||
return {
|
||||
change: isEmpty(getUnionValue(status)) ? null : status,
|
||||
type: 'InvoiceStatus',
|
||||
namespace: 'domain',
|
||||
title: `Invoice ${getKeyTitle(getUnionKey(status))}`,
|
||||
expansionTitle: 'Details',
|
||||
date: e.created_at,
|
||||
icon: {
|
||||
unpaid: STATUS_ICONS.warn,
|
||||
paid: STATUS_ICONS.success,
|
||||
cancelled: STATUS_ICONS.neutral,
|
||||
fulfilled: STATUS_ICONS.success,
|
||||
}[getUnionKey(status)],
|
||||
color: {
|
||||
unpaid: StatusColor.Warn,
|
||||
paid: StatusColor.Success,
|
||||
cancelled: StatusColor.Neutral,
|
||||
fulfilled: StatusColor.Success,
|
||||
}[getUnionKey(status)],
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_change': {
|
||||
const payload = change.invoice_payment_change.payload;
|
||||
switch (getUnionKey(payload)) {
|
||||
case 'invoice_payment_started': {
|
||||
return {
|
||||
change: payload.invoice_payment_started,
|
||||
type: 'InvoicePaymentStarted',
|
||||
namespace: 'payment_processing',
|
||||
title: 'Invoice payment started',
|
||||
expansionTitle: 'Payment',
|
||||
date: e.created_at,
|
||||
icon: 'start',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_risk_score_changed': {
|
||||
return {
|
||||
title: `Risk score changed to ${
|
||||
{ 9999: 'fatal', 100: 'high', 1: 'low' }[
|
||||
payload.invoice_payment_risk_score_changed.risk_score
|
||||
]
|
||||
} (${payload.invoice_payment_risk_score_changed.risk_score})`,
|
||||
date: e.created_at,
|
||||
icon: 'emergency',
|
||||
color: {
|
||||
9999: StatusColor.Warn,
|
||||
100: StatusColor.Pending,
|
||||
1: StatusColor.Success,
|
||||
}[payload.invoice_payment_risk_score_changed.risk_score],
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_route_changed': {
|
||||
return {
|
||||
change: payload.invoice_payment_route_changed,
|
||||
type: 'InvoicePaymentRouteChanged',
|
||||
namespace: 'payment_processing',
|
||||
title: 'Invoice payment route changed',
|
||||
expansionTitle: 'Route',
|
||||
date: e.created_at,
|
||||
icon: 'alt_route',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_cash_flow_changed': {
|
||||
return {
|
||||
change: payload.invoice_payment_cash_flow_changed.cash_flow,
|
||||
type: 'FinalCashFlow',
|
||||
namespace: 'domain',
|
||||
title: 'Invoice payment cash flow changed',
|
||||
expansionTitle: 'Cash Flow',
|
||||
date: e.created_at,
|
||||
icon: 'account_tree',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_status_changed': {
|
||||
const statusChange = {
|
||||
change: payload.invoice_payment_status_changed.status,
|
||||
type: 'InvoicePaymentStatus',
|
||||
namespace: 'domain',
|
||||
title: `Invoice payment status changed to ${getKeyTitle(
|
||||
getUnionKey(payload.invoice_payment_status_changed.status),
|
||||
)}`,
|
||||
expansionTitle: startCase(
|
||||
getKeyTitle(getUnionKey(payload.invoice_payment_status_changed.status)),
|
||||
),
|
||||
date: e.created_at,
|
||||
icon: 'priority_high',
|
||||
};
|
||||
switch (getUnionKey(payload.invoice_payment_status_changed.status)) {
|
||||
case 'pending':
|
||||
return {
|
||||
...statusChange,
|
||||
color: 'pending',
|
||||
icon: 'pending',
|
||||
change: null,
|
||||
};
|
||||
case 'processed':
|
||||
return {
|
||||
...statusChange,
|
||||
color: 'pending',
|
||||
icon: 'process_chart',
|
||||
change: null,
|
||||
};
|
||||
case 'captured':
|
||||
return { ...statusChange, color: 'success', icon: 'check' };
|
||||
case 'cancelled':
|
||||
return { ...statusChange, color: 'neutral', icon: 'block' };
|
||||
case 'refunded':
|
||||
return {
|
||||
...statusChange,
|
||||
color: 'success',
|
||||
icon: 'undo',
|
||||
change: null,
|
||||
};
|
||||
case 'failed':
|
||||
return { ...statusChange, color: 'warn', icon: 'priority_high' };
|
||||
case 'charged_back':
|
||||
return {
|
||||
...statusChange,
|
||||
color: 'success',
|
||||
icon: 'undo',
|
||||
change: null,
|
||||
};
|
||||
}
|
||||
return statusChange;
|
||||
}
|
||||
case 'invoice_payment_session_change': {
|
||||
const sessionChange = {
|
||||
change: payload.invoice_payment_session_change,
|
||||
type: 'InvoicePaymentSessionChange',
|
||||
namespace: 'payment_processing',
|
||||
title: `Invoice payment ${getKeyTitle(
|
||||
getUnionKey(payload.invoice_payment_session_change.payload),
|
||||
)}`,
|
||||
expansionTitle: startCase(
|
||||
getKeyTitle(
|
||||
getUnionKey(payload.invoice_payment_session_change.payload),
|
||||
),
|
||||
),
|
||||
date: e.created_at,
|
||||
icon: 'edit',
|
||||
};
|
||||
switch (getUnionKey(payload.invoice_payment_session_change.payload)) {
|
||||
case 'session_started':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'line_start',
|
||||
};
|
||||
case 'session_finished':
|
||||
return {
|
||||
...sessionChange,
|
||||
color: {
|
||||
succeeded: StatusColor.Success,
|
||||
failed: StatusColor.Warn,
|
||||
}[
|
||||
getUnionKey(
|
||||
payload.invoice_payment_session_change.payload
|
||||
.session_finished.result,
|
||||
)
|
||||
],
|
||||
icon: 'line_end',
|
||||
};
|
||||
case 'session_suspended':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'pause',
|
||||
color: 'pending',
|
||||
};
|
||||
case 'session_activated':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'line_start',
|
||||
};
|
||||
case 'session_transaction_bound':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'attach_file_add',
|
||||
};
|
||||
case 'session_proxy_state_changed':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'horizontal_distribute',
|
||||
};
|
||||
case 'session_interaction_changed':
|
||||
return {
|
||||
...sessionChange,
|
||||
icon: 'ads_click',
|
||||
};
|
||||
}
|
||||
return sessionChange;
|
||||
}
|
||||
case 'invoice_payment_capture_started': {
|
||||
return {
|
||||
change: payload.invoice_payment_capture_started.data,
|
||||
type: 'InvoicePaymentCaptureData',
|
||||
namespace: 'payment_processing',
|
||||
title: 'Invoice payment capture started',
|
||||
expansionTitle: payload.invoice_payment_capture_started.data.reason,
|
||||
date: e.created_at,
|
||||
icon: 'capture',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_chargeback_change': {
|
||||
const p = payload.invoice_payment_chargeback_change.payload;
|
||||
const chargebackChange = {
|
||||
change: p,
|
||||
type: 'InvoicePaymentChargebackChangePayload',
|
||||
namespace: 'payment_processing',
|
||||
title: `${upperFirst(getKeyTitle(getUnionKey(p)))} #${
|
||||
payload.invoice_payment_chargeback_change.id
|
||||
}`,
|
||||
expansionTitle: 'Chargeback change',
|
||||
date: e.created_at,
|
||||
icon: 'edit',
|
||||
};
|
||||
switch (getUnionKey(p)) {
|
||||
case 'invoice_payment_chargeback_created':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_created.chargeback,
|
||||
type: 'InvoicePaymentChargeback',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Chargeback',
|
||||
icon: 'source_notes',
|
||||
};
|
||||
case 'invoice_payment_chargeback_status_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: null,
|
||||
title: `${chargebackChange.title} to ${getKeyTitle(
|
||||
getUnionKey(p.invoice_payment_chargeback_status_changed.status),
|
||||
)}`,
|
||||
icon: {
|
||||
pending: STATUS_ICONS.pending,
|
||||
accepted: STATUS_ICONS.success,
|
||||
rejected: STATUS_ICONS.warn,
|
||||
cancelled: STATUS_ICONS.neutral,
|
||||
}[getUnionKey(p.invoice_payment_chargeback_status_changed.status)],
|
||||
color: {
|
||||
pending: StatusColor.Pending,
|
||||
accepted: StatusColor.Success,
|
||||
rejected: StatusColor.Warn,
|
||||
cancelled: StatusColor.Neutral,
|
||||
}[getUnionKey(p.invoice_payment_chargeback_status_changed.status)],
|
||||
};
|
||||
case 'invoice_payment_chargeback_cash_flow_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_cash_flow_changed.cash_flow,
|
||||
type: 'FinalCashFlow',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Cash Flow',
|
||||
icon: 'account_tree',
|
||||
};
|
||||
case 'invoice_payment_chargeback_body_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_body_changed.body,
|
||||
type: 'Cash',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Cash',
|
||||
icon: 'price_change',
|
||||
};
|
||||
case 'invoice_payment_chargeback_levy_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_levy_changed.levy,
|
||||
type: 'Cash',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Cash',
|
||||
icon: 'price_change',
|
||||
};
|
||||
case 'invoice_payment_chargeback_stage_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_stage_changed.stage,
|
||||
type: 'InvoicePaymentChargebackStage',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Stage',
|
||||
};
|
||||
case 'invoice_payment_chargeback_target_status_changed':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: null,
|
||||
title: `${chargebackChange.title} to ${getKeyTitle(
|
||||
getUnionKey(
|
||||
p.invoice_payment_chargeback_target_status_changed.status,
|
||||
),
|
||||
)}`,
|
||||
icon: {
|
||||
pending: STATUS_ICONS.pending,
|
||||
accepted: STATUS_ICONS.success,
|
||||
rejected: STATUS_ICONS.warn,
|
||||
cancelled: STATUS_ICONS.neutral,
|
||||
}[
|
||||
getUnionKey(
|
||||
p.invoice_payment_chargeback_target_status_changed.status,
|
||||
)
|
||||
],
|
||||
color: {
|
||||
pending: StatusColor.Pending,
|
||||
accepted: StatusColor.Success,
|
||||
rejected: StatusColor.Warn,
|
||||
cancelled: StatusColor.Neutral,
|
||||
}[
|
||||
getUnionKey(
|
||||
p.invoice_payment_chargeback_target_status_changed.status,
|
||||
)
|
||||
],
|
||||
};
|
||||
case 'invoice_payment_chargeback_clock_update':
|
||||
return {
|
||||
...chargebackChange,
|
||||
change: p.invoice_payment_chargeback_clock_update.clock,
|
||||
type: 'AccounterClock',
|
||||
namespace: 'domain',
|
||||
expansionTitle: 'Clock',
|
||||
icon: 'more_time',
|
||||
};
|
||||
}
|
||||
return chargebackChange;
|
||||
}
|
||||
case 'invoice_payment_rollback_started': {
|
||||
return {
|
||||
change: payload.invoice_payment_rollback_started.reason,
|
||||
type: 'OperationFailure',
|
||||
namespace: 'domain',
|
||||
title: 'Invoice payment rollback started',
|
||||
expansionTitle: 'Reason',
|
||||
date: e.created_at,
|
||||
icon: 'start',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_clock_update': {
|
||||
return {
|
||||
change: payload.invoice_payment_clock_update.clock,
|
||||
type: 'AccounterClock',
|
||||
namespace: 'domain',
|
||||
title: 'Invoice payment clock updated',
|
||||
expansionTitle: 'Clock',
|
||||
date: e.created_at,
|
||||
icon: 'more_time',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_cash_changed': {
|
||||
return {
|
||||
change: payload.invoice_payment_cash_changed,
|
||||
type: 'InvoicePaymentCashChanged',
|
||||
namespace: 'payment_processing',
|
||||
title: 'Invoice payment cash changed',
|
||||
expansionTitle: 'Cash change',
|
||||
date: e.created_at,
|
||||
icon: 'price_change',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_shop_limit_initiated': {
|
||||
return {
|
||||
title: 'Invoice payment shop limit initiated',
|
||||
date: e.created_at,
|
||||
icon: 'production_quantity_limits',
|
||||
};
|
||||
}
|
||||
case 'invoice_payment_shop_limit_applied': {
|
||||
return {
|
||||
title: 'Invoice payment shop limit applied',
|
||||
date: e.created_at,
|
||||
icon: 'production_quantity_limits',
|
||||
color: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
change: change,
|
||||
type: 'InvoiceChange',
|
||||
namespace: 'payment_processing',
|
||||
title: 'Invoice changed',
|
||||
expansionTitle: 'Invoice Change',
|
||||
date: e.created_at,
|
||||
icon: 'edit',
|
||||
};
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<cc-page-layout [progress]="isLoading$ | async" title="Refunds">
|
||||
<cc-refunds-table
|
||||
[invoiceID]="(payment$ | async).invoice_id"
|
||||
[partyID]="(payment$ | async).owner_id"
|
||||
[paymentID]="(payment$ | async).id"
|
||||
></cc-refunds-table>
|
||||
</cc-page-layout>
|
@ -0,0 +1,20 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { PageLayoutModule } from '../../../../shared';
|
||||
import { PaymentDetailsService } from '../../payment-details.service';
|
||||
import { RefundsTableModule } from '../../refunds-table';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-payment-refunds',
|
||||
standalone: true,
|
||||
imports: [CommonModule, PageLayoutModule, RefundsTableModule],
|
||||
templateUrl: './payment-refunds.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class PaymentRefundsComponent {
|
||||
payment$ = this.paymentDetailsService.payment$;
|
||||
isLoading$ = this.paymentDetailsService.isLoading$;
|
||||
|
||||
constructor(private paymentDetailsService: PaymentDetailsService) {}
|
||||
}
|
@ -14,6 +14,41 @@ import { ROUTING_CONFIG } from './routing-config';
|
||||
component: PaymentDetailsComponent,
|
||||
canActivate: [AppAuthGuardService],
|
||||
data: ROUTING_CONFIG,
|
||||
children: [
|
||||
{
|
||||
path: 'details',
|
||||
loadComponent: () =>
|
||||
import('./components/payment-details/payment-details.component').then(
|
||||
(m) => m.PaymentDetailsComponent,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'chargebacks',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./components/payment-chargebacks/payment-chargebacks.component'
|
||||
).then((m) => m.PaymentChargebacksComponent),
|
||||
},
|
||||
{
|
||||
path: 'refunds',
|
||||
loadComponent: () =>
|
||||
import('./components/payment-refunds/payment-refunds.component').then(
|
||||
(m) => m.PaymentRefundsComponent,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'events',
|
||||
loadComponent: () =>
|
||||
import('./components/payment-events/payment-events.component').then(
|
||||
(m) => m.PaymentEventsComponent,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'details',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
],
|
||||
|
@ -1,41 +1,15 @@
|
||||
<cc-page-layout
|
||||
[progress]="isLoading$ | async"
|
||||
<cc-sub-page-layout
|
||||
[links]="[
|
||||
{ label: 'Payment', url: 'details' },
|
||||
{ label: 'Events', url: 'events' },
|
||||
{ label: 'Refunds', url: 'refunds' },
|
||||
{ label: 'Chargebacks', url: 'chargebacks' }
|
||||
]"
|
||||
[tags]="tags$ | async"
|
||||
id="{{
|
||||
(payment$ | async) ? (payment$ | async)?.invoice_id + '.' + (payment$ | async)?.id : ''
|
||||
}}"
|
||||
title="Payment"
|
||||
>
|
||||
<ng-container *ngIf="payment$ | async as payment">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<cc-magista-thrift-viewer
|
||||
[extensions]="extensions$ | async"
|
||||
[value]="payment"
|
||||
type="StatPayment"
|
||||
></cc-magista-thrift-viewer>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<h2 class="mat-h1 mat-no-margin">Refunds</h2>
|
||||
<cc-refunds-table
|
||||
[invoiceID]="payment.invoice_id"
|
||||
[partyID]="payment.owner_id"
|
||||
[paymentID]="payment.id"
|
||||
></cc-refunds-table>
|
||||
|
||||
<div style="display: flex; place-content: stretch space-between">
|
||||
<h2 class="mat-h1 mat-no-margin">Chargebacks</h2>
|
||||
<v-actions>
|
||||
<button color="primary" mat-button (click)="createChargeback()">
|
||||
Create chargeback
|
||||
</button>
|
||||
</v-actions>
|
||||
</div>
|
||||
<cc-chargebacks
|
||||
[chargebacks]="chargebacks$ | async"
|
||||
[invoiceId]="payment.invoice_id"
|
||||
[paymentId]="payment.id"
|
||||
></cc-chargebacks>
|
||||
</ng-container>
|
||||
</cc-page-layout>
|
||||
<router-outlet></router-outlet>
|
||||
</cc-sub-page-layout>
|
||||
|
@ -1,27 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, DestroyRef, Inject, LOCALE_ID } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ThriftAstMetadata } from '@vality/domain-proto';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { InvoicePaymentStatus, InvoicePaymentFlow } from '@vality/domain-proto/internal/domain';
|
||||
import {
|
||||
DialogService,
|
||||
DialogResponseStatus,
|
||||
getImportValue,
|
||||
formatCurrency,
|
||||
Color,
|
||||
} from '@vality/ng-core';
|
||||
import { getUnionKey, getUnionValue, isTypeWithAliases } from '@vality/ng-thrift';
|
||||
import { Color } from '@vality/ng-core';
|
||||
import { getUnionKey } from '@vality/ng-thrift';
|
||||
import startCase from 'lodash-es/startCase';
|
||||
import { Subject, merge, defer, Observable, of } from 'rxjs';
|
||||
import { shareReplay, switchMap, map } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { InvoicingService } from '@cc/app/api/payment-processing';
|
||||
|
||||
import { MetadataViewExtension } from '../../shared/components/json-viewer';
|
||||
import { DomainMetadataViewExtensionsService } from '../../shared/components/thrift-api-crud/domain/domain-thrift-viewer/services/domain-metadata-view-extensions';
|
||||
import { AmountCurrencyService } from '../../shared/services';
|
||||
|
||||
import { CreateChargebackDialogComponent } from './create-chargeback-dialog/create-chargeback-dialog.component';
|
||||
import { PaymentDetailsService } from './payment-details.service';
|
||||
|
||||
@Component({
|
||||
@ -32,61 +15,11 @@ import { PaymentDetailsService } from './payment-details.service';
|
||||
export class PaymentDetailsComponent {
|
||||
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 }),
|
||||
);
|
||||
metadata$ = getImportValue<ThriftAstMetadata[]>(import('@vality/magista-proto/metadata.json'));
|
||||
extensions$: Observable<MetadataViewExtension[]> = this.payment$.pipe(
|
||||
map((payment): MetadataViewExtension[] => [
|
||||
this.domainMetadataViewExtensionsService.createShopExtension(payment.owner_id),
|
||||
{
|
||||
determinant: (d) => of(isTypeWithAliases(d, 'Amount', 'domain')),
|
||||
extension: (_, amount: number) =>
|
||||
this.amountCurrencyService.getCurrency(payment.currency_symbolic_code).pipe(
|
||||
map((c) => ({
|
||||
value: formatCurrency(
|
||||
amount,
|
||||
c.data.symbolic_code,
|
||||
'long',
|
||||
this._locale,
|
||||
c.data.exponent,
|
||||
),
|
||||
})),
|
||||
),
|
||||
},
|
||||
{
|
||||
determinant: (d) =>
|
||||
of(
|
||||
isTypeWithAliases(d, 'InvoicePaymentStatus', 'domain') ||
|
||||
isTypeWithAliases(d, 'InvoicePaymentFlow', 'magista'),
|
||||
),
|
||||
extension: (_, v) => of({ hidden: !Object.keys(getUnionValue(v)).length }),
|
||||
},
|
||||
{
|
||||
determinant: (d) =>
|
||||
of(
|
||||
isTypeWithAliases(d?.trueParent, 'StatPayment', 'magista') &&
|
||||
(d.field.name === 'make_recurrent' ||
|
||||
d.field.name === 'currency_symbolic_code' ||
|
||||
isTypeWithAliases(d, 'InvoiceID', 'domain') ||
|
||||
isTypeWithAliases(d, 'InvoicePaymentID', 'domain')),
|
||||
),
|
||||
extension: () => of({ hidden: true }),
|
||||
},
|
||||
]),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
tags$ = this.payment$.pipe(
|
||||
map((payment) => [
|
||||
{
|
||||
value: payment.currency_symbolic_code,
|
||||
},
|
||||
{
|
||||
value: startCase(getUnionKey(payment.status)),
|
||||
color: (
|
||||
@ -101,50 +34,29 @@ export class PaymentDetailsComponent {
|
||||
} as Record<keyof InvoicePaymentStatus, Color>
|
||||
)[getUnionKey(payment.status)],
|
||||
},
|
||||
{
|
||||
value: startCase(getUnionKey(payment.flow)),
|
||||
color: (
|
||||
{
|
||||
instant: 'success',
|
||||
hold: 'pending',
|
||||
} as Record<keyof InvoicePaymentFlow, Color>
|
||||
)[getUnionKey(payment.flow)],
|
||||
},
|
||||
{
|
||||
value: payment.make_recurrent ? 'Recurrent' : 'Not Recurrent',
|
||||
color: payment.make_recurrent ? 'success' : 'neutral',
|
||||
},
|
||||
{
|
||||
value: payment.currency_symbolic_code,
|
||||
},
|
||||
...(getUnionKey(payment.flow) === 'hold'
|
||||
? [
|
||||
{
|
||||
value: startCase(getUnionKey(payment.flow)),
|
||||
color: (
|
||||
{
|
||||
instant: 'success',
|
||||
hold: 'pending',
|
||||
} as Record<keyof InvoicePaymentFlow, Color>
|
||||
)[getUnionKey(payment.flow)],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(payment.make_recurrent
|
||||
? [
|
||||
{
|
||||
value: payment.make_recurrent ? 'Recurrent' : 'Not Recurrent',
|
||||
color: payment.make_recurrent ? 'pending' : 'neutral',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]),
|
||||
);
|
||||
|
||||
private updateChargebacks$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private paymentDetailsService: PaymentDetailsService,
|
||||
private route: ActivatedRoute,
|
||||
private invoicingService: InvoicingService,
|
||||
private dialogService: DialogService,
|
||||
private destroyRef: DestroyRef,
|
||||
private domainMetadataViewExtensionsService: DomainMetadataViewExtensionsService,
|
||||
private amountCurrencyService: AmountCurrencyService,
|
||||
@Inject(LOCALE_ID) private _locale: string,
|
||||
) {}
|
||||
|
||||
createChargeback() {
|
||||
this.dialogService
|
||||
.open(
|
||||
CreateChargebackDialogComponent,
|
||||
this.route.snapshot.params as Record<'invoiceID' | 'paymentID', string>,
|
||||
)
|
||||
.afterClosed()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(({ status }) => {
|
||||
if (status === DialogResponseStatus.Success) {
|
||||
this.updateChargebacks$.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
constructor(private paymentDetailsService: PaymentDetailsService) {}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { ActionsModule, DialogModule } from '@vality/ng-core';
|
||||
|
||||
import { StatusModule, PageLayoutModule } from '@cc/app/shared/components';
|
||||
import { StatusModule, PageLayoutModule, SubPageLayoutComponent } from '@cc/app/shared/components';
|
||||
import { DetailsItemModule } from '@cc/components/details-item';
|
||||
import { HeadlineModule } from '@cc/components/headline';
|
||||
|
||||
@ -43,6 +44,8 @@ import { RefundsTableModule } from './refunds-table';
|
||||
JsonViewerModule,
|
||||
RefundsTableModule,
|
||||
MagistaThriftViewerComponent,
|
||||
SubPageLayoutComponent,
|
||||
RouterOutlet,
|
||||
],
|
||||
declarations: [PaymentDetailsComponent, CreateChargebackDialogComponent],
|
||||
})
|
||||
|
@ -0,0 +1,26 @@
|
||||
<mat-toolbar *ngIf="tags()?.length" style="height: auto; min-height: 48px; padding: 0 24px">
|
||||
<v-actions style="width: 100%">
|
||||
<div class="mat-body-strong" style="margin: auto auto auto 0">
|
||||
{{ title() }} {{ id() ? '#' + id() : '' }}
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; align-items: center">
|
||||
@for (tag of tags(); track tag) {
|
||||
<v-tag [color]="tag.color" style="margin-top: 8px">{{ tag.value }}</v-tag>
|
||||
}
|
||||
</div>
|
||||
</v-actions>
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-sidenav-container autosize>
|
||||
<mat-sidenav-content style="overflow: unset"><ng-content></ng-content></mat-sidenav-content>
|
||||
<mat-sidenav
|
||||
[fixedTopGap]="64 + 48"
|
||||
[opened]="!(sidenavInfoService.opened$ | async)"
|
||||
fixedInViewport="true"
|
||||
mode="side"
|
||||
position="end"
|
||||
style="background: transparent; border: none; padding: 24px 0 24px 0"
|
||||
>
|
||||
<v-nav [links]="links()" type="secondary"></v-nav
|
||||
></mat-sidenav>
|
||||
</mat-sidenav-container>
|
@ -0,0 +1,32 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, input } from '@angular/core';
|
||||
import { MatSidenav, MatSidenavContent, MatSidenavContainer } from '@angular/material/sidenav';
|
||||
import { MatToolbar } from '@angular/material/toolbar';
|
||||
import { NavComponent, TagModule, Color, Link, ActionsModule } from '@vality/ng-core';
|
||||
|
||||
import { SidenavInfoService } from '../../../sidenav-info';
|
||||
|
||||
@Component({
|
||||
selector: 'cc-sub-page-layout',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NavComponent,
|
||||
MatSidenav,
|
||||
MatSidenavContent,
|
||||
MatToolbar,
|
||||
TagModule,
|
||||
MatSidenavContainer,
|
||||
ActionsModule,
|
||||
],
|
||||
templateUrl: './sub-page-layout.component.html',
|
||||
styles: ``,
|
||||
})
|
||||
export class SubPageLayoutComponent {
|
||||
title = input<string>();
|
||||
id = input<string>();
|
||||
links = input<Link[]>([]);
|
||||
tags = input<{ value: string; color: Color }[] | null>([]);
|
||||
|
||||
constructor(protected sidenavInfoService: SidenavInfoService) {}
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './page-layout.module';
|
||||
export * from './components/sub-page-layout/sub-page-layout.component';
|
||||
|
@ -10,6 +10,7 @@ import { ActionsModule, TagModule } from '@vality/ng-core';
|
||||
import { ThriftPipesModule } from '@vality/ng-thrift';
|
||||
|
||||
import { PageLayoutActionsComponent } from './components/page-layout-actions/page-layout-actions.component';
|
||||
import { SubPageLayoutComponent } from './components/sub-page-layout/sub-page-layout.component';
|
||||
import { PageLayoutComponent } from './page-layout.component';
|
||||
|
||||
@NgModule({
|
||||
@ -24,6 +25,7 @@ import { PageLayoutComponent } from './page-layout.component';
|
||||
MatToolbar,
|
||||
TagModule,
|
||||
ThriftPipesModule,
|
||||
SubPageLayoutComponent,
|
||||
],
|
||||
declarations: [PageLayoutComponent, PageLayoutActionsComponent],
|
||||
exports: [PageLayoutComponent, PageLayoutActionsComponent],
|
||||
|
@ -3,8 +3,8 @@
|
||||
[extensions]="extensions$ | async"
|
||||
[kind]="kind"
|
||||
[metadata]="metadata$ | async"
|
||||
[namespace]="namespace"
|
||||
[progress]="progress"
|
||||
[type]="type"
|
||||
[value]="value"
|
||||
namespace="domain"
|
||||
></cc-thrift-viewer>
|
||||
|
@ -20,6 +20,7 @@ export class DomainThriftViewerComponent<T> {
|
||||
@Input() compared?: T;
|
||||
@Input() type: ValueType;
|
||||
@Input({ transform: booleanAttribute }) progress: boolean = false;
|
||||
@Input() namespace = 'domain';
|
||||
// @Input() extensions?: MetadataViewExtension[];
|
||||
|
||||
metadata$ = getImportValue<ThriftAstMetadata[]>(import('@vality/domain-proto/metadata.json'));
|
||||
|
Loading…
Reference in New Issue
Block a user