Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
20195b2b1a | |||
7fa4e6ebd2 | |||
d8531ddfcb | |||
70d670b711 | |||
27b0663a80 | |||
874dfb0235 | |||
072db0d558 | |||
12e692429a | |||
e22b8b78b8 | |||
dc5052f7dc | |||
335553e891 | |||
d480ad1023 | |||
7320751056 | |||
108c0c13c4 | |||
053a5cc5b5 | |||
c456a8bcfe | |||
6fcecb5bc6 | |||
e4e0a7d9f0 | |||
c7173761a3 | |||
185e130d9f | |||
81245635af | |||
55182ac1af |
28
CHANGELOG.md
28
CHANGELOG.md
@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 1.180.0 - 18.08.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Set up `ng-extract-i18n-merge` to improve the i18n extraction and merge workflow
|
||||||
|
- Set up language localization for German (`de`)
|
||||||
|
- Resolved the feature graphic of the blog post
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Tagged template literal strings in components for localization with `$localize`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed the license component in the about page
|
||||||
|
- Fixed the links to the blog posts
|
||||||
|
|
||||||
|
## 1.179.5 - 15.08.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Set up i18n support
|
||||||
|
- Added a blog post: _500 Stars on GitHub_
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Reduced the maximum width of the performance chart on the home page
|
||||||
|
|
||||||
## 1.178.0 - 09.08.2022
|
## 1.178.0 - 09.08.2022
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
43
angular.json
43
angular.json
@ -77,41 +77,45 @@
|
|||||||
"polyfills": "apps/client/src/polyfills.ts",
|
"polyfills": "apps/client/src/polyfills.ts",
|
||||||
"tsConfig": "apps/client/tsconfig.app.json",
|
"tsConfig": "apps/client/tsconfig.app.json",
|
||||||
"assets": [
|
"assets": [
|
||||||
"apps/client/src/assets",
|
|
||||||
{
|
{
|
||||||
"glob": "assetlinks.json",
|
"glob": "assetlinks.json",
|
||||||
"input": "apps/client/src/assets",
|
"input": "apps/client/src/assets",
|
||||||
"output": "./.well-known"
|
"output": "./../.well-known"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "CHANGELOG.md",
|
"glob": "CHANGELOG.md",
|
||||||
"input": "",
|
"input": "",
|
||||||
"output": "./assets"
|
"output": "./../assets"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "LICENSE",
|
"glob": "LICENSE",
|
||||||
"input": "",
|
"input": "",
|
||||||
"output": "./assets"
|
"output": "./../assets"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "robots.txt",
|
"glob": "robots.txt",
|
||||||
"input": "apps/client/src/assets",
|
"input": "apps/client/src/assets",
|
||||||
"output": "./"
|
"output": "./../"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "sitemap.xml",
|
"glob": "sitemap.xml",
|
||||||
"input": "apps/client/src/assets",
|
"input": "apps/client/src/assets",
|
||||||
"output": "./"
|
"output": "./../"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "**/*",
|
"glob": "**/*",
|
||||||
"input": "node_modules/ionicons/dist/ionicons",
|
"input": "node_modules/ionicons/dist/ionicons",
|
||||||
"output": "./ionicons"
|
"output": "./../ionicons"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "**/*.js",
|
"glob": "**/*.js",
|
||||||
"input": "node_modules/ionicons/dist/",
|
"input": "node_modules/ionicons/dist/",
|
||||||
"output": "./"
|
"output": "./../"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "apps/client/src/assets",
|
||||||
|
"output": "./../assets/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": ["apps/client/src/styles.scss"],
|
"styles": ["apps/client/src/styles.scss"],
|
||||||
@ -124,6 +128,10 @@
|
|||||||
"namedChunks": true
|
"namedChunks": true
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development-en": {
|
||||||
|
"baseHref": "/en/",
|
||||||
|
"localize": ["en"]
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
{
|
{
|
||||||
@ -162,15 +170,21 @@
|
|||||||
"proxyConfig": "apps/client/proxy.conf.json"
|
"proxyConfig": "apps/client/proxy.conf.json"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development-en": {
|
||||||
|
"browserTarget": "client:build:development-en"
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "client:build:production"
|
"browserTarget": "client:build:production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "ng-extract-i18n-merge:ng-extract-i18n-merge",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "client:build"
|
"browserTarget": "client:build",
|
||||||
|
"includeContext": true,
|
||||||
|
"outputPath": "src/locales",
|
||||||
|
"targetFiles": ["messages.de.xlf"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
@ -188,6 +202,15 @@
|
|||||||
"outputs": ["coverage/apps/client"]
|
"outputs": ["coverage/apps/client"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"i18n": {
|
||||||
|
"locales": {
|
||||||
|
"de": {
|
||||||
|
"baseHref": "/de/",
|
||||||
|
"translation": "apps/client/src/locales/messages.de.xlf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sourceLocale": "en"
|
||||||
|
},
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
"client-e2e": {
|
"client-e2e": {
|
||||||
|
@ -10,7 +10,7 @@ import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-d
|
|||||||
import { PrismaModule } from '@ghostfolio/api/services/prisma.module';
|
import { PrismaModule } from '@ghostfolio/api/services/prisma.module';
|
||||||
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
|
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bull';
|
||||||
import { Module } from '@nestjs/common';
|
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { ScheduleModule } from '@nestjs/schedule';
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
@ -23,6 +23,7 @@ import { AuthModule } from './auth/auth.module';
|
|||||||
import { BenchmarkModule } from './benchmark/benchmark.module';
|
import { BenchmarkModule } from './benchmark/benchmark.module';
|
||||||
import { CacheModule } from './cache/cache.module';
|
import { CacheModule } from './cache/cache.module';
|
||||||
import { ExportModule } from './export/export.module';
|
import { ExportModule } from './export/export.module';
|
||||||
|
import { FrontendMiddleware } from './frontend.middleware';
|
||||||
import { ImportModule } from './import/import.module';
|
import { ImportModule } from './import/import.module';
|
||||||
import { InfoModule } from './info/info.module';
|
import { InfoModule } from './info/info.module';
|
||||||
import { OrderModule } from './order/order.module';
|
import { OrderModule } from './order/order.module';
|
||||||
@ -82,4 +83,10 @@ import { UserModule } from './user/user.module';
|
|||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [CronService]
|
providers: [CronService]
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {
|
||||||
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
consumer
|
||||||
|
.apply(FrontendMiddleware)
|
||||||
|
.forRoutes({ path: '*', method: RequestMethod.ALL });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service';
|
import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
||||||
|
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||||
import { OAuthResponse } from '@ghostfolio/common/interfaces';
|
import { OAuthResponse } from '@ghostfolio/common/interfaces';
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
@ -62,9 +63,17 @@ export class AuthController {
|
|||||||
const jwt: string = req.user.jwt;
|
const jwt: string = req.user.jwt;
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
res.redirect(`${this.configurationService.get('ROOT_URL')}/auth/${jwt}`);
|
res.redirect(
|
||||||
|
`${this.configurationService.get(
|
||||||
|
'ROOT_URL'
|
||||||
|
)}/${DEFAULT_LANGUAGE_CODE}/auth/${jwt}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
res.redirect(`${this.configurationService.get('ROOT_URL')}/auth`);
|
res.redirect(
|
||||||
|
`${this.configurationService.get(
|
||||||
|
'ROOT_URL'
|
||||||
|
)}/${DEFAULT_LANGUAGE_CODE}/auth`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
apps/api/src/app/frontend.middleware.ts
Normal file
53
apps/api/src/app/frontend.middleware.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||||
|
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
|
||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FrontendMiddleware implements NestMiddleware {
|
||||||
|
use(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (req.url.includes('cover.png')) {
|
||||||
|
Logger.log(`Referer: ${req.headers.referer}`, 'FrontendMiddleware');
|
||||||
|
|
||||||
|
// Resolve feature graphic for blog post
|
||||||
|
if (req.headers.referer?.includes('500-stars-on-github')) {
|
||||||
|
res.sendFile(
|
||||||
|
path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'client',
|
||||||
|
'assets',
|
||||||
|
'images',
|
||||||
|
'blog',
|
||||||
|
'500-stars-on-github.jpg'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Skip
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} else if (req.path.startsWith('/api/') || this.isFileRequest(req.url)) {
|
||||||
|
// Skip
|
||||||
|
next();
|
||||||
|
} else if (req.path.startsWith('/de/')) {
|
||||||
|
res.sendFile(this.getPathOfIndexHtmlFile('de'));
|
||||||
|
} else {
|
||||||
|
res.sendFile(this.getPathOfIndexHtmlFile(DEFAULT_LANGUAGE_CODE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPathOfIndexHtmlFile(aLocale: string) {
|
||||||
|
return path.join(__dirname, '..', 'client', aLocale, 'index.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
private isFileRequest(filename: string) {
|
||||||
|
if (filename === '/assets/LICENSE') {
|
||||||
|
return true;
|
||||||
|
} else if (filename.includes('auth/ey')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename.split('.').pop() !== filename;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
||||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||||
import { PROPERTY_COUPONS } from '@ghostfolio/common/config';
|
import {
|
||||||
|
DEFAULT_LANGUAGE_CODE,
|
||||||
|
PROPERTY_COUPONS
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import { Coupon } from '@ghostfolio/common/interfaces';
|
import { Coupon } from '@ghostfolio/common/interfaces';
|
||||||
import type { RequestWithUser } from '@ghostfolio/common/types';
|
import type { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
import {
|
import {
|
||||||
@ -93,7 +96,11 @@ export class SubscriptionController {
|
|||||||
'SubscriptionController'
|
'SubscriptionController'
|
||||||
);
|
);
|
||||||
|
|
||||||
res.redirect(`${this.configurationService.get('ROOT_URL')}/account`);
|
res.redirect(
|
||||||
|
`${this.configurationService.get(
|
||||||
|
'ROOT_URL'
|
||||||
|
)}/${DEFAULT_LANGUAGE_CODE}/account`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('stripe/checkout-session')
|
@Post('stripe/checkout-session')
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
||||||
|
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||||
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
|
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Subscription } from '@prisma/client';
|
import { Subscription } from '@prisma/client';
|
||||||
@ -33,7 +34,9 @@ export class SubscriptionService {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}) {
|
}) {
|
||||||
const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = {
|
const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = {
|
||||||
cancel_url: `${this.configurationService.get('ROOT_URL')}/account`,
|
cancel_url: `${this.configurationService.get(
|
||||||
|
'ROOT_URL'
|
||||||
|
)}/${DEFAULT_LANGUAGE_CODE}/account`,
|
||||||
client_reference_id: userId,
|
client_reference_id: userId,
|
||||||
line_items: [
|
line_items: [
|
||||||
{
|
{
|
||||||
|
@ -54,45 +54,52 @@ const routes: Routes = [
|
|||||||
import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
|
import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'de/blog/2021/07/hallo-ghostfolio',
|
path: 'blog/2021/07/hallo-ghostfolio',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'./pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.module'
|
'./pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.module'
|
||||||
).then((m) => m.HalloGhostfolioPageModule)
|
).then((m) => m.HalloGhostfolioPageModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'demo',
|
path: 'blog/2021/07/hello-ghostfolio',
|
||||||
loadChildren: () =>
|
|
||||||
import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'en/blog/2021/07/hello-ghostfolio',
|
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'./pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.module'
|
'./pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.module'
|
||||||
).then((m) => m.HelloGhostfolioPageModule)
|
).then((m) => m.HelloGhostfolioPageModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'en/blog/2022/01/ghostfolio-first-months-in-open-source',
|
path: 'blog/2022/01/ghostfolio-first-months-in-open-source',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'./pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.module'
|
'./pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.module'
|
||||||
).then((m) => m.FirstMonthsInOpenSourcePageModule)
|
).then((m) => m.FirstMonthsInOpenSourcePageModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'en/blog/2022/07/ghostfolio-meets-internet-identity',
|
path: 'blog/2022/07/ghostfolio-meets-internet-identity',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'./pages/blog/2022/07/ghostfolio-meets-internet-identity/ghostfolio-meets-internet-identity-page.module'
|
'./pages/blog/2022/07/ghostfolio-meets-internet-identity/ghostfolio-meets-internet-identity-page.module'
|
||||||
).then((m) => m.GhostfolioMeetsInternetIdentityPageModule)
|
).then((m) => m.GhostfolioMeetsInternetIdentityPageModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'en/blog/2022/07/how-do-i-get-my-finances-in-order',
|
path: 'blog/2022/07/how-do-i-get-my-finances-in-order',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import(
|
import(
|
||||||
'./pages/blog/2022/07/how-do-i-get-my-finances-in-order/how-do-i-get-my-finances-in-order-page.module'
|
'./pages/blog/2022/07/how-do-i-get-my-finances-in-order/how-do-i-get-my-finances-in-order-page.module'
|
||||||
).then((m) => m.HowDoIGetMyFinancesInOrderPageModule)
|
).then((m) => m.HowDoIGetMyFinancesInOrderPageModule)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'blog/2022/08/500-stars-on-github',
|
||||||
|
loadChildren: () =>
|
||||||
|
import(
|
||||||
|
'./pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.module'
|
||||||
|
).then((m) => m.FiveHundredStarsOnGitHubPageModule)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'demo',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'faq',
|
path: 'faq',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
|
@ -21,8 +21,10 @@
|
|||||||
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell>
|
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell>
|
||||||
<ng-container *ngIf="element.type === 'PUBLIC'">
|
<ng-container *ngIf="element.type === 'PUBLIC'">
|
||||||
<ion-icon class="mr-1" name="link-outline"></ion-icon>
|
<ion-icon class="mr-1" name="link-outline"></ion-icon>
|
||||||
<a href="{{ baseUrl }}/p/{{ element.id }}" target="_blank"
|
<a
|
||||||
>{{ baseUrl }}/p/{{ element.id }}</a
|
href="{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}"
|
||||||
|
target="_blank"
|
||||||
|
>{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}</a
|
||||||
>
|
>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Output
|
Output
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||||
import { Access } from '@ghostfolio/common/interfaces';
|
import { Access } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -24,6 +25,7 @@ export class AccessTableComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
public baseUrl = window.location.origin;
|
public baseUrl = window.location.origin;
|
||||||
public dataSource: MatTableDataSource<Access>;
|
public dataSource: MatTableDataSource<Access>;
|
||||||
|
public defaultLanguageCode = DEFAULT_LANGUAGE_CODE;
|
||||||
public displayedColumns = [];
|
public displayedColumns = [];
|
||||||
|
|
||||||
public constructor() {}
|
public constructor() {}
|
||||||
@ -44,7 +46,7 @@ export class AccessTableComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
public onDeleteAccess(aId: string) {
|
public onDeleteAccess(aId: string) {
|
||||||
const confirmation = confirm(
|
const confirmation = confirm(
|
||||||
'Do you really want to revoke this granted access?'
|
$localize`Do you really want to revoke this granted access?`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
|
@ -69,7 +69,9 @@ export class AccountsTableComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteAccount(aId: string) {
|
public onDeleteAccount(aId: string) {
|
||||||
const confirmation = confirm('Do you really want to delete this account?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete this account?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
this.accountDeleted.emit(aId);
|
this.accountDeleted.emit(aId);
|
||||||
|
@ -103,7 +103,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onAddCurrency() {
|
public onAddCurrency() {
|
||||||
const currency = prompt('Please add a currency:');
|
const currency = prompt($localize`Please add a currency:`);
|
||||||
|
|
||||||
if (currency) {
|
if (currency) {
|
||||||
const currencies = uniq([...this.customCurrencies, currency]);
|
const currencies = uniq([...this.customCurrencies, currency]);
|
||||||
@ -116,7 +116,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteCoupon(aCouponCode: string) {
|
public onDeleteCoupon(aCouponCode: string) {
|
||||||
const confirmation = confirm('Do you really want to delete this coupon?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete this coupon?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation === true) {
|
if (confirmation === true) {
|
||||||
const coupons = this.coupons.filter((coupon) => {
|
const coupons = this.coupons.filter((coupon) => {
|
||||||
@ -127,7 +129,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteCurrency(aCurrency: string) {
|
public onDeleteCurrency(aCurrency: string) {
|
||||||
const confirmation = confirm('Do you really want to delete this currency?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete this currency?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation === true) {
|
if (confirmation === true) {
|
||||||
const currencies = this.customCurrencies.filter((currency) => {
|
const currencies = this.customCurrencies.filter((currency) => {
|
||||||
@ -142,7 +146,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onFlushCache() {
|
public onFlushCache() {
|
||||||
const confirmation = confirm('Do you really want to flush the cache?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to flush the cache?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation === true) {
|
if (confirmation === true) {
|
||||||
this.cacheService
|
this.cacheService
|
||||||
@ -190,7 +196,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onSetSystemMessage() {
|
public onSetSystemMessage() {
|
||||||
const systemMessage = prompt('Please set your system message:');
|
const systemMessage = prompt($localize`Please set your system message:`);
|
||||||
|
|
||||||
if (systemMessage) {
|
if (systemMessage) {
|
||||||
this.putSystemMessage(systemMessage);
|
this.putSystemMessage(systemMessage);
|
||||||
|
@ -55,7 +55,9 @@ export class AdminUsersComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteUser(aId: string) {
|
public onDeleteUser(aId: string) {
|
||||||
const confirmation = confirm('Do you really want to delete this user?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete this user?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
this.dataService
|
this.dataService
|
||||||
|
@ -109,7 +109,7 @@ export class HeaderComponent implements OnChanges {
|
|||||||
data: {
|
data: {
|
||||||
accessToken: '',
|
accessToken: '',
|
||||||
hasPermissionToUseSocialLogin: this.hasPermissionForSocialLogin,
|
hasPermissionToUseSocialLogin: this.hasPermissionForSocialLogin,
|
||||||
title: 'Sign in'
|
title: $localize`Sign in`
|
||||||
},
|
},
|
||||||
width: '30rem'
|
width: '30rem'
|
||||||
});
|
});
|
||||||
@ -123,7 +123,7 @@ export class HeaderComponent implements OnChanges {
|
|||||||
.loginAnonymous(data?.accessToken)
|
.loginAnonymous(data?.accessToken)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
alert('Oops! Incorrect Security Token.');
|
alert($localize`Oops! Incorrect Security Token.`);
|
||||||
|
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
|
@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component';
|
import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component';
|
||||||
|
import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||||
import {
|
import {
|
||||||
@ -9,7 +10,6 @@ import {
|
|||||||
SettingsStorageService
|
SettingsStorageService
|
||||||
} from '@ghostfolio/client/services/settings-storage.service';
|
} from '@ghostfolio/client/services/settings-storage.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { defaultDateRangeOptions } from '@ghostfolio/common/config';
|
|
||||||
import { Position, User } from '@ghostfolio/common/interfaces';
|
import { Position, User } from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { DateRange } from '@ghostfolio/common/types';
|
import { DateRange } from '@ghostfolio/common/types';
|
||||||
@ -27,7 +27,7 @@ import { PositionDetailDialogParams } from '../position/position-detail-dialog/i
|
|||||||
})
|
})
|
||||||
export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
||||||
public dateRange: DateRange;
|
public dateRange: DateRange;
|
||||||
public dateRangeOptions = defaultDateRangeOptions;
|
public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS;
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public hasImpersonationId: boolean;
|
public hasImpersonationId: boolean;
|
||||||
public hasPermissionToCreateOrder: boolean;
|
public hasPermissionToCreateOrder: boolean;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||||
import {
|
import {
|
||||||
@ -6,7 +7,6 @@ import {
|
|||||||
SettingsStorageService
|
SettingsStorageService
|
||||||
} from '@ghostfolio/client/services/settings-storage.service';
|
} from '@ghostfolio/client/services/settings-storage.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { defaultDateRangeOptions } from '@ghostfolio/common/config';
|
|
||||||
import {
|
import {
|
||||||
PortfolioPerformance,
|
PortfolioPerformance,
|
||||||
UniqueAsset,
|
UniqueAsset,
|
||||||
@ -26,7 +26,7 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
})
|
})
|
||||||
export class HomeOverviewComponent implements OnDestroy, OnInit {
|
export class HomeOverviewComponent implements OnDestroy, OnInit {
|
||||||
public dateRange: DateRange;
|
public dateRange: DateRange;
|
||||||
public dateRangeOptions = defaultDateRangeOptions;
|
public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS;
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public errors: UniqueAsset[];
|
public errors: UniqueAsset[];
|
||||||
public hasError: boolean;
|
public hasError: boolean;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
.chart-container {
|
.chart-container {
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-width: 67rem;
|
max-width: 50rem;
|
||||||
|
|
||||||
// Fallback for aspect-ratio (using padding hack)
|
// Fallback for aspect-ratio (using padding hack)
|
||||||
@supports not (aspect-ratio: 16 / 9) {
|
@supports not (aspect-ratio: 16 / 9) {
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
src="./assets/icons/internet-computer.svg"
|
src="../assets/icons/internet-computer.svg"
|
||||||
style="height: 0.75rem"
|
style="height: 0.75rem"
|
||||||
/><span i18n>Sign in with Internet Identity</span>
|
/><span i18n>Sign in with Internet Identity</span>
|
||||||
</button>
|
</button>
|
||||||
<a href="/api/v1/auth/google" mat-stroked-button
|
<a href="../api/v1/auth/google" mat-stroked-button
|
||||||
><img
|
><img
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
src="./assets/icons/google.svg"
|
src="../assets/icons/google.svg"
|
||||||
style="height: 1rem"
|
style="height: 1rem"
|
||||||
/><span i18n>Sign in with Google</span></a
|
/><span i18n>Sign in with Google</span></a
|
||||||
>
|
>
|
||||||
|
@ -45,7 +45,7 @@ export class PortfolioSummaryComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
public onEditEmergencyFund() {
|
public onEditEmergencyFund() {
|
||||||
const emergencyFundInput = prompt(
|
const emergencyFundInput = prompt(
|
||||||
'Please enter the amount of your emergency fund:',
|
$localize`Please enter the amount of your emergency fund:`,
|
||||||
this.summary.emergencyFund.toString()
|
this.summary.emergencyFund.toString()
|
||||||
);
|
);
|
||||||
const emergencyFund = parseFloat(emergencyFundInput?.trim());
|
const emergencyFund = parseFloat(emergencyFundInput?.trim());
|
||||||
|
@ -17,6 +17,14 @@ import { ToggleOption } from '@ghostfolio/common/types';
|
|||||||
styleUrls: ['./toggle.component.scss']
|
styleUrls: ['./toggle.component.scss']
|
||||||
})
|
})
|
||||||
export class ToggleComponent implements OnChanges, OnInit {
|
export class ToggleComponent implements OnChanges, OnInit {
|
||||||
|
public static DEFAULT_DATE_RANGE_OPTIONS: ToggleOption[] = [
|
||||||
|
{ label: $localize`Today`, value: '1d' },
|
||||||
|
{ label: $localize`YTD`, value: 'ytd' },
|
||||||
|
{ label: $localize`1Y`, value: '1y' },
|
||||||
|
{ label: $localize`5Y`, value: '5y' },
|
||||||
|
{ label: $localize`Max`, value: 'max' }
|
||||||
|
];
|
||||||
|
|
||||||
@Input() defaultValue: string;
|
@Input() defaultValue: string;
|
||||||
@Input() isLoading: boolean;
|
@Input() isLoading: boolean;
|
||||||
@Input() options: ToggleOption[];
|
@Input() options: ToggleOption[];
|
||||||
|
@ -56,14 +56,16 @@ export class HttpResponseInterceptor implements HttpInterceptor {
|
|||||||
if (!this.snackBarRef) {
|
if (!this.snackBarRef) {
|
||||||
if (this.info.isReadOnlyMode) {
|
if (this.info.isReadOnlyMode) {
|
||||||
this.snackBarRef = this.snackBar.open(
|
this.snackBarRef = this.snackBar.open(
|
||||||
'This feature is currently unavailable. Please try again later.',
|
$localize`This feature is currently unavailable. Please try again later.`,
|
||||||
undefined,
|
undefined,
|
||||||
{ duration: 6000 }
|
{ duration: 6000 }
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.snackBarRef = this.snackBar.open(
|
this.snackBarRef = this.snackBar.open(
|
||||||
'This feature requires a subscription.',
|
$localize`This feature requires a subscription.`,
|
||||||
this.hasPermissionForSubscription ? 'Upgrade Plan' : undefined,
|
this.hasPermissionForSubscription
|
||||||
|
? $localize`Upgrade Plan`
|
||||||
|
: undefined,
|
||||||
{ duration: 6000 }
|
{ duration: 6000 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -79,8 +81,8 @@ export class HttpResponseInterceptor implements HttpInterceptor {
|
|||||||
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {
|
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {
|
||||||
if (!this.snackBarRef) {
|
if (!this.snackBarRef) {
|
||||||
this.snackBarRef = this.snackBar.open(
|
this.snackBarRef = this.snackBar.open(
|
||||||
'Oops! Something went wrong. Please try again later.',
|
$localize`Oops! Something went wrong. Please try again later.`,
|
||||||
'Okay',
|
$localize`Okay`,
|
||||||
{ duration: 6000 }
|
{ duration: 6000 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: AboutPageComponent,
|
component: AboutPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'About'
|
title: $localize`About`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: ChangelogPageComponent,
|
component: ChangelogPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Changelog & License'
|
title: $localize`Changelog & License`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<h3 class="mb-3 text-center" i18n>Changelog</h3>
|
<h3 class="mb-3 text-center" i18n>Changelog</h3>
|
||||||
<mat-card class="changelog">
|
<mat-card class="changelog">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<markdown [src]="'assets/CHANGELOG.md'"></markdown>
|
<markdown [src]="'../assets/CHANGELOG.md'"></markdown>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<h3 class="mb-3 text-center" i18n>License</h3>
|
<h3 class="mb-3 text-center" i18n>License</h3>
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<markdown [src]="'assets/LICENSE'"></markdown>
|
<markdown [src]="'../assets/LICENSE'"></markdown>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: PrivacyPolicyPageComponent,
|
component: PrivacyPolicyPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Privacy Policy'
|
title: $localize`Privacy Policy`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="mb-5 row">
|
<div class="mb-5 row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h3 class="mb-3 text-center" i18n>Privacy Policy</h3>
|
<h3 class="mb-3 text-center" i18n>Privacy Policy</h3>
|
||||||
<markdown [src]="'assets/privacy-policy.md'"></markdown>
|
<markdown [src]="'../assets/privacy-policy.md'"></markdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: AccountPageComponent,
|
component: AccountPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'My Ghostfolio'
|
title: $localize`My Ghostfolio`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onRedeemCoupon() {
|
public onRedeemCoupon() {
|
||||||
let couponCode = prompt('Please enter your coupon code:');
|
let couponCode = prompt($localize`Please enter your coupon code:`);
|
||||||
couponCode = couponCode?.trim();
|
couponCode = couponCode?.trim();
|
||||||
|
|
||||||
if (couponCode) {
|
if (couponCode) {
|
||||||
@ -227,17 +227,21 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
|||||||
.pipe(
|
.pipe(
|
||||||
takeUntil(this.unsubscribeSubject),
|
takeUntil(this.unsubscribeSubject),
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
this.snackBar.open('😞 Could not redeem coupon code', undefined, {
|
this.snackBar.open(
|
||||||
|
'😞 ' + $localize`Could not redeem coupon code`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
duration: 3000
|
duration: 3000
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.snackBarRef = this.snackBar.open(
|
this.snackBarRef = this.snackBar.open(
|
||||||
'✅ Coupon code has been redeemed',
|
'✅' + $localize`Coupon code has been redeemed`,
|
||||||
'Reload',
|
$localize`Reload`,
|
||||||
{
|
{
|
||||||
duration: 3000
|
duration: 3000
|
||||||
}
|
}
|
||||||
@ -283,7 +287,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
|||||||
this.registerDevice();
|
this.registerDevice();
|
||||||
} else {
|
} else {
|
||||||
const confirmation = confirm(
|
const confirmation = confirm(
|
||||||
'Do you really want to remove this sign in method?'
|
$localize`Do you really want to remove this sign in method?`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: AccountsPageComponent,
|
component: AccountsPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Accounts'
|
title: $localize`Accounts`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
component: AdminPageComponent,
|
component: AdminPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Admin Control'
|
title: $localize`Admin Control`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ export class AuthPageComponent implements OnDestroy, OnInit {
|
|||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((params) => {
|
.subscribe((params) => {
|
||||||
const jwt = params['jwt'];
|
const jwt = params['jwt'];
|
||||||
|
|
||||||
this.tokenStorageService.saveToken(
|
this.tokenStorageService.saveToken(
|
||||||
jwt,
|
jwt,
|
||||||
this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true'
|
this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true'
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
<p class="my-5 text-center">
|
<p class="my-5 text-center">
|
||||||
<img
|
<img
|
||||||
alt="Ghostfol.io Screenshot"
|
alt="Ghostfol.io Screenshot"
|
||||||
src="./assets/images/screenshot.png"
|
src="../assets/images/screenshot.png"
|
||||||
style="max-width: 100%; width: 20rem"
|
style="max-width: 100%; width: 20rem"
|
||||||
title="Ghostfol.io Screenshot"
|
title="Ghostfol.io Screenshot"
|
||||||
/>
|
/>
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<p class="my-5 text-center">
|
<p class="my-5 text-center">
|
||||||
<img
|
<img
|
||||||
alt="Ghostfol.io Screenshot"
|
alt="Ghostfol.io Screenshot"
|
||||||
src="./assets/images/screenshot.png"
|
src="../assets/images/screenshot.png"
|
||||||
style="max-width: 100%; width: 20rem"
|
style="max-width: 100%; width: 20rem"
|
||||||
title="Ghostfol.io Screenshot"
|
title="Ghostfol.io Screenshot"
|
||||||
/>
|
/>
|
||||||
|
@ -20,9 +20,7 @@
|
|||||||
<h2 class="h4">From 1* to 100 stars on GitHub</h2>
|
<h2 class="h4">From 1* to 100 stars on GitHub</h2>
|
||||||
<p>
|
<p>
|
||||||
When I decided to
|
When I decided to
|
||||||
<a [routerLink]="['/en', 'blog', '2021', '07', 'hello-ghostfolio']"
|
<a href="../en/blog/2021/07/hello-ghostfolio">publish</a>
|
||||||
>publish</a
|
|
||||||
>
|
|
||||||
the project as
|
the project as
|
||||||
<a href="https://github.com/ghostfolio/ghostfolio"
|
<a href="https://github.com/ghostfolio/ghostfolio"
|
||||||
>open source software</a
|
>open source software</a
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
<div class="mb-3 text-muted"><small>2022-07-23</small></div>
|
<div class="mb-3 text-muted"><small>2022-07-23</small></div>
|
||||||
<img
|
<img
|
||||||
alt="Ghostfolio meets Internet Identity Teaser"
|
alt="Ghostfolio meets Internet Identity Teaser"
|
||||||
class="w-100"
|
class="rounded w-100"
|
||||||
src="./assets/images/blog/ghostfolio-meets-internet-identity.png"
|
src="../assets/images/blog/ghostfolio-meets-internet-identity.png"
|
||||||
title="Ghostfolio meets Internet Identity"
|
title="Ghostfolio meets Internet Identity"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
|
||||||
|
|
||||||
|
import { FiveHundredStarsOnGitHubPageComponent } from './500-stars-on-github-page.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
component: FiveHundredStarsOnGitHubPageComponent,
|
||||||
|
path: '',
|
||||||
|
title: '500 Stars on GitHub'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class FiveHundredStarsOnGitHubRoutingModule {}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
host: { class: 'page' },
|
||||||
|
selector: 'gf-500-stars-on-github-page',
|
||||||
|
styleUrls: ['./500-stars-on-github-page.scss'],
|
||||||
|
templateUrl: './500-stars-on-github-page.html'
|
||||||
|
})
|
||||||
|
export class FiveHundredStarsOnGitHubPageComponent {}
|
@ -0,0 +1,195 @@
|
|||||||
|
<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">500 Stars</h1>
|
||||||
|
<div class="mb-3 text-muted"><small>2022-08-18</small></div>
|
||||||
|
<img
|
||||||
|
alt="500 Stars on GitHub Teaser"
|
||||||
|
class="rounded w-100"
|
||||||
|
src="../assets/images/blog/500-stars-on-github.jpg"
|
||||||
|
title="500 Stars on GitHub"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<section class="mb-4">
|
||||||
|
<p>
|
||||||
|
<a href="https://ghostfol.io">Ghostfolio</a>, the web-based personal
|
||||||
|
finance management software, is celebrating 500 stars on
|
||||||
|
<a href="https://github.com/ghostfolio/ghostfolio">GitHub</a>. This
|
||||||
|
is a major milestone for this open source project and a good time
|
||||||
|
for another
|
||||||
|
<a href="../en/blog/2022/01/ghostfolio-first-months-in-open-source"
|
||||||
|
>recap</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<h2 class="h4">Growing Community</h2>
|
||||||
|
<p>
|
||||||
|
The Ghostfolio community is growing on various platforms and has
|
||||||
|
recently passed 100 members on
|
||||||
|
<a
|
||||||
|
href="https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg"
|
||||||
|
>Slack</a
|
||||||
|
>
|
||||||
|
as well as 100 followers on
|
||||||
|
<a href="https://twitter.com/ghostfolio_">Twitter</a>. If you have
|
||||||
|
not joined yet, this is a good time to make sure you do not miss out
|
||||||
|
on any future updates.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<h2 class="h4">Message Queue: Asynchronous Processing</h2>
|
||||||
|
<p>
|
||||||
|
Overall
|
||||||
|
<a href="https://status.ghostfol.io">stability and robustness</a>
|
||||||
|
has increased significantly since the introduction of a
|
||||||
|
<a href="https://github.com/OptimalBits/bull">message queue</a>. The
|
||||||
|
workers of this robust queue system process jobs, namely gathering
|
||||||
|
historical market data, asynchronously in the background to not
|
||||||
|
bother the main service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<h2 class="h4">Ready for Web 3.0</h2>
|
||||||
|
<p>
|
||||||
|
The
|
||||||
|
<a href="../en/blog/2022/07/ghostfolio-meets-internet-identity"
|
||||||
|
>recent integration of Internet Identity</a
|
||||||
|
>, a blockchain authentication system, makes Ghostfolio ready for
|
||||||
|
Web3. This third iteration of the World Wide Web is the vision of a
|
||||||
|
new and better Internet based on decentralized blockchains to give
|
||||||
|
power back to the users. <i>Internet Identity</i> created by the
|
||||||
|
<a href="https://dfinity.org">Dfinity Foundation</a> enables you to
|
||||||
|
sign in securely and anonymously to Ghostfolio without an email
|
||||||
|
address, username, or a password. All you need is your device with
|
||||||
|
built-in biometric authentication.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<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]="['/markets']"
|
||||||
|
>Ghostfolio Premium</a
|
||||||
|
>) has been achieved. We will continue to leverage the revenue to
|
||||||
|
further improve the fully managed cloud offering for our paying
|
||||||
|
customers. A new goal we have set for ourselves is to become
|
||||||
|
profitable.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<h2 class="h4">Outlook</h2>
|
||||||
|
<p>
|
||||||
|
Besides all the positive accomplishments during the last months,
|
||||||
|
there is still a lot of room for improvement. It would be great to
|
||||||
|
onboard more contributors who are actively involved in software
|
||||||
|
engineering to realize the full potential of open source software.
|
||||||
|
If you are a web developer and interested in personal finance,
|
||||||
|
please get in touch by email via
|
||||||
|
<a href="mailto:hi@ghostfol.io">hi@ghostfol.io</a> or on Twitter
|
||||||
|
<a href="https://twitter.com/ghostfolio_">@ghostfolio_</a>. We are
|
||||||
|
happy to discuss ideas.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We would like to say thank you for all your feedback and support
|
||||||
|
since the beginning of this project.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Off to the next 500 stars!<br />
|
||||||
|
Thomas from Ghostfolio
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="mb-4">
|
||||||
|
<ul class="list-inline">
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Blockchain</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">BuildInPublic</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Cloud</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">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">Future</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Goal</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Internet Identity</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">Message Queue</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">OpenSaaS</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">Personal Finance</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Planning</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">Portfolio Tracker</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Progress</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">SaaS</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">User Feedback</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Wealth</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>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Web 3.0</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-inline-item">
|
||||||
|
<span class="badge badge-light">Worker</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,13 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { FiveHundredStarsOnGitHubRoutingModule } from './500-stars-on-github-page-routing.module';
|
||||||
|
import { FiveHundredStarsOnGitHubPageComponent } from './500-stars-on-github-page.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FiveHundredStarsOnGitHubPageComponent],
|
||||||
|
imports: [CommonModule, FiveHundredStarsOnGitHubRoutingModule, RouterModule],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class FiveHundredStarsOnGitHubPageModule {}
|
@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: BlogPageComponent,
|
component: BlogPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Blog'
|
title: $localize`Blog`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -8,7 +8,31 @@
|
|||||||
<div class="flex-nowrap no-gutters row">
|
<div class="flex-nowrap no-gutters row">
|
||||||
<a
|
<a
|
||||||
class="d-flex w-100"
|
class="d-flex w-100"
|
||||||
[routerLink]="['/en', 'blog', '2022', '07', 'ghostfolio-meets-internet-identity']"
|
href="../en/blog/2022/08/500-stars-on-github"
|
||||||
|
>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="h6 m-0 text-truncate">500 Stars on GitHub</div>
|
||||||
|
<div class="d-flex text-muted">2022-08-18</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 class="mb-3">
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="container p-0">
|
||||||
|
<div class="flex-nowrap no-gutters row">
|
||||||
|
<a
|
||||||
|
class="d-flex w-100"
|
||||||
|
href="../en/blog/2022/07/ghostfolio-meets-internet-identity"
|
||||||
>
|
>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="h6 m-0 text-truncate">
|
<div class="h6 m-0 text-truncate">
|
||||||
@ -34,7 +58,7 @@
|
|||||||
<div class="flex-nowrap no-gutters row">
|
<div class="flex-nowrap no-gutters row">
|
||||||
<a
|
<a
|
||||||
class="d-flex w-100"
|
class="d-flex w-100"
|
||||||
[routerLink]="['/en', 'blog', '2022', '07', 'how-do-i-get-my-finances-in-order']"
|
href="../en/blog/2022/07/how-do-i-get-my-finances-in-order"
|
||||||
>
|
>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="h6 m-0 text-truncate">
|
<div class="h6 m-0 text-truncate">
|
||||||
@ -60,7 +84,7 @@
|
|||||||
<div class="flex-nowrap no-gutters row">
|
<div class="flex-nowrap no-gutters row">
|
||||||
<a
|
<a
|
||||||
class="d-flex w-100"
|
class="d-flex w-100"
|
||||||
[routerLink]="['/en', 'blog', '2022', '01', 'ghostfolio-first-months-in-open-source']"
|
href="'../en/blog/2022/01/ghostfolio-first-months-in-open-source"
|
||||||
>
|
>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="h6 m-0 text-truncate">
|
<div class="h6 m-0 text-truncate">
|
||||||
@ -86,7 +110,7 @@
|
|||||||
<div class="flex-nowrap no-gutters row">
|
<div class="flex-nowrap no-gutters row">
|
||||||
<a
|
<a
|
||||||
class="d-flex w-100"
|
class="d-flex w-100"
|
||||||
[routerLink]="['/en', 'blog', '2021', '07', 'hello-ghostfolio']"
|
href="../en/blog/2021/07/hello-ghostfolio"
|
||||||
>
|
>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="h6 m-0 text-truncate">Hello Ghostfolio</div>
|
<div class="h6 m-0 text-truncate">Hello Ghostfolio</div>
|
||||||
@ -110,7 +134,7 @@
|
|||||||
<div class="flex-nowrap no-gutters row">
|
<div class="flex-nowrap no-gutters row">
|
||||||
<a
|
<a
|
||||||
class="d-flex w-100"
|
class="d-flex w-100"
|
||||||
[routerLink]="['/de', 'blog', '2021', '07', 'hallo-ghostfolio']"
|
href="../de/blog/2021/07/hallo-ghostfolio"
|
||||||
>
|
>
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="h6 m-0 text-truncate">Hallo Ghostfolio</div>
|
<div class="h6 m-0 text-truncate">Hallo Ghostfolio</div>
|
||||||
|
@ -28,7 +28,7 @@ export class DemoPageComponent implements OnDestroy {
|
|||||||
|
|
||||||
if (hasToken) {
|
if (hasToken) {
|
||||||
alert(
|
alert(
|
||||||
'As you are already logged in, you cannot access the demo account.'
|
$localize`As you are already logged in, you cannot access the demo account.`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.tokenStorageService.saveToken(this.info.demoAuthToken, true);
|
this.tokenStorageService.saveToken(this.info.demoAuthToken, true);
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: FaqPageComponent,
|
component: FaqPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'FAQ'
|
title: $localize`FAQ`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -35,8 +35,7 @@
|
|||||||
>Get Started</a
|
>Get Started</a
|
||||||
>” button at the top of the page. You have multiple options to join
|
>” button at the top of the page. You have multiple options to join
|
||||||
Ghostfolio: Create an account with a security token, using
|
Ghostfolio: Create an account with a security token, using
|
||||||
<a
|
<a href="../en/blog/2022/07/ghostfolio-meets-internet-identity"
|
||||||
[routerLink]="['/en', 'blog', '2022', '07', 'ghostfolio-meets-internet-identity']"
|
|
||||||
>Internet Identity</a
|
>Internet Identity</a
|
||||||
>
|
>
|
||||||
or <i>Google Sign</i>. We will guide you to set up your portfolio.
|
or <i>Google Sign</i>. We will guide you to set up your portfolio.
|
||||||
@ -46,8 +45,7 @@
|
|||||||
<mat-card-title i18n>Can I use Ghostfolio anonymously?</mat-card-title>
|
<mat-card-title i18n>Can I use Ghostfolio anonymously?</mat-card-title>
|
||||||
<mat-card-content i18n>
|
<mat-card-content i18n>
|
||||||
Yes, the authentication systems (via security token or
|
Yes, the authentication systems (via security token or
|
||||||
<a
|
<a href="../en/blog/2022/07/ghostfolio-meets-internet-identity"
|
||||||
[routerLink]="['/en', 'blog', '2022', '07', 'ghostfolio-meets-internet-identity']"
|
|
||||||
>Internet Identity</a
|
>Internet Identity</a
|
||||||
>) enable you to sign in securely and anonymously to Ghostfolio. There
|
>) enable you to sign in securely and anonymously to Ghostfolio. There
|
||||||
is no need for an email address, phone number, or a username.
|
is no need for an email address, phone number, or a username.
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: FeaturesPageComponent,
|
component: FeaturesPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Features'
|
title: $localize`Features`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
component: HomePageComponent,
|
component: HomePageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Overview'
|
title: $localize`Overview`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<img
|
<img
|
||||||
alt="Ghostfol.io Trailer"
|
alt="Ghostfol.io Trailer"
|
||||||
class="rounded video"
|
class="rounded video"
|
||||||
src="./assets/images/video-preview.jpg"
|
src="../assets/images/video-preview.jpg"
|
||||||
style="max-width: 100%; width: 40rem"
|
style="max-width: 100%; width: 40rem"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
@ -183,7 +183,7 @@
|
|||||||
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
|
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
|
||||||
title="Get Ghostfolio on Google Play"
|
title="Get Ghostfolio on Google Play"
|
||||||
>
|
>
|
||||||
<img alt="Google Play Badge" src="assets/badge-en-google-play.png" />
|
<img alt="Google Play Badge" src="../assets/badge-en-google-play.png" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: MarketsPageComponent,
|
component: MarketsPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Markets'
|
title: $localize`Markets`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: AllocationsPageComponent,
|
component: AllocationsPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Allocations'
|
title: $localize`Allocations`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: AnalysisPageComponent,
|
component: AnalysisPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Analysis'
|
title: $localize`Analysis`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: FirePageComponent,
|
component: FirePageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'FIRE'
|
title: $localize`FIRE`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: HoldingsPageComponent,
|
component: HoldingsPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Holdings'
|
title: $localize`Holdings`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: TransactionsPageComponent,
|
component: TransactionsPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Activities'
|
title: $localize`Activities`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
|
|||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
|
|
||||||
input.onchange = (event) => {
|
input.onchange = (event) => {
|
||||||
this.snackBar.open('⏳ Importing data...');
|
this.snackBar.open('⏳' + $localize`Importing data...`);
|
||||||
|
|
||||||
// Getting the file reference
|
// Getting the file reference
|
||||||
const file = (event.target as HTMLInputElement).files[0];
|
const file = (event.target as HTMLInputElement).files[0];
|
||||||
@ -334,7 +334,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
|
|||||||
private handleImportSuccess() {
|
private handleImportSuccess() {
|
||||||
this.fetchActivities();
|
this.fetchActivities();
|
||||||
|
|
||||||
this.snackBar.open('✅ Import has been completed', undefined, {
|
this.snackBar.open('✅' + $localize`Import has been completed`, undefined, {
|
||||||
duration: 3000
|
duration: 3000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: PricingPageComponent,
|
component: PricingPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Pricing'
|
title: $localize`Pricing`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: PublicPageComponent,
|
component: PublicPageComponent,
|
||||||
path: ':id',
|
path: ':id',
|
||||||
title: 'Portfolio'
|
title: $localize`Portfolio`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: RegisterPageComponent,
|
component: RegisterPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Registration'
|
title: $localize`Registration`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -36,15 +36,15 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
src="./assets/icons/internet-computer.svg"
|
src="../assets/icons/internet-computer.svg"
|
||||||
style="height: 0.75rem"
|
style="height: 0.75rem"
|
||||||
/>
|
/>
|
||||||
<span i18n>Continue with Internet Identity</span>
|
<span i18n>Continue with Internet Identity</span>
|
||||||
</button>
|
</button>
|
||||||
<a class="d-block" href="/api/v1/auth/google" mat-stroked-button
|
<a class="d-block" href="../api/v1/auth/google" mat-stroked-button
|
||||||
><img
|
><img
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
src="./assets/icons/google.svg"
|
src="../assets/icons/google.svg"
|
||||||
style="height: 1rem"
|
style="height: 1rem"
|
||||||
/><span i18n>Continue with Google</span></a
|
/><span i18n>Continue with Google</span></a
|
||||||
>
|
>
|
||||||
|
@ -9,7 +9,7 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
component: ResourcesPageComponent,
|
component: ResourcesPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Resources'
|
title: $localize`Resources`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -29,8 +29,7 @@
|
|||||||
easier and faster in this guide.
|
easier and faster in this guide.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a href="../en/blog/2022/07/how-do-i-get-my-finances-in-order"
|
||||||
[routerLink]="['/en', 'blog', '2022', '07', 'how-do-i-get-my-finances-in-order']"
|
|
||||||
>How do I get my finances in order? →</a
|
>How do I get my finances in order? →</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ component: WebauthnPageComponent, path: '', title: 'Login' }
|
{ component: WebauthnPageComponent, path: '', title: $localize`Login` }
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -16,7 +16,7 @@ const routes: Routes = [
|
|||||||
],
|
],
|
||||||
component: ZenPageComponent,
|
component: ZenPageComponent,
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Overview'
|
title: $localize`Overview`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
BIN
apps/client/src/assets/images/blog/500-stars-on-github.jpg
Normal file
BIN
apps/client/src/assets/images/blog/500-stars-on-github.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 197 KiB |
@ -1,6 +1,6 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
Disallow: /about/privacy-policy
|
Disallow: /en/about/privacy-policy
|
||||||
Disallow: /p/*
|
Disallow: /en/p/*
|
||||||
|
|
||||||
Sitemap: https://ghostfol.io/sitemap.xml
|
Sitemap: https://ghostfol.io/sitemap.xml
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"src": "/assets/android-chrome-192x192.png",
|
"src": "/en/assets/android-chrome-192x192.png",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"src": "/assets/android-chrome-512x512.png",
|
"src": "/en/assets/android-chrome-512x512.png",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -6,66 +6,70 @@
|
|||||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io</loc>
|
<loc>https://ghostfol.io</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://ghostfol.io/about</loc>
|
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://ghostfol.io/about/changelog</loc>
|
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://ghostfol.io/blog</loc>
|
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio</loc>
|
<loc>https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/demo</loc>
|
<loc>https://ghostfol.io/en/about</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://ghostfol.io/en/about/changelog</loc>
|
||||||
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://ghostfol.io/en/blog</loc>
|
||||||
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/en/blog/2021/07/hello-ghostfolio</loc>
|
<loc>https://ghostfol.io/en/blog/2021/07/hello-ghostfolio</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/en/blog/2022/01/ghostfolio-first-months-in-open-source</loc>
|
<loc>https://ghostfol.io/en/blog/2022/01/ghostfolio-first-months-in-open-source</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/en/blog/2022/07/ghostfolio-meets-internet-identity</loc>
|
<loc>https://ghostfol.io/en/blog/2022/07/ghostfolio-meets-internet-identity</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/en/blog/2022/07/how-do-i-get-my-finances-in-order</loc>
|
<loc>https://ghostfol.io/en/blog/2022/07/how-do-i-get-my-finances-in-order</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/faq</loc>
|
<loc>https://ghostfol.io/en/blog/2022/08/500-stars-on-github</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/features</loc>
|
<loc>https://ghostfol.io/en/demo</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/markets</loc>
|
<loc>https://ghostfol.io/en/faq</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/pricing</loc>
|
<loc>https://ghostfol.io/en/features</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/register</loc>
|
<loc>https://ghostfol.io/en/markets</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://ghostfol.io/resources</loc>
|
<loc>https://ghostfol.io/en/pricing</loc>
|
||||||
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://ghostfol.io/en/register</loc>
|
||||||
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://ghostfol.io/en/resources</loc>
|
||||||
|
<lastmod>2022-08-18T00:00:00+00:00</lastmod>
|
||||||
</url>
|
</url>
|
||||||
</urlset>
|
</urlset>
|
||||||
|
@ -19,10 +19,7 @@
|
|||||||
name="twitter:description"
|
name="twitter:description"
|
||||||
content="Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies"
|
content="Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies"
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta name="twitter:image" content="https://ghostfol.io/assets/cover.png" />
|
||||||
name="twitter:image"
|
|
||||||
content="https://www.ghostfol.io/assets/cover.png"
|
|
||||||
/>
|
|
||||||
<meta
|
<meta
|
||||||
name="twitter:title"
|
name="twitter:title"
|
||||||
content="Ghostfolio – Open Source Wealth Management Software"
|
content="Ghostfolio – Open Source Wealth Management Software"
|
||||||
@ -37,12 +34,9 @@
|
|||||||
content="Ghostfolio – Open Source Wealth Management Software"
|
content="Ghostfolio – Open Source Wealth Management Software"
|
||||||
/>
|
/>
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:url" content="https://www.ghostfol.io" />
|
<meta property="og:url" content="https://ghostfol.io" />
|
||||||
<meta
|
<meta property="og:image" content="https://ghostfol.io/assets/cover.png" />
|
||||||
property="og:image"
|
<meta property="og:updated_time" content="2022-08-18T00:00:00+00:00" />
|
||||||
content="https://www.ghostfol.io/assets/cover.png"
|
|
||||||
/>
|
|
||||||
<meta property="og:updated_time" content="2022-05-28T00:00:00+00:00" />
|
|
||||||
<meta
|
<meta
|
||||||
property="og:site_name"
|
property="og:site_name"
|
||||||
content="Ghostfolio – Open Source Wealth Management Software"
|
content="Ghostfolio – Open Source Wealth Management Software"
|
||||||
@ -51,26 +45,26 @@
|
|||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="/assets/apple-touch-icon.png"
|
href="../assets/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
href="/assets/favicon-32x32.png"
|
href="../assets/favicon-32x32.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
href="/assets/favicon-16x16.png"
|
href="../assets/favicon-16x16.png"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="/assets/site.webmanifest" />
|
<link rel="manifest" href="../assets/site.webmanifest" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<gf-root></gf-root>
|
<gf-root></gf-root>
|
||||||
|
|
||||||
<script type="module" src="ionicons/ionicons.esm.js"></script>
|
<script type="module" src="../ionicons/ionicons.esm.js"></script>
|
||||||
<script nomodule="" src="ionicons.js"></script>
|
<script nomodule="" src="ionicons.js"></script>
|
||||||
|
|
||||||
<noscript
|
<noscript
|
||||||
|
2946
apps/client/src/locales/messages.de.xlf
Normal file
2946
apps/client/src/locales/messages.de.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2614
apps/client/src/locales/messages.xlf
Normal file
2614
apps/client/src/locales/messages.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2
apps/client/src/styles/bootstrap.scss
vendored
2
apps/client/src/styles/bootstrap.scss
vendored
@ -11,7 +11,7 @@
|
|||||||
@import '~bootstrap/scss/root';
|
@import '~bootstrap/scss/root';
|
||||||
@import '~bootstrap/scss/reboot';
|
@import '~bootstrap/scss/reboot';
|
||||||
@import '~bootstrap/scss/type';
|
@import '~bootstrap/scss/type';
|
||||||
// @import '~bootstrap/scss/images';
|
@import '~bootstrap/scss/images';
|
||||||
// @import '~bootstrap/scss/code';
|
// @import '~bootstrap/scss/code';
|
||||||
@import '~bootstrap/scss/grid';
|
@import '~bootstrap/scss/grid';
|
||||||
// @import '~bootstrap/scss/tables';
|
// @import '~bootstrap/scss/tables';
|
||||||
|
@ -2,16 +2,6 @@ import { DataSource } from '@prisma/client';
|
|||||||
import { JobOptions, JobStatus } from 'bull';
|
import { JobOptions, JobStatus } from 'bull';
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
|
||||||
import { ToggleOption } from './types';
|
|
||||||
|
|
||||||
export const defaultDateRangeOptions: ToggleOption[] = [
|
|
||||||
{ label: 'Today', value: '1d' },
|
|
||||||
{ label: 'YTD', value: 'ytd' },
|
|
||||||
{ label: '1Y', value: '1y' },
|
|
||||||
{ label: '5Y', value: '5y' },
|
|
||||||
{ label: 'Max', value: 'max' }
|
|
||||||
];
|
|
||||||
|
|
||||||
export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f';
|
export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f';
|
||||||
|
|
||||||
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
|
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
|
||||||
@ -49,6 +39,7 @@ export const DATA_GATHERING_QUEUE_PRIORITY_LOW = Number.MAX_SAFE_INTEGER;
|
|||||||
export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1;
|
export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1;
|
||||||
|
|
||||||
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
|
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
|
||||||
|
export const DEFAULT_LANGUAGE_CODE = 'en';
|
||||||
|
|
||||||
export const GATHER_ASSET_PROFILE_PROCESS = 'GATHER_ASSET_PROFILE';
|
export const GATHER_ASSET_PROFILE_PROCESS = 'GATHER_ASSET_PROFILE';
|
||||||
export const GATHER_ASSET_PROFILE_PROCESS_OPTIONS: JobOptions = {
|
export const GATHER_ASSET_PROFILE_PROCESS_OPTIONS: JobOptions = {
|
||||||
|
@ -132,7 +132,9 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteActivity(aId: string) {
|
public onDeleteActivity(aId: string) {
|
||||||
const confirmation = confirm('Do you really want to delete this activity?');
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete this activity?`
|
||||||
|
);
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
this.activityDeleted.emit(aId);
|
this.activityDeleted.emit(aId);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "1.178.0",
|
"version": "1.180.0",
|
||||||
"homepage": "https://ghostfol.io",
|
"homepage": "https://ghostfol.io",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"affected:lint": "nx affected:lint",
|
"affected:lint": "nx affected:lint",
|
||||||
"affected:test": "nx affected:test",
|
"affected:test": "nx affected:test",
|
||||||
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
|
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
|
||||||
"build:all": "nx run api:build:production && nx run client:build:production && yarn replace-placeholders-in-build",
|
"build:all": "nx run api:build:production && nx run client:build:production --localize && yarn replace-placeholders-in-build",
|
||||||
"build:dev": "nx run api:build && nx run client:build && yarn replace-placeholders-in-build",
|
"build:dev": "nx run api:build && nx run client:build && yarn replace-placeholders-in-build",
|
||||||
"build:storybook": "nx run ui:build-storybook",
|
"build:storybook": "nx run ui:build-storybook",
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
@ -29,6 +29,7 @@
|
|||||||
"database:validate": "prisma validate",
|
"database:validate": "prisma validate",
|
||||||
"dep-graph": "nx dep-graph",
|
"dep-graph": "nx dep-graph",
|
||||||
"e2e": "ng e2e",
|
"e2e": "ng e2e",
|
||||||
|
"extract-locales": "ng extract-i18n client --output-path ./apps/client/src/locales",
|
||||||
"format": "nx format:write",
|
"format": "nx format:write",
|
||||||
"format:check": "nx format:check",
|
"format:check": "nx format:check",
|
||||||
"format:write": "nx format:write",
|
"format:write": "nx format:write",
|
||||||
@ -40,7 +41,7 @@
|
|||||||
"postinstall": "prisma generate && ngcc --properties es2020 browser module main",
|
"postinstall": "prisma generate && ngcc --properties es2020 browser module main",
|
||||||
"replace-placeholders-in-build": "node ./replace.build.js",
|
"replace-placeholders-in-build": "node ./replace.build.js",
|
||||||
"start": "node dist/apps/api/main",
|
"start": "node dist/apps/api/main",
|
||||||
"start:client": "ng serve client --hmr -o",
|
"start:client": "ng serve client --configuration=development-en --hmr -o",
|
||||||
"start:prod": "node apps/api/main",
|
"start:prod": "node apps/api/main",
|
||||||
"start:server": "nx serve api --watch",
|
"start:server": "nx serve api --watch",
|
||||||
"start:storybook": "nx run ui:storybook",
|
"start:storybook": "nx run ui:storybook",
|
||||||
@ -110,6 +111,7 @@
|
|||||||
"ionicons": "5.5.1",
|
"ionicons": "5.5.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
|
"ng-extract-i18n-merge": "2.1.2",
|
||||||
"ngx-device-detector": "3.0.0",
|
"ngx-device-detector": "3.0.0",
|
||||||
"ngx-markdown": "14.0.1",
|
"ngx-markdown": "14.0.1",
|
||||||
"ngx-skeleton-loader": "5.0.0",
|
"ngx-skeleton-loader": "5.0.0",
|
||||||
|
121
yarn.lock
121
yarn.lock
@ -18,6 +18,14 @@
|
|||||||
"@angular-devkit/core" "14.1.0"
|
"@angular-devkit/core" "14.1.0"
|
||||||
rxjs "6.6.7"
|
rxjs "6.6.7"
|
||||||
|
|
||||||
|
"@angular-devkit/architect@^0.1301.0":
|
||||||
|
version "0.1301.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1301.4.tgz#2fc51bcae0dcb581c8be401e2fde7bbd10b43076"
|
||||||
|
integrity sha512-p6G8CEMnE+gYwxRyEttj3QGsuNJ3Kusi7iwBIzWyf2RpJSdGzXdwUEiRGg6iS0YHFr06/ZFfAWfnM2DQvNm4TA==
|
||||||
|
dependencies:
|
||||||
|
"@angular-devkit/core" "13.1.4"
|
||||||
|
rxjs "6.6.7"
|
||||||
|
|
||||||
"@angular-devkit/build-angular@14.1.0":
|
"@angular-devkit/build-angular@14.1.0":
|
||||||
version "14.1.0"
|
version "14.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-14.1.0.tgz#f2aaaa4d28c0f39fdcfee6c0241e1bedb576b2d4"
|
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-14.1.0.tgz#f2aaaa4d28c0f39fdcfee6c0241e1bedb576b2d4"
|
||||||
@ -96,6 +104,18 @@
|
|||||||
"@angular-devkit/architect" "0.1401.0"
|
"@angular-devkit/architect" "0.1401.0"
|
||||||
rxjs "6.6.7"
|
rxjs "6.6.7"
|
||||||
|
|
||||||
|
"@angular-devkit/core@13.1.4":
|
||||||
|
version "13.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-13.1.4.tgz#b5b6ddd674ae351f83beff2e4a0d702096bdfd47"
|
||||||
|
integrity sha512-225Gjy4iVxh5Jo9njJnaG75M/Dt95UW+dEPCGWKV5E/++7UUlXlo9sNWq8x2vJm2nhtsPkpnXNOt4pW1mIDwqQ==
|
||||||
|
dependencies:
|
||||||
|
ajv "8.8.2"
|
||||||
|
ajv-formats "2.1.1"
|
||||||
|
fast-json-stable-stringify "2.1.0"
|
||||||
|
magic-string "0.25.7"
|
||||||
|
rxjs "6.6.7"
|
||||||
|
source-map "0.7.3"
|
||||||
|
|
||||||
"@angular-devkit/core@14.0.5":
|
"@angular-devkit/core@14.0.5":
|
||||||
version "14.0.5"
|
version "14.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.0.5.tgz#19f5940b53aeb0ce56479c44670d3bc3b2df92b1"
|
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.0.5.tgz#19f5940b53aeb0ce56479c44670d3bc3b2df92b1"
|
||||||
@ -118,6 +138,17 @@
|
|||||||
rxjs "6.6.7"
|
rxjs "6.6.7"
|
||||||
source-map "0.7.4"
|
source-map "0.7.4"
|
||||||
|
|
||||||
|
"@angular-devkit/core@14.1.2", "@angular-devkit/core@^13.0.0 || ^14.0.0":
|
||||||
|
version "14.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-14.1.2.tgz#f4c287295065b3845e85a84e29f12629b4a52cf9"
|
||||||
|
integrity sha512-fIfymD1erjoj1eVh7pa/dvOtUhSd7sEOGuWEJ81HJqdzwZbPWweRu3Nh/9kj/ttUy8xawWfdJHLwyG2KnRu0DA==
|
||||||
|
dependencies:
|
||||||
|
ajv "8.11.0"
|
||||||
|
ajv-formats "2.1.1"
|
||||||
|
jsonc-parser "3.1.0"
|
||||||
|
rxjs "6.6.7"
|
||||||
|
source-map "0.7.4"
|
||||||
|
|
||||||
"@angular-devkit/schematics@14.0.5":
|
"@angular-devkit/schematics@14.0.5":
|
||||||
version "14.0.5"
|
version "14.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.0.5.tgz#01777d2ad473d35bdfdbbb751521c43421ad9772"
|
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.0.5.tgz#01777d2ad473d35bdfdbbb751521c43421ad9772"
|
||||||
@ -140,6 +171,17 @@
|
|||||||
ora "5.4.1"
|
ora "5.4.1"
|
||||||
rxjs "6.6.7"
|
rxjs "6.6.7"
|
||||||
|
|
||||||
|
"@angular-devkit/schematics@14.1.2", "@angular-devkit/schematics@^13.0.0 || ^14.0.0":
|
||||||
|
version "14.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-14.1.2.tgz#6fb81aaece4ad1a24f3dd7b213e2f7000b41adbf"
|
||||||
|
integrity sha512-vC9UA3heXbq9CAkwMXHJfIu0G7Ty2oTJ5PtrrFQpig1PrFnRfr4sg+qRS1CLsEAJYQNo14xV55OQkKEPTij/Gg==
|
||||||
|
dependencies:
|
||||||
|
"@angular-devkit/core" "14.1.2"
|
||||||
|
jsonc-parser "3.1.0"
|
||||||
|
magic-string "0.26.2"
|
||||||
|
ora "5.4.1"
|
||||||
|
rxjs "6.6.7"
|
||||||
|
|
||||||
"@angular-eslint/bundled-angular-compiler@14.0.2":
|
"@angular-eslint/bundled-angular-compiler@14.0.2":
|
||||||
version "14.0.2"
|
version "14.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-14.0.2.tgz#16aed25430b96fac50d069b2e44dae89fa4b12ad"
|
resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-14.0.2.tgz#16aed25430b96fac50d069b2e44dae89fa4b12ad"
|
||||||
@ -3705,6 +3747,15 @@
|
|||||||
"@angular-devkit/schematics" "14.1.0"
|
"@angular-devkit/schematics" "14.1.0"
|
||||||
jsonc-parser "3.1.0"
|
jsonc-parser "3.1.0"
|
||||||
|
|
||||||
|
"@schematics/angular@^13.0.0 || ^14.0.0":
|
||||||
|
version "14.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.1.2.tgz#edc828c3bf3e5eb9c55d11e560d3fd680819021c"
|
||||||
|
integrity sha512-vttvYpffkG/cy9aUCXLW+Dc0msUNbyoFXTQRHN/MYX5uNVvXaRCEzWVE3tp87Dt5XlGo2r9e21gNAoY1TyXj3Q==
|
||||||
|
dependencies:
|
||||||
|
"@angular-devkit/core" "14.1.2"
|
||||||
|
"@angular-devkit/schematics" "14.1.2"
|
||||||
|
jsonc-parser "3.1.0"
|
||||||
|
|
||||||
"@simplewebauthn/browser@5.2.1":
|
"@simplewebauthn/browser@5.2.1":
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@simplewebauthn/browser/-/browser-5.2.1.tgz#569252a9f235a99aae90c4d1cc6c441f42637b8e"
|
resolved "https://registry.yarnpkg.com/@simplewebauthn/browser/-/browser-5.2.1.tgz#569252a9f235a99aae90c4d1cc6c441f42637b8e"
|
||||||
@ -5864,6 +5915,16 @@ ajv@8.11.0, ajv@^8.0.0, ajv@^8.8.0:
|
|||||||
require-from-string "^2.0.2"
|
require-from-string "^2.0.2"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
ajv@8.8.2:
|
||||||
|
version "8.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb"
|
||||||
|
integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^3.1.1"
|
||||||
|
json-schema-traverse "^1.0.0"
|
||||||
|
require-from-string "^2.0.2"
|
||||||
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1:
|
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
@ -7679,11 +7740,16 @@ commander@^6.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||||
|
|
||||||
commander@^8.0.0, commander@^8.3.0:
|
commander@^8.0.0, commander@^8.3.0, commander@~8.3.0:
|
||||||
version "8.3.0"
|
version "8.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||||
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
||||||
|
|
||||||
|
commander@~7.1.0:
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff"
|
||||||
|
integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==
|
||||||
|
|
||||||
common-path-prefix@^3.0.0:
|
common-path-prefix@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
|
resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
|
||||||
@ -10319,7 +10385,7 @@ fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9:
|
|||||||
merge2 "^1.3.0"
|
merge2 "^1.3.0"
|
||||||
micromatch "^4.0.4"
|
micromatch "^4.0.4"
|
||||||
|
|
||||||
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||||
@ -13113,6 +13179,11 @@ jest@27.5.1:
|
|||||||
import-local "^3.0.2"
|
import-local "^3.0.2"
|
||||||
jest-cli "^27.5.1"
|
jest-cli "^27.5.1"
|
||||||
|
|
||||||
|
js-levenshtein@~1.1.6:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||||
|
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
||||||
|
|
||||||
js-sha256@0.9.0, js-sha256@^0.9.0:
|
js-sha256@0.9.0, js-sha256@^0.9.0:
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
|
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
|
||||||
@ -13812,6 +13883,13 @@ luxon@^1.23.x, luxon@^1.28.0:
|
|||||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
|
||||||
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==
|
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==
|
||||||
|
|
||||||
|
magic-string@0.25.7:
|
||||||
|
version "0.25.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||||
|
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
|
||||||
|
dependencies:
|
||||||
|
sourcemap-codec "^1.4.4"
|
||||||
|
|
||||||
magic-string@0.26.1:
|
magic-string@0.26.1:
|
||||||
version "0.26.1"
|
version "0.26.1"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.1.tgz#ba9b651354fa9512474199acecf9c6dbe93f97fd"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.1.tgz#ba9b651354fa9512474199acecf9c6dbe93f97fd"
|
||||||
@ -14495,6 +14573,19 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
|
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
|
||||||
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
|
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
|
||||||
|
|
||||||
|
ng-extract-i18n-merge@2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ng-extract-i18n-merge/-/ng-extract-i18n-merge-2.1.2.tgz#1a86beccc85cf2fbeb7538009feeb8a6787d602a"
|
||||||
|
integrity sha512-dRo/oO4m3tr26uE4+DHJh0iTqSV3qVl+WGTSHGKKyAM628dP3hZunhBTs7fCpLs57aEex84cJax8X+Y8wmBXTQ==
|
||||||
|
dependencies:
|
||||||
|
"@angular-devkit/architect" "^0.1301.0"
|
||||||
|
"@angular-devkit/core" "^13.0.0 || ^14.0.0"
|
||||||
|
"@angular-devkit/schematics" "^13.0.0 || ^14.0.0"
|
||||||
|
"@schematics/angular" "^13.0.0 || ^14.0.0"
|
||||||
|
xliff-simple-merge "~0.12.4"
|
||||||
|
xml_normalize "~0.8.4"
|
||||||
|
xmldoc "~1.1.2"
|
||||||
|
|
||||||
ngx-device-detector@3.0.0:
|
ngx-device-detector@3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ngx-device-detector/-/ngx-device-detector-3.0.0.tgz#9c5b1db66e03837d5de0e93fe4a1de93948c9c81"
|
resolved "https://registry.yarnpkg.com/ngx-device-detector/-/ngx-device-detector-3.0.0.tgz#9c5b1db66e03837d5de0e93fe4a1de93948c9c81"
|
||||||
@ -17901,7 +17992,7 @@ source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
||||||
|
|
||||||
sourcemap-codec@^1.4.8:
|
sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
|
||||||
version "1.4.8"
|
version "1.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||||
@ -19873,16 +19964,40 @@ x-default-browser@^0.4.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
default-browser-id "^1.0.4"
|
default-browser-id "^1.0.4"
|
||||||
|
|
||||||
|
xliff-simple-merge@~0.12.4:
|
||||||
|
version "0.12.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xliff-simple-merge/-/xliff-simple-merge-0.12.4.tgz#d3e712c71bec4344558b11a5e2578915cee943ec"
|
||||||
|
integrity sha512-Rk88D62XUoMgyBLEILpgOx3ARnxONLwH/xz+/c1HsRHYPHDnuZhYORkwrBCXsE942esG+J+OUCCOiydXJxY4Ug==
|
||||||
|
dependencies:
|
||||||
|
commander "~8.3.0"
|
||||||
|
js-levenshtein "~1.1.6"
|
||||||
|
xmldoc "~1.1.2"
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
||||||
|
|
||||||
|
xml_normalize@~0.8.4:
|
||||||
|
version "0.8.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xml_normalize/-/xml_normalize-0.8.4.tgz#589c88a0c3f0fe7e89c94ae14b82de02ebfc1f2c"
|
||||||
|
integrity sha512-I+PiD+W/fS7YOWb4THhQfvkzd1mCuTT3KoRt4PTxiWYbH1RY74w5ewcIas8ja9APC8Ho2izg8VcNnTTN35qM9A==
|
||||||
|
dependencies:
|
||||||
|
commander "~7.1.0"
|
||||||
|
xmldoc "~1.1.2"
|
||||||
|
|
||||||
xmlchars@^2.2.0:
|
xmlchars@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||||
|
|
||||||
|
xmldoc@~1.1.2:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.4.tgz#ea4e26dca76b1d218a2f777018bce404ba374a86"
|
||||||
|
integrity sha512-rQshsBGR5s7pUNENTEncpI2LTCuzicri0DyE4SCV5XmS0q81JS8j1iPijP0Q5c4WLGbKh3W92hlOwY6N9ssW1w==
|
||||||
|
dependencies:
|
||||||
|
sax "^1.2.4"
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
Reference in New Issue
Block a user