Extracted frontend from webservice to new directory

Updated directory structure
Updated .gitignore
This commit is contained in:
2022-03-17 17:11:00 +01:00
parent 0140fe4b7c
commit ecc532b752
80 changed files with 338 additions and 204 deletions

View File

@@ -0,0 +1,30 @@
import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpHandler,
HttpRequest,
} from '@angular/common/http';
import { TokenStorageService } from '../Services/token.service';
import { Observable } from 'rxjs';
const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private token: TokenStorageService) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
let authReq = req;
const token = this.token.getToken();
if (token != null) {
authReq = req.clone({
headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token),
});
}
return next.handle(authReq);
}
}
export const authInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
];

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
const AUTH_API = 'https://aktienbot.flokaiser.com/api/';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private http: HttpClient) {}
login(username: string, password: string): Observable<any> {
return this.http.post(AUTH_API + 'login', {
username,
password,
});
}
register(username: string, password: string): Observable<any> {
return this.http.post(
AUTH_API + 'signup',
{
username,
password,
},
httpOptions
);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service';
describe('DataService', () => {
let service: DataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
const API_URL = 'http://localhost:8080/api/test/';
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(private http: HttpClient) {}
// getPublicContent(): Observable<any> {
// return this.http.get(API_URL + 'all', { responseType: 'text' });
// }
// getUserBoard(): Observable<any> {
// return this.http.get(API_URL + 'user', { responseType: 'text' });
// }
// getModeratorBoard(): Observable<any> {
// return this.http.get(API_URL + 'mod', { responseType: 'text' });
// }
// getAdminBoard(): Observable<any> {
// return this.http.get(API_URL + 'admin', { responseType: 'text' });
// }
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { TokenService } from './token.service';
describe('TokenService', () => {
let service: TokenService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TokenService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
const TOKEN_KEY = 'auth-token';
const USER_KEY = 'auth-user';
@Injectable({
providedIn: 'root',
})
export class TokenStorageService {
constructor() {}
signOut(): void {
window.sessionStorage.clear();
}
public saveToken(token: string): void {
window.sessionStorage.removeItem(TOKEN_KEY);
window.sessionStorage.setItem(TOKEN_KEY, token);
}
public getToken(): string | null {
return window.sessionStorage.getItem(TOKEN_KEY);
}
public saveUser(user: any): void {
window.sessionStorage.removeItem(USER_KEY);
window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
}
public getUser(): any {
const user = window.sessionStorage.getItem(USER_KEY);
if (user) {
return JSON.parse(user);
}
return {};
}
}

View File

@@ -0,0 +1,69 @@
<mat-grid-list cols="2" rowHeight="45%">
<!-- Stocks -->
<mat-grid-tile colspan="1" rowspan="2">
<div class="stockOverview">
<div class="heading">
<div class="vertical-center">Aktienübersicht</div>
<span class="spacer"></span>
<button
mat-icon-button
class="add-icon"
aria-label="Example icon-button with heart icon"
>
<mat-icon>add</mat-icon>
</button>
</div>
<div class="stockTable">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef>Symbol</th>
<td mat-cell *matCellDef="let element">{{ element.position }}</td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let element">{{ element.name }}</td>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef>Volume</th>
<td mat-cell *matCellDef="let element">{{ element.weight }}</td>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef>Worth</th>
<td mat-cell *matCellDef="let element">{{ element.symbol }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
</div>
</mat-grid-tile>
<!-- Depot Overview -->
<mat-grid-tile colspan="1" rowspan="1" class="right-side">
<div class="depotOverview">
<div class="heading fix-right-side">
<div class="vertical-center">Depotübersicht</div>
</div>
<mat-card class="placeholder"></mat-card>
</div>
</mat-grid-tile>
<!-- Transaktions -->
<mat-grid-tile colspan="1" rowspan="1" class="right-side">
<div class="depotOverview">
<div class="heading fix-right-side">
<div class="vertical-center">Transaktionen</div>
</div>
<mat-card class="placeholder"></mat-card>
</div>
</mat-grid-tile>
</mat-grid-list>

View File

@@ -0,0 +1,63 @@
// left gird
.stockOverview {
height: 100%;
width: 100%;
margin-top: 10%;
margin-left: 10%;
}
//right grids
.depotOverview {
height: 100%;
width: 100%;
margin-top: 10%;
margin-left: 5%;
margin-right: 10%;
}
.stockTable {
overflow: auto;
height: 100%;
width: 100%;
}
.heading {
font-size: xx-large;
height: 10%;
width: 100%;
position: relative;
display: flex;
}
.fix-right-side {
height: 20%;
}
.vertical-center {
margin: 0;
position: absolute;
top: 50%;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.spacer {
flex-grow: 1;
}
.add-icon {
transform: scale(2);
padding-top: 1%;
}
.right-side {
margin-left: 2.5%;
}
table {
width: 100%;
}
.placeholder {
height: 100%;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DashboardComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,45 @@
import { Component, OnInit } from '@angular/core';
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
const ELEMENT_DATA: PeriodicElement[] = [
{ position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
{ position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
{ position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
{ position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
{ position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
{ position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },
{ position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
{ position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
{ position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
{ position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },
{ position: 11, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
{ position: 12, name: 'Helium', weight: 4.0026, symbol: 'He' },
{ position: 13, name: 'Lithium', weight: 6.941, symbol: 'Li' },
{ position: 14, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
{ position: 15, name: 'Boron', weight: 10.811, symbol: 'B' },
{ position: 16, name: 'Carbon', weight: 12.0107, symbol: 'C' },
{ position: 17, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
{ position: 18, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
{ position: 19, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
{ position: 20, name: 'Neon', weight: 20.1797, symbol: 'Ne' },
];
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = ELEMENT_DATA;
}

View File

@@ -0,0 +1,18 @@
<mat-toolbar>
<button
mat-icon-button
class="example-icon"
aria-label="Example icon-button with menu icon"
>
<mat-icon>menu</mat-icon>
</button>
<span>Aktienbot</span>
<span class="example-spacer"></span>
<button
mat-icon-button
class="example-icon favorite-icon"
aria-label="Example icon-button with heart icon"
>
<mat-icon>settings</mat-icon>
</button>
</mat-toolbar>

View File

@@ -0,0 +1,3 @@
.example-spacer {
flex: 1 1 auto;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@@ -0,0 +1,75 @@
<div class="col-md-4 login-container">
<div class="card card-container no-border">
<img
id="profile-img"
src="https://i.kym-cdn.com/entries/icons/mobile/000/029/959/Screen_Shot_2019-06-05_at_1.26.32_PM.jpg"
class="profile-img-card"
/>
<form
*ngIf="!isLoggedIn"
name="form"
(ngSubmit)="f.form.valid && onSubmit()"
#f="ngForm"
novalidate
>
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
class="form-control"
name="username"
[(ngModel)]="form.username"
required
#username="ngModel"
/>
<div
class="alert alert-danger"
role="alert"
*ngIf="username.errors && f.submitted"
>
Username is required!
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
class="form-control"
name="password"
[(ngModel)]="form.password"
required
minlength="6"
#password="ngModel"
/>
<div
class="alert alert-danger"
role="alert"
*ngIf="password.errors && f.submitted"
>
<div *ngIf="password.errors?.['required']">Password is required</div>
<div *ngIf="password.errors?.['minlength']">
Password must be at least 6 characters
</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block">Login</button>
</div>
<div class="form-group">
<div
class="alert alert-danger"
role="alert"
*ngIf="f.submitted && isLoginFailed"
>
Login failed: {{ errorMessage }}
</div>
</div>
</form>
<div class="alert alert-success" *ngIf="isLoggedIn">
Logged in as {{ accountName }}.
</div>
<button class="btn btn-secondary btn-block" routerLink="/register">
Sign up
</button>
</div>
</div>

View File

@@ -0,0 +1,9 @@
.login-container {
margin: auto;
width: 60vh;
padding-top: 10vh;
}
.no-border {
border: none;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,55 @@
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../Services/auth.service';
import { TokenStorageService } from '../../Services/token.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
form: any = {
username: null,
password: null,
};
isLoggedIn = false;
isLoginFailed = false;
errorMessage = '';
accountName = '';
constructor(
private authService: AuthService,
private tokenStorage: TokenStorageService,
private router: Router
) {}
ngOnInit(): void {
this.tokenStorage.signOut();
if (this.tokenStorage.getToken()) {
this.isLoggedIn = true;
}
}
onSubmit(): void {
const { username, password } = this.form;
console.log(username, password);
this.authService.login(username, password).subscribe(
(data) => {
this.tokenStorage.saveToken(data.accessToken);
this.tokenStorage.saveUser(data);
this.isLoginFailed = false;
this.isLoggedIn = true;
this.accountName = username;
console.log(this.isLoggedIn);
this.router.navigate(['']);
},
(err) => {
this.errorMessage = err.error.message;
this.isLoginFailed = true;
}
);
}
reloadPage(): void {
window.location.reload();
}
}

View File

@@ -0,0 +1,69 @@
<div class="col-md-4 login-container">
<div class="card card-container no-border">
<img
id="profile-img"
src="https://i.kym-cdn.com/entries/icons/mobile/000/029/959/Screen_Shot_2019-06-05_at_1.26.32_PM.jpg"
class="profile-img-card"
/>
<form
*ngIf="!isSuccessful"
name="form"
(ngSubmit)="f.form.valid && onSubmit()"
#f="ngForm"
novalidate
>
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
class="form-control"
name="username"
[(ngModel)]="form.username"
required
minlength="3"
maxlength="20"
#username="ngModel"
/>
<div class="alert-danger" *ngIf="username.errors && f.submitted">
<div *ngIf="username.errors?.['required']">Username is required</div>
<div *ngIf="username.errors?.['minlength']">
Username must be at least 3 characters
</div>
<div *ngIf="username.errors?.['maxlength']">
Username must be at most 20 characters
</div>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
class="form-control"
name="password"
[(ngModel)]="form.password"
required
minlength="6"
#password="ngModel"
/>
<div class="alert-danger" *ngIf="password.errors && f.submitted">
<div *ngIf="password.errors?.['required']">Password is required</div>
<div *ngIf="password.errors?.['minlength']">
Password must be at least 6 characters
</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block">Sign Up</button>
</div>
<div class="alert alert-warning" *ngIf="f.submitted && isSignUpFailed">
Signup failed!<br />{{ errorMessage }}
</div>
</form>
<div class="alert alert-success" *ngIf="isSuccessful">
Your registration is successful!
</div>
<button class="btn btn-secondary btn-block" routerLink="/login">
Go Back
</button>
</div>
</div>

View File

@@ -0,0 +1,9 @@
.login-container {
margin: auto;
width: 60vh;
padding-top: 10vh;
}
.no-border {
border: none;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterComponent } from './register.component';
describe('RegisterComponent', () => {
let component: RegisterComponent;
let fixture: ComponentFixture<RegisterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RegisterComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(RegisterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,35 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../../Services/auth.service';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss'],
})
export class RegisterComponent implements OnInit {
form: any = {
username: null,
password: null,
};
isSuccessful = false;
isSignUpFailed = false;
errorMessage = '';
constructor(private authService: AuthService, private router: Router) {}
ngOnInit(): void {}
onSubmit(): void {
const { username, password } = this.form;
this.authService.register(username, password).subscribe(
(data) => {
console.log(data);
this.isSuccessful = true;
this.isSignUpFailed = false;
this.router.navigate(['/login']);
},
(err) => {
this.errorMessage = err.error.message;
this.isSignUpFailed = true;
}
);
}
}

View File

@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './Views/dashboard/dashboard.component';
import { LoginComponent } from './Views/login/login.component';
import { RegisterComponent } from './Views/register/register.component';
const routes: Routes = [
{
path: 'login',
component: LoginComponent,
},
{
path: '',
component: DashboardComponent,
},
{
path: 'register',
component: RegisterComponent,
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@@ -0,0 +1,2 @@
<app-header *ngIf="showHeader"></app-header>
<router-outlet></router-outlet>

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'Aktienbot'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('Aktienbot');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('Aktienbot app is running!');
});
});

View File

@@ -0,0 +1,49 @@
import { Component } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { TokenStorageService } from './Services/token.service';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
/**
* Application title.
*/
title = 'Aktienbot';
showHeader = false;
isLoggedIn = false;
/**
* Router import to show router-outlet.
*
* @param router Router
*/
constructor(
private router: Router,
private tokenStorage: TokenStorageService
) {
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe((event) => {
this.showHeader = !(
(event as NavigationEnd).url === '/login' ||
(event as NavigationEnd).url === '/register'
);
if (this.tokenStorage.getToken()) {
this.isLoggedIn = true;
} else {
this.isLoggedIn = false;
}
if (
this.isLoggedIn === false &&
(event as NavigationEnd).url != '/register'
) {
this.router.navigate(['/login']);
}
});
}
}

View File

@@ -0,0 +1,45 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatCardModule } from '@angular/material/card';
import { MatTableModule } from '@angular/material/table';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './Views/login/login.component';
import { HeaderComponent } from './Views/header/header.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DashboardComponent } from './Views/dashboard/dashboard.component';
import { RegisterComponent } from './Views/register/register.component';
@NgModule({
declarations: [
AppComponent,
LoginComponent,
HeaderComponent,
DashboardComponent,
RegisterComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MatToolbarModule,
MatIconModule,
MatButtonModule,
MatGridListModule,
MatCardModule,
MatTableModule,
FormsModule,
HttpClientModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}