Browse Source

Simplify data providers management

pull/4991/head
Thomas Kaul 1 week ago
parent
commit
f240264e18
  1. 42
      apps/client/src/app/components/admin-settings/admin-settings.component.html
  2. 10
      apps/client/src/app/components/admin-settings/admin-settings.component.scss
  3. 45
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  4. 2
      apps/client/src/app/components/admin-settings/admin-settings.module.ts
  5. 60
      apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts
  6. 49
      apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.html
  7. 2
      apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.scss
  8. 4
      apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/interfaces/interfaces.ts

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

@ -2,6 +2,42 @@
<div class="mb-5 row">
<div class="col">
<h2 class="text-center" i18n>Data Providers</h2>
@if (isGhostfolioApiKeyValid === false) {
<mat-card appearance="outlined" class="my-3 special">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n
>Ghostfolio Premium
<gf-premium-indicator
class="d-inline-block mx-1"
[enableLink]="false"
/></mat-card-title>
</mat-card-header>
<mat-card-content class="gf-text-wrap-balance" i18n>
The <strong>professional data provider</strong> for
<strong>self-hosters</strong>, offering
<strong>80’000+ tickers</strong> from over
<strong>50 exchanges</strong>.
</mat-card-content>
<mat-card-actions class="px-3">
<a
class="special"
href="mailto:hi@ghostfol.io?Subject=Ghostfolio Premium Data Provider&body=Hello,%0D%0DI am interested in the Ghostfolio Premium data provider. Could you please give me access so I can try it for some time?%0D%0DKind regards"
mat-flat-button
>
<ng-container i18n>Get Access</ng-container>
</a>
<div class="mx-3 text-muted" i18n>or</div>
<a
color="accent"
mat-stroked-button
target="_blank"
[href]="pricingUrl"
>
<ng-container i18n>Learn more</ng-container>
</a>
</mat-card-actions>
</mat-card>
}
<table class="gf-table w-100" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<th *matHeaderCellDef class="px-1 py-2" mat-header-cell>
@ -23,8 +59,10 @@
[enableLink]="false"
/>
@if (isGhostfolioApiKeyValid === false) {
<span class="badge badge-light early-access ml-2" i18n
>Early Access</span
<span
class="badge badge-light ml-2 new text-uppercase"
i18n
>new</span
>
}
</a>

10
apps/client/src/app/components/admin-settings/admin-settings.component.scss

@ -1,19 +1,25 @@
:host {
display: block;
a,
button {
&.special {
background: linear-gradient(45deg, rgb(228, 94, 237), rgb(104, 94, 237));
background: linear-gradient(45deg, var(--oc-pink-5), var(--oc-violet-5));
color: #fff;
}
}
.badge {
&.early-access {
&.new {
border: 1px solid var(--mat-table-row-item-outline-color);
padding-bottom: 0.05rem;
}
}
.mat-mdc-card {
--mdc-outlined-card-outline-color: var(--oc-violet-2);
}
.mat-mdc-progress-bar {
--mdc-linear-progress-active-indicator-height: 0.5rem;
--mdc-linear-progress-track-height: 0.5rem;

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

@ -19,14 +19,9 @@ import {
OnDestroy,
OnInit
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { DeviceDetectorService } from 'ngx-device-detector';
import { catchError, filter, of, Subject, takeUntil } from 'rxjs';
import { GfGhostfolioPremiumApiDialogComponent } from './ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component';
import { GhostfolioPremiumApiDialogParams } from './ghostfolio-premium-api-dialog/interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'gf-admin-settings',
@ -43,7 +38,6 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
public isLoading = false;
public pricingUrl: string;
private deviceType: string;
private unsubscribeSubject = new Subject<void>();
private user: User;
@ -51,15 +45,11 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
private adminService: AdminService,
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private deviceService: DeviceDetectorService,
private matDialog: MatDialog,
private notificationService: NotificationService,
private userService: UserService
) {}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
@ -100,25 +90,22 @@ export class AdminSettingsComponent implements OnDestroy, OnInit {
}
public onSetGhostfolioApiKey() {
const dialogRef = this.matDialog.open(
GfGhostfolioPremiumApiDialogComponent,
{
autoFocus: false,
data: {
deviceType: this.deviceType,
pricingUrl: this.pricingUrl
} as GhostfolioPremiumApiDialogParams,
height: this.deviceType === 'mobile' ? '98vh' : undefined,
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}
);
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.initialize();
});
this.notificationService.prompt({
confirmFn: (value) => {
const ghostfolioApiKey = value?.trim();
if (ghostfolioApiKey) {
this.dataService
.putAdminSetting(PROPERTY_API_KEY_GHOSTFOLIO, {
value: ghostfolioApiKey
})
.subscribe(() => {
this.initialize();
});
}
},
title: $localize`Please enter your Ghostfolio API key.`
});
}
public ngOnDestroy() {

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

@ -7,6 +7,7 @@ import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatMenuModule } from '@angular/material/menu';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatTableModule } from '@angular/material/table';
@ -25,6 +26,7 @@ import { AdminSettingsComponent } from './admin-settings.component';
GfPremiumIndicatorComponent,
GfValueComponent,
MatButtonModule,
MatCardModule,
MatMenuModule,
MatProgressBarModule,
MatTableModule,

60
apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts

@ -1,60 +0,0 @@
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { Component, Inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import {
MAT_DIALOG_DATA,
MatDialogModule,
MatDialogRef
} from '@angular/material/dialog';
import { GfDialogFooterModule } from '../../dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '../../dialog-header/dialog-header.module';
import { GhostfolioPremiumApiDialogParams } from './interfaces/interfaces';
@Component({
imports: [
GfDialogFooterModule,
GfDialogHeaderModule,
GfPremiumIndicatorComponent,
MatButtonModule,
MatDialogModule
],
selector: 'gf-ghostfolio-premium-api-dialog',
styleUrls: ['./ghostfolio-premium-api-dialog.scss'],
templateUrl: './ghostfolio-premium-api-dialog.html'
})
export class GfGhostfolioPremiumApiDialogComponent {
public constructor(
@Inject(MAT_DIALOG_DATA) public data: GhostfolioPremiumApiDialogParams,
private dataService: DataService,
public dialogRef: MatDialogRef<GfGhostfolioPremiumApiDialogComponent>,
private notificationService: NotificationService
) {}
public onCancel() {
this.dialogRef.close();
}
public onSetGhostfolioApiKey() {
this.notificationService.prompt({
confirmFn: (value) => {
const ghostfolioApiKey = value?.trim();
if (ghostfolioApiKey) {
this.dataService
.putAdminSetting(PROPERTY_API_KEY_GHOSTFOLIO, {
value: ghostfolioApiKey
})
.subscribe(() => {
this.dialogRef.close();
});
}
},
title: $localize`Please enter your Ghostfolio API key.`
});
}
}

49
apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.html

@ -1,49 +0,0 @@
<gf-dialog-header
mat-dialog-title
position="center"
title="Ghostfolio Premium Data Provider"
[deviceType]="data.deviceType"
(closeButtonClicked)="onCancel()"
/>
<div class="text-center" mat-dialog-content>
<p class="gf-text-wrap-balance">
Early access to the official
<a
class="align-items-center d-inline-flex"
target="_blank"
[href]="data.pricingUrl"
>Ghostfolio Premium
<gf-premium-indicator class="d-inline-block ml-1" [enableLink]="false" />
</a>
data provider <strong>for self-hosters</strong>, offering
<strong>80’000+ tickers</strong> from over <strong>50 exchanges</strong>, is
ready now!
</p>
<div>
<a
color="primary"
href="mailto:hi@ghostfol.io?Subject=Ghostfolio Premium Data Provider&body=Hello%0D%0DI am interested in the Ghostfolio Premium data provider. Could you please give me early access so I can try it for some time?%0D%0DKind regards"
i18n
mat-flat-button
>Get Early Access</a
>
<div>
<small class="text-muted" i18n>or</small>
</div>
<button
color="accent"
i18n
mat-stroked-button
(click)="onSetGhostfolioApiKey()"
>
I have an API key
</button>
</div>
</div>
<gf-dialog-footer
mat-dialog-actions
[deviceType]="data.deviceType"
(closeButtonClicked)="onCancel()"
/>

2
apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.scss

@ -1,2 +0,0 @@
:host {
}

4
apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/interfaces/interfaces.ts

@ -1,4 +0,0 @@
export interface GhostfolioPremiumApiDialogParams {
deviceType: string;
pricingUrl: string;
}
Loading…
Cancel
Save