Browse Source

Migrate market data management to dedicated routes

pull/7823/head
Thomas Kaul 3 days ago
parent
commit
681dd2f8c4
  1. 141
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  2. 21
      apps/client/src/app/components/admin-market-data/admin-market-data.html
  3. 10
      apps/client/src/app/components/admin-market-data/admin-market-data.service.ts
  4. 217
      apps/client/src/app/components/admin-market-data/asset-profile-dialog-host/asset-profile-dialog-host.component.ts
  5. 1
      apps/client/src/app/components/admin-market-data/asset-profile-dialog-host/types/asset-profile-dialog-mode.type.ts
  6. 5
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  7. 12
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  8. 25
      apps/client/src/app/pages/admin/admin-page.routes.ts
  9. 14
      libs/common/src/lib/routes/routes.ts
  10. 11
      libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts

141
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -1,9 +1,5 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
DEFAULT_COLOR_SCHEME,
DEFAULT_LOCALE,
DEFAULT_PAGE_SIZE
} from '@ghostfolio/common/config';
import { canDeleteAssetProfile } from '@ghostfolio/common/helper'; import { canDeleteAssetProfile } from '@ghostfolio/common/helper';
import { import {
AssetProfileIdentifier, AssetProfileIdentifier,
@ -13,6 +9,7 @@ import {
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter'; import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter';
import { GfFabComponent } from '@ghostfolio/ui/fab'; import { GfFabComponent } from '@ghostfolio/ui/fab';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -27,7 +24,6 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
computed,
DestroyRef, DestroyRef,
inject, inject,
OnInit, OnInit,
@ -36,7 +32,6 @@ import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { import {
MatPaginator, MatPaginator,
@ -51,7 +46,7 @@ import {
SortDirection SortDirection
} from '@angular/material/sort'; } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { Router, RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { AssetSubClass, DataSource, SymbolProfile } from '@prisma/client'; import { AssetSubClass, DataSource, SymbolProfile } from '@prisma/client';
import { isUUID } from 'class-validator'; import { isUUID } from 'class-validator';
@ -66,16 +61,11 @@ import {
trashOutline trashOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import ms from 'ms'; import ms from 'ms';
import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators'; import { distinctUntilChanged } from 'rxjs/operators';
import { AdminMarketDataService } from './admin-market-data.service'; import { AdminMarketDataService } from './admin-market-data.service';
import { GfAssetProfileDialogComponent } from './asset-profile-dialog/asset-profile-dialog.component';
import { AssetProfileDialogParams } from './asset-profile-dialog/interfaces/interfaces';
import { GfCreateAssetProfileDialogComponent } from './create-asset-profile-dialog/create-asset-profile-dialog.component';
import { CreateAssetProfileDialogParams } from './create-asset-profile-dialog/interfaces/interfaces';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -153,6 +143,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
protected dataSource = new MatTableDataSource<AssetProfileItem>(); protected dataSource = new MatTableDataSource<AssetProfileItem>();
protected readonly displayedColumns: string[] = []; protected readonly displayedColumns: string[] = [];
protected readonly filters$ = new Subject<Filter[]>(); protected readonly filters$ = new Subject<Filter[]>();
protected readonly internalRoutes = internalRoutes;
protected isLoading = true; protected isLoading = true;
protected readonly isUUID = isUUID; protected readonly isUUID = isUUID;
protected pageSize = DEFAULT_PAGE_SIZE; protected pageSize = DEFAULT_PAGE_SIZE;
@ -164,9 +155,6 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
private activeFilters: Filter[] = []; private activeFilters: Filter[] = [];
private benchmarks: Partial<SymbolProfile>[]; private benchmarks: Partial<SymbolProfile>[];
private readonly deviceType = computed(
() => this.deviceDetectorService.deviceInfo().deviceType
);
private readonly hasPermissionForSubscription: boolean; private readonly hasPermissionForSubscription: boolean;
private readonly info: InfoItem; private readonly info: InfoItem;
private readonly paginator = viewChild.required(MatPaginator); private readonly paginator = viewChild.required(MatPaginator);
@ -176,9 +164,6 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
private readonly changeDetectorRef = inject(ChangeDetectorRef); private readonly changeDetectorRef = inject(ChangeDetectorRef);
private readonly dataService = inject(DataService); private readonly dataService = inject(DataService);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly deviceDetectorService = inject(DeviceDetectorService);
private readonly dialog = inject(MatDialog);
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly snackBar = inject(MatSnackBar); private readonly snackBar = inject(MatSnackBar);
private readonly userService = inject(UserService); private readonly userService = inject(UserService);
@ -213,21 +198,10 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
this.displayedColumns.push('comment'); this.displayedColumns.push('comment');
this.displayedColumns.push('actions'); this.displayedColumns.push('actions');
this.route.queryParams this.adminMarketDataService.refresh$
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((params) => { .subscribe(() => {
if ( this.reloadData();
params['assetProfileDialog'] &&
params['dataSource'] &&
params['symbol']
) {
this.openAssetProfileDialog({
dataSource: params['dataSource'],
symbol: params['symbol']
});
} else if (params['createAssetProfileDialog']) {
this.openCreateAssetProfileDialog();
}
}); });
this.userService.stateChanged this.userService.stateChanged
@ -341,13 +315,12 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
dataSource, dataSource,
symbol symbol
}: AssetProfileIdentifier) { }: AssetProfileIdentifier) {
this.router.navigate([], { void this.router.navigate(
queryParams: { internalRoutes.adminControl.subRoutes.marketData.subRoutes.update.routerLink(
dataSource, dataSource,
symbol, symbol
assetProfileDialog: true )
} );
});
} }
private loadData( private loadData(
@ -418,96 +391,6 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
); );
} }
private openAssetProfileDialog({
dataSource,
symbol
}: AssetProfileIdentifier) {
this.userService
.get()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
const dialogRef = this.dialog.open<
GfAssetProfileDialogComponent,
AssetProfileDialogParams,
AssetProfileIdentifier
>(GfAssetProfileDialogComponent, {
autoFocus: false,
data: {
dataSource,
symbol,
colorScheme:
this.user?.settings.colorScheme ?? DEFAULT_COLOR_SCHEME,
deviceType: this.deviceType(),
locale: this.user?.settings?.locale ?? DEFAULT_LOCALE
} satisfies AssetProfileDialogParams,
height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
.afterClosed()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((newAssetProfileIdentifier) => {
this.reloadData();
if (newAssetProfileIdentifier) {
this.onOpenAssetProfileDialog(newAssetProfileIdentifier);
} else {
this.router.navigate(['.'], { relativeTo: this.route });
}
});
});
}
private openCreateAssetProfileDialog() {
this.userService
.get()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
const dialogRef = this.dialog.open<
GfCreateAssetProfileDialogComponent,
CreateAssetProfileDialogParams
>(GfCreateAssetProfileDialogComponent, {
autoFocus: false,
data: {
deviceType: this.deviceType(),
locale: this.user?.settings?.locale ?? DEFAULT_LOCALE
} satisfies CreateAssetProfileDialogParams,
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
dialogRef
.afterClosed()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((result) => {
if (!result) {
this.router.navigate(['.'], { relativeTo: this.route });
return;
}
const { addAssetProfile, dataSource, symbol } = result;
if (addAssetProfile && dataSource && symbol) {
this.adminService
.addAssetProfile({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.loadData();
});
} else {
this.loadData();
}
this.onOpenAssetProfileDialog({ dataSource, symbol });
});
});
}
private reloadData({ private reloadData({
pageIndex = this.paginator().pageIndex pageIndex = this.paginator().pageIndex
}: { pageIndex?: number } = {}) { }: { pageIndex?: number } = {}) {

21
apps/client/src/app/components/admin-market-data/admin-market-data.html

@ -303,12 +303,12 @@
<mat-menu #assetProfileActionsMenu="matMenu" xPosition="before"> <mat-menu #assetProfileActionsMenu="matMenu" xPosition="before">
<a <a
mat-menu-item mat-menu-item
[queryParams]="{ [routerLink]="
assetProfileDialog: true, internalRoutes.adminControl.subRoutes.marketData.subRoutes.update.routerLink(
dataSource: element.dataSource, element.dataSource,
symbol: element.symbol element.symbol
}" )
[routerLink]="[]" "
> >
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
<ion-icon class="mr-2" name="create-outline" /> <ion-icon class="mr-2" name="create-outline" />
@ -381,5 +381,12 @@
</div> </div>
</div> </div>
<gf-fab [queryParams]="{ createAssetProfileDialog: true }" /> <gf-fab
[routerLink]="
internalRoutes.adminControl.subRoutes.marketData.subRoutes.create
.routerLink
"
/>
</div> </div>
<router-outlet />

10
apps/client/src/app/components/admin-market-data/admin-market-data.service.ts

@ -8,9 +8,15 @@ import { EMPTY, Subject, catchError, finalize, forkJoin } from 'rxjs';
@Service({ autoProvided: false }) @Service({ autoProvided: false })
export class AdminMarketDataService { export class AdminMarketDataService {
private readonly refreshSubject = new Subject<void>();
private readonly adminService = inject(AdminService); private readonly adminService = inject(AdminService);
private readonly notificationService = inject(NotificationService); private readonly notificationService = inject(NotificationService);
public get refresh$() {
return this.refreshSubject.asObservable();
}
public deleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { public deleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
const assetProfileDeleted = new Subject<void>(); const assetProfileDeleted = new Subject<void>();
@ -72,4 +78,8 @@ export class AdminMarketDataService {
return assetProfilesDeleted.asObservable(); return assetProfilesDeleted.asObservable();
} }
public triggerRefresh() {
this.refreshSubject.next();
}
} }

217
apps/client/src/app/components/admin-market-data/asset-profile-dialog-host/asset-profile-dialog-host.component.ts

@ -0,0 +1,217 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
DEFAULT_COLOR_SCHEME,
DEFAULT_LOCALE
} from '@ghostfolio/common/config';
import { AssetProfileIdentifier, User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { AdminService } from '@ghostfolio/ui/services';
import {
ChangeDetectionStrategy,
Component,
computed,
DestroyRef,
inject,
OnDestroy,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { DataSource } from '@prisma/client';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { distinctUntilChanged, map, takeUntil, tap } from 'rxjs/operators';
import { AdminMarketDataService } from '../admin-market-data.service';
import { GfAssetProfileDialogComponent } from '../asset-profile-dialog/asset-profile-dialog.component';
import { AssetProfileDialogParams } from '../asset-profile-dialog/interfaces/interfaces';
import { GfCreateAssetProfileDialogComponent } from '../create-asset-profile-dialog/create-asset-profile-dialog.component';
import { CreateAssetProfileDialogParams } from '../create-asset-profile-dialog/interfaces/interfaces';
import { AssetProfileDialogMode } from './types/asset-profile-dialog-mode.type';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'gf-asset-profile-dialog-host',
template: ''
})
export class GfAssetProfileDialogHostComponent implements OnDestroy, OnInit {
private dialogRef: MatDialogRef<
GfAssetProfileDialogComponent | GfCreateAssetProfileDialogComponent
>;
private readonly deviceType = computed(() => {
return this.deviceDetectorService.deviceInfo().deviceType;
});
private readonly dialogClosed = new Subject<void>();
private readonly adminMarketDataService = inject(AdminMarketDataService);
private readonly adminService = inject(AdminService);
private readonly destroyRef = inject(DestroyRef);
private readonly deviceDetectorService = inject(DeviceDetectorService);
private readonly dialog = inject(MatDialog);
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly userService = inject(UserService);
public ngOnInit() {
const mode = this.route.snapshot.data.mode as AssetProfileDialogMode;
// The router reuses this component when only the asset profile identifier
// changes, so the parameters are observed instead of read from the
// snapshot once
this.route.paramMap
.pipe(
map((paramMap) => {
return {
dataSource: paramMap.get('dataSource'),
symbol: paramMap.get('symbol')
};
}),
distinctUntilChanged((previous, current) => {
return (
previous.dataSource === current.dataSource &&
previous.symbol === current.symbol
);
}),
tap(() => {
this.closeDialog();
}),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(({ dataSource, symbol }) => {
this.userService
.get()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
if (mode === 'create') {
this.openCreateAssetProfileDialog({ user });
} else if (dataSource && symbol) {
this.openAssetProfileDialog({
dataSource: dataSource as DataSource,
symbol,
user
});
} else {
this.navigateBack();
}
});
});
}
public ngOnDestroy() {
// The dialog lives in an overlay outside of this component, so it needs to
// be closed explicitly when leaving the route (for example via the browser
// navigation)
this.dialogRef?.close();
this.dialogClosed.complete();
}
private closeDialog() {
// Tear down the subscription of the dialog which is about to be replaced,
// so that its result is not mistaken for the user closing it
this.dialogClosed.next();
this.dialogRef?.close();
}
private navigateBack() {
void this.router.navigate(
internalRoutes.adminControl.subRoutes.marketData.routerLink
);
}
private navigateToAssetProfileDialog({
dataSource,
symbol
}: AssetProfileIdentifier) {
void this.router.navigate(
internalRoutes.adminControl.subRoutes.marketData.subRoutes.update.routerLink(
dataSource,
symbol
)
);
}
private openAssetProfileDialog({
dataSource,
symbol,
user
}: AssetProfileIdentifier & { user: User }) {
const dialogRef = this.dialog.open<
GfAssetProfileDialogComponent,
AssetProfileDialogParams,
AssetProfileIdentifier
>(GfAssetProfileDialogComponent, {
autoFocus: false,
data: {
dataSource,
symbol,
colorScheme: user?.settings.colorScheme ?? DEFAULT_COLOR_SCHEME,
deviceType: this.deviceType(),
locale: user?.settings?.locale ?? DEFAULT_LOCALE
} satisfies AssetProfileDialogParams,
height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
this.dialogRef = dialogRef;
dialogRef
.afterClosed()
.pipe(takeUntil(this.dialogClosed), takeUntilDestroyed(this.destroyRef))
.subscribe((newAssetProfileIdentifier) => {
this.adminMarketDataService.triggerRefresh();
if (newAssetProfileIdentifier) {
this.navigateToAssetProfileDialog(newAssetProfileIdentifier);
} else {
this.navigateBack();
}
});
}
private openCreateAssetProfileDialog({ user }: { user: User }) {
const dialogRef = this.dialog.open<
GfCreateAssetProfileDialogComponent,
CreateAssetProfileDialogParams,
AssetProfileIdentifier & { addAssetProfile: boolean }
>(GfCreateAssetProfileDialogComponent, {
autoFocus: false,
data: {
deviceType: this.deviceType(),
locale: user?.settings?.locale ?? DEFAULT_LOCALE
} satisfies CreateAssetProfileDialogParams,
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
});
this.dialogRef = dialogRef;
dialogRef
.afterClosed()
.pipe(takeUntil(this.dialogClosed), takeUntilDestroyed(this.destroyRef))
.subscribe((result) => {
if (!result) {
this.navigateBack();
return;
}
const { addAssetProfile, dataSource, symbol } = result;
if (addAssetProfile && dataSource && symbol) {
this.adminService
.addAssetProfile({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.navigateToAssetProfileDialog({ dataSource, symbol });
});
} else {
this.navigateToAssetProfileDialog({ dataSource, symbol });
}
});
}
}

1
apps/client/src/app/components/admin-market-data/asset-profile-dialog-host/types/asset-profile-dialog-mode.type.ts

@ -0,0 +1 @@
export type AssetProfileDialogMode = 'create' | 'update';

5
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts

@ -182,8 +182,9 @@ export class GfHoldingDetailDialogComponent implements OnInit {
protected quantityPrecision = 2; protected quantityPrecision = 2;
protected reportDataGlitchMailHref: string; protected reportDataGlitchMailHref: string;
protected readonly round = round; protected readonly round = round;
protected readonly routerLinkAdminControlMarketData = protected readonly routerLinkAdminControlMarketDataUpdate =
internalRoutes.adminControl.subRoutes.marketData.routerLink; internalRoutes.adminControl.subRoutes.marketData.subRoutes.update
.routerLink;
protected sectors: { protected sectors: {
[name: string]: { name: string; value: number }; [name: string]: { name: string; value: number };
}; };

12
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html

@ -484,12 +484,12 @@
@if (data.hasPermissionToAccessAdminControl) { @if (data.hasPermissionToAccessAdminControl) {
<a <a
mat-stroked-button mat-stroked-button
[queryParams]="{ [routerLink]="
assetProfileDialog: true, routerLinkAdminControlMarketDataUpdate(
dataSource: assetProfile?.dataSource, assetProfile?.dataSource,
symbol: assetProfile?.symbol assetProfile?.symbol
}" )
[routerLink]="routerLinkAdminControlMarketData" "
(click)="onClose()" (click)="onClose()"
><ion-icon class="mr-1" name="create-outline" /><span i18n ><ion-icon class="mr-1" name="create-outline" /><span i18n
>Manage Asset Profile</span >Manage Asset Profile</span

25
apps/client/src/app/pages/admin/admin-page.routes.ts

@ -1,5 +1,6 @@
import { GfAdminJobsComponent } from '@ghostfolio/client/components/admin-jobs/admin-jobs.component'; import { GfAdminJobsComponent } from '@ghostfolio/client/components/admin-jobs/admin-jobs.component';
import { GfAdminMarketDataComponent } from '@ghostfolio/client/components/admin-market-data/admin-market-data.component'; import { GfAdminMarketDataComponent } from '@ghostfolio/client/components/admin-market-data/admin-market-data.component';
import { GfAssetProfileDialogHostComponent } from '@ghostfolio/client/components/admin-market-data/asset-profile-dialog-host/asset-profile-dialog-host.component';
import { GfAdminOverviewComponent } from '@ghostfolio/client/components/admin-overview/admin-overview.component'; import { GfAdminOverviewComponent } from '@ghostfolio/client/components/admin-overview/admin-overview.component';
import { GfAdminSettingsComponent } from '@ghostfolio/client/components/admin-settings/admin-settings.component'; import { GfAdminSettingsComponent } from '@ghostfolio/client/components/admin-settings/admin-settings.component';
import { GfAdminUsersComponent } from '@ghostfolio/client/components/admin-users/admin-users.component'; import { GfAdminUsersComponent } from '@ghostfolio/client/components/admin-users/admin-users.component';
@ -10,6 +11,9 @@ import { Routes, UrlMatcher, UrlSegment } from '@angular/router';
import { AdminPageComponent } from './admin-page.component'; import { AdminPageComponent } from './admin-page.component';
const { create, update } =
internalRoutes.adminControl.subRoutes.marketData.subRoutes;
// Matches both the users list and the user detail dialog route within a single // Matches both the users list and the user detail dialog route within a single
// route configuration so that the component is reused (and not re-created) when // route configuration so that the component is reused (and not re-created) when
// the user detail dialog is opened or closed // the user detail dialog is opened or closed
@ -46,8 +50,27 @@ export const routes: Routes = [
title: internalRoutes.adminControl.subRoutes.jobs.title title: internalRoutes.adminControl.subRoutes.jobs.title
}, },
{ {
path: internalRoutes.adminControl.subRoutes.marketData.path, children: [
{
component: GfAssetProfileDialogHostComponent,
data: { mode: 'create' },
path: create.path,
title: create.title
},
{
children: [
{
component: GfAssetProfileDialogHostComponent,
data: { mode: 'update' },
path: update.path,
title: update.title
}
],
path: ':dataSource/:symbol'
}
],
component: GfAdminMarketDataComponent, component: GfAdminMarketDataComponent,
path: internalRoutes.adminControl.subRoutes.marketData.path,
title: internalRoutes.adminControl.subRoutes.marketData.title title: internalRoutes.adminControl.subRoutes.marketData.title
}, },
{ {

14
libs/common/src/lib/routes/routes.ts

@ -62,6 +62,20 @@ export const internalRoutes = {
marketData: { marketData: {
path: 'market-data', path: 'market-data',
routerLink: ['/admin', 'market-data'], routerLink: ['/admin', 'market-data'],
subRoutes: {
create: {
path: 'create',
routerLink: ['/admin', 'market-data', 'create'],
title: $localize`Add Asset Profile`
},
update: {
path: 'update',
routerLink: (aDataSource: string, aSymbol: string) => {
return ['/admin', 'market-data', aDataSource, aSymbol, 'update'];
},
title: $localize`Asset Profile`
}
},
title: $localize`Market Data` title: $localize`Market Data`
}, },
settings: { settings: {

11
libs/ui/src/lib/assistant/assistant-list-item/assistant-list-item.component.ts

@ -55,14 +55,13 @@ export class GfAssistantListItemComponent
this.queryParams = {}; this.queryParams = {};
this.routerLink = this.item.routerLink; this.routerLink = this.item.routerLink;
} else if (this.item?.mode === SearchMode.ASSET_PROFILE) { } else if (this.item?.mode === SearchMode.ASSET_PROFILE) {
this.queryParams = { this.queryParams = {};
assetProfileDialog: true,
dataSource: this.item.dataSource,
symbol: this.item.symbol
};
this.routerLink = this.routerLink =
internalRoutes.adminControl.subRoutes.marketData.routerLink ?? []; internalRoutes.adminControl.subRoutes.marketData.subRoutes.update.routerLink(
this.item.dataSource,
this.item.symbol
);
} else if (this.item?.mode === SearchMode.HOLDING) { } else if (this.item?.mode === SearchMode.HOLDING) {
this.queryParams = { this.queryParams = {
dataSource: this.item.dataSource, dataSource: this.item.dataSource,

Loading…
Cancel
Save