Compare commits

..

5 Commits

Author SHA1 Message Date
255af6a6e9 Release 1.132.1 (#809) 2022-04-06 22:02:40 +02:00
795a6a6799 Release 1.132.0 (#808) 2022-04-06 21:23:20 +02:00
2a854e2574 Various improvements (#807) 2022-04-06 21:21:53 +02:00
52d113e71f Feature/improve label of average price (#805)
* Improve label

* Update changelog
2022-04-06 18:02:21 +02:00
204c7360c3 Feature/prepare for localized date format (#803)
* Support localized date and number format

* Update changelog
2022-04-05 21:02:07 +02:00
24 changed files with 233 additions and 75 deletions

View File

@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.132.1 - 06.04.2022
### Fixed
- Fixed an issue with percentages in the value component
## 1.132.0 - 06.04.2022
### Added
- Added support for localization (date and number format) in user settings
### Changed
- Improved the label of the average price from _Ø Buy Price_ to _Average Unit Price_
## 1.131.1 - 04.04.2022
### Fixed

View File

@ -320,6 +320,16 @@ export class PortfolioController {
public async getSummary(
@Headers('impersonation-id') impersonationId
): Promise<PortfolioSummary> {
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic'
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
let summary = await this.portfolioServiceStrategy
.get()
.getSummary(impersonationId);

View File

@ -1,5 +1,6 @@
export interface UserSettings {
emergencyFund?: number;
locale?: string;
isNewCalculationEngine?: boolean;
isRestrictedView?: boolean;
}

View File

@ -1,4 +1,4 @@
import { IsBoolean, IsNumber, IsOptional } from 'class-validator';
import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator';
export class UpdateUserSettingDto {
@IsNumber()
@ -12,4 +12,8 @@ export class UpdateUserSettingDto {
@IsBoolean()
@IsOptional()
isRestrictedView?: boolean;
@IsString()
@IsOptional()
locale?: string;
}

View File

@ -2,17 +2,14 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.ser
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { PROPERTY_IS_READ_ONLY_MODE } from '@ghostfolio/common/config';
import { User } from '@ghostfolio/common/interfaces';
import {
hasPermission,
hasRole,
permissions
} from '@ghostfolio/common/permissions';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Body,
Controller,
Delete,
Get,
Headers,
HttpException,
Inject,
Param,
@ -63,8 +60,13 @@ export class UserController {
@Get()
@UseGuards(AuthGuard('jwt'))
public async getUser(@Param('id') id: string): Promise<User> {
return this.userService.getUser(this.request.user);
public async getUser(
@Headers('accept-language') acceptLanguage: string
): Promise<User> {
return this.userService.getUser(
this.request.user,
acceptLanguage?.split(',')?.[0]
);
}
@Post()
@ -118,7 +120,7 @@ export class UserController {
};
for (const key in userSettings) {
if (userSettings[key] === false) {
if (userSettings[key] === false || userSettings[key] === null) {
delete userSettings[key];
}
}

View File

@ -33,14 +33,17 @@ export class UserService {
private readonly subscriptionService: SubscriptionService
) {}
public async getUser({
Account,
alias,
id,
permissions,
Settings,
subscription
}: UserWithSettings): Promise<IUser> {
public async getUser(
{
Account,
alias,
id,
permissions,
Settings,
subscription
}: UserWithSettings,
aLocale = locale
): Promise<IUser> {
const access = await this.prismaService.access.findMany({
include: {
User: true
@ -63,8 +66,8 @@ export class UserService {
accounts: Account,
settings: {
...(<UserSettings>Settings.settings),
locale,
baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY,
locale: (<UserSettings>Settings.settings)?.locale ?? aLocale,
viewMode: Settings?.viewMode ?? ViewMode.DEFAULT
}
};

View File

@ -1,16 +1,14 @@
import {
DEFAULT_DATE_FORMAT,
DEFAULT_DATE_FORMAT_MONTH_YEAR
} from '@ghostfolio/common/config';
import { DEFAULT_DATE_FORMAT_MONTH_YEAR } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper';
export const DateFormats = {
display: {
dateInput: DEFAULT_DATE_FORMAT,
dateInput: getDateFormatString(),
monthYearLabel: DEFAULT_DATE_FORMAT_MONTH_YEAR,
dateA11yLabel: DEFAULT_DATE_FORMAT,
dateA11yLabel: getDateFormatString(),
monthYearA11yLabel: DEFAULT_DATE_FORMAT_MONTH_YEAR
},
parse: {
dateInput: DEFAULT_DATE_FORMAT
dateInput: getDateFormatString()
}
};

View File

@ -8,8 +8,11 @@ import {
Output
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper';
import {
DATE_FORMAT,
getDateFormatString,
getLocale
} from '@ghostfolio/common/helper';
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface';
import { DataSource, MarketData } from '@prisma/client';
import {
@ -35,13 +38,14 @@ import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data-
export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
@Input() dataSource: DataSource;
@Input() dateOfFirstActivity: string;
@Input() locale = getLocale();
@Input() marketData: MarketData[];
@Input() symbol: string;
@Output() marketDataChanged = new EventEmitter<boolean>();
public days = Array(31);
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public defaultDateFormat: string;
public deviceType: string;
public historicalDataItems: LineChartItem[];
public marketDataByMonth: {
@ -62,6 +66,8 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
public ngOnInit() {}
public ngOnChanges() {
this.defaultDateFormat = getDateFormatString(this.locale);
this.historicalDataItems = this.marketData.map((marketDataItem) => {
return {
date: format(marketDataItem.date, DATE_FORMAT),

View File

@ -7,8 +7,9 @@ import {
} from '@angular/core';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { UniqueAsset, User } from '@ghostfolio/common/interfaces';
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface';
import { DataSource, MarketData } from '@prisma/client';
import { Subject } from 'rxjs';
@ -23,9 +24,10 @@ import { takeUntil } from 'rxjs/operators';
export class AdminMarketDataComponent implements OnDestroy, OnInit {
public currentDataSource: DataSource;
public currentSymbol: string;
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public defaultDateFormat: string;
public marketData: AdminMarketDataItem[] = [];
public marketDataDetails: MarketData[] = [];
public user: User;
private unsubscribeSubject = new Subject<void>();
@ -35,8 +37,21 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
public constructor(
private adminService: AdminService,
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService
) {}
private dataService: DataService,
private userService: UserService
) {
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.defaultDateFormat = getDateFormatString(
this.user.settings.locale
);
}
});
}
/**
* Initializes the controller

View File

@ -65,6 +65,7 @@
<gf-admin-market-data-detail
[dataSource]="item.dataSource"
[dateOfFirstActivity]="item.date"
[locale]="user?.settings?.locale"
[marketData]="marketDataDetails"
[symbol]="item.symbol"
(marketDataChanged)="onMarketDataChanged($event)"

View File

@ -5,7 +5,6 @@ import { CacheService } from '@ghostfolio/client/services/cache.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
DEFAULT_DATE_FORMAT,
PROPERTY_COUPONS,
PROPERTY_CURRENCIES,
PROPERTY_IS_READ_ONLY_MODE,
@ -35,7 +34,6 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
public customCurrencies: string[];
public dataGatheringInProgress: boolean;
public dataGatheringProgress: number;
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public exchangeRates: { label1: string; label2: string; value: number }[];
public hasPermissionForSubscription: boolean;
public hasPermissionForSystemMessage: boolean;

View File

@ -7,6 +7,10 @@ import {
OnInit,
ViewChild
} from '@angular/core';
import {
getNumberFormatDecimal,
getNumberFormatGroup
} from '@ghostfolio/common/helper';
import {
PortfolioPerformance,
ResponseError
@ -50,13 +54,14 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
this.unit = this.baseCurrency;
new CountUp('value', this.performance?.currentValue, {
decimal: getNumberFormatDecimal(this.locale),
decimalPlaces:
this.deviceType === 'mobile' &&
this.performance?.currentValue >= 100000
? 0
: 2,
duration: 1,
separator: `'`
separator: getNumberFormatGroup(this.locale)
}).start();
} else if (this.performance?.currentValue === null) {
this.unit = '%';
@ -65,9 +70,10 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
'value',
this.performance?.currentNetPerformancePercent * 100,
{
decimal: getNumberFormatDecimal(this.locale),
decimalPlaces: 2,
duration: 0.75,
separator: `'`
duration: 1,
separator: getNumberFormatGroup(this.locale)
}
).start();
}

View File

@ -21,7 +21,7 @@
<gf-line-chart
class="mb-4"
benchmarkLabel="Buy Price"
benchmarkLabel="Average Unit Price"
[benchmarkDataItems]="benchmarkDataItems"
[historicalDataItems]="historicalDataItems"
[showGradient]="true"
@ -53,7 +53,7 @@
</div>
<div class="col-6 mb-3">
<gf-value
label="Ø Buy Price"
label="Average Unit Price"
size="medium"
[currency]="SymbolProfile?.currency"
[locale]="data.locale"

View File

@ -17,12 +17,14 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { InfoItem } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { StatusCodes } from 'http-status-codes';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable()
export class HttpResponseInterceptor implements HttpInterceptor {
public hasPermissionForSubscription: boolean;
public info: InfoItem;
public snackBarRef: MatSnackBarRef<TextOnlySnackBar>;
@ -34,6 +36,11 @@ export class HttpResponseInterceptor implements HttpInterceptor {
private webAuthnService: WebAuthnService
) {
this.info = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission(
this.info?.globalPermissions,
permissions.enableSubscription
);
}
public intercept(
@ -56,7 +63,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
} else {
this.snackBarRef = this.snackBar.open(
'This feature requires a subscription.',
'Upgrade Plan',
this.hasPermissionForSubscription ? 'Upgrade Plan' : undefined,
{ duration: 6000 }
);
}

View File

@ -20,9 +20,11 @@ import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { DEFAULT_DATE_FORMAT, baseCurrency } from '@ghostfolio/common/config';
import { baseCurrency } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { Access, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { uniq } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector';
import { StripeService } from 'ngx-stripe';
import { EMPTY, Subject } from 'rxjs';
@ -45,13 +47,14 @@ export class AccountPageComponent implements OnDestroy, OnInit {
public coupon: number;
public couponId: string;
public currencies: string[] = [];
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public defaultDateFormat: string;
public deviceType: string;
public hasPermissionForSubscription: boolean;
public hasPermissionToCreateAccess: boolean;
public hasPermissionToDeleteAccess: boolean;
public hasPermissionToUpdateViewMode: boolean;
public hasPermissionToUpdateUserSettings: boolean;
public locales = ['de', 'de-CH', 'en-GB', 'en-US'];
public price: number;
public priceId: string;
public snackBarRef: MatSnackBarRef<TextOnlySnackBar>;
@ -101,6 +104,10 @@ export class AccountPageComponent implements OnDestroy, OnInit {
if (state?.user) {
this.user = state.user;
this.defaultDateFormat = getDateFormatString(
this.user.settings.locale
);
this.hasPermissionToCreateAccess = hasPermission(
this.user.permissions,
permissions.createAccess
@ -121,6 +128,9 @@ export class AccountPageComponent implements OnDestroy, OnInit {
permissions.updateViewMode
);
this.locales.push(this.user.settings.locale);
this.locales = uniq(this.locales.sort());
this.changeDetectorRef.markForCheck();
}
});
@ -143,6 +153,24 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.update();
}
public onChangeUserSetting(aKey: string, aValue: string) {
this.dataService
.putUserSetting({ [aKey]: aValue })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.userService.remove();
this.userService
.get()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
}
public onChangeUserSettings(aKey: string, aValue: string) {
const settings = { ...this.user.settings, [aKey]: aValue };

View File

@ -111,6 +111,31 @@
</mat-form-field>
</div>
</div>
<div class="align-items-center d-flex mb-2">
<div class="pr-1 w-50">
<div i18n>Locale</div>
<div class="hint-text text-muted" i18n>
Date and number format
</div>
</div>
<div class="pl-1 w-50">
<mat-form-field appearance="outline" class="w-100">
<mat-select
name="locale"
[disabled]="!hasPermissionToUpdateUserSettings"
[value]="user.settings.locale"
(selectionChange)="onChangeUserSetting('locale', $event.value)"
>
<mat-option [value]="null"></mat-option>
<mat-option
*ngFor="let locale of locales"
[value]="locale"
>{{ locale }}</mat-option
>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="d-flex">
<div class="align-items-center d-flex pr-1 pt-1 w-50" i18n>
View Mode

View File

@ -10,6 +10,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { RouterModule } from '@angular/router';
import { GfPortfolioAccessTableModule } from '@ghostfolio/client/components/access-table/access-table.module';
import { GfValueModule } from '@ghostfolio/ui/value';
import { AccountPageRoutingModule } from './account-page-routing.module';
import { AccountPageComponent } from './account-page.component';
@ -24,6 +25,7 @@ import { GfCreateOrUpdateAccessDialogModule } from './create-or-update-access-di
FormsModule,
GfCreateOrUpdateAccessDialogModule,
GfPortfolioAccessTableModule,
GfValueModule,
MatButtonModule,
MatCardModule,
MatDialogModule,

View File

@ -25,7 +25,7 @@
<h4 class="align-items-center d-flex">
<span i18n>Allocations</span>
<ion-icon
*ngIf="hasPermissionForSubscription"
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1 text-muted"
name="diamond-outline"
></ion-icon>
@ -38,7 +38,6 @@
<a
color="primary"
mat-button
[disabled]="hasPermissionForSubscription && user?.settings?.viewMode !== 'DEFAULT'"
[routerLink]="['/portfolio', 'allocations']"
>
<span i18n>Open Allocations</span>
@ -52,7 +51,7 @@
<h4 class="align-items-center d-flex">
<span i18n>Analysis</span>
<ion-icon
*ngIf="hasPermissionForSubscription"
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1 text-muted"
name="diamond-outline"
></ion-icon>
@ -65,7 +64,6 @@
<a
color="primary"
mat-button
[disabled]="hasPermissionForSubscription && user?.settings?.viewMode !== 'DEFAULT'"
[routerLink]="['/portfolio', 'analysis']"
>
<span i18n>Open Analysis</span>
@ -79,7 +77,7 @@
<h4 class="align-items-center d-flex">
<span i18n>X-ray</span>
<ion-icon
*ngIf="hasPermissionForSubscription"
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1 text-muted"
name="diamond-outline"
></ion-icon>
@ -89,12 +87,7 @@
risks in your portfolio.
</div>
<div class="mt-2 text-right">
<a
color="primary"
mat-button
[disabled]="hasPermissionForSubscription && user?.settings?.viewMode !== 'DEFAULT'"
[routerLink]="['/portfolio', 'report']"
>
<a color="primary" mat-button [routerLink]="['/portfolio', 'report']">
<span i18n>Open X-ray</span>
<ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
</a>
@ -106,7 +99,7 @@
<h4 class="align-items-center d-flex">
<span i18n>FIRE</span>
<ion-icon
*ngIf="hasPermissionForSubscription"
*ngIf="user?.subscription?.type === 'Basic'"
class="ml-1 text-muted"
name="diamond-outline"
></ion-icon>
@ -116,12 +109,7 @@
<i>Financial Independence, Retire Early</i> lifestyle.
</div>
<div class="mt-2 text-right">
<a
color="primary"
mat-button
[disabled]="hasPermissionForSubscription && user?.settings?.viewMode !== 'DEFAULT'"
[routerLink]="['/portfolio', 'fire']"
>
<a color="primary" mat-button [routerLink]="['/portfolio', 'fire']">
<span i18n>Open FIRE</span>
<ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
</a>

View File

@ -1,6 +1,7 @@
import { enableProdMode } from '@angular/core';
import { LOCALE_ID } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { locale } from '@ghostfolio/common/config';
import { InfoItem } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
@ -27,7 +28,7 @@ import { environment } from './environments/environment';
platformBrowserDynamic()
.bootstrapModule(AppModule, {
providers: [{ provide: LOCALE_ID, useValue: 'de-CH' }]
providers: [{ provide: LOCALE_ID, useValue: locale }]
})
.catch((error) => console.error(error));
})();

View File

@ -19,7 +19,7 @@ export const ghostfolioCashSymbol = `${ghostfolioScraperApiSymbolPrefix}CASH`;
export const ghostfolioFearAndGreedIndexDataSource = DataSource.RAKUTEN;
export const ghostfolioFearAndGreedIndexSymbol = `${ghostfolioScraperApiSymbolPrefix}FEAR_AND_GREED_INDEX`;
export const locale = 'de-CH';
export const locale = 'en-US';
export const primaryColorHex = '#36cfcc';
export const primaryColorRgb = {
@ -44,7 +44,6 @@ export const warnColorRgb = {
export const ASSET_SUB_CLASS_EMERGENCY_FUND = 'EMERGENCY_FUND';
export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const PROPERTY_COUPONS = 'COUPONS';

View File

@ -2,7 +2,7 @@ import * as currencies from '@dinero.js/currencies';
import { DataSource } from '@prisma/client';
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
import { ghostfolioScraperApiSymbolPrefix } from './config';
import { ghostfolioScraperApiSymbolPrefix, locale } from './config';
export function capitalize(aString: string) {
return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase();
@ -44,6 +44,49 @@ export function getCssVariable(aCssVariable: string) {
);
}
export function getDateFormatString(aLocale?: string) {
const formatObject = new Intl.DateTimeFormat(aLocale).formatToParts(
new Date()
);
return formatObject
.map((object) => {
switch (object.type) {
case 'day':
return 'dd';
case 'month':
return 'MM';
case 'year':
return 'yyyy';
default:
return object.value;
}
})
.join('');
}
export function getLocale() {
return navigator.languages?.length
? navigator.languages[0]
: navigator.language ?? locale;
}
export function getNumberFormatDecimal(aLocale?: string) {
const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99);
return formatObject.find((object) => {
return object.type === 'decimal';
}).value;
}
export function getNumberFormatGroup(aLocale?: string) {
const formatObject = new Intl.NumberFormat(aLocale).formatToParts(9999.99);
return formatObject.find((object) => {
return object.type === 'group';
}).value;
}
export function getTextColor() {
const cssVariable = getCssVariable(
window.matchMedia('(prefers-color-scheme: dark)').matches

View File

@ -20,7 +20,7 @@ import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper';
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types';
import Big from 'big.js';
@ -63,7 +63,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
@ViewChild(MatSort) sort: MatSort;
public dataSource: MatTableDataSource<Activity> = new MatTableDataSource();
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public defaultDateFormat: string;
public displayedColumns = [];
public endOfToday = endOfToday();
public filters$: Subject<string[]> = new BehaviorSubject([]);
@ -153,6 +153,8 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
this.isLoading = true;
this.defaultDateFormat = getDateFormatString(this.locale);
if (this.activities) {
this.dataSource = new MatTableDataSource(this.activities);
this.dataSource.filterPredicate = (data, filter) => {

View File

@ -4,8 +4,8 @@ import {
Input,
OnChanges
} from '@angular/core';
import { DEFAULT_DATE_FORMAT, locale } from '@ghostfolio/common/config';
import { format, isDate, parseISO } from 'date-fns';
import { getLocale } from '@ghostfolio/common/helper';
import { isDate, parseISO } from 'date-fns';
import { isNumber } from 'lodash';
@Component({
@ -21,7 +21,7 @@ export class ValueComponent implements OnChanges {
@Input() isCurrency = false;
@Input() isPercent = false;
@Input() label = '';
@Input() locale = locale;
@Input() locale = getLocale();
@Input() position = '';
@Input() precision: number | undefined;
@Input() size: 'large' | 'medium' | 'small' = 'small';
@ -102,10 +102,13 @@ export class ValueComponent implements OnChanges {
try {
if (isDate(parseISO(this.value))) {
this.formattedValue = format(
new Date(<string>this.value),
DEFAULT_DATE_FORMAT
);
this.formattedValue = new Date(
<string>this.value
).toLocaleDateString(this.locale, {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
}
} catch {
this.formattedValue = this.value;

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "1.131.1",
"version": "1.132.1",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"scripts": {