Browse Source
Task/improve Angular template type safety in client (#7763)
* feat(client): resolve type errors
* feat(client): enable strict safe navigation types
* feat(client): resolve type errors
* feat(ui): resolve type errors
* feat(client): default to default locale
pull/4198/merge
Kenrick Tandrian
1 week ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with
49 additions and
27 deletions
-
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
-
apps/client/src/app/components/admin-settings/admin-settings.component.html
-
apps/client/src/app/components/admin-settings/admin-settings.component.ts
-
apps/client/src/app/components/home-holdings/home-holdings.component.ts
-
apps/client/src/app/components/home-holdings/home-holdings.html
-
apps/client/src/app/components/home-watchlist/home-watchlist.component.ts
-
apps/client/src/app/components/home-watchlist/home-watchlist.html
-
apps/client/src/app/components/markets/markets.component.ts
-
apps/client/src/app/components/markets/markets.html
-
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
-
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
-
apps/client/src/app/pages/accounts/accounts-page.component.ts
-
apps/client/src/app/pages/accounts/accounts-page.html
-
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
-
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.html
-
apps/client/src/app/pages/portfolio/allocations/allocations-page.html
-
apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.html
-
apps/client/tsconfig.json
-
libs/ui/src/lib/benchmark/benchmark-detail-dialog/benchmark-detail-dialog.html
-
libs/ui/src/lib/line-chart/line-chart.component.ts
|
|
|
@ -71,10 +71,10 @@ |
|
|
|
type="button" |
|
|
|
[disabled]=" |
|
|
|
!canDeleteAssetProfile({ |
|
|
|
activitiesCount: assetProfile?.activitiesCount, |
|
|
|
activitiesCount: assetProfile?.activitiesCount ?? 0, |
|
|
|
isBenchmark: isBenchmark, |
|
|
|
symbol: data.symbol, |
|
|
|
watchedByCount: assetProfile?.watchedByCount |
|
|
|
watchedByCount: assetProfile?.watchedByCount ?? 0 |
|
|
|
}) |
|
|
|
" |
|
|
|
(click)=" |
|
|
|
@ -272,8 +272,8 @@ |
|
|
|
> |
|
|
|
</div> |
|
|
|
@if ( |
|
|
|
assetProfile?.countries?.length > 0 || |
|
|
|
assetProfile?.sectors?.length > 0 |
|
|
|
(assetProfile?.countries?.length ?? 0) > 0 || |
|
|
|
(assetProfile?.sectors?.length ?? 0) > 0 |
|
|
|
) { |
|
|
|
@if ( |
|
|
|
assetProfile?.countries?.length === 1 && |
|
|
|
|
|
|
|
@ -230,13 +230,13 @@ |
|
|
|
<div class="mb-5 row"> |
|
|
|
<div class="col"> |
|
|
|
<h2 class="text-center" i18n>Platforms</h2> |
|
|
|
<gf-admin-platform [locale]="user?.settings?.locale" /> |
|
|
|
<gf-admin-platform [locale]="user?.settings?.locale ?? DEFAULT_LOCALE" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="row"> |
|
|
|
<div class="col"> |
|
|
|
<h2 class="text-center" i18n>Tags</h2> |
|
|
|
<gf-admin-tag [locale]="user?.settings?.locale" /> |
|
|
|
<gf-admin-tag [locale]="user?.settings?.locale ?? DEFAULT_LOCALE" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
@ -3,6 +3,7 @@ import { GfAdminTagComponent } from '@ghostfolio/client/components/admin-tag/adm |
|
|
|
import { GfDataProviderStatusComponent } from '@ghostfolio/client/components/data-provider-status/data-provider-status.component'; |
|
|
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { |
|
|
|
DEFAULT_LOCALE, |
|
|
|
E_MAIL_LINE_BREAK, |
|
|
|
PROPERTY_API_KEY_GHOSTFOLIO |
|
|
|
} from '@ghostfolio/common/config'; |
|
|
|
@ -98,6 +99,8 @@ export class GfAdminSettingsComponent implements OnInit { |
|
|
|
public pricingUrl: string; |
|
|
|
public user: User; |
|
|
|
|
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
public constructor( |
|
|
|
private adminService: AdminService, |
|
|
|
private changeDetectorRef: ChangeDetectorRef, |
|
|
|
|
|
|
|
@ -1,4 +1,5 @@ |
|
|
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { DEFAULT_LOCALE } from '@ghostfolio/common/config'; |
|
|
|
import { |
|
|
|
AssetProfileIdentifier, |
|
|
|
PortfolioPosition, |
|
|
|
@ -50,6 +51,8 @@ import { DeviceDetectorService } from 'ngx-device-detector'; |
|
|
|
export class GfHomeHoldingsComponent implements OnInit { |
|
|
|
public static DEFAULT_HOLDINGS_VIEW_MODE: HoldingsViewMode = 'TABLE'; |
|
|
|
|
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
protected deviceType: string; |
|
|
|
protected hasPermissionToAccessHoldingsChart: boolean; |
|
|
|
protected hasPermissionToCreateActivity: boolean; |
|
|
|
|
|
|
|
@ -37,11 +37,11 @@ |
|
|
|
} |
|
|
|
<div [class.d-none]="holdingsViewMode !== 'TABLE'"> |
|
|
|
<gf-holdings-table |
|
|
|
[holdings]="holdings" |
|
|
|
[locale]="user?.settings?.locale" |
|
|
|
[holdings]="holdings ?? []" |
|
|
|
[locale]="user?.settings?.locale ?? DEFAULT_LOCALE" |
|
|
|
(holdingClicked)="onHoldingClicked($event)" |
|
|
|
/> |
|
|
|
@if (hasPermissionToCreateActivity && holdings?.length > 0) { |
|
|
|
@if (hasPermissionToCreateActivity && (holdings?.length ?? 0) > 0) { |
|
|
|
<div class="text-center"> |
|
|
|
<a |
|
|
|
class="mt-3" |
|
|
|
|
|
|
|
@ -44,6 +44,8 @@ import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/ |
|
|
|
templateUrl: './home-watchlist.html' |
|
|
|
}) |
|
|
|
export class GfHomeWatchlistComponent implements OnInit { |
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
protected hasPermissionToCreateWatchlistItem: boolean; |
|
|
|
protected hasPermissionToDeleteWatchlistItem: boolean; |
|
|
|
protected user: User; |
|
|
|
|
|
|
|
@ -13,7 +13,7 @@ |
|
|
|
[benchmarks]="watchlist" |
|
|
|
[deviceType]="deviceType()" |
|
|
|
[hasPermissionToDeleteItem]="hasPermissionToDeleteWatchlistItem" |
|
|
|
[locale]="user?.settings?.locale || undefined" |
|
|
|
[locale]="user?.settings?.locale ?? DEFAULT_LOCALE" |
|
|
|
[showIcon]="true" |
|
|
|
[user]="user" |
|
|
|
(itemDeleted)="onWatchlistItemDeleted($event)" |
|
|
|
|
|
|
|
@ -1,4 +1,5 @@ |
|
|
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { DEFAULT_LOCALE } from '@ghostfolio/common/config'; |
|
|
|
import { resetHours } from '@ghostfolio/common/helper'; |
|
|
|
import { |
|
|
|
Benchmark, |
|
|
|
@ -43,6 +44,8 @@ import { DeviceDetectorService } from 'ngx-device-detector'; |
|
|
|
templateUrl: './markets.html' |
|
|
|
}) |
|
|
|
export class GfMarketsComponent implements OnInit { |
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
protected readonly benchmarks = signal<Benchmark[] | undefined>(undefined); |
|
|
|
|
|
|
|
protected readonly deviceType = computed( |
|
|
|
|
|
|
|
@ -52,7 +52,7 @@ |
|
|
|
<gf-benchmark |
|
|
|
[benchmarks]="benchmarks()" |
|
|
|
[deviceType]="deviceType()" |
|
|
|
[locale]="user?.settings?.locale || undefined" |
|
|
|
[locale]="user?.settings?.locale ?? DEFAULT_LOCALE" |
|
|
|
[showSymbol]="false" |
|
|
|
[user]="user" |
|
|
|
/> |
|
|
|
|
|
|
|
@ -1,7 +1,7 @@ |
|
|
|
<div class="container p-0"> |
|
|
|
<div class="no-gutters row"> |
|
|
|
<div class="status-container text-muted text-right"> |
|
|
|
@if (errors()?.length > 0 && !isLoading()) { |
|
|
|
@if ((errors()?.length ?? 0) > 0 && !isLoading()) { |
|
|
|
<ion-icon |
|
|
|
i18n-title |
|
|
|
name="time-outline" |
|
|
|
|
|
|
|
@ -183,7 +183,7 @@ |
|
|
|
<ng-container i18n>Holdings</ng-container> |
|
|
|
@if ( |
|
|
|
!hasImpersonationId && |
|
|
|
summary?.totalValueInBaseCurrency > 0 && |
|
|
|
(summary?.totalValueInBaseCurrency ?? 0) > 0 && |
|
|
|
user?.settings?.isExperimentalFeatures |
|
|
|
) { |
|
|
|
<gf-value |
|
|
|
@ -261,7 +261,7 @@ |
|
|
|
<ng-container i18n>Cash</ng-container> |
|
|
|
@if ( |
|
|
|
!hasImpersonationId && |
|
|
|
summary?.totalValueInBaseCurrency > 0 && |
|
|
|
(summary?.totalValueInBaseCurrency ?? 0) > 0 && |
|
|
|
user?.settings?.isExperimentalFeatures |
|
|
|
) { |
|
|
|
<gf-value |
|
|
|
@ -324,7 +324,7 @@ |
|
|
|
<ng-container i18n>Excluded from Analysis</ng-container> |
|
|
|
@if ( |
|
|
|
!hasImpersonationId && |
|
|
|
summary?.totalValueInBaseCurrency > 0 && |
|
|
|
(summary?.totalValueInBaseCurrency ?? 0) > 0 && |
|
|
|
user?.settings?.isExperimentalFeatures |
|
|
|
) { |
|
|
|
<gf-value |
|
|
|
@ -412,7 +412,7 @@ |
|
|
|
<ng-container i18n>Emergency Fund</ng-container> |
|
|
|
@if ( |
|
|
|
!hasImpersonationId && |
|
|
|
summary?.totalValueInBaseCurrency > 0 && |
|
|
|
(summary?.totalValueInBaseCurrency ?? 0) > 0 && |
|
|
|
user?.settings?.isExperimentalFeatures |
|
|
|
) { |
|
|
|
<gf-value |
|
|
|
|
|
|
|
@ -1,5 +1,6 @@ |
|
|
|
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; |
|
|
|
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { DEFAULT_LOCALE } from '@ghostfolio/common/config'; |
|
|
|
import { TransferBalanceDto } from '@ghostfolio/common/dtos'; |
|
|
|
import { AccountsResponse, User } from '@ghostfolio/common/interfaces'; |
|
|
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
|
|
|
@ -39,6 +40,8 @@ import { GfTransferBalanceDialogComponent } from './transfer-balance/transfer-ba |
|
|
|
templateUrl: './accounts-page.html' |
|
|
|
}) |
|
|
|
export class GfAccountsPageComponent implements OnInit { |
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
protected accounts: AccountWithValue[] | undefined; |
|
|
|
protected activitiesCount = 0; |
|
|
|
protected hasPermissionToCreateAccount: boolean; |
|
|
|
|
|
|
|
@ -3,12 +3,12 @@ |
|
|
|
<div class="col"> |
|
|
|
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Accounts</h1> |
|
|
|
<gf-accounts-table |
|
|
|
[accounts]="accounts" |
|
|
|
[accounts]="accounts ?? []" |
|
|
|
[activitiesCount]="activitiesCount" |
|
|
|
[baseCurrency]="user?.settings?.baseCurrency" |
|
|
|
[hasPermissionToDeleteAccount]="hasPermissionToDeleteAccount" |
|
|
|
[hasPermissionToUpdateAccount]="hasPermissionToUpdateAccount" |
|
|
|
[locale]="user?.settings?.locale" |
|
|
|
[locale]="user?.settings?.locale ?? DEFAULT_LOCALE" |
|
|
|
[showActions]=" |
|
|
|
(hasPermissionToDeleteAccount || hasPermissionToUpdateAccount) && |
|
|
|
!user.settings.isRestrictedView |
|
|
|
|
|
|
|
@ -79,6 +79,8 @@ import { ActivityType } from './types/activity-type.type'; |
|
|
|
templateUrl: 'create-or-update-activity-dialog.html' |
|
|
|
}) |
|
|
|
export class GfCreateOrUpdateActivityDialogComponent { |
|
|
|
protected readonly DEFAULT_LOCALE = DEFAULT_LOCALE; |
|
|
|
|
|
|
|
protected activityForm: FormGroup; |
|
|
|
|
|
|
|
protected readonly assetClassOptions: AssetClassSelectorOption[] = |
|
|
|
|
|
|
|
@ -309,7 +309,7 @@ |
|
|
|
<gf-value |
|
|
|
class="flex-grow-1" |
|
|
|
[isCurrency]="true" |
|
|
|
[locale]="data.user?.settings?.locale" |
|
|
|
[locale]="data.user?.settings?.locale ?? DEFAULT_LOCALE" |
|
|
|
[unit]=" |
|
|
|
activityForm.get('currencyOfUnitPrice')?.value ?? |
|
|
|
data.user?.settings?.baseCurrency |
|
|
|
|
|
|
|
@ -23,9 +23,11 @@ |
|
|
|
<mat-progress-bar |
|
|
|
mode="determinate" |
|
|
|
[title]="`${( |
|
|
|
portfolioDetails?.summary?.filteredValueInPercentage * 100 |
|
|
|
(portfolioDetails?.summary?.filteredValueInPercentage ?? 0) * 100 |
|
|
|
).toFixed(2)}%`" |
|
|
|
[value]="portfolioDetails?.summary?.filteredValueInPercentage * 100" |
|
|
|
[value]=" |
|
|
|
(portfolioDetails?.summary?.filteredValueInPercentage ?? 0) * 100 |
|
|
|
" |
|
|
|
/> |
|
|
|
</mat-card-content> |
|
|
|
</mat-card> |
|
|
|
@ -236,7 +238,7 @@ |
|
|
|
>Other Markets</gf-value |
|
|
|
> |
|
|
|
</div> |
|
|
|
@if (markets?.[UNKNOWN_KEY]?.valueInPercentage > 0) { |
|
|
|
@if ((markets?.[UNKNOWN_KEY]?.valueInPercentage ?? 0) > 0) { |
|
|
|
<div class="col-xs-12 col-md my-2"> |
|
|
|
<gf-value |
|
|
|
i18n |
|
|
|
|
|
|
|
@ -81,7 +81,7 @@ |
|
|
|
<button |
|
|
|
color="secondary" |
|
|
|
mat-flat-button |
|
|
|
[cdkCopyToClipboard]="accessToken" |
|
|
|
[cdkCopyToClipboard]="accessToken ?? ''" |
|
|
|
(click)="enableCreateAccountButton()" |
|
|
|
> |
|
|
|
<ion-icon class="mr-1" name="copy-outline" /> |
|
|
|
|
|
|
|
@ -25,7 +25,7 @@ |
|
|
|
"strictLiteralTypes": true, |
|
|
|
"strictNullInputTypes": true, |
|
|
|
"strictOutputEventTypes": true, |
|
|
|
"strictSafeNavigationTypes": false, |
|
|
|
"strictSafeNavigationTypes": true, |
|
|
|
// TODO: Enable stricter rules for this project |
|
|
|
"strictTemplates": false |
|
|
|
}, |
|
|
|
|
|
|
|
@ -1,7 +1,7 @@ |
|
|
|
<gf-dialog-header |
|
|
|
position="center" |
|
|
|
[deviceType]="data.deviceType" |
|
|
|
[title]="assetProfile?.name ?? assetProfile?.symbol" |
|
|
|
[title]="assetProfile.name ?? assetProfile.symbol ?? ''" |
|
|
|
(closeButtonClicked)="onClose()" |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
@ -4,7 +4,11 @@ import { |
|
|
|
getValueAxisOptions, |
|
|
|
getVerticalHoverLinePlugin |
|
|
|
} from '@ghostfolio/common/chart-helper'; |
|
|
|
import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; |
|
|
|
import { |
|
|
|
DEFAULT_COLOR_SCHEME, |
|
|
|
primaryColorRgb, |
|
|
|
secondaryColorRgb |
|
|
|
} from '@ghostfolio/common/config'; |
|
|
|
import { getBackgroundColor, getLocale } from '@ghostfolio/common/helper'; |
|
|
|
import { LineChartItem } from '@ghostfolio/common/interfaces'; |
|
|
|
import { ColorScheme } from '@ghostfolio/common/types'; |
|
|
|
@ -52,7 +56,7 @@ export class GfLineChartComponent |
|
|
|
{ |
|
|
|
@Input() benchmarkDataItems: LineChartItem[] = []; |
|
|
|
@Input() benchmarkLabel = ''; |
|
|
|
@Input() colorScheme: ColorScheme; |
|
|
|
@Input() colorScheme: ColorScheme = DEFAULT_COLOR_SCHEME; |
|
|
|
@Input() currency: string; |
|
|
|
@Input() historicalDataItems: LineChartItem[]; |
|
|
|
@Input() isAnimated = false; |
|
|
|
|