Merge branch 'main' of gitea.suda.codes:giteauser/ghostfolio-mirror
This commit is contained in:
commit
8e2e73137c
@ -142,13 +142,8 @@
|
|||||||
|
|
||||||
// The following rules are part of @typescript-eslint/stylistic-type-checked
|
// The following rules are part of @typescript-eslint/stylistic-type-checked
|
||||||
// and can be remove once solved
|
// and can be remove once solved
|
||||||
"@typescript-eslint/consistent-type-definitions": "warn",
|
|
||||||
"@typescript-eslint/prefer-function-type": "warn",
|
|
||||||
"@typescript-eslint/prefer-nullish-coalescing": "warn", // TODO: Requires strictNullChecks: true
|
"@typescript-eslint/prefer-nullish-coalescing": "warn", // TODO: Requires strictNullChecks: true
|
||||||
"@typescript-eslint/consistent-type-assertions": "warn",
|
"@typescript-eslint/consistent-indexed-object-style": "warn"
|
||||||
"@typescript-eslint/prefer-optional-chain": "warn",
|
|
||||||
"@typescript-eslint/consistent-indexed-object-style": "warn",
|
|
||||||
"@typescript-eslint/consistent-generic-constructors": "warn"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -9,13 +9,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Switched the `consistent-generic-constructors` rule from `warn` to `error` in the `eslint` configuration
|
||||||
|
- Switched the `consistent-type-assertions` rule from `warn` to `error` in the `eslint` configuration
|
||||||
|
- Switched the `prefer-optional-chain` rule from `warn` to `error` in the `eslint` configuration
|
||||||
|
|
||||||
|
## 2.119.0 - 2024-10-26
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Switched the `consistent-type-definitions` rule from `warn` to `error` in the `eslint` configuration
|
||||||
- Switched the `no-empty-function` rule from `warn` to `error` in the `eslint` configuration
|
- Switched the `no-empty-function` rule from `warn` to `error` in the `eslint` configuration
|
||||||
|
- Switched the `prefer-function-type` rule from `warn` to `error` in the `eslint` configuration
|
||||||
|
- Upgraded `prisma` from version `5.20.0` to `5.21.1`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed an issue with the X-axis scale of the dividend timeline on the analysis page
|
- Fixed an issue with the X-axis scale of the dividend timeline on the analysis page
|
||||||
- Fixed an issue with the X-axis scale of the investment timeline on the analysis page
|
- Fixed an issue with the X-axis scale of the investment timeline on the analysis page
|
||||||
- Fixed an issue with the X-axis scale of the portfolio evolution chart on the analysis page
|
- Fixed an issue with the X-axis scale of the portfolio evolution chart on the analysis page
|
||||||
|
- Fixed an issue in the calculation of the static portfolio analysis rule: Allocation Cluster Risk (Developed Markets)
|
||||||
|
- Fixed an issue in the calculation of the static portfolio analysis rule: Allocation Cluster Risk (Emerging Markets)
|
||||||
|
|
||||||
## 2.118.0 - 2024-10-23
|
## 2.118.0 - 2024-10-23
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ export class AccountBalanceService {
|
|||||||
this.eventEmitter.emit(
|
this.eventEmitter.emit(
|
||||||
PortfolioChangedEvent.getName(),
|
PortfolioChangedEvent.getName(),
|
||||||
new PortfolioChangedEvent({
|
new PortfolioChangedEvent({
|
||||||
userId: <string>where.userId
|
userId: where.userId as string
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -209,8 +209,8 @@ export class AccountService {
|
|||||||
const { data, where } = params;
|
const { data, where } = params;
|
||||||
|
|
||||||
await this.accountBalanceService.createOrUpdateAccountBalance({
|
await this.accountBalanceService.createOrUpdateAccountBalance({
|
||||||
accountId: <string>data.id,
|
accountId: data.id as string,
|
||||||
balance: <number>data.balance,
|
balance: data.balance as number,
|
||||||
date: format(new Date(), DATE_FORMAT),
|
date: format(new Date(), DATE_FORMAT),
|
||||||
userId: aUserId
|
userId: aUserId
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ export class QueueController {
|
|||||||
public async deleteJobs(
|
public async deleteJobs(
|
||||||
@Query('status') filterByStatus?: string
|
@Query('status') filterByStatus?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const status = <JobStatus[]>filterByStatus?.split(',') ?? undefined;
|
const status = (filterByStatus?.split(',') as JobStatus[]) ?? undefined;
|
||||||
return this.queueService.deleteJobs({ status });
|
return this.queueService.deleteJobs({ status });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ export class QueueController {
|
|||||||
public async getJobs(
|
public async getJobs(
|
||||||
@Query('status') filterByStatus?: string
|
@Query('status') filterByStatus?: string
|
||||||
): Promise<AdminJobs> {
|
): Promise<AdminJobs> {
|
||||||
const status = <JobStatus[]>filterByStatus?.split(',') ?? undefined;
|
const status = (filterByStatus?.split(',') as JobStatus[]) ?? undefined;
|
||||||
return this.queueService.getJobs({ status });
|
return this.queueService.getJobs({ status });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ export class AuthController {
|
|||||||
@Res() response: Response
|
@Res() response: Response
|
||||||
) {
|
) {
|
||||||
// Handles the Google OAuth2 callback
|
// Handles the Google OAuth2 callback
|
||||||
const jwt: string = (<any>request.user).jwt;
|
const jwt: string = (request.user as any).jwt;
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
response.redirect(
|
response.redirect(
|
||||||
|
@ -198,12 +198,12 @@ export interface AuthenticatorAssertionResponseJSON
|
|||||||
/**
|
/**
|
||||||
* A WebAuthn-compatible device and the information needed to verify assertions by it
|
* A WebAuthn-compatible device and the information needed to verify assertions by it
|
||||||
*/
|
*/
|
||||||
export declare type AuthenticatorDevice = {
|
export declare interface AuthenticatorDevice {
|
||||||
credentialPublicKey: Buffer;
|
credentialPublicKey: Buffer;
|
||||||
credentialID: Buffer;
|
credentialID: Buffer;
|
||||||
counter: number;
|
counter: number;
|
||||||
transports?: AuthenticatorTransport[];
|
transports?: AuthenticatorTransport[];
|
||||||
};
|
}
|
||||||
/**
|
/**
|
||||||
* An attempt to communicate that this isn't just any string, but a Base64URL-encoded string
|
* An attempt to communicate that this isn't just any string, but a Base64URL-encoded string
|
||||||
*/
|
*/
|
||||||
|
@ -442,10 +442,10 @@ export class BenchmarkService {
|
|||||||
|
|
||||||
await this.redisCacheService.set(
|
await this.redisCacheService.set(
|
||||||
this.CACHE_KEY_BENCHMARKS,
|
this.CACHE_KEY_BENCHMARKS,
|
||||||
JSON.stringify(<BenchmarkValue>{
|
JSON.stringify({
|
||||||
benchmarks,
|
benchmarks,
|
||||||
expiration: expiration.getTime()
|
expiration: expiration.getTime()
|
||||||
}),
|
} as BenchmarkValue),
|
||||||
CACHE_TTL_INFINITE
|
CACHE_TTL_INFINITE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -410,7 +410,7 @@ export class OrderService {
|
|||||||
where.SymbolProfile,
|
where.SymbolProfile,
|
||||||
{
|
{
|
||||||
AND: [
|
AND: [
|
||||||
{ dataSource: <DataSource>filterByDataSource },
|
{ dataSource: filterByDataSource as DataSource },
|
||||||
{ symbol: filterBySymbol }
|
{ symbol: filterBySymbol }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -419,7 +419,7 @@ export class OrderService {
|
|||||||
} else {
|
} else {
|
||||||
where.SymbolProfile = {
|
where.SymbolProfile = {
|
||||||
AND: [
|
AND: [
|
||||||
{ dataSource: <DataSource>filterByDataSource },
|
{ dataSource: filterByDataSource as DataSource },
|
||||||
{ symbol: filterBySymbol }
|
{ symbol: filterBySymbol }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -638,7 +638,7 @@ export class OrderService {
|
|||||||
{
|
{
|
||||||
dataSource:
|
dataSource:
|
||||||
data.SymbolProfile.connect.dataSource_symbol.dataSource,
|
data.SymbolProfile.connect.dataSource_symbol.dataSource,
|
||||||
date: <Date>data.date,
|
date: data.date as Date,
|
||||||
symbol: data.SymbolProfile.connect.dataSource_symbol.symbol
|
symbol: data.SymbolProfile.connect.dataSource_symbol.symbol
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -796,7 +796,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
[key: DateRange]: Big;
|
[key: DateRange]: Big;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
for (const dateRange of <DateRange[]>[
|
for (const dateRange of [
|
||||||
'1d',
|
'1d',
|
||||||
'1y',
|
'1y',
|
||||||
'5y',
|
'5y',
|
||||||
@ -812,7 +812,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
// .map((date) => {
|
// .map((date) => {
|
||||||
// return format(date, 'yyyy');
|
// return format(date, 'yyyy');
|
||||||
// })
|
// })
|
||||||
]) {
|
] as DateRange[]) {
|
||||||
const dateInterval = getIntervalFromDateRange(dateRange);
|
const dateInterval = getIntervalFromDateRange(dateRange);
|
||||||
const endDate = dateInterval.endDate;
|
const endDate = dateInterval.endDate;
|
||||||
let startDate = dateInterval.startDate;
|
let startDate = dateInterval.startDate;
|
||||||
|
@ -138,7 +138,7 @@ export class PortfolioService {
|
|||||||
some: {
|
some: {
|
||||||
SymbolProfile: {
|
SymbolProfile: {
|
||||||
AND: [
|
AND: [
|
||||||
{ dataSource: <DataSource>filterByDataSource },
|
{ dataSource: filterByDataSource as DataSource },
|
||||||
{ symbol: filterBySymbol }
|
{ symbol: filterBySymbol }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1160,7 +1160,7 @@ export class PortfolioService {
|
|||||||
|
|
||||||
public async getReport(impersonationId: string): Promise<PortfolioReport> {
|
public async getReport(impersonationId: string): Promise<PortfolioReport> {
|
||||||
const userId = await this.getUserId(impersonationId, this.request.user.id);
|
const userId = await this.getUserId(impersonationId, this.request.user.id);
|
||||||
const userSettings = <UserSettings>this.request.user.Settings.settings;
|
const userSettings = this.request.user.Settings.settings as UserSettings;
|
||||||
|
|
||||||
const { accounts, holdings, markets, summary } = await this.getDetails({
|
const { accounts, holdings, markets, summary } = await this.getDetails({
|
||||||
impersonationId,
|
impersonationId,
|
||||||
@ -1169,6 +1169,12 @@ export class PortfolioService {
|
|||||||
withSummary: true
|
withSummary: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const marketsTotalInBaseCurrency = getSum(
|
||||||
|
Object.values(markets).map(({ valueInBaseCurrency }) => {
|
||||||
|
return new Big(valueInBaseCurrency);
|
||||||
|
})
|
||||||
|
).toNumber();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rules: {
|
rules: {
|
||||||
accountClusterRisk:
|
accountClusterRisk:
|
||||||
@ -1193,12 +1199,12 @@ export class PortfolioService {
|
|||||||
[
|
[
|
||||||
new AllocationClusterRiskDevelopedMarkets(
|
new AllocationClusterRiskDevelopedMarkets(
|
||||||
this.exchangeRateDataService,
|
this.exchangeRateDataService,
|
||||||
summary.currentValueInBaseCurrency,
|
marketsTotalInBaseCurrency,
|
||||||
markets.developedMarkets.valueInBaseCurrency
|
markets.developedMarkets.valueInBaseCurrency
|
||||||
),
|
),
|
||||||
new AllocationClusterRiskEmergingMarkets(
|
new AllocationClusterRiskEmergingMarkets(
|
||||||
this.exchangeRateDataService,
|
this.exchangeRateDataService,
|
||||||
summary.currentValueInBaseCurrency,
|
marketsTotalInBaseCurrency,
|
||||||
markets.emergingMarkets.valueInBaseCurrency
|
markets.emergingMarkets.valueInBaseCurrency
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -1358,20 +1364,20 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketsTotal =
|
const marketsTotalInBaseCurrency = getSum(
|
||||||
markets.developedMarkets.valueInBaseCurrency +
|
Object.values(markets).map(({ valueInBaseCurrency }) => {
|
||||||
markets.emergingMarkets.valueInBaseCurrency +
|
return new Big(valueInBaseCurrency);
|
||||||
markets.otherMarkets.valueInBaseCurrency +
|
})
|
||||||
markets[UNKNOWN_KEY].valueInBaseCurrency;
|
).toNumber();
|
||||||
|
|
||||||
markets.developedMarkets.valueInPercentage =
|
markets.developedMarkets.valueInPercentage =
|
||||||
markets.developedMarkets.valueInBaseCurrency / marketsTotal;
|
markets.developedMarkets.valueInBaseCurrency / marketsTotalInBaseCurrency;
|
||||||
markets.emergingMarkets.valueInPercentage =
|
markets.emergingMarkets.valueInPercentage =
|
||||||
markets.emergingMarkets.valueInBaseCurrency / marketsTotal;
|
markets.emergingMarkets.valueInBaseCurrency / marketsTotalInBaseCurrency;
|
||||||
markets.otherMarkets.valueInPercentage =
|
markets.otherMarkets.valueInPercentage =
|
||||||
markets.otherMarkets.valueInBaseCurrency / marketsTotal;
|
markets.otherMarkets.valueInBaseCurrency / marketsTotalInBaseCurrency;
|
||||||
markets[UNKNOWN_KEY].valueInPercentage =
|
markets[UNKNOWN_KEY].valueInPercentage =
|
||||||
markets[UNKNOWN_KEY].valueInBaseCurrency / marketsTotal;
|
markets[UNKNOWN_KEY].valueInBaseCurrency / marketsTotalInBaseCurrency;
|
||||||
|
|
||||||
const marketsAdvancedTotal =
|
const marketsAdvancedTotal =
|
||||||
marketsAdvanced.asiaPacific.valueInBaseCurrency +
|
marketsAdvanced.asiaPacific.valueInBaseCurrency +
|
||||||
|
@ -19,11 +19,11 @@ import { RedisCacheService } from './redis-cache.service';
|
|||||||
configurationService.get('REDIS_PASSWORD')
|
configurationService.get('REDIS_PASSWORD')
|
||||||
);
|
);
|
||||||
|
|
||||||
return <RedisClientOptions>{
|
return {
|
||||||
store: redisStore,
|
store: redisStore,
|
||||||
ttl: configurationService.get('CACHE_TTL'),
|
ttl: configurationService.get('CACHE_TTL'),
|
||||||
url: `redis://${redisPassword ? `:${redisPassword}` : ''}@${configurationService.get('REDIS_HOST')}:${configurationService.get('REDIS_PORT')}/${configurationService.get('REDIS_DB')}`
|
url: `redis://${redisPassword ? `:${redisPassword}` : ''}@${configurationService.get('REDIS_HOST')}:${configurationService.get('REDIS_PORT')}/${configurationService.get('REDIS_DB')}`
|
||||||
};
|
} as RedisClientOptions;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ConfigurationModule
|
ConfigurationModule
|
||||||
|
@ -95,7 +95,7 @@ export class SubscriptionController {
|
|||||||
@Res() response: Response
|
@Res() response: Response
|
||||||
) {
|
) {
|
||||||
const userId = await this.subscriptionService.createSubscriptionViaStripe(
|
const userId = await this.subscriptionService.createSubscriptionViaStripe(
|
||||||
<string>request.query.checkoutSessionId
|
request.query.checkoutSessionId as string
|
||||||
);
|
);
|
||||||
|
|
||||||
Logger.log(
|
Logger.log(
|
||||||
@ -113,7 +113,7 @@ export class SubscriptionController {
|
|||||||
@Post('stripe/checkout-session')
|
@Post('stripe/checkout-session')
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
public async createCheckoutSession(
|
public async createCheckoutSession(
|
||||||
@Body() { couponId, priceId }: { couponId: string; priceId: string }
|
@Body() { couponId, priceId }: { couponId?: string; priceId: string }
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return this.subscriptionService.createCheckoutSession({
|
return this.subscriptionService.createCheckoutSession({
|
||||||
|
@ -124,7 +124,9 @@ export class SubscriptionService {
|
|||||||
let offer: SubscriptionOffer = price ? 'renewal' : 'default';
|
let offer: SubscriptionOffer = price ? 'renewal' : 'default';
|
||||||
|
|
||||||
if (isBefore(createdAt, parseDate('2023-01-01'))) {
|
if (isBefore(createdAt, parseDate('2023-01-01'))) {
|
||||||
offer = 'renewal-early-bird';
|
offer = 'renewal-early-bird-2023';
|
||||||
|
} else if (isBefore(createdAt, parseDate('2024-01-01'))) {
|
||||||
|
offer = 'renewal-early-bird-2024';
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code';
|
import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code';
|
||||||
|
import { XRayRulesSettings } from '@ghostfolio/common/interfaces';
|
||||||
import type {
|
import type {
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
DateRange,
|
DateRange,
|
||||||
HoldingsViewMode,
|
HoldingsViewMode,
|
||||||
ViewMode,
|
ViewMode
|
||||||
XRayRulesSettings
|
|
||||||
} from '@ghostfolio/common/types';
|
} from '@ghostfolio/common/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -31,11 +31,11 @@ export class UpdateUserSettingDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
benchmark?: string;
|
benchmark?: string;
|
||||||
|
|
||||||
@IsIn(<ColorScheme[]>['DARK', 'LIGHT'])
|
@IsIn(['DARK', 'LIGHT'] as ColorScheme[])
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
colorScheme?: ColorScheme;
|
colorScheme?: ColorScheme;
|
||||||
|
|
||||||
@IsIn(<DateRange[]>[
|
@IsIn([
|
||||||
'1d',
|
'1d',
|
||||||
'1y',
|
'1y',
|
||||||
'5y',
|
'5y',
|
||||||
@ -48,7 +48,7 @@ export class UpdateUserSettingDto {
|
|||||||
return format(date, 'yyyy');
|
return format(date, 'yyyy');
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
])
|
] as DateRange[])
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
dateRange?: DateRange;
|
dateRange?: DateRange;
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class UpdateUserSettingDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
'filters.tags'?: string[];
|
'filters.tags'?: string[];
|
||||||
|
|
||||||
@IsIn(<HoldingsViewMode[]>['CHART', 'TABLE'])
|
@IsIn(['CHART', 'TABLE'] as HoldingsViewMode[])
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
holdingsViewMode?: HoldingsViewMode;
|
holdingsViewMode?: HoldingsViewMode;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ export class UpdateUserSettingDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
savingsRate?: number;
|
savingsRate?: number;
|
||||||
|
|
||||||
@IsIn(<ViewMode[]>['DEFAULT', 'ZEN'])
|
@IsIn(['DEFAULT', 'ZEN'] as ViewMode[])
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
viewMode?: ViewMode;
|
viewMode?: ViewMode;
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ export class UserController {
|
|||||||
|
|
||||||
const userSettings: UserSettings = merge(
|
const userSettings: UserSettings = merge(
|
||||||
{},
|
{},
|
||||||
<UserSettings>this.request.user.Settings.settings,
|
this.request.user.Settings.settings as UserSettings,
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -116,8 +116,8 @@ export class UserService {
|
|||||||
accounts: Account,
|
accounts: Account,
|
||||||
dateOfFirstActivity: firstActivity?.date ?? new Date(),
|
dateOfFirstActivity: firstActivity?.date ?? new Date(),
|
||||||
settings: {
|
settings: {
|
||||||
...(<UserSettings>Settings.settings),
|
...(Settings.settings as UserSettings),
|
||||||
locale: (<UserSettings>Settings.settings)?.locale ?? aLocale
|
locale: (Settings.settings as UserSettings)?.locale ?? aLocale
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export function redactAttributes({
|
|||||||
object: any;
|
object: any;
|
||||||
options: { attribute: string; valueMap: { [key: string]: any } }[];
|
options: { attribute: string; valueMap: { [key: string]: any } }[];
|
||||||
}): any {
|
}): any {
|
||||||
if (!object || !options || !options.length) {
|
if (!object || !options?.length) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,28 +34,28 @@ export class ApiService {
|
|||||||
|
|
||||||
const filters = [
|
const filters = [
|
||||||
...accountIds.map((accountId) => {
|
...accountIds.map((accountId) => {
|
||||||
return <Filter>{
|
return {
|
||||||
id: accountId,
|
id: accountId,
|
||||||
type: 'ACCOUNT'
|
type: 'ACCOUNT'
|
||||||
};
|
} as Filter;
|
||||||
}),
|
}),
|
||||||
...assetClasses.map((assetClass) => {
|
...assetClasses.map((assetClass) => {
|
||||||
return <Filter>{
|
return {
|
||||||
id: assetClass,
|
id: assetClass,
|
||||||
type: 'ASSET_CLASS'
|
type: 'ASSET_CLASS'
|
||||||
};
|
} as Filter;
|
||||||
}),
|
}),
|
||||||
...assetSubClasses.map((assetClass) => {
|
...assetSubClasses.map((assetClass) => {
|
||||||
return <Filter>{
|
return {
|
||||||
id: assetClass,
|
id: assetClass,
|
||||||
type: 'ASSET_SUB_CLASS'
|
type: 'ASSET_SUB_CLASS'
|
||||||
};
|
} as Filter;
|
||||||
}),
|
}),
|
||||||
...tagIds.map((tagId) => {
|
...tagIds.map((tagId) => {
|
||||||
return <Filter>{
|
return {
|
||||||
id: tagId,
|
id: tagId,
|
||||||
type: 'TAG'
|
type: 'TAG'
|
||||||
};
|
} as Filter;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -144,21 +144,21 @@ export class MarketDataService {
|
|||||||
({ dataSource, date, marketPrice, symbol, state }) => {
|
({ dataSource, date, marketPrice, symbol, state }) => {
|
||||||
return this.prismaService.marketData.upsert({
|
return this.prismaService.marketData.upsert({
|
||||||
create: {
|
create: {
|
||||||
dataSource: <DataSource>dataSource,
|
dataSource: dataSource as DataSource,
|
||||||
date: <Date>date,
|
date: date as Date,
|
||||||
marketPrice: <number>marketPrice,
|
marketPrice: marketPrice as number,
|
||||||
state: <MarketDataState>state,
|
state: state as MarketDataState,
|
||||||
symbol: <string>symbol
|
symbol: symbol as string
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
marketPrice: <number>marketPrice,
|
marketPrice: marketPrice as number,
|
||||||
state: <MarketDataState>state
|
state: state as MarketDataState
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
dataSource_date_symbol: {
|
dataSource_date_symbol: {
|
||||||
dataSource: <DataSource>dataSource,
|
dataSource: dataSource as DataSource,
|
||||||
date: <Date>date,
|
date: date as Date,
|
||||||
symbol: <string>symbol
|
symbol: symbol as string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -78,7 +78,7 @@ export class DataGatheringProcessor {
|
|||||||
public async gatherHistoricalMarketData(job: Job<IDataGatheringItem>) {
|
public async gatherHistoricalMarketData(job: Job<IDataGatheringItem>) {
|
||||||
try {
|
try {
|
||||||
const { dataSource, date, symbol } = job.data;
|
const { dataSource, date, symbol } = job.data;
|
||||||
let currentDate = parseISO(<string>(<unknown>date));
|
let currentDate = parseISO(date as unknown as string);
|
||||||
|
|
||||||
Logger.log(
|
Logger.log(
|
||||||
`Historical market data gathering has been started for ${symbol} (${dataSource}) at ${format(
|
`Historical market data gathering has been started for ${symbol} (${dataSource}) at ${format(
|
||||||
|
@ -94,10 +94,10 @@ export class PortfolioSnapshotProcessor {
|
|||||||
filters: job.data.filters,
|
filters: job.data.filters,
|
||||||
userId: job.data.userId
|
userId: job.data.userId
|
||||||
}),
|
}),
|
||||||
JSON.stringify(<PortfolioSnapshotValue>(<unknown>{
|
JSON.stringify({
|
||||||
expiration: expiration.getTime(),
|
expiration: expiration.getTime(),
|
||||||
portfolioSnapshot: snapshot
|
portfolioSnapshot: snapshot
|
||||||
})),
|
} as unknown as PortfolioSnapshotValue),
|
||||||
CACHE_TTL_INFINITE
|
CACHE_TTL_INFINITE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ export class SymbolProfileService {
|
|||||||
countries: this.getCountries(
|
countries: this.getCountries(
|
||||||
symbolProfile?.countries as unknown as Prisma.JsonArray
|
symbolProfile?.countries as unknown as Prisma.JsonArray
|
||||||
),
|
),
|
||||||
dateOfFirstActivity: <Date>undefined,
|
dateOfFirstActivity: undefined as Date,
|
||||||
holdings: this.getHoldings(symbolProfile),
|
holdings: this.getHoldings(symbolProfile),
|
||||||
scraperConfiguration: this.getScraperConfiguration(symbolProfile),
|
scraperConfiguration: this.getScraperConfiguration(symbolProfile),
|
||||||
sectors: this.getSectors(symbolProfile),
|
sectors: this.getSectors(symbolProfile),
|
||||||
|
@ -292,7 +292,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
const dialogRef = this.dialog.open(GfHoldingDetailDialogComponent, {
|
const dialogRef = this.dialog.open(GfHoldingDetailDialogComponent, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <HoldingDetailDialogParams>{
|
data: {
|
||||||
dataSource,
|
dataSource,
|
||||||
symbol,
|
symbol,
|
||||||
baseCurrency: this.user?.settings?.baseCurrency,
|
baseCurrency: this.user?.settings?.baseCurrency,
|
||||||
@ -312,7 +312,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
hasPermission(this.user?.permissions, permissions.updateOrder) &&
|
hasPermission(this.user?.permissions, permissions.updateOrder) &&
|
||||||
!this.user?.settings?.isRestrictedView,
|
!this.user?.settings?.isRestrictedView,
|
||||||
locale: this.user?.settings?.locale
|
locale: this.user?.settings?.locale
|
||||||
},
|
} as HoldingDetailDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -47,8 +47,7 @@ export class AccountsTableComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<AccountModel> =
|
public dataSource = new MatTableDataSource<AccountModel>();
|
||||||
new MatTableDataSource();
|
|
||||||
public displayedColumns = [];
|
public displayedColumns = [];
|
||||||
public isLoading = true;
|
public isLoading = true;
|
||||||
public routeQueryParams: Subscription;
|
public routeQueryParams: Subscription;
|
||||||
|
@ -35,10 +35,10 @@ export class AdminJobsComponent implements OnDestroy, OnInit {
|
|||||||
DATA_GATHERING_QUEUE_PRIORITY_HIGH;
|
DATA_GATHERING_QUEUE_PRIORITY_HIGH;
|
||||||
public DATA_GATHERING_QUEUE_PRIORITY_MEDIUM =
|
public DATA_GATHERING_QUEUE_PRIORITY_MEDIUM =
|
||||||
DATA_GATHERING_QUEUE_PRIORITY_MEDIUM;
|
DATA_GATHERING_QUEUE_PRIORITY_MEDIUM;
|
||||||
|
|
||||||
|
public dataSource = new MatTableDataSource<AdminJobs['jobs'][0]>();
|
||||||
public defaultDateTimeFormat: string;
|
public defaultDateTimeFormat: string;
|
||||||
public filterForm: FormGroup;
|
public filterForm: FormGroup;
|
||||||
public dataSource: MatTableDataSource<AdminJobs['jobs'][0]> =
|
|
||||||
new MatTableDataSource();
|
|
||||||
public displayedColumns = [
|
public displayedColumns = [
|
||||||
'index',
|
'index',
|
||||||
'type',
|
'type',
|
||||||
|
@ -178,14 +178,14 @@ export class AdminMarketDataDetailComponent implements OnChanges {
|
|||||||
const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice;
|
const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice;
|
||||||
|
|
||||||
const dialogRef = this.dialog.open(MarketDataDetailDialog, {
|
const dialogRef = this.dialog.open(MarketDataDetailDialog, {
|
||||||
data: <MarketDataDetailDialogParams>{
|
data: {
|
||||||
marketPrice,
|
marketPrice,
|
||||||
currency: this.currency,
|
currency: this.currency,
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
dateString: `${yearMonth}-${day}`,
|
dateString: `${yearMonth}-${day}`,
|
||||||
symbol: this.symbol,
|
symbol: this.symbol,
|
||||||
user: this.user
|
user: this.user
|
||||||
},
|
} as MarketDataDetailDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -71,36 +71,35 @@ export class AdminMarketDataComponent
|
|||||||
return {
|
return {
|
||||||
id: assetSubClass.toString(),
|
id: assetSubClass.toString(),
|
||||||
label: translate(assetSubClass),
|
label: translate(assetSubClass),
|
||||||
type: <Filter['type']>'ASSET_SUB_CLASS'
|
type: 'ASSET_SUB_CLASS' as Filter['type']
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.concat([
|
.concat([
|
||||||
{
|
{
|
||||||
id: 'BENCHMARKS',
|
id: 'BENCHMARKS',
|
||||||
label: $localize`Benchmarks`,
|
label: $localize`Benchmarks`,
|
||||||
type: <Filter['type']>'PRESET_ID'
|
type: 'PRESET_ID' as Filter['type']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'CURRENCIES',
|
id: 'CURRENCIES',
|
||||||
label: $localize`Currencies`,
|
label: $localize`Currencies`,
|
||||||
type: <Filter['type']>'PRESET_ID'
|
type: 'PRESET_ID' as Filter['type']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ETF_WITHOUT_COUNTRIES',
|
id: 'ETF_WITHOUT_COUNTRIES',
|
||||||
label: $localize`ETFs without Countries`,
|
label: $localize`ETFs without Countries`,
|
||||||
type: <Filter['type']>'PRESET_ID'
|
type: 'PRESET_ID' as Filter['type']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ETF_WITHOUT_SECTORS',
|
id: 'ETF_WITHOUT_SECTORS',
|
||||||
label: $localize`ETFs without Sectors`,
|
label: $localize`ETFs without Sectors`,
|
||||||
type: <Filter['type']>'PRESET_ID'
|
type: 'PRESET_ID' as Filter['type']
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
public benchmarks: Partial<SymbolProfile>[];
|
public benchmarks: Partial<SymbolProfile>[];
|
||||||
public currentDataSource: DataSource;
|
public currentDataSource: DataSource;
|
||||||
public currentSymbol: string;
|
public currentSymbol: string;
|
||||||
public dataSource: MatTableDataSource<AdminMarketDataItem> =
|
public dataSource = new MatTableDataSource<AdminMarketDataItem>();
|
||||||
new MatTableDataSource();
|
|
||||||
public defaultDateFormat: string;
|
public defaultDateFormat: string;
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public displayedColumns: string[] = [];
|
public displayedColumns: string[] = [];
|
||||||
@ -375,13 +374,13 @@ export class AdminMarketDataComponent
|
|||||||
|
|
||||||
const dialogRef = this.dialog.open(AssetProfileDialog, {
|
const dialogRef = this.dialog.open(AssetProfileDialog, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <AssetProfileDialogParams>{
|
data: {
|
||||||
dataSource,
|
dataSource,
|
||||||
symbol,
|
symbol,
|
||||||
colorScheme: this.user?.settings.colorScheme,
|
colorScheme: this.user?.settings.colorScheme,
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
locale: this.user?.settings?.locale
|
locale: this.user?.settings?.locale
|
||||||
},
|
} as AssetProfileDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
@ -404,10 +403,10 @@ export class AdminMarketDataComponent
|
|||||||
|
|
||||||
const dialogRef = this.dialog.open(CreateAssetProfileDialog, {
|
const dialogRef = this.dialog.open(CreateAssetProfileDialog, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <CreateAssetProfileDialogParams>{
|
data: {
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
locale: this.user?.settings?.locale
|
locale: this.user?.settings?.locale
|
||||||
},
|
} as CreateAssetProfileDialogParams,
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -225,10 +225,10 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
$localize`Please set your system message:`,
|
$localize`Please set your system message:`,
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
this.systemMessage ??
|
this.systemMessage ??
|
||||||
<SystemMessage>{
|
({
|
||||||
message: '⚒️ Scheduled maintenance in progress...',
|
message: '⚒️ Scheduled maintenance in progress...',
|
||||||
targetGroups: ['Basic', 'Premium']
|
targetGroups: ['Basic', 'Premium']
|
||||||
}
|
} as SystemMessage)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ import { CreateOrUpdatePlatformDialog } from './create-or-update-platform-dialog
|
|||||||
export class AdminPlatformComponent implements OnInit, OnDestroy {
|
export class AdminPlatformComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<Platform> = new MatTableDataSource();
|
public dataSource = new MatTableDataSource<Platform>();
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public displayedColumns = ['name', 'url', 'accounts', 'actions'];
|
public displayedColumns = ['name', 'url', 'accounts', 'actions'];
|
||||||
public platforms: Platform[];
|
public platforms: Platform[];
|
||||||
|
@ -34,7 +34,7 @@ import { CreateOrUpdateTagDialog } from './create-or-update-tag-dialog/create-or
|
|||||||
export class AdminTagComponent implements OnInit, OnDestroy {
|
export class AdminTagComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<Tag> = new MatTableDataSource();
|
public dataSource = new MatTableDataSource<Tag>();
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public displayedColumns = ['name', 'userId', 'activities', 'actions'];
|
public displayedColumns = ['name', 'userId', 'activities', 'actions'];
|
||||||
public tags: Tag[];
|
public tags: Tag[];
|
||||||
|
@ -24,8 +24,7 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
templateUrl: './admin-users.html'
|
templateUrl: './admin-users.html'
|
||||||
})
|
})
|
||||||
export class AdminUsersComponent implements OnDestroy, OnInit {
|
export class AdminUsersComponent implements OnDestroy, OnInit {
|
||||||
public dataSource: MatTableDataSource<AdminUsers['users'][0]> =
|
public dataSource = new MatTableDataSource<AdminUsers['users'][0]>();
|
||||||
new MatTableDataSource();
|
|
||||||
public defaultDateFormat: string;
|
public defaultDateFormat: string;
|
||||||
public displayedColumns: string[] = [];
|
public displayedColumns: string[] = [];
|
||||||
public getEmojiFlag = getEmojiFlag;
|
public getEmojiFlag = getEmojiFlag;
|
||||||
|
@ -98,7 +98,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initialize() {
|
private initialize() {
|
||||||
const benchmarkDataValues: { [date: string]: number } = {};
|
const benchmarkDataValues: Record<string, number> = {};
|
||||||
|
|
||||||
for (const { date, value } of this.benchmarkDataItems) {
|
for (const { date, value } of this.benchmarkDataItems) {
|
||||||
benchmarkDataValues[date] = value;
|
benchmarkDataValues[date] = value;
|
||||||
@ -133,9 +133,8 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
|
|||||||
if (this.chartCanvas) {
|
if (this.chartCanvas) {
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.data = data;
|
this.chart.data = data;
|
||||||
this.chart.options.plugins.tooltip = <unknown>(
|
this.chart.options.plugins.tooltip =
|
||||||
this.getTooltipPluginConfiguration()
|
this.getTooltipPluginConfiguration() as unknown;
|
||||||
);
|
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
} else {
|
} else {
|
||||||
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
||||||
@ -154,7 +153,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
|
|||||||
},
|
},
|
||||||
interaction: { intersect: false, mode: 'index' },
|
interaction: { intersect: false, mode: 'index' },
|
||||||
maintainAspectRatio: true,
|
maintainAspectRatio: true,
|
||||||
plugins: <unknown>{
|
plugins: {
|
||||||
annotation: {
|
annotation: {
|
||||||
annotations: {
|
annotations: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
@ -173,7 +172,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
|
|||||||
verticalHoverLine: {
|
verticalHoverLine: {
|
||||||
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
||||||
}
|
}
|
||||||
},
|
} as unknown,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@ -238,7 +237,7 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
|
|||||||
unit: '%'
|
unit: '%'
|
||||||
}),
|
}),
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
position: <unknown>'top',
|
position: 'top' as unknown,
|
||||||
xAlign: 'center',
|
xAlign: 'center',
|
||||||
yAlign: 'bottom'
|
yAlign: 'bottom'
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,8 @@
|
|||||||
<ng-container i18n>Upgrade Plan</ng-container>
|
<ng-container i18n>Upgrade Plan</ng-container>
|
||||||
} @else if (
|
} @else if (
|
||||||
user.subscription.offer === 'renewal' ||
|
user.subscription.offer === 'renewal' ||
|
||||||
user.subscription.offer === 'renewal-early-bird'
|
user.subscription.offer === 'renewal-early-bird-2023' ||
|
||||||
|
user.subscription.offer === 'renewal-early-bird-2024'
|
||||||
) {
|
) {
|
||||||
<ng-container i18n>Renew Plan</ng-container>
|
<ng-container i18n>Renew Plan</ng-container>
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.activityForm = this.formBuilder.group({
|
this.activityForm = this.formBuilder.group({
|
||||||
tags: <string[]>[]
|
tags: [] as string[]
|
||||||
});
|
});
|
||||||
|
|
||||||
const filters: Filter[] = [
|
const filters: Filter[] = [
|
||||||
|
@ -4,14 +4,11 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
|
|||||||
import {
|
import {
|
||||||
AssetProfileIdentifier,
|
AssetProfileIdentifier,
|
||||||
PortfolioPosition,
|
PortfolioPosition,
|
||||||
|
ToggleOption,
|
||||||
User
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import {
|
import { HoldingType, HoldingsViewMode } from '@ghostfolio/common/types';
|
||||||
HoldingType,
|
|
||||||
HoldingsViewMode,
|
|
||||||
ToggleOption
|
|
||||||
} from '@ghostfolio/common/types';
|
|
||||||
|
|
||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
|
@ -154,9 +154,8 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
if (this.chartCanvas) {
|
if (this.chartCanvas) {
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.data = chartData;
|
this.chart.data = chartData;
|
||||||
this.chart.options.plugins.tooltip = <unknown>(
|
this.chart.options.plugins.tooltip =
|
||||||
this.getTooltipPluginConfiguration()
|
this.getTooltipPluginConfiguration() as unknown;
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.savingsRate &&
|
this.savingsRate &&
|
||||||
@ -186,7 +185,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
},
|
},
|
||||||
interaction: { intersect: false, mode: 'index' },
|
interaction: { intersect: false, mode: 'index' },
|
||||||
maintainAspectRatio: true,
|
maintainAspectRatio: true,
|
||||||
plugins: <unknown>{
|
plugins: {
|
||||||
annotation: {
|
annotation: {
|
||||||
annotations: {
|
annotations: {
|
||||||
savingsRate: this.savingsRate
|
savingsRate: this.savingsRate
|
||||||
@ -227,7 +226,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
verticalHoverLine: {
|
verticalHoverLine: {
|
||||||
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
||||||
}
|
}
|
||||||
},
|
} as unknown,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
@ -294,7 +293,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
unit: this.isInPercent ? '%' : undefined
|
unit: this.isInPercent ? '%' : undefined
|
||||||
}),
|
}),
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
position: <unknown>'top',
|
position: 'top' as unknown,
|
||||||
xAlign: 'center',
|
xAlign: 'center',
|
||||||
yAlign: 'bottom'
|
yAlign: 'bottom'
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
|
import {
|
||||||
import { XRayRulesSettings } from '@ghostfolio/common/types';
|
PortfolioReportRule,
|
||||||
|
XRayRulesSettings
|
||||||
|
} from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
export interface IRuleSettingsDialogParams {
|
export interface IRuleSettingsDialogParams {
|
||||||
rule: PortfolioReportRule;
|
rule: PortfolioReportRule;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { XRayRulesSettings } from '@ghostfolio/common/types';
|
import { XRayRulesSettings } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
|
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
|
||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
|
import {
|
||||||
import { XRayRulesSettings } from '@ghostfolio/common/types';
|
PortfolioReportRule,
|
||||||
|
XRayRulesSettings
|
||||||
|
} from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
|
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
|
||||||
import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
|
import {
|
||||||
import { XRayRulesSettings } from '@ghostfolio/common/types';
|
PortfolioReportRule,
|
||||||
|
XRayRulesSettings
|
||||||
|
} from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ToggleOption } from '@ghostfolio/common/types';
|
import { ToggleOption } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
<ng-container i18n>Upgrade Plan</ng-container>
|
<ng-container i18n>Upgrade Plan</ng-container>
|
||||||
} @else if (
|
} @else if (
|
||||||
user.subscription.offer === 'renewal' ||
|
user.subscription.offer === 'renewal' ||
|
||||||
user.subscription.offer === 'renewal-early-bird'
|
user.subscription.offer === 'renewal-early-bird-2023' ||
|
||||||
|
user.subscription.offer === 'renewal-early-bird-2024'
|
||||||
) {
|
) {
|
||||||
<ng-container i18n>Renew Plan</ng-container>
|
<ng-container i18n>Renew Plan</ng-container>
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
private openAccountDetailDialog(aAccountId: string) {
|
private openAccountDetailDialog(aAccountId: string) {
|
||||||
const dialogRef = this.dialog.open(AccountDetailDialog, {
|
const dialogRef = this.dialog.open(AccountDetailDialog, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <AccountDetailDialogParams>{
|
data: {
|
||||||
accountId: aAccountId,
|
accountId: aAccountId,
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
hasImpersonationId: this.hasImpersonationId,
|
hasImpersonationId: this.hasImpersonationId,
|
||||||
@ -230,7 +230,7 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
!this.hasImpersonationId &&
|
!this.hasImpersonationId &&
|
||||||
hasPermission(this.user?.permissions, permissions.createOrder) &&
|
hasPermission(this.user?.permissions, permissions.createOrder) &&
|
||||||
!this.user?.settings?.isRestrictedView
|
!this.user?.settings?.isRestrictedView
|
||||||
},
|
} as AccountDetailDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -218,10 +218,10 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
public onImport() {
|
public onImport() {
|
||||||
const dialogRef = this.dialog.open(ImportActivitiesDialog, {
|
const dialogRef = this.dialog.open(ImportActivitiesDialog, {
|
||||||
data: <ImportActivitiesDialogParams>{
|
data: {
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
user: this.user
|
user: this.user
|
||||||
},
|
} as ImportActivitiesDialogParams,
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -235,11 +235,11 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
public onImportDividends() {
|
public onImportDividends() {
|
||||||
const dialogRef = this.dialog.open(ImportActivitiesDialog, {
|
const dialogRef = this.dialog.open(ImportActivitiesDialog, {
|
||||||
data: <ImportActivitiesDialogParams>{
|
data: {
|
||||||
activityTypes: ['DIVIDEND'],
|
activityTypes: ['DIVIDEND'],
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
user: this.user
|
user: this.user
|
||||||
},
|
} as ImportActivitiesDialogParams,
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -505,7 +505,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
private openAccountDetailDialog(aAccountId: string) {
|
private openAccountDetailDialog(aAccountId: string) {
|
||||||
const dialogRef = this.dialog.open(AccountDetailDialog, {
|
const dialogRef = this.dialog.open(AccountDetailDialog, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <AccountDetailDialogParams>{
|
data: {
|
||||||
accountId: aAccountId,
|
accountId: aAccountId,
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
hasImpersonationId: this.hasImpersonationId,
|
hasImpersonationId: this.hasImpersonationId,
|
||||||
@ -513,7 +513,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
!this.hasImpersonationId &&
|
!this.hasImpersonationId &&
|
||||||
hasPermission(this.user?.permissions, permissions.createOrder) &&
|
hasPermission(this.user?.permissions, permissions.createOrder) &&
|
||||||
!this.user?.settings?.isRestrictedView
|
!this.user?.settings?.isRestrictedView
|
||||||
},
|
} as AccountDetailDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -5,13 +5,14 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
|
|||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import {
|
import {
|
||||||
HistoricalDataItem,
|
HistoricalDataItem,
|
||||||
|
InvestmentItem,
|
||||||
PortfolioInvestments,
|
PortfolioInvestments,
|
||||||
PortfolioPerformance,
|
PortfolioPerformance,
|
||||||
PortfolioPosition,
|
PortfolioPosition,
|
||||||
|
ToggleOption,
|
||||||
User
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
|
import { GroupBy } from '@ghostfolio/common/types';
|
||||||
import { GroupBy, ToggleOption } from '@ghostfolio/common/types';
|
|
||||||
import { translate } from '@ghostfolio/ui/i18n';
|
import { translate } from '@ghostfolio/ui/i18n';
|
||||||
|
|
||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
@ -278,7 +278,8 @@
|
|||||||
<ng-container i18n>Upgrade Plan</ng-container>
|
<ng-container i18n>Upgrade Plan</ng-container>
|
||||||
} @else if (
|
} @else if (
|
||||||
user.subscription.offer === 'renewal' ||
|
user.subscription.offer === 'renewal' ||
|
||||||
user.subscription.offer === 'renewal-early-bird'
|
user.subscription.offer === 'renewal-early-bird-2023' ||
|
||||||
|
user.subscription.offer === 'renewal-early-bird-2024'
|
||||||
) {
|
) {
|
||||||
<ng-container i18n>Renew Plan</ng-container>
|
<ng-container i18n>Renew Plan</ng-container>
|
||||||
}
|
}
|
||||||
|
@ -230,8 +230,8 @@ export class DataService {
|
|||||||
public fetchActivity(aActivityId: string) {
|
public fetchActivity(aActivityId: string) {
|
||||||
return this.http.get<Activity>(`/api/v1/order/${aActivityId}`).pipe(
|
return this.http.get<Activity>(`/api/v1/order/${aActivityId}`).pipe(
|
||||||
map((activity) => {
|
map((activity) => {
|
||||||
activity.createdAt = parseISO(<string>(<unknown>activity.createdAt));
|
activity.createdAt = parseISO(activity.createdAt as unknown as string);
|
||||||
activity.date = parseISO(<string>(<unknown>activity.date));
|
activity.date = parseISO(activity.date as unknown as string);
|
||||||
|
|
||||||
return activity;
|
return activity;
|
||||||
})
|
})
|
||||||
@ -387,8 +387,8 @@ export class DataService {
|
|||||||
map((data) => {
|
map((data) => {
|
||||||
if (data.orders) {
|
if (data.orders) {
|
||||||
for (const order of data.orders) {
|
for (const order of data.orders) {
|
||||||
order.createdAt = parseISO(<string>(<unknown>order.createdAt));
|
order.createdAt = parseISO(order.createdAt as unknown as string);
|
||||||
order.date = parseISO(<string>(<unknown>order.date));
|
order.date = parseISO(order.date as unknown as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,9 +399,9 @@ export class DataService {
|
|||||||
|
|
||||||
public fetchInfo(): InfoItem {
|
public fetchInfo(): InfoItem {
|
||||||
const info = cloneDeep((window as any).info);
|
const info = cloneDeep((window as any).info);
|
||||||
const utmSource = <'ios' | 'trusted-web-activity'>(
|
const utmSource = window.localStorage.getItem('utm_source') as
|
||||||
window.localStorage.getItem('utm_source')
|
| 'ios'
|
||||||
);
|
| 'trusted-web-activity';
|
||||||
|
|
||||||
info.globalPermissions = filterGlobalPermissions(
|
info.globalPermissions = filterGlobalPermissions(
|
||||||
info.globalPermissions,
|
info.globalPermissions,
|
||||||
@ -715,9 +715,9 @@ export class DataService {
|
|||||||
|
|
||||||
public updateInfo() {
|
public updateInfo() {
|
||||||
this.http.get<InfoItem>('/api/v1/info').subscribe((info) => {
|
this.http.get<InfoItem>('/api/v1/info').subscribe((info) => {
|
||||||
const utmSource = <'ios' | 'trusted-web-activity'>(
|
const utmSource = window.localStorage.getItem('utm_source') as
|
||||||
window.localStorage.getItem('utm_source')
|
| 'ios'
|
||||||
);
|
| 'trusted-web-activity';
|
||||||
|
|
||||||
info.globalPermissions = filterGlobalPermissions(
|
info.globalPermissions = filterGlobalPermissions(
|
||||||
info.globalPermissions,
|
info.globalPermissions,
|
||||||
|
@ -104,9 +104,9 @@ export class UserService extends ObservableStore<UserStoreState> {
|
|||||||
) {
|
) {
|
||||||
const dialogRef = this.dialog.open(SubscriptionInterstitialDialog, {
|
const dialogRef = this.dialog.open(SubscriptionInterstitialDialog, {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
data: <SubscriptionInterstitialDialogParams>{
|
data: {
|
||||||
user
|
user
|
||||||
},
|
} as SubscriptionInterstitialDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
height: this.deviceType === 'mobile' ? '98vh' : '80vh',
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ export async function validateObjectForForm<T>({
|
|||||||
ignoreFields = [],
|
ignoreFields = [],
|
||||||
object
|
object
|
||||||
}: {
|
}: {
|
||||||
classDto: { new (): T };
|
classDto: new () => T;
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
ignoreFields?: string[];
|
ignoreFields?: string[];
|
||||||
object: T;
|
object: T;
|
||||||
|
@ -12,9 +12,9 @@ import { environment } from './environments/environment';
|
|||||||
(async () => {
|
(async () => {
|
||||||
const response = await fetch('/api/v1/info');
|
const response = await fetch('/api/v1/info');
|
||||||
const info: InfoItem = await response.json();
|
const info: InfoItem = await response.json();
|
||||||
const utmSource = <'ios' | 'trusted-web-activity'>(
|
const utmSource = window.localStorage.getItem('utm_source') as
|
||||||
window.localStorage.getItem('utm_source')
|
| 'ios'
|
||||||
);
|
| 'trusted-web-activity';
|
||||||
|
|
||||||
info.globalPermissions = filterGlobalPermissions(
|
info.globalPermissions = filterGlobalPermissions(
|
||||||
info.globalPermissions,
|
info.globalPermissions,
|
||||||
|
@ -125,14 +125,14 @@ export const PROPERTY_SLACK_COMMUNITY_USERS = 'SLACK_COMMUNITY_USERS';
|
|||||||
export const PROPERTY_STRIPE_CONFIG = 'STRIPE_CONFIG';
|
export const PROPERTY_STRIPE_CONFIG = 'STRIPE_CONFIG';
|
||||||
export const PROPERTY_SYSTEM_MESSAGE = 'SYSTEM_MESSAGE';
|
export const PROPERTY_SYSTEM_MESSAGE = 'SYSTEM_MESSAGE';
|
||||||
|
|
||||||
export const QUEUE_JOB_STATUS_LIST = <JobStatus[]>[
|
export const QUEUE_JOB_STATUS_LIST = [
|
||||||
'active',
|
'active',
|
||||||
'completed',
|
'completed',
|
||||||
'delayed',
|
'delayed',
|
||||||
'failed',
|
'failed',
|
||||||
'paused',
|
'paused',
|
||||||
'waiting'
|
'waiting'
|
||||||
];
|
] as JobStatus[];
|
||||||
|
|
||||||
export const REPLACE_NAME_PARTS = [
|
export const REPLACE_NAME_PARTS = [
|
||||||
'Amundi Index Solutions -',
|
'Amundi Index Solutions -',
|
||||||
|
@ -108,7 +108,7 @@ export function downloadAsFile({
|
|||||||
content = JSON.stringify(content, undefined, ' ');
|
content = JSON.stringify(content, undefined, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = new Blob([<string>content], {
|
const file = new Blob([content as string], {
|
||||||
type: contentType
|
type: contentType
|
||||||
});
|
});
|
||||||
a.href = URL.createObjectURL(file);
|
a.href = URL.createObjectURL(file);
|
||||||
|
@ -50,8 +50,10 @@ import type { Subscription } from './subscription.interface';
|
|||||||
import type { SymbolMetrics } from './symbol-metrics.interface';
|
import type { SymbolMetrics } from './symbol-metrics.interface';
|
||||||
import type { SystemMessage } from './system-message.interface';
|
import type { SystemMessage } from './system-message.interface';
|
||||||
import type { TabConfiguration } from './tab-configuration.interface';
|
import type { TabConfiguration } from './tab-configuration.interface';
|
||||||
|
import type { ToggleOption } from './toggle-option.interface';
|
||||||
import type { UserSettings } from './user-settings.interface';
|
import type { UserSettings } from './user-settings.interface';
|
||||||
import type { User } from './user.interface';
|
import type { User } from './user.interface';
|
||||||
|
import type { XRayRulesSettings } from './x-ray-rules-settings.interface';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Access,
|
Access,
|
||||||
@ -104,6 +106,8 @@ export {
|
|||||||
Subscription,
|
Subscription,
|
||||||
SymbolMetrics,
|
SymbolMetrics,
|
||||||
TabConfiguration,
|
TabConfiguration,
|
||||||
|
ToggleOption,
|
||||||
User,
|
User,
|
||||||
UserSettings
|
UserSettings,
|
||||||
|
XRayRulesSettings
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export type ToggleOption = {
|
export interface ToggleOption {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
}
|
@ -1,9 +1,9 @@
|
|||||||
|
import { XRayRulesSettings } from '@ghostfolio/common/interfaces/x-ray-rules-settings.interface';
|
||||||
import {
|
import {
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
DateRange,
|
DateRange,
|
||||||
HoldingsViewMode,
|
HoldingsViewMode,
|
||||||
ViewMode,
|
ViewMode
|
||||||
XRayRulesSettings
|
|
||||||
} from '@ghostfolio/common/types';
|
} from '@ghostfolio/common/types';
|
||||||
|
|
||||||
export interface UserSettings {
|
export interface UserSettings {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export type XRayRulesSettings = {
|
export interface XRayRulesSettings {
|
||||||
AccountClusterRiskCurrentInvestment?: RuleSettings;
|
AccountClusterRiskCurrentInvestment?: RuleSettings;
|
||||||
AccountClusterRiskSingleAccount?: RuleSettings;
|
AccountClusterRiskSingleAccount?: RuleSettings;
|
||||||
AllocationClusterRiskDevelopedMarkets?: RuleSettings;
|
AllocationClusterRiskDevelopedMarkets?: RuleSettings;
|
||||||
@ -7,7 +7,7 @@ export type XRayRulesSettings = {
|
|||||||
CurrencyClusterRiskCurrentInvestment?: RuleSettings;
|
CurrencyClusterRiskCurrentInvestment?: RuleSettings;
|
||||||
EmergencyFundSetup?: RuleSettings;
|
EmergencyFundSetup?: RuleSettings;
|
||||||
FeeRatioInitialInvestment?: RuleSettings;
|
FeeRatioInitialInvestment?: RuleSettings;
|
||||||
};
|
}
|
||||||
|
|
||||||
interface RuleSettings {
|
interface RuleSettings {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
@ -16,10 +16,8 @@ import type { Market } from './market.type';
|
|||||||
import type { OrderWithAccount } from './order-with-account.type';
|
import type { OrderWithAccount } from './order-with-account.type';
|
||||||
import type { RequestWithUser } from './request-with-user.type';
|
import type { RequestWithUser } from './request-with-user.type';
|
||||||
import type { SubscriptionOffer } from './subscription-offer.type';
|
import type { SubscriptionOffer } from './subscription-offer.type';
|
||||||
import type { ToggleOption } from './toggle-option.type';
|
|
||||||
import type { UserWithSettings } from './user-with-settings.type';
|
import type { UserWithSettings } from './user-with-settings.type';
|
||||||
import type { ViewMode } from './view-mode.type';
|
import type { ViewMode } from './view-mode.type';
|
||||||
import type { XRayRulesSettings } from './x-ray-rules-settings.type';
|
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
AccessType,
|
AccessType,
|
||||||
@ -40,8 +38,6 @@ export type {
|
|||||||
OrderWithAccount,
|
OrderWithAccount,
|
||||||
RequestWithUser,
|
RequestWithUser,
|
||||||
SubscriptionOffer,
|
SubscriptionOffer,
|
||||||
ToggleOption,
|
|
||||||
UserWithSettings,
|
UserWithSettings,
|
||||||
ViewMode,
|
ViewMode
|
||||||
XRayRulesSettings
|
|
||||||
};
|
};
|
||||||
|
@ -1 +1,5 @@
|
|||||||
export type SubscriptionOffer = 'default' | 'renewal' | 'renewal-early-bird';
|
export type SubscriptionOffer =
|
||||||
|
| 'default'
|
||||||
|
| 'renewal'
|
||||||
|
| 'renewal-early-bird-2023'
|
||||||
|
| 'renewal-early-bird-2024';
|
||||||
|
@ -74,9 +74,9 @@ export class GfAccountBalancesComponent
|
|||||||
date: new FormControl(new Date(), Validators.required)
|
date: new FormControl(new Date(), Validators.required)
|
||||||
});
|
});
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<
|
public dataSource = new MatTableDataSource<
|
||||||
AccountBalancesResponse['balances'][0]
|
AccountBalancesResponse['balances'][0]
|
||||||
> = new MatTableDataSource();
|
>();
|
||||||
|
|
||||||
public displayedColumns: string[] = ['date', 'value', 'actions'];
|
public displayedColumns: string[] = ['date', 'value', 'actions'];
|
||||||
public Validators = Validators;
|
public Validators = Validators;
|
||||||
|
@ -157,7 +157,7 @@ export class GfActivitiesFilterComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
for (const type of Object.keys(filterGroupsMap)) {
|
for (const type of Object.keys(filterGroupsMap)) {
|
||||||
filterGroups.push({
|
filterGroups.push({
|
||||||
name: <Filter['type']>translate(type),
|
name: translate(type) as Filter['type'],
|
||||||
filters: filterGroupsMap[type]
|
filters: filterGroupsMap[type]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -180,10 +180,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
mergeMap(async (searchTerm) => {
|
mergeMap(async (searchTerm) => {
|
||||||
const result = <ISearchResults>{
|
const result = {
|
||||||
assetProfiles: [],
|
assetProfiles: [],
|
||||||
holdings: []
|
holdings: []
|
||||||
};
|
} as ISearchResults;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
|
@ -109,13 +109,13 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
|||||||
symbol
|
symbol
|
||||||
}: AssetProfileIdentifier) {
|
}: AssetProfileIdentifier) {
|
||||||
const dialogRef = this.dialog.open(GfBenchmarkDetailDialogComponent, {
|
const dialogRef = this.dialog.open(GfBenchmarkDetailDialogComponent, {
|
||||||
data: <BenchmarkDetailDialogParams>{
|
data: {
|
||||||
dataSource,
|
dataSource,
|
||||||
symbol,
|
symbol,
|
||||||
colorScheme: this.user?.settings?.colorScheme,
|
colorScheme: this.user?.settings?.colorScheme,
|
||||||
deviceType: this.deviceType,
|
deviceType: this.deviceType,
|
||||||
locale: this.locale
|
locale: this.locale
|
||||||
},
|
} as BenchmarkDetailDialogParams,
|
||||||
height: this.deviceType === 'mobile' ? '98vh' : undefined,
|
height: this.deviceType === 'mobile' ? '98vh' : undefined,
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
@ -67,8 +67,7 @@ export class GfHoldingsTableComponent implements OnChanges, OnDestroy {
|
|||||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<PortfolioPosition> =
|
public dataSource = new MatTableDataSource<PortfolioPosition>();
|
||||||
new MatTableDataSource();
|
|
||||||
public displayedColumns = [];
|
public displayedColumns = [];
|
||||||
public ignoreAssetSubClasses = [AssetSubClass.CASH];
|
public ignoreAssetSubClasses = [AssetSubClass.CASH];
|
||||||
public isLoading = true;
|
public isLoading = true;
|
||||||
|
@ -172,15 +172,14 @@ export class GfLineChartComponent
|
|||||||
if (this.chartCanvas) {
|
if (this.chartCanvas) {
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.data = data;
|
this.chart.data = data;
|
||||||
this.chart.options.plugins.tooltip = <unknown>(
|
this.chart.options.plugins.tooltip =
|
||||||
this.getTooltipPluginConfiguration()
|
this.getTooltipPluginConfiguration() as unknown;
|
||||||
);
|
|
||||||
this.chart.options.animation =
|
this.chart.options.animation =
|
||||||
this.isAnimated &&
|
this.isAnimated &&
|
||||||
<unknown>{
|
({
|
||||||
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
|
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
|
||||||
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
|
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
|
||||||
};
|
} as unknown);
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
} else {
|
} else {
|
||||||
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
||||||
@ -188,10 +187,10 @@ export class GfLineChartComponent
|
|||||||
options: {
|
options: {
|
||||||
animation:
|
animation:
|
||||||
this.isAnimated &&
|
this.isAnimated &&
|
||||||
<unknown>{
|
({
|
||||||
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
|
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
|
||||||
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
|
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
|
||||||
},
|
} as unknown),
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
elements: {
|
elements: {
|
||||||
point: {
|
point: {
|
||||||
@ -200,7 +199,7 @@ export class GfLineChartComponent
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
interaction: { intersect: false, mode: 'index' },
|
interaction: { intersect: false, mode: 'index' },
|
||||||
plugins: <unknown>{
|
plugins: {
|
||||||
legend: {
|
legend: {
|
||||||
align: 'start',
|
align: 'start',
|
||||||
display: this.showLegend,
|
display: this.showLegend,
|
||||||
@ -210,7 +209,7 @@ export class GfLineChartComponent
|
|||||||
verticalHoverLine: {
|
verticalHoverLine: {
|
||||||
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
|
||||||
}
|
}
|
||||||
},
|
} as unknown,
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
border: {
|
border: {
|
||||||
@ -325,7 +324,7 @@ export class GfLineChartComponent
|
|||||||
unit: this.unit
|
unit: this.unit
|
||||||
}),
|
}),
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
position: <unknown>'top',
|
position: 'top' as unknown,
|
||||||
xAlign: 'center',
|
xAlign: 'center',
|
||||||
yAlign: 'bottom'
|
yAlign: 'bottom'
|
||||||
};
|
};
|
||||||
|
@ -304,14 +304,14 @@ export class GfPortfolioProportionChartComponent
|
|||||||
if (this.chartCanvas) {
|
if (this.chartCanvas) {
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.data = data;
|
this.chart.data = data;
|
||||||
this.chart.options.plugins.tooltip = <unknown>(
|
this.chart.options.plugins.tooltip = this.getTooltipPluginConfiguration(
|
||||||
this.getTooltipPluginConfiguration(data)
|
data
|
||||||
);
|
) as unknown;
|
||||||
this.chart.update();
|
this.chart.update();
|
||||||
} else {
|
} else {
|
||||||
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
||||||
data,
|
data,
|
||||||
options: <unknown>{
|
options: {
|
||||||
animation: false,
|
animation: false,
|
||||||
cutout: '70%',
|
cutout: '70%',
|
||||||
layout: {
|
layout: {
|
||||||
@ -358,7 +358,7 @@ export class GfPortfolioProportionChartComponent
|
|||||||
legend: { display: false },
|
legend: { display: false },
|
||||||
tooltip: this.getTooltipPluginConfiguration(data)
|
tooltip: this.getTooltipPluginConfiguration(data)
|
||||||
}
|
}
|
||||||
},
|
} as unknown,
|
||||||
plugins: [ChartDataLabels],
|
plugins: [ChartDataLabels],
|
||||||
type: 'doughnut'
|
type: 'doughnut'
|
||||||
});
|
});
|
||||||
@ -405,7 +405,7 @@ export class GfPortfolioProportionChartComponent
|
|||||||
symbol = $localize`No data available`;
|
symbol = $localize`No data available`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = translate(this.positions[<string>symbol]?.name);
|
const name = translate(this.positions[symbol as string]?.name);
|
||||||
|
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
for (const item of context.dataset.data) {
|
for (const item of context.dataset.data) {
|
||||||
@ -414,12 +414,12 @@ export class GfPortfolioProportionChartComponent
|
|||||||
|
|
||||||
const percentage = (context.parsed * 100) / sum;
|
const percentage = (context.parsed * 100) / sum;
|
||||||
|
|
||||||
if (<number>context.raw === Number.MAX_SAFE_INTEGER) {
|
if ((context.raw as number) === Number.MAX_SAFE_INTEGER) {
|
||||||
return $localize`No data available`;
|
return $localize`No data available`;
|
||||||
} else if (this.isInPercent) {
|
} else if (this.isInPercent) {
|
||||||
return [`${name ?? symbol}`, `${percentage.toFixed(2)}%`];
|
return [`${name ?? symbol}`, `${percentage.toFixed(2)}%`];
|
||||||
} else {
|
} else {
|
||||||
const value = <number>context.raw;
|
const value = context.raw as number;
|
||||||
return [
|
return [
|
||||||
`${name ?? symbol}`,
|
`${name ?? symbol}`,
|
||||||
`${value.toLocaleString(this.locale, {
|
`${value.toLocaleString(this.locale, {
|
||||||
|
@ -112,7 +112,7 @@ export abstract class AbstractMatFormField<T>
|
|||||||
public _disabled: boolean = false;
|
public _disabled: boolean = false;
|
||||||
|
|
||||||
public get disabled() {
|
public get disabled() {
|
||||||
if (this.ngControl && this.ngControl.disabled !== null) {
|
if (this.ngControl?.disabled !== null) {
|
||||||
return this.ngControl.disabled;
|
return this.ngControl.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy {
|
|||||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<Holding> = new MatTableDataSource();
|
public dataSource = new MatTableDataSource<Holding>();
|
||||||
public displayedColumns: string[] = [
|
public displayedColumns: string[] = [
|
||||||
'name',
|
'name',
|
||||||
'valueInBaseCurrency',
|
'valueInBaseCurrency',
|
||||||
|
@ -48,7 +48,7 @@ export class GfValueComponent implements OnChanges {
|
|||||||
if (isNumber(this.value)) {
|
if (isNumber(this.value)) {
|
||||||
this.isNumber = true;
|
this.isNumber = true;
|
||||||
this.isString = false;
|
this.isString = false;
|
||||||
this.absoluteValue = Math.abs(<number>this.value);
|
this.absoluteValue = Math.abs(this.value as number);
|
||||||
|
|
||||||
if (this.colorizeSign) {
|
if (this.colorizeSign) {
|
||||||
if (this.isCurrency) {
|
if (this.isCurrency) {
|
||||||
@ -113,7 +113,7 @@ export class GfValueComponent implements OnChanges {
|
|||||||
this.isString = true;
|
this.isString = true;
|
||||||
|
|
||||||
if (this.isDate) {
|
if (this.isDate) {
|
||||||
this.formattedValue = new Date(<string>this.value).toLocaleDateString(
|
this.formattedValue = new Date(this.value).toLocaleDateString(
|
||||||
this.locale,
|
this.locale,
|
||||||
{
|
{
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
|
68
package-lock.json
generated
68
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "2.117.0",
|
"version": "2.118.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "2.117.0",
|
"version": "2.118.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"@nestjs/platform-express": "10.1.3",
|
"@nestjs/platform-express": "10.1.3",
|
||||||
"@nestjs/schedule": "3.0.2",
|
"@nestjs/schedule": "3.0.2",
|
||||||
"@nestjs/serve-static": "4.0.0",
|
"@nestjs/serve-static": "4.0.0",
|
||||||
"@prisma/client": "5.20.0",
|
"@prisma/client": "5.21.1",
|
||||||
"@simplewebauthn/browser": "9.0.1",
|
"@simplewebauthn/browser": "9.0.1",
|
||||||
"@simplewebauthn/server": "9.0.3",
|
"@simplewebauthn/server": "9.0.3",
|
||||||
"@stripe/stripe-js": "3.5.0",
|
"@stripe/stripe-js": "3.5.0",
|
||||||
@ -150,7 +150,7 @@
|
|||||||
"nx": "20.0.3",
|
"nx": "20.0.3",
|
||||||
"prettier": "3.3.3",
|
"prettier": "3.3.3",
|
||||||
"prettier-plugin-organize-attributes": "1.0.0",
|
"prettier-plugin-organize-attributes": "1.0.0",
|
||||||
"prisma": "5.20.0",
|
"prisma": "5.21.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"replace-in-file": "7.0.1",
|
"replace-in-file": "7.0.1",
|
||||||
@ -8753,9 +8753,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.21.1.tgz",
|
||||||
"integrity": "sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==",
|
"integrity": "sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -8771,53 +8771,53 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/debug": {
|
"node_modules/@prisma/debug": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz",
|
||||||
"integrity": "sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ==",
|
"integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines": {
|
"node_modules/@prisma/engines": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.21.1.tgz",
|
||||||
"integrity": "sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==",
|
"integrity": "sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/debug": "5.20.0",
|
"@prisma/debug": "5.21.1",
|
||||||
"@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
|
"@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36",
|
||||||
"@prisma/fetch-engine": "5.20.0",
|
"@prisma/fetch-engine": "5.21.1",
|
||||||
"@prisma/get-platform": "5.20.0"
|
"@prisma/get-platform": "5.21.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines-version": {
|
"node_modules/@prisma/engines-version": {
|
||||||
"version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
|
"version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36.tgz",
|
||||||
"integrity": "sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==",
|
"integrity": "sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/fetch-engine": {
|
"node_modules/@prisma/fetch-engine": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.21.1.tgz",
|
||||||
"integrity": "sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==",
|
"integrity": "sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/debug": "5.20.0",
|
"@prisma/debug": "5.21.1",
|
||||||
"@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
|
"@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36",
|
||||||
"@prisma/get-platform": "5.20.0"
|
"@prisma/get-platform": "5.21.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/get-platform": {
|
"node_modules/@prisma/get-platform": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.21.1.tgz",
|
||||||
"integrity": "sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==",
|
"integrity": "sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/debug": "5.20.0"
|
"@prisma/debug": "5.21.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@redis/bloom": {
|
"node_modules/@redis/bloom": {
|
||||||
@ -29794,14 +29794,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prisma": {
|
"node_modules/prisma": {
|
||||||
"version": "5.20.0",
|
"version": "5.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.21.1.tgz",
|
||||||
"integrity": "sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==",
|
"integrity": "sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines": "5.20.0"
|
"@prisma/engines": "5.21.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"prisma": "build/index.js"
|
"prisma": "build/index.js"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "2.118.0",
|
"version": "2.119.0",
|
||||||
"homepage": "https://ghostfol.io",
|
"homepage": "https://ghostfol.io",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"repository": "https://github.com/ghostfolio/ghostfolio",
|
"repository": "https://github.com/ghostfolio/ghostfolio",
|
||||||
@ -86,7 +86,7 @@
|
|||||||
"@nestjs/platform-express": "10.1.3",
|
"@nestjs/platform-express": "10.1.3",
|
||||||
"@nestjs/schedule": "3.0.2",
|
"@nestjs/schedule": "3.0.2",
|
||||||
"@nestjs/serve-static": "4.0.0",
|
"@nestjs/serve-static": "4.0.0",
|
||||||
"@prisma/client": "5.20.0",
|
"@prisma/client": "5.21.1",
|
||||||
"@simplewebauthn/browser": "9.0.1",
|
"@simplewebauthn/browser": "9.0.1",
|
||||||
"@simplewebauthn/server": "9.0.3",
|
"@simplewebauthn/server": "9.0.3",
|
||||||
"@stripe/stripe-js": "3.5.0",
|
"@stripe/stripe-js": "3.5.0",
|
||||||
@ -196,7 +196,7 @@
|
|||||||
"nx": "20.0.3",
|
"nx": "20.0.3",
|
||||||
"prettier": "3.3.3",
|
"prettier": "3.3.3",
|
||||||
"prettier-plugin-organize-attributes": "1.0.0",
|
"prettier-plugin-organize-attributes": "1.0.0",
|
||||||
"prisma": "5.20.0",
|
"prisma": "5.21.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"replace-in-file": "7.0.1",
|
"replace-in-file": "7.0.1",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user