Feature/prepare for localized date format (#803)

* Support localized date and number format

* Update changelog
This commit is contained in:
Thomas Kaul 2022-04-05 21:02:07 +02:00 committed by GitHub
parent fa41e25c8f
commit 204c7360c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 193 additions and 51 deletions

View File

@ -5,6 +5,12 @@ 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).
## Unreleased
### Added
- Added support for localization (date and number format) in user settings
## 1.131.1 - 04.04.2022 ## 1.131.1 - 04.04.2022
### Fixed ### Fixed

View File

@ -1,5 +1,6 @@
export interface UserSettings { export interface UserSettings {
emergencyFund?: number; emergencyFund?: number;
locale?: string;
isNewCalculationEngine?: boolean; isNewCalculationEngine?: boolean;
isRestrictedView?: 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 { export class UpdateUserSettingDto {
@IsNumber() @IsNumber()
@ -12,4 +12,8 @@ export class UpdateUserSettingDto {
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
isRestrictedView?: boolean; 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 { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { PROPERTY_IS_READ_ONLY_MODE } from '@ghostfolio/common/config'; import { PROPERTY_IS_READ_ONLY_MODE } from '@ghostfolio/common/config';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { import { hasPermission, permissions } from '@ghostfolio/common/permissions';
hasPermission,
hasRole,
permissions
} from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types'; import type { RequestWithUser } from '@ghostfolio/common/types';
import { import {
Body, Body,
Controller, Controller,
Delete, Delete,
Get, Get,
Headers,
HttpException, HttpException,
Inject, Inject,
Param, Param,
@ -63,8 +60,13 @@ export class UserController {
@Get() @Get()
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getUser(@Param('id') id: string): Promise<User> { public async getUser(
return this.userService.getUser(this.request.user); @Headers('accept-language') acceptLanguage: string
): Promise<User> {
return this.userService.getUser(
this.request.user,
acceptLanguage?.split(',')?.[0]
);
} }
@Post() @Post()
@ -118,7 +120,7 @@ export class UserController {
}; };
for (const key in userSettings) { for (const key in userSettings) {
if (userSettings[key] === false) { if (userSettings[key] === false || userSettings[key] === null) {
delete userSettings[key]; delete userSettings[key];
} }
} }

View File

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

View File

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

View File

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

View File

@ -7,8 +7,9 @@ import {
} from '@angular/core'; } from '@angular/core';
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 { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { UniqueAsset } from '@ghostfolio/common/interfaces'; import { getDateFormatString } from '@ghostfolio/common/helper';
import { 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 { DataSource, MarketData } from '@prisma/client';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -23,9 +24,10 @@ import { takeUntil } from 'rxjs/operators';
export class AdminMarketDataComponent implements OnDestroy, OnInit { export class AdminMarketDataComponent implements OnDestroy, OnInit {
public currentDataSource: DataSource; public currentDataSource: DataSource;
public currentSymbol: string; public currentSymbol: string;
public defaultDateFormat = DEFAULT_DATE_FORMAT; public defaultDateFormat: string;
public marketData: AdminMarketDataItem[] = []; public marketData: AdminMarketDataItem[] = [];
public marketDataDetails: MarketData[] = []; public marketDataDetails: MarketData[] = [];
public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -35,8 +37,21 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
public constructor( public constructor(
private adminService: AdminService, private adminService: AdminService,
private changeDetectorRef: ChangeDetectorRef, 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 * Initializes the controller

View File

@ -65,6 +65,7 @@
<gf-admin-market-data-detail <gf-admin-market-data-detail
[dataSource]="item.dataSource" [dataSource]="item.dataSource"
[dateOfFirstActivity]="item.date" [dateOfFirstActivity]="item.date"
[locale]="user?.settings?.locale"
[marketData]="marketDataDetails" [marketData]="marketDataDetails"
[symbol]="item.symbol" [symbol]="item.symbol"
(marketDataChanged)="onMarketDataChanged($event)" (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 { 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 { import {
DEFAULT_DATE_FORMAT,
PROPERTY_COUPONS, PROPERTY_COUPONS,
PROPERTY_CURRENCIES, PROPERTY_CURRENCIES,
PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_READ_ONLY_MODE,
@ -35,7 +34,6 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
public customCurrencies: string[]; public customCurrencies: string[];
public dataGatheringInProgress: boolean; public dataGatheringInProgress: boolean;
public dataGatheringProgress: number; public dataGatheringProgress: number;
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public exchangeRates: { label1: string; label2: string; value: number }[]; public exchangeRates: { label1: string; label2: string; value: number }[];
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionForSystemMessage: boolean; public hasPermissionForSystemMessage: boolean;

View File

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

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 { 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 { WebAuthnService } from '@ghostfolio/client/services/web-authn.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 { Access, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { uniq } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { StripeService } from 'ngx-stripe'; import { StripeService } from 'ngx-stripe';
import { EMPTY, Subject } from 'rxjs'; import { EMPTY, Subject } from 'rxjs';
@ -45,13 +47,14 @@ export class AccountPageComponent implements OnDestroy, OnInit {
public coupon: number; public coupon: number;
public couponId: string; public couponId: string;
public currencies: string[] = []; public currencies: string[] = [];
public defaultDateFormat = DEFAULT_DATE_FORMAT; public defaultDateFormat: string;
public deviceType: string; public deviceType: string;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionToCreateAccess: boolean; public hasPermissionToCreateAccess: boolean;
public hasPermissionToDeleteAccess: boolean; public hasPermissionToDeleteAccess: boolean;
public hasPermissionToUpdateViewMode: boolean; public hasPermissionToUpdateViewMode: boolean;
public hasPermissionToUpdateUserSettings: boolean; public hasPermissionToUpdateUserSettings: boolean;
public locales = ['de', 'de-CH', 'en-GB', 'en-US'];
public price: number; public price: number;
public priceId: string; public priceId: string;
public snackBarRef: MatSnackBarRef<TextOnlySnackBar>; public snackBarRef: MatSnackBarRef<TextOnlySnackBar>;
@ -101,6 +104,10 @@ export class AccountPageComponent implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
this.defaultDateFormat = getDateFormatString(
this.user.settings.locale
);
this.hasPermissionToCreateAccess = hasPermission( this.hasPermissionToCreateAccess = hasPermission(
this.user.permissions, this.user.permissions,
permissions.createAccess permissions.createAccess
@ -121,6 +128,9 @@ export class AccountPageComponent implements OnDestroy, OnInit {
permissions.updateViewMode permissions.updateViewMode
); );
this.locales.push(this.user.settings.locale);
this.locales = uniq(this.locales.sort());
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });
@ -143,6 +153,24 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.update(); 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) { public onChangeUserSettings(aKey: string, aValue: string) {
const settings = { ...this.user.settings, [aKey]: aValue }; const settings = { ...this.user.settings, [aKey]: aValue };

View File

@ -111,6 +111,31 @@
</mat-form-field> </mat-form-field>
</div> </div>
</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="d-flex">
<div class="align-items-center d-flex pr-1 pt-1 w-50" i18n> <div class="align-items-center d-flex pr-1 pt-1 w-50" i18n>
View Mode View Mode

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import * as currencies from '@dinero.js/currencies';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns'; import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
import { ghostfolioScraperApiSymbolPrefix } from './config'; import { ghostfolioScraperApiSymbolPrefix, locale } from './config';
export function capitalize(aString: string) { export function capitalize(aString: string) {
return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase(); 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() { export function getTextColor() {
const cssVariable = getCssVariable( const cssVariable = getCssVariable(
window.matchMedia('(prefers-color-scheme: dark)').matches 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 { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; 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 { UniqueAsset } from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
import Big from 'big.js'; import Big from 'big.js';
@ -63,7 +63,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public dataSource: MatTableDataSource<Activity> = new MatTableDataSource(); public dataSource: MatTableDataSource<Activity> = new MatTableDataSource();
public defaultDateFormat = DEFAULT_DATE_FORMAT; public defaultDateFormat: string;
public displayedColumns = []; public displayedColumns = [];
public endOfToday = endOfToday(); public endOfToday = endOfToday();
public filters$: Subject<string[]> = new BehaviorSubject([]); public filters$: Subject<string[]> = new BehaviorSubject([]);
@ -153,6 +153,8 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
this.isLoading = true; this.isLoading = true;
this.defaultDateFormat = getDateFormatString(this.locale);
if (this.activities) { if (this.activities) {
this.dataSource = new MatTableDataSource(this.activities); this.dataSource = new MatTableDataSource(this.activities);
this.dataSource.filterPredicate = (data, filter) => { this.dataSource.filterPredicate = (data, filter) => {

View File

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