Browse Source

Merge branch 'main' of https://github.com/yksolanki9/ghostfolio into feature/add-accounts-import-export

pull/1635/head
yksolanki9 3 years ago
parent
commit
f995fabbf7
  1. 27
      CHANGELOG.md
  2. 2
      README.md
  3. 3
      apps/api/src/app/account/account.controller.ts
  4. 20
      apps/api/src/app/app.module.ts
  5. 1
      apps/api/src/app/import/import.controller.ts
  6. 17
      apps/api/src/app/order/order.service.ts
  7. 2
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  8. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts
  9. 1
      apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts
  10. 1
      apps/api/src/app/portfolio/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  11. 1
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  12. 1
      apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts
  13. 1
      apps/api/src/app/portfolio/portfolio-calculator.ts
  14. 7
      apps/api/src/app/portfolio/portfolio.controller.ts
  15. 57
      apps/api/src/app/portfolio/portfolio.service.ts
  16. 3
      apps/api/src/app/user/user.controller.ts
  17. 22
      apps/api/src/app/user/user.service.ts
  18. 1
      apps/api/src/interceptors/redact-values-in-response.interceptor.ts
  19. 2
      apps/api/src/services/configuration.service.ts
  20. 18
      apps/client/.browserslistrc
  21. 5
      apps/client/project.json
  22. 3
      apps/client/src/app/app-routing.module.ts
  23. 31
      apps/client/src/app/app.component.ts
  24. 16
      apps/client/src/app/app.module.ts
  25. 2
      apps/client/src/app/components/access-table/access-table.component.ts
  26. 6
      apps/client/src/app/components/access-table/access-table.module.ts
  27. 5
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  28. 4
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.module.ts
  29. 2
      apps/client/src/app/components/accounts-table/accounts-table.component.ts
  30. 8
      apps/client/src/app/components/accounts-table/accounts-table.module.ts
  31. 6
      apps/client/src/app/components/admin-jobs/admin-jobs.module.ts
  32. 2
      apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
  33. 7
      apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts
  34. 8
      apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
  35. 4
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  36. 8
      apps/client/src/app/components/admin-market-data/admin-market-data.module.ts
  37. 5
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  38. 8
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts
  39. 2
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  40. 8
      apps/client/src/app/components/admin-overview/admin-overview.module.ts
  41. 4
      apps/client/src/app/components/admin-users/admin-users.module.ts
  42. 2
      apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts
  43. 2
      apps/client/src/app/components/dialog-footer/dialog-footer.module.ts
  44. 2
      apps/client/src/app/components/dialog-header/dialog-header.module.ts
  45. 2
      apps/client/src/app/components/header/header.component.ts
  46. 4
      apps/client/src/app/components/header/header.module.ts
  47. 2
      apps/client/src/app/components/home-holdings/home-holdings.component.ts
  48. 4
      apps/client/src/app/components/home-holdings/home-holdings.module.ts
  49. 8
      apps/client/src/app/components/home-summary/home-summary.component.ts
  50. 2
      apps/client/src/app/components/home-summary/home-summary.module.ts
  51. 7
      apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts
  52. 10
      apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.module.ts
  53. 15
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  54. 20
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
  55. 6
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts
  56. 2
      apps/client/src/app/components/position/position.module.ts
  57. 2
      apps/client/src/app/components/positions/positions.module.ts
  58. 4
      apps/client/src/app/components/rules/rules.module.ts
  59. 1
      apps/client/src/app/components/subscription-interstitial-dialog/interfaces/interfaces.ts
  60. 25
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts
  61. 42
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html
  62. 21
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.module.ts
  63. 11
      apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.scss
  64. 2
      apps/client/src/app/components/toggle/toggle.module.ts
  65. 8
      apps/client/src/app/core/http-response.interceptor.ts
  66. 4
      apps/client/src/app/pages/about/about-page.module.ts
  67. 2
      apps/client/src/app/pages/about/changelog/changelog-page.module.ts
  68. 16
      apps/client/src/app/pages/account/account-page.component.ts
  69. 14
      apps/client/src/app/pages/account/account-page.module.ts
  70. 7
      apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.component.ts
  71. 10
      apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.module.ts
  72. 2
      apps/client/src/app/pages/accounts/accounts-page.component.ts
  73. 2
      apps/client/src/app/pages/accounts/accounts-page.module.ts
  74. 7
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts
  75. 12
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts
  76. 8
      apps/client/src/app/pages/admin/admin-page.module.ts
  77. 2
      apps/client/src/app/pages/blog/2022/11/black-friday-2022/black-friday-2022-page.module.ts
  78. 2
      apps/client/src/app/pages/blog/2022/12/the-importance-of-tracking-your-personal-finances/the-importance-of-tracking-your-personal-finances-page.module.ts
  79. 2
      apps/client/src/app/pages/blog/blog-page.module.ts
  80. 2
      apps/client/src/app/pages/faq/faq-page.module.ts
  81. 4
      apps/client/src/app/pages/features/features-page.module.ts
  82. 2
      apps/client/src/app/pages/home/home-page.module.ts
  83. 8
      apps/client/src/app/pages/landing/landing-page.html
  84. 4
      apps/client/src/app/pages/landing/landing-page.module.ts
  85. 5
      apps/client/src/app/pages/landing/landing-page.scss
  86. 2
      apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
  87. 4
      apps/client/src/app/pages/portfolio/activities/activities-page.module.ts
  88. 7
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
  89. 8
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
  90. 16
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.module.ts
  91. 9
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
  92. 8
      apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.module.ts
  93. 9
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  94. 6
      apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts
  95. 9
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  96. 2
      apps/client/src/app/pages/portfolio/analysis/analysis-page.module.ts
  97. 2
      apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts
  98. 2
      apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts
  99. 2
      apps/client/src/app/pages/portfolio/portfolio-page.module.ts
  100. 159
      apps/client/src/app/pages/pricing/pricing-page.html

27
CHANGELOG.md

@ -5,15 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased ## 1.231.0 - 2023-02-04
### Added ### Added
- Added the dividend and fees to the position detail dialog
- Added support to link a (wealth) item to an account
### Changed
- Relaxed the validation rule of the _Redis_ host environment variable (`REDIS_HOST`)
- Improved the language localization for German (`de`)
- Eliminated `angular-material-css-vars`
- Upgraded `angular` from version `14.2.0` to `15.1.2`
- Upgraded `Nx` from version `15.0.13` to `15.6.3`
## 1.230.0 - 2023-01-29
### Added
- Added an interstitial for the subscription
- Added _SourceForge_ to the _As seen in_ section on the landing page
- Added a quote to the blog post _Ghostfolio auf Sackgeld.com vorgestellt_ - Added a quote to the blog post _Ghostfolio auf Sackgeld.com vorgestellt_
### Changed ### Changed
- Improved the unit format (`%`) in the global heat map component of the public page
- Improved the pricing page
- Upgraded `Node.js` from version `16` to `18` (`Dockerfile`) - Upgraded `Node.js` from version `16` to `18` (`Dockerfile`)
- Upgraded `prisma` from version `4.8.0` to `4.9.0`
### Fixed
- Fixed the click of unknown accounts in the portfolio proportion chart component
- Fixed an issue with `value` in the value redaction interceptor for the impersonation mode
## 1.229.0 - 2023-01-21 ## 1.229.0 - 2023-01-21

2
README.md

@ -40,7 +40,7 @@ Ghostfolio is for you if you are...
- 🧘 into minimalism - 🧘 into minimalism
- 🧺 caring about diversifying your financial resources - 🧺 caring about diversifying your financial resources
- 🆓 interested in financial independence - 🆓 interested in financial independence
- 🙅 saying no to spreadsheets in 2023 - 🙅 saying no to spreadsheets
- 😎 still reading this list - 😎 still reading this list
## Features ## Features

3
apps/api/src/app/account/account.controller.ts

@ -37,8 +37,7 @@ export class AccountController {
private readonly accountService: AccountService, private readonly accountService: AccountService,
private readonly impersonationService: ImpersonationService, private readonly impersonationService: ImpersonationService,
private readonly portfolioService: PortfolioService, private readonly portfolioService: PortfolioService,
@Inject(REQUEST) private readonly request: RequestWithUser, @Inject(REQUEST) private readonly request: RequestWithUser
private readonly userService: UserService
) {} ) {}
@Delete(':id') @Delete(':id')

20
apps/api/src/app/app.module.ts

@ -1,24 +1,23 @@
import { join } from 'path'; import { join } from 'path';
import { AuthDeviceModule } from '@ghostfolio/api/app/auth-device/auth-device.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module';
import { CronService } from '@ghostfolio/api/services/cron.service';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma.module';
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
import { BullModule } from '@nestjs/bull'; import { BullModule } from '@nestjs/bull';
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'; import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule'; import { ScheduleModule } from '@nestjs/schedule';
import { ServeStaticModule } from '@nestjs/serve-static'; import { ServeStaticModule } from '@nestjs/serve-static';
import { ConfigurationModule } from '../services/configuration.module';
import { CronService } from '../services/cron.service';
import { DataGatheringModule } from '../services/data-gathering.module';
import { DataProviderModule } from '../services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '../services/exchange-rate-data.module';
import { PrismaModule } from '../services/prisma.module';
import { TwitterBotModule } from '../services/twitter-bot/twitter-bot.module';
import { AccessModule } from './access/access.module'; import { AccessModule } from './access/access.module';
import { AccountModule } from './account/account.module'; import { AccountModule } from './account/account.module';
import { AdminModule } from './admin/admin.module'; import { AdminModule } from './admin/admin.module';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AuthDeviceModule } from './auth-device/auth-device.module';
import { AuthModule } from './auth/auth.module'; import { AuthModule } from './auth/auth.module';
import { BenchmarkModule } from './benchmark/benchmark.module'; import { BenchmarkModule } from './benchmark/benchmark.module';
import { CacheModule } from './cache/cache.module'; import { CacheModule } from './cache/cache.module';
@ -30,6 +29,7 @@ import { InfoModule } from './info/info.module';
import { LogoModule } from './logo/logo.module'; import { LogoModule } from './logo/logo.module';
import { OrderModule } from './order/order.module'; import { OrderModule } from './order/order.module';
import { PortfolioModule } from './portfolio/portfolio.module'; import { PortfolioModule } from './portfolio/portfolio.module';
import { RedisCacheModule } from './redis-cache/redis-cache.module';
import { SubscriptionModule } from './subscription/subscription.module'; import { SubscriptionModule } from './subscription/subscription.module';
import { SymbolModule } from './symbol/symbol.module'; import { SymbolModule } from './symbol/symbol.module';
import { UserModule } from './user/user.module'; import { UserModule } from './user/user.module';
@ -45,7 +45,7 @@ import { UserModule } from './user/user.module';
BullModule.forRoot({ BullModule.forRoot({
redis: { redis: {
host: process.env.REDIS_HOST, host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT, 10), port: parseInt(process.env.REDIS_PORT ?? '6379', 10),
password: process.env.REDIS_PASSWORD password: process.env.REDIS_PASSWORD
} }
}), }),

1
apps/api/src/app/import/import.controller.ts

@ -21,7 +21,6 @@ import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { isEmpty } from 'lodash';
import { ImportDataDto } from './import-data.dto'; import { ImportDataDto } from './import-data.dto';
import { ImportService } from './import.service'; import { ImportService } from './import.service';

17
apps/api/src/app/order/order.service.ts

@ -76,22 +76,20 @@ export class OrderService {
userId: string; userId: string;
} }
): Promise<Order> { ): Promise<Order> {
const defaultAccount = ( let Account;
await this.accountService.getAccounts(data.userId)
).find((account) => {
return account.isDefault === true;
});
const tags = data.tags ?? [];
let Account = { if (data.accountId) {
Account = {
connect: { connect: {
id_userId: { id_userId: {
userId: data.userId, userId: data.userId,
id: data.accountId ?? defaultAccount?.id id: data.accountId
} }
} }
}; };
}
const tags = data.tags ?? [];
if (data.type === 'ITEM') { if (data.type === 'ITEM') {
const assetClass = data.assetClass; const assetClass = data.assetClass;
@ -101,7 +99,6 @@ export class OrderService {
const id = uuidv4(); const id = uuidv4();
const name = data.SymbolProfile.connectOrCreate.create.symbol; const name = data.SymbolProfile.connectOrCreate.create.symbol;
Account = undefined;
data.id = id; data.id = id;
data.SymbolProfile.connectOrCreate.create.assetClass = assetClass; data.SymbolProfile.connectOrCreate.create.assetClass = assetClass;
data.SymbolProfile.connectOrCreate.create.assetSubClass = assetSubClass; data.SymbolProfile.connectOrCreate.create.assetSubClass = assetSubClass;

2
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -7,6 +7,8 @@ import { Tag } from '@prisma/client';
export interface PortfolioPositionDetail { export interface PortfolioPositionDetail {
averagePrice: number; averagePrice: number;
dividendInBaseCurrency: number;
feeInBaseCurrency: number;
firstBuyDate: string; firstBuyDate: string;
grossPerformance: number; grossPerformance: number;
grossPerformancePercent: number; grossPerformancePercent: number;

1
apps/api/src/app/portfolio/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
averagePrice: new Big('0'), averagePrice: new Big('0'),
currency: 'CHF', currency: 'CHF',
dataSource: 'YAHOO', dataSource: 'YAHOO',
fee: new Big('3.2'),
firstBuyDate: '2021-11-22', firstBuyDate: '2021-11-22',
grossPerformance: new Big('-12.6'), grossPerformance: new Big('-12.6'),
grossPerformancePercentage: new Big('-0.0440867739678096571'), grossPerformancePercentage: new Big('-0.0440867739678096571'),

1
apps/api/src/app/portfolio/portfolio-calculator-baln-buy.spec.ts

@ -71,6 +71,7 @@ describe('PortfolioCalculator', () => {
averagePrice: new Big('136.6'), averagePrice: new Big('136.6'),
currency: 'CHF', currency: 'CHF',
dataSource: 'YAHOO', dataSource: 'YAHOO',
fee: new Big('1.55'),
firstBuyDate: '2021-11-30', firstBuyDate: '2021-11-30',
grossPerformance: new Big('24.6'), grossPerformance: new Big('24.6'),
grossPerformancePercentage: new Big('0.09004392386530014641'), grossPerformancePercentage: new Big('0.09004392386530014641'),

1
apps/api/src/app/portfolio/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
averagePrice: new Big('320.43'), averagePrice: new Big('320.43'),
currency: 'CHF', currency: 'CHF',
dataSource: 'YAHOO', dataSource: 'YAHOO',
fee: new Big('0'),
firstBuyDate: '2015-01-01', firstBuyDate: '2015-01-01',
grossPerformance: new Big('27172.74'), grossPerformance: new Big('27172.74'),
grossPerformancePercentage: new Big('42.40043067128546016291'), grossPerformancePercentage: new Big('42.40043067128546016291'),

1
apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -82,6 +82,7 @@ describe('PortfolioCalculator', () => {
averagePrice: new Big('75.80'), averagePrice: new Big('75.80'),
currency: 'CHF', currency: 'CHF',
dataSource: 'YAHOO', dataSource: 'YAHOO',
fee: new Big('4.25'),
firstBuyDate: '2022-03-07', firstBuyDate: '2022-03-07',
grossPerformance: new Big('21.93'), grossPerformance: new Big('21.93'),
grossPerformancePercentage: new Big('0.14465699208443271768'), grossPerformancePercentage: new Big('0.14465699208443271768'),

1
apps/api/src/app/portfolio/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -102,6 +102,7 @@ describe('PortfolioCalculator', () => {
averagePrice: new Big('0'), averagePrice: new Big('0'),
currency: 'CHF', currency: 'CHF',
dataSource: 'YAHOO', dataSource: 'YAHOO',
fee: new Big('0'),
firstBuyDate: '2022-03-07', firstBuyDate: '2022-03-07',
grossPerformance: new Big('19.86'), grossPerformance: new Big('19.86'),
grossPerformancePercentage: new Big('0.13100263852242744063'), grossPerformancePercentage: new Big('0.13100263852242744063'),

1
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -431,6 +431,7 @@ export class PortfolioCalculator {
: item.investment.div(item.quantity), : item.investment.div(item.quantity),
currency: item.currency, currency: item.currency,
dataSource: item.dataSource, dataSource: item.dataSource,
fee: item.fee,
firstBuyDate: item.firstBuyDate, firstBuyDate: item.firstBuyDate,
grossPerformance: !hasErrors ? grossPerformance ?? null : null, grossPerformance: !hasErrors ? grossPerformance ?? null : null,
grossPerformancePercentage: !hasErrors grossPerformancePercentage: !hasErrors

7
apps/api/src/app/portfolio/portfolio.controller.ts

@ -131,7 +131,8 @@ export class PortfolioController {
portfolioPosition.investment / totalInvestment; portfolioPosition.investment / totalInvestment;
portfolioPosition.netPerformance = null; portfolioPosition.netPerformance = null;
portfolioPosition.quantity = null; portfolioPosition.quantity = null;
portfolioPosition.value = portfolioPosition.value / totalValue; portfolioPosition.valueInPercentage =
portfolioPosition.value / totalValue;
} }
for (const [name, { current, original }] of Object.entries(accounts)) { for (const [name, { current, original }] of Object.entries(accounts)) {
@ -322,7 +323,7 @@ export class PortfolioController {
totalInvestment: new Big(totalInvestment) totalInvestment: new Big(totalInvestment)
.div(performanceInformation.performance.totalInvestment) .div(performanceInformation.performance.totalInvestment)
.toNumber(), .toNumber(),
value: new Big(value) valueInPercentage: new Big(value)
.div(performanceInformation.performance.currentValue) .div(performanceInformation.performance.currentValue)
.toNumber() .toNumber()
}; };
@ -437,7 +438,7 @@ export class PortfolioController {
sectors: hasDetails ? portfolioPosition.sectors : [], sectors: hasDetails ? portfolioPosition.sectors : [],
symbol: portfolioPosition.symbol, symbol: portfolioPosition.symbol,
url: portfolioPosition.url, url: portfolioPosition.url,
value: portfolioPosition.value / totalValue valueInPercentage: portfolioPosition.value / totalValue
}; };
} }

57
apps/api/src/app/portfolio/portfolio.service.ts

@ -24,7 +24,7 @@ import {
MAX_CHART_ITEMS, MAX_CHART_ITEMS,
UNKNOWN_KEY UNKNOWN_KEY
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper';
import { import {
Accounts, Accounts,
EnhancedSymbolProfile, EnhancedSymbolProfile,
@ -573,7 +573,6 @@ export class PortfolioService {
const cashPositions = await this.getCashPositions({ const cashPositions = await this.getCashPositions({
cashDetails, cashDetails,
userCurrency, userCurrency,
investment: totalInvestmentInBaseCurrency,
value: filteredValueInBaseCurrency value: filteredValueInBaseCurrency
}); });
@ -599,7 +598,6 @@ export class PortfolioService {
const cashPositions = await this.getCashPositions({ const cashPositions = await this.getCashPositions({
cashDetails, cashDetails,
userCurrency, userCurrency,
investment: totalInvestmentInBaseCurrency,
value: filteredValueInBaseCurrency value: filteredValueInBaseCurrency
}); });
@ -680,6 +678,8 @@ export class PortfolioService {
return { return {
tags, tags,
averagePrice: undefined, averagePrice: undefined,
dividendInBaseCurrency: undefined,
feeInBaseCurrency: undefined,
firstBuyDate: undefined, firstBuyDate: undefined,
grossPerformance: undefined, grossPerformance: undefined,
grossPerformancePercent: undefined, grossPerformancePercent: undefined,
@ -746,12 +746,23 @@ export class PortfolioService {
averagePrice, averagePrice,
currency, currency,
dataSource, dataSource,
fee,
firstBuyDate, firstBuyDate,
marketPrice, marketPrice,
quantity, quantity,
transactionCount transactionCount
} = position; } = position;
const dividendInBaseCurrency = getSum(
orders
.filter(({ type }) => {
return type === 'DIVIDEND';
})
.map(({ valueInBaseCurrency }) => {
return new Big(valueInBaseCurrency);
})
);
// Convert investment, gross and net performance to currency of user // Convert investment, gross and net performance to currency of user
const investment = this.exchangeRateDataService.toCurrency( const investment = this.exchangeRateDataService.toCurrency(
position.investment?.toNumber(), position.investment?.toNumber(),
@ -785,8 +796,8 @@ export class PortfolioService {
historicalDataArray.push({ historicalDataArray.push({
averagePrice: orders[0].unitPrice, averagePrice: orders[0].unitPrice,
date: firstBuyDate, date: firstBuyDate,
quantity: orders[0].quantity, marketPrice: orders[0].unitPrice,
value: orders[0].unitPrice quantity: orders[0].quantity
}); });
} }
@ -815,9 +826,9 @@ export class PortfolioService {
historicalDataArray.push({ historicalDataArray.push({
date, date,
marketPrice,
averagePrice: currentAveragePrice, averagePrice: currentAveragePrice,
quantity: currentQuantity, quantity: currentQuantity
value: marketPrice
}); });
maxPrice = Math.max(marketPrice ?? 0, maxPrice); maxPrice = Math.max(marketPrice ?? 0, maxPrice);
@ -838,6 +849,12 @@ export class PortfolioService {
tags, tags,
transactionCount, transactionCount,
averagePrice: averagePrice.toNumber(), averagePrice: averagePrice.toNumber(),
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(),
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
fee.toNumber(),
SymbolProfile.currency,
userCurrency
),
grossPerformancePercent: grossPerformancePercent:
position.grossPerformancePercentage?.toNumber(), position.grossPerformancePercentage?.toNumber(),
historicalData: historicalDataArray, historicalData: historicalDataArray,
@ -894,6 +911,8 @@ export class PortfolioService {
SymbolProfile, SymbolProfile,
tags, tags,
averagePrice: 0, averagePrice: 0,
dividendInBaseCurrency: 0,
feeInBaseCurrency: 0,
firstBuyDate: undefined, firstBuyDate: undefined,
grossPerformance: undefined, grossPerformance: undefined,
grossPerformancePercent: undefined, grossPerformancePercent: undefined,
@ -1209,12 +1228,10 @@ export class PortfolioService {
private async getCashPositions({ private async getCashPositions({
cashDetails, cashDetails,
investment,
userCurrency, userCurrency,
value value
}: { }: {
cashDetails: CashDetails; cashDetails: CashDetails;
investment: Big;
userCurrency: string; userCurrency: string;
value: Big; value: Big;
}) { }) {
@ -1692,6 +1709,14 @@ export class PortfolioService {
userId: string; userId: string;
withExcludedAccounts?: boolean; withExcludedAccounts?: boolean;
}) { }) {
const ordersOfTypeItem = await this.orderService.getOrders({
filters,
userCurrency,
userId,
withExcludedAccounts,
types: ['ITEM']
});
const accounts: PortfolioDetails['accounts'] = {}; const accounts: PortfolioDetails['accounts'] = {};
let currentAccounts: (Account & { let currentAccounts: (Account & {
@ -1722,10 +1747,18 @@ export class PortfolioService {
}); });
for (const account of currentAccounts) { for (const account of currentAccounts) {
const ordersByAccount = orders.filter(({ accountId }) => { let ordersByAccount = orders.filter(({ accountId }) => {
return accountId === account.id; return accountId === account.id;
}); });
const ordersOfTypeItemByAccount = ordersOfTypeItem.filter(
({ accountId }) => {
return accountId === account.id;
}
);
ordersByAccount = ordersByAccount.concat(ordersOfTypeItemByAccount);
accounts[account.id] = { accounts[account.id] = {
balance: account.balance, balance: account.balance,
currency: account.currency, currency: account.currency,
@ -1745,7 +1778,9 @@ export class PortfolioService {
for (const order of ordersByAccount) { for (const order of ordersByAccount) {
let currentValueOfSymbolInBaseCurrency = let currentValueOfSymbolInBaseCurrency =
order.quantity * order.quantity *
portfolioItemsNow[order.SymbolProfile.symbol]?.marketPrice ?? 0; (portfolioItemsNow[order.SymbolProfile.symbol]?.marketPrice ??
order.unitPrice ??
0);
let originalValueOfSymbolInBaseCurrency = let originalValueOfSymbolInBaseCurrency =
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice, order.quantity * order.unitPrice,

3
apps/api/src/app/user/user.controller.ts

@ -1,6 +1,4 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { PROPERTY_IS_USER_SIGNUP_ENABLED } from '@ghostfolio/common/config';
import { User, UserSettings } from '@ghostfolio/common/interfaces'; import { User, UserSettings } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types'; import type { RequestWithUser } from '@ghostfolio/common/types';
@ -31,7 +29,6 @@ import { UserService } from './user.service';
@Controller('user') @Controller('user')
export class UserController { export class UserController {
public constructor( public constructor(
private readonly configurationService: ConfigurationService,
private readonly jwtService: JwtService, private readonly jwtService: JwtService,
private readonly propertyService: PropertyService, private readonly propertyService: PropertyService,
@Inject(REQUEST) private readonly request: RequestWithUser, @Inject(REQUEST) private readonly request: RequestWithUser,

22
apps/api/src/app/user/user.service.ts

@ -97,6 +97,7 @@ export class UserService {
const { const {
accessToken, accessToken,
Account, Account,
Analytics,
authChallenge, authChallenge,
createdAt, createdAt,
id, id,
@ -107,7 +108,12 @@ export class UserService {
thirdPartyId, thirdPartyId,
updatedAt updatedAt
} = await this.prismaService.user.findUnique({ } = await this.prismaService.user.findUnique({
include: { Account: true, Settings: true, Subscription: true }, include: {
Account: true,
Analytics: true,
Settings: true,
Subscription: true
},
where: userWhereUniqueInput where: userWhereUniqueInput
}); });
@ -121,7 +127,8 @@ export class UserService {
role, role,
Settings, Settings,
thirdPartyId, thirdPartyId,
updatedAt updatedAt,
activityCount: Analytics?.activityCount
}; };
if (user?.Settings) { if (user?.Settings) {
@ -154,16 +161,23 @@ export class UserService {
(user.Settings.settings as UserSettings).viewMode = 'DEFAULT'; (user.Settings.settings as UserSettings).viewMode = 'DEFAULT';
} }
let currentPermissions = getPermissions(user.role);
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
user.subscription = user.subscription =
this.subscriptionService.getSubscription(Subscription); this.subscriptionService.getSubscription(Subscription);
}
let currentPermissions = getPermissions(user.role); if (
Analytics?.activityCount % 25 === 0 &&
user.subscription?.type === 'Basic'
) {
currentPermissions.push(permissions.enableSubscriptionInterstitial);
}
if (user.subscription?.type === 'Premium') { if (user.subscription?.type === 'Premium') {
currentPermissions.push(permissions.reportDataGlitch); currentPermissions.push(permissions.reportDataGlitch);
} }
}
if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) { if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) {
if (hasRole(user, Role.ADMIN)) { if (hasRole(user, Role.ADMIN)) {

1
apps/api/src/interceptors/redact-values-in-response.interceptor.ts

@ -35,6 +35,7 @@ export class RedactValuesInResponseInterceptor<T>
'balanceInBaseCurrency', 'balanceInBaseCurrency',
'comment', 'comment',
'convertedBalance', 'convertedBalance',
'dividendInBaseCurrency',
'fee', 'fee',
'feeInBaseCurrency', 'feeInBaseCurrency',
'filteredValueInBaseCurrency', 'filteredValueInBaseCurrency',

2
apps/api/src/services/configuration.service.ts

@ -42,7 +42,7 @@ export class ConfigurationService {
MAX_ITEM_IN_CACHE: num({ default: 9999 }), MAX_ITEM_IN_CACHE: num({ default: 9999 }),
PORT: port({ default: 3333 }), PORT: port({ default: 3333 }),
RAPID_API_API_KEY: str({ default: '' }), RAPID_API_API_KEY: str({ default: '' }),
REDIS_HOST: host({ default: 'localhost' }), REDIS_HOST: str({ default: 'localhost' }),
REDIS_PASSWORD: str({ default: '' }), REDIS_PASSWORD: str({ default: '' }),
REDIS_PORT: port({ default: 6379 }), REDIS_PORT: port({ default: 6379 }),
ROOT_URL: str({ default: 'http://localhost:4200' }), ROOT_URL: str({ default: 'http://localhost:4200' }),

18
apps/client/.browserslistrc

@ -1,18 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

5
apps/client/project.json

@ -65,7 +65,10 @@
"output": "./../assets/" "output": "./../assets/"
} }
], ],
"styles": ["apps/client/src/styles.scss"], "styles": [
"apps/client/src/styles/theme.scss",
"apps/client/src/styles.scss"
],
"scripts": ["node_modules/marked/marked.min.js"], "scripts": ["node_modules/marked/marked.min.js"],
"vendorChunk": true, "vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,

3
apps/client/src/app/app-routing.module.ts

@ -222,9 +222,8 @@ const routes: Routes = [
// Preload all lazy loaded modules with the attribute preload === true // Preload all lazy loaded modules with the attribute preload === true
{ {
anchorScrolling: 'enabled', anchorScrolling: 'enabled',
preloadingStrategy: ModulePreloadService, preloadingStrategy: ModulePreloadService
// enableTracing: true // <-- debugging purposes only // enableTracing: true // <-- debugging purposes only
relativeLinkResolution: 'legacy'
} }
) )
], ],

31
apps/client/src/app/app.component.ts

@ -1,26 +1,17 @@
import { DOCUMENT } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
Inject,
OnDestroy, OnDestroy,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { import { NavigationEnd, PRIMARY_OUTLET, Router } from '@angular/router';
ActivatedRoute,
NavigationEnd,
PRIMARY_OUTLET,
Router
} from '@angular/router';
import {
primaryColorHex,
secondaryColorHex,
warnColorHex
} from '@ghostfolio/common/config';
import { InfoItem, User } from '@ghostfolio/common/interfaces'; import { InfoItem, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { ColorScheme } from '@ghostfolio/common/types'; import { ColorScheme } from '@ghostfolio/common/types';
import { MaterialCssVarsService } from 'angular-material-css-vars';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators'; import { filter, takeUntil } from 'rxjs/operators';
@ -52,7 +43,7 @@ export class AppComponent implements OnDestroy, OnInit {
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private deviceService: DeviceDetectorService, private deviceService: DeviceDetectorService,
private materialCssVarsService: MaterialCssVarsService, @Inject(DOCUMENT) private document: Document,
private router: Router, private router: Router,
private title: Title, private title: Title,
private tokenStorageService: TokenStorageService, private tokenStorageService: TokenStorageService,
@ -126,16 +117,20 @@ export class AppComponent implements OnDestroy, OnInit {
? userPreferredColorScheme === 'DARK' ? userPreferredColorScheme === 'DARK'
: window.matchMedia('(prefers-color-scheme: dark)').matches; : window.matchMedia('(prefers-color-scheme: dark)').matches;
this.materialCssVarsService.setDarkTheme(isDarkTheme); this.toggleThemeStyleClass(isDarkTheme);
window.matchMedia('(prefers-color-scheme: dark)').addListener((event) => { window.matchMedia('(prefers-color-scheme: dark)').addListener((event) => {
if (!this.user?.settings.colorScheme) { if (!this.user?.settings.colorScheme) {
this.materialCssVarsService.setDarkTheme(event.matches); this.toggleThemeStyleClass(event.matches);
} }
}); });
}
this.materialCssVarsService.setPrimaryColor(primaryColorHex); private toggleThemeStyleClass(isDarkTheme: boolean) {
this.materialCssVarsService.setAccentColor(secondaryColorHex); if (isDarkTheme) {
this.materialCssVarsService.setWarnColor(warnColorHex); this.document.body.classList.add('is-dark-theme');
} else {
this.document.body.classList.remove('is-dark-theme');
}
} }
} }

16
apps/client/src/app/app.module.ts

@ -1,20 +1,19 @@
import { Platform } from '@angular/cdk/platform'; import { Platform } from '@angular/cdk/platform';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatChipsModule } from '@angular/material/chips';
import { import {
DateAdapter, DateAdapter,
MAT_DATE_FORMATS, MAT_DATE_FORMATS,
MAT_DATE_LOCALE, MAT_DATE_LOCALE,
MatNativeDateModule MatNativeDateModule
} from '@angular/material/core'; } from '@angular/material/core';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatLegacyAutocompleteModule as MatAutocompleteModule } from '@angular/material/legacy-autocomplete';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatLegacyChipsModule as MatChipsModule } from '@angular/material/legacy-chips';
import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar';
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServiceWorkerModule } from '@angular/service-worker'; import { ServiceWorkerModule } from '@angular/service-worker';
import { MaterialCssVarsModule } from 'angular-material-css-vars';
import { MarkdownModule } from 'ngx-markdown'; import { MarkdownModule } from 'ngx-markdown';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { NgxStripeModule, STRIPE_PUBLISHABLE_KEY } from 'ngx-stripe'; import { NgxStripeModule, STRIPE_PUBLISHABLE_KEY } from 'ngx-stripe';
@ -25,6 +24,7 @@ import { DateFormats } from './adapter/date-formats';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { GfHeaderModule } from './components/header/header.module'; import { GfHeaderModule } from './components/header/header.module';
import { GfSubscriptionInterstitialDialogModule } from './components/subscription-interstitial-dialog/subscription-interstitial-dialog.module';
import { authInterceptorProviders } from './core/auth.interceptor'; import { authInterceptorProviders } from './core/auth.interceptor';
import { httpResponseInterceptorProviders } from './core/http-response.interceptor'; import { httpResponseInterceptorProviders } from './core/http-response.interceptor';
import { LanguageService } from './core/language.service'; import { LanguageService } from './core/language.service';
@ -40,15 +40,11 @@ export function NgxStripeFactory(): string {
BrowserAnimationsModule, BrowserAnimationsModule,
BrowserModule, BrowserModule,
GfHeaderModule, GfHeaderModule,
GfSubscriptionInterstitialDialogModule,
HttpClientModule, HttpClientModule,
MarkdownModule.forRoot(), MarkdownModule.forRoot(),
MatAutocompleteModule, MatAutocompleteModule,
MatChipsModule, MatChipsModule,
MaterialCssVarsModule.forRoot({
darkThemeClass: 'is-dark-theme',
isAutoContrast: true,
lightThemeClass: 'is-light-theme'
}),
MatNativeDateModule, MatNativeDateModule,
MatSnackBarModule, MatSnackBarModule,
MatTooltipModule, MatTooltipModule,

2
apps/client/src/app/components/access-table/access-table.component.ts

@ -7,7 +7,7 @@ import {
OnInit, OnInit,
Output Output
} from '@angular/core'; } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table'; import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import { Access } from '@ghostfolio/common/interfaces'; import { Access } from '@ghostfolio/common/interfaces';

6
apps/client/src/app/components/access-table/access-table.module.ts

@ -1,8 +1,8 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatTableModule } from '@angular/material/table'; import { MatLegacyTableModule as MatTableModule } from '@angular/material/legacy-table';
import { AccessTableComponent } from './access-table.component'; import { AccessTableComponent } from './access-table.component';

5
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -6,7 +6,10 @@ import {
OnDestroy, OnDestroy,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { downloadAsFile } from '@ghostfolio/common/helper'; import { downloadAsFile } from '@ghostfolio/common/helper';

4
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module'; import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';

2
apps/client/src/app/components/accounts-table/accounts-table.component.ts

@ -9,8 +9,8 @@ import {
Output, Output,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Account as AccountModel } from '@prisma/client'; import { Account as AccountModel } from '@prisma/client';
import { get } from 'lodash'; import { get } from 'lodash';

8
apps/client/src/app/components/accounts-table/accounts-table.module.ts

@ -1,10 +1,9 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyTableModule as MatTableModule } from '@angular/material/legacy-table';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module'; import { GfSymbolIconModule } from '@ghostfolio/client/components/symbol-icon/symbol-icon.module';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';
@ -20,7 +19,6 @@ import { AccountsTableComponent } from './accounts-table.component';
GfSymbolIconModule, GfSymbolIconModule,
GfValueModule, GfValueModule,
MatButtonModule, MatButtonModule,
MatInputModule,
MatMenuModule, MatMenuModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule,

6
apps/client/src/app/components/admin-jobs/admin-jobs.module.ts

@ -1,9 +1,9 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { AdminJobsComponent } from './admin-jobs.component'; import { AdminJobsComponent } from './admin-jobs.component';

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

@ -7,7 +7,7 @@ import {
OnInit, OnInit,
Output Output
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { import {
DATE_FORMAT, DATE_FORMAT,

7
apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts

@ -6,7 +6,10 @@ import {
OnDestroy OnDestroy
} from '@angular/core'; } from '@angular/core';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { AdminService } from '@ghostfolio/client/services/admin.service'; import { AdminService } from '@ghostfolio/client/services/admin.service';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -36,7 +39,7 @@ export class MarketDataDetailDialog implements OnDestroy {
this.dateAdapter.setLocale(this.locale); this.dateAdapter.setLocale(this.locale);
} }
public onCancel(): void { public onCancel() {
this.dialogRef.close({ withRefresh: false }); this.dialogRef.close({ withRefresh: false });
} }

8
apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts

@ -1,11 +1,11 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MarketDataDetailDialog } from './market-data-detail-dialog.component'; import { MarketDataDetailDialog } from './market-data-detail-dialog.component';

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

@ -6,9 +6,9 @@ import {
OnInit, OnInit,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AdminService } from '@ghostfolio/client/services/admin.service'; import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';

8
apps/client/src/app/components/admin-market-data/admin-market-data.module.ts

@ -1,13 +1,13 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatLegacyTableModule as MatTableModule } from '@angular/material/legacy-table';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module'; import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module';
import { AdminMarketDataComponent } from './admin-market-data.component'; import { AdminMarketDataComponent } from './admin-market-data.component';
import { GfAssetProfileDialogModule } from './asset-profile-dialog/assset-profile-dialog.module'; import { GfAssetProfileDialogModule } from './asset-profile-dialog/asset-profile-dialog.module';
@NgModule({ @NgModule({
declarations: [AdminMarketDataComponent], declarations: [AdminMarketDataComponent],

5
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -7,7 +7,10 @@ import {
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
import { AdminService } from '@ghostfolio/client/services/admin.service'; import { AdminService } from '@ghostfolio/client/services/admin.service';
import { import {

8
apps/client/src/app/components/admin-market-data/asset-profile-dialog/assset-profile-dialog.module.ts → apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts

@ -2,10 +2,10 @@ import { TextFieldModule } from '@angular/cdk/text-field';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module'; import { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module';
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module'; import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';

2
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { MatLegacySlideToggleChange as MatSlideToggleChange } from '@angular/material/legacy-slide-toggle';
import { CacheService } from '@ghostfolio/client/services/cache.service'; import { CacheService } from '@ghostfolio/client/services/cache.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';

8
apps/client/src/app/components/admin-overview/admin-overview.module.ts

@ -1,10 +1,10 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatLegacySlideToggleModule as MatSlideToggleModule } from '@angular/material/legacy-slide-toggle';
import { CacheService } from '@ghostfolio/client/services/cache.service'; import { CacheService } from '@ghostfolio/client/services/cache.service';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';

4
apps/client/src/app/components/admin-users/admin-users.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';

2
apps/client/src/app/components/benchmark-comparator/benchmark-comparator.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';

2
apps/client/src/app/components/dialog-footer/dialog-footer.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { DialogFooterComponent } from './dialog-footer.component'; import { DialogFooterComponent } from './dialog-footer.component';

2
apps/client/src/app/components/dialog-header/dialog-header.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { DialogHeaderComponent } from './dialog-header.component'; import { DialogHeaderComponent } from './dialog-header.component';

2
apps/client/src/app/components/header/header.component.ts

@ -6,7 +6,7 @@ import {
OnChanges, OnChanges,
Output Output
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { LoginWithAccessTokenDialog } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.component'; import { LoginWithAccessTokenDialog } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.component';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';

4
apps/client/src/app/components/header/header.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { LoginWithAccessTokenDialogModule } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.module'; import { LoginWithAccessTokenDialogModule } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.module';

2
apps/client/src/app/components/home-holdings/home-holdings.component.ts

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component';
import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component'; import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component';

4
apps/client/src/app/components/home-holdings/home-holdings.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfPositionDetailDialogModule } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.module'; import { GfPositionDetailDialogModule } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.module';
import { GfPositionsModule } from '@ghostfolio/client/components/positions/positions.module'; import { GfPositionsModule } from '@ghostfolio/client/components/positions/positions.module';

8
apps/client/src/app/components/home-summary/home-summary.component.ts

@ -1,9 +1,9 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { import {
MatSnackBar, MatLegacySnackBar as MatSnackBar,
MatSnackBarRef, MatLegacySnackBarRef as MatSnackBarRef,
TextOnlySnackBar LegacyTextOnlySnackBar as TextOnlySnackBar
} from '@angular/material/snack-bar'; } from '@angular/material/legacy-snack-bar';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';

2
apps/client/src/app/components/home-summary/home-summary.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfPortfolioSummaryModule } from '@ghostfolio/client/components/portfolio-summary/portfolio-summary.module'; import { GfPortfolioSummaryModule } from '@ghostfolio/client/components/portfolio-summary/portfolio-summary.module';

7
apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts

@ -1,6 +1,9 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatLegacyCheckboxChange as MatCheckboxChange } from '@angular/material/legacy-checkbox';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service';
import { import {

10
apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.module.ts

@ -2,11 +2,11 @@ import { TextFieldModule } from '@angular/cdk/text-field';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { GfDialogHeaderModule } from '../dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '../dialog-header/dialog-header.module';
import { LoginWithAccessTokenDialog } from './login-with-access-token-dialog.component'; import { LoginWithAccessTokenDialog } from './login-with-access-token-dialog.component';

15
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -6,7 +6,10 @@ import {
OnDestroy, OnDestroy,
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper'; import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
import { import {
@ -37,6 +40,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
public countries: { public countries: {
[code: string]: { name: string; value: number }; [code: string]: { name: string; value: number };
}; };
public dividendInBaseCurrency: number;
public feeInBaseCurrency: number;
public firstBuyDate: string; public firstBuyDate: string;
public grossPerformance: number; public grossPerformance: number;
public grossPerformancePercent: number; public grossPerformancePercent: number;
@ -78,6 +83,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
.subscribe( .subscribe(
({ ({
averagePrice, averagePrice,
dividendInBaseCurrency,
feeInBaseCurrency,
firstBuyDate, firstBuyDate,
grossPerformance, grossPerformance,
grossPerformancePercent, grossPerformancePercent,
@ -98,7 +105,8 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.averagePrice = averagePrice; this.averagePrice = averagePrice;
this.benchmarkDataItems = []; this.benchmarkDataItems = [];
this.countries = {}; this.countries = {};
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`; this.dividendInBaseCurrency = dividendInBaseCurrency;
this.feeInBaseCurrency = feeInBaseCurrency;
this.firstBuyDate = firstBuyDate; this.firstBuyDate = firstBuyDate;
this.grossPerformance = grossPerformance; this.grossPerformance = grossPerformance;
this.grossPerformancePercent = grossPerformancePercent; this.grossPerformancePercent = grossPerformancePercent;
@ -111,7 +119,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
return { return {
date: historicalDataItem.date, date: historicalDataItem.date,
value: historicalDataItem.value value: historicalDataItem.marketPrice
}; };
} }
); );
@ -123,6 +131,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.netPerformancePercent = netPerformancePercent; this.netPerformancePercent = netPerformancePercent;
this.orders = orders; this.orders = orders;
this.quantity = quantity; this.quantity = quantity;
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
this.sectors = {}; this.sectors = {};
this.SymbolProfile = SymbolProfile; this.SymbolProfile = SymbolProfile;
this.tags = tags.map(({ id, name }) => { this.tags = tags.map(({ id, name }) => {

20
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html

@ -119,6 +119,26 @@
>Investment</gf-value >Investment</gf-value
> >
</div> </div>
<div class="col-6 mb-3">
<gf-value
i18n
size="medium"
[currency]="data.baseCurrency"
[locale]="data.locale"
[value]="dividendInBaseCurrency"
>Dividend</gf-value
>
</div>
<div class="col-6 mb-3">
<gf-value
i18n
size="medium"
[currency]="data.baseCurrency"
[locale]="data.locale"
[value]="feeInBaseCurrency"
>Fees</gf-value
>
</div>
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value <gf-value
i18n i18n

6
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.module.ts

@ -1,8 +1,8 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatChipsModule } from '@angular/material/chips'; import { MatLegacyChipsModule as MatChipsModule } from '@angular/material/legacy-chips';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module'; import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';

2
apps/client/src/app/components/position/position.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
import { GfTrendIndicatorModule } from '@ghostfolio/ui/trend-indicator'; import { GfTrendIndicatorModule } from '@ghostfolio/ui/trend-indicator';

2
apps/client/src/app/components/positions/positions.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { GfNoTransactionsInfoModule } from '@ghostfolio/ui/no-transactions-info'; import { GfNoTransactionsInfoModule } from '@ghostfolio/ui/no-transactions-info';
import { GfPositionModule } from '../position/position.module'; import { GfPositionModule } from '../position/position.module';

4
apps/client/src/app/components/rules/rules.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { GfRuleModule } from '@ghostfolio/client/components/rule/rule.module'; import { GfRuleModule } from '@ghostfolio/client/components/rule/rule.module';
import { GfNoTransactionsInfoModule } from '@ghostfolio/ui/no-transactions-info'; import { GfNoTransactionsInfoModule } from '@ghostfolio/ui/no-transactions-info';

1
apps/client/src/app/components/subscription-interstitial-dialog/interfaces/interfaces.ts

@ -0,0 +1 @@
export interface SubscriptionInterstitialDialogParams {}

25
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.component.ts

@ -0,0 +1,25 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { SubscriptionInterstitialDialogParams } from './interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'd-flex flex-column flex-grow-1 h-100' },
selector: 'gf-subscription-interstitial-dialog',
styleUrls: ['./subscription-interstitial-dialog.scss'],
templateUrl: 'subscription-interstitial-dialog.html'
})
export class SubscriptionInterstitialDialog {
public constructor(
@Inject(MAT_DIALOG_DATA) public data: SubscriptionInterstitialDialogParams,
public dialogRef: MatDialogRef<SubscriptionInterstitialDialog>
) {}
public onCancel() {
this.dialogRef.close({});
}
}

42
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html

@ -0,0 +1,42 @@
<h1 class="align-items-center d-flex" mat-dialog-title>
<span>Ghostfolio Premium</span>
<gf-premium-indicator class="ml-1"></gf-premium-indicator>
</h1>
<div class="flex-grow-1" mat-dialog-content>
<p class="h5" i18n>
Are you an ambitious investor who needs the full picture?
</p>
<p i18n>
By upgrading to Ghostfolio Premium, you will get these additional features:
</p>
<ul class="list-unstyled mb-3">
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline"></ion-icon>
<span i18n>Portfolio Summary</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline"></ion-icon>
<span i18n>Performance Benchmarks</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline"></ion-icon>
<span i18n>Allocations</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline"></ion-icon>
<span i18n>FIRE Calculator</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline"></ion-icon>
<a i18n [routerLink]="['/features']">and more Features...</a>
</li>
</ul>
<p>Refine your personal investment strategy now.</p>
</div>
<div class="justify-content-end" mat-dialog-actions>
<button i18n mat-button (click)="onCancel()">Skip</button>
<a color="primary" mat-flat-button [routerLink]="['/pricing']">
<span i18n>Upgrade Plan</span>
<ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
</a>
</div>

21
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.module.ts

@ -0,0 +1,21 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { RouterModule } from '@angular/router';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
import { SubscriptionInterstitialDialog } from './subscription-interstitial-dialog.component';
@NgModule({
declarations: [SubscriptionInterstitialDialog],
imports: [
CommonModule,
GfPremiumIndicatorModule,
MatButtonModule,
MatDialogModule,
RouterModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GfSubscriptionInterstitialDialogModule {}

11
apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.scss

@ -0,0 +1,11 @@
:host {
display: block;
.mat-dialog-content {
max-height: unset;
ion-icon[name='checkmark-circle-outline'] {
color: rgba(var(--palette-accent-500), 1);
}
}
}

2
apps/client/src/app/components/toggle/toggle.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { MatRadioModule } from '@angular/material/radio'; import { MatLegacyRadioModule as MatRadioModule } from '@angular/material/legacy-radio';
import { ToggleComponent } from './toggle.component'; import { ToggleComponent } from './toggle.component';

8
apps/client/src/app/core/http-response.interceptor.ts

@ -8,10 +8,10 @@ import {
} from '@angular/common/http'; } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import {
MatSnackBar, MatLegacySnackBar as MatSnackBar,
MatSnackBarRef, MatLegacySnackBarRef as MatSnackBarRef,
TextOnlySnackBar LegacyTextOnlySnackBar as TextOnlySnackBar
} from '@angular/material/snack-bar'; } from '@angular/material/legacy-snack-bar';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';

4
apps/client/src/app/pages/about/about-page.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';
import { AboutPageRoutingModule } from './about-page-routing.module'; import { AboutPageRoutingModule } from './about-page-routing.module';

2
apps/client/src/app/pages/about/changelog/changelog-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MarkdownModule } from 'ngx-markdown'; import { MarkdownModule } from 'ngx-markdown';
import { ChangelogPageRoutingModule } from './changelog-page-routing.module'; import { ChangelogPageRoutingModule } from './changelog-page-routing.module';

16
apps/client/src/app/pages/account/account-page.component.ts

@ -5,16 +5,16 @@ import {
OnInit, OnInit,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { import {
MatSlideToggle, MatLegacySlideToggle as MatSlideToggle,
MatSlideToggleChange MatLegacySlideToggleChange as MatSlideToggleChange
} from '@angular/material/slide-toggle'; } from '@angular/material/legacy-slide-toggle';
import { import {
MatSnackBar, MatLegacySnackBar as MatSnackBar,
MatSnackBarRef, MatLegacySnackBarRef as MatSnackBarRef,
TextOnlySnackBar LegacyTextOnlySnackBar as TextOnlySnackBar
} from '@angular/material/snack-bar'; } from '@angular/material/legacy-snack-bar';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto'; import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';

14
apps/client/src/app/pages/account/account-page.module.ts

@ -1,13 +1,12 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatInputModule } from '@angular/material/input'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySlideToggleModule as MatSlideToggleModule } from '@angular/material/legacy-slide-toggle';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfPortfolioAccessTableModule } from '@ghostfolio/client/components/access-table/access-table.module'; import { GfPortfolioAccessTableModule } from '@ghostfolio/client/components/access-table/access-table.module';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
@ -31,7 +30,6 @@ import { GfCreateOrUpdateAccessDialogModule } from './create-or-update-access-di
MatCardModule, MatCardModule,
MatDialogModule, MatDialogModule,
MatFormFieldModule, MatFormFieldModule,
MatInputModule,
MatSelectModule, MatSelectModule,
MatSlideToggleModule, MatSlideToggleModule,
ReactiveFormsModule, ReactiveFormsModule,

7
apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.component.ts

@ -4,7 +4,10 @@ import {
Inject, Inject,
OnDestroy OnDestroy
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { CreateOrUpdateAccessDialogParams } from './interfaces/interfaces'; import { CreateOrUpdateAccessDialogParams } from './interfaces/interfaces';
@ -26,7 +29,7 @@ export class CreateOrUpdateAccessDialog implements OnDestroy {
ngOnInit() {} ngOnInit() {}
public onCancel(): void { public onCancel() {
this.dialogRef.close(); this.dialogRef.close();
} }

10
apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.module.ts

@ -1,11 +1,11 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { CreateOrUpdateAccessDialog } from './create-or-update-access-dialog.component'; import { CreateOrUpdateAccessDialog } from './create-or-update-access-dialog.component';

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

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';

2
apps/client/src/app/pages/accounts/accounts-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfAccountDetailDialogModule } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.module'; import { GfAccountDetailDialogModule } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.module';
import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-table/accounts-table.module'; import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-table/accounts-table.module';

7
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts

@ -4,7 +4,10 @@ import {
Inject, Inject,
OnDestroy OnDestroy
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { DataService } from '../../../services/data.service'; import { DataService } from '../../../services/data.service';
@ -36,7 +39,7 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
this.platforms = platforms; this.platforms = platforms;
} }
public onCancel(): void { public onCancel() {
this.dialogRef.close(); this.dialogRef.close();
} }

12
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.module.ts

@ -1,12 +1,12 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog.component'; import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog.component';

8
apps/client/src/app/pages/admin/admin-page.module.ts

@ -1,9 +1,9 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MatMenuModule } from '@angular/material/menu'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatTabsModule } from '@angular/material/tabs'; import { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs';
import { GfAdminJobsModule } from '@ghostfolio/client/components/admin-jobs/admin-jobs.module'; import { GfAdminJobsModule } from '@ghostfolio/client/components/admin-jobs/admin-jobs.module';
import { GfAdminMarketDataModule } from '@ghostfolio/client/components/admin-market-data/admin-market-data.module'; import { GfAdminMarketDataModule } from '@ghostfolio/client/components/admin-market-data/admin-market-data.module';
import { GfAdminOverviewModule } from '@ghostfolio/client/components/admin-overview/admin-overview.module'; import { GfAdminOverviewModule } from '@ghostfolio/client/components/admin-overview/admin-overview.module';

2
apps/client/src/app/pages/blog/2022/11/black-friday-2022/black-friday-2022-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';

2
apps/client/src/app/pages/blog/2022/12/the-importance-of-tracking-your-personal-finances/the-importance-of-tracking-your-personal-finances-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { TheImportanceOfTrackingYourPersonalFinancesRoutingModule } from './the-importance-of-tracking-your-personal-finances-page-routing.module'; import { TheImportanceOfTrackingYourPersonalFinancesRoutingModule } from './the-importance-of-tracking-your-personal-finances-page-routing.module';

2
apps/client/src/app/pages/blog/blog-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { BlogPageRoutingModule } from './blog-page-routing.module'; import { BlogPageRoutingModule } from './blog-page-routing.module';
import { BlogPageComponent } from './blog-page.component'; import { BlogPageComponent } from './blog-page.component';

2
apps/client/src/app/pages/faq/faq-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { FaqPageRoutingModule } from './faq-page-routing.module'; import { FaqPageRoutingModule } from './faq-page-routing.module';
import { FaqPageComponent } from './faq-page.component'; import { FaqPageComponent } from './faq-page.component';

4
apps/client/src/app/pages/features/features-page.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator'; import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
import { FeaturesPageRoutingModule } from './features-page-routing.module'; import { FeaturesPageRoutingModule } from './features-page-routing.module';

2
apps/client/src/app/pages/home/home-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs'; import { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfHomeHoldingsModule } from '@ghostfolio/client/components/home-holdings/home-holdings.module'; import { GfHomeHoldingsModule } from '@ghostfolio/client/components/home-holdings/home-holdings.module';
import { GfHomeMarketModule } from '@ghostfolio/client/components/home-market/home-market.module'; import { GfHomeMarketModule } from '@ghostfolio/client/components/home-market/home-market.module';

8
apps/client/src/app/pages/landing/landing-page.html

@ -154,6 +154,14 @@
title="Sackgeld.com – Apps für ein höheres Sackgeld" title="Sackgeld.com – Apps für ein höheres Sackgeld"
></a> ></a>
</div> </div>
<div class="col-md-3 d-flex justify-content-center my-1">
<a
class="d-block logo logo-sourceforge mask"
href="https://sourceforge.net"
target="_blank"
title="SourceForge: The Complete Open-Source and Business Software Platform"
></a>
</div>
<div class="col-md-3 d-flex justify-content-center my-1"> <div class="col-md-3 d-flex justify-content-center my-1">
<a <a
class="d-block logo logo-unraid mask" class="d-block logo logo-unraid mask"

4
apps/client/src/app/pages/landing/landing-page.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module'; import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { GfLogoModule } from '@ghostfolio/ui/logo'; import { GfLogoModule } from '@ghostfolio/ui/logo';

5
apps/client/src/app/pages/landing/landing-page.scss

@ -79,6 +79,10 @@
mask-image: url('/assets/images/logo-sackgeld.png'); mask-image: url('/assets/images/logo-sackgeld.png');
} }
&.logo-sourceforge {
mask-image: url('/assets/images/logo-sourceforge.svg');
}
&.logo-unraid { &.logo-unraid {
mask-image: url('/assets/images/logo-unraid.svg'); mask-image: url('/assets/images/logo-unraid.svg');
} }
@ -117,6 +121,7 @@
&.logo-alternative-to, &.logo-alternative-to,
&.logo-privacy-tools, &.logo-privacy-tools,
&.logo-sackgeld, &.logo-sackgeld,
&.logo-sourceforge,
&.logo-unraid { &.logo-unraid {
background-color: rgba(var(--light-primary-text)); background-color: rgba(var(--light-primary-text));
} }

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

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';

4
apps/client/src/app/pages/portfolio/activities/activities-page.module.ts

@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service'; import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service';
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module'; import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';

7
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts

@ -9,9 +9,12 @@ import {
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatLegacyAutocompleteSelectedEvent as MatAutocompleteSelectedEvent } from '@angular/material/legacy-autocomplete';
import {
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';

8
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html

@ -18,12 +18,14 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div <div>
[ngClass]="{ 'd-none': !activityForm.controls['accountId'].hasValidator(Validators.required) }"
>
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Account</mat-label> <mat-label i18n>Account</mat-label>
<mat-select formControlName="accountId"> <mat-select formControlName="accountId">
<mat-option
*ngIf="!activityForm.controls['accountId'].hasValidator(Validators.required)"
[value]="null"
></mat-option>
<mat-option *ngFor="let account of data.accounts" [value]="account.id" <mat-option *ngFor="let account of data.accounts" [value]="account.id"
>{{ account.name }}</mat-option >{{ account.name }}</mat-option
> >

16
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.module.ts

@ -1,15 +1,15 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyAutocompleteModule as MatAutocompleteModule } from '@angular/material/legacy-autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatInputModule } from '@angular/material/input'; import { MatLegacyChipsModule as MatChipsModule } from '@angular/material/legacy-chips';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';

9
apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts

@ -6,8 +6,11 @@ import {
OnDestroy OnDestroy
} from '@angular/core'; } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {
import { MatSnackBar } from '@angular/material/snack-bar'; MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
MatLegacyDialogRef as MatDialogRef
} from '@angular/material/legacy-dialog';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
@ -82,7 +85,7 @@ export class ImportActivitiesDialog implements OnDestroy {
} }
} }
public onCancel(): void { public onCancel() {
this.dialogRef.close(); this.dialogRef.close();
} }

8
apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.module.ts

@ -1,11 +1,11 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatExpansionModule } from '@angular/material/expansion'; import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatSelectModule } from '@angular/material/select'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module'; import { GfActivitiesTableModule } from '@ghostfolio/ui/activities-table/activities-table.module';

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

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component'; import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component';
import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces'; import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces';
@ -21,6 +21,7 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Market } from '@ghostfolio/common/types'; import { Market } from '@ghostfolio/common/types';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
import { Account, AssetClass, DataSource } from '@prisma/client'; import { Account, AssetClass, DataSource } from '@prisma/client';
import { isNumber } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators'; import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
@ -339,7 +340,9 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
dataSource: position.dataSource, dataSource: position.dataSource,
name: position.name, name: position.name,
symbol: prettifySymbol(symbol), symbol: prettifySymbol(symbol),
value: position.value value: isNumber(position.value)
? position.value
: position.valueInPercentage
}; };
} }
@ -357,7 +360,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
} }
public onAccountChartClicked({ symbol }: UniqueAsset) { public onAccountChartClicked({ symbol }: UniqueAsset) {
if (symbol) { if (symbol && symbol !== UNKNOWN_KEY) {
this.router.navigate([], { this.router.navigate([], {
queryParams: { accountId: symbol, accountDetailDialog: true } queryParams: { accountId: symbol, accountDetailDialog: true }
}); });

6
apps/client/src/app/pages/portfolio/allocations/allocations-page.module.ts

@ -1,8 +1,8 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { MatDialogModule } from '@angular/material/dialog'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar';
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module'; import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module'; import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module';
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module'; import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';

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

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces';
import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component';
@ -19,7 +19,7 @@ import { DateRange, GroupBy, ToggleOption } from '@ghostfolio/common/types';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
import { AssetClass, DataSource, SymbolProfile } from '@prisma/client'; import { AssetClass, DataSource, SymbolProfile } from '@prisma/client';
import { differenceInDays } from 'date-fns'; import { differenceInDays } from 'date-fns';
import { sortBy } from 'lodash'; import { isNumber, sortBy } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
@ -312,12 +312,13 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
date, date,
netPerformanceInPercentage, netPerformanceInPercentage,
totalInvestment, totalInvestment,
value value,
valueInPercentage
} of chart) { } of chart) {
this.investments.push({ date, investment: totalInvestment }); this.investments.push({ date, investment: totalInvestment });
this.performanceDataItems.push({ this.performanceDataItems.push({
date, date,
value value: isNumber(value) ? value : valueInPercentage
}); });
this.performanceDataItemsInPercentage.push({ this.performanceDataItemsInPercentage.push({
date, date,

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

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card';
import { GfBenchmarkComparatorModule } from '@ghostfolio/client/components/benchmark-comparator/benchmark-comparator.module'; import { GfBenchmarkComparatorModule } from '@ghostfolio/client/components/benchmark-comparator/benchmark-comparator.module';
import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module'; import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module';
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module'; import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';

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

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces'; import { PositionDetailDialogParams } from '@ghostfolio/client/components/position/position-detail-dialog/interfaces/interfaces';
import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component';

2
apps/client/src/app/pages/portfolio/holdings/holdings-page.module.ts

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module'; import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module';
import { GfHoldingsTableModule } from '@ghostfolio/ui/holdings-table/holdings-table.module'; import { GfHoldingsTableModule } from '@ghostfolio/ui/holdings-table/holdings-table.module';

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

@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs'; import { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { PortfolioPageRoutingModule } from './portfolio-page-routing.module'; import { PortfolioPageRoutingModule } from './portfolio-page-routing.module';

159
apps/client/src/app/pages/pricing/pricing-page.html

@ -31,50 +31,71 @@
<mat-card class="d-flex flex-column h-100"> <mat-card class="d-flex flex-column h-100">
<div class="flex-grow-1"> <div class="flex-grow-1">
<h4>Open Source</h4> <h4>Open Source</h4>
<p> <p i18n>
For tech-savvy investors who prefer to run For tech-savvy investors who prefer to run Ghostfolio on their
<strong>Ghostfolio</strong> on their own infrastructure. own infrastructure.
</p> </p>
<ul class="list-unstyled mb-3"> <ul class="list-unstyled mb-3">
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Unlimited Transactions</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Unlimited Accounts</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Portfolio Performance</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Unlimited Transactions</span> <span i18n>Portfolio Summary</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Portfolio Performance</span> <span i18n>Portfolio Allocations</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Zen Mode</span> <span i18n>Performance Benchmarks</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Portfolio Summary</span> <span i18n>FIRE Calculator</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Advanced Insights</span> <a i18n [routerLink]="['/features']">and more Features...</a>
</li> </li>
</ul> </ul>
</div> </div>
<p>Self-hosted, update manually.</p> <p i18n>Self-hosted, update manually.</p>
<p class="h5 text-right">Free</p> <p class="h5 text-right" i18n>Free</p>
<div <div
*ngIf="user?.subscription?.type === 'Basic'" *ngIf="user?.subscription?.type === 'Basic'"
class="d-none d-lg-block hidden mt-3 text-center" class="d-none d-lg-block hidden mt-3 text-center"
@ -92,31 +113,54 @@
[ngClass]="{ 'active': user?.subscription?.type === 'Basic' }" [ngClass]="{ 'active': user?.subscription?.type === 'Basic' }"
> >
<div class="flex-grow-1"> <div class="flex-grow-1">
<h4 class="align-items-center d-flex">Basic</h4> <div class="align-items-center d-flex mb-2">
<p> <h4 class="flex-grow-1 m-0">Basic</h4>
<div *ngIf="user?.subscription?.type === 'Basic'">
<ion-icon class="mr-1" name="checkmark-outline"></ion-icon>
</div>
</div>
<p i18n>
For new investors who are just getting started with trading. For new investors who are just getting started with trading.
</p> </p>
<ul class="list-unstyled mb-3"> <ul class="list-unstyled mb-3">
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Unlimited Transactions</span> <span i18n>Unlimited Transactions</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Portfolio Performance</span> <span i18n>Unlimited Accounts</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Portfolio Performance</span>
</li>
<li>
<ion-icon
class="invisible"
name="checkmark-circle-outline"
></ion-icon>
</li>
<li>
<ion-icon
class="invisible"
name="checkmark-circle-outline"
></ion-icon>
</li>
<li>
<ion-icon
class="invisible"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Zen Mode</span>
</li> </li>
<li> <li>
<ion-icon <ion-icon
@ -132,8 +176,8 @@
</li> </li>
</ul> </ul>
</div> </div>
<p>Fully managed <strong>Ghostfolio</strong> cloud offering.</p> <p i18n>Fully managed Ghostfolio cloud offering.</p>
<p class="h5 text-right">Free</p> <p class="h5 text-right" i18n>Free</p>
<div <div
*ngIf="user?.subscription?.type === 'Basic'" *ngIf="user?.subscription?.type === 'Basic'"
class="d-none d-lg-block hidden mt-3 text-center" class="d-none d-lg-block hidden mt-3 text-center"
@ -151,56 +195,82 @@
[ngClass]="{ 'active': user?.subscription?.type === 'Premium' }" [ngClass]="{ 'active': user?.subscription?.type === 'Premium' }"
> >
<div class="flex-grow-1"> <div class="flex-grow-1">
<h4 class="align-items-center d-flex"> <div class="align-items-center d-flex mb-2">
<h4 class="align-items-center d-flex flex-grow-1 m-0">
<span>Premium</span> <span>Premium</span>
<gf-premium-indicator <gf-premium-indicator
class="ml-1" class="ml-1"
[enableLink]="false" [enableLink]="false"
></gf-premium-indicator> ></gf-premium-indicator>
</h4> </h4>
<p> <div *ngIf="user?.subscription?.type === 'Premium'">
<ion-icon class="mr-1" name="checkmark-outline"></ion-icon>
</div>
</div>
<p i18n>
For ambitious investors who need the full picture of their For ambitious investors who need the full picture of their
financial assets. financial assets.
</p> </p>
<ul class="list-unstyled mb-3"> <ul class="list-unstyled mb-3">
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Unlimited Transactions</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<span i18n>Unlimited Accounts</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Unlimited Transactions</span> <span i18n>Portfolio Performance</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Portfolio Performance</span> <span i18n>Portfolio Summary</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Zen Mode</span> <span i18n>Portfolio Allocations</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Portfolio Summary</span> <span i18n>Performance Benchmarks</span>
</li> </li>
<li class="align-items-center d-flex mb-1"> <li class="align-items-center d-flex mb-1">
<ion-icon <ion-icon
class="mr-1 text-muted" class="mr-1"
name="checkmark-circle-outline" name="checkmark-circle-outline"
></ion-icon> ></ion-icon>
<span>Advanced Insights</span> <span i18n>FIRE Calculator</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<a i18n [routerLink]="['/features']">and more Features...</a>
</li> </li>
</ul> </ul>
</div> </div>
<p>Fully managed <strong>Ghostfolio</strong> cloud offering.</p> <p i18n>Fully managed Ghostfolio cloud offering.</p>
<p class="h5 text-right" [hidden]="!price"> <p class="h5 text-right" [hidden]="!price">
<span class="font-weight-normal"> <span class="font-weight-normal">
<ng-container *ngIf="coupon" <ng-container *ngIf="coupon"
@ -221,11 +291,16 @@
*ngIf="user?.subscription?.type === 'Basic'" *ngIf="user?.subscription?.type === 'Basic'"
class="mt-3 text-center" class="mt-3 text-center"
> >
<a color="primary" mat-flat-button [routerLink]="['/account']"> <a
color="primary"
i18n
mat-flat-button
[routerLink]="['/account']"
>
Upgrade Plan Upgrade Plan
</a> </a>
<p class="m-0 text-muted"> <p class="m-0 text-muted">
<small>One-time payment, no auto-renewal.</small> <small i18n>One-time payment, no auto-renewal.</small>
</p> </p>
</div> </div>
</mat-card> </mat-card>
@ -235,10 +310,10 @@
</div> </div>
<div *ngIf="!user" class="row"> <div *ngIf="!user" class="row">
<div class="col mt-3 text-center"> <div class="col mt-3 text-center">
<a color="primary" mat-flat-button [routerLink]="['/register']"> <a color="primary" i18n mat-flat-button [routerLink]="['/register']">
Get Started Get Started
</a> </a>
<p class="m-0 text-muted"><small>It’s free.</small></p> <p class="m-0 text-muted"><small i18n>It’s free.</small></p>
</div> </div>
</div> </div>
</div> </div>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save