Browse Source

Improve label of cash positions

pull/8007/head
Thomas Kaul 2 weeks ago
parent
commit
dc8aee6d36
  1. 3
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  2. 3
      apps/client/src/app/pages/public/public-page.component.ts
  3. 36
      libs/common/src/lib/helper.spec.ts
  4. 16
      libs/common/src/lib/helper.ts
  5. 12
      libs/ui/src/lib/holdings-table/holdings-table.component.html
  6. 3
      libs/ui/src/lib/holdings-table/holdings-table.component.ts
  7. 14
      libs/ui/src/lib/treemap-chart/treemap-chart.component.ts

3
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -11,6 +11,7 @@ import {
convertValuesToPercentagesOfTotal, convertValuesToPercentagesOfTotal,
getAssetProfileIdentifier, getAssetProfileIdentifier,
getCountryName, getCountryName,
getHoldingName,
isCashPosition isCashPosition
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
import { import {
@ -533,7 +534,7 @@ export class GfAllocationsPageComponent implements OnInit {
value, value,
dataSource: position.assetProfile.dataSource, dataSource: position.assetProfile.dataSource,
isClickable: canOpenHoldingDetail(position), isClickable: canOpenHoldingDetail(position),
name: position.assetProfile.name ?? '' name: getHoldingName(position.assetProfile)
}; };
} }
} }

3
apps/client/src/app/pages/public/public-page.component.ts

@ -3,6 +3,7 @@ import {
convertValuesToPercentagesOfTotal, convertValuesToPercentagesOfTotal,
getAssetProfileIdentifier, getAssetProfileIdentifier,
getCountryName, getCountryName,
getHoldingName,
isCashPosition isCashPosition
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
import { import {
@ -260,7 +261,7 @@ export class GfPublicPageComponent implements OnInit {
this.symbols[symbol] = { this.symbols[symbol] = {
symbol, symbol,
value, value,
name: position.assetProfile.name ?? symbol name: getHoldingName(position.assetProfile)
}; };
} }
} }

36
libs/common/src/lib/helper.spec.ts

@ -7,6 +7,7 @@ import {
extractNumberFromString, extractNumberFromString,
getCountryCodeFromCurrency, getCountryCodeFromCurrency,
getEmojiFlag, getEmojiFlag,
getHoldingName,
getNumberFormatGroup, getNumberFormatGroup,
getStringOrNull, getStringOrNull,
getStringOrUndefined, getStringOrUndefined,
@ -143,6 +144,41 @@ describe('Helper', () => {
}); });
}); });
describe('Get holding name', () => {
it('Cash position', () => {
expect(
getHoldingName({
assetSubClass: 'CASH',
assetSubClassLabel: 'Cash',
name: 'CHF',
symbol: 'CHF'
})
).toEqual('Cash (CHF)');
});
it('Holding with name', () => {
expect(
getHoldingName({
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
name: 'Apple Inc.',
symbol: 'AAPL'
})
).toEqual('Apple Inc.');
});
it('Holding without name', () => {
expect(
getHoldingName({
assetSubClass: 'STOCK',
assetSubClassLabel: 'Stock',
name: undefined,
symbol: 'AAPL'
})
).toEqual('AAPL');
});
});
describe('Get number format group', () => { describe('Get number format group', () => {
let languageGetter: jest.SpyInstance<string, [], any>; let languageGetter: jest.SpyInstance<string, [], any>;

16
libs/common/src/lib/helper.ts

@ -453,6 +453,22 @@ export function getEmojiFlag(aCountryCode: string) {
}); });
} }
export function getHoldingName({
assetSubClass,
assetSubClassLabel,
name,
symbol
}: Pick<
PortfolioPosition['assetProfile'],
'assetSubClass' | 'assetSubClassLabel' | 'name' | 'symbol'
>) {
if (isCashPosition({ assetSubClass })) {
return `${assetSubClassLabel} (${symbol})`;
}
return name ?? symbol;
}
export function getLocale() { export function getLocale() {
return navigator.language ?? DEFAULT_LOCALE; return navigator.language ?? DEFAULT_LOCALE;
} }

12
libs/ui/src/lib/holdings-table/holdings-table.component.html

@ -20,7 +20,7 @@
[countryCode]="getCountryCodeForCashPosition(element)" [countryCode]="getCountryCodeForCashPosition(element)"
[dataSource]="element.assetProfile.dataSource" [dataSource]="element.assetProfile.dataSource"
[symbol]="element.assetProfile.symbol" [symbol]="element.assetProfile.symbol"
[tooltip]="element.assetProfile.name" [tooltip]="getHoldingName(element.assetProfile)"
/> />
</td> </td>
</ng-container> </ng-container>
@ -40,11 +40,11 @@
mat-cell mat-cell
[class.w-100]="mode() === 'simple'" [class.w-100]="mode() === 'simple'"
> >
<div class="text-truncate" [title]="element.assetProfile.name"> <div
{{ element.assetProfile.name }} class="text-truncate"
@if (element.assetProfile.name === element.assetProfile.symbol) { [title]="getHoldingName(element.assetProfile)"
<span>({{ element.assetProfile.assetSubClassLabel }})</span> >
} {{ getHoldingName(element.assetProfile) }}
</div> </div>
@if (mode() === 'default') { @if (mode() === 'default') {
<div> <div>

3
libs/ui/src/lib/holdings-table/holdings-table.component.ts

@ -1,6 +1,7 @@
import { import {
canOpenHoldingDetail, canOpenHoldingDetail,
getCountryCodeFromCurrency, getCountryCodeFromCurrency,
getHoldingName,
getLocale, getLocale,
getLowercase, getLowercase,
isCashPosition isCashPosition
@ -89,6 +90,8 @@ export class GfHoldingsTableComponent {
return columns; return columns;
}); });
protected readonly getHoldingName = getHoldingName;
protected readonly isLoading = computed(() => { protected readonly isLoading = computed(() => {
return !this.holdings(); return !this.holdings();
}); });

14
libs/ui/src/lib/treemap-chart/treemap-chart.component.ts

@ -3,7 +3,11 @@ import {
getIntervalFromDateRange getIntervalFromDateRange
} from '@ghostfolio/common/calculation-helper'; } from '@ghostfolio/common/calculation-helper';
import { getTooltipOptions } from '@ghostfolio/common/chart-helper'; import { getTooltipOptions } from '@ghostfolio/common/chart-helper';
import { canOpenHoldingDetail, getLocale } from '@ghostfolio/common/helper'; import {
canOpenHoldingDetail,
getHoldingName,
getLocale
} from '@ghostfolio/common/helper';
import { import {
AssetProfileIdentifier, AssetProfileIdentifier,
PortfolioPosition PortfolioPosition
@ -399,20 +403,18 @@ export class GfTreemapChartComponent
const raw = context.raw as GfTreemapDataPoint; const raw = context.raw as GfTreemapDataPoint;
const allocationInPercentage = `${(raw._data.allocationInPercentage * 100).toFixed(2)}%`; const allocationInPercentage = `${(raw._data.allocationInPercentage * 100).toFixed(2)}%`;
const name = raw._data.assetProfile.name; const name = getHoldingName(raw._data.assetProfile);
const sign = const sign =
raw._data.netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''; raw._data.netPerformancePercentWithCurrencyEffect > 0 ? '+' : '';
const symbol = raw._data.assetProfile.symbol;
const netPerformanceInPercentageWithSign = `${sign}${(raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`; const netPerformanceInPercentageWithSign = `${sign}${(raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`;
if (raw._data.valueInBaseCurrency !== null) { if (raw._data.valueInBaseCurrency !== null) {
const value = raw._data.valueInBaseCurrency; const value = raw._data.valueInBaseCurrency;
return [ return [
`${name ?? symbol} (${allocationInPercentage})`, `${name} (${allocationInPercentage})`,
`${value?.toLocaleString(this.locale(), { `${value?.toLocaleString(this.locale(), {
maximumFractionDigits: 2, maximumFractionDigits: 2,
minimumFractionDigits: 2 minimumFractionDigits: 2
@ -429,7 +431,7 @@ export class GfTreemapChartComponent
]; ];
} else { } else {
return [ return [
`${name ?? symbol} (${allocationInPercentage})`, `${name} (${allocationInPercentage})`,
'', '',
$localize`Performance`, $localize`Performance`,
netPerformanceInPercentageWithSign netPerformanceInPercentageWithSign

Loading…
Cancel
Save