Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
7c8530483c | |||
539d3ff754 | |||
9d28b63da6 | |||
24abbd85e6 | |||
b6f395fd3b |
18
CHANGELOG.md
18
CHANGELOG.md
@ -5,6 +5,24 @@ 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.183.0 - 24.08.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a filter by asset sub class for the asset profiles in the admin control
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved the language localization for German (`de`)
|
||||||
|
|
||||||
|
## 1.182.0 - 23.08.2022
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved the language localization for German (`de`)
|
||||||
|
- Extended and made the columns of the asset profiles sortable in the admin control
|
||||||
|
- Moved the asset profile details in the admin control panel to a dialog
|
||||||
|
|
||||||
## 1.181.2 - 21.08.2022
|
## 1.181.2 - 21.08.2022
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -8,7 +8,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
AdminData,
|
AdminData,
|
||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
AdminMarketDataDetails
|
AdminMarketDataDetails,
|
||||||
|
Filter
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import type { RequestWithUser } from '@ghostfolio/common/types';
|
import type { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
@ -22,6 +23,7 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
|
Query,
|
||||||
UseGuards
|
UseGuards
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
@ -226,7 +228,9 @@ export class AdminController {
|
|||||||
|
|
||||||
@Get('market-data')
|
@Get('market-data')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async getMarketData(): Promise<AdminMarketData> {
|
public async getMarketData(
|
||||||
|
@Query('assetSubClasses') filterByAssetSubClasses?: string
|
||||||
|
): Promise<AdminMarketData> {
|
||||||
if (
|
if (
|
||||||
!hasPermission(
|
!hasPermission(
|
||||||
this.request.user.permissions,
|
this.request.user.permissions,
|
||||||
@ -239,7 +243,18 @@ export class AdminController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.adminService.getMarketData();
|
const assetSubClasses = filterByAssetSubClasses?.split(',') ?? [];
|
||||||
|
|
||||||
|
const filters: Filter[] = [
|
||||||
|
...assetSubClasses.map((assetSubClass) => {
|
||||||
|
return <Filter>{
|
||||||
|
id: assetSubClass,
|
||||||
|
type: 'ASSET_SUB_CLASS'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.adminService.getMarketData(filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('market-data/:dataSource/:symbol')
|
@Get('market-data/:dataSource/:symbol')
|
||||||
|
@ -11,11 +11,13 @@ import {
|
|||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
AdminMarketDataDetails,
|
AdminMarketDataDetails,
|
||||||
AdminMarketDataItem,
|
AdminMarketDataItem,
|
||||||
|
Filter,
|
||||||
UniqueAsset
|
UniqueAsset
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Property } from '@prisma/client';
|
import { AssetSubClass, Prisma, Property } from '@prisma/client';
|
||||||
import { differenceInDays } from 'date-fns';
|
import { differenceInDays } from 'date-fns';
|
||||||
|
import { groupBy } from 'lodash';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
@ -63,14 +65,27 @@ export class AdminService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getMarketData(): Promise<AdminMarketData> {
|
public async getMarketData(filters?: Filter[]): Promise<AdminMarketData> {
|
||||||
|
const where: Prisma.SymbolProfileWhereInput = {};
|
||||||
|
|
||||||
|
const { ASSET_SUB_CLASS: filtersByAssetSubClass } = groupBy(
|
||||||
|
filters,
|
||||||
|
(filter) => {
|
||||||
|
return filter.type;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const marketData = await this.prismaService.marketData.groupBy({
|
const marketData = await this.prismaService.marketData.groupBy({
|
||||||
_count: true,
|
_count: true,
|
||||||
by: ['dataSource', 'symbol']
|
by: ['dataSource', 'symbol']
|
||||||
});
|
});
|
||||||
|
|
||||||
const currencyPairsToGather: AdminMarketDataItem[] =
|
let currencyPairsToGather: AdminMarketDataItem[] = [];
|
||||||
this.exchangeRateDataService
|
|
||||||
|
if (filtersByAssetSubClass) {
|
||||||
|
where.assetSubClass = AssetSubClass[filtersByAssetSubClass[0].id];
|
||||||
|
} else {
|
||||||
|
currencyPairsToGather = this.exchangeRateDataService
|
||||||
.getCurrencyPairs()
|
.getCurrencyPairs()
|
||||||
.map(({ dataSource, symbol }) => {
|
.map(({ dataSource, symbol }) => {
|
||||||
const marketDataItemCount =
|
const marketDataItemCount =
|
||||||
@ -84,17 +99,24 @@ export class AdminService {
|
|||||||
return {
|
return {
|
||||||
dataSource,
|
dataSource,
|
||||||
marketDataItemCount,
|
marketDataItemCount,
|
||||||
symbol
|
symbol,
|
||||||
|
countriesCount: 0,
|
||||||
|
sectorsCount: 0
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const symbolProfilesToGather: AdminMarketDataItem[] = (
|
const symbolProfilesToGather: AdminMarketDataItem[] = (
|
||||||
await this.prismaService.symbolProfile.findMany({
|
await this.prismaService.symbolProfile.findMany({
|
||||||
|
where,
|
||||||
orderBy: [{ symbol: 'asc' }],
|
orderBy: [{ symbol: 'asc' }],
|
||||||
select: {
|
select: {
|
||||||
_count: {
|
_count: {
|
||||||
select: { Order: true }
|
select: { Order: true }
|
||||||
},
|
},
|
||||||
|
assetClass: true,
|
||||||
|
assetSubClass: true,
|
||||||
|
countries: true,
|
||||||
dataSource: true,
|
dataSource: true,
|
||||||
Order: {
|
Order: {
|
||||||
orderBy: [{ date: 'asc' }],
|
orderBy: [{ date: 'asc' }],
|
||||||
@ -102,10 +124,14 @@ export class AdminService {
|
|||||||
take: 1
|
take: 1
|
||||||
},
|
},
|
||||||
scraperConfiguration: true,
|
scraperConfiguration: true,
|
||||||
|
sectors: true,
|
||||||
symbol: true
|
symbol: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
).map((symbolProfile) => {
|
).map((symbolProfile) => {
|
||||||
|
const countriesCount = symbolProfile.countries
|
||||||
|
? Object.keys(symbolProfile.countries).length
|
||||||
|
: 0;
|
||||||
const marketDataItemCount =
|
const marketDataItemCount =
|
||||||
marketData.find((marketDataItem) => {
|
marketData.find((marketDataItem) => {
|
||||||
return (
|
return (
|
||||||
@ -113,10 +139,17 @@ export class AdminService {
|
|||||||
marketDataItem.symbol === symbolProfile.symbol
|
marketDataItem.symbol === symbolProfile.symbol
|
||||||
);
|
);
|
||||||
})?._count ?? 0;
|
})?._count ?? 0;
|
||||||
|
const sectorsCount = symbolProfile.sectors
|
||||||
|
? Object.keys(symbolProfile.sectors).length
|
||||||
|
: 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
countriesCount,
|
||||||
marketDataItemCount,
|
marketDataItemCount,
|
||||||
|
sectorsCount,
|
||||||
activityCount: symbolProfile._count.Order,
|
activityCount: symbolProfile._count.Order,
|
||||||
|
assetClass: symbolProfile.assetClass,
|
||||||
|
assetSubClass: symbolProfile.assetSubClass,
|
||||||
dataSource: symbolProfile.dataSource,
|
dataSource: symbolProfile.dataSource,
|
||||||
date: symbolProfile.Order?.[0]?.date,
|
date: symbolProfile.Order?.[0]?.date,
|
||||||
symbol: symbolProfile.symbol
|
symbol: symbolProfile.symbol
|
||||||
|
@ -3,17 +3,27 @@ import {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit
|
OnInit,
|
||||||
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { getDateFormatString } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, getDateFormatString } from '@ghostfolio/common/helper';
|
||||||
import { UniqueAsset, User } from '@ghostfolio/common/interfaces';
|
import { Filter, UniqueAsset, User } from '@ghostfolio/common/interfaces';
|
||||||
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface';
|
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface';
|
||||||
import { DataSource, MarketData } from '@prisma/client';
|
import { AssetSubClass, DataSource } from '@prisma/client';
|
||||||
|
import { format, parseISO } from 'date-fns';
|
||||||
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { AssetProfileDialog } from './asset-profile-dialog/asset-profile-dialog.component';
|
||||||
|
import { AssetProfileDialogParams } from './asset-profile-dialog/interfaces/interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -22,11 +32,46 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
templateUrl: './admin-market-data.html'
|
templateUrl: './admin-market-data.html'
|
||||||
})
|
})
|
||||||
export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
||||||
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
|
public activeFilters: Filter[] = [];
|
||||||
|
public allFilters: Filter[] = [
|
||||||
|
AssetSubClass.BOND,
|
||||||
|
AssetSubClass.COMMODITY,
|
||||||
|
AssetSubClass.CRYPTOCURRENCY,
|
||||||
|
AssetSubClass.ETF,
|
||||||
|
AssetSubClass.MUTUALFUND,
|
||||||
|
AssetSubClass.PRECIOUS_METAL,
|
||||||
|
AssetSubClass.PRIVATE_EQUITY,
|
||||||
|
AssetSubClass.STOCK
|
||||||
|
].map((id) => {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
label: id,
|
||||||
|
type: 'ASSET_SUB_CLASS'
|
||||||
|
};
|
||||||
|
});
|
||||||
public currentDataSource: DataSource;
|
public currentDataSource: DataSource;
|
||||||
public currentSymbol: string;
|
public currentSymbol: string;
|
||||||
|
public dataSource: MatTableDataSource<AdminMarketDataItem> =
|
||||||
|
new MatTableDataSource();
|
||||||
public defaultDateFormat: string;
|
public defaultDateFormat: string;
|
||||||
public marketData: AdminMarketDataItem[] = [];
|
public deviceType: string;
|
||||||
public marketDataDetails: MarketData[] = [];
|
public displayedColumns = [
|
||||||
|
'symbol',
|
||||||
|
'dataSource',
|
||||||
|
'assetClass',
|
||||||
|
'assetSubClass',
|
||||||
|
'date',
|
||||||
|
'activityCount',
|
||||||
|
'marketDataItemCount',
|
||||||
|
'countriesCount',
|
||||||
|
'sectorsCount',
|
||||||
|
'actions'
|
||||||
|
];
|
||||||
|
public filters$ = new Subject<Filter[]>();
|
||||||
|
public isLoading = false;
|
||||||
|
public placeholder = '';
|
||||||
public user: User;
|
public user: User;
|
||||||
|
|
||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
@ -35,8 +80,29 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
|||||||
private adminService: AdminService,
|
private adminService: AdminService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
|
private deviceService: DeviceDetectorService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
private userService: UserService
|
private userService: UserService
|
||||||
) {
|
) {
|
||||||
|
this.route.queryParams
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((params) => {
|
||||||
|
if (
|
||||||
|
params['assetProfileDialog'] &&
|
||||||
|
params['dataSource'] &&
|
||||||
|
params['dateOfFirstActivity'] &&
|
||||||
|
params['symbol']
|
||||||
|
) {
|
||||||
|
this.openAssetProfileDialog({
|
||||||
|
dataSource: params['dataSource'],
|
||||||
|
dateOfFirstActivity: params['dateOfFirstActivity'],
|
||||||
|
symbol: params['symbol']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.userService.stateChanged
|
this.userService.stateChanged
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((state) => {
|
.subscribe((state) => {
|
||||||
@ -51,7 +117,31 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.fetchAdminMarketData();
|
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
|
||||||
|
|
||||||
|
this.filters$
|
||||||
|
.pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap((filters) => {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.activeFilters = filters;
|
||||||
|
this.placeholder =
|
||||||
|
this.activeFilters.length <= 0 ? $localize`Filter by...` : '';
|
||||||
|
|
||||||
|
return this.dataService.fetchAdminMarketData({
|
||||||
|
filters: this.activeFilters
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
takeUntil(this.unsubscribeSubject)
|
||||||
|
)
|
||||||
|
.subscribe(({ marketData }) => {
|
||||||
|
this.dataSource = new MatTableDataSource(marketData);
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
||||||
@ -75,28 +165,19 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
|||||||
.subscribe(() => {});
|
.subscribe(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onMarketDataChanged(withRefresh: boolean = false) {
|
public onOpenAssetProfileDialog({
|
||||||
if (withRefresh) {
|
dataSource,
|
||||||
this.fetchAdminMarketData();
|
dateOfFirstActivity,
|
||||||
this.fetchAdminMarketDataBySymbol({
|
symbol
|
||||||
dataSource: this.currentDataSource,
|
}: UniqueAsset & { dateOfFirstActivity: string }) {
|
||||||
symbol: this.currentSymbol
|
this.router.navigate([], {
|
||||||
});
|
queryParams: {
|
||||||
}
|
dataSource,
|
||||||
}
|
symbol,
|
||||||
|
assetProfileDialog: true,
|
||||||
public setCurrentProfile({ dataSource, symbol }: UniqueAsset) {
|
dateOfFirstActivity: format(parseISO(dateOfFirstActivity), DATE_FORMAT)
|
||||||
this.marketDataDetails = [];
|
}
|
||||||
|
});
|
||||||
if (this.currentSymbol === symbol) {
|
|
||||||
this.currentDataSource = undefined;
|
|
||||||
this.currentSymbol = '';
|
|
||||||
} else {
|
|
||||||
this.currentDataSource = dataSource;
|
|
||||||
this.currentSymbol = symbol;
|
|
||||||
|
|
||||||
this.fetchAdminMarketDataBySymbol({ dataSource, symbol });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
@ -104,25 +185,40 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
|||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchAdminMarketData() {
|
private openAssetProfileDialog({
|
||||||
this.dataService
|
dataSource,
|
||||||
.fetchAdminMarketData()
|
dateOfFirstActivity,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
dateOfFirstActivity: string;
|
||||||
|
symbol: string;
|
||||||
|
}) {
|
||||||
|
this.userService
|
||||||
|
.get()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(({ marketData }) => {
|
.subscribe((user) => {
|
||||||
this.marketData = marketData;
|
this.user = user;
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
const dialogRef = this.dialog.open(AssetProfileDialog, {
|
||||||
});
|
autoFocus: false,
|
||||||
}
|
data: <AssetProfileDialogParams>{
|
||||||
|
dataSource,
|
||||||
|
dateOfFirstActivity,
|
||||||
|
symbol,
|
||||||
|
deviceType: this.deviceType,
|
||||||
|
locale: this.user?.settings?.locale
|
||||||
|
},
|
||||||
|
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
|
||||||
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
|
});
|
||||||
|
|
||||||
private fetchAdminMarketDataBySymbol({ dataSource, symbol }: UniqueAsset) {
|
dialogRef
|
||||||
this.adminService
|
.afterClosed()
|
||||||
.fetchAdminMarketDataBySymbol({ dataSource, symbol })
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.subscribe(() => {
|
||||||
.subscribe(({ marketData }) => {
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
this.marketDataDetails = marketData;
|
});
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,76 +1,147 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<table class="gf-table w-100">
|
<gf-activities-filter
|
||||||
<thead>
|
[allFilters]="allFilters"
|
||||||
<tr class="mat-header-row">
|
[isLoading]="isLoading"
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Symbol</th>
|
[placeholder]="placeholder"
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Data Source</th>
|
(valueChanged)="filters$.next($event)"
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>First Activity</th>
|
></gf-activities-filter>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Activity Count</th>
|
</div>
|
||||||
<th class="mat-header-cell px-1 py-2" i18n>Historical Data</th>
|
</div>
|
||||||
<th class="mat-header-cell px-1 py-2"></th>
|
<div class="row">
|
||||||
</tr>
|
<div class="col">
|
||||||
</thead>
|
<table
|
||||||
<tbody>
|
class="gf-table w-100"
|
||||||
<ng-container *ngFor="let item of marketData; let i = index">
|
matSort
|
||||||
<tr
|
matSortActive="symbol"
|
||||||
class="cursor-pointer mat-row"
|
matSortDirection="asc"
|
||||||
(click)="setCurrentProfile({ dataSource: item.dataSource, symbol: item.symbol })"
|
mat-table
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
>
|
||||||
|
<ng-container matColumnDef="symbol">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Symbol</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
{{ element.symbol }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="dataSource">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Data Source</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
{{ element.dataSource }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="assetClass">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Asset Class</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
{{ element.assetClass }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="assetSubClass">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Asset Sub Class</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
{{ element.assetSubClass }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="date">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>First Activity</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
{{ (element.date | date: defaultDateFormat) ?? '' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="activityCount">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Activity Count</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
||||||
|
{{ element.activityCount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="marketDataItemCount">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Historical Data</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
||||||
|
{{ element.marketDataItemCount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="countriesCount">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Countries Count</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
||||||
|
{{ element.countriesCount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="sectorsCount">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
|
<ng-container i18n>Sectors Count</ng-container>
|
||||||
|
</th>
|
||||||
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
|
||||||
|
{{ element.sectorsCount }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th>
|
||||||
|
<td *matCellDef="let element" class="px-1 text-center" mat-cell>
|
||||||
|
<button
|
||||||
|
class="mx-1 no-min-width px-2"
|
||||||
|
mat-button
|
||||||
|
[matMenuTriggerFor]="accountMenu"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
>
|
>
|
||||||
<td class="mat-cell px-1 py-2">{{ item.symbol }}</td>
|
<ion-icon name="ellipsis-vertical"></ion-icon>
|
||||||
<td class="mat-cell px-1 py-2">{{ item.dataSource }}</td>
|
</button>
|
||||||
<td class="mat-cell px-1 py-2">
|
<mat-menu #accountMenu="matMenu" xPosition="before">
|
||||||
{{ (item.date | date: defaultDateFormat) ?? '' }}
|
<button
|
||||||
</td>
|
mat-menu-item
|
||||||
<td class="mat-cell px-1 py-2">{{ item.activityCount }}</td>
|
(click)="onGatherSymbol({dataSource: element.dataSource, symbol: element.symbol})"
|
||||||
<td class="mat-cell px-1 py-2">{{ item.marketDataItemCount }}</td>
|
>
|
||||||
<td class="mat-cell px-1 py-2">
|
<ng-container i18n>Gather Data</ng-container>
|
||||||
<button
|
</button>
|
||||||
class="mx-1 no-min-width px-2"
|
<button
|
||||||
mat-button
|
mat-menu-item
|
||||||
[matMenuTriggerFor]="accountMenu"
|
(click)="onGatherProfileDataBySymbol({dataSource: element.dataSource, symbol: element.symbol})"
|
||||||
(click)="$event.stopPropagation()"
|
>
|
||||||
>
|
<ng-container i18n>Gather Profile Data</ng-container>
|
||||||
<ion-icon name="ellipsis-vertical"></ion-icon>
|
</button>
|
||||||
</button>
|
<button
|
||||||
<mat-menu #accountMenu="matMenu" xPosition="before">
|
mat-menu-item
|
||||||
<button
|
[disabled]="element.activityCount !== 0"
|
||||||
mat-menu-item
|
(click)="onDeleteProfileData({dataSource: element.dataSource, symbol: element.symbol})"
|
||||||
(click)="onGatherSymbol({dataSource: item.dataSource, symbol: item.symbol})"
|
>
|
||||||
>
|
<ng-container i18n>Delete</ng-container>
|
||||||
<ng-container i18n>Gather Data</ng-container>
|
</button>
|
||||||
</button>
|
</mat-menu>
|
||||||
<button
|
</td>
|
||||||
mat-menu-item
|
</ng-container>
|
||||||
(click)="onGatherProfileDataBySymbol({dataSource: item.dataSource, symbol: item.symbol})"
|
|
||||||
>
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||||
<ng-container i18n>Gather Profile Data</ng-container>
|
<tr
|
||||||
</button>
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
<button
|
class="cursor-pointer"
|
||||||
mat-menu-item
|
mat-row
|
||||||
[disabled]="item.activityCount !== 0"
|
(click)="onOpenAssetProfileDialog({ dateOfFirstActivity: row.date, dataSource: row.dataSource, symbol: row.symbol })"
|
||||||
(click)="onDeleteProfileData({dataSource: item.dataSource, symbol: item.symbol})"
|
></tr>
|
||||||
>
|
|
||||||
<ng-container i18n>Delete</ng-container>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr *ngIf="currentSymbol === item.symbol" class="mat-row">
|
|
||||||
<td class="p-1" colspan="6">
|
|
||||||
<gf-admin-market-data-detail
|
|
||||||
[dataSource]="item.dataSource"
|
|
||||||
[dateOfFirstActivity]="item.date"
|
|
||||||
[locale]="user?.settings?.locale"
|
|
||||||
[marketData]="marketDataDetails"
|
|
||||||
[symbol]="item.symbol"
|
|
||||||
(marketDataChanged)="onMarketDataChanged($event)"
|
|
||||||
></gf-admin-market-data-detail>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,17 +2,23 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module';
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { GfActivitiesFilterModule } from '@ghostfolio/ui/activities-filter/activities-filter.module';
|
||||||
|
|
||||||
import { AdminMarketDataComponent } from './admin-market-data.component';
|
import { AdminMarketDataComponent } from './admin-market-data.component';
|
||||||
|
import { GfAssetProfileDialogModule } from './asset-profile-dialog/assset-profile-dialog.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AdminMarketDataComponent],
|
declarations: [AdminMarketDataComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
GfAdminMarketDataDetailModule,
|
GfActivitiesFilterModule,
|
||||||
|
GfAssetProfileDialogModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatMenuModule
|
MatMenuModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatTableModule
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.mat-dialog-content {
|
||||||
|
max-height: unset;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Inject,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
|
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||||
|
import { MarketData } from '@prisma/client';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { AssetProfileDialogParams } from './interfaces/interfaces';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
host: { class: 'd-flex flex-column h-100' },
|
||||||
|
selector: 'gf-asset-profile-dialog',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
templateUrl: 'asset-profile-dialog.html',
|
||||||
|
styleUrls: ['./asset-profile-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class AssetProfileDialog implements OnDestroy, OnInit {
|
||||||
|
public marketDataDetails: MarketData[] = [];
|
||||||
|
|
||||||
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private adminService: AdminService,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
public dialogRef: MatDialogRef<AssetProfileDialog>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: AssetProfileDialogParams
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClose(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onMarketDataChanged(withRefresh: boolean = false) {
|
||||||
|
if (withRefresh) {
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy() {
|
||||||
|
this.unsubscribeSubject.next();
|
||||||
|
this.unsubscribeSubject.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchAdminMarketDataBySymbol({ dataSource, symbol }: UniqueAsset) {
|
||||||
|
this.adminService
|
||||||
|
.fetchAdminMarketDataBySymbol({ dataSource, symbol })
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(({ marketData }) => {
|
||||||
|
this.marketDataDetails = marketData;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialize() {
|
||||||
|
this.fetchAdminMarketDataBySymbol({
|
||||||
|
dataSource: this.data.dataSource,
|
||||||
|
symbol: this.data.symbol
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<gf-dialog-header
|
||||||
|
mat-dialog-title
|
||||||
|
position="center"
|
||||||
|
[deviceType]="data.deviceType"
|
||||||
|
[title]="data.symbol"
|
||||||
|
(closeButtonClicked)="onClose()"
|
||||||
|
></gf-dialog-header>
|
||||||
|
|
||||||
|
<div class="flex-grow-1" mat-dialog-content>
|
||||||
|
<gf-admin-market-data-detail
|
||||||
|
[dataSource]="data.dataSource"
|
||||||
|
[dateOfFirstActivity]="data.dateOfFirstActivity"
|
||||||
|
[locale]="data.locale"
|
||||||
|
[marketData]="marketDataDetails"
|
||||||
|
[symbol]="data.symbol"
|
||||||
|
(marketDataChanged)="onMarketDataChanged($event)"
|
||||||
|
></gf-admin-market-data-detail>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gf-dialog-footer
|
||||||
|
mat-dialog-actions
|
||||||
|
[deviceType]="data.deviceType"
|
||||||
|
(closeButtonClicked)="onClose()"
|
||||||
|
></gf-dialog-footer>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module';
|
||||||
|
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
||||||
|
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||||
|
|
||||||
|
import { AssetProfileDialog } from './asset-profile-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AssetProfileDialog],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
GfAdminMarketDataDetailModule,
|
||||||
|
GfDialogFooterModule,
|
||||||
|
GfDialogHeaderModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatDialogModule
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class GfAssetProfileDialogModule {}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { DataSource } from '@prisma/client';
|
||||||
|
|
||||||
|
export interface AssetProfileDialogParams {
|
||||||
|
dateOfFirstActivity: string;
|
||||||
|
dataSource: DataSource;
|
||||||
|
deviceType: string;
|
||||||
|
locale: string;
|
||||||
|
symbol: string;
|
||||||
|
}
|
@ -47,7 +47,7 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
|||||||
private settingsStorageService: SettingsStorageService,
|
private settingsStorageService: SettingsStorageService,
|
||||||
private userService: UserService
|
private userService: UserService
|
||||||
) {
|
) {
|
||||||
route.queryParams
|
this.route.queryParams
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((params) => {
|
.subscribe((params) => {
|
||||||
if (
|
if (
|
||||||
|
@ -21,6 +21,8 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
export class HomeMarketComponent implements OnDestroy, OnInit {
|
export class HomeMarketComponent implements OnDestroy, OnInit {
|
||||||
public benchmarks: Benchmark[];
|
public benchmarks: Benchmark[];
|
||||||
public fearAndGreedIndex: number;
|
public fearAndGreedIndex: number;
|
||||||
|
public fearLabel = $localize`Fear`;
|
||||||
|
public greedLabel = $localize`Greed`;
|
||||||
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
||||||
public historicalData: HistoricalDataItem[];
|
public historicalData: HistoricalDataItem[];
|
||||||
public info: InfoItem;
|
public info: InfoItem;
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
class="mb-3"
|
class="mb-3"
|
||||||
symbol="Fear & Greed Index"
|
symbol="Fear & Greed Index"
|
||||||
yMax="100"
|
yMax="100"
|
||||||
yMaxLabel="Greed"
|
|
||||||
yMin="0"
|
yMin="0"
|
||||||
yMinLabel="Fear"
|
|
||||||
[historicalDataItems]="historicalData"
|
[historicalDataItems]="historicalData"
|
||||||
[locale]="user?.settings?.locale"
|
[locale]="user?.settings?.locale"
|
||||||
[showXAxis]="true"
|
[showXAxis]="true"
|
||||||
[showYAxis]="true"
|
[showYAxis]="true"
|
||||||
|
[yMaxLabel]="greedLabel"
|
||||||
|
[yMinLabel]="fearLabel"
|
||||||
></gf-line-chart>
|
></gf-line-chart>
|
||||||
<gf-fear-and-greed-index
|
<gf-fear-and-greed-index
|
||||||
class="d-flex justify-content-center"
|
class="d-flex justify-content-center"
|
||||||
|
@ -122,7 +122,7 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy {
|
|||||||
data: this.investments.map((position) => {
|
data: this.investments.map((position) => {
|
||||||
return position.investment;
|
return position.investment;
|
||||||
}),
|
}),
|
||||||
label: 'Investment',
|
label: $localize`Deposit`,
|
||||||
segment: {
|
segment: {
|
||||||
borderColor: (context: unknown) =>
|
borderColor: (context: unknown) =>
|
||||||
this.isInFuture(
|
this.isInFuture(
|
||||||
|
@ -54,8 +54,8 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
};
|
};
|
||||||
public period = 'current';
|
public period = 'current';
|
||||||
public periodOptions: ToggleOption[] = [
|
public periodOptions: ToggleOption[] = [
|
||||||
{ label: 'Initial', value: 'original' },
|
{ label: $localize`Initial`, value: 'original' },
|
||||||
{ label: 'Current', value: 'current' }
|
{ label: $localize`Current`, value: 'current' }
|
||||||
];
|
];
|
||||||
public placeholder = '';
|
public placeholder = '';
|
||||||
public portfolioDetails: PortfolioDetails;
|
public portfolioDetails: PortfolioDetails;
|
||||||
@ -85,7 +85,6 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
public user: User;
|
public user: User;
|
||||||
|
|
||||||
private readonly SEARCH_PLACEHOLDER = 'Filter by account or tag...';
|
|
||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@ -133,7 +132,9 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.activeFilters = filters;
|
this.activeFilters = filters;
|
||||||
this.placeholder =
|
this.placeholder =
|
||||||
this.activeFilters.length <= 0 ? this.SEARCH_PLACEHOLDER : '';
|
this.activeFilters.length <= 0
|
||||||
|
? $localize`Filter by account or tag...`
|
||||||
|
: '';
|
||||||
|
|
||||||
return this.dataService.fetchPortfolioDetails({
|
return this.dataService.fetchPortfolioDetails({
|
||||||
filters: this.activeFilters
|
filters: this.activeFilters
|
||||||
|
@ -26,8 +26,8 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
|||||||
public investmentsByMonth: InvestmentItem[];
|
public investmentsByMonth: InvestmentItem[];
|
||||||
public mode: GroupBy;
|
public mode: GroupBy;
|
||||||
public modeOptions: ToggleOption[] = [
|
public modeOptions: ToggleOption[] = [
|
||||||
{ label: 'Monthly', value: 'month' },
|
{ label: $localize`Monthly`, value: 'month' },
|
||||||
{ label: 'Accumulating', value: undefined }
|
{ label: $localize`Accumulating`, value: undefined }
|
||||||
];
|
];
|
||||||
public top3: Position[];
|
public top3: Position[];
|
||||||
public user: User;
|
public user: User;
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<mat-card class="mb-3">
|
<mat-card class="mb-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title class="align-items-center d-flex" i18n
|
<mat-card-title class="align-items-center d-flex" i18n
|
||||||
>Top 3</mat-card-title
|
>Top</mat-card-title
|
||||||
>
|
>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
<mat-card class="mb-3">
|
<mat-card class="mb-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title class="align-items-center d-flex" i18n
|
<mat-card-title class="align-items-center d-flex" i18n
|
||||||
>Bottom 3</mat-card-title
|
>Bottom</mat-card-title
|
||||||
>
|
>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
|
@ -133,8 +133,32 @@ export class DataService {
|
|||||||
return this.http.get<AdminData>('/api/v1/admin');
|
return this.http.get<AdminData>('/api/v1/admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchAdminMarketData() {
|
public fetchAdminMarketData({ filters }: { filters?: Filter[] }) {
|
||||||
return this.http.get<AdminMarketData>('/api/v1/admin/market-data');
|
let params = new HttpParams();
|
||||||
|
|
||||||
|
if (filters?.length > 0) {
|
||||||
|
const { ASSET_SUB_CLASS: filtersByAssetSubClass } = groupBy(
|
||||||
|
filters,
|
||||||
|
(filter) => {
|
||||||
|
return filter.type;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filtersByAssetSubClass) {
|
||||||
|
params = params.append(
|
||||||
|
'assetSubClasses',
|
||||||
|
filtersByAssetSubClass
|
||||||
|
.map(({ id }) => {
|
||||||
|
return id;
|
||||||
|
})
|
||||||
|
.join(',')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<AdminMarketData>('/api/v1/admin/market-data', {
|
||||||
|
params
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteAccess(aId: string) {
|
public deleteAccess(aId: string) {
|
||||||
|
@ -186,7 +186,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">55</context>
|
<context context-type="linenumber">132</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
|
||||||
@ -222,7 +222,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">7</context>
|
<context context-type="linenumber">24</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/positions-table/positions-table.component.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/positions-table/positions-table.component.html</context>
|
||||||
@ -242,7 +242,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">8</context>
|
<context context-type="linenumber">33</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html</context>
|
||||||
@ -406,7 +406,7 @@
|
|||||||
<target state="translated">Erste Aktivität</target>
|
<target state="translated">Erste Aktivität</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">9</context>
|
<context context-type="linenumber">60</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ced0954194f098201837bb03b32441e4991b5193" datatype="html">
|
<trans-unit id="ced0954194f098201837bb03b32441e4991b5193" datatype="html">
|
||||||
@ -414,7 +414,7 @@
|
|||||||
<target state="translated">Anzahl Aktivitäten</target>
|
<target state="translated">Anzahl Aktivitäten</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">10</context>
|
<context context-type="linenumber">69</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
||||||
@ -426,7 +426,7 @@
|
|||||||
<target state="translated">Historische Daten</target>
|
<target state="translated">Historische Daten</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">78</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="f835caf68bff562ddd23556a651e834d5af3380b" datatype="html">
|
<trans-unit id="f835caf68bff562ddd23556a651e834d5af3380b" datatype="html">
|
||||||
@ -434,7 +434,7 @@
|
|||||||
<target state="translated">Daten einholen</target>
|
<target state="translated">Daten einholen</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">42</context>
|
<context context-type="linenumber">119</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="912825160188860007" datatype="html">
|
<trans-unit id="912825160188860007" datatype="html">
|
||||||
@ -514,7 +514,7 @@
|
|||||||
<target state="translated">Profildaten einholen</target>
|
<target state="translated">Profildaten einholen</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">48</context>
|
<context context-type="linenumber">125</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
||||||
@ -1709,16 +1709,16 @@
|
|||||||
<context context-type="linenumber">10</context>
|
<context context-type="linenumber">10</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="50167734b0d4b546842e9f548eb89480a72d0fa1" datatype="html">
|
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
|
||||||
<source>Top 3</source>
|
<source>Top</source>
|
||||||
<target state="translated">Gewinner</target>
|
<target state="translated">Gewinner</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
||||||
<context context-type="linenumber">55</context>
|
<context context-type="linenumber">55</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="d4fb5bf78e78dd3c791bcec0dcddddb1fdc9d51a" datatype="html">
|
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
|
||||||
<source>Bottom 3</source>
|
<source>Bottom</source>
|
||||||
<target state="translated">Verlierer</target>
|
<target state="translated">Verlierer</target>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
||||||
@ -1988,6 +1988,10 @@
|
|||||||
<trans-unit id="584c9433705e9bfdd2e7a9f0192690f453d36196" datatype="html">
|
<trans-unit id="584c9433705e9bfdd2e7a9f0192690f453d36196" datatype="html">
|
||||||
<source>Asset Class</source>
|
<source>Asset Class</source>
|
||||||
<target state="translated">Anlageklasse</target>
|
<target state="translated">Anlageklasse</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">42</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
||||||
<context context-type="linenumber">145</context>
|
<context context-type="linenumber">145</context>
|
||||||
@ -2380,6 +2384,10 @@
|
|||||||
<trans-unit id="27fe3d097c64eaec7ff564358f80fb7ba795f484" datatype="html">
|
<trans-unit id="27fe3d097c64eaec7ff564358f80fb7ba795f484" datatype="html">
|
||||||
<source>Asset Sub Class</source>
|
<source>Asset Sub Class</source>
|
||||||
<target state="translated">Anlageunterklasse</target>
|
<target state="translated">Anlageunterklasse</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">51</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
||||||
<context context-type="linenumber">154</context>
|
<context context-type="linenumber">154</context>
|
||||||
@ -2493,6 +2501,122 @@
|
|||||||
<context context-type="linenumber">171</context>
|
<context context-type="linenumber">171</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
|
||||||
|
<source>Projected Total Amount</source>
|
||||||
|
<target state="translated">Geschätzter Gesamtbetrag</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.html</context>
|
||||||
|
<context context-type="linenumber">44</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2937311350146031865" datatype="html">
|
||||||
|
<source>Initial</source>
|
||||||
|
<target state="new">Beginn</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">57</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6603000223840533819" datatype="html">
|
||||||
|
<source>Current</source>
|
||||||
|
<target state="translated">Aktuell</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">58</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6762743264882388498" datatype="html">
|
||||||
|
<source>Monthly</source>
|
||||||
|
<target state="translated">Monatlich</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">29</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1975246224413290232" datatype="html">
|
||||||
|
<source>Accumulating</source>
|
||||||
|
<target state="translated">Aufsummiert</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">30</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5213771062241898526" datatype="html">
|
||||||
|
<source>Deposit</source>
|
||||||
|
<target state="translated">Einlage</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context>
|
||||||
|
<context context-type="linenumber">125</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">276</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3441715041566940420" datatype="html">
|
||||||
|
<source>Interest</source>
|
||||||
|
<target state="translated">Verzinsung</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">286</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1054498214311181686" datatype="html">
|
||||||
|
<source>Savings</source>
|
||||||
|
<target state="new">Ersparnisse</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">296</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="aad5320acd7453f912bc8714e72c2fa71e8ab18e" datatype="html">
|
||||||
|
<source>Countries Count</source>
|
||||||
|
<target state="translated">Anzahl Länder</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">87</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8511b16abcf065252b350d64e337ba2447db3ffb" datatype="html">
|
||||||
|
<source>Sectors Count</source>
|
||||||
|
<target state="translated">Anzahl Sektoren</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">96</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5486880308148746399" datatype="html">
|
||||||
|
<source>Fear</source>
|
||||||
|
<target state="translated">Angst</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/home-market/home-market.component.ts</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6844699413925472826" datatype="html">
|
||||||
|
<source>Greed</source>
|
||||||
|
<target state="translated">Gier</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/home-market/home-market.component.ts</context>
|
||||||
|
<context context-type="linenumber">25</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4550487415324294802" datatype="html">
|
||||||
|
<source>Filter by...</source>
|
||||||
|
<target state="translated">Filtern nach...</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
|
||||||
|
<context context-type="linenumber">129</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2078421919111943467" datatype="html">
|
||||||
|
<source>Filter by account or tag...</source>
|
||||||
|
<target state="translated">Filtern nach Konto oder Tag...</target>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">136</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -174,7 +174,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">55</context>
|
<context context-type="linenumber">132</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-users/admin-users.html</context>
|
||||||
@ -207,7 +207,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">7</context>
|
<context context-type="linenumber">24</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/positions-table/positions-table.component.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/positions-table/positions-table.component.html</context>
|
||||||
@ -226,7 +226,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">8</context>
|
<context context-type="linenumber">33</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html</context>
|
||||||
@ -375,14 +375,14 @@
|
|||||||
<source>First Activity</source>
|
<source>First Activity</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">9</context>
|
<context context-type="linenumber">60</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ced0954194f098201837bb03b32441e4991b5193" datatype="html">
|
<trans-unit id="ced0954194f098201837bb03b32441e4991b5193" datatype="html">
|
||||||
<source>Activity Count</source>
|
<source>Activity Count</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">10</context>
|
<context context-type="linenumber">69</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
||||||
@ -393,14 +393,14 @@
|
|||||||
<source>Historical Data</source>
|
<source>Historical Data</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">78</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="f835caf68bff562ddd23556a651e834d5af3380b" datatype="html">
|
<trans-unit id="f835caf68bff562ddd23556a651e834d5af3380b" datatype="html">
|
||||||
<source>Gather Data</source>
|
<source>Gather Data</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">42</context>
|
<context context-type="linenumber">119</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="912825160188860007" datatype="html">
|
<trans-unit id="912825160188860007" datatype="html">
|
||||||
@ -470,7 +470,7 @@
|
|||||||
<source>Gather Profile Data</source>
|
<source>Gather Profile Data</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
<context context-type="linenumber">48</context>
|
<context context-type="linenumber">125</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/admin-overview/admin-overview.html</context>
|
||||||
@ -1534,15 +1534,15 @@
|
|||||||
<context context-type="linenumber">10</context>
|
<context context-type="linenumber">10</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="50167734b0d4b546842e9f548eb89480a72d0fa1" datatype="html">
|
<trans-unit id="6ae1c94f6bad274424f97e9bc8766242c1577447" datatype="html">
|
||||||
<source>Top 3</source>
|
<source>Top</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
||||||
<context context-type="linenumber">55</context>
|
<context context-type="linenumber">55</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="d4fb5bf78e78dd3c791bcec0dcddddb1fdc9d51a" datatype="html">
|
<trans-unit id="6723d5c967329a3ac75524cf0c1af5ced022b9a3" datatype="html">
|
||||||
<source>Bottom 3</source>
|
<source>Bottom</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.html</context>
|
||||||
<context context-type="linenumber">91</context>
|
<context context-type="linenumber">91</context>
|
||||||
@ -1781,6 +1781,10 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="584c9433705e9bfdd2e7a9f0192690f453d36196" datatype="html">
|
<trans-unit id="584c9433705e9bfdd2e7a9f0192690f453d36196" datatype="html">
|
||||||
<source>Asset Class</source>
|
<source>Asset Class</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">42</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
||||||
<context context-type="linenumber">145</context>
|
<context context-type="linenumber">145</context>
|
||||||
@ -2108,6 +2112,10 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="27fe3d097c64eaec7ff564358f80fb7ba795f484" datatype="html">
|
<trans-unit id="27fe3d097c64eaec7ff564358f80fb7ba795f484" datatype="html">
|
||||||
<source>Asset Sub Class</source>
|
<source>Asset Sub Class</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">51</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
<context context-type="sourcefile">apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html</context>
|
||||||
<context context-type="linenumber">154</context>
|
<context context-type="linenumber">154</context>
|
||||||
@ -2227,6 +2235,108 @@
|
|||||||
<context context-type="linenumber">145</context>
|
<context context-type="linenumber">145</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="e34e2478d2d30c9d01758d01b7212411171b9bd5" datatype="html">
|
||||||
|
<source>Projected Total Amount</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.html</context>
|
||||||
|
<context context-type="linenumber">44</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1054498214311181686" datatype="html">
|
||||||
|
<source>Savings</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">296</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1975246224413290232" datatype="html">
|
||||||
|
<source>Accumulating</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">30</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2937311350146031865" datatype="html">
|
||||||
|
<source>Initial</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">57</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3441715041566940420" datatype="html">
|
||||||
|
<source>Interest</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">286</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5213771062241898526" datatype="html">
|
||||||
|
<source>Deposit</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/investment-chart/investment-chart.component.ts</context>
|
||||||
|
<context context-type="linenumber">125</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">libs/ui/src/lib/fire-calculator/fire-calculator.component.ts</context>
|
||||||
|
<context context-type="linenumber">276</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6603000223840533819" datatype="html">
|
||||||
|
<source>Current</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">58</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6762743264882388498" datatype="html">
|
||||||
|
<source>Monthly</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">29</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8511b16abcf065252b350d64e337ba2447db3ffb" datatype="html">
|
||||||
|
<source>Sectors Count</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">96</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="aad5320acd7453f912bc8714e72c2fa71e8ab18e" datatype="html">
|
||||||
|
<source>Countries Count</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.html</context>
|
||||||
|
<context context-type="linenumber">87</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5486880308148746399" datatype="html">
|
||||||
|
<source>Fear</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/home-market/home-market.component.ts</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6844699413925472826" datatype="html">
|
||||||
|
<source>Greed</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/home-market/home-market.component.ts</context>
|
||||||
|
<context context-type="linenumber">25</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2078421919111943467" datatype="html">
|
||||||
|
<source>Filter by account or tag...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts</context>
|
||||||
|
<context context-type="linenumber">136</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4550487415324294802" datatype="html">
|
||||||
|
<source>Filter by...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">apps/client/src/app/components/admin-market-data/admin-market-data.component.ts</context>
|
||||||
|
<context context-type="linenumber">129</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
@ -1,12 +1,16 @@
|
|||||||
import { DataSource } from '@prisma/client';
|
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
|
||||||
|
|
||||||
export interface AdminMarketData {
|
export interface AdminMarketData {
|
||||||
marketData: AdminMarketDataItem[];
|
marketData: AdminMarketDataItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminMarketDataItem {
|
export interface AdminMarketDataItem {
|
||||||
|
assetClass?: AssetClass;
|
||||||
|
assetSubClass?: AssetSubClass;
|
||||||
|
countriesCount: number;
|
||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
date?: Date;
|
date?: Date;
|
||||||
marketDataItemCount?: number;
|
marketDataItemCount: number;
|
||||||
|
sectorsCount: number;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export interface Filter {
|
export interface Filter {
|
||||||
id: string;
|
id: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'ACCOUNT' | 'ASSET_CLASS' | 'SYMBOL' | 'TAG';
|
type: 'ACCOUNT' | 'ASSET_CLASS' | 'ASSET_SUB_CLASS' | 'SYMBOL' | 'TAG';
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,14 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<gf-value
|
<gf-value
|
||||||
label="Projected Total Amount"
|
i18n
|
||||||
size="large"
|
size="large"
|
||||||
[currency]="currency"
|
[currency]="currency"
|
||||||
[isCurrency]="true"
|
[isCurrency]="true"
|
||||||
[locale]="locale"
|
[locale]="locale"
|
||||||
[value]="projectedTotalAmount"
|
[value]="projectedTotalAmount"
|
||||||
></gf-value>
|
>Projected Total Amount</gf-value
|
||||||
|
>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9 text-center">
|
<div class="col-md-9 text-center">
|
||||||
|
@ -273,7 +273,7 @@ export class FireCalculatorComponent
|
|||||||
const datasetDeposit = {
|
const datasetDeposit = {
|
||||||
backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
|
backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
|
||||||
data: [],
|
data: [],
|
||||||
label: 'Deposit'
|
label: $localize`Deposit`
|
||||||
};
|
};
|
||||||
|
|
||||||
const datasetInterest = {
|
const datasetInterest = {
|
||||||
@ -283,7 +283,7 @@ export class FireCalculatorComponent
|
|||||||
.lighten(0.5)
|
.lighten(0.5)
|
||||||
.hex(),
|
.hex(),
|
||||||
data: [],
|
data: [],
|
||||||
label: 'Interest'
|
label: $localize`Interest`
|
||||||
};
|
};
|
||||||
|
|
||||||
const datasetSavings = {
|
const datasetSavings = {
|
||||||
@ -293,7 +293,7 @@ export class FireCalculatorComponent
|
|||||||
.lighten(0.25)
|
.lighten(0.25)
|
||||||
.hex(),
|
.hex(),
|
||||||
data: [],
|
data: [],
|
||||||
label: 'Savings'
|
label: $localize`Savings`
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let period = 1; period <= t; period++) {
|
for (let period = 1; period <= t; period++) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "1.181.2",
|
"version": "1.183.0",
|
||||||
"homepage": "https://ghostfol.io",
|
"homepage": "https://ghostfol.io",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Reference in New Issue
Block a user