Browse Source

Feature/migrate data providers overview to Angular Material table (#4704)

* Migrate data providers overview to Angular Material table

* Update changelog
pull/4706/head
andiz2 2 months ago
committed by GitHub
parent
commit
0b7fc7a3b2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 88
      apps/client/src/app/components/admin-settings/admin-settings.component.html
  3. 19
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  4. 6
      apps/client/src/app/components/admin-settings/admin-settings.module.ts

1
CHANGELOG.md

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Harmonized the data providers management style of the admin control panel
- Renamed `Order` to `activities` in the `User` database schema - Renamed `Order` to `activities` in the `User` database schema
- Improved the language localization for Catalan (`ca`) - Improved the language localization for Catalan (`ca`)
- Improved the language localization for Chinese (`zh`) - Improved the language localization for Chinese (`zh`)

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

@ -1,19 +1,17 @@
<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"
[url]="dataProvider.url"
/>
<div> <div>
@if (isGhostfolioDataProvider(element)) {
<a <a
class="align-items-center d-inline-flex" class="align-items-center d-inline-flex"
target="_blank" target="_blank"
@ -37,22 +35,32 @@
{{ {{
ghostfolioApiStatus?.subscription?.expiresAt ghostfolioApiStatus?.subscription?.expiresAt
| date: defaultDateFormat | date: defaultDateFormat
}}</small }}
> </small>
</div>
}
</div>
</div>
</div> </div>
<div class="w-50"> <div class="line-height-1 mt-1">
@if (isGhostfolioApiKeyValid === true) { <small class="text-muted">
<div class="align-items-center d-flex flex-wrap">
<div class="flex-grow-1 mr-3">
{{ 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>
</small>
</div>
}
} @else {
{{ element.name }}
}
</div> </div>
</div>
</td>
</ng-container>
<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 (isGhostfolioDataProvider(element)) {
@if (isGhostfolioApiKeyValid === true) {
<button <button
class="mx-1 no-min-width px-2" class="mx-1 no-min-width px-2"
mat-button mat-button
@ -62,17 +70,13 @@
<ion-icon name="ellipsis-horizontal" /> <ion-icon name="ellipsis-horizontal" />
</button> </button>
<mat-menu #ghostfolioApiMenu="matMenu" xPosition="before"> <mat-menu #ghostfolioApiMenu="matMenu" xPosition="before">
<button <button mat-menu-item (click)="onRemoveGhostfolioApiKey()">
mat-menu-item
(click)="onRemoveGhostfolioApiKey()"
>
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
<ion-icon class="mr-2" name="trash-outline" /> <ion-icon class="mr-2" name="trash-outline" />
<span i18n>Remove API key</span> <span i18n>Remove API key</span>
</span> </span>
</button> </button>
</mat-menu> </mat-menu>
</div>
} @else if (isGhostfolioApiKeyValid === false) { } @else if (isGhostfolioApiKeyValid === false) {
<button <button
color="accent" color="accent"
@ -83,23 +87,23 @@
<span i18n>Set API key</span> <span i18n>Set API key</span>
</button> </button>
} }
</div>
} @else {
<div class="w-50">
<div class="d-flex">
<gf-asset-profile-icon
class="mr-1"
[url]="dataProvider.url"
/>
{{ dataProvider.name }}
</div>
</div>
<div class="w-50"></div>
} }
</div> </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%'
}"
/>
} }
</mat-card-content>
</mat-card>
</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,10 +37,12 @@ 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 displayedColumns = ['name', 'actions'];
public ghostfolioApiStatus: DataProviderGhostfolioStatusResponse; public ghostfolioApiStatus: DataProviderGhostfolioStatusResponse;
public isGhostfolioApiKeyValid: boolean; public isGhostfolioApiKeyValid: boolean;
public isLoading = false;
public pricingUrl: string; public pricingUrl: string;
private deviceType: string; private deviceType: string;
@ -83,6 +86,10 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
this.initialize(); this.initialize();
} }
public isGhostfolioDataProvider(provider: DataProviderInfo): boolean {
return provider.dataSource === 'GHOSTFOLIO';
}
public onRemoveGhostfolioApiKey() { public onRemoveGhostfolioApiKey() {
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
@ -125,14 +132,20 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
} }
private initialize() { private initialize() {
this.isLoading = true;
this.dataSource = new MatTableDataSource();
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
@ -157,6 +170,8 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

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

@ -6,9 +6,10 @@ import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; 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 { 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 { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { AdminSettingsComponent } from './admin-settings.component'; import { AdminSettingsComponent } from './admin-settings.component';
@ -21,8 +22,9 @@ import { AdminSettingsComponent } from './admin-settings.component';
GfAssetProfileIconComponent, GfAssetProfileIconComponent,
GfPremiumIndicatorComponent, GfPremiumIndicatorComponent,
MatButtonModule, MatButtonModule,
MatCardModule,
MatMenuModule, MatMenuModule,
MatTableModule,
NgxSkeletonLoaderModule,
RouterModule RouterModule
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]

Loading…
Cancel
Save