Skip to content

Commit

Permalink
Merge pull request #240 from amosproj/feat/xd-230
Browse files Browse the repository at this point in the history
Feat/xd 230 Login State Management
  • Loading branch information
IngoSternberg authored Jul 9, 2024
2 parents 4c87f40 + 23acd8e commit 3bfb1cd
Show file tree
Hide file tree
Showing 26 changed files with 337 additions and 171 deletions.
7 changes: 7 additions & 0 deletions apps/frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Route } from '@angular/router';
import { AuthenticationGuard } from 'common-frontend-models';

import { HeaderComponent } from './components/header/header.component';


export const APP_ROUTES: Route[] = [
{
path: '',
Expand All @@ -14,15 +16,18 @@ export const APP_ROUTES: Route[] = [
children: [
{
path: '',
canActivate: [ AuthenticationGuard ],
loadComponent: () =>
import('./pages/home/home.component').then((m) => m.HomeComponent),
},
{
path: 'home',
canActivate: [ AuthenticationGuard ],
redirectTo: '',
},
{
path: 'facilities',
canActivate: [ AuthenticationGuard ],
data: {
breadcrumb: 'Facilities',
title: 'Facilities Dashboard',
Expand All @@ -33,6 +38,7 @@ export const APP_ROUTES: Route[] = [
},
{
path: 'cases',
canActivate: [ AuthenticationGuard ],
data: {
breadcrumb: 'Cases',
title: 'Cases',
Expand All @@ -45,6 +51,7 @@ export const APP_ROUTES: Route[] = [
},
{
path: 'not-found',
canActivate: [ AuthenticationGuard ],
loadComponent: () =>
import('./pages/not-found/not-found.component').then((m) => m.NotFoundComponent),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
<ix-button class="mt-1" variant="secondary">
Powered by Siemens Xcelerator
</ix-button>
<ix-avatar username="John Doe" extra="Administrator"> </ix-avatar>
<ix-avatar [username]="userMail" extra="Administrator">
<ix-dropdown-item label="Logout" (click)="logout()"></ix-dropdown-item>
</ix-avatar>
</ix-application-header>

<ix-breadcrumb visibleItemCount="3" class="ml-8">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { ReplaySubject } from 'rxjs';
import { HeaderComponent } from './header.component';

const HEADER_ROUTES = {
snapshot: {
firstChild: {
routeConfig: {
path: ''
}
},
},
root: {
snapshot: {
data: {
Expand Down
23 changes: 16 additions & 7 deletions apps/frontend/src/app/components/header/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import {
ChangeDetectionStrategy,
Component,
computed,
inject,
ViewEncapsulation,
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute, NavigationEnd, Router, RouterLink, RouterOutlet } from '@angular/router';
import { IxModule, themeSwitcher } from '@siemens/ix-angular';
import { LocalStorageService } from 'common-frontend-models';
import { AuthenticationService,LocalStorageService } from 'common-frontend-models';
import { filter } from 'rxjs';

import { LegalInformationComponent } from './legal-information/legal-information.component';
Expand All @@ -28,14 +27,12 @@ import { LegalInformationComponent } from './legal-information/legal-information
})
export class HeaderComponent {

private readonly _activatedRoute: ActivatedRoute = inject(ActivatedRoute);
private readonly _router: Router = inject(Router);
private localStorageService = inject(LocalStorageService);
protected lightMode = computed(() => {
const theme = this.localStorageService.getOrCreate('theme', 'theme-classic-dark')();
const theme = this._localStorageService.getOrCreate('theme', 'theme-classic-dark')();
themeSwitcher.setTheme(theme);
return theme === 'theme-classic-light';
});
protected userMail = this.authenticationService.getUserMail();

readonly routerEvents = toSignal(
this._router.events.pipe(filter((e) => e instanceof NavigationEnd)),
Expand Down Expand Up @@ -67,6 +64,13 @@ export class HeaderComponent {
return this._activatedRoute.snapshot.firstChild?.routeConfig?.path === '';
});

constructor(
protected authenticationService: AuthenticationService,
private _localStorageService: LocalStorageService,
private _router: Router,
private _activatedRoute: ActivatedRoute,
) {}

/**
* from the right cut the current url until a '/' is reached n times
* So for /cases/10/abc, goBack(1) yields /cases/10
Expand All @@ -80,10 +84,15 @@ export class HeaderComponent {
}

toggleMode() {
this.localStorageService.set('theme', this.lightMode() ? 'theme-classic-dark' : 'theme-classic-light');
this._localStorageService.set('theme', this.lightMode() ? 'theme-classic-dark' : 'theme-classic-light');
}

refresh() {
window.location.reload();
}

logout(){
this.authenticationService.logout();
this._router.navigate([ '/account/login' ]);
}
}
17 changes: 5 additions & 12 deletions libs/account/frontend/shell/src/lib/shell.routes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { Route } from '@angular/router';

/**
* The routes for the facilities domain.
* The routes for the account domain.
* Note they are lazy loaded!
*
* @example
*
* {
* path: '',
* loadComponent: () => import('@xd/facilities/view').then(m => m.XdFacilitiesViewPage)
* },
*/
export const ACCOUNT_SHELL_ROUTES: Route[] = [
{
Expand All @@ -19,10 +12,10 @@ export const ACCOUNT_SHELL_ROUTES: Route[] = [
path: 'login',
loadComponent: () => import('account-frontend-view').then((m) => m.LoginPage),
},
{
path: 'register',
loadComponent: () => import('account-frontend-view').then((m) => m.RegisterPage),
},
{
path: '**',
redirectTo: 'login',
}
],
},
];
1 change: 0 additions & 1 deletion libs/account/frontend/view/src/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './login';
export * from './register';
103 changes: 52 additions & 51 deletions libs/account/frontend/view/src/lib/components/login/login.page.html
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
<ix-card class="login-card">
<ix-card-title>
<div class="login-title">
<ix-icon name="user" size="large"></ix-icon>
Xcelerator Demo Login
</div>
</ix-card-title>
<ix-card-content>
<form class="needs-validation m-2" (ngSubmit)="onSubmit()" #loginForm="ngForm">
<ix-input-group style="margin-bottom: 0.5rem">
<span slot="input-start"></span>
<input
[(ngModel)]="username"
placeholder="Enter Username"
type="text"
name="username"
required
/>
</ix-input-group>
<ix-input-group style="margin-bottom: 0.5rem">
<span slot="input-start"></span>
<input
[(ngModel)]="password"
placeholder="Enter password"
type="password"
name="password"
required
/>
</ix-input-group>
<ix-button type="submit" variant="primary" [disabled]="!loginForm.valid">
Login
</ix-button>
</form>
</ix-card-content>
<ix-button (click)="onForgotPassword()">Forgot Password?</ix-button>
<div class="absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/3">

<button class="github-login" size="32" (click)="onGitHubLogin()">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
class="bi bi-github"
viewBox="0 0 16 16"
>
<path
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"
/>
</svg>
Login with GitHub
</button>
</ix-card>
<form
class="needs-validation w-96 bg-[#283236] px-6 py-9"
[ngClass]="{ 'was-validated': wasValidated }"
(submit)="onSubmit()"
>
<ix-typography format="h2" class="pb-10">
Xcelerator Demo App
</ix-typography>

<label>Email</label>
<ix-input-group>
<input
class="pl-2"
name="username"
placeholder="Enter email address"
type="email"
[(ngModel)]="email"
required
/>
<div class="invalid-feedback">Please enter a valid email address</div>
</ix-input-group>

<label class="pt-3">Password</label>
<ix-input-group>
<input
id="passwordElement"
class="pl-2"
name="password"
placeholder="Enter password"
type="password"
[(ngModel)]="password"
required
>
<span slot="input-end" (click)="togglePassword()">
<ix-icon [name]="showPassword ? 'eye-cancelled-filled' : 'eye-filled'" size="16"></ix-icon>
</span>
<div class="invalid-feedback">Please enter a password</div>
</ix-input-group>

<div class="pt-10">
@if (formValid()) {
@if (loginSuccess()) {
<div class="text-[#3dbe0c] pb-2">Login success!</div>
} @else {
<div class="text-[#f9526c] pb-2">Invalid email address or password!</div>
}
}
<button class="btn btn-primary w-full" type="submit">Login</button>
</div>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -1,25 +0,0 @@
.login-card {
max-width: 400px;
margin: auto;
padding: 20px;
margin-top: 100px;
}
.login-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 20px;
font-weight: 500;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.github-login {
border: 300px grey;
margin-top: 15px;
display: flex;
align-items: center;
font-size: 20px;
gap: 10px;
}
92 changes: 57 additions & 35 deletions libs/account/frontend/view/src/lib/components/login/login.page.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,65 @@
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, signal, ViewEncapsulation } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { IxModule } from '@siemens/ix-angular';
import { IxModule, themeSwitcher } from '@siemens/ix-angular';
import { AuthenticationService } from 'common-frontend-models';

@Component({
selector: 'lib-login',
standalone: true,
imports: [ CommonModule, FormsModule, IxModule ],
templateUrl: './login.page.html',
styleUrls: [ './login.page.scss' ],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'lib-login',
standalone: true,
imports: [ CommonModule, FormsModule, IxModule ],
templateUrl: './login.page.html',
styleUrls: [ './login.page.scss' ],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginPage {
username = '';
password = '';

constructor(private router: Router) {}

onSubmit() {
// Mock authentication process
if (this.username === 'siemens' && this.password === 'siemens') {
// Redirect to dashboard on successful login
this.router.navigate([ '/facilities' ]);
} else {
// Display an error message for failed login
alert('Invalid username or password');
}
}

onForgotPassword() {
// TODO: Handle forgot password action
alert('Forgot password functionality is not implemented yet.');
}

onGitHubLogin() {
// Redirect to GitHub OAuth login page
window.location.href =
'https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI';
}
protected email = '';
protected password = '';

protected formValid = signal(false);
protected loginSuccess = signal(false);
protected wasValidated = false;
protected showPassword = false;

private readonly emailRegExp = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

constructor(
private _router: Router,
private _authenticationService: AuthenticationService,
) {
// this site is always in dark mode
themeSwitcher.setTheme('theme-classic-dark');
}


onSubmit() {
this.wasValidated = true;

const formValid = this.emailRegExp.test(this.email) && this.password !== '';
this.formValid.set(formValid);
if(!formValid){
return;
}

const loginSuccess = this._authenticationService.login(this.email, this.password);
this.loginSuccess.set(loginSuccess);
if(loginSuccess){
this._router.navigate([ '/' ]);
}

}

togglePassword() {
const passwordElement = document.getElementById('passwordElement');
if(!passwordElement) {
return;
}

this.showPassword = !this.showPassword;
const typeVal = this.showPassword ? 'text' : 'password';
passwordElement.setAttribute('type', typeVal);
}

}
Loading

0 comments on commit 3bfb1cd

Please sign in to comment.