mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
26 changed files with 679 additions and 351 deletions
@ -1,178 +1,20 @@ |
|||
<div class="container"> |
|||
<div class="mb-5 row"> |
|||
<div class="col"> |
|||
<h3 class="d-none d-sm-block mb-3 text-center">About Ghostfolio</h3> |
|||
<div class="about-container"> |
|||
<p> |
|||
Ghostfolio is a lightweight wealth management application for |
|||
individuals to keep track of stocks, ETFs or cryptocurrencies and make |
|||
solid, data-driven investment decisions. The source code is fully |
|||
available as |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
title="Find Ghostfolio on GitHub" |
|||
>open source software</a |
|||
> |
|||
(OSS) under the |
|||
<a |
|||
href="https://www.gnu.org/licenses/agpl-3.0.html" |
|||
title="GNU Affero General Public License" |
|||
>AGPL-3.0 license</a |
|||
> |
|||
and we share aggregated |
|||
<a |
|||
href="https://ghostfol.io/{{ defaultLanguageCode }}/open" |
|||
title="Open Startup" |
|||
>key metrics</a |
|||
> |
|||
of the platform’s performance. The project has been initiated by |
|||
<a href="https://dotsilver.ch" title="Website of Thomas Kaul" |
|||
>Thomas Kaul</a |
|||
> |
|||
and is driven by the efforts of its |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio/graphs/contributors" |
|||
title="Contributors to Ghostfolio" |
|||
>contributors</a |
|||
>. |
|||
<ng-container *ngIf="version"> |
|||
This instance is running Ghostfolio {{ version }}. |
|||
</ng-container> |
|||
<ng-container *ngIf="hasPermissionForSubscription" |
|||
>Check the system status at |
|||
<a href="https://status.ghostfol.io" title="Ghostfolio Status" |
|||
>status.ghostfol.io</a |
|||
>.</ng-container |
|||
> |
|||
</p> |
|||
<p> |
|||
If you encounter a bug or would like to suggest an improvement or a |
|||
new |
|||
<a [routerLink]="['/features']">feature</a>, please join the |
|||
Ghostfolio |
|||
<a |
|||
href="https://ghostfolio.slack.com" |
|||
title="Join the Ghostfolio Slack community" |
|||
>Slack community</a |
|||
>, tweet to |
|||
<a |
|||
href="https://twitter.com/ghostfolio_" |
|||
title="Tweet to Ghostfolio on Twitter" |
|||
>@ghostfolio_</a |
|||
><ng-container *ngIf="user?.subscription?.type === 'Premium'" |
|||
>, send an e-mail to |
|||
<a href="mailto:hi@ghostfol.io" title="Send an e-mail" |
|||
>hi@ghostfol.io</a |
|||
></ng-container |
|||
> |
|||
or start a discussion at |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
title="Find Ghostfolio on GitHub" |
|||
>GitHub</a |
|||
>. |
|||
</p> |
|||
<p class="text-center"> |
|||
<a |
|||
class="mx-2" |
|||
href="https://twitter.com/ghostfolio_" |
|||
mat-icon-button |
|||
title="Follow Ghostfolio on Twitter" |
|||
> |
|||
<ion-icon name="logo-twitter"></ion-icon> |
|||
</a> |
|||
<a |
|||
*ngIf="user?.subscription?.type === 'Premium'" |
|||
class="mx-2" |
|||
href="mailto:hi@ghostfol.io" |
|||
mat-icon-button |
|||
title="Send an e-mail" |
|||
> |
|||
<ion-icon name="mail"></ion-icon> |
|||
</a> |
|||
<a |
|||
class="mx-2" |
|||
href="https://ghostfolio.slack.com" |
|||
mat-icon-button |
|||
title="Join the Ghostfolio Slack channel" |
|||
> |
|||
<ion-icon name="logo-slack"></ion-icon> |
|||
</a> |
|||
<a |
|||
class="mx-2" |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
mat-icon-button |
|||
title="Find Ghostfolio on GitHub" |
|||
> |
|||
<ion-icon name="logo-github"></ion-icon> |
|||
</a> |
|||
</p> |
|||
<div |
|||
*ngIf="hasPermissionForSubscription" |
|||
class="d-flex justify-content-center" |
|||
> |
|||
<div |
|||
class="independent-and-bootstrapped-logo mb-2" |
|||
title="Ghostfolio is an independent & bootstrapped business" |
|||
></div> |
|||
</div> |
|||
<div |
|||
*ngIf="!hasPermissionForSubscription" |
|||
class="d-flex justify-content-center" |
|||
> |
|||
<a |
|||
href="https://www.buymeacoffee.com/ghostfolio" |
|||
target="_blank" |
|||
title="Support Ghostfolio" |
|||
><img |
|||
class="mb-2" |
|||
src="../assets/images/button-buy-me-a-coffee.png" |
|||
width="180" |
|||
/></a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto"> |
|||
<router-outlet></router-outlet> |
|||
</mat-tab-nav-panel> |
|||
|
|||
<div class="row"> |
|||
<div *ngIf="hasPermissionForSubscription" class="col-md-3 col-xs-12 my-2"> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/faq']" |
|||
>FAQ</a |
|||
> |
|||
</div> |
|||
<div |
|||
class="col-md-3 col-xs-12 my-2" |
|||
[ngClass]="{ 'mx-auto': !hasPermissionForBlog }" |
|||
<nav mat-align-tabs="center" mat-tab-nav-bar [tabPanel]="tabPanel"> |
|||
<ng-container *ngFor="let tab of tabs"> |
|||
<a |
|||
#rla="routerLinkActive" |
|||
*ngIf="tab.showCondition !== false" |
|||
class="px-3" |
|||
mat-tab-link |
|||
routerLinkActive |
|||
[active]="rla.isActive" |
|||
[routerLink]="tab.path" |
|||
> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/about', 'changelog']" |
|||
>Changelog & License</a |
|||
> |
|||
</div> |
|||
<div *ngIf="hasPermissionForSubscription" class="col-md-3 col-xs-12 my-2"> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/about', 'privacy-policy']" |
|||
>Privacy Policy</a |
|||
> |
|||
</div> |
|||
<div *ngIf="hasPermissionForBlog" class="col-md-3 col-xs-12 my-2"> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/blog']" |
|||
>Blog</a |
|||
> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<ion-icon size="large" [name]="tab.iconName"></ion-icon> |
|||
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div> |
|||
</a> |
|||
</ng-container> |
|||
</nav> |
|||
|
@ -1,13 +1,14 @@ |
|||
import { CommonModule } from '@angular/common'; |
|||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
|||
import { MatButtonModule } from '@angular/material/button'; |
|||
import { MatTabsModule } from '@angular/material/tabs'; |
|||
import { RouterModule } from '@angular/router'; |
|||
|
|||
import { AboutPageRoutingModule } from './about-page-routing.module'; |
|||
import { AboutPageComponent } from './about-page.component'; |
|||
|
|||
@NgModule({ |
|||
declarations: [AboutPageComponent], |
|||
imports: [AboutPageRoutingModule, CommonModule, MatButtonModule], |
|||
imports: [CommonModule, MatTabsModule, AboutPageRoutingModule, RouterModule], |
|||
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
|||
}) |
|||
export class AboutPageModule {} |
|||
|
@ -1,36 +1,35 @@ |
|||
@import 'apps/client/src/styles/ghostfolio-style'; |
|||
|
|||
:host { |
|||
color: rgb(var(--dark-primary-text)); |
|||
display: block; |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: calc(100vh - 5rem); |
|||
overflow-y: auto; |
|||
|
|||
.about-container { |
|||
a { |
|||
color: rgba(var(--palette-primary-500), 1); |
|||
font-weight: 500; |
|||
padding-bottom: env(safe-area-inset-bottom); |
|||
padding-bottom: constant(safe-area-inset-bottom); |
|||
|
|||
&:hover { |
|||
color: rgba(var(--palette-primary-300), 1); |
|||
} |
|||
::ng-deep { |
|||
gf-about-page, |
|||
gf-changelog-page, |
|||
gf-privacy-policy-page { |
|||
flex: 1 1 auto; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.independent-and-bootstrapped-logo { |
|||
background-image: url('/assets/bootstrapped-dark.svg'); |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
background-size: contain; |
|||
height: 2rem; |
|||
opacity: 0.87; |
|||
width: 10rem; |
|||
.mat-mdc-tab-link-container { |
|||
--mdc-tab-indicator-active-indicator-color: transparent; |
|||
|
|||
.mat-mdc-tab-link { |
|||
&:hover { |
|||
opacity: 0.75; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host-context(.is-dark-theme) { |
|||
color: rgb(var(--light-primary-text)); |
|||
|
|||
.about-container { |
|||
.independent-and-bootstrapped-logo { |
|||
background-image: url('/assets/bootstrapped-light.svg'); |
|||
opacity: 1; |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,20 @@ |
|||
import { NgModule } from '@angular/core'; |
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; |
|||
|
|||
import { AboutOverviewPageComponent } from './about-overview-page.component'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
canActivate: [AuthGuard], |
|||
component: AboutOverviewPageComponent, |
|||
path: '', |
|||
title: $localize`About` |
|||
} |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule] |
|||
}) |
|||
export class AboutOverviewPageRoutingModule {} |
@ -0,0 +1,61 @@ |
|||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
|||
import { environment } from '@ghostfolio/client/../environments/environment'; |
|||
import { DataService } from '@ghostfolio/client/services/data.service'; |
|||
import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|||
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; |
|||
import { User } from '@ghostfolio/common/interfaces'; |
|||
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
|||
import { Subject } from 'rxjs'; |
|||
import { takeUntil } from 'rxjs/operators'; |
|||
|
|||
@Component({ |
|||
host: { class: 'page' }, |
|||
selector: 'gf-about-overview-page', |
|||
styleUrls: ['./about-overview-page.scss'], |
|||
templateUrl: './about-overview-page.html' |
|||
}) |
|||
export class AboutOverviewPageComponent implements OnDestroy, OnInit { |
|||
public defaultLanguageCode = DEFAULT_LANGUAGE_CODE; |
|||
public hasPermissionForBlog: boolean; |
|||
public hasPermissionForSubscription: boolean; |
|||
public isLoggedIn: boolean; |
|||
public user: User; |
|||
public version = environment.version; |
|||
|
|||
private unsubscribeSubject = new Subject<void>(); |
|||
|
|||
public constructor( |
|||
private changeDetectorRef: ChangeDetectorRef, |
|||
private dataService: DataService, |
|||
private userService: UserService |
|||
) { |
|||
const { globalPermissions } = this.dataService.fetchInfo(); |
|||
|
|||
this.hasPermissionForBlog = hasPermission( |
|||
globalPermissions, |
|||
permissions.enableBlog |
|||
); |
|||
|
|||
this.hasPermissionForSubscription = hasPermission( |
|||
globalPermissions, |
|||
permissions.enableSubscription |
|||
); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.userService.stateChanged |
|||
.pipe(takeUntil(this.unsubscribeSubject)) |
|||
.subscribe((state) => { |
|||
if (state?.user) { |
|||
this.user = state.user; |
|||
|
|||
this.changeDetectorRef.markForCheck(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.unsubscribeSubject.next(); |
|||
this.unsubscribeSubject.complete(); |
|||
} |
|||
} |
@ -0,0 +1,157 @@ |
|||
<div class="container"> |
|||
<div class="mb-5 row"> |
|||
<div class="col"> |
|||
<h3 class="d-none d-sm-block mb-3 text-center">About Ghostfolio</h3> |
|||
<div class="about-container"> |
|||
<p> |
|||
Ghostfolio is a lightweight wealth management application for |
|||
individuals to keep track of stocks, ETFs or cryptocurrencies and make |
|||
solid, data-driven investment decisions. The source code is fully |
|||
available as |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
title="Find Ghostfolio on GitHub" |
|||
>open source software</a |
|||
> |
|||
(OSS) under the |
|||
<a |
|||
href="https://www.gnu.org/licenses/agpl-3.0.html" |
|||
title="GNU Affero General Public License" |
|||
>AGPL-3.0 license</a |
|||
> |
|||
and we share aggregated |
|||
<a |
|||
href="https://ghostfol.io/{{ defaultLanguageCode }}/open" |
|||
title="Open Startup" |
|||
>key metrics</a |
|||
> |
|||
of the platform’s performance. The project has been initiated by |
|||
<a href="https://dotsilver.ch" title="Website of Thomas Kaul" |
|||
>Thomas Kaul</a |
|||
> |
|||
and is driven by the efforts of its |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio/graphs/contributors" |
|||
title="Contributors to Ghostfolio" |
|||
>contributors</a |
|||
>. |
|||
<ng-container *ngIf="version"> |
|||
This instance is running Ghostfolio {{ version }}. |
|||
</ng-container> |
|||
<ng-container *ngIf="hasPermissionForSubscription" |
|||
>Check the system status at |
|||
<a href="https://status.ghostfol.io" title="Ghostfolio Status" |
|||
>status.ghostfol.io</a |
|||
>.</ng-container |
|||
> |
|||
</p> |
|||
<p> |
|||
If you encounter a bug or would like to suggest an improvement or a |
|||
new |
|||
<a [routerLink]="['/features']">feature</a>, please join the |
|||
Ghostfolio |
|||
<a |
|||
href="https://ghostfolio.slack.com" |
|||
title="Join the Ghostfolio Slack community" |
|||
>Slack community</a |
|||
>, tweet to |
|||
<a |
|||
href="https://twitter.com/ghostfolio_" |
|||
title="Tweet to Ghostfolio on Twitter" |
|||
>@ghostfolio_</a |
|||
><ng-container *ngIf="user?.subscription?.type === 'Premium'" |
|||
>, send an e-mail to |
|||
<a href="mailto:hi@ghostfol.io" title="Send an e-mail" |
|||
>hi@ghostfol.io</a |
|||
></ng-container |
|||
> |
|||
or start a discussion at |
|||
<a |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
title="Find Ghostfolio on GitHub" |
|||
>GitHub</a |
|||
>. |
|||
</p> |
|||
<p class="text-center"> |
|||
<a |
|||
class="mx-2" |
|||
href="https://twitter.com/ghostfolio_" |
|||
mat-icon-button |
|||
title="Follow Ghostfolio on Twitter" |
|||
> |
|||
<ion-icon name="logo-twitter"></ion-icon> |
|||
</a> |
|||
<a |
|||
*ngIf="user?.subscription?.type === 'Premium'" |
|||
class="mx-2" |
|||
href="mailto:hi@ghostfol.io" |
|||
mat-icon-button |
|||
title="Send an e-mail" |
|||
> |
|||
<ion-icon name="mail"></ion-icon> |
|||
</a> |
|||
<a |
|||
class="mx-2" |
|||
href="https://ghostfolio.slack.com" |
|||
mat-icon-button |
|||
title="Join the Ghostfolio Slack channel" |
|||
> |
|||
<ion-icon name="logo-slack"></ion-icon> |
|||
</a> |
|||
<a |
|||
class="mx-2" |
|||
href="https://github.com/ghostfolio/ghostfolio" |
|||
mat-icon-button |
|||
title="Find Ghostfolio on GitHub" |
|||
> |
|||
<ion-icon name="logo-github"></ion-icon> |
|||
</a> |
|||
</p> |
|||
<div |
|||
*ngIf="hasPermissionForSubscription" |
|||
class="d-flex justify-content-center" |
|||
> |
|||
<div |
|||
class="independent-and-bootstrapped-logo mb-2" |
|||
title="Ghostfolio is an independent & bootstrapped business" |
|||
></div> |
|||
</div> |
|||
<div |
|||
*ngIf="!hasPermissionForSubscription" |
|||
class="d-flex justify-content-center" |
|||
> |
|||
<a |
|||
href="https://www.buymeacoffee.com/ghostfolio" |
|||
target="_blank" |
|||
title="Support Ghostfolio" |
|||
><img |
|||
class="mb-2" |
|||
src="../assets/images/button-buy-me-a-coffee.png" |
|||
width="180" |
|||
/></a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row"> |
|||
<div *ngIf="hasPermissionForSubscription" class="col-md-6 col-xs-12 my-2"> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/faq']" |
|||
>Frequently Asked Questions (FAQ)</a |
|||
> |
|||
</div> |
|||
<div *ngIf="hasPermissionForBlog" class="col-md-6 col-xs-12 my-2"> |
|||
<a |
|||
class="py-4 w-100" |
|||
color="primary" |
|||
mat-flat-button |
|||
[routerLink]="['/blog']" |
|||
>Blog</a |
|||
> |
|||
</div> |
|||
</div> |
|||
</div> |
@ -0,0 +1,13 @@ |
|||
import { CommonModule } from '@angular/common'; |
|||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
|||
import { MatButtonModule } from '@angular/material/button'; |
|||
|
|||
import { AboutOverviewPageRoutingModule } from './about-overview-page-routing.module'; |
|||
import { AboutOverviewPageComponent } from './about-overview-page.component'; |
|||
|
|||
@NgModule({ |
|||
declarations: [AboutOverviewPageComponent], |
|||
imports: [AboutOverviewPageRoutingModule, CommonModule, MatButtonModule], |
|||
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
|||
}) |
|||
export class AboutOverviewPageModule {} |
@ -0,0 +1,36 @@ |
|||
:host { |
|||
color: rgb(var(--dark-primary-text)); |
|||
display: block; |
|||
|
|||
.about-container { |
|||
a { |
|||
color: rgba(var(--palette-primary-500), 1); |
|||
font-weight: 500; |
|||
|
|||
&:hover { |
|||
color: rgba(var(--palette-primary-300), 1); |
|||
} |
|||
} |
|||
|
|||
.independent-and-bootstrapped-logo { |
|||
background-image: url('/assets/bootstrapped-dark.svg'); |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
background-size: contain; |
|||
height: 2rem; |
|||
opacity: 0.87; |
|||
width: 10rem; |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host-context(.is-dark-theme) { |
|||
color: rgb(var(--light-primary-text)); |
|||
|
|||
.about-container { |
|||
.independent-and-bootstrapped-logo { |
|||
background-image: url('/assets/bootstrapped-light.svg'); |
|||
opacity: 1; |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue