Feature/add data providers management to admin control panel (#3950)

* Add data providers management to admin control panel

* Update changelog
pull/3958/head
Thomas Kaul 3 days ago committed by GitHub
parent 68cb4b27d1
commit a414cfab52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added the logotype to the footer
- Added the data providers management to the admin control panel
### Changed

@ -1,4 +1,39 @@
<div class="container">
<div class="d-md-block d-none mb-5 row">
<div class="col">
<h2 class="text-center" i18n>Data Providers</h2>
<mat-card appearance="outlined">
<mat-card-content>
<div class="align-items-center d-flex my-3">
<div class="w-50">
<a
class="align-items-center d-inline-flex"
target="_blank"
[href]="pricingUrl"
>
<span class="badge badge-warning mr-1" i18n>NEW</span>
Ghostfolio Premium
<gf-premium-indicator
class="d-inline-block ml-1"
[enableLink]="false"
/>
</a>
</div>
<div class="w-50">
<button
color="accent"
mat-flat-button
(click)="onSetGhostfolioApiKey()"
>
<ion-icon class="mr-1" name="key-outline" />
<span i18n>Set API Key</span>
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="mb-5 row">
<div class="col">
<h2 class="text-center" i18n>Platforms</h2>

@ -1,10 +1,18 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit
} from '@angular/core';
import { Subject } from 'rxjs';
import { MatDialog } from '@angular/material/dialog';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject, takeUntil } from 'rxjs';
import { GfGhostfolioPremiumApiDialogComponent } from './ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@ -12,12 +20,49 @@ import { Subject } from 'rxjs';
styleUrls: ['./admin-settings.component.scss'],
templateUrl: './admin-settings.component.html'
})
export class AdminSettingsComponent implements OnInit, OnDestroy {
export class AdminSettingsComponent implements OnDestroy, OnInit {
public pricingUrl: string;
private deviceType: string;
private unsubscribeSubject = new Subject<void>();
private user: User;
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private deviceService: DeviceDetectorService,
private matDialog: MatDialog,
private userService: UserService
) {}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
public constructor() {}
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
public ngOnInit() {}
this.pricingUrl =
`https://ghostfol.io/${this.user.settings.language}/` +
$localize`:snake-case:pricing`;
this.changeDetectorRef.markForCheck();
}
});
}
public onSetGhostfolioApiKey() {
this.matDialog.open(GfGhostfolioPremiumApiDialogComponent, {
autoFocus: false,
data: {
deviceType: this.deviceType,
pricingUrl: this.pricingUrl
},
height: this.deviceType === 'mobile' ? '97.5vh' : undefined,
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
}
public ngOnDestroy() {
this.unsubscribeSubject.next();

@ -1,8 +1,11 @@
import { GfAdminPlatformModule } from '@ghostfolio/client/components/admin-platform/admin-platform.module';
import { GfAdminTagModule } from '@ghostfolio/client/components/admin-tag/admin-tag.module';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
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 { RouterModule } from '@angular/router';
import { AdminSettingsComponent } from './admin-settings.component';
@ -13,6 +16,9 @@ import { AdminSettingsComponent } from './admin-settings.component';
CommonModule,
GfAdminPlatformModule,
GfAdminTagModule,
GfPremiumIndicatorComponent,
MatButtonModule,
MatCardModule,
RouterModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]

@ -0,0 +1,39 @@
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { CommonModule } from '@angular/common';
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: [
CommonModule,
GfDialogFooterModule,
GfDialogHeaderModule,
GfPremiumIndicatorComponent,
MatButtonModule,
MatDialogModule
],
selector: 'gf-ghostfolio-premium-api-dialog',
standalone: true,
styleUrls: ['./ghostfolio-premium-api-dialog.scss'],
templateUrl: './ghostfolio-premium-api-dialog.html'
})
export class GfGhostfolioPremiumApiDialogComponent {
public constructor(
@Inject(MAT_DIALOG_DATA) public data: GhostfolioPremiumApiDialogParams,
public dialogRef: MatDialogRef<GfGhostfolioPremiumApiDialogComponent>
) {}
public onCancel() {
this.dialogRef.close();
}
}

@ -0,0 +1,42 @@
<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 mb-1">
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>100000+ tickers</strong> from over <strong>50 exchanges</strong>,
is coming soon!
</p>
<p i18n>
Want to stay updated? Click below to get notified as soon as its available.
</p>
<div>
<a
color="primary"
href="mailto:hi@ghostfol.io?Subject=Ghostfolio Premium Data Provider&body=Hello%0D%0DPlease notify me as soon as the Ghostfolio Premium Data Provider is available.%0D%0DKind regards"
i18n
mat-flat-button
>
Notify me
</a>
</div>
</div>
<gf-dialog-footer
mat-dialog-actions
[deviceType]="data.deviceType"
(closeButtonClicked)="onCancel()"
/>

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

@ -33,6 +33,9 @@ export class PricingPageComponent implements OnDestroy, OnInit {
public isLoggedIn: boolean;
public price: number;
public priceId: string;
public professionalDataProviderTooltipPremium = translate(
'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM'
);
public routerLinkFeatures = ['/' + $localize`:snake-case:features`];
public routerLinkRegister = ['/' + $localize`:snake-case:register`];
public user: User;

@ -228,6 +228,13 @@
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline" />
<span i18n>Professional Data Provider</span>
<span
class="align-items-center d-flex ml-1"
matTooltipPosition="above"
[matTooltip]="professionalDataProviderTooltipPremium"
>
<ion-icon name="information-circle-outline" />
</span>
</li>
<li class="align-items-center d-flex mb-1">
<ion-icon class="mr-1" name="checkmark-circle-outline" />

@ -377,6 +377,10 @@ ngx-skeleton-loader {
@include gf-table;
}
.gf-text-wrap-balance {
text-wrap: balance;
}
.has-fab {
padding-bottom: 3rem !important;
}

@ -21,6 +21,7 @@ const locales = {
MONTH: $localize`Month`,
MONTHS: $localize`Months`,
OTHER: $localize`Other`,
PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM: $localize`Get access to 100000+ tickers from over 50 exchanges`,
PRESET_ID: $localize`Preset`,
RETIREMENT_PROVISION: $localize`Retirement Provision`,
SATELLITE: $localize`Satellite`,

Loading…
Cancel
Save