Browse Source

Merge remote-tracking branch 'origin/main' into feature/enable-strict-null-checks-in-ui

pull/6264/head
KenTandrian 3 weeks ago
parent
commit
3c96aad620
  1. 1
      CHANGELOG.md
  2. 64
      apps/client/src/locales/messages.pl.xlf
  3. 2
      libs/ui/src/lib/benchmark/benchmark-detail-dialog/interfaces/interfaces.ts
  4. 16
      libs/ui/src/lib/benchmark/benchmark.component.html
  5. 129
      libs/ui/src/lib/benchmark/benchmark.component.ts
  6. 4
      libs/ui/src/lib/holdings-table/holdings-table.component.html
  7. 22
      libs/ui/src/lib/holdings-table/holdings-table.component.ts

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Consolidated the sign-out logic within the user service to unify cookie, state and token clearance - Consolidated the sign-out logic within the user service to unify cookie, state and token clearance
- Improved the language localization for Polish (`pl`)
- Upgraded `svgmap` from version `2.14.0` to `2.19.2` - Upgraded `svgmap` from version `2.14.0` to `2.19.2`
## 2.249.0 - 2026-03-10 ## 2.249.0 - 2026-03-10

64
apps/client/src/locales/messages.pl.xlf

@ -660,7 +660,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5611965261696422586" datatype="html"> <trans-unit id="5611965261696422586" datatype="html">
<source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">and is driven by the efforts of its <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>contributors<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">i jest rozwijany dzięki pracy jego <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio/graphs/contributors&quot; title=&quot;Contributors to Ghostfolio&quot; &gt;"/>współtwórców<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">49</context> <context context-type="linenumber">49</context>
@ -1580,7 +1580,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5289957034780335504" datatype="html"> <trans-unit id="5289957034780335504" datatype="html">
<source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source> <source>The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></source>
<target state="new">The source code is fully available as <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>open source software<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) under the <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target> <target state="translated">Kod źródłowy jest w pełni dostępny jako <x id="START_LINK" ctype="x-a" equiv-text="&lt;a href=&quot;https://github.com/ghostfolio/ghostfolio&quot; title=&quot;Find Ghostfolio on GitHub&quot; &gt;"/>oprogramowanie open source<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/> (OSS) na licencji <x id="START_LINK_1" equiv-text="&lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.html&quot; title=&quot;GNU Affero General Public License&quot; &gt;"/>AGPL-3.0 license<x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a &gt;"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
@ -2148,7 +2148,7 @@
</trans-unit> </trans-unit>
<trans-unit id="366169681580494481" datatype="html"> <trans-unit id="366169681580494481" datatype="html">
<source>Performance with currency effect</source> <source>Performance with currency effect</source>
<target state="new">Performance with currency effect</target> <target state="translated">Wynik z efektem walutowym</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
@ -2376,7 +2376,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3004519800638083911" datatype="html"> <trans-unit id="3004519800638083911" datatype="html">
<source>this is projected to increase to</source> <source>this is projected to increase to</source>
<target state="new">this is projected to increase to</target> <target state="translated">prognozuje się wzrost tej kwoty do</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">147</context>
@ -3564,7 +3564,7 @@
</trans-unit> </trans-unit>
<trans-unit id="70768492340592330" datatype="html"> <trans-unit id="70768492340592330" datatype="html">
<source>and a safe withdrawal rate (SWR) of</source> <source>and a safe withdrawal rate (SWR) of</source>
<target state="new">and a safe withdrawal rate (SWR) of</target> <target state="translated">oraz bezpiecznej stopy wypłaty (SWR) na poziomie</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">108</context> <context context-type="linenumber">108</context>
@ -4124,7 +4124,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5211792611718918888" datatype="html"> <trans-unit id="5211792611718918888" datatype="html">
<source>annual interest rate</source> <source>annual interest rate</source>
<target state="new">annual interest rate</target> <target state="translated">rocznej stopy zwrotu</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">185</context>
@ -4464,7 +4464,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2003818202621229370" datatype="html"> <trans-unit id="2003818202621229370" datatype="html">
<source>Sustainable retirement income</source> <source>Sustainable retirement income</source>
<target state="new">Sustainable retirement income</target> <target state="translated">Zrównoważony dochód na emeryturze</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
@ -4597,7 +4597,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4905798562247431262" datatype="html"> <trans-unit id="4905798562247431262" datatype="html">
<source>per month</source> <source>per month</source>
<target state="new">per month</target> <target state="translated">miesięcznie</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">94</context> <context context-type="linenumber">94</context>
@ -5397,7 +5397,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1468015720862673946" datatype="html"> <trans-unit id="1468015720862673946" datatype="html">
<source>View Details</source> <source>View Details</source>
<target state="new">View Details</target> <target state="translated">Zobacz szczegóły</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context> <context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">225</context>
@ -5425,7 +5425,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2149165958319691680" datatype="html"> <trans-unit id="2149165958319691680" datatype="html">
<source>Buy</source> <source>Buy</source>
<target state="translated">Zakup</target> <target state="translated">Kupno</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context> <context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">31</context> <context context-type="linenumber">31</context>
@ -5469,7 +5469,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4881880242577556" datatype="html"> <trans-unit id="4881880242577556" datatype="html">
<source>Sell</source> <source>Sell</source>
<target state="translated">Sprzedaj</target> <target state="translated">Sprzedaż</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context> <context context-type="sourcefile">apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html</context>
<context context-type="linenumber">44</context> <context context-type="linenumber">44</context>
@ -5625,7 +5625,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3227075298129844075" datatype="html"> <trans-unit id="3227075298129844075" datatype="html">
<source>If you retire today, you would be able to withdraw</source> <source>If you retire today, you would be able to withdraw</source>
<target state="new">If you retire today, you would be able to withdraw</target> <target state="translated">Gdybyś przeszedł na emeryturę dziś, mógłbyś wypłacać</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">68</context> <context context-type="linenumber">68</context>
@ -6710,7 +6710,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1355312194390410495" datatype="html"> <trans-unit id="1355312194390410495" datatype="html">
<source>, based on your total assets of</source> <source>, based on your total assets of</source>
<target state="new">, based on your total assets of</target> <target state="translated">, na podstawie całkowitej wartości aktywów wynoszącej</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">96</context> <context context-type="linenumber">96</context>
@ -6974,7 +6974,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2878377610946588870" datatype="html"> <trans-unit id="2878377610946588870" datatype="html">
<source>, assuming a</source> <source>, assuming a</source>
<target state="new">, assuming a</target> <target state="translated">, przyjmując</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/fire/fire-page.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">174</context>
@ -7062,7 +7062,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3528767106831563012" datatype="html"> <trans-unit id="3528767106831563012" datatype="html">
<source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source> <source>Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</source>
<target state="new">Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.</target> <target state="translated">Ghostfolio to aplikacja do zarządzania majątkiem, przeznaczona dla osób prywatnych do śledzenia akcji, ETF‑ów i kryptowalut oraz podejmowania solidnych, opartych na danych decyzji inwestycyjnych.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -7392,7 +7392,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7825231215382064101" datatype="html"> <trans-unit id="7825231215382064101" datatype="html">
<source>Change with currency effect</source> <source>Change with currency effect</source>
<target state="new">Change with currency effect</target> <target state="translated">Zmiana z efektem walutowym</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">116</context>
@ -7532,7 +7532,7 @@
</trans-unit> </trans-unit>
<trans-unit id="1325095699053123251" datatype="html"> <trans-unit id="1325095699053123251" datatype="html">
<source>The project has been initiated by</source> <source>The project has been initiated by</source>
<target state="new">The project has been initiated by</target> <target state="translated">Projekt został zainicjowany przez</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/about/overview/about-overview-page.html</context>
<context context-type="linenumber">40</context> <context context-type="linenumber">40</context>
@ -7556,7 +7556,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5004550577313573215" datatype="html"> <trans-unit id="5004550577313573215" datatype="html">
<source>Total amount</source> <source>Total amount</source>
<target state="new">Total amount</target> <target state="translated">Wartość portfela</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">95</context>
@ -7898,7 +7898,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.feeRatioTotalInvestmentVolume" datatype="html"> <trans-unit id="rule.feeRatioTotalInvestmentVolume" datatype="html">
<source>Fee Ratio</source> <source>Fee Ratio</source>
<target state="new">Fee Ratio</target> <target state="translated">Wskaźnik opłat</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">152</context> <context context-type="linenumber">152</context>
@ -8333,7 +8333,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.accountClusterRisk.category" datatype="html"> <trans-unit id="rule.accountClusterRisk.category" datatype="html">
<source>Account Cluster Risks</source> <source>Account Cluster Risks</source>
<target state="new">Account Cluster Risks</target> <target state="translated">Ryzyka skupienia w obrębie rachunków</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">14</context>
@ -8341,7 +8341,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.assetClassClusterRisk.category" datatype="html"> <trans-unit id="rule.assetClassClusterRisk.category" datatype="html">
<source>Asset Class Cluster Risks</source> <source>Asset Class Cluster Risks</source>
<target state="new">Asset Class Cluster Risks</target> <target state="translated">Ryzyka skupienia w obrębie klas aktywów</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">39</context> <context context-type="linenumber">39</context>
@ -8349,7 +8349,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.currencyClusterRisk.category" datatype="html"> <trans-unit id="rule.currencyClusterRisk.category" datatype="html">
<source>Currency Cluster Risks</source> <source>Currency Cluster Risks</source>
<target state="new">Currency Cluster Risks</target> <target state="translated">Ryzyka koncentracji walutowej</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">83</context>
@ -8357,7 +8357,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRisk.category" datatype="html"> <trans-unit id="rule.economicMarketClusterRisk.category" datatype="html">
<source>Economic Market Cluster Risks</source> <source>Economic Market Cluster Risks</source>
<target state="new">Economic Market Cluster Risks</target> <target state="translated">Ryzyka skupienia w obrębie segmentów rynku gospodarczego</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">106</context>
@ -8373,7 +8373,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.fees.category" datatype="html"> <trans-unit id="rule.fees.category" datatype="html">
<source>Fees</source> <source>Fees</source>
<target state="new">Fees</target> <target state="translated">Opłaty</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">161</context> <context context-type="linenumber">161</context>
@ -8381,7 +8381,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidity.category" datatype="html"> <trans-unit id="rule.liquidity.category" datatype="html">
<source>Liquidity</source> <source>Liquidity</source>
<target state="new">Liquidity</target> <target state="translated">Płynność</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">70</context> <context context-type="linenumber">70</context>
@ -8389,7 +8389,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower" datatype="html"> <trans-unit id="rule.liquidityBuyingPower" datatype="html">
<source>Buying Power</source> <source>Buying Power</source>
<target state="new">Buying Power</target> <target state="translated">Siła nabywcza</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -8413,7 +8413,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.liquidityBuyingPower.true" datatype="html"> <trans-unit id="rule.liquidityBuyingPower.true" datatype="html">
<source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source> <source>Your buying power exceeds ${thresholdMin} ${baseCurrency}</source>
<target state="new">Your buying power exceeds ${thresholdMin} ${baseCurrency}</target> <target state="translated">Twoja siła nabywcza przekracza ${thresholdMin} ${baseCurrency}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">80</context> <context context-type="linenumber">80</context>
@ -8421,7 +8421,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRisk.category" datatype="html"> <trans-unit id="rule.regionalMarketClusterRisk.category" datatype="html">
<source>Regional Market Cluster Risks</source> <source>Regional Market Cluster Risks</source>
<target state="new">Regional Market Cluster Risks</target> <target state="translated">Ryzyka skupienia w obrębie regionów rynkowych</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">163</context> <context context-type="linenumber">163</context>
@ -8437,7 +8437,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskDevelopedMarkets" datatype="html">
<source>Developed Markets</source> <source>Developed Markets</source>
<target state="new">Developed Markets</target> <target state="translated">Rynki rozwinięte</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">109</context> <context context-type="linenumber">109</context>
@ -8469,7 +8469,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.economicMarketClusterRiskEmergingMarkets" datatype="html"> <trans-unit id="rule.economicMarketClusterRiskEmergingMarkets" datatype="html">
<source>Emerging Markets</source> <source>Emerging Markets</source>
<target state="new">Emerging Markets</target> <target state="translated">Rynki wschodzące</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">127</context> <context context-type="linenumber">127</context>
@ -8581,7 +8581,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskEurope" datatype="html">
<source>Europe</source> <source>Europe</source>
<target state="new">Europe</target> <target state="translated">Europa</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
@ -8613,7 +8613,7 @@
</trans-unit> </trans-unit>
<trans-unit id="rule.regionalMarketClusterRiskJapan" datatype="html"> <trans-unit id="rule.regionalMarketClusterRiskJapan" datatype="html">
<source>Japan</source> <source>Japan</source>
<target state="new">Japan</target> <target state="translated">Japonia</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context> <context context-type="sourcefile">apps/client/src/app/pages/i18n/i18n-page.html</context>
<context context-type="linenumber">209</context> <context context-type="linenumber">209</context>

2
libs/ui/src/lib/benchmark/benchmark-detail-dialog/interfaces/interfaces.ts

@ -3,7 +3,7 @@ import { ColorScheme } from '@ghostfolio/common/types';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
export interface BenchmarkDetailDialogParams { export interface BenchmarkDetailDialogParams {
colorScheme: ColorScheme; colorScheme?: ColorScheme;
dataSource: DataSource; dataSource: DataSource;
deviceType: string; deviceType: string;
locale: string; locale: string;

16
libs/ui/src/lib/benchmark/benchmark.component.html

@ -15,7 +15,7 @@
<div class="text-truncate"> <div class="text-truncate">
{{ element?.name }} {{ element?.name }}
</div> </div>
@if (showSymbol) { @if (showSymbol()) {
<div> <div>
<small class="text-muted">{{ element?.symbol }}</small> <small class="text-muted">{{ element?.symbol }}</small>
</div> </div>
@ -98,7 +98,7 @@
@if (element?.performances?.allTimeHigh?.date) { @if (element?.performances?.allTimeHigh?.date) {
<gf-value <gf-value
[isDate]="true" [isDate]="true"
[locale]="locale" [locale]="locale()"
[value]="element?.performances?.allTimeHigh?.date" [value]="element?.performances?.allTimeHigh?.date"
/> />
} }
@ -123,7 +123,7 @@
<gf-value <gf-value
class="d-inline-block justify-content-end" class="d-inline-block justify-content-end"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale()"
[ngClass]="{ [ngClass]="{
'text-danger': 'text-danger':
element?.performances?.allTimeHigh?.performancePercent < 0, element?.performances?.allTimeHigh?.performancePercent < 0,
@ -150,7 +150,7 @@
<ng-container matColumnDef="actions" stickyEnd> <ng-container matColumnDef="actions" stickyEnd>
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th> <th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th>
<td *matCellDef="let element" class="px-1 text-center" mat-cell> <td *matCellDef="let element" class="px-1 text-center" mat-cell>
@if (hasPermissionToDeleteItem) { @if (hasPermissionToDeleteItem()) {
<button <button
class="mx-1 no-min-width px-2" class="mx-1 no-min-width px-2"
mat-button mat-button
@ -163,7 +163,7 @@
<mat-menu #benchmarkMenu="matMenu" xPosition="before"> <mat-menu #benchmarkMenu="matMenu" xPosition="before">
<button <button
mat-menu-item mat-menu-item
[disabled]="!hasPermissionToDeleteItem" [disabled]="!hasPermissionToDeleteItem()"
(click)=" (click)="
onDeleteItem({ onDeleteItem({
dataSource: element.dataSource, dataSource: element.dataSource,
@ -180,9 +180,9 @@
</td> </td>
</ng-container> </ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumns()" mat-header-row></tr>
<tr <tr
*matRowDef="let row; columns: displayedColumns" *matRowDef="let row; columns: displayedColumns()"
class="cursor-pointer" class="cursor-pointer"
mat-row mat-row
(click)=" (click)="
@ -204,7 +204,7 @@
width: '100%' width: '100%'
}" }"
/> />
} @else if (benchmarks?.length === 0) { } @else if (benchmarks()?.length === 0) {
<div class="p-3 text-center text-muted"> <div class="p-3 text-center text-muted">
<small i18n>No data available</small> <small i18n>No data available</small>
</div> </div>

129
libs/ui/src/lib/benchmark/benchmark.component.ts

@ -16,13 +16,15 @@ import {
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter, DestroyRef,
Input, computed,
OnChanges, effect,
OnDestroy, inject,
Output, input,
ViewChild output,
viewChild
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
@ -34,7 +36,6 @@ import { addIcons } from 'ionicons';
import { ellipsisHorizontal, trashOutline } from 'ionicons/icons'; import { ellipsisHorizontal, trashOutline } from 'ionicons/icons';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject, takeUntil } from 'rxjs';
import { translate } from '../i18n'; import { translate } from '../i18n';
import { GfTrendIndicatorComponent } from '../trend-indicator/trend-indicator.component'; import { GfTrendIndicatorComponent } from '../trend-indicator/trend-indicator.component';
@ -61,41 +62,58 @@ import { BenchmarkDetailDialogParams } from './benchmark-detail-dialog/interface
styleUrls: ['./benchmark.component.scss'], styleUrls: ['./benchmark.component.scss'],
templateUrl: './benchmark.component.html' templateUrl: './benchmark.component.html'
}) })
export class GfBenchmarkComponent implements OnChanges, OnDestroy { export class GfBenchmarkComponent {
@Input() benchmarks: Benchmark[]; public readonly benchmarks = input.required<Benchmark[]>();
@Input() deviceType: string; public readonly deviceType = input.required<string>();
@Input() hasPermissionToDeleteItem: boolean; public readonly hasPermissionToDeleteItem = input<boolean>();
@Input() locale = getLocale(); public readonly locale = input(getLocale());
@Input() showSymbol = true; public readonly showSymbol = input(true);
@Input() user: User; public readonly user = input<User>();
@Output() itemDeleted = new EventEmitter<AssetProfileIdentifier>(); public readonly itemDeleted = output<AssetProfileIdentifier>();
@ViewChild(MatSort) sort: MatSort; protected readonly sort = viewChild(MatSort);
public dataSource = new MatTableDataSource<Benchmark>([]); protected readonly dataSource = new MatTableDataSource<Benchmark>([]);
public displayedColumns = [ protected readonly displayedColumns = computed(() => {
return [
'name', 'name',
...(this.user()?.settings?.isExperimentalFeatures
? ['trend50d', 'trend200d']
: []),
'date', 'date',
'change', 'change',
'marketCondition', 'marketCondition',
'actions' 'actions'
]; ];
public isLoading = true; });
public isNumber = isNumber; protected isLoading = true;
public resolveMarketCondition = resolveMarketCondition; protected readonly isNumber = isNumber;
public translate = translate; protected readonly resolveMarketCondition = resolveMarketCondition;
protected readonly translate = translate;
private unsubscribeSubject = new Subject<void>();
private readonly destroyRef = inject(DestroyRef);
public constructor( private readonly dialog = inject(MatDialog);
private dialog: MatDialog, private readonly notificationService = inject(NotificationService);
private notificationService: NotificationService, private readonly route = inject(ActivatedRoute);
private route: ActivatedRoute, private readonly router = inject(Router);
private router: Router
) { public constructor() {
effect(() => {
const benchmarks = this.benchmarks();
if (benchmarks) {
this.dataSource.data = benchmarks;
this.dataSource.sortingDataAccessor = getLowercase;
this.dataSource.sort = this.sort() ?? null;
this.isLoading = false;
}
});
this.route.queryParams this.route.queryParams
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((params) => { .subscribe((params) => {
if ( if (
params['benchmarkDetailDialog'] && params['benchmarkDetailDialog'] &&
@ -112,30 +130,7 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
addIcons({ ellipsisHorizontal, trashOutline }); addIcons({ ellipsisHorizontal, trashOutline });
} }
public ngOnChanges() { protected onDeleteItem({ dataSource, symbol }: AssetProfileIdentifier) {
if (this.benchmarks) {
this.dataSource.data = this.benchmarks;
this.dataSource.sortingDataAccessor = getLowercase;
this.dataSource.sort = this.sort;
this.isLoading = false;
}
if (this.user?.settings?.isExperimentalFeatures) {
this.displayedColumns = [
'name',
'trend50d',
'trend200d',
'date',
'change',
'marketCondition',
'actions'
];
}
}
public onDeleteItem({ dataSource, symbol }: AssetProfileIdentifier) {
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
this.itemDeleted.emit({ dataSource, symbol }); this.itemDeleted.emit({ dataSource, symbol });
@ -145,17 +140,15 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
}); });
} }
public onOpenBenchmarkDialog({ dataSource, symbol }: AssetProfileIdentifier) { protected onOpenBenchmarkDialog({
dataSource,
symbol
}: AssetProfileIdentifier) {
this.router.navigate([], { this.router.navigate([], {
queryParams: { dataSource, symbol, benchmarkDetailDialog: true } queryParams: { dataSource, symbol, benchmarkDetailDialog: true }
}); });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private openBenchmarkDetailDialog({ private openBenchmarkDetailDialog({
dataSource, dataSource,
symbol symbol
@ -167,17 +160,17 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
data: { data: {
dataSource, dataSource,
symbol, symbol,
colorScheme: this.user?.settings?.colorScheme, colorScheme: this.user()?.settings?.colorScheme,
deviceType: this.deviceType, deviceType: this.deviceType(),
locale: this.locale locale: this.locale()
}, },
height: this.deviceType === 'mobile' ? '98vh' : undefined, height: this.deviceType() === 'mobile' ? '98vh' : undefined,
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });

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

@ -193,7 +193,7 @@
</table> </table>
</div> </div>
<mat-paginator class="d-none" [pageSize]="pageSize" /> <mat-paginator class="d-none" [pageSize]="pageSize()" />
@if (isLoading()) { @if (isLoading()) {
<ngx-skeleton-loader <ngx-skeleton-loader
@ -206,7 +206,7 @@
/> />
} }
@if (dataSource.data.length > pageSize && !isLoading()) { @if (dataSource.data.length > pageSize() && !isLoading()) {
<div class="my-3 text-center"> <div class="my-3 text-center">
<button mat-stroked-button (click)="onShowAllHoldings()"> <button mat-stroked-button (click)="onShowAllHoldings()">
<ng-container i18n>Show all</ng-container> <ng-container i18n>Show all</ng-container>

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

@ -9,12 +9,11 @@ import {
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
Input,
Output,
computed, computed,
effect, effect,
input, input,
model,
output,
viewChild viewChild
} from '@angular/core'; } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -47,17 +46,17 @@ import { GfValueComponent } from '../value/value.component';
templateUrl: './holdings-table.component.html' templateUrl: './holdings-table.component.html'
}) })
export class GfHoldingsTableComponent { export class GfHoldingsTableComponent {
@Input() pageSize = Number.MAX_SAFE_INTEGER;
@Output() holdingClicked = new EventEmitter<AssetProfileIdentifier>();
public readonly hasPermissionToOpenDetails = input(true); public readonly hasPermissionToOpenDetails = input(true);
public readonly hasPermissionToShowQuantities = input(true); public readonly hasPermissionToShowQuantities = input(true);
public readonly hasPermissionToShowValues = input(true); public readonly hasPermissionToShowValues = input(true);
public readonly holdings = input.required<PortfolioPosition[]>(); public readonly holdings = input.required<PortfolioPosition[]>();
public readonly locale = input(getLocale()); public readonly locale = input(getLocale());
public readonly paginator = viewChild.required(MatPaginator); public readonly pageSize = model(Number.MAX_SAFE_INTEGER);
public readonly sort = viewChild.required(MatSort);
public readonly holdingClicked = output<AssetProfileIdentifier>();
protected readonly paginator = viewChild.required(MatPaginator);
protected readonly sort = viewChild.required(MatSort);
protected readonly dataSource = new MatTableDataSource<PortfolioPosition>([]); protected readonly dataSource = new MatTableDataSource<PortfolioPosition>([]);
@ -106,8 +105,7 @@ export class GfHoldingsTableComponent {
protected canShowDetails(holding: PortfolioPosition): boolean { protected canShowDetails(holding: PortfolioPosition): boolean {
return ( return (
this.hasPermissionToOpenDetails() && this.hasPermissionToOpenDetails() &&
!!holding.assetSubClass && !this.ignoreAssetSubClasses.includes(holding.assetProfile.assetSubClass)
!this.ignoreAssetSubClasses.includes(holding.assetSubClass)
); );
} }
@ -119,7 +117,7 @@ export class GfHoldingsTableComponent {
} }
protected onShowAllHoldings() { protected onShowAllHoldings() {
this.pageSize = Number.MAX_SAFE_INTEGER; this.pageSize.set(Number.MAX_SAFE_INTEGER);
setTimeout(() => { setTimeout(() => {
this.dataSource.paginator = this.paginator(); this.dataSource.paginator = this.paginator();

Loading…
Cancel
Save