Browse Source

Add statistics to landing page

pull/1306/head
Thomas 3 years ago
parent
commit
207e690fb3
  1. 16
      apps/client/src/app/pages/landing/landing-page.component.ts
  2. 53
      apps/client/src/app/pages/landing/landing-page.html
  3. 2
      apps/client/src/app/pages/landing/landing-page.module.ts
  4. 5
      apps/client/src/styles.scss
  5. 128
      libs/ui/src/lib/value/value.component.html
  6. 2
      libs/ui/src/lib/value/value.component.scss
  7. 1
      libs/ui/src/lib/value/value.component.ts
  8. 4
      libs/ui/src/lib/value/value.module.ts

16
apps/client/src/app/pages/landing/landing-page.component.ts

@ -1,4 +1,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service';
import { Statistics } from '@ghostfolio/common/interfaces/statistics.interface';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { format } from 'date-fns';
import { Subject } from 'rxjs';
@ -11,6 +14,8 @@ import { Subject } from 'rxjs';
export class LandingPageComponent implements OnDestroy, OnInit {
public currentYear = format(new Date(), 'yyyy');
public demoAuthToken: string;
public hasPermissionForStatistics: boolean;
public statistics: Statistics;
public testimonials = [
{
author: 'Philipp',
@ -36,7 +41,16 @@ export class LandingPageComponent implements OnDestroy, OnInit {
private unsubscribeSubject = new Subject<void>();
public constructor() {}
public constructor(private dataService: DataService) {
const { globalPermissions, statistics } = this.dataService.fetchInfo();
this.hasPermissionForStatistics = hasPermission(
globalPermissions,
permissions.enableStatistics
);
this.statistics = statistics;
}
public ngOnInit() {}

53
apps/client/src/app/pages/landing/landing-page.html

@ -42,7 +42,52 @@
</div>
</div>
<div class="row my-3">
<div *ngIf="hasPermissionForStatistics" class="row mb-5">
<div class="col-md-4 d-flex my-1">
<a
class="d-block"
title="Ghostfolio in Numbers: Monthly Active Users (MAU)"
[routerLink]="['/about']"
>
<gf-value
icon="people-outline"
size="large"
[value]="statistics?.activeUsers30d ?? '-'"
>Monthly Active Users</gf-value
>
</a>
</div>
<div class="col-md-4 d-flex my-1">
<a
class="d-block"
title="Ghostfolio in Numbers: Stars on GitHub"
[routerLink]="['/about']"
>
<gf-value
icon="star-outline"
size="large"
[value]="statistics?.gitHubStargazers ?? '-'"
>Stars on GitHub</gf-value
>
</a>
</div>
<div class="col-md-4 d-flex my-1">
<a
class="d-block"
title="Ghostfolio in Numbers: Pulls on Docker Hub"
[routerLink]="['/about']"
>
<gf-value
icon="cloud-download-outline"
size="large"
[value]="statistics?.dockerHubPulls ?? '-'"
>Pulls on Docker Hub</gf-value
>
</a>
</div>
</div>
<div class="row mb-5">
<div class="col-12 text-center text-muted"><small>As seen in</small></div>
<div class="col-md-2 d-flex justify-content-center my-1">
<a
@ -110,20 +155,20 @@
<div class="row my-3">
<div class="col-md-4 my-2">
<mat-card>
<mat-card-title class="text-center">360° View</mat-card-title>
<mat-card-title>360° View</mat-card-title>
Get the full picture of your personal finances across multiple
platforms.
</mat-card>
</div>
<div class="col-md-4 my-2">
<mat-card>
<mat-card-title class="text-center">Web3 Ready</mat-card-title>
<mat-card-title>Web3 Ready</mat-card-title>
Use Ghostfolio anonymously and own your financial data.
</mat-card>
</div>
<div class="col-md-4 my-2">
<mat-card>
<mat-card-title class="text-center">Open Source</mat-card-title>
<mat-card-title>Open Source</mat-card-title>
Benefit from continuous improvements through a strong community.
</mat-card>
</div>

2
apps/client/src/app/pages/landing/landing-page.module.ts

@ -4,6 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router';
import { GfLogoModule } from '@ghostfolio/ui/logo';
import { GfValueModule } from '@ghostfolio/ui/value';
import { LandingPageRoutingModule } from './landing-page-routing.module';
import { LandingPageComponent } from './landing-page.component';
@ -13,6 +14,7 @@ import { LandingPageComponent } from './landing-page.component';
imports: [
CommonModule,
GfLogoModule,
GfValueModule,
LandingPageRoutingModule,
MatButtonModule,
MatCardModule,

5
apps/client/src/styles.scss

@ -18,6 +18,7 @@ $mat-css-light-theme-selector: '.is-light-theme';
:root {
--dark-background: rgb(39, 39, 39);
--font-family-sans-serif: Roboto, 'Helvetica Neue', sans-serif;
--light-background: rgb(255, 255, 255);
}
@ -146,6 +147,10 @@ ngx-skeleton-loader {
@include gf-table;
}
.lead {
font-weight: unset;
}
.mat-card {
&:not([class*='mat-elevation-z']) {
border: 1px solid rgba(var(--dark-dividers));

128
libs/ui/src/lib/value/value.component.html

@ -1,67 +1,73 @@
<ng-template #label><ng-content></ng-content></ng-template>
<ng-container *ngIf="value || value === 0 || value === null">
<div
class="d-flex"
[ngClass]="position === 'end' ? 'justify-content-end' : ''"
>
<ng-container *ngIf="isNumber || value === null">
<ng-container *ngIf="colorizeSign && !useAbsoluteValue">
<div *ngIf="value > 0" class="mr-1 text-success">+</div>
<div *ngIf="value < 0" class="mr-1 text-danger">-</div>
</ng-container>
<div
*ngIf="isPercent"
class="mb-0 value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
{{ formattedValue }}%
</div>
<div
*ngIf="!isPercent"
class="mb-0 value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
<ng-container *ngIf="value === null">
<span class="text-monospace text-muted">***</span>
</ng-container>
<ng-container *ngIf="value !== null">
{{ formattedValue }}
<div *ngIf="icon" class="align-self-center mr-3">
<ion-icon class="h3 m-0" [name]="icon"></ion-icon>
</div>
<div>
<ng-template #label><ng-content></ng-content></ng-template>
<ng-container *ngIf="value || value === 0 || value === null">
<div
class="d-flex"
[ngClass]="position === 'end' ? 'justify-content-end' : ''"
>
<ng-container *ngIf="isNumber || value === null">
<ng-container *ngIf="colorizeSign && !useAbsoluteValue">
<div *ngIf="value > 0" class="mr-1 text-success">+</div>
<div *ngIf="value < 0" class="mr-1 text-danger">-</div>
</ng-container>
<div
*ngIf="isPercent"
class="mb-0 value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
{{ formattedValue }}%
</div>
<div
*ngIf="!isPercent"
class="mb-0 value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
<ng-container *ngIf="value === null">
<span class="text-monospace text-muted">***</span>
</ng-container>
<ng-container *ngIf="value !== null">
{{ formattedValue }}
</ng-container>
</div>
<small *ngIf="currency && size === 'medium'" class="ml-1">
{{ currency }}
</small>
<div *ngIf="currency && size !== 'medium'" class="ml-1">
{{ currency }}
</div>
</ng-container>
<ng-container *ngIf="isString">
<div
class="mb-0 text-truncate value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
{{ formattedValue | titlecase }}
</div>
</ng-container>
</div>
<ng-container>
<div *ngIf="size === 'large'">
<span class="h6"
><ng-container *ngTemplateOutlet="label"></ng-container
></span>
<span *ngIf="subLabel" class="text-muted"> {{ subLabel }}</span>
</div>
<small *ngIf="currency && size === 'medium'" class="ml-1">
{{ currency }}
<small *ngIf="size !== 'large'">
<ng-container *ngTemplateOutlet="label"></ng-container>
</small>
<div *ngIf="currency && size !== 'medium'" class="ml-1">
{{ currency }}
</div>
</ng-container>
<ng-container *ngIf="isString">
<div
class="mb-0 text-truncate value"
[ngClass]="{ h2: size === 'large', h4: size === 'medium' }"
>
{{ formattedValue | titlecase }}
</div>
</ng-container>
</div>
<ng-container>
<div *ngIf="size === 'large'">
<span class="h6"
><ng-container *ngTemplateOutlet="label"></ng-container
></span>
<span *ngIf="subLabel" class="text-muted"> {{ subLabel }}</span>
</div>
<small *ngIf="size !== 'large'">
<ng-container *ngTemplateOutlet="label"></ng-container>
</small>
</ng-container>
</ng-container>
<ngx-skeleton-loader
*ngIf="value === undefined"
animation="pulse"
[theme]="{
height: size === 'large' ? '2.5rem' : size === 'medium' ? '2rem' : '1.5rem',
width: '5rem'
}"
></ngx-skeleton-loader>
<ngx-skeleton-loader
*ngIf="value === undefined"
animation="pulse"
[theme]="{
height:
size === 'large' ? '2.5rem' : size === 'medium' ? '2rem' : '1.5rem',
width: '5rem'
}"
></ngx-skeleton-loader>
</div>

2
libs/ui/src/lib/value/value.component.scss

@ -1,6 +1,6 @@
:host {
display: flex;
flex-direction: column;
flex-direction: row;
font-variant-numeric: tabular-nums;
.h2 {

1
libs/ui/src/lib/value/value.component.ts

@ -16,6 +16,7 @@ import { isNumber } from 'lodash';
export class ValueComponent implements OnChanges {
@Input() colorizeSign = false;
@Input() currency = '';
@Input() icon = '';
@Input() isAbsolute = false;
@Input() isCurrency = false;
@Input() isDate = false;

4
libs/ui/src/lib/value/value.module.ts

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { ValueComponent } from './value.component';
@ -8,6 +8,6 @@ import { ValueComponent } from './value.component';
declarations: [ValueComponent],
exports: [ValueComponent],
imports: [CommonModule, NgxSkeletonLoaderModule],
providers: []
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GfValueModule {}

Loading…
Cancel
Save