Feature/add account registration page (#141)

* Add account registration page

* Update changelog
This commit is contained in:
Thomas 2021-06-05 17:16:07 +02:00 committed by GitHub
parent aa4206af0e
commit 026a5011d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 323 additions and 121 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Added a dedicated page for the account registration
### Changed ### Changed
- Changed the buttons to links (`<a>`) on the tools page - Changed the buttons to links (`<a>`) on the tools page

View File

@ -45,6 +45,13 @@ const routes: Routes = [
(m) => m.PricingPageModule (m) => m.PricingPageModule
) )
}, },
{
path: 'register',
loadChildren: () =>
import('./pages/register/register-page.module').then(
(m) => m.RegisterPageModule
)
},
{ {
path: 'resources', path: 'resources',
loadChildren: () => loadChildren: () =>
@ -55,7 +62,9 @@ const routes: Routes = [
{ {
path: 'start', path: 'start',
loadChildren: () => loadChildren: () =>
import('./pages/login/login-page.module').then((m) => m.LoginPageModule) import('./pages/landing/landing-page.module').then(
(m) => m.LandingPageModule
)
}, },
{ {
path: 'tools', path: 'tools',

View File

@ -12,13 +12,15 @@
<div *ngIf="canCreateAccount" class="container create-account-container"> <div *ngIf="canCreateAccount" class="container create-account-container">
<div class="row mb-5"> <div class="row mb-5">
<div class="col-md-6 offset-md-3"> <div class="col-md-6 offset-md-3">
<div <a [routerLink]="['/']">
class="create-account-box p-2 text-center" <mat-card
(click)="onCreateAccount()" class="create-account-box p-2 text-center"
(click)="onCreateAccount()"
>
<div class="mt-1" i18n>You are using the Live Demo.</div>
<button mat-button color="primary" i18n>Create Account</button>
</mat-card></a
> >
<div class="mt-1" i18n>You are using the Live Demo.</div>
<button mat-button color="primary" i18n>Create Account</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,8 +5,6 @@
padding: 5rem 0; padding: 5rem 0;
.create-account-box { .create-account-box {
border: 1px solid rgba(var(--palette-primary-500), 1);
border-radius: 0.25rem;
cursor: pointer; cursor: pointer;
font-size: 90%; font-size: 90%;

View File

@ -68,17 +68,12 @@ export class AppComponent implements OnDestroy, OnInit {
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => { .subscribe((state) => {
if (state?.user) { this.user = state.user;
this.user = state.user;
this.canCreateAccount = hasPermission( this.canCreateAccount = hasPermission(
this.user.permissions, this.user?.permissions,
permissions.createUserAccount permissions.createUserAccount
); );
} else if (!this.tokenStorageService.getToken()) {
// User has not been logged in
this.user = null;
}
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
@ -86,7 +81,6 @@ export class AppComponent implements OnDestroy, OnInit {
public onCreateAccount() { public onCreateAccount() {
this.tokenStorageService.signOut(); this.tokenStorageService.signOut();
window.location.reload();
} }
public onSignOut() { public onSignOut() {

View File

@ -2,6 +2,7 @@ import { Platform } from '@angular/cdk/platform';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { import {
DateAdapter, DateAdapter,
MAT_DATE_FORMATS, MAT_DATE_FORMATS,
@ -34,6 +35,7 @@ import { LanguageService } from './core/language.service';
HttpClientModule, HttpClientModule,
MarkdownModule.forRoot(), MarkdownModule.forRoot(),
MatButtonModule, MatButtonModule,
MatCardModule,
MaterialCssVarsModule.forRoot({ MaterialCssVarsModule.forRoot({
darkThemeClass: 'is-dark-theme', darkThemeClass: 'is-dark-theme',
isAutoContrast: true, isAutoContrast: true,

View File

@ -8,9 +8,11 @@
class="d-none d-sm-block" class="d-none d-sm-block"
i18n i18n
mat-flat-button mat-flat-button
[color]=" [ngClass]="{
currentRoute === 'home' || currentRoute === 'zen' ? 'primary' : null 'font-weight-bold': currentRoute === 'home' || currentRoute === 'zen',
" 'text-decoration-underline':
currentRoute === 'home' || currentRoute === 'zen'
}"
[routerLink]="['/']" [routerLink]="['/']"
>Overview</a >Overview</a
> >
@ -19,13 +21,16 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]=" [ngClass]="{
currentRoute === 'analysis' || 'font-weight-bold':
currentRoute === 'report' || currentRoute === 'analysis' ||
currentRoute === 'tools' currentRoute === 'report' ||
? 'primary' currentRoute === 'tools',
: null 'text-decoration-underline':
" currentRoute === 'analysis' ||
currentRoute === 'report' ||
currentRoute === 'tools'
}"
[routerLink]="['/tools']" [routerLink]="['/tools']"
>Tools</a >Tools</a
> >
@ -33,7 +38,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'transactions' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'transactions',
'text-decoration-underline': currentRoute === 'transactions'
}"
[routerLink]="['/transactions']" [routerLink]="['/transactions']"
>Transactions</a >Transactions</a
> >
@ -41,7 +49,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'accounts' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'accounts',
'text-decoration-underline': currentRoute === 'accounts'
}"
[routerLink]="['/accounts']" [routerLink]="['/accounts']"
>Accounts</a >Accounts</a
> >
@ -50,7 +61,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'admin' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'admin',
'text-decoration-underline': currentRoute === 'admin'
}"
[routerLink]="['/admin']" [routerLink]="['/admin']"
>Admin Control</a >Admin Control</a
> >
@ -58,7 +72,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'resources' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'resources',
'text-decoration-underline': currentRoute === 'resources'
}"
[routerLink]="['/resources']" [routerLink]="['/resources']"
>Resources</a >Resources</a
> >
@ -67,7 +84,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'pricing' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'pricing',
'text-decoration-underline': currentRoute === 'pricing'
}"
[routerLink]="['/pricing']" [routerLink]="['/pricing']"
>Pricing</a >Pricing</a
> >
@ -75,7 +95,10 @@
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'about' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'about',
'text-decoration-underline': currentRoute === 'about'
}"
[routerLink]="['/about']" [routerLink]="['/about']"
>About</a >About</a
> >
@ -226,28 +249,44 @@
<gf-logo></gf-logo> <gf-logo></gf-logo>
</a> </a>
<span class="spacer"></span> <span class="spacer"></span>
<a
*ngIf="hasPermissionForSubscription"
i18n
mat-flat-button
[color]="currentRoute === 'pricing' ? 'primary' : null"
[routerLink]="['/pricing']"
>Pricing</a
>
<a <a
class="d-none d-sm-block mx-1" class="d-none d-sm-block mx-1"
i18n i18n
mat-flat-button mat-flat-button
[color]="currentRoute === 'about' ? 'primary' : null" [ngClass]="{
'font-weight-bold': currentRoute === 'about',
'text-decoration-underline': currentRoute === 'about'
}"
[routerLink]="['/about']" [routerLink]="['/about']"
>About</a >About</a
> >
<a <a
class="d-none d-sm-block mx-1" *ngIf="hasPermissionForSubscription"
i18n
mat-flat-button
[ngClass]="{
'font-weight-bold': currentRoute === 'pricing',
'text-decoration-underline': currentRoute === 'pricing'
}"
[routerLink]="['/pricing']"
>Pricing</a
>
<a
class="d-none d-sm-block mx-1 no-min-width px-1"
href="https://github.com/ghostfolio/ghostfolio" href="https://github.com/ghostfolio/ghostfolio"
mat-flat-button mat-flat-button
>GitHub</a ><ion-icon name="logo-github"></ion-icon
> ></a>
<button i18n mat-flat-button (click)="openLoginDialog()">Sign in</button> <button class="mx-1" i18n mat-flat-button (click)="openLoginDialog()">
Sign In
</button>
<a
class="d-none d-sm-block"
color="primary"
i18n
mat-flat-button
[routerLink]="['/register']"
>Get Started
</a>
</ng-container> </ng-container>
</mat-toolbar> </mat-toolbar>

View File

@ -8,7 +8,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { LoginWithAccessTokenDialog } from '@ghostfolio/client/pages/login/login-with-access-token-dialog/login-with-access-token-dialog.component'; import { LoginWithAccessTokenDialog } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.component';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';

View File

@ -4,7 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { LoginWithAccessTokenDialogModule } from '@ghostfolio/client/pages/login/login-with-access-token-dialog/login-with-access-token-dialog.module'; import { LoginWithAccessTokenDialogModule } from '@ghostfolio/client/components/login-with-access-token-dialog/login-with-access-token-dialog.module';
import { GfLogoModule } from '../logo/logo.module'; import { GfLogoModule } from '../logo/logo.module';
import { HeaderComponent } from './header.component'; import { HeaderComponent } from './header.component';

View File

@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({ @Component({
selector: 'login-with-access-token-dialog', selector: 'gf-login-with-access-token-dialog',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./login-with-access-token-dialog.scss'], styleUrls: ['./login-with-access-token-dialog.scss'],
templateUrl: 'login-with-access-token-dialog.html' templateUrl: 'login-with-access-token-dialog.html'

View File

@ -14,7 +14,12 @@ import { UserService } from '../services/user/user.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate { export class AuthGuard implements CanActivate {
private static PUBLIC_PAGE_ROUTES = ['/about', '/pricing', '/resources']; private static PUBLIC_PAGE_ROUTES = [
'/about',
'/pricing',
'/register',
'/resources'
];
constructor( constructor(
private router: Router, private router: Router,

View File

@ -2,14 +2,14 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { LoginPageComponent } from './login-page.component'; import { LandingPageComponent } from './landing-page.component';
const routes: Routes = [ const routes: Routes = [
{ path: '', component: LoginPageComponent, canActivate: [AuthGuard] } { path: '', component: LandingPageComponent, canActivate: [AuthGuard] }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule]
}) })
export class LoginPageRoutingModule {} export class LandingPageRoutingModule {}

View File

@ -1,21 +1,17 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface'; import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ShowAccessTokenDialog } from './show-access-token-dialog/show-access-token-dialog.component';
@Component({ @Component({
selector: 'gf-login-page', selector: 'gf-landing-page',
templateUrl: './login-page.html', templateUrl: './landing-page.html',
styleUrls: ['./login-page.scss'] styleUrls: ['./landing-page.scss']
}) })
export class LoginPageComponent implements OnDestroy, OnInit { export class LandingPageComponent implements OnDestroy, OnInit {
public currentYear = format(new Date(), 'yyyy'); public currentYear = format(new Date(), 'yyyy');
public demoAuthToken: string; public demoAuthToken: string;
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
@ -28,7 +24,6 @@ export class LoginPageComponent implements OnDestroy, OnInit {
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
private dialog: MatDialog,
private router: Router, private router: Router,
private tokenStorageService: TokenStorageService private tokenStorageService: TokenStorageService
) {} ) {}
@ -46,15 +41,6 @@ export class LoginPageComponent implements OnDestroy, OnInit {
}); });
} }
public async createAccount() {
this.dataService
.postUser()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ accessToken, authToken }) => {
this.openShowAccessTokenDialog(accessToken, authToken);
});
}
public initializeLineChart() { public initializeLineChart() {
this.historicalDataItems = [ this.historicalDataItems = [
{ {
@ -268,28 +254,6 @@ export class LoginPageComponent implements OnDestroy, OnInit {
]; ];
} }
public openShowAccessTokenDialog(
accessToken: string,
authToken: string
): void {
const dialogRef = this.dialog.open(ShowAccessTokenDialog, {
data: {
accessToken,
authToken
},
disableClose: true,
width: '30rem'
});
dialogRef.afterClosed().subscribe((data) => {
if (data?.authToken) {
this.tokenStorageService.saveToken(authToken);
this.router.navigate(['/']);
}
});
}
public setToken(aToken: string) { public setToken(aToken: string) {
this.tokenStorageService.saveToken(aToken); this.tokenStorageService.saveToken(aToken);

View File

@ -13,16 +13,16 @@
class="align-items-center col d-flex justify-content-center position-relative" class="align-items-center col d-flex justify-content-center position-relative"
> >
<div class="py-5 text-center"> <div class="py-5 text-center">
<button <a
class="d-inline-block" class="d-inline-block"
color="primary" color="primary"
i18n i18n
mat-flat-button mat-flat-button
[disabled]="!demoAuthToken" [disabled]="!demoAuthToken"
(click)="createAccount()" [routerLink]="['/register']"
> >
Create Account Get Started
</button> </a>
<div class="d-inline-block mx-3 text-muted" i18n>or</div> <div class="d-inline-block mx-3 text-muted" i18n>or</div>
<button <button
class="d-inline-block" class="d-inline-block"
@ -135,15 +135,15 @@
Join now or check out the example account Join now or check out the example account
</p> </p>
<div class="py-2 text-center"> <div class="py-2 text-center">
<button <a
color="primary" color="primary"
i18n i18n
mat-flat-button mat-flat-button
[disabled]="!demoAuthToken" [disabled]="!demoAuthToken"
(click)="createAccount()" [routerLink]="['/register']"
> >
Create Account Get Started
</button> </a>
<div class="d-inline-block mx-3 text-muted" i18n>or</div> <div class="d-inline-block mx-3 text-muted" i18n>or</div>
<button <button
class="d-inline-block" class="d-inline-block"

View File

@ -0,0 +1,25 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
import { GfLogoModule } from '@ghostfolio/client/components/logo/logo.module';
import { LandingPageRoutingModule } from './landing-page-routing.module';
import { LandingPageComponent } from './landing-page.component';
@NgModule({
declarations: [LandingPageComponent],
exports: [],
imports: [
CommonModule,
GfLineChartModule,
GfLogoModule,
LandingPageRoutingModule,
MatButtonModule,
RouterModule
],
providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class LandingPageModule {}

View File

@ -1,7 +1,9 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h3 class="d-flex justify-content-center mb-3" i18n>Pricing Plans</h3> <h3 class="d-flex justify-content-center mb-3 text-center" i18n>
Pricing Plans
</h3>
<mat-card class="mb-4"> <mat-card class="mb-4">
<mat-card-content> <mat-card-content>
<p> <p>
@ -188,8 +190,8 @@
</div> </div>
<div *ngIf="!user" class="row"> <div *ngIf="!user" class="row">
<div class="col mt-3 text-center"> <div class="col mt-3 text-center">
<a color="primary" i18n mat-flat-button [routerLink]="['/start']"> <a color="primary" i18n mat-flat-button [routerLink]="['/register']">
Create Account Get Started
</a> </a>
<p class="text-muted"><small>It's free</small></p> <p class="text-muted"><small>It's free</small></p>
</div> </div>

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { RegisterPageComponent } from './register-page.component';
const routes: Routes = [
{ path: '', component: RegisterPageComponent, canActivate: [AuthGuard] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RegisterPageRoutingModule {}

View File

@ -0,0 +1,98 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { format } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ShowAccessTokenDialog } from './show-access-token-dialog/show-access-token-dialog.component';
@Component({
selector: 'gf-register-page',
templateUrl: './register-page.html',
styleUrls: ['./register-page.scss']
})
export class RegisterPageComponent implements OnDestroy, OnInit {
public currentYear = format(new Date(), 'yyyy');
public demoAuthToken: string;
public hasPermissionForSocialLogin: boolean;
public historicalDataItems: LineChartItem[];
private unsubscribeSubject = new Subject<void>();
/**
* @constructor
*/
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private dialog: MatDialog,
private router: Router,
private tokenStorageService: TokenStorageService
) {
this.tokenStorageService.signOut();
}
/**
* Initializes the controller
*/
public ngOnInit() {
this.dataService
.fetchInfo()
.subscribe(({ demoAuthToken, globalPermissions }) => {
this.demoAuthToken = demoAuthToken;
this.hasPermissionForSocialLogin = hasPermission(
globalPermissions,
permissions.enableSocialLogin
);
this.changeDetectorRef.markForCheck();
});
}
public async createAccount() {
this.dataService
.postUser()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ accessToken, authToken }) => {
this.openShowAccessTokenDialog(accessToken, authToken);
});
}
public openShowAccessTokenDialog(
accessToken: string,
authToken: string
): void {
const dialogRef = this.dialog.open(ShowAccessTokenDialog, {
data: {
accessToken,
authToken
},
disableClose: true,
width: '30rem'
});
dialogRef.afterClosed().subscribe((data) => {
if (data?.authToken) {
this.tokenStorageService.saveToken(authToken);
this.router.navigate(['/']);
}
});
}
public setToken(aToken: string) {
this.tokenStorageService.saveToken(aToken);
this.router.navigate(['/']);
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

View File

@ -0,0 +1,30 @@
<div class="container">
<div class="row">
<div class="col">
<h3 class="d-flex justify-content-center mb-3 text-center" i18n>
Create your Ghostfolio account
</h3>
<mat-card class="mb-4">
<mat-card-content class="text-center">
<button
class="d-inline-block"
color="primary"
i18n
mat-flat-button
[disabled]="!demoAuthToken"
(click)="createAccount()"
>
Create Account
</button>
<ng-container *ngIf="hasPermissionForSocialLogin">
<div class="my-3 text-muted" i18n>or</div>
<a color="accent" href="/api/auth/google" mat-flat-button
><ion-icon class="mr-1" name="logo-google"></ion-icon
><span i18n>Continue with Google</span></a
>
</ng-container>
</mat-card-content>
</mat-card>
</div>
</div>
</div>

View File

@ -1,27 +1,27 @@
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 { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
import { GfLogoModule } from '@ghostfolio/client/components/logo/logo.module'; import { GfLogoModule } from '@ghostfolio/client/components/logo/logo.module';
import { LoginPageRoutingModule } from './login-page-routing.module'; import { RegisterPageRoutingModule } from './register-page-routing.module';
import { LoginPageComponent } from './login-page.component'; import { RegisterPageComponent } from './register-page.component';
import { ShowAccessTokenDialogModule } from './show-access-token-dialog/show-access-token-dialog.module'; import { ShowAccessTokenDialogModule } from './show-access-token-dialog/show-access-token-dialog.module';
@NgModule({ @NgModule({
declarations: [LoginPageComponent], declarations: [RegisterPageComponent],
exports: [], exports: [],
imports: [ imports: [
CommonModule, CommonModule,
GfLineChartModule,
GfLogoModule, GfLogoModule,
LoginPageRoutingModule,
MatButtonModule, MatButtonModule,
MatCardModule,
RegisterPageRoutingModule,
RouterModule, RouterModule,
ShowAccessTokenDialogModule ShowAccessTokenDialogModule
], ],
providers: [], providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
}) })
export class LoginPageModule {} export class RegisterPageModule {}

View File

@ -0,0 +1,3 @@
:host {
display: block;
}

View File

@ -7,7 +7,7 @@ import {
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({ @Component({
selector: 'show-access-token-dialog', selector: 'gf-show-access-token-dialog',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./show-access-token-dialog.scss'], styleUrls: ['./show-access-token-dialog.scss'],
templateUrl: 'show-access-token-dialog.html' templateUrl: 'show-access-token-dialog.html'

View File

@ -1,5 +1,5 @@
import { ClipboardModule } from '@angular/cdk/clipboard'; import { ClipboardModule } from '@angular/cdk/clipboard';
import { CdkTextareaAutosize, TextFieldModule } from '@angular/cdk/text-field'; import { TextFieldModule } from '@angular/cdk/text-field';
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 { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';

View File

@ -1,12 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { UserService } from './user/user.service';
const TOKEN_KEY = 'auth-token'; const TOKEN_KEY = 'auth-token';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class TokenStorageService { export class TokenStorageService {
public constructor() {} public constructor(private userService: UserService) {}
public getToken(): string { public getToken(): string {
return window.localStorage.getItem(TOKEN_KEY); return window.localStorage.getItem(TOKEN_KEY);
@ -22,6 +24,8 @@ export class TokenStorageService {
window.localStorage.clear(); window.localStorage.clear();
this.userService.remove();
if (utmSource) { if (utmSource) {
window.localStorage.setItem('utm_source', utmSource); window.localStorage.setItem('utm_source', utmSource);
} }

View File

@ -6,18 +6,22 @@
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url> <url>
<loc>https://ghostfol.io</loc> <loc>https://ghostfol.io</loc>
<lastmod>2021-05-14T20:24:46+00:00</lastmod> <lastmod>2021-06-03T00:00:00+00:00</lastmod>
</url> </url>
<url> <url>
<loc>https://ghostfol.io/about</loc> <loc>https://ghostfol.io/about</loc>
<lastmod>2021-05-14T20:24:46+00:00</lastmod> <lastmod>2021-06-03T00:00:00+00:00</lastmod>
</url> </url>
<url> <url>
<loc>https://ghostfol.io/pricing</loc> <loc>https://ghostfol.io/pricing</loc>
<lastmod>2021-05-14T20:24:46+00:00</lastmod> <lastmod>2021-06-03T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/register</loc>
<lastmod>2021-06-03T00:00:00+00:00</lastmod>
</url> </url>
<url> <url>
<loc>https://ghostfol.io/resources</loc> <loc>https://ghostfol.io/resources</loc>
<lastmod>2021-05-14T20:24:46+00:00</lastmod> <lastmod>2021-06-03T00:00:00+00:00</lastmod>
</url> </url>
</urlset> </urlset>

View File

@ -153,3 +153,7 @@ ngx-skeleton-loader {
.no-min-width { .no-min-width {
min-width: unset !important; min-width: unset !important;
} }
.text-decoration-underline {
text-decoration: underline !important;
}