Compare commits

...

20 Commits

Author SHA1 Message Date
b0fb986208 Release 1.304.0 (#2272) 2023-08-27 11:19:14 +02:00
0b59fc639d Feature/upgrade prettier to version 3 (#2163)
* Upgrade prettier to version 3.0.2

* Prettify code

* Update changelog
2023-08-27 11:13:11 +02:00
7ddd6f27b5 Feature/upgrade nx to version 16.7.4 (#2271)
* Upgrade Nx to version 16.7.4

* Update changelog
2023-08-27 10:44:06 +02:00
c5d56f4b47 Fix border (#2268) 2023-08-27 10:20:36 +02:00
2f2b712999 Fix breadcrumb (#2267) 2023-08-27 10:20:21 +02:00
c2fd31f5e5 Feature/add health check endpoints for data enhancers (#2265)
* Add health check for data enhancers

* Update changelog
2023-08-27 10:19:53 +02:00
f2d70f9070 Sort imports (#2266) 2023-08-26 11:22:19 +02:00
f41dd9cd8e Fix lint script (#2264) 2023-08-25 15:13:04 +02:00
7d238b4935 Release 1.303.0 (#2261) 2023-08-23 18:52:59 +02:00
da6591fca0 Bugfix/fix base url in trackinsight data enhancer (#2258)
* Fix base url

* Update changelog
2023-08-23 18:51:02 +02:00
1f9b9e9998 Feature/blog post ghostfolio joins oss friends (#2260)
* Add blog post: Ghostfolio joins OSS Friends

* Update changelog
2023-08-23 18:49:53 +02:00
49c4ea306d Feature/improve oss friends page (#2257)
* Improve OSS Friends page

* Update changelog
2023-08-22 09:02:39 +02:00
ccb5c664ef Feature/refresh cryptocurrencies list 20230821 (#2256)
* Update cryptocurrencies.json

* Update changelog
2023-08-21 20:20:59 +02:00
97e165ff69 Improve localization (#2254) 2023-08-21 18:05:08 +02:00
45aefb6a45 Reorder charts (#2253) 2023-08-21 18:04:49 +02:00
2435535975 Release 1.302.0 (#2252) 2023-08-20 10:35:13 +02:00
bd3d43bf05 Feature/upgrade nx to version 16.7.2 (#2251)
* Upgrade Nx and Angular dependencies

* Update changelog
2023-08-20 10:32:52 +02:00
02dc7c52b1 Localize routes (#2250)
* Localize about path

* Localize faq path

* Localize features path

* Localize markets path

* Localize pricing path

* Localize register path

* Localize resources path

* Extend sitemap
2023-08-20 10:01:40 +02:00
ff59fd4196 Feature/improve language localization for german 20230819 (#2249)
* Improve language localization

* Update changelog
2023-08-19 19:49:40 +02:00
4955555ddd Release 1.301.1 (#2247) 2023-08-19 08:49:31 +02:00
130 changed files with 16143 additions and 7337 deletions

View File

@ -9,6 +9,7 @@
],
"attributeSort": "ASC",
"endOfLine": "auto",
"plugins": ["prettier-plugin-organize-attributes"],
"printWidth": 80,
"singleQuote": true,
"tabWidth": 2,

View File

@ -5,7 +5,41 @@ 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).
## 1.301.0 - 2023-08-18
## 1.304.0 - 2023-08-27
### Added
- Added health check endpoints for data enhancers
### Changed
- Upgraded `Nx` from version `16.7.2` to `16.7.4`
- Upgraded `prettier` from version `2.8.4` to `3.0.2`
## 1.303.0 - 2023-08-23
### Added
- Added a blog post: _Ghostfolio joins OSS Friends_
### Changed
- Refreshed the cryptocurrencies list
- Improved the _OSS Friends_ page
### Fixed
- Fixed an issue with the _Trackinsight_ data enhancer for asset profile data
## 1.302.0 - 2023-08-20
### Changed
- Improved the language localization for German (`de`)
- Upgraded `angular` from version `16.1.8` to `16.2.1`
- Upgraded `Nx` from version `16.6.0` to `16.7.2`
## 1.301.1 - 2023-08-19
### Added

View File

@ -41,9 +41,8 @@ export class AuthController {
@Param('accessToken') accessToken: string
): Promise<OAuthResponse> {
try {
const authToken = await this.authService.validateAnonymousLogin(
accessToken
);
const authToken =
await this.authService.validateAnonymousLogin(accessToken);
return { authToken };
} catch {
throw new HttpException(

View File

@ -7,10 +7,10 @@ import {
UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { ExchangeRateService } from './exchange-rate.service';
import { parseISO } from 'date-fns';
@Controller('exchange-rate')
export class ExchangeRateController {

View File

@ -18,6 +18,19 @@ export class HealthController {
@Get()
public async getHealth() {}
@Get('data-enhancer/:name')
public async getHealthOfDataEnhancer(@Param('name') name: string) {
const hasResponse =
await this.healthService.hasResponseFromDataEnhancer(name);
if (hasResponse !== true) {
throw new HttpException(
getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE),
StatusCodes.SERVICE_UNAVAILABLE
);
}
}
@Get('data-provider/:dataSource')
@UseInterceptors(TransformDataSourceInRequestInterceptor)
public async getHealthOfDataProvider(
@ -30,9 +43,8 @@ export class HealthController {
);
}
const hasResponse = await this.healthService.hasResponseFromDataProvider(
dataSource
);
const hasResponse =
await this.healthService.hasResponseFromDataProvider(dataSource);
if (hasResponse !== true) {
throw new HttpException(

View File

@ -1,4 +1,5 @@
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataEnhancerModule } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { Module } from '@nestjs/common';
@ -7,7 +8,7 @@ import { HealthService } from './health.service';
@Module({
controllers: [HealthController],
imports: [ConfigurationModule, DataProviderModule],
imports: [ConfigurationModule, DataEnhancerModule, DataProviderModule],
providers: [HealthService]
})
export class HealthModule {}

View File

@ -1,3 +1,4 @@
import { DataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { Injectable } from '@nestjs/common';
import { DataSource } from '@prisma/client';
@ -5,9 +6,14 @@ import { DataSource } from '@prisma/client';
@Injectable()
export class HealthService {
public constructor(
private readonly dataEnhancerService: DataEnhancerService,
private readonly dataProviderService: DataProviderService
) {}
public async hasResponseFromDataEnhancer(aName: string) {
return this.dataEnhancerService.enhance(aName);
}
public async hasResponseFromDataProvider(aDataSource: DataSource) {
return this.dataProviderService.checkQuote(aDataSource);
}

View File

@ -470,9 +470,8 @@ export class PortfolioService {
transactionPoints[0]?.date ?? format(new Date(), DATE_FORMAT)
);
const startDate = this.getStartDate(dateRange, portfolioStart);
const currentPositions = await portfolioCalculator.getCurrentPositions(
startDate
);
const currentPositions =
await portfolioCalculator.getCurrentPositions(startDate);
const cashDetails = await this.accountService.getCashDetails({
filters,
@ -810,9 +809,8 @@ export class PortfolioService {
const transactionPoints = portfolioCalculator.getTransactionPoints();
const portfolioStart = parseDate(transactionPoints[0].date);
const currentPositions = await portfolioCalculator.getCurrentPositions(
portfolioStart
);
const currentPositions =
await portfolioCalculator.getCurrentPositions(portfolioStart);
const position = currentPositions.positions.find(
(item) => item.symbol === aSymbol
@ -1046,9 +1044,8 @@ export class PortfolioService {
const portfolioStart = parseDate(transactionPoints[0].date);
const startDate = this.getStartDate(dateRange, portfolioStart);
const currentPositions = await portfolioCalculator.getCurrentPositions(
startDate
);
const currentPositions =
await portfolioCalculator.getCurrentPositions(startDate);
const positions = currentPositions.positions.filter(
(item) => !item.quantity.eq(0)
@ -1238,9 +1235,8 @@ export class PortfolioService {
portfolioCalculator.setTransactionPoints(transactionPoints);
const portfolioStart = parseDate(transactionPoints[0].date);
const currentPositions = await portfolioCalculator.getCurrentPositions(
portfolioStart
);
const currentPositions =
await portfolioCalculator.getCurrentPositions(portfolioStart);
const positions = currentPositions.positions.filter(
(item) => !item.quantity.eq(0)

View File

@ -93,9 +93,8 @@ export class SubscriptionService {
public async createSubscriptionViaStripe(aCheckoutSessionId: string) {
try {
const session = await this.stripe.checkout.sessions.retrieve(
aCheckoutSessionId
);
const session =
await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId);
await this.createSubscription({
price: session.amount_total / 100,

View File

@ -15,13 +15,13 @@ import {
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { DataSource } from '@prisma/client';
import { parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { isDate, isEmpty } from 'lodash';
import { LookupItem } from './interfaces/lookup-item.interface';
import { SymbolItem } from './interfaces/symbol-item.interface';
import { SymbolService } from './symbol.service';
import { parseISO } from 'date-fns';
@Controller('symbol')
export class SymbolController {

View File

@ -51,7 +51,9 @@
"3FT": "ThreeFold Token",
"3ULL": "3ULL Coin",
"3XD": "3DChain",
"420CHAN": "420chan",
"4ART": "4ART Coin",
"4CHAN": "4Chan",
"4JNET": "4JNET",
"77G": "GraphenTech",
"7E": "7ELEVEN",
@ -60,6 +62,7 @@
"8BT": "8 Circuit Studios",
"8PAY": "8Pay",
"8X8": "8X8 Protocol",
"9GAG": "9GAG",
"A5T": "Alpha5",
"AAA": "Moon Rabbit",
"AAB": "AAX Token",
@ -101,6 +104,7 @@
"ACN": "AvonCoin",
"ACOIN": "ACoin",
"ACP": "Anarchists Prime",
"ACQ": "Acquire.Fi",
"ACS": "Access Protocol",
"ACT": "Achain",
"ACTIN": "Actinium",
@ -180,7 +184,7 @@
"AGX": "Agricoin",
"AHOO": "Ahoolee",
"AHT": "AhaToken",
"AI": "Multiverse",
"AI": "AiDoge",
"AIB": "AdvancedInternetBlock",
"AIBB": "AiBB",
"AIBK": "AIB Utility Token",
@ -213,6 +217,7 @@
"AKA": "Akroma",
"AKITA": "Akita Inu",
"AKN": "Akoin",
"AKNC": "Aave KNC v1",
"AKRO": "Akropolis",
"AKT": "Akash Network",
"AKTIO": "AKTIO Coin",
@ -237,12 +242,14 @@
"ALIC": "AliCoin",
"ALICE": "My Neighbor Alice",
"ALIEN": "AlienCoin",
"ALINK": "Aave LINK v1",
"ALIS": "ALISmedia",
"ALITA": "Alita Network",
"ALIX": "AlinX",
"ALKI": "Alkimi",
"ALLBI": "ALL BEST ICO",
"ALLEY": "NFT Alley",
"ALLIN": "All in",
"ALN": "Aluna",
"ALOHA": "Aloha",
"ALP": "Alphacon",
@ -410,12 +417,14 @@
"ARIX": "Arix",
"ARK": "ARK",
"ARKER": "Arker",
"ARKM": "Arkham",
"ARKN": "Ark Rivals",
"ARM": "Armory Coin",
"ARMOR": "ARMOR",
"ARMR": "ARMR",
"ARMS": "2Acoin",
"ARNA": "ARNA Panacea",
"ARNM": "Arenum",
"ARNO": "ARNO",
"ARNX": "Aeron",
"ARNXM": "Armor NXM",
@ -472,6 +481,7 @@
"ASTO": "Altered State Token",
"ASTON": "Aston",
"ASTR": "Astar",
"ASTRAFER": "Astrafer",
"ASTRAL": "Astral",
"ASTRO": "AstroSwap",
"ASTROC": "Astroport Classic",
@ -531,6 +541,7 @@
"AURY": "Aurory",
"AUSCM": "Auric Network",
"AUSD": "Appeal dollar",
"AUSDC": "Aave USDC v1",
"AUT": "Autoria",
"AUTHORSHIP": "Authorship",
"AUTO": "Auto",
@ -612,6 +623,7 @@
"BACK": "DollarBack",
"BACOIN": "BACoin",
"BACON": "BaconDAO (BACON)",
"BAD": "Bad Idea AI",
"BADGER": "Badger DAO",
"BAG": "BondAppetit",
"BAGS": "Basis Gold Share",
@ -662,6 +674,7 @@
"BBCT": "TraDove B2BCoin",
"BBDT": "BBD Token",
"BBF": "Bubblefong",
"BBFT": "Block Busters Tech Token",
"BBG": "BigBang",
"BBGC": "BigBang Game",
"BBI": "BelugaPay",
@ -725,6 +738,7 @@
"BDX": "Beldex",
"BDY": "Buddy DAO",
"BEACH": "BeachCoin",
"BEAI": "BeNFT Solutions",
"BEAM": "Beam",
"BEAN": "BeanCash",
"BEAST": "CryptoBeast",
@ -806,6 +820,7 @@
"BIDR": "Binance IDR Stable Coin",
"BIFI": "Beefy.Finance",
"BIFIF": "BiFi",
"BIG": "Big Eyes",
"BIGHAN": "BighanCoin",
"BIGSB": "BigShortBets",
"BIGUP": "BigUp",
@ -1090,6 +1105,7 @@
"BRNK": "Brank",
"BRNX": "Bronix",
"BRO": "Bitradio",
"BROCK": "Bitrock",
"BRONZ": "BitBronze",
"BRT": "Bikerush",
"BRTR": "Barter",
@ -1226,7 +1242,7 @@
"BULL": "Bullieverse",
"BULLC": "BuySell",
"BULLION": "BullionFX",
"BULLS": "BullshitCoin",
"BULLS": "Bull Coin",
"BULLSH": "Bullshit Inu",
"BUMN": "BUMooN",
"BUMP": "Bumper",
@ -1319,8 +1335,10 @@
"CAP": "BottleCaps",
"CAPD": "Capdax",
"CAPP": "Cappasity",
"CAPRICOIN": "CapriCoin",
"CAPS": "Ternoa",
"CAPT": "Bitcoin Captain",
"CAPTAINPLANET": "Captain Planet",
"CAR": "CarBlock",
"CARAT": "Carats Token",
"CARBON": "Carboncoin",
@ -1478,6 +1496,7 @@
"CHECKR": "CheckerChain",
"CHECOIN": "CheCoin",
"CHEDDA": "Chedda",
"CHEEL": "Cheelee",
"CHEESE": "CHEESE",
"CHEESUS": "Cheesus",
"CHEQ": "CHEQD Network",
@ -1520,7 +1539,8 @@
"CHX": "Own",
"CHY": "Concern Poverty Chain",
"CHZ": "Chiliz",
"CIC": "CIChain",
"CIC": "Crazy Internet Coin",
"CICHAIN": "CIChain",
"CIF": "Crypto Improvement Fund",
"CIM": "COINCOME",
"CIN": "CinderCoin",
@ -1630,7 +1650,6 @@
"COB": "Cobinhood",
"COC": "Coin of the champions",
"COCK": "Shibacock",
"COCOS": "COCOS BCX",
"CODEO": "Codeo Token",
"CODEX": "CODEX Finance",
"CODI": "Codi Finance",
@ -1659,7 +1678,7 @@
"COLX": "ColossusCoinXT",
"COM": "Coliseum",
"COMB": "Combo",
"COMBO": "Furucombo",
"COMBO": "COMBO",
"COMFI": "CompliFi",
"COMM": "Community Coin",
"COMMUNITYCOIN": "Community Coin",
@ -1672,7 +1691,6 @@
"CONI": "CoinBene",
"CONS": "ConSpiracy Coin",
"CONSENTIUM": "Consentium",
"CONT": "Contentos",
"CONUN": "CONUN",
"CONV": "Convergence",
"COOK": "Cook",
@ -1683,17 +1701,19 @@
"COPS": "Cops Finance",
"COR": "Corion",
"CORAL": "CoralPay",
"CORE": "Coreum",
"CORE": "Core",
"COREDAO": "coreDAO",
"COREG": "Core Group Asset",
"COREUM": "Coreum",
"CORGI": "Corgi Inu",
"CORN": "CORN",
"CORX": "CorionX",
"COS": "COS",
"COS": "Contentos",
"COSHI": "CoShi Inu",
"COSM": "CosmoChain",
"COSMIC": "CosmicSwap",
"COSP": "Cosplay Token",
"COSS": "COS",
"COSX": "Cosmecoin",
"COT": "CoTrader",
"COTI": "COTI",
@ -1729,7 +1749,7 @@
"CPOOL": "Clearpool",
"CPROP": "CPROP",
"CPRX": "Crypto Perx",
"CPS": "CapriCoin",
"CPS": "Cryptostone",
"CPT": "Cryptaur",
"CPU": "CPUcoin",
"CPX": "Apex Token",
@ -1796,6 +1816,7 @@
"CRTS": "Cratos",
"CRU": "Crust Network",
"CRV": "Curve DAO Token",
"CRVUSD": "crvUSD",
"CRW": "Crown Coin",
"CRWD": "CRWD Network",
"CRWNY": "Crowny Token",
@ -1843,7 +1864,7 @@
"CTLX": "Cash Telex",
"CTN": "Continuum Finance",
"CTO": "Crypto",
"CTP": "Captain Planet",
"CTP": "Ctomorrow Platform",
"CTPL": "Cultiplan",
"CTPT": "Contents Protocol",
"CTR": "Creator Platform",
@ -2007,6 +2028,7 @@
"DBC": "DeepBrain Chain",
"DBCCOIN": "Datablockchain",
"DBD": "Day By Day",
"DBEAR": "DBear Coin",
"DBET": "Decent.bet",
"DBIC": "DubaiCoin",
"DBIX": "DubaiCoin",
@ -2058,6 +2080,7 @@
"DEEP": "DeepCloud AI",
"DEEPG": "Deep Gold",
"DEEX": "DEEX",
"DEEZ": "DEEZ NUTS",
"DEFI": "Defi",
"DEFI5": "DEFI Top 5 Tokens Index",
"DEFIL": "DeFIL",
@ -2162,11 +2185,12 @@
"DIEM": "Facebook Diem",
"DIESEL": "Diesel",
"DIFX": "Digital Financial Exchange",
"DIG": "Dignity",
"DIG": "DIEGO",
"DIGG": "DIGG",
"DIGIC": "DigiCube",
"DIGIF": "DigiFel",
"DIGITAL": "Digital Reserve Currency",
"DIGNITY": "Dignity",
"DIGS": "Diggits",
"DIKO": "Arkadiko",
"DILI": "D Community",
@ -2246,6 +2270,7 @@
"DOGBOSS": "Dog Boss",
"DOGDEFI": "DogDeFiCoin",
"DOGE": "Dogecoin",
"DOGE20": "Doge 2.0",
"DOGEBNB": "DogeBNB",
"DOGEC": "DogeCash",
"DOGECEO": "Doge CEO",
@ -2559,6 +2584,7 @@
"EMC2": "Einsteinium",
"EMD": "Emerald",
"EMIGR": "EmiratesGoldCoin",
"EML": "EML Protocol",
"EMN.CUR": "Eastman Chemical",
"EMON": "Ethermon",
"EMOT": "Sentigraph.io",
@ -2692,6 +2718,7 @@
"ETHD": "Ethereum Dark",
"ETHER": "Etherparty",
"ETHERDELTA": "EtherDelta",
"ETHERKING": "Ether Kingdoms Token",
"ETHERNITY": "Ethernity Chain",
"ETHF": "EthereumFair",
"ETHIX": "EthicHub",
@ -2709,6 +2736,7 @@
"ETHSHIB": "Eth Shiba",
"ETHV": "Ethverse",
"ETHW": "Ethereum PoW",
"ETHX": "Stader ETHx",
"ETHY": "Ethereum Yield",
"ETI": "EtherInc",
"ETK": "Energi Token",
@ -2722,7 +2750,7 @@
"ETR": "Electric Token",
"ETRNT": "Eternal Trusts",
"ETS": "ETH Share",
"ETSC": "Ether star blockchain",
"ETSC": "Ether star blockchain",
"ETT": "EncryptoTel",
"ETY": "Ethereum Cloud",
"ETZ": "EtherZero",
@ -2773,6 +2801,7 @@
"EXB": "ExaByte (EXB)",
"EXC": "Eximchain",
"EXCC": "ExchangeCoin",
"EXCHANGEN": "ExchangeN",
"EXCL": "Exclusive Coin",
"EXE": "ExeCoin",
"EXFI": "Flare Finance",
@ -2781,7 +2810,7 @@
"EXLT": "ExtraLovers",
"EXM": "EXMO Coin",
"EXMR": "EXMR FDN",
"EXN": "ExchangeN",
"EXN": "Exeno",
"EXO": "Exosis",
"EXP": "Expanse",
"EXRD": "Radix",
@ -2814,6 +2843,7 @@
"FAIR": "FairCoin",
"FAIRC": "Faireum Token",
"FAIRG": "FairGame",
"FAKE": "FAKE COIN",
"FAKT": "Medifakt",
"FALCONS": "Falcon Swaps",
"FAME": "Fame MMA",
@ -2870,6 +2900,7 @@
"FEN": "First Ever NFT",
"FENOMY": "Fenomy",
"FER": "Ferro",
"FERC": "FairERC20",
"FERMA": "Ferma",
"FESS": "Fesschain",
"FET": "Fetch.AI",
@ -2931,7 +2962,7 @@
"FLASH": "Flashstake",
"FLASHC": "FLASH coin",
"FLC": "FlowChainCoin",
"FLD": "FLUID",
"FLD": "FluidAI",
"FLDC": "Folding Coin",
"FLDT": "FairyLand",
"FLETA": "FLETA",
@ -3091,6 +3122,7 @@
"FUEL": "Jetfuel Finance",
"FUJIN": "Fujinto",
"FUKU": "Furukuru",
"FUMO": "Alien Milady Fumo",
"FUN": "FUN Token",
"FUNC": "FunCoin",
"FUND": "Unification",
@ -3101,6 +3133,7 @@
"FUNDZ": "FundFantasy",
"FUNK": "Cypherfunks Coin",
"FUR": "Furio",
"FURU": "Furucombo",
"FURY": "Engines of Fury",
"FUS": "Fus",
"FUSE": "Fuse Network Token",
@ -3118,6 +3151,7 @@
"FXP": "FXPay",
"FXS": "Frax Share",
"FXT": "FuzeX",
"FXY": "Floxypay",
"FYN": "Affyn",
"FYP": "FlypMe",
"FYZ": "Fyooz",
@ -3172,6 +3206,7 @@
"GAT": "GATCOIN",
"GATE": "GATENet",
"GATEWAY": "Gateway Protocol",
"GAYPEPE": "Gay Pepe",
"GAZE": "GazeTV",
"GB": "GoldBlocks",
"GBA": "Geeba",
@ -3222,6 +3257,7 @@
"GEMZ": "Gemz Social",
"GEN": "DAOstack",
"GENE": "Genopets",
"GENIE": "The Genie",
"GENIX": "Genix",
"GENS": "Genshiro",
"GENSTAKE": "Genstake",
@ -3261,6 +3297,7 @@
"GHCOLD": "Galaxy Heroes Coin",
"GHD": "Giftedhands",
"GHNY": "Grizzly Honey",
"GHO": "GHO",
"GHOST": "GhostbyMcAfee",
"GHOSTCOIN": "GhostCoin",
"GHOSTM": "GhostMarket",
@ -3274,6 +3311,7 @@
"GIFT": "GiftNet",
"GIG": "GigaCoin",
"GIGA": "GigaSwap",
"GIGX": "GigXCoin",
"GIM": "Gimli",
"GIMMER": "Gimmer",
"GIN": "GINcoin",
@ -3385,6 +3423,7 @@
"GOVT": "The Government Network",
"GOZ": "Göztepe S.K. Fan Token",
"GP": "Wizards And Dragons",
"GPBP": "Genius Playboy Billionaire Philanthropist",
"GPKR": "Gold Poker",
"GPL": "Gold Pressed Latinum",
"GPPT": "Pluto Project Coin",
@ -3501,7 +3540,8 @@
"HALF": "0.5X Long Bitcoin Token",
"HALFSHIT": "0.5X Long Shitcoin Index Token",
"HALLO": "Halloween Coin",
"HALO": "Halo Platform",
"HALO": "Halo Coin",
"HALOPLATFORM": "Halo Platform",
"HAM": "Hamster",
"HAMS": "HamsterCoin",
"HANA": "Hanacoin",
@ -3598,6 +3638,7 @@
"HILL": "President Clinton",
"HINA": "Hina Inu",
"HINT": "Hintchain",
"HIPPO": "HIPPO",
"HIRE": "HireMatch",
"HIT": "HitChain",
"HITBTC": "HitBTC Token",
@ -3634,6 +3675,7 @@
"HNTR": "Hunter",
"HNY": "Honey",
"HNZO": "Hanzo Inu",
"HOBO": "HOBO THE BEAR",
"HOD": "HoDooi.com",
"HODL": "HOdlcoin",
"HOGE": "Hoge Finance",
@ -3839,7 +3881,7 @@
"IMPCN": "Brain Space",
"IMPER": "Impermax",
"IMPS": "Impulse Coin",
"IMPT": "Ether Kingdoms Token",
"IMPT": "IMPT",
"IMPULSE": "IMPULSE by FDR",
"IMS": "Independent Money System",
"IMST": "Imsmart",
@ -4001,6 +4043,7 @@
"JAM": "Tune.Fm",
"JANE": "JaneCoin",
"JAR": "Jarvis+",
"JARED": "Jared From Subway",
"JASMY": "JasmyCoin",
"JBS": "JumBucks Coin",
"JBX": "Juicebox",
@ -4163,9 +4206,10 @@
"KIN": "Kin",
"KIND": "Kind Ads",
"KINE": "Kine Protocol",
"KING": "King Finance",
"KING": "KING",
"KING93": "King93",
"KINGDOMQUEST": "Kingdom Quest",
"KINGF": "King Finance",
"KINGSHIB": "King Shiba",
"KINGSWAP": "KingSwap",
"KINT": "Kintsugi",
@ -4175,6 +4219,7 @@
"KISC": "Kaiser",
"KISHIMOTO": "Kishimoto Inu",
"KISHU": "Kishu Inu",
"KITA": "KITA INU",
"KITSU": "Kitsune Inu",
"KITTY": "Kitty Inu",
"KKO": "Kineko",
@ -4267,10 +4312,12 @@
"KUBO": "KUBO",
"KUBOS": "KubosCoin",
"KUE": "Kuende",
"KUJI": "Kujira",
"KUMA": "Kuma Inu",
"KUNCI": "Kunci Coin",
"KUR": "Kuro",
"KURT": "Kurrent",
"KUSA": "Kusa Inu",
"KUSD": "Kowala",
"KUSH": "KushCoin",
"KUV": "Kuverit",
@ -4280,6 +4327,7 @@
"KVT": "Kinesis Velocity Token",
"KWATT": "4New",
"KWD": "KIWI DEFI",
"KWENTA": "Kwenta",
"KWH": "KWHCoin",
"KWIK": "KwikSwap",
"KWS": "Knight War Spirits",
@ -4299,7 +4347,9 @@
"LABX": "Stakinglab",
"LACCOIN": "LocalAgro",
"LACE": "Lovelace World",
"LADYS": "Milady Meme Coin",
"LAEEB": "LaEeb",
"LAELAPS": "Laelaps",
"LAIKA": "Laika Protocol",
"LALA": "LaLa World",
"LAMB": "Lambda",
@ -4455,13 +4505,14 @@
"LLAND": "Lyfe Land",
"LLG": "Loligo",
"LLION": "Lydian Lion",
"LM": "LM Token",
"LM": "LeisureMeta",
"LMAO": "LMAO Finance",
"LMC": "LomoCoin",
"LMCH": "Latamcash",
"LMCSWAP": "LimoCoin SWAP",
"LMR": "Lumerin",
"LMT": "Lympo Market Token",
"LMTOKEN": "LM Token",
"LMXC": "LimonX",
"LMY": "Lunch Money",
"LN": "LINK",
@ -4530,6 +4581,7 @@
"LRG": "Largo Coin",
"LRN": "Loopring [NEO]",
"LSD": "LightSpeedCoin",
"LSETH": "Liquid Staked ETH",
"LSK": "Lisk",
"LSP": "Lumenswap",
"LSS": "Lossless",
@ -4626,6 +4678,7 @@
"MAEP": "Maester Protocol",
"MAG": "Magnet",
"MAGIC": "Magic",
"MAGICF": "MagicFox",
"MAHA": "MahaDAO",
"MAI": "Mindsync",
"MAID": "MaidSafe Coin",
@ -4639,6 +4692,7 @@
"MANDOX": "MandoX",
"MANGA": "Manga Token",
"MANNA": "Manna",
"MANTLE": "Mantle",
"MAP": "MAP Protocol",
"MAPC": "MapCoin",
"MAPE": "Mecha Morphing",
@ -4672,6 +4726,7 @@
"MATIC": "Polygon",
"MATPAD": "MaticPad",
"MATTER": "AntiMatter",
"MAV": "Maverick Protocol",
"MAX": "MaxCoin",
"MAXR": "Max Revive",
"MAY": "Theresa May Coin",
@ -4776,6 +4831,7 @@
"MESA": "MetaVisa",
"MESG": "MESG",
"MESH": "MeshBox",
"MESSI": "MESSI COIN",
"MET": "Metronome",
"META": "Metadium",
"METAC": "Metacoin",
@ -4881,6 +4937,7 @@
"MIODIO": "MIODIOCOIN",
"MIOTA": "IOTA",
"MIR": "Mirror Protocol",
"MIRACLE": "MIRACLE",
"MIRC": "MIR COIN",
"MIS": "Mithril Share",
"MISA": "Sangkara",
@ -4938,7 +4995,6 @@
"MNRB": "MoneyRebel",
"MNS": "Monnos",
"MNST": "MoonStarter",
"MNT": "microNFT",
"MNTC": "Manet Coin",
"MNTG": "Monetas",
"MNTL": "AssetMantle",
@ -4967,6 +5023,7 @@
"MOF": "Molecular Future (TRC20)",
"MOFI": "MobiFi",
"MOFOLD": "Molecular Future (ERC20)",
"MOG": "Mog Coin",
"MOGU": "Mogu",
"MOGX": "Mogu",
"MOI": "MyOwnItem",
@ -4989,9 +5046,11 @@
"MONEYIMT": "MoneyToken",
"MONF": "Monfter",
"MONG": "MongCoin",
"MONG20": "Mongoose 2.0",
"MONI": "Monsta Infinite",
"MONK": "Monkey Project",
"MONKEY": "Monkey",
"MONKEYS": "Monkeys Token",
"MONO": "MonoX",
"MONONOKEINU": "Mononoke Inu",
"MONS": "Monsters Clan",
@ -5011,11 +5070,13 @@
"MOONSHOT": "Moonshot",
"MOOO": "Hashtagger",
"MOOV": "dotmoovs",
"MOOX": "Moox Protocol",
"MOPS": "Mops",
"MORA": "Meliora",
"MORE": "More Coin",
"MOS": "MOS Coin",
"MOT": "Olympus Labs",
"MOTG": "MetaOctagon",
"MOTI": "Motion",
"MOTO": "Motocoin",
"MOV": "MovieCoin",
@ -5076,6 +5137,7 @@
"MSWAP": "MoneySwap",
"MT": "MyToken",
"MTA": "Meta",
"MTB": "MetaBridge",
"MTBC": "Metabolic",
"MTC": "MEDICAL TOKEN CURRENCY",
"MTCMN": "MTC Mesh",
@ -5108,6 +5170,7 @@
"MUE": "MonetaryUnit",
"MULTI": "Multichain",
"MULTIBOT": "Multibot",
"MULTIV": "Multiverse",
"MUN": "MUNcoin",
"MUNCH": "Munch Token",
"MUSD": "mStable USD",
@ -5648,6 +5711,7 @@
"OZP": "OZAPHYRE",
"P202": "Project 202",
"P2PS": "P2P Solutions Foundation",
"PAAL": "PAAL AI",
"PAC": "PAC Protocol",
"PACOCA": "Pacoca",
"PAD": "NearPad",
@ -5736,6 +5800,7 @@
"PEARL": "Pearl Finance",
"PEC": "PeaceCoin",
"PEEL": "Meta Apes",
"PEEPA": "Peepa",
"PEEPS": "The Peoples Coin",
"PEG": "PegNet",
"PEGS": "PegShares",
@ -5748,6 +5813,7 @@
"PEOPLE": "ConstitutionDAO",
"PEOS": "pEOS",
"PEPE": "Pepe",
"PEPE20": "Pepe 2.0",
"PEPECASH": "Pepe Cash",
"PEPPER": "Pepper Token",
"PEPS": "PEPS Coin",
@ -5822,6 +5888,7 @@
"PINK": "PinkCoin",
"PINKX": "PantherCoin",
"PINMO": "Pinmo",
"PINO": "Pinocchu",
"PINU": "Piccolo Inu",
"PIO": "Pioneershares",
"PIPI": "Pippi Finance",
@ -5885,6 +5952,7 @@
"PLS": "Pulsechain",
"PLSD": "PulseDogecoin",
"PLSPAD": "PulsePad",
"PLSX": "PulseX",
"PLT": "Poollotto.finance",
"PLTC": "PlatonCoin",
"PLTX": "PlutusX",
@ -5911,7 +5979,6 @@
"PNK": "Kleros",
"PNL": "True PNL",
"PNODE": "Pinknode",
"PNP": "LogisticsX",
"PNT": "pNetwork Token",
"PNX": "PhantomX",
"PNY": "Peony Coin",
@ -5927,6 +5994,7 @@
"POINTS": "Cryptsy Points",
"POK": "Pokmonsters",
"POKEM": "Pokemonio",
"POKEMON": "Pokemon",
"POKER": "PokerCoin",
"POKT": "Pocket Network",
"POL": "Pool-X",
@ -6010,6 +6078,7 @@
"PRIME": "Echelon Prime",
"PRIMECHAIN": "PrimeChain",
"PRINT": "Printer.Finance",
"PRINTERIUM": "Printerium",
"PRINTS": "FingerprintsDAO",
"PRISM": "Prism",
"PRIX": "Privatix",
@ -6033,7 +6102,7 @@
"PROTON": "Proton",
"PROUD": "PROUD Money",
"PROXI": "PROXI",
"PRP": "Papyrus",
"PRP": "Pepe Prime",
"PRPS": "Purpose",
"PRPT": "Purple Token",
"PRQ": "PARSIQ",
@ -6042,7 +6111,7 @@
"PRTG": "Pre-Retogeum",
"PRV": "PrivacySwap",
"PRVS": "Previse",
"PRX": "Printerium",
"PRX": "Parex",
"PRXY": "Proxy",
"PRY": "PRIMARY",
"PSB": "Planet Sandbox",
@ -6120,6 +6189,7 @@
"PYRAM": "Pyram Token",
"PYRK": "Pyrk",
"PYT": "Payther",
"PYUSD": "PayPal USD",
"PZM": "Prizm",
"Q1S": "Quantum1Net",
"Q2C": "QubitCoin",
@ -6178,6 +6248,7 @@
"QUA": "Quantum Tech",
"QUACK": "Rich Quack",
"QUAM": "Quam Network",
"QUANT": "Quant Finance",
"QUARASHI": "Quarashi Network",
"QUARTZ": "Sandclock",
"QUASA": "Quasacoin",
@ -6201,7 +6272,7 @@
"RAC": "RAcoin",
"RACA": "Radio Caca",
"RACEFI": "RaceFi",
"RAD": "Radicle",
"RAD": "Radworks",
"RADAR": "DappRadar",
"RADI": "RadicalCoin",
"RADIO": "RadioShack",
@ -6220,7 +6291,7 @@
"RAM": "Ramifi Protocol",
"RAMP": "RAMP",
"RANKER": "RankerDao",
"RAP": "Rapture",
"RAP": "Philosoraptor",
"RAPDOGE": "RapDoge",
"RARE": "SuperRare",
"RARI": "Rarible",
@ -6277,6 +6348,7 @@
"REA": "Realisto",
"REAL": "RealLink",
"REALM": "Realm",
"REALMS": "Realms of Ethernity",
"REALPLATFORM": "REAL",
"REALY": "Realy Metaverse",
"REAP": "ReapChain",
@ -6287,6 +6359,7 @@
"RED": "RED TOKEN",
"REDC": "RedCab",
"REDCO": "Redcoin",
"REDDIT": "Reddit",
"REDI": "REDi",
"REDLANG": "RED",
"REDLC": "Redlight Chain",
@ -6324,7 +6397,7 @@
"REST": "Restore",
"RET": "RealTract",
"RETAIL": "Retail.Global",
"RETH": "Realms of Ethernity",
"RETH": "Rocket Pool ETH",
"RETH2": "rETH2",
"RETIRE": "Retire Token",
"REU": "REUCOIN",
@ -6351,6 +6424,7 @@
"RGP": "Rigel Protocol",
"RGT": "Rari Governance Token",
"RHEA": "Rhea",
"RHINO": "RHINO",
"RHOC": "RChain",
"RHP": "Rhypton Club",
"RIC": "Riecoin",
@ -6490,6 +6564,7 @@
"RWE": "Real-World Evidence",
"RWN": "Rowan Token",
"RWS": "Robonomics Web Services",
"RXD": "Radiant",
"RXT": "RIMAUNANGIS",
"RYC": "RoyalCoin",
"RYCN": "RoyalCoin 2.0",
@ -6564,6 +6639,7 @@
"SBTC": "Super Bitcoin",
"SC": "Siacoin",
"SCA": "SiaClassic",
"SCAM": "Scam Coin",
"SCAP": "SafeCapital",
"SCAR": "Velhalla",
"SCASH": "SpaceCash",
@ -6624,6 +6700,7 @@
"SEER": "SEER",
"SEI": "Sei",
"SEL": "SelenCoin",
"SELF": "SELFCrypto",
"SEM": "Semux",
"SEN": "Sentaro",
"SENATE": "SENATE",
@ -6665,6 +6742,7 @@
"SGE": "Society of Galactic Exploration",
"SGLY": "Singularity",
"SGN": "Signals Network",
"SGO": "SafuuGO",
"SGOLD": "SpaceGold",
"SGP": "SGPay",
"SGR": "Sogur Currency",
@ -6684,6 +6762,7 @@
"SHEESH": "Sheesh it is bussin bussin",
"SHEESHA": "Sheesha Finance",
"SHELL": "Shell Token",
"SHERA": "Shera Tokens",
"SHFL": "SHUFFLE!",
"SHFT": "Shyft Network",
"SHI": "Shirtum",
@ -6719,6 +6798,8 @@
"SHR": "ShareToken",
"SHREK": "ShrekCoin",
"SHROOM": "Shroom.Finance",
"SHROOMFOX": "Magic Shroom",
"SHS": "SHEESH",
"SHX": "Stronghold Token",
"SI": "Siren",
"SIB": "SibCoin",
@ -7018,9 +7099,11 @@
"STEN": "Steneum Coin",
"STEP": "Step Finance",
"STEPH": "Step Hero",
"STEPR": "Step",
"STEPS": "Steps",
"STERLINGCOIN": "SterlingCoin",
"STETH": "Staked Ether",
"STEWIE": "Stewie Coin",
"STEX": "STEX",
"STF": "Structure Finance",
"STFX": "STFX",
@ -7055,7 +7138,7 @@
"STR": "Sourceless",
"STRAKS": "Straks",
"STRAX": "Stratis",
"STRAY": "Animal Token",
"STRAY": "Stray Dog",
"STREAM": "STREAMIT COIN",
"STRIP": "Stripto",
"STRK": "Strike",
@ -7361,6 +7444,7 @@
"TOM": "TOM Finance",
"TOMAHAWKCOIN": "Tomahawkcoin",
"TOMB": "Tomb",
"TOMI": "tomiNet",
"TOMO": "TomoChain",
"TOMOE": "TomoChain ERC20",
"TOMS": "TomTomCoin",
@ -7385,6 +7469,7 @@
"TOTM": "Totem",
"TOWER": "Tower",
"TOWN": "Town Star",
"TOX": "INTOverse",
"TOZ": "Tozex",
"TP": "Token Swap",
"TPAD": "TrustPad",
@ -7600,6 +7685,7 @@
"UNITY": "SuperNET",
"UNIVRS": "Universe",
"UNIX": "UniX",
"UNLEASH": "UnleashClub",
"UNN": "UNION Protocol Governance Token",
"UNO": "Unobtanium",
"UNORE": "UnoRe",
@ -7673,6 +7759,7 @@
"UTT": "United Traders Token",
"UTU": "UTU Protocol",
"UUU": "U Network",
"UWU": "uwu",
"UZUMAKI": "Uzumaki Inu",
"VAB": "Vabble",
"VADER": "Vader Protocol",
@ -7695,6 +7782,7 @@
"VCF": "Valencia CF Fan Token",
"VCG": "VCGamers",
"VCK": "28VCK",
"VCORE": "VCORE",
"VDG": "VeriDocGlobal",
"VDL": "Vidulum",
"VDO": "VidioCoin",
@ -7710,6 +7798,7 @@
"VEIL": "VEIL",
"VELA": "Vela Token",
"VELO": "Velo",
"VELOD": "Velodrome Finance",
"VELOX": "Velox",
"VELOXPROJECT": "Velox",
"VEMP": "vEmpire DDAO",
@ -7782,6 +7871,7 @@
"VNT": "VNT Chain",
"VNTW": "Value Network Token",
"VNX": "VisionX",
"VNXAU": "VNX Gold",
"VNXLU": "VNX Exchange",
"VOCO": "Provoco",
"VODKA": "Vodka Token",
@ -7902,7 +7992,8 @@
"WEC": "Whole Earth Coin",
"WEGEN": "WeGen Platform",
"WELD": "Weld",
"WELL": "Well",
"WELL": "Moonwell",
"WELLTOKEN": "Well",
"WELT": "Fabwelt",
"WELUPS": "Welups Blockchain",
"WEMIX": "WEMIX",
@ -7958,6 +8049,7 @@
"WIX": "Wixlar",
"WIZ": "WIZ Protocol",
"WKD": "Wakanda Inu",
"WLD": "Worldcoin",
"WLF": "Wolfs Group",
"WLITI": "wLITI",
"WLK": "Wolk",
@ -7983,6 +8075,7 @@
"WNZ": "Winerz",
"WOA": "Wrapped Origin Axie",
"WOD": "World of Defish",
"WOID": "WORLD ID",
"WOJ": "Wojak Finance",
"WOLF": "Insanity Coin",
"WOLFILAND": "Wolfiland",
@ -8000,6 +8093,7 @@
"WOOFY": "Woofy",
"WOOL": "Wolf Game Wool",
"WOONK": "Woonkly",
"WOOO": "wooonen",
"WOOP": "Woonkly Power",
"WOP": "WorldPay",
"WORLD": "World Token",
@ -8010,6 +8104,7 @@
"WOZX": "Efforce",
"WPC": "WePiggy Coin",
"WPE": "OPES (Wrapped PE)",
"WPLS": "Wrapped Pulse",
"WPP": "Green Energy Token",
"WPR": "WePower",
"WQT": "Work Quest",
@ -8049,6 +8144,7 @@
"WZEC": "Wrapped Zcash",
"WZENIQ": "Wrapped Zeniq (ETH)",
"WZRD": "Wizardia",
"X": "AI-X",
"X2": "X2Coin",
"X2Y2": "X2Y2",
"X42": "X42 Protocol",
@ -8096,7 +8192,7 @@
"XCI": "Cannabis Industry Coin",
"XCLR": "ClearCoin",
"XCM": "CoinMetro",
"XCN": "Chain",
"XCN": "Onyxcoin",
"XCO": "XCoin",
"XCONSOL": "X-Consoles",
"XCP": "CounterParty",
@ -8365,6 +8461,7 @@
"YUANG": "Yuang Coin",
"YUCJ": "Yu Coin",
"YUCT": "Yucreat",
"YUDI": "Yudi",
"YUM": "Yumerium",
"YUMMY": "Yummy",
"YUP": "Crowdholding",

View File

@ -142,6 +142,10 @@
<loc>https://ghostfol.io/en/blog/2023/07/exploring-the-path-to-fire</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/en/blog/2023/08/ghostfolio-joins-oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/en/faq</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
@ -322,6 +326,10 @@
<loc>https://ghostfol.io/es/sobre/licencia</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/es/sobre/oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/es/sobre/politica-de-privacidad</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
@ -342,6 +350,10 @@
<loc>https://ghostfol.io/fr/a-propos/licence</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/fr/a-propos/oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/fr/a-propos/politique-de-confidentialite</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
@ -396,12 +408,16 @@
<loc>https://ghostfol.io/it/informazioni-su/changelog</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/it/informazioni-su/informativa-sulla-privacy</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/it/informazioni-su/licenza</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/it/informazioni-su/informativa-sulla-privacy</loc>
<loc>https://ghostfol.io/it/informazioni-su/oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
@ -460,6 +476,10 @@
<loc>https://ghostfol.io/nl/over/licentie</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/nl/over/oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/nl/over/privacybeleid</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
@ -520,6 +540,10 @@
<loc>https://ghostfol.io/pt/sobre/licenca</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/pt/sobre/oss-friends</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/pt/sobre/politica-de-privacidade</loc>
<lastmod>${currentDate}T00:00:00+00:00</lastmod>

View File

@ -71,6 +71,10 @@ const locales = {
'/en/blog/2023/07/exploring-the-path-to-fire': {
featureGraphicPath: 'assets/images/blog/20230701.jpg',
title: `Exploring the Path to FIRE - ${titleShort}`
},
'/en/blog/2023/08/ghostfolio-joins-oss-friends': {
featureGraphicPath: 'assets/images/blog/ghostfolio-joins-oss-friends.png',
title: `Ghostfolio joins OSS Friends - ${titleShort}`
}
};

View File

@ -127,12 +127,10 @@ export class DataGatheringService {
uniqueAssets = await this.getUniqueAssets();
}
const assetProfiles = await this.dataProviderService.getAssetProfiles(
uniqueAssets
);
const symbolProfiles = await this.symbolProfileService.getSymbolProfiles(
uniqueAssets
);
const assetProfiles =
await this.dataProviderService.getAssetProfiles(uniqueAssets);
const symbolProfiles =
await this.symbolProfileService.getSymbolProfiles(uniqueAssets);
for (const [symbol, assetProfile] of Object.entries(assetProfiles)) {
const symbolMapping = symbolProfiles.find((symbolProfile) => {

View File

@ -4,14 +4,18 @@ import { TrackinsightDataEnhancerService } from '@ghostfolio/api/services/data-p
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
import { Module } from '@nestjs/common';
import { DataEnhancerService } from './data-enhancer.service';
@Module({
exports: [
'DataEnhancers',
DataEnhancerService,
TrackinsightDataEnhancerService,
YahooFinanceDataEnhancerService
YahooFinanceDataEnhancerService,
'DataEnhancers'
],
imports: [ConfigurationModule, CryptocurrencyModule],
providers: [
DataEnhancerService,
TrackinsightDataEnhancerService,
YahooFinanceDataEnhancerService,
{

View File

@ -0,0 +1,44 @@
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
import { HttpException, Inject, Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
@Injectable()
export class DataEnhancerService {
public constructor(
@Inject('DataEnhancers')
private readonly dataEnhancers: DataEnhancerInterface[]
) {}
public async enhance(aName: string) {
const dataEnhancer = this.dataEnhancers.find((dataEnhancer) => {
return dataEnhancer.getName() === aName;
});
if (!dataEnhancer) {
throw new HttpException(
getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
}
try {
const assetProfile = await dataEnhancer.enhance({
response: {
assetClass: 'EQUITY',
assetSubClass: 'ETF'
},
symbol: dataEnhancer.getTestSymbol()
});
if (
(assetProfile.countries as unknown as Prisma.JsonArray)?.length > 0 &&
(assetProfile.sectors as unknown as Prisma.JsonArray)?.length > 0
) {
return true;
}
} catch {}
return false;
}
}

View File

@ -7,7 +7,7 @@ import got from 'got';
@Injectable()
export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
private static baseUrl = 'https://data.trackinsight.com';
private static baseUrl = 'https://www.trackinsight.com/data-api';
private static countries = require('countries-list/dist/countries.json');
private static countriesMapping = {
'Russian Federation': 'Russia'
@ -33,14 +33,22 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
}
const profile = await got(
`${TrackinsightDataEnhancerService.baseUrl}/data-api/funds/${symbol}.json`
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`
)
.json<any>()
.catch(() => {
return {};
return got(
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol.split(
'.'
)?.[0]}.json`
)
.json<any>()
.catch(() => {
return {};
});
});
const isin = profile.isin?.split(';')?.[0];
const isin = profile?.isin?.split(';')?.[0];
if (isin) {
response.isin = isin;
@ -52,10 +60,14 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
.json<any>()
.catch(() => {
return got(
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${
symbol.split('.')?.[0]
}.json`
);
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol.split(
'.'
)?.[0]}.json`
)
.json<any>()
.catch(() => {
return {};
});
});
if (holdings?.weight < 0.95) {
@ -114,4 +126,8 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
public getName() {
return 'TRACKINSIGHT';
}
public getTestSymbol() {
return 'QQQ';
}
}

View File

@ -99,9 +99,8 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
yahooSymbol = quotes[0].symbol;
}
const { countries, sectors, url } = await this.getAssetProfile(
yahooSymbol
);
const { countries, sectors, url } =
await this.getAssetProfile(yahooSymbol);
if (countries) {
response.countries = countries;
@ -234,6 +233,10 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
return DataSource.YAHOO;
}
public getTestSymbol() {
return 'AAPL';
}
public parseAssetClass({
quoteType,
shortName

View File

@ -10,4 +10,6 @@ export interface DataEnhancerInterface {
}): Promise<Partial<SymbolProfile>>;
getName(): string;
getTestSymbol(): string;
}

View File

@ -65,9 +65,8 @@ export class TwitterBotService {
status += benchmarkListing;
}
const { data: createdTweet } = await this.twitterClient.v2.tweet(
status
);
const { data: createdTweet } =
await this.twitterClient.v2.tweet(status);
Logger.log(
`Fear & Greed Index has been tweeted: https://twitter.com/ghostfolio_/status/${createdTweet.id}`,

View File

@ -4,20 +4,24 @@ import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strate
import { ModulePreloadService } from './core/module-preload.service';
export const paths = {
about: $localize`about`,
faq: $localize`faq`,
features: $localize`features`,
license: $localize`license`,
markets: $localize`markets`,
pricing: $localize`pricing`,
privacyPolicy: $localize`privacy-policy`,
register: $localize`register`,
resources: $localize`resources`
};
const routes: Routes = [
...[
'about',
/////
'a-propos',
'informazioni-su',
'over',
'sobre',
'ueber-uns'
].map((path) => ({
path,
{
path: paths.about,
loadChildren: () =>
import('./pages/about/about-page.module').then((m) => m.AboutPageModule)
})),
},
{
path: 'account',
loadChildren: () =>
@ -42,64 +46,40 @@ const routes: Routes = [
loadChildren: () =>
import('./pages/auth/auth-page.module').then((m) => m.AuthPageModule)
},
...['blog'].map((path) => ({
path,
{
path: 'blog',
loadChildren: () =>
import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
})),
},
{
path: 'demo',
loadChildren: () =>
import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule)
},
...[
'faq',
/////
'domande-piu-frequenti',
'foire-aux-questions',
'haeufig-gestellte-fragen',
'perguntas-mais-frequentes',
'preguntas-mas-frecuentes',
'vaak-gestelde-vragen'
].map((path) => ({
path,
{
path: paths.faq,
loadChildren: () =>
import('./pages/faq/faq-page.module').then((m) => m.FaqPageModule)
})),
...[
'features',
/////
'fonctionnalites',
'funcionalidades',
'funzionalita',
'kenmerken'
].map((path) => ({
path,
},
{
path: paths.features,
loadChildren: () =>
import('./pages/features/features-page.module').then(
(m) => m.FeaturesPageModule
)
})),
},
{
path: 'home',
loadChildren: () =>
import('./pages/home/home-page.module').then((m) => m.HomePageModule)
},
...[
'markets',
/////
'maerkte',
'marches',
'markten',
'mercados',
'mercati'
].map((path) => ({
path,
{
path: paths.markets,
loadChildren: () =>
import('./pages/markets/markets-page.module').then(
(m) => m.MarketsPageModule
)
})),
},
{
path: 'open',
loadChildren: () =>
@ -119,53 +99,27 @@ const routes: Routes = [
(m) => m.PortfolioPageModule
)
},
...[
'pricing',
/////
'precios',
'precos',
'preise',
'prezzi',
'prijzen',
'prix'
].map((path) => ({
path,
{
path: paths.pricing,
loadChildren: () =>
import('./pages/pricing/pricing-page.module').then(
(m) => m.PricingPageModule
)
})),
...[
'register',
/////
'enregistrement',
'iscrizione',
'registo',
'registratie',
'registrierung',
'registro'
].map((path) => ({
path,
},
{
path: paths.register,
loadChildren: () =>
import('./pages/register/register-page.module').then(
(m) => m.RegisterPageModule
)
})),
...[
'resources',
/////
'bronnen',
'recursos',
'ressourcen',
'ressources',
'risorse'
].map((path) => ({
path,
},
{
path: paths.resources,
loadChildren: () =>
import('./pages/resources/resources-page.module').then(
(m) => m.ResourcesPageModule
)
})),
},
{
path: 'start',
loadChildren: () =>

View File

@ -19,7 +19,7 @@
<a
*ngIf="canCreateAccount"
class="text-center"
[routerLink]="['/register']"
[routerLink]="routerLinkRegister"
>
<div
class="cursor-pointer d-inline-block info-message px-3 py-2"
@ -43,22 +43,7 @@
<router-outlet></router-outlet>
</main>
<footer
*ngIf="
(currentRoute === 'blog' ||
currentRoute === 'faq' ||
currentRoute === 'features' ||
currentRoute === 'markets' ||
currentRoute === 'open' ||
currentRoute === 'p' ||
currentRoute === 'pricing' ||
currentRoute === 'resources' ||
currentRoute === 'register' ||
currentRoute === 'start') &&
deviceType !== 'mobile'
"
class="d-flex justify-content-center py-4 w-100"
>
<footer *ngIf="showFooter" class="d-flex justify-content-center py-4 w-100">
<div class="container">
<div class="mb-3 row">
<div class="col-sm">
@ -68,36 +53,38 @@
<div class="h6 mt-2" i18n>Personal Finance</div>
<ul class="list-unstyled">
<li *ngIf="hasPermissionToAccessFearAndGreedIndex">
<a i18n [routerLink]="['/markets']">Markets</a>
<a i18n [routerLink]="routerLinkMarkets">Markets</a>
</li>
<li><a i18n [routerLink]="['/resources']">Resources</a></li>
<li><a i18n [routerLink]="routerLinkResources">Resources</a></li>
</ul>
</div>
<div class="col-sm">
<div class="h6 mt-2">Ghostfolio</div>
<ul class="list-unstyled">
<li><a i18n [routerLink]="['/about']">About</a></li>
<li><a i18n [routerLink]="routerLinkAbout">About</a></li>
<li *ngIf="hasPermissionForBlog">
<a i18n [routerLink]="['/blog']">Blog</a>
</li>
<li>
<a i18n [routerLink]="['/about', 'changelog']">Changelog</a>
<a i18n [routerLink]="routerLinkAboutChangelog">Changelog</a>
</li>
<li><a i18n [routerLink]="['/features']">Features</a></li>
<li><a i18n [routerLink]="routerLinkFeatures">Features</a></li>
<li *ngIf="hasPermissionForSubscription">
<a i18n [routerLink]="['/faq']">Frequently Asked Questions (FAQ)</a>
<a i18n [routerLink]="routerLinkFaq"
>Frequently Asked Questions (FAQ)</a
>
</li>
<li>
<a i18n [routerLink]="['/about', 'license']">License</a>
<a i18n [routerLink]="routerLinkAboutLicense">License</a>
</li>
<li *ngIf="hasPermissionForStatistics">
<a [routerLink]="['/open']">Open Startup</a>
</li>
<li *ngIf="hasPermissionForSubscription">
<a i18n [routerLink]="['/pricing']">Pricing</a>
<a i18n [routerLink]="routerLinkPricing">Pricing</a>
</li>
<li *ngIf="hasPermissionForSubscription">
<a i18n [routerLink]="['/about', 'privacy-policy']"
<a i18n [routerLink]="routerLinkAboutPrivacyPolicy"
>Privacy Policy</a
>
</li>

View File

@ -38,6 +38,20 @@ export class AppComponent implements OnDestroy, OnInit {
public hasPermissionToAccessFearAndGreedIndex: boolean;
public info: InfoItem;
public pageTitle: string;
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkAboutChangelog = ['/' + $localize`about`, 'changelog'];
public routerLinkAboutLicense = ['/' + $localize`about`, $localize`license`];
public routerLinkAboutPrivacyPolicy = [
'/' + $localize`about`,
$localize`privacy-policy`
];
public routerLinkFaq = ['/' + $localize`faq`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkMarkets = ['/' + $localize`markets`];
public routerLinkPricing = ['/' + $localize`pricing`];
public routerLinkRegister = ['/' + $localize`register`];
public routerLinkResources = ['/' + $localize`resources`];
public showFooter = false;
public user: User;
public version = environment.version;
@ -89,6 +103,19 @@ export class AppComponent implements OnDestroy, OnInit {
const urlSegments = urlSegmentGroup.segments;
this.currentRoute = urlSegments[0].path;
this.showFooter =
(this.currentRoute === 'blog' ||
this.currentRoute === this.routerLinkFaq[0].slice(1) ||
this.currentRoute === this.routerLinkFeatures[0].slice(1) ||
this.currentRoute === this.routerLinkMarkets[0].slice(1) ||
this.currentRoute === 'open' ||
this.currentRoute === 'p' ||
this.currentRoute === this.routerLinkPricing[0].slice(1) ||
this.currentRoute === this.routerLinkRegister[0].slice(1) ||
this.currentRoute === this.routerLinkResources[0].slice(1) ||
this.currentRoute === 'start') &&
this.deviceType !== 'mobile';
if (this.deviceType === 'mobile') {
setTimeout(() => {
const index = this.title.getTitle().indexOf('');

View File

@ -29,7 +29,7 @@
}"
[title]="
(itemByMonth.key + '-' + (i + 1 < 10 ? '0' + (i + 1) : i + 1)
| date : defaultDateFormat) ?? ''
| date: defaultDateFormat) ?? ''
"
(click)="
onOpenMarketDataDetail({

View File

@ -1,14 +1,14 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="text-center" i18n>Platforms</h3>
<h2 class="text-center" i18n>Platforms</h2>
<gf-admin-platform></gf-admin-platform>
</div>
</div>
<!--
<div class="row">
<div class="col">
<h3 class="text-center" i18n>Tags</h3>
<h2 class="text-center" i18n>Tags</h2>
</div>
</div>
-->

View File

@ -69,10 +69,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'resources',
'text-decoration-underline': currentRoute === 'resources'
'font-weight-bold': currentRoute === routeResources,
'text-decoration-underline': currentRoute === routeResources
}"
[routerLink]="['/resources']"
[routerLink]="routerLinkResources"
>Resources</a
>
</li>
@ -87,10 +87,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'pricing',
'text-decoration-underline': currentRoute === 'pricing'
'font-weight-bold': currentRoute === routePricing,
'text-decoration-underline': currentRoute === routePricing
}"
[routerLink]="['/pricing']"
[routerLink]="routerLinkPricing"
>Pricing</a
>
</li>
@ -100,10 +100,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'about',
'text-decoration-underline': currentRoute === 'about'
'font-weight-bold': currentRoute === routeAbout,
'text-decoration-underline': currentRoute === routeAbout
}"
[routerLink]="['/about']"
[routerLink]="routerLinkAbout"
>About</a
>
</li>
@ -210,9 +210,9 @@
i18n
mat-menu-item
[ngClass]="{
'font-weight-bold': currentRoute === 'resources'
'font-weight-bold': currentRoute === routeResources
}"
[routerLink]="['/resources']"
[routerLink]="routerLinkResources"
>Resources</a
>
<a
@ -223,16 +223,16 @@
class="d-flex d-sm-none"
i18n
mat-menu-item
[ngClass]="{ 'font-weight-bold': currentRoute === 'pricing' }"
[routerLink]="['/pricing']"
[ngClass]="{ 'font-weight-bold': currentRoute === routePricing }"
[routerLink]="routerLinkPricing"
>Pricing</a
>
<a
class="d-flex d-sm-none"
i18n
mat-menu-item
[ngClass]="{ 'font-weight-bold': currentRoute === 'about' }"
[routerLink]="['/about']"
[ngClass]="{ 'font-weight-bold': currentRoute === routeAbout }"
[routerLink]="routerLinkAbout"
>About Ghostfolio</a
>
<hr class="d-flex d-sm-none m-0" />
@ -260,10 +260,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'features',
'text-decoration-underline': currentRoute === 'features'
'font-weight-bold': currentRoute === routeFeatures,
'text-decoration-underline': currentRoute === routeFeatuers
}"
[routerLink]="['/features']"
[routerLink]="routerLinkFeatures"
>Features</a
>
</li>
@ -273,10 +273,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'about',
'text-decoration-underline': currentRoute === 'about'
'font-weight-bold': currentRoute === routeAbout,
'text-decoration-underline': currentRoute === routeAbout
}"
[routerLink]="['/about']"
[routerLink]="routerLinkAbout"
>About</a
>
</li>
@ -285,10 +285,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'pricing',
'text-decoration-underline': currentRoute === 'pricing'
'font-weight-bold': currentRoute === routePricing,
'text-decoration-underline': currentRoute === routePricing
}"
[routerLink]="['/pricing']"
[routerLink]="routerLinkPricing"
>Pricing</a
>
</li>
@ -301,10 +301,10 @@
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'markets',
'text-decoration-underline': currentRoute === 'markets'
'font-weight-bold': currentRoute === routeMarkets,
'text-decoration-underline': currentRoute === routeMarkets
}"
[routerLink]="['/markets']"
[routerLink]="routerLinkMarkets"
>Markets</a
>
</li>
@ -329,7 +329,7 @@
class="d-none d-sm-block"
color="primary"
mat-flat-button
[routerLink]="['/register']"
[routerLink]="routerLinkRegister"
><ng-container i18n>Get started</ng-container>
</a>
</li>

View File

@ -42,6 +42,17 @@ export class HeaderComponent implements OnChanges {
public hasPermissionToCreateUser: boolean;
public impersonationId: string;
public isMenuOpen: boolean;
public routeAbout = $localize`about`;
public routeFeatures = $localize`features`;
public routeMarkets = $localize`markets`;
public routePricing = $localize`pricing`;
public routeResources = $localize`resources`;
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkMarkets = ['/' + $localize`markets`];
public routerLinkPricing = ['/' + $localize`pricing`];
public routerLinkRegister = ['/' + $localize`register`];
public routerLinkResources = ['/' + $localize`resources`];
private unsubscribeSubject = new Subject<void>();

View File

@ -1,5 +1,5 @@
<div class="container">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Markets</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Markets</h1>
<div class="mb-5 row">
<div class="col-xs-12 col-md-8 offset-md-2">
<div class="mb-2 text-center text-muted">

View File

@ -121,7 +121,7 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
});
this.snackBarRef.onAction().subscribe(() => {
this.router.navigate(['/pricing']);
this.router.navigate(['/' + $localize`pricing`]);
});
}

View File

@ -1,5 +1,5 @@
<div class="container pb-3 px-3">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Summary</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Summary</h1>
<div class="row">
<div class="col-xs-12 col-md-8 offset-md-2">
<mat-card appearance="outlined">

View File

@ -11,6 +11,8 @@ import { SubscriptionInterstitialDialogParams } from './interfaces/interfaces';
templateUrl: 'subscription-interstitial-dialog.html'
})
export class SubscriptionInterstitialDialog {
public routerLinkPricing = ['/' + $localize`pricing`];
public constructor(
@Inject(MAT_DIALOG_DATA) public data: SubscriptionInterstitialDialogParams,
public dialogRef: MatDialogRef<SubscriptionInterstitialDialog>

View File

@ -56,7 +56,7 @@
<a
color="primary"
mat-flat-button
[routerLink]="['/pricing']"
[routerLink]="routerLinkPricing"
(click)="closeDialog()"
>
<span i18n>Upgrade Plan</span>

View File

@ -4,6 +4,7 @@ import {
Router,
RouterStateSnapshot
} from '@angular/router';
import { paths } from '@ghostfolio/client/app-routing.module';
import { DataService } from '@ghostfolio/client/services/data.service';
import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
@ -13,21 +14,17 @@ import { catchError } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class AuthGuard {
private static PUBLIC_PAGE_ROUTES = [
'/about',
'/about/changelog',
'/about/privacy-policy',
`/${paths.about}`,
'/blog',
'/de/blog',
'/demo',
'/en/blog',
'/faq',
'/features',
'/markets',
`/${paths.faq}`,
`/${paths.features}`,
`/${paths.markets}`,
'/open',
'/p',
'/pricing',
'/register',
'/resources'
`/${paths.pricing}`,
`/${paths.register}`,
`/${paths.resources}`
];
constructor(
@ -53,7 +50,7 @@ export class AuthGuard {
this.router.navigate(['/demo']);
resolve(false);
} else if (utmSource === 'trusted-web-activity') {
this.router.navigate(['/register']);
this.router.navigate(['/' + $localize`register`]);
resolve(false);
} else if (
AuthGuard.PUBLIC_PAGE_ROUTES.filter((publicPageRoute) =>

View File

@ -77,7 +77,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
});
this.snackBarRef.onAction().subscribe(() => {
this.router.navigate(['/pricing']);
this.router.navigate(['/' + $localize`pricing`]);
});
}
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {

View File

@ -1,5 +1,8 @@
import * as path from 'path';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { paths } from '@ghostfolio/client/app-routing.module';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { AboutPageComponent } from './about-page.component';
@ -22,22 +25,13 @@ const routes: Routes = [
(m) => m.ChangelogPageModule
)
},
...[
'license',
/////
'licenca',
'licence',
'licencia',
'licentie',
'lizenz',
'licenza'
].map((path) => ({
path,
{
path: paths.license,
loadChildren: () =>
import('./license/license-page.module').then(
(m) => m.LicensePageModule
)
})),
},
{
path: 'oss-friends',
loadChildren: () =>
@ -45,22 +39,13 @@ const routes: Routes = [
(m) => m.OpenSourceSoftwareFriendsPageModule
)
},
...[
'privacy-policy',
/////
'datenschutzbestimmungen',
'informativa-sulla-privacy',
'politique-de-confidentialite',
'politica-de-privacidad',
'politica-de-privacidade',
'privacybeleid'
].map((path) => ({
path,
{
path: paths.privacyPolicy,
loadChildren: () =>
import('./privacy-policy/privacy-policy-page.module').then(
(m) => m.PrivacyPolicyPageModule
)
}))
}
],
component: AboutPageComponent,
path: '',

View File

@ -48,17 +48,17 @@ export class AboutPageComponent implements OnDestroy, OnInit {
{
iconName: 'reader-outline',
label: $localize`About`,
path: ['/about']
path: ['/' + $localize`about`]
},
{
iconName: 'sparkles-outline',
label: $localize`Changelog`,
path: ['/about', 'changelog']
path: ['/' + $localize`about`, 'changelog']
},
{
iconName: 'ribbon-outline',
label: $localize`License`,
path: ['/about', 'license']
path: ['/' + $localize`about`, $localize`license`]
}
];
@ -66,7 +66,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs.push({
iconName: 'shield-checkmark-outline',
label: $localize`Privacy Policy`,
path: ['/about', 'privacy-policy'],
path: ['/' + $localize`about`, $localize`privacy-policy`],
showCondition: this.hasPermissionForSubscription
});
this.user = state.user;
@ -83,7 +83,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
this.tabs.push({
iconName: 'happy-outline',
label: 'OSS Friends',
path: ['/about', 'oss-friends']
path: ['/' + $localize`about`, 'oss-friends']
});
});
}

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Changelog</h1>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Changelog</h1>
<div class="changelog">
<markdown [src]="'../assets/CHANGELOG.md'"></markdown>
</div>

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>License</h1>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>License</h1>
<div>
<markdown [src]="'../assets/LICENSE'"></markdown>
</div>

View File

@ -1,6 +1,8 @@
import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
const ossFriends = require('../../../../assets/oss-friends.json');
@Component({
host: { class: 'page' },
selector: 'gf-oss-friends-page',
@ -8,131 +10,7 @@ import { Subject } from 'rxjs';
templateUrl: './oss-friends-page.html'
})
export class OpenSourceSoftwareFriendsPageComponent implements OnDestroy {
public ossFriends = [
{
description: 'Build build custom software on top of your data.',
name: 'Appsmith',
url: 'https://www.appsmith.com'
},
{
description:
'BoxyHQs suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster.',
name: 'BoxyHQ',
url: 'https://boxyhq.com'
},
{
description:
'Cal.com is a scheduling tool that helps you schedule meetings without the back-and-forth emails.',
name: 'Cal.com',
url: 'https://cal.com'
},
{
description:
'Centralize community, product, and customer data to understand which companies are engaging with your open source project.',
name: 'Crowd.dev',
url: 'https://www.crowd.dev'
},
{
description:
'The Open-Source DocuSign Alternative. We aim to earn your trust by enabling you to self-host the platform and examine its inner workings.',
name: 'Documenso',
url: 'https://documenso.com'
},
{
description:
'The Open-Source HubSpot Alternative. A single XOS enables to create unique and life-changing experiences that work for all types of business.',
name: 'Erxes',
url: 'https://erxes.io'
},
{
description:
'Survey granular user segments at any point in the user journey. Gather up to 6x more insights with targeted micro-surveys. All open-source.',
name: 'Formbricks',
url: 'https://formbricks.com'
},
{
description:
'GitWonk is an open-source technical documentation tool, designed and built focusing on the developer experience.',
name: 'GitWonk',
url: 'https://gitwonk.com'
},
{
description:
'Open-source authentication and user management for the passkey era. Integrated in minutes, for web and mobile apps.',
name: 'Hanko',
url: 'https://www.hanko.io'
},
{
description:
'HTMX is a dependency-free JavaScript library that allows you to access AJAX, CSS Transitions, WebSockets, and Server Sent Events directly in HTML.',
name: 'HTMX',
url: 'https://htmx.org'
},
{
description:
'Open source, end-to-end encrypted platform that lets you securely manage secrets and configs across your team, devices, and infrastructure.',
name: 'Infisical',
url: 'https://infisical.com'
},
{
description:
'Mockoon is the easiest and quickest way to design and run mock REST APIs.',
name: 'Mockoon',
url: 'https://mockoon.com'
},
{
description:
'The open-source notification infrastructure for developers. Simple components and APIs for managing all communication channels in one place.',
name: 'Novu',
url: 'https://novu.co'
},
{
description:
'Democratizing investment research through an open source financial ecosystem. The OpenBB Terminal allows everyone to perform investment research, from everywhere.',
name: 'OpenBB',
url: 'https://openbb.co'
},
{
description:
'Sniffnet is a network monitoring tool to help you easily keep track of your Internet traffic.',
name: 'Sniffnet',
url: 'https://www.sniffnet.net'
},
{
description: 'Software localization from A to Z made really easy.',
name: 'Tolgee',
url: 'https://tolgee.io'
},
{
description:
'Create long-running Jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays.',
name: 'Trigger.dev',
url: 'https://trigger.dev'
},
{
description:
'Typebot gives you powerful blocks to create unique chat experiences. Embed them anywhere on your apps and start collecting results like magic.',
name: 'Typebot',
url: 'https://typebot.io'
},
{
description:
'A modern CRM offering the flexibility of open-source, advanced features and sleek design.',
name: 'Twenty',
url: 'https://twenty.com'
},
{
description:
'Open-source enterprise-grade serverless CMS. Own your data. Scale effortlessly. Customize everything.',
name: 'Webiny',
url: 'https://www.webiny.com'
},
{
description: 'Webstudio is an open source alternative to Webflow',
name: 'Webstudio',
url: 'https://webstudio.is'
}
];
public ossFriends = ossFriends.data;
private unsubscribeSubject = new Subject<void>();

View File

@ -1,34 +1,40 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center">
<ng-container i18n>Our</ng-container> OSS Friends
</h3>
<h1 class="h3 mb-4 text-center">
<span class="d-none d-sm-block"
><ng-container i18n>Our</ng-container> OSS Friends</span
>
<small class="text-muted" i18n
>Discover other exciting Open Source Software projects</small
>
</h1>
<div class="row">
<div
*ngFor="let ossFriend of ossFriends"
class="col-xs-12 col-md-4 mb-3"
>
<mat-card appearance="outlined" class="d-flex flex-column h-100">
<mat-card-header>
<mat-card-title class="h4"
><a target="_blank" [href]="ossFriend.url"
>{{ ossFriend.name }}</a
></mat-card-title
>
</mat-card-header>
<mat-card-content class="flex-grow-1">
<p>{{ ossFriend.description }}</p>
</mat-card-content>
<mat-card-actions class="justify-content-end">
<a mat-button target="_blank" [href]="ossFriend.url">
<span
><ng-container i18n>Visit</ng-container> {{ ossFriend.name
}}</span
><ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
</a>
</mat-card-actions>
</mat-card>
<a target="_blank" [href]="ossFriend.href">
<mat-card appearance="outlined" class="d-flex flex-column h-100">
<mat-card-header>
<mat-card-title class="h4">{{ ossFriend.name }}</mat-card-title>
</mat-card-header>
<mat-card-content class="flex-grow-1">
<p>{{ ossFriend.description }}</p>
</mat-card-content>
<mat-card-actions class="justify-content-end">
<a mat-button target="_blank" [href]="ossFriend.href">
<span
><ng-container i18n>Visit</ng-container> {{ ossFriend.name
}}</span
><ion-icon
class="ml-1"
name="arrow-forward-outline"
></ion-icon>
</a>
</mat-card-actions>
</mat-card>
</a>
</div>
</div>
</div>

View File

@ -1,10 +1,10 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { OpenSourceSoftwareFriendsPageRoutingModule } from './oss-friends-page-routing.module';
import { OpenSourceSoftwareFriendsPageComponent } from './oss-friends-page.component';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [OpenSourceSoftwareFriendsPageComponent],

View File

@ -1,3 +1,9 @@
:host {
display: block;
.mat-mdc-card {
&:hover {
border-color: var(--gf-theme-primary-500);
}
}
}

View File

@ -18,6 +18,8 @@ export class AboutOverviewPageComponent implements OnDestroy, OnInit {
public hasPermissionForStatistics: boolean;
public hasPermissionForSubscription: boolean;
public isLoggedIn: boolean;
public routerLinkFaq = ['/' + $localize`faq`];
public routerLinkFeatures = ['/' + $localize`features`];
public user: User;
public version = environment.version;

View File

@ -1,7 +1,9 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center">About Ghostfolio</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center">
<ng-container i18n>About Ghostfolio</ng-container>
</h1>
<div class="about-container">
<p>
Ghostfolio is a lightweight wealth management application for
@ -46,7 +48,7 @@
<p>
If you encounter a bug or would like to suggest an improvement or a
new
<a [routerLink]="['/features']">feature</a>, please join the
<a [routerLink]="routerLinkFeatures">feature</a>, please join the
Ghostfolio
<a
href="https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg"
@ -139,7 +141,7 @@
class="py-4 w-100"
color="primary"
mat-flat-button
[routerLink]="['/faq']"
[routerLink]="routerLinkFaq"
>Frequently Asked Questions (FAQ)</a
>
</div>

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Privacy Policy</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Privacy Policy</h1>
<markdown [src]="'../assets/privacy-policy.md'"></markdown>
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Accounts</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Accounts</h1>
<div class="accounts">
<gf-accounts-table
[accounts]="accounts"

View File

@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './hallo-ghostfolio-page.html'
})
export class HalloGhostfolioPageComponent {}
export class HalloGhostfolioPageComponent {
public routerLinkPricing = ['/' + $localize`pricing`];
public routerLinkResources = ['/' + $localize`resources`];
}

View File

@ -19,7 +19,7 @@
Aufgrund der steigenden Inflation und den Negativzinsen befasse ich
mich seit einiger Zeit, wie ich mein Vermögen möglichst
diversifiziert anlegen kann. Konkret verfolge ich eine
<a [routerLink]="['/resources']">Buy and Hold Strategie</a> mit
<a [routerLink]="routerLinkResources">Buy and Hold Strategie</a> mit
Investitionen in verschiedene Anlageklassen verteilt auf
unterschiedliche Plattformen. Deshalb suchte ich nach einer App, die
mein Portfolio ganzheitlich zusammenfasst. Bei meiner
@ -119,7 +119,7 @@
Anlagestrategie? Ich freue mich über alle, die Ghostfolio
ausprobieren. Bist du überzeugt vom Potential der Software? Jede
Unterstützung für Ghostfolio ist willkommen. Sei es mit einer
<a [routerLink]="['/pricing']">Ghostfolio Premium</a>
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
Subscription zur Finanzierung des Hostings, einem positiven Rating
im
<a

View File

@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './hello-ghostfolio-page.html'
})
export class HelloGhostfolioPageComponent {}
export class HelloGhostfolioPageComponent {
public routerLinkPricing = ['/' + $localize`pricing`];
public routerLinkResources = ['/' + $localize`resources`];
}

View File

@ -19,7 +19,7 @@
Due to rising inflation and negative interest rates, I have been
looking for some time at how I can invest my assets in the most
diversified way possible. Specifically, I follow a
<a [routerLink]="['/resources']">buy and hold strategy</a> with
<a [routerLink]="routerLinkResources">buy and hold strategy</a> with
investments in different asset classes spread across different
platforms. Therefore, I was looking for an app that would
holistically aggregate my portfolio. During my research on the
@ -115,7 +115,7 @@
strategy? I'm happy for everyone who tries Ghostfolio. Are you
convinced of its potential? Any support for Ghostfolio is welcome.
Be it with a
<a [routerLink]="['/pricing']">Ghostfolio Premium</a>
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
Subscription to finance the hosting, a positive rating in the
<a
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"

View File

@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './first-months-in-open-source-page.html'
})
export class FirstMonthsInOpenSourcePageComponent {}
export class FirstMonthsInOpenSourcePageComponent {
public routerLinkPricing = ['/' + $localize`pricing`];
}

View File

@ -86,7 +86,7 @@
</p>
<p>
My personal goal is to reach break-even with the Saas offering (<a
[routerLink]="['/pricing']"
[routerLink]="routerLinkPricing"
>Ghostfolio Premium</a
>) and regularly report about the progress and my learnings on this
exciting journey.

View File

@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './how-do-i-get-my-finances-in-order-page.html'
})
export class HowDoIGetMyFinancesInOrderPageComponent {}
export class HowDoIGetMyFinancesInOrderPageComponent {
public routerLinkResources = ['/' + $localize`resources`];
}

View File

@ -9,9 +9,9 @@
<section class="mb-4">
<p>
Before you can think of
<a [routerLink]="['/resources']">long-term investing</a>, you have
to get your finances in order. Take a look at Peter's journey to see
how you can achieve it, too.
<a [routerLink]="routerLinkResources">long-term investing</a>, you
have to get your finances in order. Take a look at Peter's journey
to see how you can achieve it, too.
</p>
<p>
Peter enjoys life, but sometimes he overspends a bit. He realizes it

View File

@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './500-stars-on-github-page.html'
})
export class FiveHundredStarsOnGitHubPageComponent {}
export class FiveHundredStarsOnGitHubPageComponent {
public routerLinkMarkets = ['/' + $localize`markets`];
public routerLinkPricing = ['/' + $localize`pricing`];
}

View File

@ -71,10 +71,10 @@
<h2 class="h4">Break-even Point</h2>
<p>
Despite the complicated
<a [routerLink]="['/markets']">economic situation</a> at this time,
the goal set at the beginning of the year to build a sustainable
business and reach break-even with the SaaS offering (<a
[routerLink]="['/pricing']"
<a [routerLink]="routerLinkMarkets">economic situation</a> at this
time, the goal set at the beginning of the year to build a
sustainable business and reach break-even with the SaaS offering (<a
[routerLink]="routerLinkPricing"
>Ghostfolio Premium</a
>) has been achieved. We will continue to leverage the revenue to
further improve the fully managed cloud offering for our paying

View File

@ -11,5 +11,6 @@ import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
templateUrl: './black-friday-2022-page.html'
})
export class BlackFriday2022PageComponent {
public constructor() {}
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkPricing = ['/' + $localize`pricing`];
}

View File

@ -35,17 +35,18 @@
software presents the current assets (stocks, ETFs,
cryptocurrencies, commodities etc.) in real time to make solid,
data-driven investment decisions. Check out the numerous
<a [routerLink]="['/features']">features</a> to manage your wealth.
<a [routerLink]="routerLinkFeatures">features</a> to manage your
wealth.
</p>
</section>
<section class="mb-4">
<p>
Snap the limited Black Friday 2022 deal before its gone. For
detailed information on plans and pricing, please visit our
<a [routerLink]="['/pricing']">pricing page</a>.
<a [routerLink]="routerLinkPricing">pricing page</a>.
</p>
<p class="text-center">
<a color="primary" mat-flat-button [routerLink]="['/pricing']"
<a color="primary" mat-flat-button [routerLink]="routerLinkPricing"
>Get the Deal</a
>
</p>

View File

@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './1000-stars-on-github-page.html'
})
export class ThousandStarsOnGitHubPageComponent {}
export class ThousandStarsOnGitHubPageComponent {
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkPricing = ['/' + $localize`pricing`];
}

View File

@ -92,7 +92,7 @@
<p>
These self-hosting platforms allow users to run applications on
their own hardware rather than rely on a
<a [routerLink]="['/pricing']">SaaS offering</a>. As a result,
<a [routerLink]="routerLinkPricing">SaaS offering</a>. As a result,
Ghostfolio has become accessible to an even wider range of users who
would like to take control of their wealth management.
</p>
@ -108,9 +108,9 @@
As the project continues to evolve, we can expect to see even more
exciting developments and innovations around Ghostfolio which guides
users through the process of
<a [routerLink]="['/features']">tracking their assets</a>, such as
stocks, ETFs, or cryptocurrencies. Especially in the areas of data
import and portfolio analysis.
<a [routerLink]="routerLinkFeatures">tracking their assets</a>, such
as stocks, ETFs, or cryptocurrencies. Especially in the areas of
data import and portfolio analysis.
</p>
<p>
We are honored to be a part of this vibrant and growing community,

View File

@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './unlock-your-financial-potential-with-ghostfolio-page.html'
})
export class UnlockYourFinancialPotentialWithGhostfolioPageComponent {}
export class UnlockYourFinancialPotentialWithGhostfolioPageComponent {
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResources = ['/' + $localize`resources`];
}

View File

@ -44,13 +44,13 @@
<h2 class="h4">Empowering Buy & Hold Strategies</h2>
<p>
For those committed to a
<a [routerLink]="['/resources']">buy & hold strategy</a>, Ghostfolio
provides an intuitive interface to monitor long-term investments.
Users can track performance over time, gaining insights into
portfolio growth and stability. With strong visualizations and
reporting <a [routerLink]="['/features']">features</a>, Ghostfolio
equips users to make well-informed decisions aligned with their
long-term investment goals.
<a [routerLink]="routerLinkResources">buy & hold strategy</a>,
Ghostfolio provides an intuitive interface to monitor long-term
investments. Users can track performance over time, gaining insights
into portfolio growth and stability. With strong visualizations and
reporting <a [routerLink]="routerLinkFeatures">features</a>,
Ghostfolio equips users to make well-informed decisions aligned with
their long-term investment goals.
</p>
</section>
<section class="mb-4">
@ -91,7 +91,7 @@
<h2 class="h4">Driving Financial Independence (FIRE)</h2>
<p>
Achieving
<a [routerLink]="['/resources']">financial independence</a>
<a [routerLink]="routerLinkResources">financial independence</a>
including early retirement (<a
href="../en/blog/2023/07/exploring-the-path-to-fire"
>FIRE</a

View File

@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
standalone: true,
templateUrl: './exploring-the-path-to-fire-page.html'
})
export class ExploringThePathToFirePageComponent {}
export class ExploringThePathToFirePageComponent {
public routerLinkFeatures = ['/' + $localize`features`];
}

View File

@ -10,7 +10,7 @@
<div class="mb-3 text-muted"><small>2023-07-01</small></div>
<img
alt="Exploring the Path to Financial Independence and Retiring Early (FIRE) Teaser"
class="border rounded w-100"
class="rounded w-100"
src="../assets/images/blog/20230701.jpg"
title="Exploring the Path to Financial Independence and Retiring Early (FIRE)"
/>
@ -135,10 +135,10 @@
track your investments, and make informed decisions to accelerate
your progress towards financial independence. Ghostfolio also
provides a dedicated
<a [routerLink]="['/features']">FIRE calculator</a>, allowing you to
simulate your customized plan to achieve FIRE. You get the tools to
optimize your financial journey and confidently strive for a future
that is both personally fulfilling and financially secure.
<a [routerLink]="routerLinkFeatures">FIRE calculator</a>, allowing
you to simulate your customized plan to achieve FIRE. You get the
tools to optimize your financial journey and confidently strive for
a future that is both personally fulfilling and financially secure.
</p>
</section>
<section class="mb-4 py-3">

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
@Component({
host: { class: 'page' },
imports: [MatButtonModule, RouterModule],
selector: 'gf-ghostfolio-joins-oss-friends-page',
standalone: true,
templateUrl: './ghostfolio-joins-oss-friends-page.html'
})
export class GhostfolioJoinsOssFriendsPageComponent {
public routerLinkAboutOssFriends = ['/' + $localize`about`, 'oss-friends'];
}

View File

@ -0,0 +1,167 @@
<div class="blog container">
<div class="row">
<div class="col-md-8 offset-md-2">
<article>
<div class="mb-4 text-center">
<h1 class="mb-1">Ghostfolio joins OSS Friends</h1>
<div class="mb-3 text-muted"><small>2023-08-23</small></div>
<img
alt="Ghostfolio joins OSS Friends Teaser"
class="rounded w-100"
src="../assets/images/blog/ghostfolio-joins-oss-friends.png"
title="Ghostfolio joins OSS Friends"
/>
</div>
<section class="mb-4">
<p>
We are excited to announce that Ghostfolio is now part of the
<a [routerLink]="routerLinkAboutOssFriends">OSS Friends</a>. This
new initiative is all about helping open source projects grow and
become more popular.
</p>
</section>
<section class="mb-4">
<h2 class="h4">The Story of OSS Friends</h2>
<p>
OSS Friends started as a simple
<a
href="https://twitter.com/formbricks/status/1660735970281508878"
target="_blank"
>post</a
>
on X (formerly known as <i>Twitter</i>). The idea came from
<a href="https://formbricks.com" target="_blank">Formbricks</a>, an
open source experience management platform to create surveys in
minutes, and is all about giving open source projects a boost.
</p>
<p>
If you are excited about the OSS Friends movement and want to bring
your own open source project along, just take a moment to fill out
<a
href="https://app.formbricks.com/s/clhys1p9r001cpr0hu65rwh17"
target="_blank"
>this form</a
>. Lets work and learn together all the open source way.
</p>
</section>
<section class="mb-4">
<h2 class="h4">
Ghostfolio Next Generation Software for your Personal Finances
</h2>
<p>
Money management can be tricky, especially when you have various
investments like cryptocurrencies, ETFs and stocks in your
portfolio. But guess what? There are cooler ways than staring at
boring spreadsheets. Say hello to Ghostfolio, a privacy-first, open
source dashboard for your personal finances.
</p>
</section>
<section class="mb-4 py-3">
<h2 class="h4 mb-0 text-center">
Would you like to simplify asset tracking?
</h2>
<p class="lead mb-2 text-center">
Ghostfolio empowers you to make informed investment decisions.
</p>
<div class="text-center">
<a color="primary" href="https://ghostfol.io" mat-flat-button>
Get Started
</a>
</div>
</section>
<section class="mb-4">
<ul class="list-inline">
<li class="list-inline-item">
<span class="badge badge-light">Asset</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Collaboration</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Cryptocurrency</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Community</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Dashboard</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">ETF</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Fintech</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Ghostfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Initiative</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Innovation</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Investment</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Open Source</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">OSS</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">OSS Friends</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Personal Finance</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Platform</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Portfolio</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Privacy</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Software</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Stock</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Technology</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Tracking</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Wealth Management</span>
</li>
<li class="list-inline-item">
<span class="badge badge-light">Web3</span>
</li>
</ul>
</section>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a i18n [routerLink]="['/blog']">Blog</a>
</li>
<li
aria-current="page"
class="active breadcrumb-item text-truncate"
>
Ghostfolio joins OSS Friends
</li>
</ol>
</nav>
</article>
</div>
</div>
</div>

View File

@ -136,6 +136,15 @@ const routes: Routes = [
'./2023/07/exploring-the-path-to-fire/exploring-the-path-to-fire-page.component'
).then((c) => c.ExploringThePathToFirePageComponent),
title: 'Exploring the Path to FIRE'
},
{
canActivate: [AuthGuard],
path: '2023/08/ghostfolio-joins-oss-friends',
loadComponent: () =>
import(
'./2023/08/ghostfolio-joins-oss-friends/ghostfolio-joins-oss-friends-page.component'
).then((c) => c.GhostfolioJoinsOssFriendsPageComponent),
title: 'Ghostfolio joins OSS Friends'
}
];

View File

@ -1,7 +1,39 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Blog</h3>
<h1 class="h3 mb-4 text-center">
<span class="d-none d-sm-block" i18n>Blog</span>
<small class="text-muted" i18n
>Discover the latest Ghostfolio updates and insights on personal
finance</small
>
</h1>
<mat-card appearance="outlined" class="mb-3">
<mat-card-content>
<div class="container p-0">
<div class="flex-nowrap no-gutters row">
<a
class="d-flex overflow-hidden w-100"
href="../en/blog/2023/08/ghostfolio-joins-oss-friends"
>
<div class="flex-grow-1 overflow-hidden">
<div class="h6 m-0 text-truncate">
Ghostfolio joins OSS Friends
</div>
<div class="d-flex text-muted">2023-08-23</div>
</div>
<div class="align-items-center d-flex">
<ion-icon
class="chevron text-muted"
name="chevron-forward-outline"
size="small"
></ion-icon>
</div>
</a>
</div>
</div>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" class="mb-3">
<mat-card-content>
<div class="container p-0">

View File

@ -10,6 +10,10 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './faq-page.html'
})
export class FaqPageComponent implements OnDestroy {
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkMarkets = ['/' + $localize`markets`];
public routerLinkPricing = ['/' + $localize`pricing`];
public routerLinkRegister = ['/' + $localize`register`];
public user: User;
private unsubscribeSubject = new Subject<void>();

View File

@ -1,9 +1,9 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center">
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>
Frequently Asked Questions (FAQ)
</h3>
</h1>
<p>
Find quick answers to commonly asked questions about Ghostfolio in our
Frequently Asked Questions (FAQ) section. Discover what Ghostfolio is,
@ -39,7 +39,7 @@
>
<mat-card-content>
Please find a feature overview to manage your wealth
<a [routerLink]="['/features']">here</a>.
<a [routerLink]="routerLinkFeatures">here</a>.
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" class="mb-3">
@ -47,7 +47,7 @@
<mat-card-title>How do I start?</mat-card-title></mat-card-header
>
<mat-card-content>
You can sign up via the “<a [routerLink]="['/register']"
You can sign up via the “<a [routerLink]="routerLinkRegister"
>Get Started</a
>” button at the top of the page. You have multiple options to join
Ghostfolio: Create an account with a security token, using
@ -93,7 +93,7 @@
world. The
<a href="https://github.com/ghostfolio/ghostfolio">source code</a> is
fully available as open source software (OSS). Thanks to our generous
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> users and
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> users and
<a href="https://www.buymeacoffee.com/ghostfolio">sponsors</a> we have
the ability to run a free, limited plan for novice
investors.</mat-card-content
@ -105,8 +105,8 @@
>
<mat-card-content
>Yes, it is! Our
<a [routerLink]="['/pricing']">pricing page</a> details everything you
get for free.</mat-card-content
<a [routerLink]="routerLinkPricing">pricing page</a> details
everything you get for free.</mat-card-content
>
</mat-card>
<mat-card appearance="outlined" class="mb-3">
@ -127,7 +127,8 @@
></mat-card-header
>
<mat-card-content
>By offering <a [routerLink]="['/pricing']">Ghostfolio Premium</a>, a
>By offering
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>, a
subscription plan with a managed hosting service and enhanced
features, we fund our business while providing added value to our
users.</mat-card-content
@ -140,12 +141,12 @@
></mat-card-header
>
<mat-card-content
><a [routerLink]="['/pricing']">Ghostfolio Premium</a> is a fully
><a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> is a fully
managed Ghostfolio cloud offering for ambitious investors. The revenue
is used to cover the hosting infrastructure and to fund the ongoing
development. It is the Open Source code base with some extras like the
<a [routerLink]="['/markets']">markets overview</a> and a professional
data provider.</mat-card-content
<a [routerLink]="routerLinkMarkets">markets overview</a> and a
professional data provider.</mat-card-content
>
</mat-card>
<mat-card appearance="outlined" class="mb-3">
@ -156,9 +157,9 @@
>
<mat-card-content
>Yes, you can try
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> by signing up
for Ghostfolio and applying for a trial (see “My Ghostfolio”). It is
easy, free and there is no commitment. You can stop using it at any
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> by signing
up for Ghostfolio and applying for a trial (see “My Ghostfolio”). It
is easy, free and there is no commitment. You can stop using it at any
time.</mat-card-content
>
</mat-card>
@ -214,8 +215,9 @@
</mat-card-header>
<mat-card-content
>Any support for Ghostfolio is welcome. Be it with a
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> subscription to
finance the hosting infrastructure, a positive rating in the
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
subscription to finance the hosting infrastructure, a positive rating
in the
<a
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
>Google Play Store</a

View File

@ -14,6 +14,7 @@ import { Subject, takeUntil } from 'rxjs';
export class FeaturesPageComponent implements OnDestroy {
public hasPermissionForSubscription: boolean;
public info: InfoItem;
public routerLinkRegister = ['/' + $localize`register`];
public user: User;
private unsubscribeSubject = new Subject<void>();

View File

@ -1,13 +1,12 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Features</h3>
<div class="mb-4">
<p>
Check out the numerous features of <strong>Ghostfolio</strong> to
manage your wealth.
</p>
</div>
<h1 class="h3 mb-4 text-center">
<span class="d-none d-sm-block" i18n>Features</span>
<small class="text-muted" i18n>
Check out the numerous features of Ghostfolio to manage your wealth
</small>
</h1>
<div class="row">
<div class="col-xs-12 col-md-4 mb-3">
<mat-card appearance="outlined" class="d-flex flex-column h-100">
@ -212,7 +211,7 @@
</h4>
<p class="m-0">
Check the current market mood (<a
[routerLink]="['/resources']"
[routerLink]="routerLinkResources"
>Fear & Greed Index</a
>) within the app.
</p>
@ -295,7 +294,7 @@
</div>
<div *ngIf="!user" class="row">
<div class="col mt-3 text-center">
<a color="primary" i18n mat-flat-button [routerLink]="['/register']"
<a color="primary" i18n mat-flat-button [routerLink]="routerLinkRegister"
>Get Started</a
>
</div>

View File

@ -22,6 +22,8 @@ export class LandingPageComponent implements OnDestroy, OnInit {
public hasPermissionForStatistics: boolean;
public hasPermissionForSubscription: boolean;
public hasPermissionToCreateUser: boolean;
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkRegister = ['/' + $localize`register`];
public statistics: Statistics;
public testimonials = [
{

View File

@ -1,10 +1,10 @@
<div class="container">
<div class="row">
<div class="col text-center">
<h1 class="font-weight-bold intro mt-5">
<h1 class="font-weight-bold intro mt-5" i18n>
Manage your wealth like a boss
</h1>
<p class="lead mb-4">
<p class="lead mb-4" i18n>
Ghostfolio is a privacy-first, open source dashboard for your personal
finances. Break down your asset allocation, know your net worth and make
solid, data-driven investment decisions.
@ -31,13 +31,20 @@
<div class="button-container mb-5 row">
<div class="align-items-center col d-flex justify-content-center">
<ng-container *ngIf="hasPermissionToCreateUser">
<a color="primary" mat-flat-button [routerLink]="['/register']">
<a
color="primary"
i18n
mat-flat-button
[routerLink]="routerLinkRegister"
>
Get Started
</a>
</ng-container>
<ng-container *ngIf="hasPermissionForDemo">
<div *ngIf="hasPermissionToCreateUser" class="mx-3 text-muted">or</div>
<a mat-stroked-button [routerLink]="['/demo']"> Live Demo </a>
<div *ngIf="hasPermissionToCreateUser" class="mx-3 text-muted" i18n>
or
</div>
<a i18n mat-stroked-button [routerLink]="['/demo']">Live Demo</a>
</ng-container>
</div>
</div>
@ -53,6 +60,7 @@
[routerLink]="['/open']"
>
<gf-value
i18n
icon="people-outline"
size="large"
[value]="statistics?.activeUsers30d ?? '-'"
@ -70,6 +78,7 @@
[routerLink]="['/open']"
>
<gf-value
i18n
icon="star-outline"
size="large"
[value]="statistics?.gitHubStargazers ?? '-'"
@ -87,6 +96,7 @@
[routerLink]="['/open']"
>
<gf-value
i18n
icon="cloud-download-outline"
size="large"
[value]="statistics?.dockerHubPulls ?? '-'"
@ -97,7 +107,9 @@
</div>
<div class="row mb-5">
<div class="col-12 text-center text-muted"><small>As seen in</small></div>
<div class="col-12 text-center text-muted">
<small i18n>As seen in</small>
</div>
<div class="col-md-3 d-flex justify-content-center my-1">
<a
class="d-block logo logo-alternative-to mask"
@ -190,11 +202,11 @@
<div class="pt-3 row">
<div class="col text-center">
<h2 class="h4 mb-1 text-center">
<h2 class="h4 mb-1 text-center" i18n>
Protect your <strong>assets</strong>. Refine your
<strong>personal investment strategy</strong>.
</h2>
<p class="lead m-0">
<p class="lead m-0" i18n>
Ghostfolio empowers busy people to keep track of stocks, ETFs or
cryptocurrencies without being tracked.
</p>
@ -205,19 +217,20 @@
<div class="col-md-4 my-2">
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>360° View</mat-card-title>
<mat-card-title i18n>360° View</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-card-content i18n>
Get the full picture of your personal finances across multiple
platforms.
</mat-card-content>
</mat-card>
</div>
<div class="col-md-4 my-2">
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Web3 Ready</mat-card-title>
<mat-card-title i18n>Web3 Ready</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-card-content i18n>
Use Ghostfolio anonymously and own your financial data.
</mat-card-content>
</mat-card>
@ -225,9 +238,9 @@
<div class="col-md-4 my-2">
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Open Source</mat-card-title>
<mat-card-title i18n>Open Source</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-card-content i18n>
Benefit from continuous improvements through a strong community.
</mat-card-content>
</mat-card>
@ -236,51 +249,53 @@
<div class="row my-5">
<div class="col-md-6 offset-md-3">
<h2 class="h4 mb-1 text-center">Why <strong>Ghostfolio</strong>?</h2>
<p class="lead mb-3 text-center">Ghostfolio is for you if you are...</p>
<h2 class="h4 mb-1 text-center" i18n>Why <strong>Ghostfolio</strong>?</h2>
<p class="lead mb-3 text-center" i18n>
Ghostfolio is for you if you are...
</p>
<ul class="list-unstyled">
<li class="d-flex mb-3">
<span class="mr-3">💼 </span
><span
><span i18n
>trading stocks, ETFs or cryptocurrencies on multiple
platforms</span
>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🏦</span
><span>pursuing a buy & hold strategy</span>
><span i18n>pursuing a buy & hold strategy</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🎯</span
><span
><span i18n
>interested in getting insights of your portfolio composition</span
>
</li>
<li class="d-flex mb-3">
<span class="mr-3">👻</span
><span>valuing privacy and data ownership</span>
><span i18n>valuing privacy and data ownership</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🧘</span><span>into minimalism</span>
<span class="mr-3">🧘</span><span i18n>into minimalism</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🧺</span
><span>caring about diversifying your financial resources</span>
><span i18n>caring about diversifying your financial resources</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🆓</span
><span>interested in financial independence</span>
><span i18n>interested in financial independence</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">🙅</span
><span>saying no to spreadsheets in {{ currentYear }}</span>
><span i18n>saying no to spreadsheets in {{ currentYear }}</span>
</li>
<li class="d-flex mb-3">
<span class="mr-3">😎</span><span>still reading this list</span>
<span class="mr-3">😎</span><span i18n>still reading this list</span>
</li>
</ul>
<div class="mt-4 text-center">
<a mat-stroked-button [routerLink]="['/about']"
<a i18n mat-stroked-button [routerLink]="routerLinkAbout"
>Learn more about Ghostfolio</a
>
</div>
@ -289,7 +304,7 @@
<div class="row my-5">
<div class="col-12">
<h2 class="h4 mb-1 text-center">
<h2 class="h4 mb-1 text-center" i18n>
What our <strong>users</strong> are saying
</h2>
</div>
@ -319,7 +334,7 @@
<div *ngIf="hasPermissionForSubscription" class="row my-5">
<div class="col-12">
<h2 class="h4 text-center">
<h2 class="h4 text-center" i18n>
Members from around the globe are using
<a href="pricing"><strong>Ghostfolio Premium</strong></a>
</h2>
@ -334,17 +349,17 @@
<div *ngIf="hasPermissionForSubscription" class="row my-3">
<div class="col-12">
<h2 class="h4 mb-1 text-center">
<h2 class="h4 mb-1 text-center" i18n>
How does <strong>Ghostfolio</strong> work?
</h2>
<p class="lead mb-3 text-center">Get started in only 3 steps</p>
<p class="lead mb-3 text-center" i18n>Get started in only 3 steps</p>
</div>
<div class="col-md-4 my-2">
<mat-card appearance="outlined" class="h-100">
<mat-card-content class="d-flex flex-row">
<div class="flex-grow-1">
<div class="font-weight-bold">Sign up anonymously*</div>
<div class="text-muted">
<div class="font-weight-bold" i18n>Sign up anonymously*</div>
<div class="text-muted" i18n>
<small>* no e-mail address nor credit card required</small>
</div>
</div>
@ -356,7 +371,7 @@
<mat-card appearance="outlined" class="h-100">
<mat-card-content class="d-flex flex-row">
<div class="flex-grow-1">
<div class="font-weight-bold">
<div class="font-weight-bold" i18n>
Add any of your historical transactions
</div>
</div>
@ -368,7 +383,7 @@
<mat-card appearance="outlined" class="h-100">
<mat-card-content class="d-flex flex-row">
<div class="flex-grow-1">
<div class="font-weight-bold">
<div class="font-weight-bold" i18n>
Get valuable insights of your portfolio composition
</div>
</div>
@ -380,19 +395,24 @@
<div *ngIf="hasPermissionToCreateUser" class="row my-5">
<div class="col">
<h2 class="h4 mb-1 text-center">Are <strong>you</strong> ready?</h2>
<p class="lead mb-3 text-center">
<h2 class="h4 mb-1 text-center" i18n>Are <strong>you</strong> ready?</h2>
<p class="lead mb-3 text-center" i18n>
Join now<ng-container *ngIf="hasPermissionForDemo">
or check out the example account</ng-container
>
</p>
<div class="align-items-center d-flex justify-content-center py-2">
<a color="primary" mat-flat-button [routerLink]="['/register']">
<a
color="primary"
i18n
mat-flat-button
[routerLink]="routerLinkRegister"
>
Get Started
</a>
<ng-container *ngIf="hasPermissionForDemo">
<div class="mx-3 text-muted">or</div>
<a mat-stroked-button [routerLink]="['/demo']"> Live Demo </a>
<div class="mx-3 text-muted" i18n>or</div>
<a i18n mat-stroked-button [routerLink]="['/demo']">Live Demo</a>
</ng-container>
</div>
</div>

View File

@ -1,8 +1,8 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center">Open Startup</h3>
<div class="intro-container">
<h1 class="d-none d-sm-block h3 mb-4 text-center">Open Startup</h1>
<div class="intro-container mb-4">
<p i18n>
At Ghostfolio, transparency is at the core of our values. We publish
the source code as

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row mb-3">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Activities</h3>
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Activities</h1>
<gf-activities-table
[activities]="activities"
[baseCurrency]="user?.settings?.baseCurrency"

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Allocations</h3>
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Allocations</h1>
<gf-activities-filter
[allFilters]="allFilters"
[isLoading]="isLoading"
@ -260,30 +260,6 @@
</div>
</div>
<div class="row">
<div class="col-md-4">
<mat-card appearance="outlined" class="mb-3">
<mat-card-header class="overflow-hidden w-100">
<mat-card-title class="align-items-center d-flex text-truncate"
><span i18n>By Country</span
><gf-premium-indicator
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1"
></gf-premium-indicator
></mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
[baseCurrency]="user?.settings?.baseCurrency"
[colorScheme]="user?.settings?.colorScheme"
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[keys]="['name']"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="countries"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
<div class="col-md-4">
<mat-card appearance="outlined" class="mb-3">
<mat-card-header class="overflow-hidden w-100">
@ -326,5 +302,29 @@
</mat-card-content>
</mat-card>
</div>
<div class="col-md-4">
<mat-card appearance="outlined" class="mb-3">
<mat-card-header class="overflow-hidden w-100">
<mat-card-title class="align-items-center d-flex text-truncate"
><span i18n>By Country</span
><gf-premium-indicator
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1"
></gf-premium-indicator
></mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-proportion-chart
[baseCurrency]="user?.settings?.baseCurrency"
[colorScheme]="user?.settings?.colorScheme"
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[keys]="['name']"
[locale]="user?.settings?.locale"
[maxItems]="10"
[positions]="countries"
></gf-portfolio-proportion-chart>
</mat-card-content>
</mat-card>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<div class="container">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Analysis</h3>
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Analysis</h1>
<div *ngIf="user?.settings?.viewMode !== 'ZEN'" class="my-4 text-center">
<gf-toggle
[defaultValue]="user?.settings?.dateRange"

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row mb-5">
<div class="col-lg">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>FIRE</h3>
<h2 class="d-none d-sm-block h3 mb-3 text-center" i18n>FIRE</h2>
<div>
<h4 class="align-items-center d-flex mb-3">
<span i18n>Calculator</span
@ -94,7 +94,7 @@
<div class="container mt-5">
<div class="row">
<div class="col">
<h3 class="mb-3 text-center">X-ray</h3>
<h2 class="h3 mb-3 text-center">X-ray</h2>
<p class="mb-4">
Ghostfolio X-ray uses static analysis to identify potential issues and
risks in your portfolio.

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Holdings</h3>
<h1 class="d-none d-sm-block h3 mb-3 text-center" i18n>Holdings</h1>
<gf-activities-filter
[allFilters]="allFilters"
[isLoading]="isLoading"

View File

@ -29,6 +29,8 @@ export class PricingPageComponent implements OnDestroy, OnInit {
public isLoggedIn: boolean;
public price: number;
public priceId: string;
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkRegister = ['/' + $localize`register`];
public user: User;
private unsubscribeSubject = new Subject<void>();

View File

@ -1,7 +1,7 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>Pricing Plans</h3>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Pricing Plans</h1>
<div class="mb-4">
<p i18n>
Our official Ghostfolio Premium cloud offering is the easiest way to
@ -21,7 +21,7 @@
<a href="mailto:hi@ghostfol.io?Subject=Student Discount">here</a> with
your university e-mail address.
</p>
<p>
<p i18n>
If you prefer to run Ghostfolio on your own infrastructure, please
find the source code and further instructions on
<a href="https://github.com/ghostfolio/ghostfolio">GitHub</a>.
@ -106,7 +106,7 @@
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<a i18n [routerLink]="['/features']"
<a i18n [routerLink]="routerLinkFeatures"
>and more Features...</a
>
</li>
@ -302,7 +302,7 @@
class="mr-1"
name="checkmark-circle-outline"
></ion-icon>
<a i18n [routerLink]="['/features']"
<a i18n [routerLink]="routerLinkFeatures"
>and more Features...</a
>
</li>
@ -360,7 +360,7 @@
</div>
<div *ngIf="!user" class="row">
<div class="col mt-3 text-center">
<a color="primary" i18n mat-flat-button [routerLink]="['/register']">
<a color="primary" i18n mat-flat-button [routerLink]="routerLinkRegister">
Get Started
</a>
<p class="m-0 text-muted"><small i18n>Its free.</small></p>

View File

@ -1,10 +1,10 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="h4 mb-3 text-center" i18n>
<h1 class="h4 mb-3 text-center" i18n>
Hello, {{ portfolioPublicDetails?.alias ?? 'someone' }} has shared a
<strong>Portfolio</strong> with you!
</h3>
</h1>
</div>
</div>
<div class="proportion-charts row">

View File

@ -22,7 +22,7 @@ const routes: Routes = [
path: `open-source-alternative-to-${key}`,
loadComponent: () =>
import(`./products/${key}-page.component`).then(() => component),
title: `Open Source Alternative to ${name}`
title: $localize`Open Source Alternative to ${name}`
};
})
];

View File

@ -10,9 +10,11 @@ import { products } from './products';
templateUrl: './personal-finance-tools-page.html'
})
export class PersonalFinanceToolsPageComponent implements OnDestroy {
public pathResources = '/' + $localize`resources`;
public products = products.filter(({ key }) => {
return key !== 'ghostfolio';
});
public routerLinkAbout = ['/' + $localize`about`];
private unsubscribeSubject = new Subject<void>();

View File

@ -1,19 +1,19 @@
<div class="container">
<div class="mb-5 row">
<div class="col">
<h3 class="d-none d-sm-block mb-3 text-center" i18n>
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>
Discover Open Source Alternatives for Personal Finance Tools
</h3>
</h1>
<div class="introduction mb-4">
<p>
<p i18n>
This overview page features a curated collection of personal finance
tools compared to the open source alternative
<a [routerLink]="['/about']">Ghostfolio</a>. If you value
<a [routerLink]="routerLinkAbout">Ghostfolio</a>. If you value
transparency, data privacy, and community collaboration, Ghostfolio
provides an excellent opportunity to take control of your financial
management.
</p>
<p>
<p i18n>
Explore the links below to compare a variety of personal finance tools
with Ghostfolio.
</p>
@ -29,10 +29,10 @@
<a
class="d-flex overflow-hidden w-100"
title="Compare Ghostfolio to {{ product.name }}"
[routerLink]="['/resources', 'personal-finance-tools', 'open-source-alternative-to-' + product.key]"
[routerLink]="[pathResources, 'personal-finance-tools', 'open-source-alternative-to-' + product.key]"
>
<div class="flex-grow-1 overflow-hidden">
<div class="h6 m-0 text-truncate">
<div class="h6 m-0 text-truncate" i18n>
Open Source Alternative to {{ product.name }}
</div>
</div>

View File

@ -4,19 +4,22 @@
<article>
<div class="mb-4 text-center">
<h1 class="mb-1">
<strong>Ghostfolio</strong>: The Open Source Alternative to
<strong>{{ product2.name }}</strong>
<strong>Ghostfolio</strong>:
<ng-container i18n>The Open Source Alternative to</ng-container
>&nbsp;<strong>{{ product2.name }}</strong>
</h1>
</div>
<section class="mb-4">
<p>
Are you looking for an open source alternative to {{ product2.name
}}? <a [routerLink]="['/about']">Ghostfolio</a> is a powerful
}}? <a [routerLink]="routerLinkAbout">Ghostfolio</a> is a powerful
portfolio management tool that provides individuals with a
comprehensive platform to track, analyze, and optimize their
investments. Whether you are an experienced investor or just
starting out, Ghostfolio offers an intuitive user interface and a
<a [routerLink]="['/features']">wide range of functionalities</a>
<a [routerLink]="routerLinkFeatures"
>wide range of functionalities</a
>
to help you make informed decisions and take control of your
financial future.
</p>
@ -280,7 +283,7 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a i18n [routerLink]="['/resources', 'personal-finance-tools']"
<a i18n [routerLink]="routerLinkResourcesPersonalFinanceTools"
>Personal Finance Tools</a
>
</li>

View File

@ -21,4 +21,11 @@ export class AltooPageComponent {
public product2 = products.find(({ key }) => {
return key === 'altoo';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class CopilotMoneyPageComponent {
public product2 = products.find(({ key }) => {
return key === 'copilot-money';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class DeltaPageComponent {
public product2 = products.find(({ key }) => {
return key === 'delta';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class DivvyDiaryPageComponent {
public product2 = products.find(({ key }) => {
return key === 'divvydiary';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class ExirioPageComponent {
public product2 = products.find(({ key }) => {
return key === 'exirio';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class FolisharePageComponent {
public product2 = products.find(({ key }) => {
return key === 'folishare';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class GetquinPageComponent {
public product2 = products.find(({ key }) => {
return key === 'getquin';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class GoSpatzPageComponent {
public product2 = products.find(({ key }) => {
return key === 'gospatz';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class JustEtfPageComponent {
public product2 = products.find(({ key }) => {
return key === 'justetf';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class KuberaPageComponent {
public product2 = products.find(({ key }) => {
return key === 'kubera';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class MarketsShPageComponent {
public product2 = products.find(({ key }) => {
return key === 'markets.sh';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class MaybeFinancePageComponent {
public product2 = products.find(({ key }) => {
return key === 'maybe-finance';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

View File

@ -21,4 +21,11 @@ export class MonsePageComponent {
public product2 = products.find(({ key }) => {
return key === 'monse';
});
public routerLinkAbout = ['/' + $localize`about`];
public routerLinkFeatures = ['/' + $localize`features`];
public routerLinkResourcesPersonalFinanceTools = [
'/' + $localize`resources`,
'personal-finance-tools'
];
}

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