Prettify markup (#3029)
This commit is contained in:
parent
982c71c728
commit
b62488628c
@ -25,7 +25,9 @@
|
||||
class="h-100"
|
||||
[currency]="user?.settings?.baseCurrency"
|
||||
[historicalDataItems]="historicalDataItems"
|
||||
[isInPercent]="data.hasImpersonationId || user.settings.isRestrictedView"
|
||||
[isInPercent]="
|
||||
data.hasImpersonationId || user.settings.isRestrictedView
|
||||
"
|
||||
[isLoading]="isLoadingChart"
|
||||
[locale]="user?.settings?.locale"
|
||||
/>
|
||||
@ -92,7 +94,9 @@
|
||||
[dataSource]="dataSource"
|
||||
[deviceType]="data.deviceType"
|
||||
[hasPermissionToCreateActivity]="false"
|
||||
[hasPermissionToExportActivities]="!data.hasImpersonationId && !user.settings.isRestrictedView"
|
||||
[hasPermissionToExportActivities]="
|
||||
!data.hasImpersonationId && !user.settings.isRestrictedView
|
||||
"
|
||||
[hasPermissionToFilter]="false"
|
||||
[hasPermissionToOpenDetails]="false"
|
||||
[locale]="user?.settings?.locale"
|
||||
@ -113,7 +117,11 @@
|
||||
[accountBalances]="accountBalances"
|
||||
[accountId]="data.accountId"
|
||||
[locale]="user?.settings?.locale"
|
||||
[showActions]="!data.hasImpersonationId && hasPermissionToDeleteAccountBalance && !user.settings.isRestrictedView"
|
||||
[showActions]="
|
||||
!data.hasImpersonationId &&
|
||||
hasPermissionToDeleteAccountBalance &&
|
||||
!user.settings.isRestrictedView
|
||||
"
|
||||
(accountBalanceDeleted)="onDeleteAccountBalance($event)"
|
||||
/>
|
||||
</mat-tab>
|
||||
|
@ -163,7 +163,12 @@
|
||||
<mat-menu #assetProfileActionsMenu="matMenu" xPosition="before">
|
||||
<button
|
||||
mat-menu-item
|
||||
(click)="onOpenAssetProfileDialog({ dataSource: element.dataSource, symbol: element.symbol })"
|
||||
(click)="
|
||||
onOpenAssetProfileDialog({
|
||||
dataSource: element.dataSource,
|
||||
symbol: element.symbol
|
||||
})
|
||||
"
|
||||
>
|
||||
<span class="align-items-center d-flex">
|
||||
<ion-icon class="mr-2" name="create-outline" />
|
||||
@ -173,7 +178,12 @@
|
||||
<button
|
||||
mat-menu-item
|
||||
[disabled]="element.activitiesCount !== 0"
|
||||
(click)="onDeleteProfileData({dataSource: element.dataSource, symbol: element.symbol})"
|
||||
(click)="
|
||||
onDeleteProfileData({
|
||||
dataSource: element.dataSource,
|
||||
symbol: element.symbol
|
||||
})
|
||||
"
|
||||
>
|
||||
<span class="align-items-center d-flex">
|
||||
<ion-icon class="mr-2" name="trash-outline" />
|
||||
@ -189,16 +199,19 @@
|
||||
*matRowDef="let row; columns: displayedColumns"
|
||||
class="cursor-pointer"
|
||||
mat-row
|
||||
(click)="onOpenAssetProfileDialog({ dataSource: row.dataSource, symbol: row.symbol })"
|
||||
(click)="
|
||||
onOpenAssetProfileDialog({
|
||||
dataSource: row.dataSource,
|
||||
symbol: row.symbol
|
||||
})
|
||||
"
|
||||
></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[length]="totalItems"
|
||||
[ngClass]="{
|
||||
'd-none':
|
||||
(isLoading && totalItems === 0) ||
|
||||
totalItems <= pageSize
|
||||
'd-none': (isLoading && totalItems === 0) || totalItems <= pageSize
|
||||
}"
|
||||
[pageSize]="pageSize"
|
||||
[showFirstLastButtons]="true"
|
||||
|
@ -25,7 +25,9 @@
|
||||
mat-menu-item
|
||||
type="button"
|
||||
[disabled]="assetProfileForm.dirty"
|
||||
(click)="onGatherSymbol({dataSource: data.dataSource, symbol: data.symbol})"
|
||||
(click)="
|
||||
onGatherSymbol({ dataSource: data.dataSource, symbol: data.symbol })
|
||||
"
|
||||
>
|
||||
<ng-container i18n>Gather Historical Data</ng-container>
|
||||
</button>
|
||||
@ -33,7 +35,12 @@
|
||||
mat-menu-item
|
||||
type="button"
|
||||
[disabled]="assetProfileForm.dirty"
|
||||
(click)="onGatherProfileDataBySymbol({dataSource: data.dataSource, symbol: data.symbol})"
|
||||
(click)="
|
||||
onGatherProfileDataBySymbol({
|
||||
dataSource: data.dataSource,
|
||||
symbol: data.symbol
|
||||
})
|
||||
"
|
||||
>
|
||||
<ng-container i18n>Gather Profile Data</ng-container>
|
||||
</button>
|
||||
@ -73,7 +80,12 @@
|
||||
color="accent"
|
||||
mat-flat-button
|
||||
type="button"
|
||||
[disabled]="!assetProfileForm.controls['historicalData']?.controls['csvString'].touched || assetProfileForm.controls['historicalData']?.controls['csvString']?.value === ''"
|
||||
[disabled]="
|
||||
!assetProfileForm.controls['historicalData']?.controls['csvString']
|
||||
.touched ||
|
||||
assetProfileForm.controls['historicalData']?.controls['csvString']
|
||||
?.value === ''
|
||||
"
|
||||
(click)="onImportHistoricalData()"
|
||||
>
|
||||
<ng-container i18n>Import</ng-container>
|
||||
@ -129,49 +141,54 @@
|
||||
>
|
||||
</div>
|
||||
<ng-container
|
||||
*ngIf="assetProfile?.countries?.length > 0 || assetProfile?.sectors?.length > 0"
|
||||
*ngIf="
|
||||
assetProfile?.countries?.length > 0 ||
|
||||
assetProfile?.sectors?.length > 0
|
||||
"
|
||||
>
|
||||
@if (assetProfile?.countries?.length === 1 &&
|
||||
assetProfile?.sectors?.length === 1 ) {
|
||||
<div *ngIf="assetProfile?.sectors?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="assetProfile?.sectors[0].name"
|
||||
>Sector</gf-value
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="assetProfile?.countries?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="assetProfile?.countries[0].name"
|
||||
>Country</gf-value
|
||||
>
|
||||
</div>
|
||||
@if (
|
||||
assetProfile?.countries?.length === 1 &&
|
||||
assetProfile?.sectors?.length === 1
|
||||
) {
|
||||
<div *ngIf="assetProfile?.sectors?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="assetProfile?.sectors[0].name"
|
||||
>Sector</gf-value
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="assetProfile?.countries?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="assetProfile?.countries[0].name"
|
||||
>Country</gf-value
|
||||
>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Sectors</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[maxItems]="10"
|
||||
[positions]="sectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Countries</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Sectors</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[maxItems]="10"
|
||||
[positions]="sectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Countries</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
</div>
|
||||
@ -222,7 +239,17 @@
|
||||
color="primary"
|
||||
i18n
|
||||
[checked]="isBenchmark"
|
||||
(change)="isBenchmark ? onUnsetBenchmark({dataSource: data.dataSource, symbol: data.symbol}) : onSetBenchmark({dataSource: data.dataSource, symbol: data.symbol})"
|
||||
(change)="
|
||||
isBenchmark
|
||||
? onUnsetBenchmark({
|
||||
dataSource: data.dataSource,
|
||||
symbol: data.symbol
|
||||
})
|
||||
: onSetBenchmark({
|
||||
dataSource: data.dataSource,
|
||||
symbol: data.symbol
|
||||
})
|
||||
"
|
||||
>Benchmark</mat-checkbox
|
||||
>
|
||||
</div>
|
||||
@ -253,7 +280,9 @@
|
||||
color="accent"
|
||||
mat-flat-button
|
||||
type="button"
|
||||
[disabled]="assetProfileForm.controls['scraperConfiguration'].value === '{}'"
|
||||
[disabled]="
|
||||
assetProfileForm.controls['scraperConfiguration'].value === '{}'
|
||||
"
|
||||
(click)="onTestMarketData()"
|
||||
>
|
||||
<ng-container i18n>Test</ng-container>
|
||||
|
@ -28,7 +28,7 @@
|
||||
[value]="transactionCount"
|
||||
/>
|
||||
<div *ngIf="transactionCount && userCount">
|
||||
{{ transactionCount / userCount | number : '1.2-2' }}
|
||||
{{ transactionCount / userCount | number: '1.2-2' }}
|
||||
<span i18n>per User</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -69,10 +69,10 @@
|
||||
<a
|
||||
mat-menu-item
|
||||
[queryParams]="{
|
||||
assetProfileDialog: true,
|
||||
dataSource: exchangeRate.dataSource,
|
||||
symbol: exchangeRate.symbol
|
||||
}"
|
||||
assetProfileDialog: true,
|
||||
dataSource: exchangeRate.dataSource,
|
||||
symbol: exchangeRate.symbol
|
||||
}"
|
||||
[routerLink]="['/admin', 'market-data']"
|
||||
>
|
||||
<span class="align-items-center d-flex">
|
||||
@ -112,7 +112,9 @@
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
hideIcon="true"
|
||||
[checked]="info.globalPermissions.includes(permissions.createUserAccount)"
|
||||
[checked]="
|
||||
info.globalPermissions.includes(permissions.createUserAccount)
|
||||
"
|
||||
(change)="onEnableUserSignupModeChange($event)"
|
||||
/>
|
||||
</div>
|
||||
@ -143,7 +145,7 @@
|
||||
<div class="w-50" i18n>System Message</div>
|
||||
<div class="w-50">
|
||||
<div *ngIf="systemMessage" class="align-items-center d-flex">
|
||||
<div class="text-truncate">{{ systemMessage | json }}</div>
|
||||
<div class="text-truncate">{{ systemMessage | json }}</div>
|
||||
<button
|
||||
class="h-100 mx-1 no-min-width px-2"
|
||||
mat-button
|
||||
|
@ -12,7 +12,7 @@
|
||||
#
|
||||
</th>
|
||||
<td
|
||||
*matCellDef="let element; let i=index"
|
||||
*matCellDef="let element; let i = index"
|
||||
class="mat-mdc-cell px-1 py-2 text-right"
|
||||
mat-cell
|
||||
>
|
||||
@ -35,17 +35,23 @@
|
||||
mat-cell
|
||||
>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-none d-sm-inline-block text-monospace"
|
||||
>{{ element.id }}</span
|
||||
>
|
||||
<span class="d-inline-block d-sm-none text-monospace"
|
||||
>{{ (element.id | slice:0:5) + '...' }}</span
|
||||
>
|
||||
<span class="d-none d-sm-inline-block text-monospace">{{
|
||||
element.id
|
||||
}}</span>
|
||||
<span class="d-inline-block d-sm-none text-monospace">{{
|
||||
(element.id | slice: 0 : 5) + '...'
|
||||
}}</span>
|
||||
<gf-premium-indicator
|
||||
*ngIf="element?.subscription?.type === 'Premium'"
|
||||
class="ml-1"
|
||||
[enableLink]="false"
|
||||
[title]="'Expires ' + formatDistanceToNow(element.subscription.expiresAt) + ' (' + (element.subscription.expiresAt | date: defaultDateFormat) + ')'"
|
||||
[title]="
|
||||
'Expires ' +
|
||||
formatDistanceToNow(element.subscription.expiresAt) +
|
||||
' (' +
|
||||
(element.subscription.expiresAt | date: defaultDateFormat) +
|
||||
')'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@ -67,9 +73,9 @@
|
||||
class="mat-mdc-cell px-1 py-2"
|
||||
mat-cell
|
||||
>
|
||||
<span class="h5" [title]="element.country"
|
||||
>{{ getEmojiFlag(element.country) }}</span
|
||||
>
|
||||
<span class="h5" [title]="element.country">{{
|
||||
getEmojiFlag(element.country)
|
||||
}}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
@ -1,100 +1,100 @@
|
||||
<div
|
||||
class="align-items-center container d-flex flex-column h-100 justify-content-center overview p-0 position-relative"
|
||||
>
|
||||
@if(hasPermissionToCreateOrder && historicalDataItems?.length === 0) {
|
||||
<div class="justify-content-center row w-100">
|
||||
<div class="col introduction">
|
||||
<h4 i18n>Welcome to Ghostfolio</h4>
|
||||
<p i18n>Ready to take control of your personal finances?</p>
|
||||
<ol class="font-weight-bold">
|
||||
<li
|
||||
class="mb-2"
|
||||
[ngClass]="{ 'text-muted': user?.accounts?.length > 1 }"
|
||||
>
|
||||
<a class="d-block" [routerLink]="['/accounts']"
|
||||
><span i18n>Setup your accounts</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Get a comprehensive financial overview by adding your bank and
|
||||
brokerage accounts.</span
|
||||
></a
|
||||
@if (hasPermissionToCreateOrder && historicalDataItems?.length === 0) {
|
||||
<div class="justify-content-center row w-100">
|
||||
<div class="col introduction">
|
||||
<h4 i18n>Welcome to Ghostfolio</h4>
|
||||
<p i18n>Ready to take control of your personal finances?</p>
|
||||
<ol class="font-weight-bold">
|
||||
<li
|
||||
class="mb-2"
|
||||
[ngClass]="{ 'text-muted': user?.accounts?.length > 1 }"
|
||||
>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a class="d-block" [routerLink]="['/portfolio', 'activities']">
|
||||
<span i18n>Capture your activities</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Record your investment activities to keep your portfolio up to
|
||||
date.</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a class="d-block" [routerLink]="['/portfolio']">
|
||||
<span i18n>Monitor and analyze your portfolio</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Track your progress in real-time with comprehensive analysis and
|
||||
insights.</span
|
||||
<a class="d-block" [routerLink]="['/accounts']"
|
||||
><span i18n>Setup your accounts</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Get a comprehensive financial overview by adding your bank and
|
||||
brokerage accounts.</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a class="d-block" [routerLink]="['/portfolio', 'activities']">
|
||||
<span i18n>Capture your activities</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Record your investment activities to keep your portfolio up to
|
||||
date.</span
|
||||
></a
|
||||
>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<a class="d-block" [routerLink]="['/portfolio']">
|
||||
<span i18n>Monitor and analyze your portfolio</span><br />
|
||||
<span class="font-weight-normal" i18n
|
||||
>Track your progress in real-time with comprehensive analysis
|
||||
and insights.</span
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="d-flex justify-content-center">
|
||||
<a
|
||||
*ngIf="user?.accounts?.length === 1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/accounts']"
|
||||
>
|
||||
<ng-container i18n>Setup accounts</ng-container>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="d-flex justify-content-center">
|
||||
<a
|
||||
*ngIf="user?.accounts?.length === 1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/accounts']"
|
||||
>
|
||||
<ng-container i18n>Setup accounts</ng-container>
|
||||
</a>
|
||||
<a
|
||||
*ngIf="user?.accounts?.length > 1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/portfolio', 'activities']"
|
||||
>
|
||||
<ng-container i18n>Add activity</ng-container>
|
||||
</a>
|
||||
<a
|
||||
*ngIf="user?.accounts?.length > 1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/portfolio', 'activities']"
|
||||
>
|
||||
<ng-container i18n>Add activity</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="row w-100">
|
||||
<div class="col p-0">
|
||||
<div class="chart-container mx-auto position-relative">
|
||||
<gf-line-chart
|
||||
class="position-absolute"
|
||||
symbol="Performance"
|
||||
unit="%"
|
||||
[colorScheme]="user?.settings?.colorScheme"
|
||||
[hidden]="historicalDataItems?.length === 0"
|
||||
[historicalDataItems]="historicalDataItems"
|
||||
[isAnimated]="user?.settings?.dateRange === '1d' ? false : true"
|
||||
<div class="row w-100">
|
||||
<div class="col p-0">
|
||||
<div class="chart-container mx-auto position-relative">
|
||||
<gf-line-chart
|
||||
class="position-absolute"
|
||||
symbol="Performance"
|
||||
unit="%"
|
||||
[colorScheme]="user?.settings?.colorScheme"
|
||||
[hidden]="historicalDataItems?.length === 0"
|
||||
[historicalDataItems]="historicalDataItems"
|
||||
[isAnimated]="user?.settings?.dateRange === '1d' ? false : true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[ngClass]="{ 'pr-3': deviceType === 'mobile' }"
|
||||
[showGradient]="true"
|
||||
[showLoader]="false"
|
||||
[showXAxis]="false"
|
||||
[showYAxis]="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview-container row mt-1">
|
||||
<div class="col">
|
||||
<gf-portfolio-performance
|
||||
class="pb-4"
|
||||
[deviceType]="deviceType"
|
||||
[errors]="errors"
|
||||
[isAllTimeHigh]="isAllTimeHigh"
|
||||
[isAllTimeLow]="isAllTimeLow"
|
||||
[isLoading]="isLoadingPerformance"
|
||||
[locale]="user?.settings?.locale"
|
||||
[ngClass]="{ 'pr-3': deviceType === 'mobile' }"
|
||||
[showGradient]="true"
|
||||
[showLoader]="false"
|
||||
[showXAxis]="false"
|
||||
[showYAxis]="false"
|
||||
[performance]="performance"
|
||||
[showDetails]="showDetails"
|
||||
[unit]="unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview-container row mt-1">
|
||||
<div class="col">
|
||||
<gf-portfolio-performance
|
||||
class="pb-4"
|
||||
[deviceType]="deviceType"
|
||||
[errors]="errors"
|
||||
[isAllTimeHigh]="isAllTimeHigh"
|
||||
[isAllTimeLow]="isAllTimeLow"
|
||||
[isLoading]="isLoadingPerformance"
|
||||
[locale]="user?.settings?.locale"
|
||||
[performance]="performance"
|
||||
[showDetails]="showDetails"
|
||||
[unit]="unit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -6,7 +6,9 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-summary
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[hasPermissionToUpdateUserSettings]="!hasImpersonationId && hasPermissionToUpdateUserSettings"
|
||||
[hasPermissionToUpdateUserSettings]="
|
||||
!hasImpersonationId && hasPermissionToUpdateUserSettings
|
||||
"
|
||||
[isLoading]="isLoading"
|
||||
[language]="user?.settings?.language"
|
||||
[locale]="user?.settings?.locale"
|
||||
|
@ -87,7 +87,11 @@
|
||||
size="medium"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[ngClass]="{ 'text-danger': minPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||
[ngClass]="{
|
||||
'text-danger':
|
||||
minPrice?.toFixed(2) === marketPrice?.toFixed(2) &&
|
||||
maxPrice?.toFixed(2) !== minPrice?.toFixed(2)
|
||||
}"
|
||||
[unit]="SymbolProfile?.currency"
|
||||
[value]="minPrice"
|
||||
>Minimum Price</gf-value
|
||||
@ -99,7 +103,11 @@
|
||||
size="medium"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.locale"
|
||||
[ngClass]="{ 'text-success': maxPrice?.toFixed(2) === marketPrice?.toFixed(2) && maxPrice?.toFixed(2) !== minPrice?.toFixed(2) }"
|
||||
[ngClass]="{
|
||||
'text-success':
|
||||
maxPrice?.toFixed(2) === marketPrice?.toFixed(2) &&
|
||||
maxPrice?.toFixed(2) !== minPrice?.toFixed(2)
|
||||
}"
|
||||
[unit]="SymbolProfile?.currency"
|
||||
[value]="maxPrice"
|
||||
>Maximum Price</gf-value
|
||||
@ -184,53 +192,61 @@
|
||||
>
|
||||
</div>
|
||||
<ng-container
|
||||
*ngIf="SymbolProfile?.countries?.length > 0 || SymbolProfile?.sectors?.length > 0"
|
||||
*ngIf="
|
||||
SymbolProfile?.countries?.length > 0 ||
|
||||
SymbolProfile?.sectors?.length > 0
|
||||
"
|
||||
>
|
||||
@if(SymbolProfile?.countries?.length === 1 &&
|
||||
SymbolProfile?.sectors?.length === 1) {
|
||||
<div *ngIf="SymbolProfile?.sectors?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.sectors[0].name"
|
||||
>Sector</gf-value
|
||||
@if (
|
||||
SymbolProfile?.countries?.length === 1 &&
|
||||
SymbolProfile?.sectors?.length === 1
|
||||
) {
|
||||
<div *ngIf="SymbolProfile?.sectors?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.sectors[0].name"
|
||||
>Sector</gf-value
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="SymbolProfile?.countries?.length === 1"
|
||||
class="col-6 mb-3"
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="SymbolProfile?.countries?.length === 1" class="col-6 mb-3">
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.countries[0].name"
|
||||
>Country</gf-value
|
||||
>
|
||||
</div>
|
||||
<gf-value
|
||||
i18n
|
||||
size="medium"
|
||||
[locale]="data.locale"
|
||||
[value]="SymbolProfile.countries[0].name"
|
||||
>Country</gf-value
|
||||
>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Sectors</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="data.baseCurrency"
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="data.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="sectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Countries</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="data.baseCurrency"
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="data.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Sectors</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="data.baseCurrency"
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="data.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="sectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="h5" i18n>Countries</div>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="data.baseCurrency"
|
||||
[colorScheme]="data.colorScheme"
|
||||
[isInPercent]="true"
|
||||
[keys]="['name']"
|
||||
[locale]="data.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
<div *ngIf="dataProviderInfo" class="col-md-12 mb-3 text-center">
|
||||
@ -257,7 +273,9 @@
|
||||
[dataSource]="dataSource"
|
||||
[deviceType]="data.deviceType"
|
||||
[hasPermissionToCreateActivity]="false"
|
||||
[hasPermissionToExportActivities]="!data.hasImpersonationId && !user.settings.isRestrictedView"
|
||||
[hasPermissionToExportActivities]="
|
||||
!data.hasImpersonationId && !user.settings.isRestrictedView
|
||||
"
|
||||
[hasPermissionToFilter]="false"
|
||||
[hasPermissionToOpenDetails]="false"
|
||||
[locale]="data.locale"
|
||||
@ -294,15 +312,17 @@
|
||||
<div class="col">
|
||||
<div class="h5" i18n>Tags</div>
|
||||
<mat-chip-listbox>
|
||||
<mat-chip-option *ngFor="let tag of tags" disabled
|
||||
>{{ tag.name }}</mat-chip-option
|
||||
>
|
||||
<mat-chip-option *ngFor="let tag of tags" disabled>{{
|
||||
tag.name
|
||||
}}</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="activities?.length > 0 && data.hasPermissionToReportDataGlitch === true"
|
||||
*ngIf="
|
||||
activities?.length > 0 && data.hasPermissionToReportDataGlitch === true
|
||||
"
|
||||
class="row"
|
||||
>
|
||||
<div class="col">
|
||||
|
@ -28,30 +28,32 @@
|
||||
</div>
|
||||
|
||||
@if (accessForm.controls['type'].value === 'PRIVATE') {
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Permission</mat-label>
|
||||
<mat-select formControlName="permissions">
|
||||
<mat-option i18n value="READ_RESTRICTED">Restricted view</mat-option>
|
||||
@if(data?.user?.settings?.isExperimentalFeatures) {
|
||||
<mat-option i18n value="READ">View</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label>
|
||||
Ghostfolio <ng-container i18n>User ID</ng-container>
|
||||
</mat-label>
|
||||
<input
|
||||
formControlName="userId"
|
||||
matInput
|
||||
type="text"
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Permission</mat-label>
|
||||
<mat-select formControlName="permissions">
|
||||
<mat-option i18n value="READ_RESTRICTED"
|
||||
>Restricted view</mat-option
|
||||
>
|
||||
@if (data?.user?.settings?.isExperimentalFeatures) {
|
||||
<mat-option i18n value="READ">View</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label>
|
||||
Ghostfolio <ng-container i18n>User ID</ng-container>
|
||||
</mat-label>
|
||||
<input
|
||||
formControlName="userId"
|
||||
matInput
|
||||
type="text"
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="justify-content-end" mat-dialog-actions>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="col">
|
||||
<div class="align-items-center d-flex flex-column">
|
||||
<gf-membership-card
|
||||
[expiresAt]="user?.subscription?.expiresAt | date: defaultDateFormat"
|
||||
[expiresAt]="user?.subscription?.expiresAt | date: defaultDateFormat"
|
||||
[name]="user?.subscription?.type"
|
||||
/>
|
||||
<div
|
||||
@ -11,14 +11,19 @@
|
||||
class="d-flex flex-column mt-5"
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="hasPermissionForSubscription && hasPermissionToUpdateUserSettings"
|
||||
*ngIf="
|
||||
hasPermissionForSubscription && hasPermissionToUpdateUserSettings
|
||||
"
|
||||
>
|
||||
<button color="primary" mat-flat-button (click)="onCheckout()">
|
||||
<ng-container *ngIf="user.subscription.offer === 'default'" i18n
|
||||
>Upgrade Plan</ng-container
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="user.subscription.offer === 'renewal' || user.subscription.offer === 'renewal-early-bird'"
|
||||
*ngIf="
|
||||
user.subscription.offer === 'renewal' ||
|
||||
user.subscription.offer === 'renewal-early-bird'
|
||||
"
|
||||
i18n
|
||||
>Renew Plan</ng-container
|
||||
>
|
||||
@ -27,7 +32,8 @@
|
||||
<ng-container *ngIf="coupon"
|
||||
><del class="text-muted"
|
||||
>{{ baseCurrency }} {{ price }}</del
|
||||
> {{ baseCurrency }} {{ price - coupon
|
||||
> {{ baseCurrency }} {{
|
||||
price - coupon
|
||||
}}</ng-container
|
||||
>
|
||||
<ng-container *ngIf="!coupon"
|
||||
|
@ -32,7 +32,9 @@
|
||||
name="baseCurrency"
|
||||
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||
[value]="user.settings.baseCurrency"
|
||||
(selectionChange)="onChangeUserSetting('baseCurrency', $event.value)"
|
||||
(selectionChange)="
|
||||
onChangeUserSetting('baseCurrency', $event.value)
|
||||
"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let currency of currencies"
|
||||
@ -53,7 +55,9 @@
|
||||
>
|
||||
If a translation is missing, kindly support us in extending it
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/apps/client/src/locales/messages.{{language}}.xlf"
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/apps/client/src/locales/messages.{{
|
||||
language
|
||||
}}.xlf"
|
||||
target="_blank"
|
||||
>here</a
|
||||
>.
|
||||
@ -65,7 +69,9 @@
|
||||
name="language"
|
||||
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||
[value]="language"
|
||||
(selectionChange)="onChangeUserSetting('language', $event.value)"
|
||||
(selectionChange)="
|
||||
onChangeUserSetting('language', $event.value)
|
||||
"
|
||||
>
|
||||
<mat-option [value]="null" />
|
||||
<mat-option value="de">Deutsch</mat-option>
|
||||
@ -115,12 +121,14 @@
|
||||
name="locale"
|
||||
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||
[value]="user.settings.locale"
|
||||
(selectionChange)="onChangeUserSetting('locale', $event.value)"
|
||||
(selectionChange)="
|
||||
onChangeUserSetting('locale', $event.value)
|
||||
"
|
||||
>
|
||||
<mat-option [value]="null" />
|
||||
<mat-option *ngFor="let locale of locales" [value]="locale"
|
||||
>{{ locale }}</mat-option
|
||||
>
|
||||
<mat-option *ngFor="let locale of locales" [value]="locale">{{
|
||||
locale
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@ -137,7 +145,9 @@
|
||||
[disabled]="!hasPermissionToUpdateUserSettings"
|
||||
[placeholder]="appearancePlaceholder"
|
||||
[value]="user?.settings?.colorScheme"
|
||||
(selectionChange)="onChangeUserSetting('colorScheme', $event.value)"
|
||||
(selectionChange)="
|
||||
onChangeUserSetting('colorScheme', $event.value)
|
||||
"
|
||||
>
|
||||
<mat-option i18n [value]="null">Auto</mat-option>
|
||||
<mat-option i18n value="LIGHT">Light</mat-option>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -11,26 +11,28 @@
|
||||
</h1>
|
||||
<div class="row">
|
||||
@for (ossFriend of ossFriends; track ossFriend) {
|
||||
<div class="col-xs-12 col-md-4 mb-3">
|
||||
<a target="_blank" [href]="ossFriend.href">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-header>
|
||||
<mat-card-title class="h4">{{ ossFriend.name }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex-grow-1">
|
||||
<p>{{ ossFriend.description }}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="justify-content-end">
|
||||
<a mat-button target="_blank" [href]="ossFriend.href">
|
||||
<span
|
||||
><ng-container i18n>Visit</ng-container> {{ ossFriend.name
|
||||
}}</span
|
||||
><ion-icon class="ml-1" name="arrow-forward-outline" />
|
||||
</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 mb-3">
|
||||
<a target="_blank" [href]="ossFriend.href">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-header>
|
||||
<mat-card-title class="h4">{{
|
||||
ossFriend.name
|
||||
}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex-grow-1">
|
||||
<p>{{ ossFriend.description }}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="justify-content-end">
|
||||
<a mat-button target="_blank" [href]="ossFriend.href">
|
||||
<span
|
||||
><ng-container i18n>Visit</ng-container>
|
||||
{{ ossFriend.name }}</span
|
||||
><ion-icon class="ml-1" name="arrow-forward-outline" />
|
||||
</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -147,15 +147,15 @@
|
||||
>
|
||||
</div>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<div class="col-md-6 col-xs-12 my-2">
|
||||
<a
|
||||
class="py-4 w-100"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/blog']"
|
||||
>Blog</a
|
||||
>
|
||||
</div>
|
||||
<div class="col-md-6 col-xs-12 my-2">
|
||||
<a
|
||||
class="py-4 w-100"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/blog']"
|
||||
>Blog</a
|
||||
>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,11 @@
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[deviceType]="deviceType"
|
||||
[locale]="user?.settings?.locale"
|
||||
[showActions]="!hasImpersonationId && hasPermissionToUpdateAccount && !user.settings.isRestrictedView"
|
||||
[showActions]="
|
||||
!hasImpersonationId &&
|
||||
hasPermissionToUpdateAccount &&
|
||||
!user.settings.isRestrictedView
|
||||
"
|
||||
[totalBalanceInBaseCurrency]="totalBalanceInBaseCurrency"
|
||||
[totalValueInBaseCurrency]="totalValueInBaseCurrency"
|
||||
[transactionCount]="transactionCount"
|
||||
@ -21,7 +25,11 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="!hasImpersonationId && hasPermissionToCreateAccount && !user.settings.isRestrictedView"
|
||||
*ngIf="
|
||||
!hasImpersonationId &&
|
||||
hasPermissionToCreateAccount &&
|
||||
!user.settings.isRestrictedView
|
||||
"
|
||||
class="fab-container"
|
||||
>
|
||||
<a
|
||||
|
@ -5,9 +5,9 @@
|
||||
(ngSubmit)="onSubmit()"
|
||||
>
|
||||
@if (data.account.id) {
|
||||
<h1 i18n mat-dialog-title>Update account</h1>
|
||||
<h1 i18n mat-dialog-title>Update account</h1>
|
||||
} @else {
|
||||
<h1 i18n mat-dialog-title>Add account</h1>
|
||||
<h1 i18n mat-dialog-title>Add account</h1>
|
||||
}
|
||||
<div class="flex-grow-1 py-3" mat-dialog-content>
|
||||
<div>
|
||||
@ -38,9 +38,9 @@
|
||||
type="number"
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
/>
|
||||
<span class="ml-2" matTextSuffix
|
||||
>{{ accountForm.controls['currency']?.value?.value }}</span
|
||||
>
|
||||
<span class="ml-2" matTextSuffix>{{
|
||||
accountForm.controls['currency']?.value?.value
|
||||
}}</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div [ngClass]="{ 'd-none': platforms?.length < 1 }">
|
||||
@ -55,18 +55,20 @@
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
/>
|
||||
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
|
||||
@for (platformEntry of filteredPlatforms | async; track platformEntry)
|
||||
{
|
||||
<mat-option [value]="platformEntry">
|
||||
<span class="d-flex">
|
||||
<gf-symbol-icon
|
||||
class="mr-1"
|
||||
[tooltip]="platformEntry.name"
|
||||
[url]="platformEntry.url"
|
||||
/>
|
||||
<span>{{ platformEntry.name }}</span>
|
||||
</span>
|
||||
</mat-option>
|
||||
@for (
|
||||
platformEntry of filteredPlatforms | async;
|
||||
track platformEntry
|
||||
) {
|
||||
<mat-option [value]="platformEntry">
|
||||
<span class="d-flex">
|
||||
<gf-symbol-icon
|
||||
class="mr-1"
|
||||
[tooltip]="platformEntry.name"
|
||||
[url]="platformEntry.url"
|
||||
/>
|
||||
<span>{{ platformEntry.name }}</span>
|
||||
</span>
|
||||
</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
@ -89,12 +91,12 @@
|
||||
>
|
||||
</div>
|
||||
@if (data.account.id) {
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Account ID</mat-label>
|
||||
<input formControlName="accountId" matInput />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Account ID</mat-label>
|
||||
<input formControlName="accountId" matInput />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="justify-content-end" mat-dialog-actions>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -9,30 +9,30 @@
|
||||
>
|
||||
</h1>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
href="../en/blog/2023/11/black-week-2023"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">Black Week 2023</div>
|
||||
<div class="d-flex text-muted">2023-11-19</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
href="../en/blog/2023/11/black-week-2023"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">Black Week 2023</div>
|
||||
<div class="d-flex text-muted">2023-11-19</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
@ -294,30 +294,30 @@
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
href="../en/blog/2022/11/black-friday-2022"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">Black Friday 2022</div>
|
||||
<div class="d-flex text-muted">2022-11-13</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
href="../en/blog/2022/11/black-friday-2022"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">Black Friday 2022</div>
|
||||
<div class="d-flex text-muted">2022-11-13</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -140,7 +140,7 @@
|
||||
<h4 class="align-items-center d-flex">
|
||||
<span i18n>Portfolio Calculations</span>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
}
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
@ -159,7 +159,7 @@
|
||||
<h4 class="align-items-center d-flex">
|
||||
<span i18n>Portfolio Allocations</span>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
}
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
@ -197,24 +197,24 @@
|
||||
</mat-card>
|
||||
</div>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<div class="col-xs-12 col-md-4 mb-3">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-content>
|
||||
<div class="flex-grow-1">
|
||||
<h4 class="align-items-center d-flex">
|
||||
<span i18n>Market Mood</span>
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
Check the current market mood (<a
|
||||
[routerLink]="routerLinkResources"
|
||||
>Fear & Greed Index</a
|
||||
>) within the app.
|
||||
</p>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 mb-3">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-content>
|
||||
<div class="flex-grow-1">
|
||||
<h4 class="align-items-center d-flex">
|
||||
<span i18n>Market Mood</span>
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
Check the current market mood (<a
|
||||
[routerLink]="routerLinkResources"
|
||||
>Fear & Greed Index</a
|
||||
>) within the app.
|
||||
</p>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
<div class="col-xs-12 col-md-4 mb-3">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
@ -223,7 +223,7 @@
|
||||
<h4 class="align-items-center d-flex">
|
||||
<span i18n>Static Analysis</span>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
<gf-premium-indicator class="ml-1" />
|
||||
}
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
@ -290,12 +290,16 @@
|
||||
</div>
|
||||
</div>
|
||||
@if (!user) {
|
||||
<div class="row">
|
||||
<div class="col mt-3 text-center">
|
||||
<a color="primary" i18n mat-flat-button [routerLink]="routerLinkRegister"
|
||||
>Get Started</a
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col mt-3 text-center">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-flat-button
|
||||
[routerLink]="routerLinkRegister"
|
||||
>Get Started</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -339,7 +339,8 @@
|
||||
[href]="testimonial.url"
|
||||
>{{ testimonial.author }}</a
|
||||
>
|
||||
<span *ngIf="!testimonial.url">{{ testimonial.author }}</span>,
|
||||
<span *ngIf="!testimonial.url">{{ testimonial.author }}</span
|
||||
>,
|
||||
{{ testimonial.country }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,11 @@
|
||||
[locale]="user?.settings?.locale"
|
||||
[pageIndex]="pageIndex"
|
||||
[pageSize]="pageSize"
|
||||
[showActions]="!hasImpersonationId && hasPermissionToDeleteActivity && !user.settings.isRestrictedView"
|
||||
[showActions]="
|
||||
!hasImpersonationId &&
|
||||
hasPermissionToDeleteActivity &&
|
||||
!user.settings.isRestrictedView
|
||||
"
|
||||
[sortColumn]="sortColumn"
|
||||
[sortDirection]="sortDirection"
|
||||
[totalItems]="totalItems"
|
||||
@ -29,18 +33,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!hasImpersonationId && hasPermissionToCreateActivity &&
|
||||
!user.settings.isRestrictedView) {
|
||||
<div class="fab-container">
|
||||
<a
|
||||
class="align-items-center d-flex justify-content-center"
|
||||
color="primary"
|
||||
mat-fab
|
||||
[queryParams]="{ createDialog: true }"
|
||||
[routerLink]="[]"
|
||||
>
|
||||
<ion-icon name="add-outline" size="large" />
|
||||
</a>
|
||||
</div>
|
||||
@if (
|
||||
!hasImpersonationId &&
|
||||
hasPermissionToCreateActivity &&
|
||||
!user.settings.isRestrictedView
|
||||
) {
|
||||
<div class="fab-container">
|
||||
<a
|
||||
class="align-items-center d-flex justify-content-center"
|
||||
color="primary"
|
||||
mat-fab
|
||||
[queryParams]="{ createDialog: true }"
|
||||
[routerLink]="[]"
|
||||
>
|
||||
<ion-icon name="add-outline" size="large" />
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -11,48 +11,61 @@
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Type</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
<mat-select-trigger
|
||||
>{{ typesTranslationMap[activityForm.controls['type'].value]
|
||||
}}</mat-select-trigger
|
||||
>
|
||||
<mat-select-trigger>{{
|
||||
typesTranslationMap[activityForm.controls['type'].value]
|
||||
}}</mat-select-trigger>
|
||||
<mat-option value="BUY">
|
||||
<span><b>{{ typesTranslationMap['BUY'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['BUY'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Stocks, ETFs, bonds, cryptocurrencies, commodities</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="FEE">
|
||||
<span><b>{{ typesTranslationMap['FEE'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['FEE'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>One-time fee, annual account fees</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="DIVIDEND">
|
||||
<span><b>{{ typesTranslationMap['DIVIDEND'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['DIVIDEND'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Distribution of corporate earnings</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="INTEREST">
|
||||
<span><b>{{ typesTranslationMap['INTEREST'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['INTEREST'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Revenue for lending out money</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="LIABILITY">
|
||||
<span><b>{{ typesTranslationMap['LIABILITY'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['LIABILITY'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Mortgages, personal loans, credit cards</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="SELL">
|
||||
<span><b>{{ typesTranslationMap['SELL'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['SELL'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Stocks, ETFs, bonds, cryptocurrencies, commodities</small
|
||||
>
|
||||
</mat-option>
|
||||
<mat-option value="ITEM">
|
||||
<span><b>{{ typesTranslationMap['ITEM'] }}</b></span>
|
||||
<span
|
||||
><b>{{ typesTranslationMap['ITEM'] }}</b></span
|
||||
>
|
||||
<small class="d-block line-height-1 text-muted text-nowrap" i18n
|
||||
>Luxury items, real estate, private companies</small
|
||||
>
|
||||
@ -60,16 +73,20 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div [ngClass]="{'mb-3': data.activity.id}">
|
||||
<div [ngClass]="{ 'mb-3': data.activity.id }">
|
||||
<mat-form-field
|
||||
appearance="outline"
|
||||
class="w-100"
|
||||
[ngClass]="{'mb-1 without-hint': !data.activity.id}"
|
||||
[ngClass]="{ 'mb-1 without-hint': !data.activity.id }"
|
||||
>
|
||||
<mat-label i18n>Account</mat-label>
|
||||
<mat-select formControlName="accountId">
|
||||
<mat-option
|
||||
*ngIf="!activityForm.controls['accountId'].hasValidator(Validators.required)"
|
||||
*ngIf="
|
||||
!activityForm.controls['accountId'].hasValidator(
|
||||
Validators.required
|
||||
)
|
||||
"
|
||||
[value]="null"
|
||||
/>
|
||||
<mat-option
|
||||
@ -88,14 +105,18 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="mb-3" [ngClass]="{'d-none': data.activity.id}">
|
||||
<div class="mb-3" [ngClass]="{ 'd-none': data.activity.id }">
|
||||
<mat-checkbox color="primary" formControlName="updateAccountBalance" i18n
|
||||
>Update Cash Balance</mat-checkbox
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="{ 'd-none': !activityForm.controls['searchSymbol'].hasValidator(Validators.required) }"
|
||||
[ngClass]="{
|
||||
'd-none': !activityForm.controls['searchSymbol'].hasValidator(
|
||||
Validators.required
|
||||
)
|
||||
}"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Name, symbol or ISIN</mat-label>
|
||||
@ -107,7 +128,11 @@
|
||||
</div>
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="{ 'd-none': !activityForm.controls['name'].hasValidator(Validators.required) }"
|
||||
[ngClass]="{
|
||||
'd-none': !activityForm.controls['name'].hasValidator(
|
||||
Validators.required
|
||||
)
|
||||
}"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Name</mat-label>
|
||||
@ -118,9 +143,9 @@
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Currency</mat-label>
|
||||
<mat-select formControlName="currency">
|
||||
<mat-option *ngFor="let currency of currencies" [value]="currency"
|
||||
>{{ currency }}</mat-option
|
||||
>
|
||||
<mat-option *ngFor="let currency of currencies" [value]="currency">{{
|
||||
currency
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@ -146,7 +171,13 @@
|
||||
</div>
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="{ 'd-none': activityForm.controls['type']?.value === 'FEE' || activityForm.controls['type']?.value === 'INTEREST' || activityForm.controls['type']?.value === 'ITEM' || activityForm.controls['type']?.value === 'LIABILITY' }"
|
||||
[ngClass]="{
|
||||
'd-none':
|
||||
activityForm.controls['type']?.value === 'FEE' ||
|
||||
activityForm.controls['type']?.value === 'INTEREST' ||
|
||||
activityForm.controls['type']?.value === 'ITEM' ||
|
||||
activityForm.controls['type']?.value === 'LIABILITY'
|
||||
}"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Quantity</mat-label>
|
||||
@ -155,7 +186,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="{ 'd-none': activityForm.controls['type']?.value === 'FEE' }"
|
||||
[ngClass]="{ 'd-none': activityForm.controls['type']?.value === 'FEE' }"
|
||||
>
|
||||
<div class="align-items-start d-flex">
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
@ -192,17 +223,25 @@
|
||||
</mat-select>
|
||||
</div>
|
||||
<mat-error
|
||||
*ngIf="activityForm.controls['unitPriceInCustomCurrency'].hasError('invalid')"
|
||||
*ngIf="
|
||||
activityForm.controls['unitPriceInCustomCurrency'].hasError(
|
||||
'invalid'
|
||||
)
|
||||
"
|
||||
><ng-container i18n
|
||||
>Oops! Could not get the historical exchange rate
|
||||
from</ng-container
|
||||
>
|
||||
{{ activityForm.controls['date']?.value | date: defaultDateFormat
|
||||
{{
|
||||
activityForm.controls['date']?.value | date: defaultDateFormat
|
||||
}}</mat-error
|
||||
>
|
||||
</mat-form-field>
|
||||
<button
|
||||
*ngIf="currentMarketPrice && (data.activity.type === 'BUY' || data.activity.type === 'SELL')"
|
||||
*ngIf="
|
||||
currentMarketPrice &&
|
||||
(data.activity.type === 'BUY' || data.activity.type === 'SELL')
|
||||
"
|
||||
class="ml-2 mt-1 no-min-width"
|
||||
mat-button
|
||||
title="Apply current market price"
|
||||
@ -228,14 +267,19 @@
|
||||
</ng-container>
|
||||
</mat-label>
|
||||
<input formControlName="unitPrice" matInput type="number" />
|
||||
<span class="ml-2" matTextSuffix
|
||||
>{{ activityForm.controls['currency'].value }}</span
|
||||
>
|
||||
<span class="ml-2" matTextSuffix>{{
|
||||
activityForm.controls['currency'].value
|
||||
}}</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="{ 'd-none': activityForm.controls['type']?.value === 'INTEREST' || activityForm.controls['type']?.value === 'ITEM' || activityForm.controls['type']?.value === 'LIABILITY' }"
|
||||
[ngClass]="{
|
||||
'd-none':
|
||||
activityForm.controls['type']?.value === 'INTEREST' ||
|
||||
activityForm.controls['type']?.value === 'ITEM' ||
|
||||
activityForm.controls['type']?.value === 'LIABILITY'
|
||||
}"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Fee</mat-label>
|
||||
@ -252,11 +296,14 @@
|
||||
</mat-select>
|
||||
</div>
|
||||
<mat-error
|
||||
*ngIf="activityForm.controls['feeInCustomCurrency'].hasError('invalid')"
|
||||
*ngIf="
|
||||
activityForm.controls['feeInCustomCurrency'].hasError('invalid')
|
||||
"
|
||||
><ng-container i18n
|
||||
>Oops! Could not get the historical exchange rate from</ng-container
|
||||
>
|
||||
{{ activityForm.controls['date']?.value | date: defaultDateFormat
|
||||
{{
|
||||
activityForm.controls['date']?.value | date: defaultDateFormat
|
||||
}}</mat-error
|
||||
>
|
||||
</mat-form-field>
|
||||
@ -265,9 +312,9 @@
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Fee</mat-label>
|
||||
<input formControlName="fee" matInput type="number" />
|
||||
<span class="ml-2" matTextSuffix
|
||||
>{{ activityForm.controls['currency'].value }}</span
|
||||
>
|
||||
<span class="ml-2" matTextSuffix>{{
|
||||
activityForm.controls['currency'].value
|
||||
}}</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
@ -340,7 +387,7 @@
|
||||
(optionSelected)="onAddTag($event)"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let tag of filteredTagsObservable | async"
|
||||
*ngFor="let tag of filteredTagsObservable | async"
|
||||
[value]="tag.id"
|
||||
>
|
||||
{{ tag.name }}
|
||||
@ -354,7 +401,10 @@
|
||||
class="flex-grow-1"
|
||||
[isCurrency]="true"
|
||||
[locale]="data.user?.settings?.locale"
|
||||
[unit]="activityForm.controls['currency']?.value ?? data.user?.settings?.baseCurrency"
|
||||
[unit]="
|
||||
activityForm.controls['currency']?.value ??
|
||||
data.user?.settings?.baseCurrency
|
||||
"
|
||||
[value]="total"
|
||||
/>
|
||||
<div>
|
||||
|
@ -25,81 +25,86 @@
|
||||
</ng-template>
|
||||
<div class="pt-3">
|
||||
@if (mode === 'DIVIDEND') {
|
||||
<form
|
||||
[formGroup]="uniqueAssetForm"
|
||||
(ngSubmit)="onLoadDividends(stepper)"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Holding</mat-label>
|
||||
<mat-select formControlName="uniqueAsset">
|
||||
<mat-select-trigger
|
||||
>{{ uniqueAssetForm.controls['uniqueAsset']?.value?.name
|
||||
}}</mat-select-trigger
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let holding of holdings"
|
||||
class="line-height-1"
|
||||
[value]="{ dataSource: holding.dataSource, name: holding.name, symbol: holding.symbol }"
|
||||
>
|
||||
<span><b>{{ holding.name }}</b></span>
|
||||
<br />
|
||||
<small class="text-muted"
|
||||
>{{ holding.symbol | gfSymbol }} · {{ holding.currency
|
||||
}}</small
|
||||
<form
|
||||
[formGroup]="uniqueAssetForm"
|
||||
(ngSubmit)="onLoadDividends(stepper)"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Holding</mat-label>
|
||||
<mat-select formControlName="uniqueAsset">
|
||||
<mat-select-trigger>{{
|
||||
uniqueAssetForm.controls['uniqueAsset']?.value?.name
|
||||
}}</mat-select-trigger>
|
||||
<mat-option
|
||||
*ngFor="let holding of holdings"
|
||||
class="line-height-1"
|
||||
[value]="{
|
||||
dataSource: holding.dataSource,
|
||||
name: holding.name,
|
||||
symbol: holding.symbol
|
||||
}"
|
||||
>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-spinner
|
||||
*ngIf="isLoading"
|
||||
class="position-absolute"
|
||||
[diameter]="20"
|
||||
/>
|
||||
</mat-form-field>
|
||||
<span
|
||||
><b>{{ holding.name }}</b></span
|
||||
>
|
||||
<br />
|
||||
<small class="text-muted"
|
||||
>{{ holding.symbol | gfSymbol }} ·
|
||||
{{ holding.currency }}</small
|
||||
>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-spinner
|
||||
*ngIf="isLoading"
|
||||
class="position-absolute"
|
||||
[diameter]="20"
|
||||
/>
|
||||
</mat-form-field>
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<button
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
type="submit"
|
||||
[disabled]="!uniqueAssetForm.valid"
|
||||
>
|
||||
<span i18n>Load Dividends</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
} @else {
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<button
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
type="submit"
|
||||
[disabled]="!uniqueAssetForm.valid"
|
||||
class="drop-area p-4 text-center text-muted"
|
||||
gfFileDrop
|
||||
(click)="onSelectFile(stepper)"
|
||||
(filesDropped)="onFilesDropped({ stepper, files: $event })"
|
||||
>
|
||||
<span i18n>Load Dividends</span>
|
||||
<div
|
||||
class="align-items-center d-flex flex-column justify-content-center"
|
||||
>
|
||||
<ion-icon class="cloud-icon" name="cloud-upload-outline" />
|
||||
<span i18n>Choose or drop a file here</span>
|
||||
</div>
|
||||
</button>
|
||||
<p class="mb-0 mt-3 text-center">
|
||||
<small>
|
||||
<span class="mr-1" i18n
|
||||
>The following file formats are supported:</span
|
||||
>
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/test/import/ok.csv"
|
||||
target="_blank"
|
||||
>CSV</a
|
||||
>
|
||||
<span class="mx-1" i18n>or</span>
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/test/import/ok.json"
|
||||
target="_blank"
|
||||
>JSON</a
|
||||
>
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
} @else {
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<button
|
||||
class="drop-area p-4 text-center text-muted"
|
||||
gfFileDrop
|
||||
(click)="onSelectFile(stepper)"
|
||||
(filesDropped)="onFilesDropped({stepper, files: $event})"
|
||||
>
|
||||
<div
|
||||
class="align-items-center d-flex flex-column justify-content-center"
|
||||
>
|
||||
<ion-icon class="cloud-icon" name="cloud-upload-outline" />
|
||||
<span i18n>Choose or drop a file here</span>
|
||||
</div>
|
||||
</button>
|
||||
<p class="mb-0 mt-3 text-center">
|
||||
<small>
|
||||
<span class="mr-1" i18n
|
||||
>The following file formats are supported:</span
|
||||
>
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/test/import/ok.csv"
|
||||
target="_blank"
|
||||
>CSV</a
|
||||
>
|
||||
<span class="mx-1" i18n>or</span>
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio/blob/main/test/import/ok.json"
|
||||
target="_blank"
|
||||
>JSON</a
|
||||
>
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-step>
|
||||
@ -114,76 +119,76 @@
|
||||
>
|
||||
</ng-template>
|
||||
<div class="pt-3">
|
||||
@if(errorMessages?.length === 0) {
|
||||
<gf-activities-table
|
||||
*ngIf="importStep === 1"
|
||||
[baseCurrency]="data?.user?.settings?.baseCurrency"
|
||||
[dataSource]="dataSource"
|
||||
[deviceType]="data?.deviceType"
|
||||
[hasPermissionToCreateActivity]="false"
|
||||
[hasPermissionToExportActivities]="false"
|
||||
[hasPermissionToFilter]="false"
|
||||
[hasPermissionToOpenDetails]="false"
|
||||
[locale]="data?.user?.settings?.locale"
|
||||
[pageSize]="maxSafeInteger"
|
||||
[showActions]="false"
|
||||
[showCheckbox]="true"
|
||||
[showSymbolColumn]="false"
|
||||
[sortColumn]="sortColumn"
|
||||
[sortDirection]="sortDirection"
|
||||
[sortDisabled]="true"
|
||||
[totalItems]="totalItems"
|
||||
(selectedActivities)="updateSelection($event)"
|
||||
/>
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button mat-button (click)="onReset(stepper)">
|
||||
<ng-container i18n>Back</ng-container>
|
||||
</button>
|
||||
<button
|
||||
class="ml-1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[disabled]="!selectedActivities?.length"
|
||||
(click)="onImportActivities()"
|
||||
>
|
||||
<ng-container i18n>Import</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
@if (errorMessages?.length === 0) {
|
||||
<gf-activities-table
|
||||
*ngIf="importStep === 1"
|
||||
[baseCurrency]="data?.user?.settings?.baseCurrency"
|
||||
[dataSource]="dataSource"
|
||||
[deviceType]="data?.deviceType"
|
||||
[hasPermissionToCreateActivity]="false"
|
||||
[hasPermissionToExportActivities]="false"
|
||||
[hasPermissionToFilter]="false"
|
||||
[hasPermissionToOpenDetails]="false"
|
||||
[locale]="data?.user?.settings?.locale"
|
||||
[pageSize]="maxSafeInteger"
|
||||
[showActions]="false"
|
||||
[showCheckbox]="true"
|
||||
[showSymbolColumn]="false"
|
||||
[sortColumn]="sortColumn"
|
||||
[sortDirection]="sortDirection"
|
||||
[sortDisabled]="true"
|
||||
[totalItems]="totalItems"
|
||||
(selectedActivities)="updateSelection($event)"
|
||||
/>
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button mat-button (click)="onReset(stepper)">
|
||||
<ng-container i18n>Back</ng-container>
|
||||
</button>
|
||||
<button
|
||||
class="ml-1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[disabled]="!selectedActivities?.length"
|
||||
(click)="onImportActivities()"
|
||||
>
|
||||
<ng-container i18n>Import</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
} @else {
|
||||
<mat-accordion displayMode="flat">
|
||||
<mat-expansion-panel
|
||||
*ngFor="let message of errorMessages; let i = index"
|
||||
[disabled]="!details[i]"
|
||||
>
|
||||
<mat-expansion-panel-header class="pl-1">
|
||||
<mat-panel-title>
|
||||
<div class="d-flex">
|
||||
<div class="align-items-center d-flex mr-2">
|
||||
<ion-icon name="warning-outline" />
|
||||
<mat-accordion displayMode="flat">
|
||||
<mat-expansion-panel
|
||||
*ngFor="let message of errorMessages; let i = index"
|
||||
[disabled]="!details[i]"
|
||||
>
|
||||
<mat-expansion-panel-header class="pl-1">
|
||||
<mat-panel-title>
|
||||
<div class="d-flex">
|
||||
<div class="align-items-center d-flex mr-2">
|
||||
<ion-icon name="warning-outline" />
|
||||
</div>
|
||||
<div>{{ message }}</div>
|
||||
</div>
|
||||
<div>{{ message }}</div>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<pre
|
||||
*ngIf="details[i]"
|
||||
class="m-0"
|
||||
><code>{{ details[i] | json }}</code></pre>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button mat-button (click)="onReset(stepper)">
|
||||
<ng-container i18n>Back</ng-container>
|
||||
</button>
|
||||
<button
|
||||
class="ml-1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[disabled]="true"
|
||||
>
|
||||
<ng-container i18n>Import</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<pre
|
||||
*ngIf="details[i]"
|
||||
class="m-0"
|
||||
><code>{{ details[i] | json }}</code></pre>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button mat-button (click)="onReset(stepper)">
|
||||
<ng-container i18n>Back</ng-container>
|
||||
</button>
|
||||
<button
|
||||
class="ml-1"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[disabled]="true"
|
||||
>
|
||||
<ng-container i18n>Import</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-step>
|
||||
|
@ -15,13 +15,20 @@
|
||||
class="justify-content-end l-2"
|
||||
size="medium"
|
||||
[isPercent]="true"
|
||||
[value]="isLoading ? undefined : portfolioDetails?.filteredValueInPercentage"
|
||||
[value]="
|
||||
isLoading
|
||||
? undefined
|
||||
: portfolioDetails?.filteredValueInPercentage
|
||||
"
|
||||
/>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-progress-bar
|
||||
mode="determinate"
|
||||
[title]="(portfolioDetails?.filteredValueInPercentage * 100).toFixed(2) + '%'"
|
||||
[title]="
|
||||
(portfolioDetails?.filteredValueInPercentage * 100).toFixed(2) +
|
||||
'%'
|
||||
"
|
||||
[value]="portfolioDetails?.filteredValueInPercentage * 100"
|
||||
/>
|
||||
</mat-card-content>
|
||||
@ -204,7 +211,9 @@
|
||||
<gf-world-map-chart
|
||||
[countries]="countries"
|
||||
[format]="worldMapChartFormat"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[isInPercent]="
|
||||
hasImpersonationId || user.settings.isRestrictedView
|
||||
"
|
||||
[locale]="user?.settings?.locale"
|
||||
/>
|
||||
</div>
|
||||
|
@ -19,105 +19,134 @@
|
||||
</div>
|
||||
|
||||
@if (user?.settings?.isExperimentalFeatures) {
|
||||
<div class="mb-5 row">
|
||||
<div class="col">
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Asset Performance
|
||||
<div class="mb-5 row">
|
||||
<div class="col">
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Asset Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformance
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="isLoadingInvestmentChart ? undefined : performance?.currentNetPerformance"
|
||||
/>
|
||||
<div class="d-flex mb-3 ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Asset Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformancePercent
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex mb-3 ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Asset Performance
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Currency Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformanceWithCurrencyEffect ===
|
||||
null
|
||||
? null
|
||||
: performance?.currentNetPerformanceWithCurrencyEffect -
|
||||
performance?.currentNetPerformance
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="isLoadingInvestmentChart ? undefined : performance?.currentNetPerformancePercent"
|
||||
/>
|
||||
<div class="d-flex ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Currency Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformancePercentWithCurrencyEffect -
|
||||
performance?.currentNetPerformancePercent
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Currency Performance
|
||||
<div><hr /></div>
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Net Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformanceWithCurrencyEffect
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="isLoadingInvestmentChart ? undefined : (performance?.currentNetPerformanceWithCurrencyEffect === null ? null : performance?.currentNetPerformanceWithCurrencyEffect - performance?.currentNetPerformance)"
|
||||
/>
|
||||
<div class="d-flex ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Net Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="
|
||||
isLoadingInvestmentChart
|
||||
? undefined
|
||||
: performance?.currentNetPerformancePercentWithCurrencyEffect
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Currency Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="isLoadingInvestmentChart ? undefined : performance?.currentNetPerformancePercentWithCurrencyEffect - performance?.currentNetPerformancePercent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div><hr /></div>
|
||||
<div class="d-flex py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Absolute Net Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[isCurrency]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[unit]="user?.settings?.baseCurrency"
|
||||
[value]="isLoadingInvestmentChart ? undefined : performance?.currentNetPerformanceWithCurrencyEffect"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex ml-3 py-1">
|
||||
<div class="flex-grow-1 mr-2 text-truncate" i18n>
|
||||
Net Performance
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gf-value
|
||||
class="justify-content-end"
|
||||
position="end"
|
||||
[colorizeSign]="true"
|
||||
[isPercent]="true"
|
||||
[locale]="user?.settings?.locale"
|
||||
[value]="isLoadingInvestmentChart ? undefined : performance?.currentNetPerformancePercentWithCurrencyEffect"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-5 row">
|
||||
|
@ -16,11 +16,14 @@
|
||||
[currency]="user?.settings?.baseCurrency"
|
||||
[deviceType]="deviceType"
|
||||
[fireWealth]="fireWealth?.toNumber()"
|
||||
[hasPermissionToUpdateUserSettings]="!hasImpersonationId && hasPermissionToUpdateUserSettings"
|
||||
[hasPermissionToUpdateUserSettings]="
|
||||
!hasImpersonationId && hasPermissionToUpdateUserSettings
|
||||
"
|
||||
[locale]="user?.settings?.locale"
|
||||
[ngStyle]="{
|
||||
opacity: user?.subscription?.type === 'Basic' ? '0.67' : 'initial',
|
||||
'pointer-events': user?.subscription?.type === 'Basic' ? 'none' : 'initial'
|
||||
'pointer-events':
|
||||
user?.subscription?.type === 'Basic' ? 'none' : 'initial'
|
||||
}"
|
||||
[projectedTotalAmount]="user?.settings?.projectedTotalAmount"
|
||||
[retirementDate]="user?.settings?.retirementDate"
|
||||
|
@ -14,15 +14,15 @@
|
||||
[locale]="user?.settings?.locale"
|
||||
/>
|
||||
@if (hasPermissionToCreateOrder && holdings?.length > 0) {
|
||||
<div class="text-center">
|
||||
<a
|
||||
class="mt-3"
|
||||
i18n
|
||||
mat-stroked-button
|
||||
[routerLink]="['/portfolio', 'activities']"
|
||||
>Manage Activities</a
|
||||
>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<a
|
||||
class="mt-3"
|
||||
i18n
|
||||
mat-stroked-button
|
||||
[routerLink]="['/portfolio', 'activities']"
|
||||
>Manage Activities</a
|
||||
>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -107,7 +107,7 @@
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="h-100"
|
||||
[ngClass]="{ 'active': user?.subscription?.type === 'Basic' }"
|
||||
[ngClass]="{ active: user?.subscription?.type === 'Basic' }"
|
||||
>
|
||||
<mat-card-content class="d-flex flex-column h-100">
|
||||
<div class="flex-grow-1">
|
||||
@ -164,7 +164,7 @@
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="h-100"
|
||||
[ngClass]="{ 'active': user?.subscription?.type === 'Premium' }"
|
||||
[ngClass]="{ active: user?.subscription?.type === 'Premium' }"
|
||||
>
|
||||
<mat-card-content class="d-flex flex-column h-100">
|
||||
<div class="flex-grow-1">
|
||||
@ -243,19 +243,22 @@
|
||||
<ng-container *ngIf="coupon"
|
||||
><del class="text-muted"
|
||||
>{{ baseCurrency }} {{ price }}</del
|
||||
> {{ baseCurrency }} <strong
|
||||
>{{ price - coupon }}</strong
|
||||
>
|
||||
> {{ baseCurrency }} <strong>{{
|
||||
price - coupon
|
||||
}}</strong>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!coupon"
|
||||
>{{ baseCurrency }} <strong
|
||||
>{{ price }}</strong
|
||||
></ng-container
|
||||
>{{ baseCurrency }} <strong>{{
|
||||
price
|
||||
}}</strong></ng-container
|
||||
> <span i18n>per year</span></span
|
||||
>
|
||||
</p>
|
||||
<div
|
||||
*ngIf="hasPermissionToUpdateUserSettings && user?.subscription?.type === 'Basic'"
|
||||
*ngIf="
|
||||
hasPermissionToUpdateUserSettings &&
|
||||
user?.subscription?.type === 'Basic'
|
||||
"
|
||||
class="mt-3 text-center"
|
||||
>
|
||||
<button color="primary" mat-flat-button (click)="onCheckout()">
|
||||
@ -265,7 +268,10 @@
|
||||
>Upgrade Plan</ng-container
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="user.subscription.offer === 'renewal' || user.subscription.offer === 'renewal-early-bird'"
|
||||
*ngIf="
|
||||
user.subscription.offer === 'renewal' ||
|
||||
user.subscription.offer === 'renewal-early-bird'
|
||||
"
|
||||
i18n
|
||||
>Renew Plan</ng-container
|
||||
>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span i18n>Create Account</span
|
||||
><span *ngIf="data.role === 'ADMIN'" class="badge badge-light ml-2"
|
||||
>{{ data.role }}</span
|
||||
>
|
||||
><span *ngIf="data.role === 'ADMIN'" class="badge badge-light ml-2">{{
|
||||
data.role
|
||||
}}</span>
|
||||
</h1>
|
||||
<div class="py-3" mat-dialog-content>
|
||||
<div>
|
||||
|
@ -19,32 +19,38 @@
|
||||
</p>
|
||||
</div>
|
||||
@for (product of products; track product) {
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
title="Compare Ghostfolio to {{ product.name }} - {{ product.slogan }}"
|
||||
[routerLink]="[pathResources, 'personal-finance-tools', pathAlternativeTo + (product.alias ?? product.key)]"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate" i18n>
|
||||
Open Source Alternative to {{ product.name }}
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
title="Compare Ghostfolio to {{ product.name }} - {{
|
||||
product.slogan
|
||||
}}"
|
||||
[routerLink]="[
|
||||
pathResources,
|
||||
'personal-finance-tools',
|
||||
pathAlternativeTo + (product.alias ?? product.key)
|
||||
]"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate" i18n>
|
||||
Open Source Alternative to {{ product.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,8 +11,9 @@
|
||||
</div>
|
||||
<section class="mb-4">
|
||||
<p i18n>
|
||||
Are you looking for an open source alternative to {{ product2.name
|
||||
}}? <a [routerLink]="routerLinkAbout">Ghostfolio</a> is a powerful
|
||||
Are you looking for an open source alternative to
|
||||
{{ product2.name }}?
|
||||
<a [routerLink]="routerLinkAbout">Ghostfolio</a> is a powerful
|
||||
portfolio management tool that provides individuals with a
|
||||
comprehensive platform to track, analyze, and optimize their
|
||||
investments. Whether you are an experienced investor or just
|
||||
@ -35,18 +36,22 @@
|
||||
its capabilities, security, and user experience.
|
||||
</p>
|
||||
<p i18n>
|
||||
Let’s dive deeper into the detailed Ghostfolio vs {{ product2.name
|
||||
}} comparison table below to gain a thorough understanding of how
|
||||
Ghostfolio positions itself relative to {{ product2.name }}. We will
|
||||
explore various aspects such as features, data privacy, pricing, and
|
||||
more, allowing you to make a well-informed choice for your personal
|
||||
requirements.
|
||||
Let’s dive deeper into the detailed Ghostfolio vs
|
||||
{{ product2.name }} comparison table below to gain a thorough
|
||||
understanding of how Ghostfolio positions itself relative to
|
||||
{{ product2.name }}. We will explore various aspects such as
|
||||
features, data privacy, pricing, and more, allowing you to make a
|
||||
well-informed choice for your personal requirements.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<table class="gf-table w-100">
|
||||
<caption class="text-center" i18n>
|
||||
Ghostfolio vs {{ product2.name }} comparison table
|
||||
Ghostfolio vs
|
||||
{{
|
||||
product2.name
|
||||
}}
|
||||
comparison table
|
||||
</caption>
|
||||
<thead>
|
||||
<tr class="mat-mdc-header-row">
|
||||
@ -187,8 +192,9 @@
|
||||
</td>
|
||||
<td class="mat-mdc-cell px-1 py-2">
|
||||
<ng-container *ngIf="product2.pricingPerYear"
|
||||
><span i18n>Starting from</span> {{ product2.pricingPerYear
|
||||
}} / <span i18n>year</span></ng-container
|
||||
><span i18n>Starting from</span>
|
||||
{{ product2.pricingPerYear }} /
|
||||
<span i18n>year</span></ng-container
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
@ -202,13 +208,13 @@
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<p i18n>
|
||||
Please note that the information provided in the Ghostfolio vs {{
|
||||
product2.name }} comparison table is based on our independent
|
||||
research and analysis. This website is not affiliated with {{
|
||||
product2.name }} or any other product mentioned in the comparison.
|
||||
As the landscape of personal finance tools evolves, it is essential
|
||||
to verify any specific details or changes directly from the
|
||||
respective product page. Data needs a refresh? Help us maintain
|
||||
Please note that the information provided in the Ghostfolio vs
|
||||
{{ product2.name }} comparison table is based on our independent
|
||||
research and analysis. This website is not affiliated with
|
||||
{{ product2.name }} or any other product mentioned in the
|
||||
comparison. As the landscape of personal finance tools evolves, it
|
||||
is essential to verify any specific details or changes directly from
|
||||
the respective product page. Data needs a refresh? Help us maintain
|
||||
accurate data on
|
||||
<a href="https://github.com/ghostfolio/ghostfolio">GitHub</a>.
|
||||
</p>
|
||||
|
@ -171,21 +171,21 @@
|
||||
</div>
|
||||
</div>
|
||||
@if (hasPermissionForSubscription) {
|
||||
<div class="mb-4 media">
|
||||
<div class="media-body">
|
||||
<h3 class="h5 mt-0">Personal Finance Tools</h3>
|
||||
<div class="mb-1">
|
||||
Personal finance tools are software applications that help
|
||||
individuals manage their money, track expenses, set budgets,
|
||||
monitor investments, and make informed financial decisions.
|
||||
</div>
|
||||
<div>
|
||||
<a [routerLink]="routerLinkResourcesPersonalFinanceTools"
|
||||
>Personal Finance Tools →</a
|
||||
>
|
||||
<div class="mb-4 media">
|
||||
<div class="media-body">
|
||||
<h3 class="h5 mt-0">Personal Finance Tools</h3>
|
||||
<div class="mb-1">
|
||||
Personal finance tools are software applications that help
|
||||
individuals manage their money, track expenses, set budgets,
|
||||
monitor investments, and make informed financial decisions.
|
||||
</div>
|
||||
<div>
|
||||
<a [routerLink]="routerLinkResourcesPersonalFinanceTools"
|
||||
>Personal Finance Tools →</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-4 media">
|
||||
<div class="media-body">
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -8,29 +8,29 @@
|
||||
</div>
|
||||
|
||||
@if (!hasError) {
|
||||
<div class="col d-flex justify-content-center">
|
||||
<mat-spinner [diameter]="20" />
|
||||
</div>
|
||||
<div class="col d-flex justify-content-center">
|
||||
<mat-spinner [diameter]="20" />
|
||||
</div>
|
||||
} @else {
|
||||
<div
|
||||
class="align-items-center col d-flex flex-column justify-content-center"
|
||||
>
|
||||
<h1 class="d-flex h5 justify-content-center mb-0 text-center">
|
||||
<ng-container i18n>Oops, authentication has failed.</ng-container>
|
||||
</h1>
|
||||
<button
|
||||
class="mb-3 mt-4"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
(click)="signIn()"
|
||||
<div
|
||||
class="align-items-center col d-flex flex-column justify-content-center"
|
||||
>
|
||||
<ng-container i18n>Try again</ng-container>
|
||||
</button>
|
||||
<div class="text-muted"><ng-container i18n>or</ng-container></div>
|
||||
<button class="mt-1" mat-flat-button (click)="deregisterDevice()">
|
||||
<ng-container i18n>Go back to Home Page</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="d-flex h5 justify-content-center mb-0 text-center">
|
||||
<ng-container i18n>Oops, authentication has failed.</ng-container>
|
||||
</h1>
|
||||
<button
|
||||
class="mb-3 mt-4"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
(click)="signIn()"
|
||||
>
|
||||
<ng-container i18n>Try again</ng-container>
|
||||
</button>
|
||||
<div class="text-muted"><ng-container i18n>or</ng-container></div>
|
||||
<button class="mt-1" mat-flat-button (click)="deregisterDevice()">
|
||||
<ng-container i18n>Go back to Home Page</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@
|
||||
>
|
||||
<ion-icon
|
||||
[name]="tab.iconName"
|
||||
[size]="deviceType === 'mobile' ? 'large': 'small'"
|
||||
[size]="deviceType === 'mobile' ? 'large' : 'small'"
|
||||
/>
|
||||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
|
||||
</a>
|
||||
|
@ -4,12 +4,13 @@
|
||||
[queryParams]="queryParams"
|
||||
[routerLink]="routerLink"
|
||||
(click)="onClick()"
|
||||
><span><b>{{ item?.name }}</b></span>
|
||||
><span
|
||||
><b>{{ item?.name }}</b></span
|
||||
>
|
||||
<br />
|
||||
<small class="text-muted"
|
||||
>{{ item?.symbol | gfSymbol }} · {{ item?.currency }}<ng-container
|
||||
*ngIf="item?.assetSubClassString"
|
||||
>
|
||||
>{{ item?.symbol | gfSymbol }} · {{ item?.currency
|
||||
}}<ng-container *ngIf="item?.assetSubClassString">
|
||||
· {{ item?.assetSubClassString }}</ng-container
|
||||
></small
|
||||
></a
|
||||
|
@ -39,7 +39,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="isLoading || searchFormControl.value"
|
||||
*ngIf="isLoading || searchFormControl.value"
|
||||
class="overflow-auto py-3 result-container"
|
||||
>
|
||||
<div>
|
||||
@ -56,9 +56,9 @@
|
||||
animation="pulse"
|
||||
class="mx-2"
|
||||
[theme]="{
|
||||
height: '1.5rem',
|
||||
width: '100%'
|
||||
}"
|
||||
height: '1.5rem',
|
||||
width: '100%'
|
||||
}"
|
||||
/>
|
||||
<div *ngIf="!isLoading" class="px-2 py-1" i18n>No entries...</div>
|
||||
</ng-container>
|
||||
@ -77,9 +77,9 @@
|
||||
animation="pulse"
|
||||
class="mx-2"
|
||||
[theme]="{
|
||||
height: '1.5rem',
|
||||
width: '100%'
|
||||
}"
|
||||
height: '1.5rem',
|
||||
width: '100%'
|
||||
}"
|
||||
/>
|
||||
<div *ngIf="!isLoading" class="px-2 py-1" i18n>No entries...</div>
|
||||
</ng-container>
|
||||
@ -87,7 +87,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<form [formGroup]="filterForm">
|
||||
<ng-container *ngIf="!(isLoading || searchFormControl.value)">
|
||||
<ng-container *ngIf="!(isLoading || searchFormControl.value)">
|
||||
<div class="date-range-selector-container p-3">
|
||||
<mat-form-field appearance="outline" class="w-100 without-hint">
|
||||
<mat-label i18n>Date Range</mat-label>
|
||||
@ -96,7 +96,7 @@
|
||||
(selectionChange)="onChangeDateRange($event.value)"
|
||||
>
|
||||
@for (range of dateRangeOptions; track range) {
|
||||
<mat-option [value]="range.value">{{ range.label }}</mat-option>
|
||||
<mat-option [value]="range.value">{{ range.label }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@ -108,16 +108,16 @@
|
||||
<mat-select formControlName="account">
|
||||
<mat-option [value]="null" />
|
||||
@for (account of accounts; track account.id) {
|
||||
<mat-option [value]="account.id">
|
||||
<div class="d-flex">
|
||||
<gf-symbol-icon
|
||||
*ngIf="account.Platform?.url"
|
||||
class="mr-1"
|
||||
[tooltip]="account.Platform?.name"
|
||||
[url]="account.Platform?.url"
|
||||
/><span>{{ account.name }}</span>
|
||||
</div>
|
||||
</mat-option>
|
||||
<mat-option [value]="account.id">
|
||||
<div class="d-flex">
|
||||
<gf-symbol-icon
|
||||
*ngIf="account.Platform?.url"
|
||||
class="mr-1"
|
||||
[tooltip]="account.Platform?.name"
|
||||
[url]="account.Platform?.url"
|
||||
/><span>{{ account.name }}</span>
|
||||
</div>
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@ -128,7 +128,7 @@
|
||||
<mat-select formControlName="tag">
|
||||
<mat-option [value]="null" />
|
||||
@for (tag of tags; track tag.id) {
|
||||
<mat-option [value]="tag.id">{{ tag.label }}</mat-option>
|
||||
<mat-option [value]="tag.id">{{ tag.label }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@ -139,9 +139,9 @@
|
||||
<mat-select formControlName="assetClass">
|
||||
<mat-option [value]="null" />
|
||||
@for (assetClass of assetClasses; track assetClass.id) {
|
||||
<mat-option [value]="assetClass.id"
|
||||
>{{ assetClass.label }}</mat-option
|
||||
>
|
||||
<mat-option [value]="assetClass.id">{{
|
||||
assetClass.label
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
Loading…
x
Reference in New Issue
Block a user