Browse Source

Task/improve type safety in admin market data component (#6885)

Improve type safety
pull/6872/head
Kenrick Tandrian 7 days ago
committed by GitHub
parent
commit
e1e6ae7b57
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 152
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  2. 3
      libs/common/src/lib/config.ts
  3. 4
      libs/ui/src/lib/treemap-chart/treemap-chart.component.stories.ts

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

@ -1,7 +1,8 @@
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { import {
DEFAULT_COLOR_SCHEME,
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
ghostfolioScraperApiSymbolPrefix locale
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper'; import { getDateFormatString } from '@ghostfolio/common/helper';
import { import {
@ -26,9 +27,11 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
computed,
DestroyRef, DestroyRef,
inject,
OnInit, OnInit,
ViewChild viewChild
} from '@angular/core'; } from '@angular/core';
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';
@ -97,11 +100,8 @@ import { CreateAssetProfileDialogParams } from './create-asset-profile-dialog/in
templateUrl: './admin-market-data.html' templateUrl: './admin-market-data.html'
}) })
export class GfAdminMarketDataComponent implements AfterViewInit, OnInit { export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator; protected readonly adminMarketDataService = inject(AdminMarketDataService);
@ViewChild(MatSort) sort: MatSort; protected readonly allFilters: Filter[] = [
public activeFilters: Filter[] = [];
public allFilters: Filter[] = [
...Object.keys(AssetSubClass) ...Object.keys(AssetSubClass)
.filter((assetSubClass) => { .filter((assetSubClass) => {
return assetSubClass !== 'CASH'; return assetSubClass !== 'CASH';
@ -146,37 +146,39 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
type: 'PRESET_ID' as Filter['type'] type: 'PRESET_ID' as Filter['type']
} }
]; ];
public benchmarks: Partial<SymbolProfile>[]; protected dataSource = new MatTableDataSource<AdminMarketDataItem>();
public currentDataSource: DataSource; protected defaultDateFormat: string;
public currentSymbol: string; protected readonly displayedColumns: string[] = [];
public dataSource = new MatTableDataSource<AdminMarketDataItem>(); protected readonly filters$ = new Subject<Filter[]>();
public defaultDateFormat: string; protected isLoading = true;
public deviceType: string; protected readonly isUUID = isUUID;
public displayedColumns: string[] = []; protected pageSize = DEFAULT_PAGE_SIZE;
public filters$ = new Subject<Filter[]>(); protected placeholder = '';
public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; protected readonly selection = new SelectionModel<AdminMarketDataItem>(true);
public hasPermissionForSubscription: boolean; protected totalItems = 0;
public info: InfoItem; protected user: User;
public isLoading = true;
public isUUID = isUUID; private activeFilters: Filter[] = [];
public placeholder = ''; private benchmarks: Partial<SymbolProfile>[];
public pageSize = DEFAULT_PAGE_SIZE; private readonly deviceType = computed(
public selection: SelectionModel<Partial<SymbolProfile>>; () => this.deviceDetectorService.deviceInfo().deviceType
public totalItems = 0; );
public user: User; private readonly hasPermissionForSubscription: boolean;
private readonly info: InfoItem;
public constructor( private readonly paginator = viewChild.required(MatPaginator);
public adminMarketDataService: AdminMarketDataService, private readonly sort = viewChild.required(MatSort);
private adminService: AdminService,
private changeDetectorRef: ChangeDetectorRef, private readonly adminService = inject(AdminService);
private dataService: DataService, private readonly changeDetectorRef = inject(ChangeDetectorRef);
private destroyRef: DestroyRef, private readonly dataService = inject(DataService);
private deviceDetectorService: DeviceDetectorService, private readonly destroyRef = inject(DestroyRef);
private dialog: MatDialog, private readonly deviceDetectorService = inject(DeviceDetectorService);
private route: ActivatedRoute, private readonly dialog = inject(MatDialog);
private router: Router, private readonly route = inject(ActivatedRoute);
private userService: UserService private readonly router = inject(Router);
) { private readonly userService = inject(UserService);
public constructor() {
this.info = this.dataService.fetchInfo(); this.info = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission( this.hasPermissionForSubscription = hasPermission(
@ -255,14 +257,14 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
} }
public ngAfterViewInit() { public ngAfterViewInit() {
this.sort.sortChange.subscribe( this.sort().sortChange.subscribe(
({ active: sortColumn, direction }: Sort) => { ({ active: sortColumn, direction }: Sort) => {
this.paginator.pageIndex = 0; this.paginator().pageIndex = 0;
this.loadData({ this.loadData({
sortColumn, sortColumn,
sortDirection: direction, sortDirection: direction,
pageIndex: this.paginator.pageIndex pageIndex: this.paginator().pageIndex
}); });
} }
); );
@ -272,24 +274,24 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
const { benchmarks } = this.dataService.fetchInfo(); const { benchmarks } = this.dataService.fetchInfo();
this.benchmarks = benchmarks; this.benchmarks = benchmarks;
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.selection = new SelectionModel(true);
} }
public onChangePage(page: PageEvent) { protected onChangePage(page: PageEvent) {
this.loadData({ this.loadData({
pageIndex: page.pageIndex, pageIndex: page.pageIndex,
sortColumn: this.sort.active, sortColumn: this.sort().active,
sortDirection: this.sort.direction sortDirection: this.sort().direction
}); });
} }
public onDeleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { protected onDeleteAssetProfile({
dataSource,
symbol
}: AssetProfileIdentifier) {
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol }); this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol });
} }
public onDeleteAssetProfiles() { protected onDeleteAssetProfiles() {
this.adminMarketDataService.deleteAssetProfiles( this.adminMarketDataService.deleteAssetProfiles(
this.selection.selected.map(({ dataSource, symbol }) => { this.selection.selected.map(({ dataSource, symbol }) => {
return { dataSource, symbol }; return { dataSource, symbol };
@ -297,7 +299,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
); );
} }
public onGather7Days() { protected onGather7Days() {
this.adminService this.adminService
.gather7Days() .gather7Days()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
@ -308,7 +310,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
}); });
} }
public onGatherMax() { protected onGatherMax() {
this.adminService this.adminService
.gatherMax() .gatherMax()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
@ -319,31 +321,14 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
}); });
} }
public onGatherProfileData() { protected onGatherProfileData() {
this.adminService this.adminService
.gatherProfileData() .gatherProfileData()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(); .subscribe();
} }
public onGatherProfileDataBySymbol({ protected onOpenAssetProfileDialog({
dataSource,
symbol
}: AssetProfileIdentifier) {
this.adminService
.gatherProfileDataBySymbol({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe();
}
public onGatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
this.adminService
.gatherSymbol({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe();
}
public onOpenAssetProfileDialog({
dataSource, dataSource,
symbol symbol
}: AssetProfileIdentifier) { }: AssetProfileIdentifier) {
@ -375,8 +360,8 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
? Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER
: DEFAULT_PAGE_SIZE; : DEFAULT_PAGE_SIZE;
if (pageIndex === 0 && this.paginator) { if (pageIndex === 0 && this.paginator()) {
this.paginator.pageIndex = 0; this.paginator().pageIndex = 0;
} }
this.placeholder = this.placeholder =
@ -406,7 +391,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
}; };
}) })
); );
this.dataSource.sort = this.sort; this.dataSource.sort = this.sort();
this.isLoading = false; this.isLoading = false;
@ -435,12 +420,13 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
data: { data: {
dataSource, dataSource,
symbol, symbol,
colorScheme: this.user?.settings.colorScheme, colorScheme:
deviceType: this.deviceType, this.user?.settings.colorScheme ?? DEFAULT_COLOR_SCHEME,
locale: this.user?.settings?.locale deviceType: this.deviceType(),
}, locale: this.user?.settings?.locale ?? locale
height: this.deviceType === 'mobile' ? '98vh' : '80vh', } satisfies AssetProfileDialogParams,
width: this.deviceType === 'mobile' ? '100vw' : '50rem' height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef dialogRef
@ -471,10 +457,10 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
>(GfCreateAssetProfileDialogComponent, { >(GfCreateAssetProfileDialogComponent, {
autoFocus: false, autoFocus: false,
data: { data: {
deviceType: this.deviceType, deviceType: this.deviceType(),
locale: this.user?.settings?.locale locale: this.user?.settings?.locale ?? locale
}, } satisfies CreateAssetProfileDialogParams,
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef dialogRef

3
libs/common/src/lib/config.ts

@ -2,6 +2,8 @@ import { AssetClass, AssetSubClass, DataSource, Type } from '@prisma/client';
import { JobOptions, JobStatus } from 'bull'; import { JobOptions, JobStatus } from 'bull';
import ms from 'ms'; import ms from 'ms';
import { ColorScheme } from './types';
export const ghostfolioPrefix = 'GF'; export const ghostfolioPrefix = 'GF';
export const ghostfolioScraperApiSymbolPrefix = `_${ghostfolioPrefix}_`; export const ghostfolioScraperApiSymbolPrefix = `_${ghostfolioPrefix}_`;
export const ghostfolioFearAndGreedIndexDataSourceCryptocurrencies = export const ghostfolioFearAndGreedIndexDataSourceCryptocurrencies =
@ -77,6 +79,7 @@ export const PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW =
export const STATISTICS_GATHERING_QUEUE = 'STATISTICS_GATHERING_QUEUE'; export const STATISTICS_GATHERING_QUEUE = 'STATISTICS_GATHERING_QUEUE';
export const DEFAULT_COLOR_SCHEME: ColorScheme = 'LIGHT';
export const DEFAULT_CURRENCY = 'USD'; export const DEFAULT_CURRENCY = 'USD';
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy'; export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const DEFAULT_HOST = '0.0.0.0'; export const DEFAULT_HOST = '0.0.0.0';

4
libs/ui/src/lib/treemap-chart/treemap-chart.component.stories.ts

@ -1,3 +1,5 @@
import { DEFAULT_COLOR_SCHEME } from '@ghostfolio/common/config';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import '@angular/localize/init'; import '@angular/localize/init';
import { moduleMetadata } from '@storybook/angular'; import { moduleMetadata } from '@storybook/angular';
@ -37,7 +39,7 @@ export const Default: Story = {
args: { args: {
holdings, holdings,
baseCurrency: 'USD', baseCurrency: 'USD',
colorScheme: 'LIGHT', colorScheme: DEFAULT_COLOR_SCHEME,
cursor: undefined, cursor: undefined,
dateRange: 'mtd', dateRange: 'mtd',
locale: 'en-US' locale: 'en-US'

Loading…
Cancel
Save