diff --git a/frontend/src/app/app.component.scss b/frontend/src/app/app.component.scss
index 240b43c..1f9ece3 100644
--- a/frontend/src/app/app.component.scss
+++ b/frontend/src/app/app.component.scss
@@ -4,5 +4,6 @@
app-header {
width: 100%;
+ margin-bottom: 1em;
}
}
\ No newline at end of file
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index a006cd0..18fa3f8 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -1,6 +1,16 @@
import { Routes } from '@angular/router';
export const routes: Routes = [
- { path: 'login', loadComponent: () => import('./pages/login/login.component').then(module => module.LoginComponent) },
- { path: '**', loadComponent: () => import('./pages/home/home.component').then(module => module.HomeComponent) }
+ {
+ path: 'login',
+ loadComponent: () => import('./pages/login/login.component').then(module => module.LoginComponent)
+ },
+ {
+ path: 'disconnect',
+ loadComponent: () => import('./pages/disconnection/disconnection.component').then(module => module.DisconnectionComponent)
+ },
+ {
+ path: '**',
+ loadComponent: () => import('./pages/home/home.component').then(module => module.HomeComponent)
+ }
];
diff --git a/frontend/src/app/components/header/header.component.html b/frontend/src/app/components/header/header.component.html
index 8c01a1c..7de179d 100644
--- a/frontend/src/app/components/header/header.component.html
+++ b/frontend/src/app/components/header/header.component.html
@@ -2,8 +2,10 @@
-
- Codiki
+
+
+ Codiki
+
@@ -12,5 +14,10 @@
\ No newline at end of file
diff --git a/frontend/src/app/components/header/header.component.scss b/frontend/src/app/components/header/header.component.scss
index 3068014..84db464 100644
--- a/frontend/src/app/components/header/header.component.scss
+++ b/frontend/src/app/components/header/header.component.scss
@@ -25,14 +25,24 @@ $headerHeight: 3.5em;
gap: 1em;
padding: 0 1em;
- img {
- $imageSize: 2em;
- width: $imageSize;
- height: $imageSize;
- }
+ a {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ color: white;
+ text-decoration: none;
+ gap: .5em;
- .title {
- font-size: 1.5em;
+ img {
+ $imageSize: 2em;
+ width: $imageSize;
+ height: $imageSize;
+ }
+
+ .title {
+ font-size: 1.5em;
+ }
}
}
@@ -76,6 +86,7 @@ $headerHeight: 3.5em;
align-items: center;
min-width: 5em;
color: white;
+ margin: 0 .5em;
}
}
}
diff --git a/frontend/src/app/components/header/header.component.ts b/frontend/src/app/components/header/header.component.ts
index e20662d..3522c24 100644
--- a/frontend/src/app/components/header/header.component.ts
+++ b/frontend/src/app/components/header/header.component.ts
@@ -1,13 +1,21 @@
-import { Component } from '@angular/core';
+import { Component, inject } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
+import { AuthenticationService } from '../../core/service/authentication.service';
+import { CommonModule } from '@angular/common';
@Component({
selector: 'app-header',
standalone: true,
- imports: [MatButtonModule, MatIconModule, RouterModule],
+ imports: [CommonModule, MatButtonModule, MatIconModule, RouterModule],
templateUrl: './header.component.html',
styleUrl: './header.component.scss',
})
-export class HeaderComponent {}
+export class HeaderComponent {
+ private authenticationService = inject(AuthenticationService);
+
+ get isAuthenticated(): boolean {
+ return this.authenticationService.isAuthenticated();
+ }
+}
diff --git a/frontend/src/app/core/service/authentication.service.ts b/frontend/src/app/core/service/authentication.service.ts
index 0aa69b8..117f4d6 100644
--- a/frontend/src/app/core/service/authentication.service.ts
+++ b/frontend/src/app/core/service/authentication.service.ts
@@ -55,7 +55,8 @@ export class AuthenticationService {
const tokenParts = token?.split('.');
if (tokenParts?.length === 3 && tokenParts[1].length) {
- const userDetails: UserDetails = JSON.parse(tokenParts[1]);
+ const decodedTokenPart = atob(tokenParts[1]);
+ const userDetails: UserDetails = JSON.parse(decodedTokenPart);
result = userDetails;
}
diff --git a/frontend/src/app/pages/disconnection/disconnection.component.html b/frontend/src/app/pages/disconnection/disconnection.component.html
new file mode 100644
index 0000000..6d04722
--- /dev/null
+++ b/frontend/src/app/pages/disconnection/disconnection.component.html
@@ -0,0 +1,2 @@
+Disconnection...
+
\ No newline at end of file
diff --git a/frontend/src/app/pages/disconnection/disconnection.component.scss b/frontend/src/app/pages/disconnection/disconnection.component.scss
new file mode 100644
index 0000000..01013a5
--- /dev/null
+++ b/frontend/src/app/pages/disconnection/disconnection.component.scss
@@ -0,0 +1,6 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/frontend/src/app/pages/disconnection/disconnection.component.ts b/frontend/src/app/pages/disconnection/disconnection.component.ts
new file mode 100644
index 0000000..7ba4d73
--- /dev/null
+++ b/frontend/src/app/pages/disconnection/disconnection.component.ts
@@ -0,0 +1,21 @@
+import { Component, OnInit, inject } from '@angular/core';
+import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
+import { AuthenticationService } from '../../core/service/authentication.service';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-disconnection',
+ standalone: true,
+ imports: [MatProgressSpinnerModule],
+ templateUrl: './disconnection.component.html',
+ styleUrl: './disconnection.component.scss'
+})
+export class DisconnectionComponent implements OnInit {
+ private authenticationService = inject(AuthenticationService);
+ private router = inject(Router);
+
+ ngOnInit(): void {
+ this.authenticationService.unauthenticate();
+ this.router.navigate(['/home']);
+ }
+}
diff --git a/frontend/src/app/pages/home/home.component.html b/frontend/src/app/pages/home/home.component.html
index 5f2c53f..26e0443 100644
--- a/frontend/src/app/pages/home/home.component.html
+++ b/frontend/src/app/pages/home/home.component.html
@@ -1 +1 @@
-home works!
+Welcome to Codiki application!
diff --git a/frontend/src/app/pages/home/home.component.scss b/frontend/src/app/pages/home/home.component.scss
index e69de29..01013a5 100644
--- a/frontend/src/app/pages/home/home.component.scss
+++ b/frontend/src/app/pages/home/home.component.scss
@@ -0,0 +1,6 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/frontend/src/app/pages/login/login.component.html b/frontend/src/app/pages/login/login.component.html
index d05990b..45e31f5 100644
--- a/frontend/src/app/pages/login/login.component.html
+++ b/frontend/src/app/pages/login/login.component.html
@@ -4,7 +4,7 @@
-
+
-
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/pages/login/login.component.scss b/frontend/src/app/pages/login/login.component.scss
index e69de29..2592ed7 100644
--- a/frontend/src/app/pages/login/login.component.scss
+++ b/frontend/src/app/pages/login/login.component.scss
@@ -0,0 +1,37 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ form {
+ min-width: 40em;
+ max-width: 90%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: .5em;
+
+ div {
+ display: flex;
+ gap: .5em;
+
+ &.actions {
+ justify-content: end;
+
+ button {
+ width: 10em;
+ height: 2em;
+ }
+ }
+
+ label {
+ flex: 1 30%;
+ }
+
+ input {
+ flex: 1 70%;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/pages/login/login.service.ts b/frontend/src/app/pages/login/login.service.ts
index a8c2ec5..84fd89f 100644
--- a/frontend/src/app/pages/login/login.service.ts
+++ b/frontend/src/app/pages/login/login.service.ts
@@ -1,76 +1,81 @@
-import { Injectable, inject } from "@angular/core";
-import { BehaviorSubject, Observable } from "rxjs";
-import { copy } from "../../core/utils/ObjectUtils";
-import { FormError } from "../../core/model/FormError";
-import { UserRestService } from "../../core/rest-services/user.rest-service";
-import { LoginRequest } from "../../core/rest-services/model/login.model";
-import { AuthenticationService } from "../../core/service/authentication.service";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { Router } from "@angular/router";
+import { Injectable, inject } from '@angular/core';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { copy } from '../../core/utils/ObjectUtils';
+import { FormError } from '../../core/model/FormError';
+import { UserRestService } from '../../core/rest-services/user.rest-service';
+import { LoginRequest } from '../../core/rest-services/model/login.model';
+import { AuthenticationService } from '../../core/service/authentication.service';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { Router } from '@angular/router';
export interface LoginState {
- request: LoginRequest;
- errors: FormError[]
+ request: LoginRequest;
+ errors: FormError[];
}
const DEFAULT_STATE: LoginState = {
- request: {
- email: undefined,
- password: undefined
- },
- errors: []
-}
+ request: {
+ email: undefined,
+ password: undefined,
+ },
+ errors: [],
+};
@Injectable()
export class LoginService {
- private stateSubject = new BehaviorSubject(copy(DEFAULT_STATE));
- private userRestService = inject(UserRestService);
- private authenticationService = inject(AuthenticationService);
- private snackBar = inject(MatSnackBar);
- private router = inject(Router);
-
- get state$(): Observable {
- return this.stateSubject.asObservable();
- }
+ private stateSubject = new BehaviorSubject(copy(DEFAULT_STATE));
+ private userRestService = inject(UserRestService);
+ private authenticationService = inject(AuthenticationService);
+ private snackBar = inject(MatSnackBar);
+ private router = inject(Router);
- private get state(): LoginState {
- return this.stateSubject.value;
- }
+ get state$(): Observable {
+ return this.stateSubject.asObservable();
+ }
- private save(newState: LoginState): void {
- this.stateSubject.next(newState);
- }
+ private get state(): LoginState {
+ return this.stateSubject.value;
+ }
- editEmail(newEmail: string): void {
- const state = this.state;
+ private save(newState: LoginState): void {
+ this.stateSubject.next(newState);
+ }
- state.request.email = newEmail;
+ editEmail(newEmail: string): void {
+ const state = this.state;
- this.save(state);
- }
+ state.request.email = newEmail;
- editPassword(newPassword: string): void {
- const state = this.state;
+ this.save(state);
+ }
- state.request.password = newPassword;
+ editPassword(newPassword: string): void {
+ const state = this.state;
- this.save(state);
- }
+ state.request.password = newPassword;
- performLogin(): void {
- const state = this.state;
+ this.save(state);
+ }
- // Check state is valid
+ performLogin(): void {
+ const state = this.state;
- this.userRestService.login(state.request)
- .then(response => {
- this.authenticationService.authenticate(response.accessToken);
- this.snackBar.open('Authentication succeeded!', 'Close', { duration: 5000 });
- this.router.navigate(['/home']);
- })
- .catch(error => {
- console.error(error)
- this.snackBar.open('Authentication failed.', 'Close', { duration: 5000 });
- });
- }
-}
\ No newline at end of file
+ // Check state is valid
+
+ this.userRestService
+ .login(state.request)
+ .then((response) => {
+ this.authenticationService.authenticate(response.accessToken);
+ this.snackBar.open('Authentication succeeded!', 'Close', {
+ duration: 5000,
+ });
+ this.router.navigate(['/home']);
+ })
+ .catch((error) => {
+ console.error(error);
+ this.snackBar.open('Authentication failed.', 'Close', {
+ duration: 5000,
+ });
+ });
+ }
+}