Browse Source

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 months ago
committed by GitHub
parent
commit
a414cfab52
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 35
      apps/client/src/app/components/admin-settings/admin-settings.component.html
  3. 53
      apps/client/src/app/components/admin-settings/admin-settings.component.ts
  4. 6
      apps/client/src/app/components/admin-settings/admin-settings.module.ts
  5. 39
      apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts
  6. 42
      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
  9. 3
      apps/client/src/app/pages/pricing/pricing-page.component.ts
  10. 7
      apps/client/src/app/pages/pricing/pricing-page.html
  11. 4
      apps/client/src/styles.scss
  12. 1
      libs/ui/src/lib/i18n.ts

1
CHANGELOG.md

@ -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

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

@ -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>

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

@ -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();

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

@ -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]

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

@ -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();
}
}

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

@ -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()"
/>

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

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

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

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

3
apps/client/src/app/pages/pricing/pricing-page.component.ts

@ -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;

7
apps/client/src/app/pages/pricing/pricing-page.html

@ -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" />

4
apps/client/src/styles.scss

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

1
libs/ui/src/lib/i18n.ts

@ -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…
Cancel
Save