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