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
|
||||||
|
|
||||||
- Added the logotype to the footer
|
- Added the logotype to the footer
|
||||||
|
- Added the data providers management to the admin control panel
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -1,4 +1,39 @@
|
|||||||
<div class="container">
|
<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="mb-5 row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h2 class="text-center" i18n>Platforms</h2>
|
<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 {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit
|
OnInit
|
||||||
} from '@angular/core';
|
} 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({
|
@Component({
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -12,12 +20,49 @@ import { Subject } from 'rxjs';
|
|||||||
styleUrls: ['./admin-settings.component.scss'],
|
styleUrls: ['./admin-settings.component.scss'],
|
||||||
templateUrl: './admin-settings.component.html'
|
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 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() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { GfAdminPlatformModule } from '@ghostfolio/client/components/admin-platform/admin-platform.module';
|
import { GfAdminPlatformModule } from '@ghostfolio/client/components/admin-platform/admin-platform.module';
|
||||||
import { GfAdminTagModule } from '@ghostfolio/client/components/admin-tag/admin-tag.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 { 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 { MatCardModule } from '@angular/material/card';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { AdminSettingsComponent } from './admin-settings.component';
|
import { AdminSettingsComponent } from './admin-settings.component';
|
||||||
@ -13,6 +16,9 @@ import { AdminSettingsComponent } from './admin-settings.component';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
GfAdminPlatformModule,
|
GfAdminPlatformModule,
|
||||||
GfAdminTagModule,
|
GfAdminTagModule,
|
||||||
|
GfPremiumIndicatorComponent,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
RouterModule
|
RouterModule
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
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 isLoggedIn: boolean;
|
||||||
public price: number;
|
public price: number;
|
||||||
public priceId: string;
|
public priceId: string;
|
||||||
|
public professionalDataProviderTooltipPremium = translate(
|
||||||
|
'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM'
|
||||||
|
);
|
||||||
public routerLinkFeatures = ['/' + $localize`:snake-case:features`];
|
public routerLinkFeatures = ['/' + $localize`:snake-case:features`];
|
||||||
public routerLinkRegister = ['/' + $localize`:snake-case:register`];
|
public routerLinkRegister = ['/' + $localize`:snake-case:register`];
|
||||||
public user: User;
|
public user: User;
|
||||||
|
@ -228,6 +228,13 @@
|
|||||||
<li class="align-items-center d-flex mb-1">
|
<li class="align-items-center d-flex mb-1">
|
||||||
<ion-icon class="mr-1" name="checkmark-circle-outline" />
|
<ion-icon class="mr-1" name="checkmark-circle-outline" />
|
||||||
<span i18n>Professional Data Provider</span>
|
<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>
|
||||||
<li class="align-items-center d-flex mb-1">
|
<li class="align-items-center d-flex mb-1">
|
||||||
<ion-icon class="mr-1" name="checkmark-circle-outline" />
|
<ion-icon class="mr-1" name="checkmark-circle-outline" />
|
||||||
|
@ -377,6 +377,10 @@ ngx-skeleton-loader {
|
|||||||
@include gf-table;
|
@include gf-table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gf-text-wrap-balance {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
.has-fab {
|
.has-fab {
|
||||||
padding-bottom: 3rem !important;
|
padding-bottom: 3rem !important;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ const locales = {
|
|||||||
MONTH: $localize`Month`,
|
MONTH: $localize`Month`,
|
||||||
MONTHS: $localize`Months`,
|
MONTHS: $localize`Months`,
|
||||||
OTHER: $localize`Other`,
|
OTHER: $localize`Other`,
|
||||||
|
PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM: $localize`Get access to 100’000+ tickers from over 50 exchanges`,
|
||||||
PRESET_ID: $localize`Preset`,
|
PRESET_ID: $localize`Preset`,
|
||||||
RETIREMENT_PROVISION: $localize`Retirement Provision`,
|
RETIREMENT_PROVISION: $localize`Retirement Provision`,
|
||||||
SATELLITE: $localize`Satellite`,
|
SATELLITE: $localize`Satellite`,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user