Browse Source

Prepare for localized date format

pull/803/head
Thomas 3 years ago
parent
commit
2ac5fb71a1
  1. 1
      apps/api/src/app/user/interfaces/user-settings.interface.ts
  2. 2
      apps/api/src/app/user/user.service.ts
  3. 12
      apps/client/src/app/adapter/date-formats.ts
  4. 2
      apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.html
  5. 5
      apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
  6. 19
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  7. 5
      apps/client/src/app/components/admin-market-data/admin-market-data.html
  8. 2
      apps/client/src/app/components/admin-market-data/admin-market-data.module.ts
  9. 2
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  10. 3
      apps/client/src/app/pages/account/account-page.component.ts
  11. 8
      apps/client/src/app/pages/account/account-page.html
  12. 2
      apps/client/src/app/pages/account/account-page.module.ts
  13. 1
      libs/common/src/lib/config.ts
  14. 29
      libs/common/src/lib/helper.ts
  15. 2
      libs/ui/src/lib/activities-table/activities-table.component.html
  16. 2
      libs/ui/src/lib/activities-table/activities-table.component.ts
  17. 17
      libs/ui/src/lib/value/value.component.ts

1
apps/api/src/app/user/interfaces/user-settings.interface.ts

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

2
apps/api/src/app/user/user.service.ts

@ -63,7 +63,7 @@ export class UserService {
accounts: Account, accounts: Account,
settings: { settings: {
...(<UserSettings>Settings.settings), ...(<UserSettings>Settings.settings),
locale, locale: (<UserSettings>Settings.settings).locale ?? locale,
baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY, baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY,
viewMode: Settings?.viewMode ?? ViewMode.DEFAULT viewMode: Settings?.viewMode ?? ViewMode.DEFAULT
} }

12
apps/client/src/app/adapter/date-formats.ts

@ -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()
} }
}; };

2
apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.html

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

5
apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts

@ -8,8 +8,7 @@ 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 { DATE_FORMAT, getDateFormatString } from '@ghostfolio/common/helper';
import { DATE_FORMAT } 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 {
@ -40,8 +39,8 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
@Output() marketDataChanged = new EventEmitter<boolean>(); @Output() marketDataChanged = new EventEmitter<boolean>();
public dateFormat = getDateFormatString();
public days = Array(31); public days = Array(31);
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public deviceType: string; public deviceType: string;
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public marketDataByMonth: { public marketDataByMonth: {

19
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -7,8 +7,8 @@ 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 { 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 +23,9 @@ 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 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 +35,17 @@ 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;
}
});
}
/** /**
* Initializes the controller * Initializes the controller

5
apps/client/src/app/components/admin-market-data/admin-market-data.html

@ -21,7 +21,10 @@
<td class="mat-cell px-1 py-2">{{ item.symbol }}</td> <td class="mat-cell px-1 py-2">{{ item.symbol }}</td>
<td class="mat-cell px-1 py-2">{{ item.dataSource }}</td> <td class="mat-cell px-1 py-2">{{ item.dataSource }}</td>
<td class="mat-cell px-1 py-2"> <td class="mat-cell px-1 py-2">
{{ (item.date | date: defaultDateFormat) ?? '' }} <gf-value
[locale]="user?.settings?.locale"
[value]="item.date ?? ''"
></gf-value>
</td> </td>
<td class="mat-cell px-1 py-2">{{ item.activityCount }}</td> <td class="mat-cell px-1 py-2">{{ item.activityCount }}</td>
<td class="mat-cell px-1 py-2">{{ item.marketDataItemCount }}</td> <td class="mat-cell px-1 py-2">{{ item.marketDataItemCount }}</td>

2
apps/client/src/app/components/admin-market-data/admin-market-data.module.ts

@ -3,6 +3,7 @@ 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 { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module';
import { GfValueModule } from '@ghostfolio/ui/value';
import { AdminMarketDataComponent } from './admin-market-data.component'; import { AdminMarketDataComponent } from './admin-market-data.component';
@ -11,6 +12,7 @@ import { AdminMarketDataComponent } from './admin-market-data.component';
imports: [ imports: [
CommonModule, CommonModule,
GfAdminMarketDataDetailModule, GfAdminMarketDataDetailModule,
GfValueModule,
MatButtonModule, MatButtonModule,
MatMenuModule MatMenuModule
], ],

2
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -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;

3
apps/client/src/app/pages/account/account-page.component.ts

@ -20,7 +20,7 @@ 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 { 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 { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -45,7 +45,6 @@ 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 deviceType: string; public deviceType: string;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionToCreateAccess: boolean; public hasPermissionToCreateAccess: boolean;

8
apps/client/src/app/pages/account/account-page.html

@ -24,8 +24,12 @@
></ion-icon> ></ion-icon>
</div> </div>
<div *ngIf="user?.subscription?.type === 'Premium'"> <div *ngIf="user?.subscription?.type === 'Premium'">
Valid until {{ user?.subscription?.expiresAt | date: Valid until
defaultDateFormat }} <gf-value
class="d-inline-block"
[locale]="user?.settings?.locale"
[value]="user?.subscription?.expiresAt"
></gf-value>
</div> </div>
<div *ngIf="user?.subscription?.type === 'Basic'"> <div *ngIf="user?.subscription?.type === 'Basic'">
<ng-container *ngIf="hasPermissionForSubscription"> <ng-container *ngIf="hasPermissionForSubscription">

2
apps/client/src/app/pages/account/account-page.module.ts

@ -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,

1
libs/common/src/lib/config.ts

@ -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';

29
libs/common/src/lib/helper.ts

@ -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,33 @@ 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 getTextColor() { export function getTextColor() {
const cssVariable = getCssVariable( const cssVariable = getCssVariable(
window.matchMedia('(prefers-color-scheme: dark)').matches window.matchMedia('(prefers-color-scheme: dark)').matches

2
libs/ui/src/lib/activities-table/activities-table.component.html

@ -71,7 +71,7 @@
</th> </th>
<td *matCellDef="let element" class="px-1" mat-cell> <td *matCellDef="let element" class="px-1" mat-cell>
<div class="d-flex"> <div class="d-flex">
{{ element.date | date: defaultDateFormat }} <gf-value [locale]="locale" [value]="element.date"></gf-value>
</div> </div>
</td> </td>
<td *matFooterCellDef class="px-1" i18n mat-footer-cell>Total</td> <td *matFooterCellDef class="px-1" i18n mat-footer-cell>Total</td>

2
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -20,7 +20,6 @@ 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 { 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 +62,6 @@ 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 displayedColumns = []; public displayedColumns = [];
public endOfToday = endOfToday(); public endOfToday = endOfToday();
public filters$: Subject<string[]> = new BehaviorSubject([]); public filters$: Subject<string[]> = new BehaviorSubject([]);

17
libs/ui/src/lib/value/value.component.ts

@ -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;

Loading…
Cancel
Save