merge
This commit is contained in:
commit
d1688242b0
151
.eslintrc.json
151
.eslintrc.json
@ -1,151 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nx/enforce-module-boundaries": [
|
||||
"warn",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-extra-semi": "error",
|
||||
"no-extra-semi": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": ["plugin:@nx/typescript"]
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nx/javascript"]
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"plugins": ["eslint-plugin-import", "@typescript-eslint"],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-indexed-object-style": "off",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"off",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "warn",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": ["variable", "classProperty", "typeProperty"],
|
||||
"format": ["camelCase", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "objectLiteralProperty",
|
||||
"format": null
|
||||
},
|
||||
{
|
||||
"selector": "enumMember",
|
||||
"format": ["camelCase", "UPPER_CASE", "PascalCase"]
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-empty-interface": "warn",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"warn",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"warn",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"@typescript-eslint/no-loss-of-precision": "warn",
|
||||
"@typescript-eslint/no-var-requires": "warn",
|
||||
"@typescript-eslint/ban-types": "warn",
|
||||
"arrow-body-style": "off",
|
||||
"constructor-super": "error",
|
||||
"eqeqeq": ["error", "smart"],
|
||||
"guard-for-in": "warn",
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"import/no-deprecated": "warn",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-restricted-imports": ["error", "rxjs/Rx"],
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-var": "error",
|
||||
"radix": "error",
|
||||
"no-unsafe-optional-chaining": "warn",
|
||||
"no-extra-boolean-cast": "warn",
|
||||
"no-empty-pattern": "warn",
|
||||
"no-useless-catch": "warn",
|
||||
"no-unsafe-finally": "warn",
|
||||
"no-prototype-builtins": "warn",
|
||||
"no-async-promise-executor": "warn",
|
||||
"no-constant-condition": "warn",
|
||||
|
||||
// The following rules are part of @typescript-eslint/recommended-type-checked
|
||||
// and can be remove once solved
|
||||
"@typescript-eslint/await-thenable": "warn",
|
||||
"@typescript-eslint/ban-ts-comment": "warn",
|
||||
"@typescript-eslint/no-base-to-string": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-floating-promises": "warn",
|
||||
"@typescript-eslint/no-misused-promises": "warn",
|
||||
"@typescript-eslint/no-redundant-type-constituents": "warn",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
|
||||
"@typescript-eslint/no-unsafe-argument": "warn",
|
||||
"@typescript-eslint/no-unsafe-assignment": "warn",
|
||||
"@typescript-eslint/no-unsafe-enum-comparison": "warn",
|
||||
"@typescript-eslint/no-unsafe-member-access": "warn",
|
||||
"@typescript-eslint/no-unsafe-return": "warn",
|
||||
"@typescript-eslint/no-unsafe-call": "warn",
|
||||
"@typescript-eslint/require-await": "warn",
|
||||
"@typescript-eslint/restrict-template-expressions": "warn",
|
||||
"@typescript-eslint/unbound-method": "warn",
|
||||
|
||||
// The following rules are part of @typescript-eslint/stylistic-type-checked
|
||||
// and can be remove once solved
|
||||
"@typescript-eslint/prefer-nullish-coalescing": "warn" // TODO: Requires strictNullChecks: true
|
||||
}
|
||||
}
|
||||
],
|
||||
"extends": ["plugin:storybook/recommended"]
|
||||
}
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -5,6 +5,22 @@ 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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 2.135.0 - 2025-01-19
|
||||
|
||||
### Changed
|
||||
|
||||
- Moved the language localization for Polski (`pl`) from experimental to general availability
|
||||
- Extended the _Financial Modeling Prep_ service
|
||||
- Switched to _ESLint_’s flat config format
|
||||
- Upgraded `chart.js` from version `4.2.0` to `4.4.7`
|
||||
- Upgraded `chartjs-chart-treemap` from version `2.3.1` to `3.1.0`
|
||||
- Upgraded `chartjs-plugin-annotation` from version `2.1.2` to `3.1.0`
|
||||
- Upgraded `eslint` dependencies
|
||||
- Upgraded `nestjs` from version `10.1.3` to `10.4.15`
|
||||
- Upgraded `Nx` from version `20.3.0` to `20.3.2`
|
||||
- Upgraded `reflect-metadata` from version `0.1.13` to `0.2.2`
|
||||
- Upgraded `uuid` from version `11.0.2` to `11.0.5`
|
||||
|
||||
## 2.134.0 - 2025-01-15
|
||||
|
||||
### Added
|
||||
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"extends": "../../.eslintrc.json",
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"rules": {},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"parserOptions": {
|
||||
"project": ["apps/api/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
31
apps/api/eslint.config.cjs
Normal file
31
apps/api/eslint.config.cjs
Normal file
@ -0,0 +1,31 @@
|
||||
const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
rules: {}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['apps/api/tsconfig.*?.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
},
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
}
|
||||
];
|
@ -47,7 +47,7 @@ export class SymbolController {
|
||||
try {
|
||||
return this.symbolService.lookup({
|
||||
includeIndices,
|
||||
query: query.toLowerCase(),
|
||||
query,
|
||||
user: this.request.user
|
||||
});
|
||||
} catch {
|
||||
|
@ -504,12 +504,10 @@
|
||||
<loc>https://ghostfol.io/pl/o-ghostfolio</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<!--
|
||||
<url>
|
||||
<loc>https://ghostfol.io/pl/open</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
-->
|
||||
<url>
|
||||
<loc>https://ghostfol.io/pl/rynki</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
const cryptocurrencies = require('../../assets/cryptocurrencies/cryptocurrencies.json');
|
||||
@ -9,7 +11,11 @@ export class CryptocurrencyService {
|
||||
|
||||
public isCryptocurrency(aSymbol = '') {
|
||||
const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3);
|
||||
return this.getCryptocurrencies().includes(cryptocurrencySymbol);
|
||||
|
||||
return (
|
||||
aSymbol.endsWith(DEFAULT_CURRENCY) &&
|
||||
this.getCryptocurrencies().includes(cryptocurrencySymbol)
|
||||
);
|
||||
}
|
||||
|
||||
private getCryptocurrencies() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||
import {
|
||||
DataProviderInterface,
|
||||
GetDividendsParams,
|
||||
@ -10,7 +11,6 @@ import {
|
||||
IDataProviderHistoricalResponse,
|
||||
IDataProviderResponse
|
||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
DataProviderInfo,
|
||||
@ -19,16 +19,31 @@ import {
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource, SymbolProfile } from '@prisma/client';
|
||||
import { format, isAfter, isBefore, isSameDay } from 'date-fns';
|
||||
import {
|
||||
AssetClass,
|
||||
AssetSubClass,
|
||||
DataSource,
|
||||
SymbolProfile
|
||||
} from '@prisma/client';
|
||||
import { isISIN } from 'class-validator';
|
||||
import { countries } from 'countries-list';
|
||||
import {
|
||||
addDays,
|
||||
format,
|
||||
isAfter,
|
||||
isBefore,
|
||||
isSameDay,
|
||||
parseISO
|
||||
} from 'date-fns';
|
||||
|
||||
@Injectable()
|
||||
export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
private apiKey: string;
|
||||
private readonly URL = 'https://financialmodelingprep.com/api/v3';
|
||||
private readonly URL = this.getUrl({ version: 3 });
|
||||
|
||||
public constructor(
|
||||
private readonly configurationService: ConfigurationService
|
||||
private readonly configurationService: ConfigurationService,
|
||||
private readonly cryptocurrencyService: CryptocurrencyService
|
||||
) {
|
||||
this.apiKey = this.configurationService.get(
|
||||
'API_KEY_FINANCIAL_MODELING_PREP'
|
||||
@ -44,10 +59,152 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
}: {
|
||||
symbol: string;
|
||||
}): Promise<Partial<SymbolProfile>> {
|
||||
return {
|
||||
const response: Partial<SymbolProfile> = {
|
||||
symbol,
|
||||
dataSource: this.getName()
|
||||
};
|
||||
|
||||
try {
|
||||
if (this.cryptocurrencyService.isCryptocurrency(symbol)) {
|
||||
const [quote] = await fetch(
|
||||
`${this.URL}/quote/${symbol}?apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
response.assetClass = AssetClass.LIQUIDITY;
|
||||
response.assetSubClass = AssetSubClass.CRYPTOCURRENCY;
|
||||
response.currency = symbol.substring(symbol.length - 3);
|
||||
response.name = quote.name;
|
||||
} else {
|
||||
const [assetProfile] = await fetch(
|
||||
`${this.URL}/profile/${symbol}?apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
const { assetClass, assetSubClass } =
|
||||
this.parseAssetClass(assetProfile);
|
||||
|
||||
response.assetClass = assetClass;
|
||||
response.assetSubClass = assetSubClass;
|
||||
|
||||
if (assetSubClass === AssetSubClass.ETF) {
|
||||
const etfCountryWeightings = await fetch(
|
||||
`${this.URL}/etf-country-weightings/${symbol}?apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
response.countries = etfCountryWeightings.map(
|
||||
({ country: countryName, weightPercentage }) => {
|
||||
let countryCode: string;
|
||||
|
||||
for (const [code, country] of Object.entries(countries)) {
|
||||
if (country.name === countryName) {
|
||||
countryCode = code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: countryCode,
|
||||
weight: parseFloat(weightPercentage.slice(0, -1)) / 100
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const [portfolioDate] = await fetch(
|
||||
`${this.getUrl({ version: 4 })}/etf-holdings/portfolio-date?symbol=${symbol}&apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
if (portfolioDate) {
|
||||
const etfHoldings = await fetch(
|
||||
`${this.getUrl({ version: 4 })}/etf-holdings?date=${portfolioDate.date}&symbol=${symbol}&apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
const sortedTopHoldings = etfHoldings
|
||||
.sort((a, b) => {
|
||||
return b.pctVal - a.pctVal;
|
||||
})
|
||||
.slice(0, 10);
|
||||
|
||||
response.holdings = sortedTopHoldings.map(({ name, pctVal }) => {
|
||||
return { name, weight: pctVal / 100 };
|
||||
});
|
||||
}
|
||||
|
||||
const etfSectorWeightings = await fetch(
|
||||
`${this.URL}/etf-sector-weightings/${symbol}?apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
response.sectors = etfSectorWeightings.map(
|
||||
({ sector, weightPercentage }) => {
|
||||
return {
|
||||
name: sector,
|
||||
weight: parseFloat(weightPercentage.slice(0, -1)) / 100
|
||||
};
|
||||
}
|
||||
);
|
||||
} else if (assetSubClass === AssetSubClass.STOCK) {
|
||||
if (assetProfile.country) {
|
||||
response.countries = [{ code: assetProfile.country, weight: 1 }];
|
||||
}
|
||||
|
||||
if (assetProfile.sector) {
|
||||
response.sectors = [{ name: assetProfile.sector, weight: 1 }];
|
||||
}
|
||||
}
|
||||
|
||||
response.currency = assetProfile.currency;
|
||||
|
||||
if (assetProfile.isin) {
|
||||
response.isin = assetProfile.isin;
|
||||
}
|
||||
|
||||
response.name = assetProfile.companyName;
|
||||
|
||||
if (assetProfile.website) {
|
||||
response.url = assetProfile.website;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let message = error;
|
||||
|
||||
if (error?.name === 'AbortError') {
|
||||
message = `RequestError: The operation to get the asset profile for ${symbol} was aborted because the request to the data provider took more than ${(
|
||||
this.configurationService.get('REQUEST_TIMEOUT') / 1000
|
||||
).toFixed(3)} seconds`;
|
||||
}
|
||||
|
||||
Logger.error(message, 'FinancialModelingPrepService');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public getDataProviderInfo(): DataProviderInfo {
|
||||
@ -58,9 +215,55 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
};
|
||||
}
|
||||
|
||||
public async getDividends({}: GetDividendsParams) {
|
||||
public async getDividends({
|
||||
from,
|
||||
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||
symbol,
|
||||
to
|
||||
}: GetDividendsParams) {
|
||||
if (isSameDay(from, to)) {
|
||||
to = addDays(to, 1);
|
||||
}
|
||||
|
||||
try {
|
||||
const response: {
|
||||
[date: string]: IDataProviderHistoricalResponse;
|
||||
} = {};
|
||||
|
||||
const { historical } = await fetch(
|
||||
`${this.URL}/historical-price-full/stock_dividend/${symbol}?apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(requestTimeout)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
historical
|
||||
.filter(({ date }) => {
|
||||
return (
|
||||
(isSameDay(parseISO(date), from) ||
|
||||
isAfter(parseISO(date), from)) &&
|
||||
isBefore(parseISO(date), to)
|
||||
);
|
||||
})
|
||||
.forEach(({ adjDividend, date }) => {
|
||||
response[date] = {
|
||||
marketPrice: adjDividend
|
||||
};
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
Logger.error(
|
||||
`Could not get dividends for ${symbol} (${this.getName()}) from ${format(
|
||||
from,
|
||||
DATE_FORMAT
|
||||
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}`,
|
||||
'FinancialModelingPrepService'
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
public async getHistorical({
|
||||
from,
|
||||
@ -84,14 +287,14 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
[symbol]: {}
|
||||
};
|
||||
|
||||
for (const { close, date } of historical) {
|
||||
for (const { adjClose, date } of historical) {
|
||||
if (
|
||||
(isSameDay(parseDate(date), from) ||
|
||||
isAfter(parseDate(date), from)) &&
|
||||
isBefore(parseDate(date), to)
|
||||
) {
|
||||
result[symbol][date] = {
|
||||
marketPrice: close
|
||||
marketPrice: adjClose
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -130,8 +333,10 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
).then((res) => res.json());
|
||||
|
||||
for (const { price, symbol } of quotes) {
|
||||
const { currency } = await this.getAssetProfile({ symbol });
|
||||
|
||||
response[symbol] = {
|
||||
currency: DEFAULT_CURRENCY,
|
||||
currency,
|
||||
dataProviderInfo: this.getDataProviderInfo(),
|
||||
dataSource: DataSource.FINANCIAL_MODELING_PREP,
|
||||
marketPrice: price,
|
||||
@ -161,6 +366,28 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
let items: LookupItem[] = [];
|
||||
|
||||
try {
|
||||
if (isISIN(query)) {
|
||||
const result = await fetch(
|
||||
`${this.getUrl({ version: 4 })}/search/isin?isin=${query}&apikey=${this.apiKey}`,
|
||||
{
|
||||
signal: AbortSignal.timeout(
|
||||
this.configurationService.get('REQUEST_TIMEOUT')
|
||||
)
|
||||
}
|
||||
).then((res) => res.json());
|
||||
|
||||
items = result.map(({ companyName, currency, symbol }) => {
|
||||
return {
|
||||
currency,
|
||||
symbol,
|
||||
assetClass: undefined, // TODO
|
||||
assetSubClass: undefined, // TODO
|
||||
dataProviderInfo: this.getDataProviderInfo(),
|
||||
dataSource: this.getName(),
|
||||
name: companyName
|
||||
};
|
||||
});
|
||||
} else {
|
||||
const result = await fetch(
|
||||
`${this.URL}/search?query=${query}&apikey=${this.apiKey}`,
|
||||
{
|
||||
@ -172,14 +399,16 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
|
||||
items = result.map(({ currency, name, symbol }) => {
|
||||
return {
|
||||
// TODO: Add assetClass
|
||||
// TODO: Add assetSubClass
|
||||
currency,
|
||||
name,
|
||||
symbol,
|
||||
assetClass: undefined, // TODO
|
||||
assetSubClass: undefined, // TODO
|
||||
dataProviderInfo: this.getDataProviderInfo(),
|
||||
dataSource: this.getName()
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
let message = error;
|
||||
|
||||
@ -194,4 +423,29 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
||||
|
||||
return { items };
|
||||
}
|
||||
|
||||
private getUrl({ version }: { version: number }) {
|
||||
return `https://financialmodelingprep.com/api/v${version}`;
|
||||
}
|
||||
|
||||
private parseAssetClass(profile: any): {
|
||||
assetClass: AssetClass;
|
||||
assetSubClass: AssetSubClass;
|
||||
} {
|
||||
let assetClass: AssetClass;
|
||||
let assetSubClass: AssetSubClass;
|
||||
|
||||
if (profile.isEtf) {
|
||||
assetClass = AssetClass.EQUITY;
|
||||
assetSubClass = AssetSubClass.ETF;
|
||||
} else if (profile.isFund) {
|
||||
assetClass = AssetClass.EQUITY;
|
||||
assetSubClass = AssetSubClass.MUTUALFUND;
|
||||
} else {
|
||||
assetClass = AssetClass.EQUITY;
|
||||
assetSubClass = AssetSubClass.STOCK;
|
||||
}
|
||||
|
||||
return { assetClass, assetSubClass };
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"parserOptions": {
|
||||
"project": ["apps/client/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/prefer-standalone": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "gf",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "gf",
|
||||
"style": "camelCase"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
62
apps/client/eslint.config.cjs
Normal file
62
apps/client/eslint.config.cjs
Normal file
@ -0,0 +1,62 @@
|
||||
const baseConfig = require('../../eslint.config.cjs');
|
||||
const angularEslintPlugin = require('@angular-eslint/eslint-plugin');
|
||||
const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
plugins: {
|
||||
'@angular-eslint': angularEslintPlugin,
|
||||
'@typescript-eslint': typescriptEslintPlugin
|
||||
}
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'gf',
|
||||
style: 'kebab-case'
|
||||
}
|
||||
],
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'gf',
|
||||
style: 'camelCase'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['apps/client/tsconfig.*?.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
},
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off'
|
||||
}
|
||||
}
|
||||
];
|
@ -12,7 +12,9 @@
|
||||
[href]="pricingUrl"
|
||||
>
|
||||
@if (isGhostfolioApiKeyValid === false) {
|
||||
<span class="badge badge-warning mr-1" i18n>NEW</span>
|
||||
<span class="badge badge-warning mr-1" i18n
|
||||
>Early Access</span
|
||||
>
|
||||
}
|
||||
Ghostfolio Premium
|
||||
<gf-premium-indicator
|
||||
|
@ -100,12 +100,10 @@
|
||||
>Nederlands (<ng-container i18n>Community</ng-container
|
||||
>)</mat-option
|
||||
>
|
||||
@if (user?.settings?.isExperimentalFeatures) {
|
||||
<mat-option value="pl"
|
||||
>Polski (<ng-container i18n>Community</ng-container
|
||||
>)</mat-option
|
||||
>
|
||||
}
|
||||
<mat-option value="pt"
|
||||
>Português (<ng-container i18n>Community</ng-container
|
||||
>)</mat-option
|
||||
|
@ -244,9 +244,8 @@
|
||||
Use Ghostfolio in multiple languages: English,
|
||||
<!-- Català, -->
|
||||
<!-- Chinese, -->
|
||||
Dutch, French, German, Italian,
|
||||
<!-- Polish, -->
|
||||
Portuguese, Spanish and Turkish
|
||||
Dutch, French, German, Italian, Polish, Portuguese, Spanish
|
||||
and Turkish
|
||||
<!-- and Ukrainian -->
|
||||
are currently supported.
|
||||
</p>
|
||||
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"parserOptions": {
|
||||
"project": ["apps/ui-e2e/tsconfig.json"]
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["src/plugins/index.js"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-undef": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
33
apps/ui-e2e/eslint.config.cjs
Normal file
33
apps/ui-e2e/eslint.config.cjs
Normal file
@ -0,0 +1,33 @@
|
||||
const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...baseConfig,
|
||||
...compat.extends('plugin:cypress/recommended'),
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['apps/ui-e2e/tsconfig.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['src/plugins/index.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'no-undef': 'off'
|
||||
}
|
||||
}
|
||||
];
|
196
eslint.config.cjs
Normal file
196
eslint.config.cjs
Normal file
@ -0,0 +1,196 @@
|
||||
const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
const storybook = require('eslint-plugin-storybook');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...storybook.configs['flat/recommended'],
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
rules: {
|
||||
'@nx/enforce-module-boundaries': [
|
||||
'warn',
|
||||
{
|
||||
enforceBuildableLibDependency: true,
|
||||
allow: [],
|
||||
depConstraints: [
|
||||
{
|
||||
sourceTag: '*',
|
||||
onlyDependOnLibsWithTags: ['*']
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-extra-semi': 'error',
|
||||
'no-extra-semi': 'off'
|
||||
}
|
||||
},
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/typescript']
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules
|
||||
}
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/javascript']
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules
|
||||
}
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
plugins: ['eslint-plugin-import', '@typescript-eslint'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended-type-checked',
|
||||
'plugin:@typescript-eslint/stylistic-type-checked'
|
||||
]
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/dot-notation': 'off',
|
||||
'@typescript-eslint/explicit-member-accessibility': [
|
||||
'off',
|
||||
{
|
||||
accessibility: 'explicit'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/member-ordering': 'warn',
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'off',
|
||||
{
|
||||
selector: 'default',
|
||||
format: ['camelCase'],
|
||||
leadingUnderscore: 'allow',
|
||||
trailingUnderscore: 'allow'
|
||||
},
|
||||
{
|
||||
selector: ['variable', 'classProperty', 'typeProperty'],
|
||||
format: ['camelCase', 'UPPER_CASE'],
|
||||
leadingUnderscore: 'allow',
|
||||
trailingUnderscore: 'allow'
|
||||
},
|
||||
{
|
||||
selector: 'objectLiteralProperty',
|
||||
format: null
|
||||
},
|
||||
{
|
||||
selector: 'enumMember',
|
||||
format: ['camelCase', 'UPPER_CASE', 'PascalCase']
|
||||
},
|
||||
{
|
||||
selector: 'typeLike',
|
||||
format: ['PascalCase']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-empty-interface': 'warn',
|
||||
'@typescript-eslint/no-inferrable-types': [
|
||||
'warn',
|
||||
{
|
||||
ignoreParameters: true
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-non-null-assertion': 'warn',
|
||||
'@typescript-eslint/no-shadow': [
|
||||
'warn',
|
||||
{
|
||||
hoist: 'all'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/unified-signatures': 'error',
|
||||
'@typescript-eslint/no-loss-of-precision': 'warn',
|
||||
'@typescript-eslint/no-var-requires': 'warn',
|
||||
'arrow-body-style': 'off',
|
||||
'constructor-super': 'error',
|
||||
eqeqeq: ['error', 'smart'],
|
||||
'guard-for-in': 'warn',
|
||||
'id-blacklist': 'off',
|
||||
'id-match': 'off',
|
||||
'import/no-deprecated': 'warn',
|
||||
'no-bitwise': 'error',
|
||||
'no-caller': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-empty': 'off',
|
||||
'no-eval': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-new-wrappers': 'error',
|
||||
'no-restricted-imports': ['error', 'rxjs/Rx'],
|
||||
'no-undef-init': 'error',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-var': 'error',
|
||||
radix: 'error',
|
||||
'no-unsafe-optional-chaining': 'warn',
|
||||
'no-extra-boolean-cast': 'warn',
|
||||
'no-empty-pattern': 'warn',
|
||||
'no-useless-catch': 'warn',
|
||||
'no-unsafe-finally': 'warn',
|
||||
'no-prototype-builtins': 'warn',
|
||||
'no-async-promise-executor': 'warn',
|
||||
'no-constant-condition': 'warn',
|
||||
|
||||
// The following rules are part of eslint:recommended
|
||||
// and can be remove once solved
|
||||
'no-constant-binary-expression': 'warn',
|
||||
'no-loss-of-precision': 'warn',
|
||||
|
||||
// The following rules are part of @typescript-eslint/recommended-type-checked
|
||||
// and can be remove once solved
|
||||
'@typescript-eslint/await-thenable': 'warn',
|
||||
'@typescript-eslint/ban-ts-comment': 'warn',
|
||||
'@typescript-eslint/no-base-to-string': 'warn',
|
||||
'@typescript-eslint/no-empty-object-type': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
'@typescript-eslint/no-misused-promises': 'warn',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'warn',
|
||||
'@typescript-eslint/no-require-imports': 'warn',
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||
'@typescript-eslint/no-unsafe-enum-comparison': 'warn',
|
||||
'@typescript-eslint/no-unsafe-function-type': 'warn',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
||||
'@typescript-eslint/no-unsafe-return': 'warn',
|
||||
'@typescript-eslint/no-unsafe-call': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
caughtErrors: 'none'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-wrapper-object-types': 'warn',
|
||||
'@typescript-eslint/only-throw-error': 'warn',
|
||||
'@typescript-eslint/prefer-promise-reject-errors': 'warn',
|
||||
'@typescript-eslint/require-await': 'warn',
|
||||
'@typescript-eslint/restrict-template-expressions': 'warn',
|
||||
'@typescript-eslint/unbound-method': 'warn',
|
||||
|
||||
// The following rules are part of @typescript-eslint/stylistic-type-checked
|
||||
// and can be remove once solved
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'warn', // TODO: Requires strictNullChecks: true
|
||||
'@typescript-eslint/prefer-regexp-exec': 'warn'
|
||||
}
|
||||
}))
|
||||
];
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"parserOptions": {
|
||||
"project": ["libs/common/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
28
libs/common/eslint.config.cjs
Normal file
28
libs/common/eslint.config.cjs
Normal file
@ -0,0 +1,28 @@
|
||||
const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['libs/common/tsconfig.*?.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
},
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
}
|
||||
];
|
@ -1,40 +0,0 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "**/*.stories.ts"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"parserOptions": {
|
||||
"project": ["libs/ui/tsconfig.*?.json"]
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@nx/angular",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "gf",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "gf",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/prefer-standalone": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
65
libs/ui/eslint.config.cjs
Normal file
65
libs/ui/eslint.config.cjs
Normal file
@ -0,0 +1,65 @@
|
||||
const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist']
|
||||
},
|
||||
...baseConfig,
|
||||
...compat
|
||||
.config({
|
||||
extends: [
|
||||
'plugin:@nx/angular',
|
||||
'plugin:@angular-eslint/template/process-inline-templates'
|
||||
]
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'gf',
|
||||
style: 'camelCase'
|
||||
}
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'gf',
|
||||
style: 'kebab-case'
|
||||
}
|
||||
],
|
||||
'@angular-eslint/prefer-standalone': 'off'
|
||||
},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['libs/ui/tsconfig.*?.json']
|
||||
}
|
||||
}
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/angular-template']
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.html'],
|
||||
rules: {
|
||||
...config.rules
|
||||
}
|
||||
})),
|
||||
{
|
||||
ignores: ['**/*.stories.ts']
|
||||
}
|
||||
];
|
@ -77,7 +77,7 @@ export class GfPortfolioProportionChartComponent
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
public chart: Chart<'pie'>;
|
||||
public chart: Chart<'doughnut'>;
|
||||
public isLoading = true;
|
||||
|
||||
private readonly OTHER_KEY = 'OTHER';
|
||||
@ -257,7 +257,7 @@ export class GfPortfolioProportionChartComponent
|
||||
});
|
||||
});
|
||||
|
||||
const datasets: ChartConfiguration['data']['datasets'] = [
|
||||
const datasets: ChartConfiguration<'doughnut'>['data']['datasets'] = [
|
||||
{
|
||||
backgroundColor: chartDataSorted.map(([, item]) => {
|
||||
return item.color;
|
||||
@ -295,7 +295,7 @@ export class GfPortfolioProportionChartComponent
|
||||
datasets[1].data[1] = Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
const data: ChartConfiguration['data'] = {
|
||||
const data: ChartConfiguration<'doughnut'>['data'] = {
|
||||
datasets,
|
||||
labels
|
||||
};
|
||||
@ -308,7 +308,7 @@ export class GfPortfolioProportionChartComponent
|
||||
) as unknown;
|
||||
this.chart.update();
|
||||
} else {
|
||||
this.chart = new Chart(this.chartCanvas.nativeElement, {
|
||||
this.chart = new Chart<'doughnut'>(this.chartCanvas.nativeElement, {
|
||||
data,
|
||||
options: {
|
||||
animation: false,
|
||||
|
@ -196,7 +196,7 @@ export class GfTreemapChartComponent
|
||||
min: Math.min(...negativeNetPerformancePercents)
|
||||
};
|
||||
|
||||
const data: ChartConfiguration['data'] = {
|
||||
const data: ChartConfiguration<'treemap'>['data'] = {
|
||||
datasets: [
|
||||
{
|
||||
backgroundColor: (ctx) => {
|
||||
|
1
nx.json
1
nx.json
@ -63,6 +63,7 @@
|
||||
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
"!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
|
||||
"!{projectRoot}/.storybook/**/*",
|
||||
"!{projectRoot}/eslint.config.cjs",
|
||||
"!{projectRoot}/jest.config.[jt]s",
|
||||
"!{projectRoot}/src/test-setup.[jt]s",
|
||||
"!{projectRoot}/tsconfig.storybook.json",
|
||||
|
3209
package-lock.json
generated
3209
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ghostfolio",
|
||||
"version": "2.134.0",
|
||||
"version": "2.135.0",
|
||||
"homepage": "https://ghostfol.io",
|
||||
"license": "AGPL-3.0",
|
||||
"repository": "https://github.com/ghostfolio/ghostfolio",
|
||||
@ -75,17 +75,17 @@
|
||||
"@dfinity/principal": "0.15.7",
|
||||
"@dinero.js/currencies": "2.0.0-alpha.8",
|
||||
"@internationalized/number": "3.6.0",
|
||||
"@nestjs/bull": "10.0.1",
|
||||
"@nestjs/cache-manager": "2.2.2",
|
||||
"@nestjs/common": "10.1.3",
|
||||
"@nestjs/config": "3.0.0",
|
||||
"@nestjs/core": "10.1.3",
|
||||
"@nestjs/event-emitter": "2.0.4",
|
||||
"@nestjs/jwt": "10.1.0",
|
||||
"@nestjs/bull": "10.2.3",
|
||||
"@nestjs/cache-manager": "2.3.0",
|
||||
"@nestjs/common": "10.4.15",
|
||||
"@nestjs/config": "3.3.0",
|
||||
"@nestjs/core": "10.4.15",
|
||||
"@nestjs/event-emitter": "2.1.1",
|
||||
"@nestjs/jwt": "10.2.0",
|
||||
"@nestjs/passport": "10.0.3",
|
||||
"@nestjs/platform-express": "10.1.3",
|
||||
"@nestjs/schedule": "3.0.2",
|
||||
"@nestjs/serve-static": "4.0.0",
|
||||
"@nestjs/platform-express": "10.4.15",
|
||||
"@nestjs/schedule": "4.1.2",
|
||||
"@nestjs/serve-static": "4.0.2",
|
||||
"@prisma/client": "6.2.1",
|
||||
"@simplewebauthn/browser": "9.0.1",
|
||||
"@simplewebauthn/server": "9.0.3",
|
||||
@ -93,13 +93,13 @@
|
||||
"alphavantage": "2.2.0",
|
||||
"big.js": "6.2.2",
|
||||
"bootstrap": "4.6.0",
|
||||
"bull": "4.16.2",
|
||||
"bull": "4.16.4",
|
||||
"cache-manager": "5.7.6",
|
||||
"cache-manager-redis-yet": "5.1.4",
|
||||
"chart.js": "4.2.0",
|
||||
"chart.js": "4.4.7",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-treemap": "2.3.1",
|
||||
"chartjs-plugin-annotation": "2.1.2",
|
||||
"chartjs-chart-treemap": "3.1.0",
|
||||
"chartjs-plugin-annotation": "3.1.0",
|
||||
"chartjs-plugin-datalabels": "2.2.0",
|
||||
"cheerio": "1.0.0",
|
||||
"class-transformer": "0.5.1",
|
||||
@ -129,12 +129,12 @@
|
||||
"passport-google-oauth20": "2.0.0",
|
||||
"passport-headerapikey": "1.2.2",
|
||||
"passport-jwt": "4.0.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rxjs": "7.5.6",
|
||||
"stripe": "17.3.0",
|
||||
"svgmap": "2.6.0",
|
||||
"twitter-api-v2": "1.14.2",
|
||||
"uuid": "11.0.2",
|
||||
"uuid": "11.0.5",
|
||||
"yahoo-finance2": "2.11.3",
|
||||
"zone.js": "0.15.0"
|
||||
},
|
||||
@ -150,19 +150,21 @@
|
||||
"@angular/language-service": "19.0.5",
|
||||
"@angular/localize": "19.0.5",
|
||||
"@angular/pwa": "19.0.6",
|
||||
"@nestjs/schematics": "10.0.1",
|
||||
"@nestjs/testing": "10.1.3",
|
||||
"@nx/angular": "20.3.0",
|
||||
"@nx/cypress": "20.3.0",
|
||||
"@nx/eslint-plugin": "20.3.0",
|
||||
"@nx/jest": "20.3.0",
|
||||
"@nx/js": "20.3.0",
|
||||
"@nx/module-federation": "20.3.0",
|
||||
"@nx/nest": "20.3.0",
|
||||
"@nx/node": "20.3.0",
|
||||
"@nx/storybook": "20.3.0",
|
||||
"@nx/web": "20.3.0",
|
||||
"@nx/workspace": "20.3.0",
|
||||
"@eslint/eslintrc": "3.2.0",
|
||||
"@eslint/js": "9.18.0",
|
||||
"@nestjs/schematics": "10.2.3",
|
||||
"@nestjs/testing": "10.4.15",
|
||||
"@nx/angular": "20.3.2",
|
||||
"@nx/cypress": "20.3.2",
|
||||
"@nx/eslint-plugin": "20.3.2",
|
||||
"@nx/jest": "20.3.2",
|
||||
"@nx/js": "20.3.2",
|
||||
"@nx/module-federation": "20.3.2",
|
||||
"@nx/nest": "20.3.2",
|
||||
"@nx/node": "20.3.2",
|
||||
"@nx/storybook": "20.3.2",
|
||||
"@nx/web": "20.3.2",
|
||||
"@nx/workspace": "20.3.2",
|
||||
"@schematics/angular": "19.0.6",
|
||||
"@simplewebauthn/types": "9.0.1",
|
||||
"@storybook/addon-essentials": "8.4.7",
|
||||
@ -179,20 +181,20 @@
|
||||
"@types/node": "20.14.10",
|
||||
"@types/papaparse": "5.3.7",
|
||||
"@types/passport-google-oauth20": "2.0.16",
|
||||
"@typescript-eslint/eslint-plugin": "6.21.0",
|
||||
"@typescript-eslint/parser": "6.21.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.20.0",
|
||||
"@typescript-eslint/parser": "8.20.0",
|
||||
"codelyzer": "6.0.1",
|
||||
"cypress": "6.2.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint": "9.18.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-cypress": "2.15.1",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-storybook": "0.6.15",
|
||||
"eslint-plugin-cypress": "3.2.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-storybook": "0.10.2",
|
||||
"husky": "9.1.7",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jest-preset-angular": "14.4.2",
|
||||
"nx": "20.3.0",
|
||||
"nx": "20.3.2",
|
||||
"prettier": "3.4.2",
|
||||
"prettier-plugin-organize-attributes": "1.0.0",
|
||||
"prisma": "6.2.1",
|
||||
@ -203,7 +205,7 @@
|
||||
"storybook": "8.4.7",
|
||||
"ts-jest": "29.1.0",
|
||||
"ts-node": "10.9.2",
|
||||
"tslib": "2.6.0",
|
||||
"tslib": "2.8.1",
|
||||
"typescript": "5.6.3",
|
||||
"webpack-bundle-analyzer": "4.10.2"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user