From d133876325533696d6a30892652dbc17479cfc52 Mon Sep 17 00:00:00 2001 From: Eneko Nieto Date: Thu, 21 Jan 2021 01:14:17 +0100 Subject: [PATCH] Inicio de home y Angular Material --- angular.json | 6 +- package-lock.json | 37 ++++++++++++ package.json | 2 + src/app/app.component.html | 12 +--- src/app/app.component.ts | 10 +-- src/app/app.config.ts | 3 + src/app/app.module.ts | 22 ++++--- src/app/app.tokens.ts | 3 - ...uth-code-flow.config.ts => auth.config.ts} | 5 +- .../flight-booking/flight-booking.routes.ts | 2 +- .../flight-booking/services/flight.service.ts | 7 +-- src/app/header/header.component.css | 0 src/app/header/header.component.html | 13 ++++ src/app/header/header.component.spec.ts | 25 ++++++++ src/app/header/header.component.ts | 37 ++++++++++++ src/app/home/home.component.html | 57 ------------------ src/app/home/home.component.ts | 22 ------- src/assets/img/logo.png | Bin 0 -> 14727 bytes src/custom-theme.scss | 35 +++++++++++ src/index.html | 10 +-- src/styles.css | 3 + 21 files changed, 192 insertions(+), 119 deletions(-) create mode 100644 src/app/app.config.ts delete mode 100644 src/app/app.tokens.ts rename src/app/{auth-code-flow.config.ts => auth.config.ts} (94%) create mode 100644 src/app/header/header.component.css create mode 100644 src/app/header/header.component.html create mode 100644 src/app/header/header.component.spec.ts create mode 100644 src/app/header/header.component.ts create mode 100644 src/assets/img/logo.png create mode 100644 src/custom-theme.scss diff --git a/angular.json b/angular.json index 1f3c173..ed528bf 100644 --- a/angular.json +++ b/angular.json @@ -28,6 +28,7 @@ "src/assets" ], "styles": [ + "src/custom-theme.scss", "src/styles.css" ], "scripts": [] @@ -123,6 +124,7 @@ } } } - }}, + } + }, "defaultProject": "sample-oauth" -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ce544de..1243483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -249,6 +249,28 @@ } } }, + "@angular/cdk": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-10.2.7.tgz", + "integrity": "sha512-ZQjDfTRTn7JuAKsf3jiIdU2XBaxxGBi/ZWYv5Pb3HCl6B4PISsIE5VWRhkoUogoAB0MiFHpjnWeIqknJEm11YQ==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, "@angular/cli": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-10.2.1.tgz", @@ -512,6 +534,21 @@ } } }, + "@angular/material": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-10.2.7.tgz", + "integrity": "sha512-uk6JkRrKHaM9VFMzX7pWC83YNLVgXPB3D8U1yjSOafCdWwrRZgUHGr8MPlSILCr3o2nxgg5SsKdWcWwHuXXUZA==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, "@angular/platform-browser": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-10.2.4.tgz", diff --git a/package.json b/package.json index 3bcc5bb..b44acdf 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ "private": true, "dependencies": { "@angular/animations": "~10.2.4", + "@angular/cdk": "^10.2.7", "@angular/common": "~10.2.4", "@angular/compiler": "~10.2.4", "@angular/core": "~10.2.4", "@angular/forms": "~10.2.4", + "@angular/material": "^10.2.7", "@angular/platform-browser": "~10.2.4", "@angular/platform-browser-dynamic": "~10.2.4", "@angular/router": "~10.2.4", diff --git a/src/app/app.component.html b/src/app/app.component.html index d713365..6aa0064 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,12 +1,4 @@ - +
@@ -17,5 +9,3 @@
- - diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 451dc24..1c506c0 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -2,12 +2,12 @@ import { Component } from '@angular/core'; import { OAuthService, NullValidationHandler } from 'angular-oauth2-oidc'; import { Router } from '@angular/router'; import { filter } from 'rxjs/operators'; -import { authCodeFlowConfig } from './auth-code-flow.config'; +import { authConfig } from './auth.config'; import { useHash } from '../flags'; @Component({ // tslint:disable-next-line:component-selector - selector: 'flight-app', + selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent { @@ -24,7 +24,7 @@ export class AppComponent { } private configureCodeFlow(): void { - this.oauthService.configure(authCodeFlowConfig); + this.oauthService.configure(authConfig); this.oauthService.loadDiscoveryDocumentAndTryLogin().then((_) => { if (useHash) { this.router.navigate(['/']); @@ -32,6 +32,8 @@ export class AppComponent { }); // Optional - this.oauthService.setupAutomaticSilentRefresh(); + if (authConfig.useSilentRefresh) { + this.oauthService.setupAutomaticSilentRefresh(); + } } } diff --git a/src/app/app.config.ts b/src/app/app.config.ts new file mode 100644 index 0000000..848f8d3 --- /dev/null +++ b/src/app/app.config.ts @@ -0,0 +1,3 @@ +export const API_BASE_URL = 'http://localhost:8080'; +export const API_RELATIVE_PATH = '/api'; +export const API_URL = API_BASE_URL + API_RELATIVE_PATH; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d92cb77..1d5b3cc 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -6,17 +6,19 @@ import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { APP_ROUTES } from './app.routes'; -import { BASE_URL } from './app.tokens'; +import { API_URL } from './app.config'; import { FlightHistoryComponent } from './flight-history/flight-history.component'; import { HomeComponent } from './home/home.component'; import { SharedModule } from './shared/shared.module'; import { RouterModule, ExtraOptions } from '@angular/router'; import { useHash } from '../flags'; +import { HeaderComponent } from './header/header.component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; const ROUTING_OPTIONS: ExtraOptions = { // preloadingStrategy: CustomPreloadingStrategy, useHash, - initialNavigation: !useHash + initialNavigation: !useHash, }; @NgModule({ @@ -29,23 +31,25 @@ const ROUTING_OPTIONS: ExtraOptions = { SharedModule.forRoot(), OAuthModule.forRoot({ resourceServer: { - allowedUrls: ['http://localhost:8080/api'], - sendAccessToken: true - } - }) + allowedUrls: [API_URL], + sendAccessToken: true, + }, + }), + BrowserAnimationsModule, ], declarations: [ AppComponent, HomeComponent, - FlightHistoryComponent + FlightHistoryComponent, + HeaderComponent ], providers: [ // (useHash) ? { provide: LocationStrategy, useClass: HashLocationStrategy } : [], // {provide: AuthConfig, useValue: authConfig }, // { provide: OAuthStorage, useValue: localStorage }, // { provide: ValidationHandler, useClass: JwksValidationHandler }, - { provide: BASE_URL, useValue: 'http://localhost:8080' } + // { provide: BASE_URL, useValue: 'http://localhost:8080' } ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/app.tokens.ts b/src/app/app.tokens.ts deleted file mode 100644 index 8ca1a03..0000000 --- a/src/app/app.tokens.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { InjectionToken } from '@angular/core'; - -export const BASE_URL = new InjectionToken('http://localhost:8080'); diff --git a/src/app/auth-code-flow.config.ts b/src/app/auth.config.ts similarity index 94% rename from src/app/auth-code-flow.config.ts rename to src/app/auth.config.ts index d8b64cc..07c13e5 100644 --- a/src/app/auth-code-flow.config.ts +++ b/src/app/auth.config.ts @@ -1,7 +1,7 @@ import { AuthConfig } from 'angular-oauth2-oidc'; import { useSilentRefreshForCodeFlow } from '../flags'; -export const authCodeFlowConfig: AuthConfig = { +export const authConfig: AuthConfig = { issuer: 'https://auth.fosil.eu/auth/realms/fosil', // URL of the SPA to redirect the user to after login @@ -45,8 +45,9 @@ export const authCodeFlowConfig: AuthConfig = { showDebugInformation: true, sessionChecksEnabled: true, + sessionCheckIntervall: 10000, - timeoutFactor: 0.5, + timeoutFactor: 0.75, // disablePKCI: true, clearHashAfterLogin: false diff --git a/src/app/flight-booking/flight-booking.routes.ts b/src/app/flight-booking/flight-booking.routes.ts index ffbed7b..8e7ad7c 100644 --- a/src/app/flight-booking/flight-booking.routes.ts +++ b/src/app/flight-booking/flight-booking.routes.ts @@ -6,7 +6,7 @@ import { FlightBookingComponent } from './flight-booking.component'; import { AuthGuard } from '../shared/auth/auth.guard'; import { LeaveComponentGuard } from '../shared/deactivation/LeaveComponentGuard'; -let FLIGHT_BOOKING_ROUTES: Routes = [ +const FLIGHT_BOOKING_ROUTES: Routes = [ { path: '', component: FlightBookingComponent, diff --git a/src/app/flight-booking/services/flight.service.ts b/src/app/flight-booking/services/flight.service.ts index 27171c6..0667830 100644 --- a/src/app/flight-booking/services/flight.service.ts +++ b/src/app/flight-booking/services/flight.service.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { BASE_URL } from '../../app.tokens'; +import { API_URL } from '../../app.config'; import { Observable } from 'rxjs'; import { Flight } from '../../entities/flight'; import { OAuthService } from 'angular-oauth2-oidc'; @@ -9,14 +9,13 @@ import { OAuthService } from 'angular-oauth2-oidc'; export class FlightService { constructor( private oauthService: OAuthService, - private http: HttpClient, - @Inject(BASE_URL) private baseUrl: string + private http: HttpClient ) {} public flights: Array = []; find(from: string, to: string): void { - let url = this.baseUrl + '/api/flight'; + let url = API_URL + '/flight'; let headers = new HttpHeaders().set('Accept', 'application/json'); //.set('Authorization', 'Bearer ' + this.oauthService.getAccessToken()); diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html new file mode 100644 index 0000000..e09d086 --- /dev/null +++ b/src/app/header/header.component.html @@ -0,0 +1,13 @@ +
+ + +
+ + {{ name }} + + + + + + +
diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts new file mode 100644 index 0000000..7efd662 --- /dev/null +++ b/src/app/header/header.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './header.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts new file mode 100644 index 0000000..3b3d50d --- /dev/null +++ b/src/app/header/header.component.ts @@ -0,0 +1,37 @@ +import { Component, OnInit } from '@angular/core'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { authConfig } from '../auth.config'; + +@Component({ + selector: 'app-header', + templateUrl: './header.component.html', + styleUrls: ['./header.component.css'], +}) +export class HeaderComponent implements OnInit { + constructor(private oauthService: OAuthService) {} + + ngOnInit(): void {} + + async login(): Promise { + // Tweak config for code flow + this.oauthService.configure(authConfig); + console.log('login pre'); + await this.oauthService.loadDiscoveryDocument(); + console.log('login post'); + sessionStorage.setItem('flow', 'code'); + + this.oauthService.initLoginFlow(); + } + + logout(): void { + this.oauthService.revokeTokenAndLogout(); + } + + get name(): any { + const claims = this.oauthService.getIdentityClaims(); + if (!claims) { + return null; + } + return claims['name']; + } +} diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 92ca2f3..f2f502b 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,60 +1,3 @@ -Status: {{ givenName ? 'logged in' : 'logged out' }} -

Welcome!

-

Welcome, {{ givenName }} {{ familyName }}!

- -
- You have to login before you can search for flights. -
- -
-
-

Login with Authorization Server

-
- -
-
-
-

Test settings

-
- -
-
-
- -
-
-

Login with Code Flow

-

- - -

- Username/Password: alice/alice -
-
- -
-
-

Login with Code Flow in popup

-

- - -

-

Username/Password: alice/alice

-

- Note: When using IE, some security settings block the communication - with popups. This prevents that this feature works. -

-
-
-

access_token_expiration: {{ access_token_expiration }}

diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 3e5b50c..aa4fe97 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; -import { authCodeFlowConfig } from '../auth-code-flow.config'; import { ActivatedRoute } from '@angular/router'; @Component({ @@ -33,27 +32,6 @@ export class HomeComponent implements OnInit { */ } - async loginCode(): Promise { - // Tweak config for code flow - this.oauthService.configure(authCodeFlowConfig); - await this.oauthService.loadDiscoveryDocument(); - sessionStorage.setItem('flow', 'code'); - - this.oauthService.initLoginFlow('/some-state;p1=1;p2=2?p3=3&p4=4'); - // the parameter here is optional. It's passed around and can be used after logging in - } - - async loginCodeInPopup(): Promise { - // Tweak config for code flow - this.oauthService.configure(authCodeFlowConfig); - await this.oauthService.loadDiscoveryDocument(); - sessionStorage.setItem('flow', 'code'); - - this.oauthService.initLoginFlowInPopup().then(() => { - this.loadUserProfile(); - }); - } - logout(): void { // this.oauthService.logOut(); this.oauthService.revokeTokenAndLogout(); diff --git a/src/assets/img/logo.png b/src/assets/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..32bc79aa84510c1452c5bd18058a1787b90eb8e9 GIT binary patch literal 14727 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 - Sample + Okupa mi coche - - - + + + + + diff --git a/src/styles.css b/src/styles.css index 90d4ee0..7e7239a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }