Feature/add data providers management to admin control panel (#3950)
* Add data providers management to admin control panel * Update changelog
This commit is contained in:
parent
68cb4b27d1
commit
a414cfab52
@ -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() {}
|
||||
public constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private deviceService: DeviceDetectorService,
|
||||
private matDialog: MatDialog,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
public ngOnInit() {}
|
||||
public ngOnInit() {
|
||||
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
|
||||
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
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>100’000+ 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 it’s 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,2 @@
|
||||
:host {
|
||||
}
|
@ -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 100’000+ tickers from over 50 exchanges`,
|
||||
PRESET_ID: $localize`Preset`,
|
||||
RETIREMENT_PROVISION: $localize`Retirement Provision`,
|
||||
SATELLITE: $localize`Satellite`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user