Compare commits

...

4 Commits

Author SHA1 Message Date
ba47212057 Release 1.29.0 (#233) 2021-07-26 22:14:04 +02:00
f0c6517019 Improve tabs layout (#232) 2021-07-26 21:36:52 +02:00
80ba112bc0 Feature/change menu icon if menu open (#231)
* Change menu icon

* Update changelog
2021-07-26 21:06:10 +02:00
40696b425e Feature/add tabs on the home page (#230)
* Add tabs

* Update changelog
2021-07-26 20:23:32 +02:00
13 changed files with 375 additions and 242 deletions

View File

@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.29.0 - 26.07.2021
### Changed
- Introduced tabs on the home page
- Changed the menu icon if the menu is open on mobile
## 1.28.0 - 24.07.2021
### Added

View File

@ -9,7 +9,7 @@
</header>
<main role="main">
<div *ngIf="canCreateAccount" class="container create-account-container mb-2">
<div *ngIf="canCreateAccount" class="container create-account-container">
<div class="row">
<div class="col-md-8 offset-md-2 text-center">
<a class="text-center" [routerLink]="['/']">

View File

@ -9,6 +9,7 @@
padding-top: 5rem;
.create-account-container {
height: 2.5rem;
margin-top: -0.5rem;
.create-account-box {

View File

@ -106,6 +106,8 @@
class="no-min-width px-1"
mat-flat-button
[matMenuTriggerFor]="accountMenu"
(menuClosed)="onMenuClosed()"
(menuOpened)="onMenuOpened()"
>
<ion-icon
class="d-none d-sm-block"
@ -114,8 +116,8 @@
></ion-icon>
<ion-icon
class="d-block d-sm-none"
name="menu-outline"
size="large"
[name]="isMenuOpen ? 'close-outline' : 'menu-outline'"
></ion-icon>
</button>
<mat-menu #accountMenu="matMenu" xPosition="before">

View File

@ -38,6 +38,7 @@ export class HeaderComponent implements OnChanges {
public hasPermissionForSubscription: boolean;
public hasPermissionToAccessAdminControl: boolean;
public impersonationId: string;
public isMenuOpen: boolean;
private unsubscribeSubject = new Subject<void>();
@ -84,6 +85,14 @@ export class HeaderComponent implements OnChanges {
window.location.reload();
}
public onMenuClosed() {
this.isMenuOpen = false;
}
public onMenuOpened() {
this.isMenuOpen = true;
}
public onSignOut() {
this.signOut.next();
}

View File

@ -4,6 +4,7 @@ import {
ChangeDetectorRef,
Component,
ElementRef,
HostBinding,
OnDestroy,
OnInit,
ViewChild
@ -38,8 +39,13 @@ import { first, takeUntil } from 'rxjs/operators';
styleUrls: ['./home-page.scss']
})
export class HomePageComponent implements AfterViewInit, OnDestroy, OnInit {
@HostBinding('class.with-create-account-container') get isDemo() {
return this.canCreateAccount;
}
@ViewChild('positionsContainer') positionsContainer: ElementRef;
public canCreateAccount: boolean;
public dateRange: DateRange;
public dateRangeOptions: ToggleOption[] = [
{ label: 'Today', value: '1d' },
@ -95,6 +101,11 @@ export class HomePageComponent implements AfterViewInit, OnDestroy, OnInit {
if (state?.user) {
this.user = state.user;
this.canCreateAccount = hasPermission(
this.user?.permissions,
permissions.createUserAccount
);
this.hasPermissionToAccessFearAndGreedIndex = hasPermission(
this.user.permissions,
permissions.accessFearAndGreedIndex

View File

@ -1,108 +1,150 @@
<ng-container *ngIf="hasPositions || !historicalDataItems">
<div class="container overview position-relative">
<div class="row">
<a
class="chart-container col mr-3"
[routerLink]="[]"
[queryParams]="{performanceChartDialog: true}"
>
<gf-line-chart
symbol="Performance"
[historicalDataItems]="historicalDataItems"
[showLoader]="false"
[showXAxis]="false"
[showYAxis]="false"
></gf-line-chart>
</a>
</div>
<div class="overview-container row mb-5 mt-1">
<div class="col">
<gf-portfolio-performance-summary
class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale"
[performance]="performance"
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
></gf-portfolio-performance-summary>
<div class="text-center">
<gf-toggle
[defaultValue]="dateRange"
<mat-tab-group
animationDuration="0ms"
class="position-absolute"
headerPosition="below"
mat-align-tabs="center"
>
<mat-tab>
<ng-template mat-tab-label>
<ion-icon name="analytics-outline" size="large"></ion-icon>
</ng-template>
<div
class="
align-items-center
container
d-flex
flex-column
h-100
justify-content-center
overview
position-relative
"
>
<div class="row w-100">
<a
*ngIf="historicalDataItems?.length !== 0"
class="chart-container col"
[routerLink]="[]"
[queryParams]="{performanceChartDialog: true}"
>
<gf-line-chart
class="mr-3"
symbol="Performance"
[historicalDataItems]="historicalDataItems"
[showLoader]="false"
[showXAxis]="false"
[showYAxis]="false"
></gf-line-chart>
</a>
<div
*ngIf="historicalDataItems?.length === 0"
class="
align-items-center
chart-container
d-flex
justify-content-center
w-100
"
>
<div class="d-flex justify-content-center">
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</div>
</div>
<div class="overview-container row mt-1">
<div class="col">
<gf-portfolio-performance-summary
class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[options]="dateRangeOptions"
(change)="onChangeDateRange($event.value)"
></gf-toggle>
[locale]="user?.settings?.locale"
[performance]="performance"
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
></gf-portfolio-performance-summary>
<div class="text-center">
<gf-toggle
[defaultValue]="dateRange"
[isLoading]="isLoadingPerformance"
[options]="dateRangeOptions"
(change)="onChangeDateRange($event.value)"
></gf-toggle>
</div>
</div>
</div>
</div>
<div
class="button-container d-flex justify-content-center position-absolute"
>
<a
*ngIf="showPositionsButton"
[routerLink]="['/home']"
fragment="positions-container"
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<ion-icon name="wallet-outline" size="large"></ion-icon>
</ng-template>
<div class="container justify-content-center pb-3 px-3 positions">
<h3
*ngIf="hasPositions === true"
class="d-flex justify-content-center mb-3"
i18n
mat-flat-button
(click)="showPositionsButton = false"
>Positions</a
>
</div>
</div>
<div id="positions-container" class="container positions">
<div class="row mb-3">
<div class="col">
<mat-card class="p-0">
<mat-card-content>
<gf-positions
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positions"
[range]="dateRange"
></gf-positions>
</mat-card-content>
</mat-card>
Positions
</h3>
<div class="row">
<div class="align-items-center col">
<mat-card *ngIf="hasPositions === true" class="p-0">
<mat-card-content>
<gf-positions
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positions"
[range]="dateRange"
></gf-positions>
</mat-card-content>
</mat-card>
<div
*ngIf="hasPositions === false"
class="d-flex justify-content-center"
>
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6 mb-3">
<mat-card class="h-100">
<mat-card-header>
<mat-card-title i18n>Performance</mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-performance
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale"
[performance]="performance"
></gf-portfolio-performance>
</mat-card-content>
</mat-card>
</div>
<div class="col-xs-12 col-md-6 mb-3">
<mat-card class="h-100">
<mat-card-header>
<mat-card-title i18n>Summary</mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-overview
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingOverview"
[locale]="user?.settings?.locale"
[overview]="overview"
></gf-portfolio-overview>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<ion-icon name="reader-outline" size="large"></ion-icon>
</ng-template>
<div class="container pb-3 px-3 positions">
<div class="row">
<div class="col-xs-12 col-md-6 mb-3">
<mat-card class="h-100">
<mat-card-header>
<mat-card-title i18n>Performance</mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-performance
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale"
[performance]="performance"
></gf-portfolio-performance>
</mat-card-content>
</mat-card>
</div>
<div class="col-xs-12 col-md-6">
<mat-card class="h-100">
<mat-card-header>
<mat-card-title i18n>Summary</mat-card-title>
</mat-card-header>
<mat-card-content>
<gf-portfolio-overview
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingOverview"
[locale]="user?.settings?.locale"
[overview]="overview"
></gf-portfolio-overview>
</mat-card-content>
</mat-card>
</div>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="!hasPositions && historicalDataItems">
<div class="d-flex justify-content-center my-5">
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</ng-container>
</mat-tab>
</mat-tab-group>

View File

@ -2,8 +2,10 @@ 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 { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
import { GfNoTransactionsInfoModule } from '@ghostfolio/client/components/no-transactions-info/no-transactions-info.module';
import { GfPerformanceChartDialogModule } from '@ghostfolio/client/components/performance-chart-dialog/performance-chart-dialog.module';
import { GfPortfolioOverviewModule } from '@ghostfolio/client/components/portfolio-overview/portfolio-overview.module';
import { GfPortfolioPerformanceSummaryModule } from '@ghostfolio/client/components/portfolio-performance-summary/portfolio-performance-summary.module';
@ -20,6 +22,7 @@ import { HomePageComponent } from './home-page.component';
imports: [
CommonModule,
GfLineChartModule,
GfNoTransactionsInfoModule,
GfPerformanceChartDialogModule,
GfPortfolioOverviewModule,
GfPortfolioPerformanceModule,
@ -29,6 +32,7 @@ import { HomePageComponent } from './home-page.component';
HomePageRoutingModule,
MatButtonModule,
MatCardModule,
MatTabsModule,
RouterModule
],
providers: [],

View File

@ -3,58 +3,75 @@
:host {
color: rgb(var(--dark-primary-text));
display: block;
min-height: 100vh;
min-height: calc(100vh - 5rem);
position: relative;
.container {
&.overview {
min-height: calc(100vh - 5rem);
&.with-create-account-container {
min-height: calc(100vh - 5rem - 2.5rem);
}
.button-container {
bottom: 3rem;
left: 0;
right: 0;
.mat-flat-button {
background-color: rgba(0, 0, 0, $alpha-hover);
border-radius: 2rem;
.mat-tab-group {
bottom: 0;
left: 0;
right: 0;
top: 0;
::ng-deep {
.mat-tab-body-wrapper {
height: 100%;
.container {
&.overview {
.chart-container {
aspect-ratio: 16 / 9;
cursor: pointer;
max-height: 50vh;
// Fallback for aspect-ratio (using padding hack)
@supports not (aspect-ratio: 16 / 9) {
&::before {
float: left;
padding-top: 56.25%;
content: '';
}
&::after {
display: block;
content: '';
clear: both;
}
}
gf-line-chart {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: -1;
}
}
}
&.positions {
min-height: 100%;
}
}
}
.chart-container {
aspect-ratio: 16 / 9;
cursor: pointer;
margin-top: -1rem;
max-height: 50vh;
.mat-tab-header {
border-top: 0;
// Fallback for aspect-ratio (using padding hack)
@supports not (aspect-ratio: 16 / 9) {
&::before {
float: left;
padding-top: 56.25%;
content: '';
}
&::after {
display: block;
content: '';
clear: both;
}
.mat-ink-bar {
visibility: hidden !important;
}
gf-line-chart {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: -1;
.mat-tab-label-active {
color: rgba(var(--palette-primary-500), 1);
opacity: 1;
}
}
}
&.positions {
padding-top: 5rem;
min-height: calc(100vh - 5rem);
}
}
::ng-deep {

View File

@ -1,65 +1,91 @@
<ng-container *ngIf="hasPositions || !historicalDataItems">
<div class="container overview position-relative">
<div class="row">
<div class="chart-container col mr-3">
<gf-line-chart
symbol="Performance"
[historicalDataItems]="historicalDataItems"
[showLoader]="false"
[showXAxis]="false"
[showYAxis]="false"
></gf-line-chart>
</div>
</div>
<div class="overview-container row mb-5 mt-1">
<div class="col">
<gf-portfolio-performance-summary
class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale"
[performance]="performance"
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
></gf-portfolio-performance-summary>
</div>
</div>
<mat-tab-group
animationDuration="0ms"
class="position-absolute"
headerPosition="below"
mat-align-tabs="center"
>
<mat-tab>
<ng-template mat-tab-label>
<ion-icon name="analytics-outline" size="large"></ion-icon>
</ng-template>
<div
class="button-container d-flex justify-content-center position-absolute"
class="
container
d-flex
flex-column
h-100
justify-content-center
overview
position-relative
"
>
<a
*ngIf="showPositionsButton"
[routerLink]="['/zen']"
fragment="positions-container"
i18n
mat-flat-button
(click)="showPositionsButton = false"
>Positions</a
>
</div>
</div>
<div id="positions-container" class="container positions">
<div class="row mb-3">
<div class="col">
<mat-card class="p-0">
<mat-card-content>
<gf-positions
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positions"
[range]="dateRange"
></gf-positions>
</mat-card-content>
</mat-card>
<div class="row">
<div
class="chart-container d-flex flex-column col justify-content-center"
>
<gf-line-chart
class="mr-3"
symbol="Performance"
[historicalDataItems]="historicalDataItems"
[showLoader]="false"
[showXAxis]="false"
[showYAxis]="false"
></gf-line-chart>
<div
*ngIf="historicalDataItems?.length === 0"
class="d-flex justify-content-center"
>
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</div>
</div>
<div class="overview-container row mb-5 mt-1">
<div class="col">
<gf-portfolio-performance-summary
class="pb-4"
[baseCurrency]="user?.settings?.baseCurrency"
[isLoading]="isLoadingPerformance"
[locale]="user?.settings?.locale"
[performance]="performance"
[showDetails]="!hasImpersonationId || hasPermissionToReadForeignPortfolio"
></gf-portfolio-performance-summary>
</div>
</div>
</div>
</div>
</ng-container>
</mat-tab>
<ng-container *ngIf="!hasPositions && historicalDataItems">
<div class="d-flex justify-content-center my-5">
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</ng-container>
<mat-tab>
<ng-template mat-tab-label>
<ion-icon name="wallet-outline" size="large"></ion-icon>
</ng-template>
<div
*ngIf="hasPositions === true"
class="container justify-content-center pb-3 px-3 positions"
>
<h3 class="d-flex justify-content-center mb-3" i18n>Positions</h3>
<div class="row">
<div class="align-items-center col">
<mat-card *ngIf="hasPositions === true" class="p-0">
<mat-card-content>
<gf-positions
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[locale]="user?.settings?.locale"
[positions]="positions"
[range]="dateRange"
></gf-positions>
</mat-card-content>
</mat-card>
<div
*ngIf="hasPositions === false"
class="d-flex justify-content-center"
>
<div>
<gf-no-transactions-info-indicator></gf-no-transactions-info-indicator>
</div>
</div>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>

View File

@ -2,6 +2,7 @@ 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 { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
import { GfNoTransactionsInfoModule } from '@ghostfolio/client/components/no-transactions-info/no-transactions-info.module';
@ -22,6 +23,7 @@ import { ZenPageComponent } from './zen-page.component';
GfPositionsModule,
MatButtonModule,
MatCardModule,
MatTabsModule,
RouterModule,
ZenPageRoutingModule
],

View File

@ -3,58 +3,70 @@
:host {
color: rgb(var(--dark-primary-text));
display: block;
min-height: 100vh;
min-height: calc(100vh - 5rem);
position: relative;
.container {
&.overview {
min-height: calc(100vh - 5rem);
.mat-tab-group {
bottom: 0;
left: 0;
right: 0;
top: 0;
.button-container {
bottom: 3rem;
left: 0;
right: 0;
::ng-deep {
.mat-tab-body-wrapper {
height: 100%;
.mat-flat-button {
background-color: rgba(0, 0, 0, $alpha-hover);
border-radius: 2rem;
.container {
&.overview {
.chart-container {
aspect-ratio: 16 / 9;
max-height: 50vh;
// Fallback for aspect-ratio (using padding hack)
@supports not (aspect-ratio: 16 / 9) {
&::before {
float: left;
padding-top: 56.25%;
content: '';
}
&::after {
display: block;
content: '';
clear: both;
}
}
gf-line-chart {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: -1;
}
}
}
&.positions {
min-height: 100%;
}
}
}
.chart-container {
aspect-ratio: 16 / 9;
margin-top: 3rem;
max-height: 50vh;
.mat-tab-header {
border-top: 0;
// Fallback for aspect-ratio (using padding hack)
@supports not (aspect-ratio: 16 / 9) {
&::before {
float: left;
padding-top: 56.25%;
content: '';
}
&::after {
display: block;
content: '';
clear: both;
}
.mat-ink-bar {
visibility: hidden !important;
}
gf-line-chart {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: -1;
.mat-tab-label-active {
color: rgba(var(--palette-primary-500), 1);
opacity: 1;
}
}
}
&.positions {
padding-top: 5rem;
min-height: calc(100vh - 5rem);
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "1.28.0",
"version": "1.29.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"scripts": {