diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1635131d..720bde60 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; +import { Link } from '@vality/ng-core'; import { KeycloakService } from 'keycloak-angular'; import sortBy from 'lodash-es/sortBy'; import { from, Observable } from 'rxjs'; -import { shareReplay, map } from 'rxjs/operators'; +import { shareReplay, map, startWith } from 'rxjs/operators'; -import { AppAuthGuardService } from '@cc/app/shared/services'; +import { AppAuthGuardService, Services } from '@cc/app/shared/services'; import { environment } from '../environments/environment'; @@ -28,9 +29,8 @@ import { SidenavInfoService } from './shared/components/sidenav-info'; styleUrls: ['./app.component.scss'], }) export class AppComponent { - menuItemsGroups$: Observable<{ name: string; route: string }[][]> = from( - this.keycloakService.loadUserProfile(), - ).pipe( + links$: Observable = from(this.keycloakService.loadUserProfile()).pipe( + startWith(null), map(() => this.getMenuItemsGroups()), shareReplay({ refCount: true, bufferSize: 1 }), ); @@ -58,80 +58,80 @@ export class AppComponent { } private getMenuItemsGroups() { - const menuItems = [ + const menuItems: (Link & { services: Services[] })[][] = [ [ { - name: 'Domain config', - route: '/domain', + label: 'Domain config', + url: '/domain', services: DOMAIN_ROUTING_CONFIG.services, }, { - name: 'Terminals', - route: '/terminals', + label: 'Terminals', + url: '/terminals', services: TERMINALS_ROUTING_CONFIG.services, }, { - name: 'Repairing', - route: '/repairing', + label: 'Repairing', + url: '/repairing', services: REPAIRING_ROUTING_CONFIG.services, }, { - name: 'Sources', - route: '/sources', + label: 'Sources', + url: '/sources', services: SOURCES_ROUTING_CONFIG.services, }, ], [ { - name: 'Merchants', - route: '/parties', + label: 'Merchants', + url: '/parties', services: PARTIES_ROUTING_CONFIG.services, }, { - name: 'Shops', - route: '/shops', + label: 'Shops', + url: '/shops', services: SHOPS_ROUTING_CONFIG.services, }, { - name: 'Wallets', - route: '/wallets', + label: 'Wallets', + url: '/wallets', services: WALLETS_ROUTING_CONFIG.services, }, { - name: 'Claims', - route: '/claims', + label: 'Claims', + url: '/claims', services: CLAIMS_ROUTING_CONFIG.services, }, ], sortBy( [ { - name: 'Payments', - route: '/payments', + label: 'Payments', + url: '/payments', services: PAYMENTS_ROUTING_CONFIG.services, }, { - name: 'Payouts', - route: '/payouts', + label: 'Payouts', + url: '/payouts', services: PAYOUTS_ROUTING_CONFIG.services, }, { - name: 'Chargebacks', - route: '/chargebacks', + label: 'Chargebacks', + url: '/chargebacks', services: WALLETS_ROUTING_CONFIG.services, }, { - name: 'Deposits', - route: '/deposits', + label: 'Deposits', + url: '/deposits', services: DEPOSITS_ROUTING_CONFIG.services, }, { - name: 'Withdrawals', - route: '/withdrawals', + label: 'Withdrawals', + url: '/withdrawals', services: WITHDRAWALS_ROUTING_CONFIG.services, }, ], - 'name', + 'label', ), ]; return menuItems.map((group) => diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 97692774..171a5f5b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,7 +13,7 @@ import { MatToolbarModule } from '@angular/material/toolbar'; import { BrowserModule, DomSanitizer } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { InputMaskModule } from '@ngneat/input-mask'; -import { QUERY_PARAMS_SERIALIZERS } from '@vality/ng-core'; +import { NavComponent, QUERY_PARAMS_SERIALIZERS } from '@vality/ng-core'; import { MonacoEditorModule } from 'ngx-monaco-editor-v2'; import { KeycloakTokenInfoModule } from '@cc/app/shared/services'; @@ -72,6 +72,7 @@ export let AppInjector: Injector; SectionsModule, SidenavInfoComponent, ToolbarComponent, + NavComponent, MonacoEditorModule.forRoot(), // TODO: hack for metadata datetime 😡 MatDatepickerModule, diff --git a/src/app/core/components/toolbar/toolbar.component.html b/src/app/core/components/toolbar/toolbar.component.html index 6ea2fcc4..47dce167 100644 --- a/src/app/core/components/toolbar/toolbar.component.html +++ b/src/app/core/components/toolbar/toolbar.component.html @@ -1,6 +1,27 @@ - Control Center{{ username$ | async }} - + + +
+ {{ username$ | async }} + +
diff --git a/src/app/core/components/toolbar/toolbar.component.scss b/src/app/core/components/toolbar/toolbar.component.scss index 2174df56..1c585816 100644 --- a/src/app/core/components/toolbar/toolbar.component.scss +++ b/src/app/core/components/toolbar/toolbar.component.scss @@ -1,8 +1,52 @@ .toolbar { position: fixed; z-index: 2; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + + & > * { + display: flex; + align-items: center; + } } -.spacer { - flex: 1 1 auto; +.search { + align-self: end; + margin-bottom: -9px; + justify-self: center; + position: relative; + + .copy { + cursor: pointer; + position: absolute; + right: -291px; + top: 8px; + } + + ::ng-deep .mdc-floating-label { + pointer-events: none !important; + } + + ::ng-deep & > * { + min-width: 280px; + } + + ::ng-deep * { + color: #fff !important; + } + + ::ng-deep .mdc-notched-outline * { + border-color: rgba(255, 255, 255, 0.5) !important; + } + + ::ng-deep .ng-spinner-loader { + border-color: rgba(255, 255, 255, 0.2); + border-left-color: #fff; + } + + --mtx-select-enabled-arrow-color: #fff; +} + +.user { + justify-self: end; } diff --git a/src/app/core/components/toolbar/toolbar.component.ts b/src/app/core/components/toolbar/toolbar.component.ts index 0da65884..841588ca 100644 --- a/src/app/core/components/toolbar/toolbar.component.ts +++ b/src/app/core/components/toolbar/toolbar.component.ts @@ -1,29 +1,92 @@ +import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, DestroyRef, OnInit } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { MatToolbarModule } from '@angular/material/toolbar'; +import { Router } from '@angular/router'; +import { UrlService } from '@vality/ng-core'; import { KeycloakService } from 'keycloak-angular'; import { from } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { map, shareReplay, distinctUntilChanged } from 'rxjs/operators'; + +import { MerchantFieldModule } from '../../../shared/components/merchant-field'; @Component({ selector: 'cc-toolbar', standalone: true, - imports: [MatButtonModule, MatIconModule, MatMenuModule, MatToolbarModule, CommonModule], + imports: [ + MatButtonModule, + MatIconModule, + MatMenuModule, + MatToolbarModule, + CommonModule, + MerchantFieldModule, + FormsModule, + ReactiveFormsModule, + CdkCopyToClipboard, + ], templateUrl: './toolbar.component.html', styleUrl: './toolbar.component.scss', }) -export class ToolbarComponent { +export class ToolbarComponent implements OnInit { username$ = from(this.keycloakService.loadUserProfile()).pipe( map(() => this.keycloakService.getUsername()), shareReplay({ refCount: true, bufferSize: 1 }), ); + partyIdControl = new FormControl(this.getPartyId()); - constructor(private keycloakService: KeycloakService) {} + constructor( + private keycloakService: KeycloakService, + private router: Router, + private urlService: UrlService, + private destroyRef: DestroyRef, + private snackBar: MatSnackBar, + ) {} + + ngOnInit() { + this.partyIdControl.valueChanges + .pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)) + .subscribe((partyId) => { + if (partyId) { + if (this.getPartyId() !== partyId) { + void this.router.navigate([`/party/${partyId}`]); + } + } else { + void this.router.navigate([`/parties`]); + } + }); + this.urlService.path$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((path) => { + const partyId = this.getPartyId(path); + if (partyId) { + if (partyId !== this.partyIdControl.value) { + this.partyIdControl.setValue(partyId, { emitEvent: false }); + } + } else if (this.partyIdControl.value) { + this.partyIdControl.setValue(null, { emitEvent: false }); + } + }); + } logout() { void this.keycloakService.logout(); } + + copyNotify(res: boolean) { + this.snackBar.open( + res + ? `Party Id #${this.partyIdControl.value} copied` + : `Party Id not copied, select and copy yourself: ${this.partyIdControl.value}`, + 'OK', + { duration: res ? 2_000 : 60_000 }, + ); + } + + private getPartyId(path: string[] = this.urlService.path) { + return path[0] === 'party' && path[1] ? path[1] : null; + } } diff --git a/src/app/sections/claim/claim.component.html b/src/app/sections/claim/claim.component.html index 49a1bb0e..c9037fe9 100644 --- a/src/app/sections/claim/claim.component.html +++ b/src/app/sections/claim/claim.component.html @@ -1,20 +1,4 @@ - + {{ claim.status | ccUnionKey | keyTitle | titlecase diff --git a/src/app/sections/claims/claims-routing.module.ts b/src/app/sections/claims/claims-routing.module.ts index f9d6e356..e19291a7 100644 --- a/src/app/sections/claims/claims-routing.module.ts +++ b/src/app/sections/claims/claims-routing.module.ts @@ -10,7 +10,7 @@ import { ROUTING_CONFIG } from './routing-config'; imports: [ RouterModule.forChild([ { - path: 'claims', + path: '', component: ClaimsComponent, canActivate: [AppAuthGuardService], data: ROUTING_CONFIG, diff --git a/src/app/sections/claims/claims-table/claims-table.component.html b/src/app/sections/claims/claims-table/claims-table.component.html index 4247718f..4576b42a 100644 --- a/src/app/sections/claims/claims-table/claims-table.component.html +++ b/src/app/sections/claims/claims-table/claims-table.component.html @@ -1,5 +1,5 @@ (); @Output() more = new EventEmitter(); - columns: Column[] = [ + columns = computed[]>(() => + this.sourceColumns.filter( + (c) => (isObject(c) && c?.field !== 'party_id') || !this.noParty(), + ), + ); + private sourceColumns: Column[] = [ { field: 'id', link: (d) => this.getClaimLink(d.party_id, d.id) }, createPartyColumn('party_id'), { @@ -51,10 +65,7 @@ export class ClaimsTableComponent { ]), ]; - constructor( - private router: Router, - private partiesStoreService: PartiesStoreService, - ) {} + constructor(private router: Router) {} navigateToClaim(partyId: string, claimID: number) { void this.router.navigate([this.getClaimLink(partyId, claimID)]); diff --git a/src/app/sections/claims/claims.component.html b/src/app/sections/claims/claims.component.html index c76e7c95..bf46facd 100644 --- a/src/app/sections/claims/claims.component.html +++ b/src/app/sections/claims/claims.component.html @@ -4,6 +4,10 @@ > + - diff --git a/src/app/sections/claims/claims.component.ts b/src/app/sections/claims/claims.component.ts index 3460dbd6..6d39d5ff 100644 --- a/src/app/sections/claims/claims.component.ts +++ b/src/app/sections/claims/claims.component.ts @@ -4,15 +4,17 @@ import { NonNullableFormBuilder } from '@angular/forms'; import { PartyID } from '@vality/domain-proto/domain'; import { DialogService, LoadOptions, QueryParamsService, clean } from '@vality/ng-core'; import { debounceTime } from 'rxjs'; -import { startWith } from 'rxjs/operators'; +import { startWith, take } from 'rxjs/operators'; import { CLAIM_STATUSES } from '../../api/claim-management'; +import { PartyStoreService } from '../party'; import { CreateClaimDialogComponent } from './components/create-claim-dialog/create-claim-dialog.component'; import { FetchClaimsService } from './fetch-claims.service'; @Component({ templateUrl: './claims.component.html', + providers: [PartyStoreService], }) export class ClaimsComponent implements OnInit { isLoading$ = this.fetchClaimsService.isLoading$; @@ -25,6 +27,7 @@ export class ClaimsComponent implements OnInit { statuses: [[] as string[]], }); active = 0; + party$ = this.partyStoreService.party$; private selectedPartyId: PartyID; @@ -34,6 +37,7 @@ export class ClaimsComponent implements OnInit { private fb: NonNullableFormBuilder, private qp: QueryParamsService, private destroyRef: DestroyRef, + private partyStoreService: PartyStoreService, ) {} ngOnInit(): void { @@ -48,11 +52,17 @@ export class ClaimsComponent implements OnInit { load(options?: LoadOptions): void { const filters = clean(this.filtersForm.value); void this.qp.set(filters); - this.fetchClaimsService.load( - { ...filters, statuses: filters.statuses?.map((status) => ({ [status]: {} })) || [] }, - options, - ); this.active = Object.keys(filters).length; + this.partyStoreService.party$.pipe(take(1)).subscribe((p) => { + this.fetchClaimsService.load( + clean({ + party_id: p ? p.id : undefined, + ...filters, + statuses: filters.statuses?.map((status) => ({ [status]: {} })) || [], + }), + options, + ); + }); } more(): void { diff --git a/src/app/sections/claims/index.ts b/src/app/sections/claims/index.ts new file mode 100644 index 00000000..395f398e --- /dev/null +++ b/src/app/sections/claims/index.ts @@ -0,0 +1 @@ +export * from './claims.module'; diff --git a/src/app/sections/domain/domain-routing.module.ts b/src/app/sections/domain/domain-routing.module.ts index 5a86e4ce..07fd6d82 100644 --- a/src/app/sections/domain/domain-routing.module.ts +++ b/src/app/sections/domain/domain-routing.module.ts @@ -13,12 +13,7 @@ import { ROUTING_CONFIG } from './routing-config'; path: '', canActivate: [AppAuthGuardService], data: ROUTING_CONFIG, - children: [ - { - path: '', - component: DomainInfoComponent, - }, - ], + component: DomainInfoComponent, }, ]), ], diff --git a/src/app/sections/party-shops/party-shops.component.html b/src/app/sections/party-shops/party-shops.component.html index 201ec99d..e1744965 100644 --- a/src/app/sections/party-shops/party-shops.component.html +++ b/src/app/sections/party-shops/party-shops.component.html @@ -1,9 +1,8 @@ -
-
Shops
+ -
+
diff --git a/src/app/sections/party-shops/party-shops.module.ts b/src/app/sections/party-shops/party-shops.module.ts index dcb81711..b983a82d 100644 --- a/src/app/sections/party-shops/party-shops.module.ts +++ b/src/app/sections/party-shops/party-shops.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { InputFieldModule } from '@vality/ng-core'; +import { PageLayoutModule } from '../../shared'; import { ShopsTableComponent } from '../../shared/components/shops-table'; import { PartyShopsRoutingModule } from './party-shops-routing.module'; @@ -16,6 +17,7 @@ import { PartyShopsComponent } from './party-shops.component'; ReactiveFormsModule, InputFieldModule, ShopsTableComponent, + PageLayoutModule, ], declarations: [PartyShopsComponent], }) diff --git a/src/app/sections/party/index.ts b/src/app/sections/party/index.ts new file mode 100644 index 00000000..df798033 --- /dev/null +++ b/src/app/sections/party/index.ts @@ -0,0 +1,2 @@ +export * from './party-store.service'; +export * from './party.module'; diff --git a/src/app/sections/party/party-routing.module.ts b/src/app/sections/party/party-routing.module.ts index a446f922..32287d4d 100644 --- a/src/app/sections/party/party-routing.module.ts +++ b/src/app/sections/party/party-routing.module.ts @@ -25,7 +25,28 @@ import { ROUTING_CONFIG } from './routing-config'; loadChildren: () => import('../routing-rules').then((m) => m.RoutingRulesModule), }, - { path: '', redirectTo: 'shops', pathMatch: 'full' }, + { + path: 'claims', + loadChildren: () => import('../claims').then((m) => m.ClaimsModule), + }, + { + path: 'claim', + redirectTo: 'claims', + }, + { + path: 'wallets', + loadChildren: () => + import('../wallets/wallets.module').then((m) => m.WalletsModule), + }, + { + path: 'wallet', + redirectTo: 'wallets', + }, + { + path: '', + redirectTo: 'shops', + pathMatch: 'full', + }, ], }, ]), diff --git a/src/app/sections/party/party-store.service.ts b/src/app/sections/party/party-store.service.ts new file mode 100644 index 00000000..62447547 --- /dev/null +++ b/src/app/sections/party/party-store.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Party } from '@vality/domain-proto/domain'; +import { NotifyLogService } from '@vality/ng-core'; +import { EMPTY, Observable, of } from 'rxjs'; +import { + startWith, + switchMap, + catchError, + shareReplay, + distinctUntilChanged, +} from 'rxjs/operators'; + +import { PartyManagementService } from '../../api/payment-processing'; + +@Injectable() +export class PartyStoreService { + party$: Observable | null> = this.route.params.pipe( + startWith(this.route.snapshot.params), + switchMap(({ partyID }) => + partyID + ? this.partyManagementService.Get(partyID).pipe( + catchError((err) => { + this.log.error(err); + return EMPTY; + }), + ) + : of(null), + ), + startWith(this.partyId ? { id: this.partyId } : null), + distinctUntilChanged(), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + + private get partyId() { + return this.route.snapshot.params.partyID; + } + + constructor( + private route: ActivatedRoute, + private partyManagementService: PartyManagementService, + private log: NotifyLogService, + ) {} +} diff --git a/src/app/sections/party/party.component.html b/src/app/sections/party/party.component.html index 63c60393..fd54b2a3 100644 --- a/src/app/sections/party/party.component.html +++ b/src/app/sections/party/party.component.html @@ -1,24 +1,35 @@ -
-
-

- {{ (party$ | async)?.contact_info?.email }} -

-

{{ (party$ | async)?.id }}

+ +
+ + + + {{ party?.blocking | ccUnionKey | titlecase }} + {{ party?.suspension | ccUnionKey | titlecase }}
+
- -
+ + + + + diff --git a/src/app/sections/party/party.component.scss b/src/app/sections/party/party.component.scss deleted file mode 100644 index 761b279b..00000000 --- a/src/app/sections/party/party.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -.party-container { - margin: 24px; -} - -router-outlet { - margin-bottom: 0 !important; -} diff --git a/src/app/sections/party/party.component.ts b/src/app/sections/party/party.component.ts index 83f204d1..6255e938 100644 --- a/src/app/sections/party/party.component.ts +++ b/src/app/sections/party/party.component.ts @@ -1,89 +1,57 @@ import { Component } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; -import { NotifyLogService } from '@vality/ng-core'; -import { EMPTY } from 'rxjs'; -import { catchError, filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators'; +import { Link } from '@vality/ng-core'; -import { AppAuthGuardService } from '@cc/app/shared/services'; +import { AppAuthGuardService, Services } from '@cc/app/shared/services'; -import { PartyManagementService } from '../../api/payment-processing'; -import { ROUTING_CONFIG as SHOPS_ROUTING_CONFIG } from '../party-shops/routing-config'; +import { SidenavInfoService } from '../../shared/components/sidenav-info'; +import { ROUTING_CONFIG as CLAIMS_CONFIG } from '../claims/routing-config'; import { ROUTING_CONFIG as RULESET_ROUTING_CONFIG } from '../routing-rules/party-routing-ruleset/routing-config'; +import { SHOPS_ROUTING_CONFIG } from '../shops'; +import { ROUTING_CONFIG as WALLETS_ROUTING_CONFIG } from '../wallets/routing-config'; + +import { PartyStoreService } from './party-store.service'; + +interface PartyLink extends Link { + services?: Services[]; +} @Component({ templateUrl: 'party.component.html', - styleUrls: ['party.component.scss'], + providers: [PartyStoreService], }) export class PartyComponent { - links = this.getLinks(); - activeLinkByFragment$ = this.router.events.pipe( - filter((e) => e instanceof NavigationEnd), - startWith(undefined), - map(() => this.findLinkWithMaxActiveFragments()), - shareReplay(1), - ); - party$ = this.route.params.pipe( - startWith(this.route.snapshot.params), - switchMap(({ partyID }) => this.partyManagementService.Get(partyID)), - catchError((err) => { - this.log.error(err); - return EMPTY; - }), - startWith({ id: this.route.snapshot.params.partyID }), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + links: PartyLink[] = [ + { + label: 'Shops', + url: 'shops', + services: SHOPS_ROUTING_CONFIG.services, + }, + { + label: 'Wallets', + url: 'wallets', + services: WALLETS_ROUTING_CONFIG.services, + }, + { + label: 'Claims', + url: 'claims', + services: CLAIMS_CONFIG.services, + }, + { + label: 'Payment Routing Rules', + url: 'routing-rules/payment', + services: RULESET_ROUTING_CONFIG.services, + }, + { + label: 'Withdrawal Routing Rules', + url: 'routing-rules/withdrawal', + services: RULESET_ROUTING_CONFIG.services, + }, + ].filter((item) => this.appAuthGuardService.userHasSomeServiceMethods(item.services)); + party$ = this.partyStoreService.party$; constructor( - private route: ActivatedRoute, - private router: Router, private appAuthGuardService: AppAuthGuardService, - private partyManagementService: PartyManagementService, - private log: NotifyLogService, + protected sidenavInfoService: SidenavInfoService, + private partyStoreService: PartyStoreService, ) {} - - private getLinks() { - const links = [ - { - name: 'Shops', - url: 'shops', - otherActiveUrlFragments: ['shop'], - services: SHOPS_ROUTING_CONFIG.services, - }, - { - name: 'Payment Routing Rules', - url: 'routing-rules/payment', - services: RULESET_ROUTING_CONFIG.services, - }, - { - name: 'Withdrawal Routing Rules', - url: 'routing-rules/withdrawal', - services: RULESET_ROUTING_CONFIG.services, - }, - ]; - return links.filter((item) => - this.appAuthGuardService.userHasSomeServiceMethods(item.services), - ); - } - - private activeFragments(fragments: string[]): number { - if (fragments?.length) { - const ulrFragments = this.router.url.split('/'); - if ( - ulrFragments.filter((fragment) => fragments.includes(fragment)).length === - fragments.length - ) { - return fragments.length; - } - } - return 0; - } - - private findLinkWithMaxActiveFragments() { - return this.links.reduce(([maxLink, maxActiveFragments], link) => { - const activeFragments = this.activeFragments(link.otherActiveUrlFragments); - return maxActiveFragments > activeFragments - ? [maxLink, maxActiveFragments] - : [link, activeFragments]; - }, [])?.[0]; - } } diff --git a/src/app/sections/party/party.module.ts b/src/app/sections/party/party.module.ts index 10c5e477..d3056984 100644 --- a/src/app/sections/party/party.module.ts +++ b/src/app/sections/party/party.module.ts @@ -1,15 +1,29 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatSidenavModule } from '@angular/material/sidenav'; import { MatTabsModule } from '@angular/material/tabs'; +import { MatToolbar } from '@angular/material/toolbar'; +import { NavComponent, TagModule } from '@vality/ng-core'; -import { PageLayoutModule } from '../../shared'; +import { PageLayoutModule, ThriftPipesModule } from '../../shared'; import { PartyRouting } from './party-routing.module'; import { PartyComponent } from './party.component'; @NgModule({ - imports: [PartyRouting, CommonModule, MatTabsModule, MatButtonModule, PageLayoutModule], + imports: [ + PartyRouting, + CommonModule, + MatTabsModule, + MatButtonModule, + PageLayoutModule, + NavComponent, + MatSidenavModule, + MatToolbar, + TagModule, + ThriftPipesModule, + ], declarations: [PartyComponent], }) export class PartyModule {} diff --git a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts index 79657ef0..7278f9ba 100644 --- a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts +++ b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts @@ -32,6 +32,11 @@ import { ROUTING_CONFIG } from './routing-config'; }, ], }, + { + path: '', + redirectTo: 'payment', + pathMatch: 'prefix', + }, ]), ], }) diff --git a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.html b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.html index c4b37ff1..9949a81f 100644 --- a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.html +++ b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.html @@ -1,13 +1,14 @@ -
-
-
Party delegate rulesets
- -
- + + + + -
+ diff --git a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts index f8ea4893..d09fb15b 100644 --- a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts +++ b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, DestroyRef } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; -import { DialogService } from '@vality/ng-core'; +import { DialogService, NotifyLogService } from '@vality/ng-core'; import { first, map } from 'rxjs/operators'; import { DomainStoreService } from '@cc/app/api/domain-config'; import { RoutingRulesType } from '@cc/app/sections/routing-rules/types/routing-rules-type'; -import { NotificationErrorService } from '@cc/app/shared/services/notification-error'; import { handleError } from '../../../../utils/operators/handle-error'; +import { RoutingRulesTypeService } from '../routing-rules-type.service'; import { RoutingRulesService } from '../services/routing-rules'; import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog'; @@ -18,7 +18,7 @@ import { PartyDelegateRulesetsService } from './party-delegate-rulesets.service' selector: 'cc-party-delegate-rulesets', templateUrl: 'party-delegate-rulesets.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - providers: [PartyDelegateRulesetsService], + providers: [PartyDelegateRulesetsService, RoutingRulesTypeService], }) export class PartyDelegateRulesetsComponent { displayedColumns = [ @@ -60,9 +60,10 @@ export class PartyDelegateRulesetsComponent { private router: Router, private dialogService: DialogService, private domainStoreService: DomainStoreService, - private notificationErrorService: NotificationErrorService, + private log: NotifyLogService, private route: ActivatedRoute, private destroyRef: DestroyRef, + protected routingRulesTypeService: RoutingRulesTypeService, ) {} attachNewRuleset() { @@ -72,10 +73,7 @@ export class PartyDelegateRulesetsComponent { type: this.route.snapshot.params.type, }) .afterClosed() - .pipe( - handleError(this.notificationErrorService.error), - takeUntilDestroyed(this.destroyRef), - ) + .pipe(handleError(this.log.error), takeUntilDestroyed(this.destroyRef)) .subscribe(); } diff --git a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts index 07ca3053..d2f79132 100644 --- a/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts +++ b/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts @@ -17,9 +17,9 @@ import { DialogModule } from '@vality/ng-core'; import { DetailsItemModule } from '@cc/components/details-item'; +import { PageLayoutModule } from '../../../shared'; import { ChangeTargetDialogModule } from '../change-target-dialog'; import { RoutingRulesListModule } from '../routing-rules-list'; -import { RoutingRulesetHeaderModule } from '../routing-ruleset-header'; import { TargetRulesetFormModule } from '../target-ruleset-form'; import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog'; @@ -31,7 +31,6 @@ const EXPORTED_DECLARATIONS = [PartyDelegateRulesetsComponent, AttachNewRulesetD @NgModule({ imports: [ PartyDelegateRulesetsRoutingModule, - RoutingRulesetHeaderModule, MatButtonModule, CommonModule, RouterModule, @@ -51,6 +50,7 @@ const EXPORTED_DECLARATIONS = [PartyDelegateRulesetsComponent, AttachNewRulesetD TargetRulesetFormModule, RoutingRulesListModule, DialogModule, + PageLayoutModule, ], declarations: EXPORTED_DECLARATIONS, exports: EXPORTED_DECLARATIONS, diff --git a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html index 6498fddf..d2f29f81 100644 --- a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html +++ b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html @@ -1,39 +1,30 @@ - - - - -
- - Party routing rules - - - - - -
- - -
-
Routing rules not found
- -
-
-
+ + + + + + + diff --git a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.scss b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.scss index a6291315..f36f1f56 100644 --- a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.scss +++ b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.scss @@ -1,9 +1,3 @@ -.content { - display: flex; - flex-direction: column; - gap: 24px; -} - .empty-party-delegate { display: flex; flex-direction: column; diff --git a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.ts b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.ts index 28563fda..e9650696 100644 --- a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.ts +++ b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.ts @@ -2,12 +2,14 @@ import { Component, DestroyRef } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { DialogService, DialogResponseStatus } from '@vality/ng-core'; -import { combineLatest, Observable } from 'rxjs'; -import { filter, map, pluck, shareReplay, startWith, switchMap, take } from 'rxjs/operators'; +import { combineLatest } from 'rxjs'; +import { filter, map, shareReplay, startWith, switchMap, take } from 'rxjs/operators'; import { DomainStoreService } from '@cc/app/api/domain-config'; -import { RoutingRulesType } from '../types/routing-rules-type'; +import { SidenavInfoService } from '../../../shared/components/sidenav-info'; +import { DomainObjectCardComponent } from '../../../shared/components/thrift-api-crud'; +import { RoutingRulesTypeService } from '../routing-rules-type.service'; import { AddPartyRoutingRuleDialogComponent } from './add-party-routing-rule-dialog'; import { InitializeRoutingRulesDialogComponent } from './initialize-routing-rules-dialog'; @@ -17,15 +19,11 @@ import { PartyRoutingRulesetService } from './party-routing-ruleset.service'; selector: 'cc-party-routing-ruleset', templateUrl: 'party-routing-ruleset.component.html', styleUrls: ['party-routing-ruleset.component.scss'], - providers: [PartyRoutingRulesetService], + providers: [PartyRoutingRulesetService, RoutingRulesTypeService], }) export class PartyRoutingRulesetComponent { partyRuleset$ = this.partyRoutingRulesetService.partyRuleset$; partyID$ = this.partyRoutingRulesetService.partyID$; - routingRulesType$ = this.route.params.pipe( - startWith(this.route.snapshot.params), - pluck('type'), - ) as Observable; isLoading$ = this.domainStoreService.isLoading$; shopsDisplayedColumns = [ @@ -60,6 +58,7 @@ export class PartyRoutingRulesetComponent { }; }), ), + startWith([]), takeUntilDestroyed(this.destroyRef), shareReplay(1), ); @@ -87,6 +86,7 @@ export class PartyRoutingRulesetComponent { }; }), ), + startWith([]), takeUntilDestroyed(this.destroyRef), shareReplay(1), ); @@ -98,9 +98,45 @@ export class PartyRoutingRulesetComponent { private route: ActivatedRoute, private domainStoreService: DomainStoreService, private destroyRef: DestroyRef, + private sidenavInfoService: SidenavInfoService, + protected routingRulesTypeService: RoutingRulesTypeService, ) {} - initialize() { + add() { + this.partyRuleset$.pipe(take(1)).subscribe((partyRuleset) => { + if (partyRuleset) { + this.addPartyRule(); + } else { + this.initialize(); + } + }); + } + + navigateToDelegate(parentRefId: number, delegateIdx: number) { + this.partyRoutingRulesetService.partyRuleset$ + .pipe(take(1), takeUntilDestroyed(this.destroyRef)) + .subscribe((ruleset) => + this.router.navigate([ + 'party', + this.route.snapshot.params.partyID, + 'routing-rules', + this.route.snapshot.params.type, + parentRefId, + 'delegate', + ruleset?.data?.decisions?.delegates?.[delegateIdx]?.ruleset?.id, + ]), + ); + } + + openRefId() { + this.partyRuleset$.pipe(take(1), filter(Boolean)).subscribe(({ ref }) => { + this.sidenavInfoService.toggle(DomainObjectCardComponent, { + ref: { routing_rules: { id: Number(ref.id) } }, + }); + }); + } + + private initialize() { combineLatest([ this.partyRoutingRulesetService.partyID$, this.partyRoutingRulesetService.refID$, @@ -121,12 +157,12 @@ export class PartyRoutingRulesetComponent { }); } - addPartyRule() { + private addPartyRule() { combineLatest([ this.partyRoutingRulesetService.refID$, this.partyRoutingRulesetService.shops$, this.partyRoutingRulesetService.wallets$, - this.routingRulesType$, + this.routingRulesTypeService.routingRulesType$, this.partyRoutingRulesetService.partyID$, ]) .pipe( @@ -152,20 +188,4 @@ export class PartyRoutingRulesetComponent { }, }); } - - navigateToDelegate(parentRefId: number, delegateIdx: number) { - this.partyRoutingRulesetService.partyRuleset$ - .pipe(take(1), takeUntilDestroyed(this.destroyRef)) - .subscribe((ruleset) => - this.router.navigate([ - 'party', - this.route.snapshot.params.partyID, - 'routing-rules', - this.route.snapshot.params.type, - parentRefId, - 'delegate', - ruleset?.data?.decisions?.delegates?.[delegateIdx]?.ruleset?.id, - ]), - ); - } } diff --git a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts index 70151a21..7eddfa90 100644 --- a/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts +++ b/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts @@ -16,9 +16,9 @@ import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { RouterModule } from '@angular/router'; +import { PageLayoutModule } from '../../../shared'; import { ChangeTargetDialogModule } from '../change-target-dialog'; import { RoutingRulesListModule } from '../routing-rules-list'; -import { RoutingRulesetHeaderModule } from '../routing-ruleset-header'; import { AddPartyRoutingRuleDialogModule } from './add-party-routing-rule-dialog'; import { InitializeRoutingRulesDialogModule } from './initialize-routing-rules-dialog'; @@ -43,12 +43,12 @@ import { PartyRoutingRulesetComponent } from './party-routing-ruleset.component' MatSelectModule, MatRadioModule, MatExpansionModule, - RoutingRulesetHeaderModule, AddPartyRoutingRuleDialogModule, InitializeRoutingRulesDialogModule, MatProgressBarModule, ChangeTargetDialogModule, RoutingRulesListModule, + PageLayoutModule, ], declarations: [PartyRoutingRulesetComponent], }) diff --git a/src/app/sections/routing-rules/routing-rules-type.service.ts b/src/app/sections/routing-rules/routing-rules-type.service.ts new file mode 100644 index 00000000..d12876fc --- /dev/null +++ b/src/app/sections/routing-rules/routing-rules-type.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { startWith, map } from 'rxjs/operators'; + +import { getEnumValues } from '../../../utils'; + +import { RoutingRulesType } from './types/routing-rules-type'; + +@Injectable() +export class RoutingRulesTypeService { + routingRulesType$ = this.route.params.pipe( + startWith(this.route.snapshot.params), + map((p) => (getEnumValues(RoutingRulesType).includes(p.type) ? p.type : null)), + ) as Observable; + + constructor(private route: ActivatedRoute) {} +} diff --git a/src/app/sections/routing-rules/routing-ruleset-header/index.ts b/src/app/sections/routing-rules/routing-ruleset-header/index.ts deleted file mode 100644 index 55d6c9e1..00000000 --- a/src/app/sections/routing-rules/routing-ruleset-header/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './routing-ruleset-header.module'; diff --git a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.html b/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.html deleted file mode 100644 index efe24b72..00000000 --- a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
-
-
- arrow_back -
-
- -
-
- {{ description }} -
-
- - Ruleset ref ID: {{ refID }} -
-
diff --git a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.scss b/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.scss deleted file mode 100644 index 53b06471..00000000 --- a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -.title { - margin: 0; -} - -.back { - cursor: pointer; -} diff --git a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.ts b/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.ts deleted file mode 100644 index ec7e2844..00000000 --- a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Router } from '@angular/router'; - -import { SidenavInfoService } from '../../../shared/components/sidenav-info'; -import { DomainObjectCardComponent } from '../../../shared/components/thrift-api-crud'; - -@Component({ - selector: 'cc-routing-ruleset-header', - templateUrl: 'routing-ruleset-header.component.html', - styleUrls: ['routing-ruleset-header.component.scss'], -}) -export class RoutingRulesetHeaderComponent { - @Input() refID: string; - @Input() description?: string; - @Input() backTo?: string; - - @Output() add = new EventEmitter(); - - get queryParams() { - return { - types: JSON.stringify(['RoutingRulesObject']), - sidenav: JSON.stringify({ - id: 'domainObject', - inputs: { ref: { routing_rules: { id: this.refID } } }, - }), - }; - } - - constructor( - private router: Router, - private sidenavInfoService: SidenavInfoService, - ) {} - - navigateBack() { - void this.router.navigate([this.backTo]); - } - - openRule() { - this.sidenavInfoService.toggle(DomainObjectCardComponent, { - ref: { routing_rules: { id: Number(this.refID) } }, - }); - } -} diff --git a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.module.ts b/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.module.ts deleted file mode 100644 index 634223f0..00000000 --- a/src/app/sections/routing-rules/routing-ruleset-header/routing-ruleset-header.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; -import { RouterLink } from '@angular/router'; - -import { RoutingRulesetHeaderComponent } from './routing-ruleset-header.component'; - -@NgModule({ - imports: [CommonModule, MatIconModule, MatButtonModule, RouterLink], - declarations: [RoutingRulesetHeaderComponent], - exports: [RoutingRulesetHeaderComponent], -}) -export class RoutingRulesetHeaderModule {} diff --git a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html index 2d844579..4b61c701 100644 --- a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html +++ b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html @@ -1,20 +1,19 @@ -
- - Routing rules - - + ]" + (idLinkClick)="openRefId()" +> + + + -
+ diff --git a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts index 59b312ee..49d45e32 100644 --- a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts +++ b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts @@ -13,11 +13,14 @@ import { } from '@vality/ng-core'; import cloneDeep from 'lodash-es/cloneDeep'; import { Observable, combineLatest, filter } from 'rxjs'; -import { first, map, switchMap, withLatestFrom } from 'rxjs/operators'; +import { first, map, switchMap, withLatestFrom, take } from 'rxjs/operators'; import { DomainStoreService } from '@cc/app/api/domain-config'; import { RoutingRulesType } from '@cc/app/sections/routing-rules/types/routing-rules-type'; -import { DomainThriftFormDialogComponent } from '@cc/app/shared/components/thrift-api-crud'; +import { + DomainThriftFormDialogComponent, + DomainObjectCardComponent, +} from '@cc/app/shared/components/thrift-api-crud'; import { objectToJSON } from '../../../../utils'; import { createPredicateColumn } from '../../../shared'; @@ -285,4 +288,12 @@ export class RoutingRulesetComponent { }, }); } + + openRefId() { + this.shopRuleset$.pipe(take(1), filter(Boolean)).subscribe(({ ref }) => { + this.sidenavInfoService.toggle(DomainObjectCardComponent, { + ref: { routing_rules: { id: Number(ref.id) } }, + }); + }); + } } diff --git a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.module.ts b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.module.ts index 33074124..5302f795 100644 --- a/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.module.ts +++ b/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.module.ts @@ -19,8 +19,8 @@ import { TableModule, DialogModule } from '@vality/ng-core'; import { DomainThriftViewerComponent } from '@cc/app/shared/components/thrift-api-crud'; +import { PageLayoutModule } from '../../../shared'; import { ThriftViewerModule } from '../../../shared/components/thrift-viewer'; -import { RoutingRulesetHeaderModule } from '../routing-ruleset-header'; import { ChangeCandidatesPrioritiesDialogComponent } from './components/change-candidates-priorities-dialog/change-candidates-priorities-dialog.component'; import { RoutingRulesetRoutingModule } from './routing-ruleset-routing.module'; @@ -44,12 +44,12 @@ import { RoutingRulesetComponent } from './routing-ruleset.component'; MatSelectModule, MatRadioModule, MatExpansionModule, - RoutingRulesetHeaderModule, MatAutocompleteModule, TableModule, DomainThriftViewerComponent, ThriftViewerModule, DialogModule, + PageLayoutModule, ], declarations: [RoutingRulesetComponent, ChangeCandidatesPrioritiesDialogComponent], }) diff --git a/src/app/sections/search-parties/search-parties-routing.module.ts b/src/app/sections/search-parties/search-parties-routing.module.ts index fc37bbd9..5275d6b7 100644 --- a/src/app/sections/search-parties/search-parties-routing.module.ts +++ b/src/app/sections/search-parties/search-parties-routing.module.ts @@ -15,6 +15,10 @@ import { SearchPartiesComponent } from './search-parties.component'; canActivate: [AppAuthGuardService], data: ROUTING_CONFIG, }, + { + path: 'party', + redirectTo: 'parties', + }, ]), ], exports: [RouterModule], diff --git a/src/app/sections/sections-routing.module.ts b/src/app/sections/sections-routing.module.ts index 5e7db503..20d685b7 100644 --- a/src/app/sections/sections-routing.module.ts +++ b/src/app/sections/sections-routing.module.ts @@ -11,8 +11,8 @@ const ROUTES: Routes = [ loadChildren: () => import('./party/party.module').then((m) => m.PartyModule), }, { - path: 'party', - loadChildren: () => import('./party/party.module').then((m) => m.PartyModule), + path: 'claims', + loadChildren: () => import('./claims').then((m) => m.ClaimsModule), }, { path: 'party/:partyID', diff --git a/src/app/sections/wallets/wallets.component.html b/src/app/sections/wallets/wallets.component.html index 75312faa..e666de95 100644 --- a/src/app/sections/wallets/wallets.component.html +++ b/src/app/sections/wallets/wallets.component.html @@ -12,19 +12,22 @@ - + + Identity ID - [] = [ - { field: 'id' }, - { field: 'name' }, - 'currency_symbolic_code', - 'identity_id', - { field: 'created_at', type: 'datetime' }, - createCurrencyColumn( - '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$ = this.partyStoreService.party$.pipe( + map((party) => + runInInjectionContext(this.injector, () => [ + { field: 'id' }, + { field: 'name' }, + 'currency_symbolic_code', + 'identity_id', + { field: 'created_at', type: 'datetime' }, + createCurrencyColumn( + '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 }, + ), + createCurrencyColumn( + '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( + '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( + 'party', + (d) => + this.getIdentity(d.identity_id).pipe( + map((identity) => identity.party_id), + ), + undefined, + { lazy: true }, + ), + ]), + ]), ), - createCurrencyColumn( - '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( - '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, - }, - createPartyColumn( - 'party', - (d) => this.getIdentity(d.identity_id).pipe(map((identity) => identity.party_id)), - undefined, - { lazy: true }, - ), - ]; + ); fullTextSearchColumns: Column[] = [ { field: 'wallet.id' }, { field: 'wallet.name' }, @@ -118,6 +140,7 @@ export class WalletsComponent implements OnInit { active = 0; @ViewChild(FiltersComponent) filters!: FiltersComponent; typeQp = this.qp.createNamespace<{ isFilter: boolean }>('type'); + party$ = this.partyStoreService.party$; constructor( private fetchWalletsService: FetchWalletsService, @@ -129,6 +152,8 @@ export class WalletsComponent implements OnInit { @Inject(DEBOUNCE_TIME_MS) private debounceTimeMs: number, private destroyRef: DestroyRef, private identityManagementService: IdentityManagementService, + private partyStoreService: PartyStoreService, + private injector: Injector, ) {} ngOnInit() { @@ -152,8 +177,16 @@ export class WalletsComponent implements OnInit { filterSearch(opts?: UpdateOptions) { const props = clean(this.filtersForm.value); - this.fetchWalletsService.load(props, opts); this.active = countProps(props); + this.partyStoreService.party$.pipe(take(1)).subscribe((p) => { + this.fetchWalletsService.load( + clean({ + party_id: p ? p.id : undefined, + ...props, + }), + opts, + ); + }); } filterMore() { diff --git a/src/app/shared/components/merchant-field/merchant-field.component.html b/src/app/shared/components/merchant-field/merchant-field.component.html index b31057d0..dc6556a5 100644 --- a/src/app/shared/components/merchant-field/merchant-field.component.html +++ b/src/app/shared/components/merchant-field/merchant-field.component.html @@ -1,9 +1,12 @@ diff --git a/src/app/shared/components/merchant-field/merchant-field.component.ts b/src/app/shared/components/merchant-field/merchant-field.component.ts index 91accdec..de565448 100644 --- a/src/app/shared/components/merchant-field/merchant-field.component.ts +++ b/src/app/shared/components/merchant-field/merchant-field.component.ts @@ -6,9 +6,17 @@ import { NotifyLogService, FormControlSuperclass, createControlProviders, + getValueChanges, } from '@vality/ng-core'; -import { BehaviorSubject, Observable, of, ReplaySubject, Subject } from 'rxjs'; -import { catchError, debounceTime, map, switchMap, tap, startWith } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of, ReplaySubject, Subject, merge } from 'rxjs'; +import { + catchError, + debounceTime, + map, + switchMap, + tap, + distinctUntilChanged, +} from 'rxjs/operators'; import { DeanonimusService } from '@cc/app/api/deanonimus'; @@ -23,6 +31,9 @@ export class MerchantFieldComponent { @Input() label: string; @Input({ transform: booleanAttribute }) required: boolean; + @Input() size?: string; + @Input() appearance?: string; + @Input() hint?: string; options$ = new ReplaySubject[]>(1); searchChange$ = new Subject(); @@ -37,9 +48,9 @@ export class MerchantFieldComponent } ngAfterViewInit() { - this.searchChange$ + merge(getValueChanges(this.control), this.searchChange$) .pipe( - startWith(this.control.value), + distinctUntilChanged(), tap(() => { this.options$.next([]); this.progress$.next(true); diff --git a/src/app/shared/components/page-layout/page-layout.component.html b/src/app/shared/components/page-layout/page-layout.component.html index fb02936d..68832ee2 100644 --- a/src/app/shared/components/page-layout/page-layout.component.html +++ b/src/app/shared/components/page-layout/page-layout.component.html @@ -1,42 +1,60 @@ -
-

- -
- {{ title }} - {{ description }} +
+
+
+ + {{ p.label }} + / +
-

-
- +
+

+ +
+ {{ title }} + {{ description }} + + + {{ id ? '#' + id : '' }} + + +
+

+
+ +
+
+
+ +
+
-
- -
- {{ pathPart.label }} - {{ pathPart.label }} -
-
/
-
-
- -
diff --git a/src/app/shared/components/page-layout/page-layout.component.scss b/src/app/shared/components/page-layout/page-layout.component.scss index bdbbb580..6ac9074e 100644 --- a/src/app/shared/components/page-layout/page-layout.component.scss +++ b/src/app/shared/components/page-layout/page-layout.component.scss @@ -1,7 +1,10 @@ -:host { +.wrapper { + &__offset { + padding: 24px; + } + display: grid; gap: 24px; - padding: 24px; .header { display: flex; diff --git a/src/app/shared/components/page-layout/page-layout.component.ts b/src/app/shared/components/page-layout/page-layout.component.ts index 35da8f9e..85290bc1 100644 --- a/src/app/shared/components/page-layout/page-layout.component.ts +++ b/src/app/shared/components/page-layout/page-layout.component.ts @@ -1,6 +1,17 @@ import { Location } from '@angular/common'; -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Params } from '@angular/router'; +import { + ChangeDetectionStrategy, + Component, + Input, + booleanAttribute, + input, + computed, + Output, + EventEmitter, +} from '@angular/core'; +import { Router } from '@angular/router'; +import { UrlService } from '@vality/ng-core'; +import { map } from 'rxjs/operators'; @Component({ selector: 'cc-page-layout', @@ -11,21 +22,49 @@ import { Params } from '@angular/router'; export class PageLayoutComponent { @Input() title!: string; @Input() description?: string; + @Input() id?: string; @Input() progress?: boolean; - @Input() path?: { - label: string; - link?: unknown[] | string | null | undefined; - queryParams?: Params | null; - tooltip?: string; - }[]; + @Input({ transform: booleanAttribute }) noOffset = false; - // 1 and 2 is default history length - isBackAvailable = - window.history.length > 2 && window.location.pathname.split('/').slice(1).length > 1; + @Output() idLinkClick = new EventEmitter(); - constructor(private location: Location) {} + backLink = input(); + upLink = input(); + idLink = input(); + + isBackAvailable = computed( + () => + this.backLink() || + this.upLink() || + // 1 and 2 is default history length + (window.history.length > 2 && window.location.pathname.split('/').slice(1).length > 1), + ); + + path$ = this.urlService.path$.pipe( + map((path) => { + return path + .reduce( + (acc, p) => { + acc.push({ url: [...(acc.at(-1)?.url || ['']), p], label: p }); + return acc; + }, + [] as { url: string[]; label: string }[], + ) + .map((v) => ({ ...v, url: v.url.join('/') })); + }), + ); + + constructor( + private location: Location, + private router: Router, + private urlService: UrlService, + ) {} back() { - this.location.back(); + if (this.backLink() || this.upLink()) { + void this.router.navigate(this.backLink() || this.upLink()); + } else { + this.location.back(); + } } } diff --git a/src/app/shared/components/sidenav-info/sidenav-info.service.ts b/src/app/shared/components/sidenav-info/sidenav-info.service.ts index e12da74e..f2c65c5c 100644 --- a/src/app/shared/components/sidenav-info/sidenav-info.service.ts +++ b/src/app/shared/components/sidenav-info/sidenav-info.service.ts @@ -1,14 +1,14 @@ import { Injectable, Type, Inject, Optional } from '@angular/core'; -import { Router } from '@angular/router'; import { QueryParamsService, QueryParamsNamespace, getPossiblyAsyncObservable, PossiblyAsync, + UrlService, } from '@vality/ng-core'; import isEqual from 'lodash-es/isEqual'; import { BehaviorSubject } from 'rxjs'; -import { filter, startWith, map, distinctUntilChanged } from 'rxjs/operators'; +import { filter, map } from 'rxjs/operators'; import { SIDENAV_INFO_COMPONENTS, SidenavInfoComponents } from './tokens'; @@ -24,7 +24,7 @@ export class SidenavInfoService { private qp!: QueryParamsNamespace<{ id?: string; inputs?: Record }>; constructor( - router: Router, + urlService: UrlService, private qps: QueryParamsService, @Optional() @Inject(SIDENAV_INFO_COMPONENTS) @@ -33,17 +33,9 @@ export class SidenavInfoService { if (!this.sidenavInfoComponents) { this.sidenavInfoComponents = sidenavInfoComponents ?? {}; } - router.events - .pipe( - startWith(null), - filter(() => router.navigated), - map(() => router.url?.split('?', 1)[0].split('#', 1)[0]), - distinctUntilChanged(), - filter(() => !!this.component$.value), - ) - .subscribe(() => { - this.close(); - }); + urlService.url$.pipe(filter(() => !!this.component$.value)).subscribe(() => { + this.close(); + }); this.qp = this.qps.createNamespace('sidenav'); this.qp.params$ .pipe(