|
|
@ -40,179 +40,181 @@ |
|
|
</mat-card-actions> |
|
|
</mat-card-actions> |
|
|
</mat-card> |
|
|
</mat-card> |
|
|
} |
|
|
} |
|
|
<table |
|
|
<div class="overflow-x-auto"> |
|
|
class="gf-table w-100" |
|
|
<table |
|
|
mat-table |
|
|
class="gf-table w-100" |
|
|
matSort |
|
|
mat-table |
|
|
matSortActive="name" |
|
|
matSort |
|
|
matSortDirection="asc" |
|
|
matSortActive="name" |
|
|
[dataSource]="dataSource" |
|
|
matSortDirection="asc" |
|
|
> |
|
|
[dataSource]="dataSource" |
|
|
<ng-container matColumnDef="name"> |
|
|
> |
|
|
<th |
|
|
<ng-container matColumnDef="icon" sticky> |
|
|
*matHeaderCellDef |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th> |
|
|
class="px-1" |
|
|
<td *matCellDef="let element" class="px-1 text-center" mat-cell> |
|
|
mat-header-cell |
|
|
<gf-entity-logo [hasPlaceholder]="true" [url]="element.url" /> |
|
|
mat-sort-header="name" |
|
|
</td> |
|
|
> |
|
|
</ng-container> |
|
|
<ng-container i18n>Name</ng-container> |
|
|
|
|
|
</th> |
|
|
<ng-container matColumnDef="name"> |
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
<th |
|
|
<div class="d-flex align-items-center"> |
|
|
*matHeaderCellDef |
|
|
<gf-entity-logo |
|
|
class="px-1" |
|
|
class="mr-1" |
|
|
mat-header-cell |
|
|
[hasPlaceholder]="true" |
|
|
mat-sort-header="name" |
|
|
[url]="element.url" |
|
|
> |
|
|
/> |
|
|
<ng-container i18n>Name</ng-container> |
|
|
<div> |
|
|
</th> |
|
|
@if (isGhostfolioDataProvider(element)) { |
|
|
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell> |
|
|
<a |
|
|
@if (isGhostfolioDataProvider(element)) { |
|
|
class="align-items-center d-inline-flex" |
|
|
<a |
|
|
target="_blank" |
|
|
class="align-items-center d-inline-flex" |
|
|
[href]="pricingUrl" |
|
|
target="_blank" |
|
|
> |
|
|
[href]="pricingUrl" |
|
|
Ghostfolio Premium |
|
|
> |
|
|
<gf-premium-indicator |
|
|
Ghostfolio Premium |
|
|
class="d-inline-block ml-1" |
|
|
<gf-premium-indicator |
|
|
[enableLink]="false" |
|
|
class="d-inline-block ml-1" |
|
|
/> |
|
|
[enableLink]="false" |
|
|
@if (isGhostfolioApiKeyValid === false) { |
|
|
/> |
|
|
<span |
|
|
@if (isGhostfolioApiKeyValid === false) { |
|
|
class="badge badge-pill badge-secondary ml-2 text-uppercase" |
|
|
<span |
|
|
i18n |
|
|
class="badge badge-pill badge-secondary ml-2 text-uppercase" |
|
|
>popular</span |
|
|
i18n |
|
|
> |
|
|
>popular</span |
|
|
} |
|
|
> |
|
|
</a> |
|
|
|
|
|
@if (isGhostfolioApiKeyValid === true) { |
|
|
|
|
|
<div class="line-height-1"> |
|
|
|
|
|
<small class="text-muted"> |
|
|
|
|
|
<ng-container i18n>Valid until</ng-container> |
|
|
|
|
|
{{ |
|
|
|
|
|
ghostfolioApiStatus?.subscription?.expiresAt |
|
|
|
|
|
| date: defaultDateFormat |
|
|
|
|
|
}} |
|
|
|
|
|
</small> |
|
|
|
|
|
</div> |
|
|
|
|
|
} |
|
|
} |
|
|
} @else { |
|
|
</a> |
|
|
{{ element.name }} |
|
|
@if (isGhostfolioApiKeyValid === true) { |
|
|
|
|
|
<div class="line-height-1"> |
|
|
|
|
|
<small class="text-muted"> |
|
|
|
|
|
<ng-container i18n>Valid until</ng-container> |
|
|
|
|
|
{{ |
|
|
|
|
|
ghostfolioApiStatus?.subscription?.expiresAt |
|
|
|
|
|
| date: defaultDateFormat |
|
|
|
|
|
}} |
|
|
|
|
|
</small> |
|
|
|
|
|
</div> |
|
|
} |
|
|
} |
|
|
</div> |
|
|
} @else { |
|
|
</div> |
|
|
{{ element.name }} |
|
|
</td> |
|
|
} |
|
|
</ng-container> |
|
|
</td> |
|
|
|
|
|
</ng-container> |
|
|
|
|
|
|
|
|
<ng-container matColumnDef="status"> |
|
|
<ng-container matColumnDef="status"> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell> |
|
|
<ng-container i18n>Status</ng-container> |
|
|
<ng-container i18n>Status</ng-container> |
|
|
</th> |
|
|
</th> |
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
@if ( |
|
|
@if ( |
|
|
hasGhostfolioApiKey && |
|
|
hasGhostfolioApiKey && |
|
|
isGhostfolioApiKeyValid === false && |
|
|
isGhostfolioApiKeyValid === false && |
|
|
isGhostfolioDataProvider(element) |
|
|
isGhostfolioDataProvider(element) |
|
|
) { |
|
|
) { |
|
|
<span class="text-danger" i18n>Invalid API key</span> |
|
|
<span class="text-danger" i18n>Invalid API key</span> |
|
|
} @else if ( |
|
|
} @else if ( |
|
|
hasGhostfolioApiKey || !isGhostfolioDataProvider(element) |
|
|
hasGhostfolioApiKey || !isGhostfolioDataProvider(element) |
|
|
) { |
|
|
) { |
|
|
<gf-data-provider-status [dataSource]="element.dataSource" /> |
|
|
<gf-data-provider-status [dataSource]="element.dataSource" /> |
|
|
} |
|
|
} |
|
|
</td> |
|
|
</td> |
|
|
</ng-container> |
|
|
</ng-container> |
|
|
|
|
|
|
|
|
<ng-container matColumnDef="assetProfileCount"> |
|
|
<ng-container matColumnDef="assetProfileCount"> |
|
|
<th |
|
|
<th |
|
|
*matHeaderCellDef |
|
|
*matHeaderCellDef |
|
|
class="justify-content-end px-1" |
|
|
class="justify-content-end px-1" |
|
|
mat-header-cell |
|
|
mat-header-cell |
|
|
mat-sort-header="assetProfileCount" |
|
|
mat-sort-header="assetProfileCount" |
|
|
> |
|
|
> |
|
|
<ng-container i18n>Asset Profiles</ng-container> |
|
|
<ng-container i18n>Asset Profiles</ng-container> |
|
|
</th> |
|
|
</th> |
|
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|
|
<gf-value |
|
|
<gf-value |
|
|
class="d-inline-block justify-content-end" |
|
|
class="d-inline-block justify-content-end" |
|
|
[locale]="user?.settings?.locale" |
|
|
[locale]="user?.settings?.locale" |
|
|
[value]="element.assetProfileCount" |
|
|
[value]="element.assetProfileCount" |
|
|
/> |
|
|
/> |
|
|
</td> |
|
|
</td> |
|
|
</ng-container> |
|
|
</ng-container> |
|
|
|
|
|
|
|
|
<ng-container matColumnDef="usage"> |
|
|
<ng-container matColumnDef="usage"> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th> |
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
<td *matCellDef="let element" class="px-1" mat-cell> |
|
|
@if ( |
|
|
@if ( |
|
|
isGhostfolioDataProvider(element) && |
|
|
isGhostfolioDataProvider(element) && |
|
|
isGhostfolioApiKeyValid === true |
|
|
isGhostfolioApiKeyValid === true |
|
|
) { |
|
|
) { |
|
|
<div |
|
|
<div |
|
|
[matTooltip]="ghostfolioApiStatusTooltip" |
|
|
[matTooltip]="ghostfolioApiStatusTooltip" |
|
|
[matTooltipDisabled]="!ghostfolioApiStatus.isWithinSetupPeriod" |
|
|
[matTooltipDisabled]=" |
|
|
> |
|
|
!ghostfolioApiStatus.isWithinSetupPeriod |
|
|
<mat-progress-bar |
|
|
|
|
|
mode="determinate" |
|
|
|
|
|
[value]=" |
|
|
|
|
|
100 - |
|
|
|
|
|
(ghostfolioApiStatus.dailyRequests / |
|
|
|
|
|
ghostfolioApiStatus.dailyRequestsMax) * |
|
|
|
|
|
100 |
|
|
|
|
|
" |
|
|
" |
|
|
/> |
|
|
> |
|
|
<small class="text-muted"> |
|
|
<mat-progress-bar |
|
|
{{ ghostfolioApiStatus.dailyRequests }} |
|
|
mode="determinate" |
|
|
<ng-container i18n>of</ng-container> |
|
|
[value]=" |
|
|
{{ ghostfolioApiStatus.dailyRequestsMax }} |
|
|
100 - |
|
|
<ng-container i18n>daily requests</ng-container> |
|
|
(ghostfolioApiStatus.dailyRequests / |
|
|
</small> |
|
|
ghostfolioApiStatus.dailyRequestsMax) * |
|
|
</div> |
|
|
100 |
|
|
} |
|
|
" |
|
|
</td> |
|
|
/> |
|
|
</ng-container> |
|
|
<small class="text-muted"> |
|
|
|
|
|
{{ ghostfolioApiStatus.dailyRequests }} |
|
|
|
|
|
<ng-container i18n>of</ng-container> |
|
|
|
|
|
{{ ghostfolioApiStatus.dailyRequestsMax }} |
|
|
|
|
|
<ng-container i18n>daily requests</ng-container> |
|
|
|
|
|
</small> |
|
|
|
|
|
</div> |
|
|
|
|
|
} |
|
|
|
|
|
</td> |
|
|
|
|
|
</ng-container> |
|
|
|
|
|
|
|
|
<ng-container matColumnDef="actions"> |
|
|
<ng-container matColumnDef="actions"> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th> |
|
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th> |
|
|
|
|
|
|
|
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|
|
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|
|
@if (isGhostfolioDataProvider(element)) { |
|
|
@if (isGhostfolioDataProvider(element)) { |
|
|
@if (hasGhostfolioApiKey) { |
|
|
@if (hasGhostfolioApiKey) { |
|
|
<button |
|
|
<button |
|
|
class="mx-1 no-min-width px-2" |
|
|
class="mx-1 no-min-width px-2" |
|
|
mat-button |
|
|
mat-button |
|
|
[matMenuTriggerFor]="ghostfolioApiMenu" |
|
|
[matMenuTriggerFor]="ghostfolioApiMenu" |
|
|
(click)="$event.stopPropagation()" |
|
|
(click)="$event.stopPropagation()" |
|
|
> |
|
|
> |
|
|
<ion-icon name="ellipsis-horizontal" /> |
|
|
<ion-icon name="ellipsis-horizontal" /> |
|
|
</button> |
|
|
|
|
|
<mat-menu |
|
|
|
|
|
#ghostfolioApiMenu="matMenu" |
|
|
|
|
|
class="no-max-width" |
|
|
|
|
|
xPosition="before" |
|
|
|
|
|
> |
|
|
|
|
|
<button mat-menu-item (click)="onRemoveGhostfolioApiKey()"> |
|
|
|
|
|
<span class="align-items-center d-flex"> |
|
|
|
|
|
<ion-icon class="mr-2" name="trash-outline" /> |
|
|
|
|
|
<span i18n>Remove API key</span> |
|
|
|
|
|
</span> |
|
|
|
|
|
</button> |
|
|
</button> |
|
|
</mat-menu> |
|
|
<mat-menu |
|
|
} @else if (hasGhostfolioApiKey === false) { |
|
|
#ghostfolioApiMenu="matMenu" |
|
|
<button |
|
|
class="no-max-width" |
|
|
class="special" |
|
|
xPosition="before" |
|
|
mat-flat-button |
|
|
> |
|
|
(click)="onSetGhostfolioApiKey()" |
|
|
<button mat-menu-item (click)="onRemoveGhostfolioApiKey()"> |
|
|
> |
|
|
<span class="align-items-center d-flex"> |
|
|
<ng-container i18n>Set API key</ng-container> |
|
|
<ion-icon class="mr-2" name="trash-outline" /> |
|
|
</button> |
|
|
<span i18n>Remove API key</span> |
|
|
|
|
|
</span> |
|
|
|
|
|
</button> |
|
|
|
|
|
</mat-menu> |
|
|
|
|
|
} @else if (hasGhostfolioApiKey === false) { |
|
|
|
|
|
<button |
|
|
|
|
|
class="special text-nowrap" |
|
|
|
|
|
mat-flat-button |
|
|
|
|
|
(click)="onSetGhostfolioApiKey()" |
|
|
|
|
|
> |
|
|
|
|
|
<ng-container i18n>Set API key</ng-container> |
|
|
|
|
|
</button> |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
</td> |
|
|
</td> |
|
|
</ng-container> |
|
|
</ng-container> |
|
|
|
|
|
|
|
|
|
|
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> |
|
|
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> |
|
|
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr> |
|
|
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr> |
|
|
</table> |
|
|
</table> |
|
|
|
|
|
</div> |
|
|
@if (isLoading) { |
|
|
@if (isLoading) { |
|
|
<ngx-skeleton-loader |
|
|
<ngx-skeleton-loader |
|
|
animation="pulse" |
|
|
animation="pulse" |
|
|
|