diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 40bb38b8a..877cee680 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -61,12 +61,13 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; }; public placeholder = ''; - public platforms: { + public platformsValues: { [id: string]: Pick & { id: string; value: number; }; }; + public platforms: Platform[]; public portfolioDetails: PortfolioDetails; public positions: { [symbol: string]: Pick< @@ -107,6 +108,9 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { private router: Router, private userService: UserService ) { + const { platforms } = this.dataService.fetchInfo(); + this.platforms = platforms; + route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { @@ -173,8 +177,15 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { this.user = state.user; const accountFilters: Filter[] = this.user.accounts.map( - ({ id, name }) => { + ({ id, name, platformId }) => { + let accountPlatformUrl = ''; + for (const platform of this.platforms) { + if (platform.id === platformId) { + accountPlatformUrl = platform.url; + } + } return { + accountPlatformUrl, id, label: name, type: 'ACCOUNT' @@ -286,7 +297,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { value: 0 } }; - this.platforms = {}; + this.platformsValues = {}; this.portfolioDetails = { accounts: {}, filteredValueInPercentage: 0, @@ -517,7 +528,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { value = valueInBaseCurrency; } - this.platforms[id] = { + this.platformsValues[id] = { id, name, value diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 51dcee24c..dd8bb0e43 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -18,7 +18,12 @@ import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.in import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { DateRange, GroupBy, ToggleOption } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; -import { AssetClass, DataSource, SymbolProfile } from '@prisma/client'; +import { + AssetClass, + DataSource, + Platform, + SymbolProfile +} from '@prisma/client'; import { differenceInDays } from 'date-fns'; import { isNumber, sortBy } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; @@ -57,6 +62,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public performanceDataItems: HistoricalDataItem[]; public performanceDataItemsInPercentage: HistoricalDataItem[]; public placeholder = ''; + public platforms: Platform[]; public portfolioEvolutionDataLabel = $localize`Deposit`; public streaks: PortfolioInvestments['streaks']; public top3: Position[]; @@ -76,8 +82,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { private router: Router, private userService: UserService ) { - const { benchmarks } = this.dataService.fetchInfo(); + const { benchmarks, platforms } = this.dataService.fetchInfo(); this.benchmarks = benchmarks; + this.platforms = platforms; route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) @@ -139,8 +146,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { this.user = state.user; const accountFilters: Filter[] = this.user.accounts.map( - ({ id, name }) => { + ({ id, name, platformId }) => { + let accountPlatformUrl = ''; + for (const { id, url } of this.platforms) { + if (id === platformId) { + accountPlatformUrl = url; + } + } return { + accountPlatformUrl, id, label: name, type: 'ACCOUNT' diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts index d65f20165..1fa4e98e6 100644 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts +++ b/apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts @@ -14,7 +14,7 @@ import { } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { translate } from '@ghostfolio/ui/i18n'; -import { AssetClass, DataSource } from '@prisma/client'; +import { AssetClass, DataSource, Platform } from '@prisma/client'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators'; @@ -33,6 +33,7 @@ export class HoldingsPageComponent implements OnDestroy, OnInit { public hasPermissionToCreateOrder: boolean; public isLoading = false; public placeholder = ''; + public platforms: Platform[]; public portfolioDetails: PortfolioDetails; public positionsArray: PortfolioPosition[]; public user: User; @@ -49,6 +50,9 @@ export class HoldingsPageComponent implements OnDestroy, OnInit { private router: Router, private userService: UserService ) { + const { platforms } = this.dataService.fetchInfo(); + this.platforms = platforms; + route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { @@ -114,8 +118,15 @@ export class HoldingsPageComponent implements OnDestroy, OnInit { ); const accountFilters: Filter[] = this.user.accounts.map( - ({ id, name }) => { + ({ id, name, platformId }) => { + let accountPlatformUrl = ''; + for (const { id, url } of this.platforms) { + if (id === platformId) { + accountPlatformUrl = url; + } + } return { + accountPlatformUrl, id, label: name, type: 'ACCOUNT' diff --git a/apps/client/src/styles.scss b/apps/client/src/styles.scss index 7085effc5..ed415b56c 100644 --- a/apps/client/src/styles.scss +++ b/apps/client/src/styles.scss @@ -459,6 +459,10 @@ ngx-skeleton-loader { } } +.mat-mdc-optgroup-label { + font-weight: bold; +} + .mat-mdc-paginator { background-color: rgba(var(--palette-foreground-base-light), 0.02); diff --git a/libs/common/src/lib/interfaces/filter.interface.ts b/libs/common/src/lib/interfaces/filter.interface.ts index 356b3add7..716311494 100644 --- a/libs/common/src/lib/interfaces/filter.interface.ts +++ b/libs/common/src/lib/interfaces/filter.interface.ts @@ -1,12 +1,19 @@ +import { DataSource } from '@prisma/client'; + export interface Filter { id: string; label?: string; + symbolDataSource?: DataSource; + accountPlatformUrl?: string; type: | 'ACCOUNT' | 'ASSET_CLASS' | 'ASSET_SUB_CLASS' + | 'CURRENCY' | 'PRESET_ID' | 'SEARCH_QUERY' | 'SYMBOL' - | 'TAG'; + | 'TAG' + | 'TYPE' + | 'YEAR'; } diff --git a/libs/ui/src/lib/activities-filter/activities-filter.component.html b/libs/ui/src/lib/activities-filter/activities-filter.component.html index 56540c5e3..7eb984b69 100644 --- a/libs/ui/src/lib/activities-filter/activities-filter.component.html +++ b/libs/ui/src/lib/activities-filter/activities-filter.component.html @@ -38,7 +38,43 @@ *ngFor="let filter of filterGroup.filters" [value]="filter.id" > - {{ filter.label | gfSymbol }} + + + {{ filter.label }} + + + {{ filter.id !== filter.label ? filter.id : '' }} + {{ filter.label }} + + + + + {{ filter.id }} + + {{ filter.label | gfSymbol }} + + + + + + {{ filter.label }} + diff --git a/libs/ui/src/lib/activities-filter/activities-filter.component.scss b/libs/ui/src/lib/activities-filter/activities-filter.component.scss index 7d0649bfc..ebbff384c 100644 --- a/libs/ui/src/lib/activities-filter/activities-filter.component.scss +++ b/libs/ui/src/lib/activities-filter/activities-filter.component.scss @@ -30,3 +30,7 @@ } } } + +gf-symbol-icon { + display: contents; +} diff --git a/libs/ui/src/lib/activities-filter/activities-filter.module.ts b/libs/ui/src/lib/activities-filter/activities-filter.module.ts index b85bdcb81..527eeedc6 100644 --- a/libs/ui/src/lib/activities-filter/activities-filter.module.ts +++ b/libs/ui/src/lib/activities-filter/activities-filter.module.ts @@ -6,6 +6,8 @@ import { MatButtonModule } from '@angular/material/button'; import { MatChipsModule } from '@angular/material/chips'; import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { GfActivityTypeModule } from '@ghostfolio/ui/activity-type/activity-type.module'; +import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { ActivitiesFilterComponent } from './activities-filter.component'; @@ -15,6 +17,8 @@ import { ActivitiesFilterComponent } from './activities-filter.component'; exports: [ActivitiesFilterComponent], imports: [ CommonModule, + GfActivityTypeModule, + GfSymbolIconModule, GfSymbolModule, MatAutocompleteModule, MatButtonModule, diff --git a/libs/ui/src/lib/activities-table/activities-table.component.ts b/libs/ui/src/lib/activities-table/activities-table.component.ts index 66c065709..6256e6b26 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.ts @@ -10,6 +10,7 @@ import { Output, ViewChild } from '@angular/core'; +import { getCurrencySymbol } from '@angular/common'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; @@ -307,15 +308,16 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { fieldValueMap[activity.Account.id] = { id: activity.Account.id, label: activity.Account.name, + accountPlatformUrl: activity.Account.Platform?.url, type: 'ACCOUNT' }; } if (activity.SymbolProfile?.currency) { fieldValueMap[activity.SymbolProfile.currency] = { - id: activity.SymbolProfile.currency, + id: getCurrencySymbol(activity.SymbolProfile.currency, 'narrow'), label: activity.SymbolProfile.currency, - type: 'TAG' + type: 'CURRENCY' }; } @@ -325,21 +327,30 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy, OnInit { ) { fieldValueMap[activity.SymbolProfile.symbol] = { id: activity.SymbolProfile.symbol, - label: activity.SymbolProfile.symbol, + label: activity.SymbolProfile.name, + symbolDataSource: activity.SymbolProfile.dataSource, type: 'SYMBOL' }; } + for (const { name } of activity.tags) { + fieldValueMap[name] = { + id: name, + label: name, + type: 'TAG' + }; + } + fieldValueMap[activity.type] = { id: activity.type, label: activity.type, - type: 'TAG' + type: 'TYPE' }; fieldValueMap[format(new Date(activity.date), 'yyyy')] = { id: format(new Date(activity.date), 'yyyy'), label: format(new Date(activity.date), 'yyyy'), - type: 'TAG' + type: 'YEAR' }; return Object.values(fieldValueMap); diff --git a/libs/ui/src/lib/i18n.ts b/libs/ui/src/lib/i18n.ts index 2279bb089..235ce157b 100644 --- a/libs/ui/src/lib/i18n.ts +++ b/libs/ui/src/lib/i18n.ts @@ -5,6 +5,7 @@ const locales = { 'Asia-Pacific': $localize`Asia-Pacific`, ASSET_CLASS: $localize`Asset Class`, ASSET_SUB_CLASS: $localize`Asset Sub Class`, + CURRENCY: $localize`Currency`, CORE: $localize`Core`, DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC: $localize`Switch to Ghostfolio Premium or Ghostfolio Open Source easily`, DATA_IMPORT_AND_EXPORT_TOOLTIP_OSS: $localize`Switch to Ghostfolio Premium easily`, @@ -23,6 +24,7 @@ const locales = { SATELLITE: $localize`Satellite`, SYMBOL: $localize`Symbol`, TAG: $localize`Tag`, + TYPE: $localize`Type`, YEAR: $localize`Year`, YEARS: $localize`Years`,