Browse Source

Task/improve portfolio summary (#7453)

* Improve portfolio summary

* Update changelog
pull/7454/head
Thomas Kaul 2 days ago
committed by GitHub
parent
commit
f1f1f2ca27
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 2
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  3. 44
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  4. 10
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts
  5. 2
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Improved the portfolio summary tab on the home page
- Upgraded `@openrouter/ai-sdk-provider` from version `2.9.1` to `3.0.0` - Upgraded `@openrouter/ai-sdk-provider` from version `2.9.1` to `3.0.0`
- Upgraded `ai` from version `6.0.174` to `7.0.37` - Upgraded `ai` from version `6.0.174` to `7.0.37`

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

@ -170,7 +170,7 @@
" "
[unit]="data.baseCurrency" [unit]="data.baseCurrency"
[value]="investmentInBaseCurrencyWithCurrencyEffect" [value]="investmentInBaseCurrencyWithCurrencyEffect"
>Investment</gf-value >Invested Capital</gf-value
> >
</div> </div>
@if ( @if (

44
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -57,7 +57,7 @@
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 text-truncate" i18n>Investment</div> <div class="flex-grow-1 text-truncate" i18n>Invested Capital</div>
<div class="justify-content-end"> <div class="justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -165,7 +165,7 @@
</div> </div>
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1 indent-1"> <div class="align-items-center d-flex flex-grow-1 indent-1">
<ng-container i18n>Cash</ng-container> <ng-container i18n>Holdings</ng-container>
@if ( @if (
!hasImpersonationId && !hasImpersonationId &&
summary?.totalValueInBaseCurrency > 0 && summary?.totalValueInBaseCurrency > 0 &&
@ -175,7 +175,8 @@
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : cashPercentage" [precision]="0"
[value]="isLoading ? undefined : holdingsPercentage"
/> />
} }
</div> </div>
@ -187,13 +188,13 @@
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.totalCashInBaseCurrency" [value]="isLoading ? undefined : holdingsInBaseCurrency"
/> />
</div> </div>
</div> </div>
@if (isLoading || summary?.emergencyFund?.cash > 0) { @if (isLoading || summary?.emergencyFund?.assets > 0) {
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Buying Power</div> <div class="flex-grow-1 indent-2 text-truncate" i18n>Investments</div>
<div class="flex-column flex-wrap justify-content-end"> <div class="flex-column flex-wrap justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -202,7 +203,7 @@
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash" [value]="isLoading ? undefined : investmentsInBaseCurrency"
/> />
</div> </div>
</div> </div>
@ -216,14 +217,14 @@
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.cash" [value]="isLoading ? undefined : summary?.emergencyFund?.assets"
/> />
</div> </div>
</div> </div>
} }
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="align-items-center d-flex flex-grow-1 indent-1"> <div class="align-items-center d-flex flex-grow-1 indent-1">
<ng-container i18n>Holdings</ng-container> <ng-container i18n>Cash</ng-container>
@if ( @if (
!hasImpersonationId && !hasImpersonationId &&
summary?.totalValueInBaseCurrency > 0 && summary?.totalValueInBaseCurrency > 0 &&
@ -233,7 +234,8 @@
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : holdingsPercentage" [precision]="0"
[value]="isLoading ? undefined : cashPercentage"
/> />
} }
</div> </div>
@ -245,11 +247,25 @@
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : holdingsInBaseCurrency" [value]="isLoading ? undefined : summary?.totalCashInBaseCurrency"
/> />
</div> </div>
</div> </div>
@if (isLoading || summary?.emergencyFund?.assets > 0) { @if (isLoading || summary?.emergencyFund?.cash > 0) {
<div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Buying Power</div>
<div class="flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[isCurrency]="true"
[locale]="locale"
[precision]="precision"
[unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.cash"
/>
</div>
</div>
<div class="flex-nowrap px-3 py-1 row"> <div class="flex-nowrap px-3 py-1 row">
<div class="flex-grow-1 indent-2 text-truncate" i18n>Emergency Fund</div> <div class="flex-grow-1 indent-2 text-truncate" i18n>Emergency Fund</div>
<div class="flex-column flex-wrap justify-content-end"> <div class="flex-column flex-wrap justify-content-end">
@ -260,7 +276,7 @@
[locale]="locale" [locale]="locale"
[precision]="precision" [precision]="precision"
[unit]="baseCurrency" [unit]="baseCurrency"
[value]="isLoading ? undefined : summary?.emergencyFund?.assets" [value]="isLoading ? undefined : summary?.emergencyFund?.cash"
/> />
</div> </div>
</div> </div>
@ -277,6 +293,7 @@
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : excludedFromAnalysisPercentage" [value]="isLoading ? undefined : excludedFromAnalysisPercentage"
/> />
} }
@ -363,6 +380,7 @@
class="d-lg-inline-block d-none ml-2 small text-muted" class="d-lg-inline-block d-none ml-2 small text-muted"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[precision]="0"
[value]="isLoading ? undefined : emergencyFundPercentage" [value]="isLoading ? undefined : emergencyFundPercentage"
/> />
} }

10
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts

@ -98,6 +98,16 @@ export class GfPortfolioSummaryComponent implements OnChanges {
: 0; : 0;
} }
protected get investmentsInBaseCurrency() {
if (!isNumber(this.holdingsInBaseCurrency)) {
return null;
}
return (
this.holdingsInBaseCurrency - (this.summary.emergencyFund?.assets ?? 0)
);
}
public ngOnChanges() { public ngOnChanges() {
if (this.summary) { if (this.summary) {
if ( if (

2
apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

@ -82,7 +82,7 @@ export class GfAnalysisPageComponent implements OnInit {
protected hasImpersonationId: boolean; protected hasImpersonationId: boolean;
protected hasPermissionToReadAiPrompt: boolean; protected hasPermissionToReadAiPrompt: boolean;
protected investments: InvestmentItem[]; protected investments: InvestmentItem[];
protected readonly investmentTimelineDataLabel = $localize`Investment`; protected readonly investmentTimelineDataLabel = $localize`Invested Capital`;
protected investmentsByGroup: InvestmentItem[]; protected investmentsByGroup: InvestmentItem[];
protected isLoadingAnalysisPrompt: boolean; protected isLoadingAnalysisPrompt: boolean;
protected isLoadingBenchmarkComparator: boolean; protected isLoadingBenchmarkComparator: boolean;

Loading…
Cancel
Save