Browse Source

migrate data providers from mat-card to mat-table

pull/4704/head
Andrei D 4 months ago
parent
commit
3d3936b067
  1. 182
      apps/client/src/app/components/admin-settings/admin-settings.component.html
  2. 19
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  3. 4
      apps/client/src/app/components/admin-settings/admin-settings.module.ts

182
apps/client/src/app/components/admin-settings/admin-settings.component.html

@ -1,105 +1,109 @@
<div class="container"> <div class="container">
<div class="d-md-block d-none mb-5 row"> <div class="mb-5 row">
<div class="col"> <div class="col">
<h2 class="text-center" i18n>Data Providers</h2> <h2 class="text-center" i18n>Data Providers</h2>
<mat-card appearance="outlined"> <table class="gf-table w-100" mat-table [dataSource]="dataSource">
<mat-card-content> <ng-container matColumnDef="name">
@for (dataProvider of dataProviders; track dataProvider.name) { <th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
<div class="align-items-center d-flex my-3"> <ng-container i18n>Name</ng-container>
@if (dataProvider.name === 'Ghostfolio') { </th>
<div class="w-50"> <td *matCellDef="let element" class="px-1 py-2" mat-cell>
<div class="d-flex"> <div class="d-flex align-items-center">
<gf-asset-profile-icon <gf-asset-profile-icon class="mr-1" [url]="element.url" />
class="mr-1" <div>
[url]="dataProvider.url" @if (isGhostfolioProvider(element)) {
<a
class="align-items-center d-inline-flex"
target="_blank"
[href]="pricingUrl"
>
Ghostfolio Premium
<gf-premium-indicator
class="d-inline-block ml-1"
[enableLink]="false"
/> />
<div> @if (isGhostfolioApiKeyValid === false) {
<a <span class="badge badge-warning ml-2" i18n
class="align-items-center d-inline-flex" >Early Access</span
target="_blank"
[href]="pricingUrl"
> >
Ghostfolio Premium }
<gf-premium-indicator </a>
class="d-inline-block ml-1"
[enableLink]="false"
/>
@if (isGhostfolioApiKeyValid === false) {
<span class="badge badge-warning ml-2" i18n
>Early Access</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>
}
</div>
</div>
</div>
<div class="w-50">
@if (isGhostfolioApiKeyValid === true) { @if (isGhostfolioApiKeyValid === true) {
<div class="align-items-center d-flex flex-wrap"> <div class="line-height-1">
<div class="flex-grow-1 mr-3"> <small class="text-muted">
<ng-container i18n>Valid until</ng-container>
{{
ghostfolioApiStatus?.subscription?.expiresAt
| date: defaultDateFormat
}}
</small>
</div>
<div class="line-height-1 mt-1">
<small class="text-muted">
{{ ghostfolioApiStatus.dailyRequests }} {{ ghostfolioApiStatus.dailyRequests }}
<ng-container i18n>of</ng-container> <ng-container i18n>of</ng-container>
{{ ghostfolioApiStatus.dailyRequestsMax }} {{ ghostfolioApiStatus.dailyRequestsMax }}
<ng-container i18n>daily requests</ng-container> <ng-container i18n>daily requests</ng-container>
</div> </small>
<button
class="mx-1 no-min-width px-2"
mat-button
[matMenuTriggerFor]="ghostfolioApiMenu"
(click)="$event.stopPropagation()"
>
<ion-icon name="ellipsis-horizontal" />
</button>
<mat-menu #ghostfolioApiMenu="matMenu" 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>
</mat-menu>
</div> </div>
} @else if (isGhostfolioApiKeyValid === false) {
<button
color="accent"
mat-flat-button
(click)="onSetGhostfolioApiKey()"
>
<ion-icon class="mr-1" name="key-outline" />
<span i18n>Set API key</span>
</button>
} }
</div> } @else {
} @else { {{ element.name }}
<div class="w-50"> }
<div class="d-flex"> </div>
<gf-asset-profile-icon
class="mr-1"
[url]="dataProvider.url"
/>
{{ dataProvider.name }}
</div>
</div>
<div class="w-50"></div>
}
</div> </div>
} </td>
</mat-card-content> </ng-container>
</mat-card>
<ng-container matColumnDef="actions">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell></th>
<td *matCellDef="let element" class="px-1 py-2 text-right" mat-cell>
@if (isGhostfolioProvider(element)) {
@if (isGhostfolioApiKeyValid === true) {
<button
class="mx-1 no-min-width px-2"
mat-button
[matMenuTriggerFor]="ghostfolioApiMenu"
(click)="$event.stopPropagation()"
>
<ion-icon name="ellipsis-horizontal" />
</button>
<mat-menu #ghostfolioApiMenu="matMenu" 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>
</mat-menu>
} @else if (isGhostfolioApiKeyValid === false) {
<button
color="accent"
mat-flat-button
(click)="onSetGhostfolioApiKey()"
>
<ion-icon class="mr-1" name="key-outline" />
<span i18n>Set API key</span>
</button>
}
}
</td>
</ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
</table>
@if (isLoading) {
<ngx-skeleton-loader
animation="pulse"
class="px-4 py-3"
[theme]="{
height: '1.5rem',
width: '100%'
}"
/>
}
</div> </div>
</div> </div>
<div class="mb-5 row"> <div class="mb-5 row">

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

@ -22,6 +22,7 @@ import {
OnInit OnInit
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { catchError, filter, of, Subject, takeUntil } from 'rxjs'; import { catchError, filter, of, Subject, takeUntil } from 'rxjs';
@ -36,11 +37,13 @@ import { GhostfolioPremiumApiDialogParams } from './ghostfolio-premium-api-dialo
standalone: false standalone: false
}) })
export class AdminSettingsComponent implements OnDestroy, OnInit { export class AdminSettingsComponent implements OnDestroy, OnInit {
public dataProviders: DataProviderInfo[]; public dataSource = new MatTableDataSource<DataProviderInfo>();
public defaultDateFormat: string; public defaultDateFormat: string;
public ghostfolioApiStatus: DataProviderGhostfolioStatusResponse; public ghostfolioApiStatus: DataProviderGhostfolioStatusResponse;
public isGhostfolioApiKeyValid: boolean; public isGhostfolioApiKeyValid: boolean;
public pricingUrl: string; public pricingUrl: string;
public displayedColumns = ['name', 'actions'];
public isLoading = false;
private deviceType: string; private deviceType: string;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -119,20 +122,28 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
}); });
} }
public isGhostfolioProvider(provider: DataProviderInfo): boolean {
return provider.name === 'Ghostfolio';
}
public ngOnDestroy() { public ngOnDestroy() {
this.unsubscribeSubject.next(); this.unsubscribeSubject.next();
this.unsubscribeSubject.complete(); this.unsubscribeSubject.complete();
} }
private initialize() { private initialize() {
this.isLoading = true;
this.adminService this.adminService
.fetchAdminData() .fetchAdminData()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ dataProviders, settings }) => { .subscribe(({ dataProviders, settings }) => {
this.dataProviders = dataProviders.filter(({ dataSource }) => { const filteredProviders = dataProviders.filter(({ dataSource }) => {
return dataSource !== 'MANUAL'; return dataSource !== 'MANUAL';
}); });
this.dataSource = new MatTableDataSource(filteredProviders);
this.adminService this.adminService
.fetchGhostfolioDataProviderStatus( .fetchGhostfolioDataProviderStatus(
settings[PROPERTY_API_KEY_GHOSTFOLIO] as string settings[PROPERTY_API_KEY_GHOSTFOLIO] as string
@ -140,7 +151,7 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
.pipe( .pipe(
catchError(() => { catchError(() => {
this.isGhostfolioApiKeyValid = false; this.isGhostfolioApiKeyValid = false;
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
return of(null); return of(null);
@ -153,7 +164,7 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
.subscribe((status) => { .subscribe((status) => {
this.ghostfolioApiStatus = status; this.ghostfolioApiStatus = status;
this.isGhostfolioApiKeyValid = true; this.isGhostfolioApiKeyValid = true;
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });

4
apps/client/src/app/components/admin-settings/admin-settings.module.ts

@ -8,6 +8,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { AdminSettingsComponent } from './admin-settings.component'; import { AdminSettingsComponent } from './admin-settings.component';
@ -23,7 +24,8 @@ import { AdminSettingsComponent } from './admin-settings.component';
MatButtonModule, MatButtonModule,
MatCardModule, MatCardModule,
MatMenuModule, MatMenuModule,
RouterModule RouterModule,
MatTableModule
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
}) })

Loading…
Cancel
Save