diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c390f54..02e222f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -23,6 +23,7 @@ import { NewTravelComponent } from './pages/new-travel/new-travel.component'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; import { AppRouterModule } from './router.module'; +import { AvailablePlacesPipe } from './pipes/available-places.pipe'; // We need a factory, since localStorage is not available during AOT build time. export function storageFactory(): OAuthStorage { @@ -57,7 +58,8 @@ export function storageFactory(): OAuthStorage { HeaderComponent, ListComponent, TravelFormComponent, - NewTravelComponent + NewTravelComponent, + AvailablePlacesPipe ], providers: [ { provide: OAuthStorage, useFactory: storageFactory } diff --git a/src/app/entities/travel.ts b/src/app/entities/travel.ts index 255fa36..1b92123 100644 --- a/src/app/entities/travel.ts +++ b/src/app/entities/travel.ts @@ -1,12 +1,12 @@ -import { UserInfo } from './user-info'; +import { User } from './user'; export type TravelId = number; export class Travel { constructor( public id: TravelId = -1, - public driverInfo: UserInfo = null, - public travelersInfo: UserInfo[] = [], + public driverInfo: User = null, + public travelersInfo: User[] = [], public departureDate: string = '', public origin: string = '', public destination: string = '', @@ -14,4 +14,8 @@ export class Travel { public matrixRoomId: string = '', public description?: string ) { } + + availablePlaces(): number { + return this.places - this.travelersInfo.length; + } } diff --git a/src/app/entities/user-info.ts b/src/app/entities/user-info.ts deleted file mode 100644 index e3f9c8d..0000000 --- a/src/app/entities/user-info.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type UserId = string; - -export interface UserInfo { - id: UserId; - username: string; - matrixId?: string; - name: string; -} diff --git a/src/app/entities/user.ts b/src/app/entities/user.ts index 9f11ae2..b9a71a5 100644 --- a/src/app/entities/user.ts +++ b/src/app/entities/user.ts @@ -1,8 +1,24 @@ export type UserId = string; -export interface User { - id: UserId; - username: string; - matrixId?: string; - name: string; +export class User { + constructor( + public id: UserId, + public username: string, + public matrixId: string = null, + public name: string, + public admin: boolean = false + ) { } + + static fromClaims(claims: object): User { + if (claims == null) { + return null; + } + return new User( + claims["sub"].toString(), + claims["preferred_username"].toString(), + claims["preferred_username"] + ':fosil.eu', + claims["name"].toString(), + claims["admin"] === true + ); + } } diff --git a/src/app/pages/edit-travel/edit-travel.component.html b/src/app/pages/edit-travel/edit-travel.component.html index 585a22b..d857b71 100644 --- a/src/app/pages/edit-travel/edit-travel.component.html +++ b/src/app/pages/edit-travel/edit-travel.component.html @@ -1,4 +1,5 @@

Editar viaje

- +
\ No newline at end of file diff --git a/src/app/pages/travel/travel.component.html b/src/app/pages/travel/travel.component.html index e20d2b2..1f7349b 100644 --- a/src/app/pages/travel/travel.component.html +++ b/src/app/pages/travel/travel.component.html @@ -13,4 +13,4 @@ - + diff --git a/src/app/pages/travel/travel.component.ts b/src/app/pages/travel/travel.component.ts index 4830eaa..3161dd5 100644 --- a/src/app/pages/travel/travel.component.ts +++ b/src/app/pages/travel/travel.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { OAuthService } from 'angular-oauth2-oidc'; +import { AuthService } from 'src/app/services/auth.service'; import { Travel, TravelId } from '../../entities/travel'; import { ApiService } from '../../services/api.service'; @@ -16,7 +16,7 @@ export class TravelComponent implements OnInit { constructor( private router: Router, - private oauthService: OAuthService, + private authService: AuthService, private apiService: ApiService, private route: ActivatedRoute ) { } @@ -45,8 +45,7 @@ export class TravelComponent implements OnInit { this.router.navigateByUrl(`/travel/${this.travelId}/edit`); } - get logged(): boolean { - return this.oauthService.getIdentityClaims() != null; - // return this.oauthService.hasValidIdToken() && this.oauthService.hasValidAccessToken(); + get canEdit(): boolean { + return this.authService.canEditTravel(this.travel); } } diff --git a/src/app/pipes/available-places.pipe.ts b/src/app/pipes/available-places.pipe.ts new file mode 100644 index 0000000..7f31ab4 --- /dev/null +++ b/src/app/pipes/available-places.pipe.ts @@ -0,0 +1,9 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Travel } from '../entities/travel'; + +@Pipe({ name: 'availablePlaces' }) +export class AvailablePlacesPipe implements PipeTransform { + transform(travel: Travel): number { + return travel.places - travel.travelersInfo.length; + } +} diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts new file mode 100644 index 0000000..f1251ca --- /dev/null +++ b/src/app/services/auth.service.spec.ts @@ -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(); + }); +}); diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts new file mode 100644 index 0000000..be8d431 --- /dev/null +++ b/src/app/services/auth.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { Travel } from '../entities/travel'; +import { User } from '../entities/user'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + constructor(private oauthService: OAuthService) { } + + currentUser(): User { + const claims = this.oauthService.getIdentityClaims(); + return User.fromClaims(claims); + } + + canEditTravel(travel: Travel): boolean { + const user = this.currentUser(); + if (user == null) { + return false; + } + + return user.admin || (user.id === travel.driverInfo.id); + } +} diff --git a/src/app/views/list/list.component.css b/src/app/views/list/list.component.css index 4086ae4..5c596e2 100644 --- a/src/app/views/list/list.component.css +++ b/src/app/views/list/list.component.css @@ -4,19 +4,8 @@ margin: 0 auto; } -.description-cell { +.mat-cell { text-align: left; - margin: 10px auto; -} - -.duration-cell { - text-align: center; -} - -.duration-cell mat-icon { - display: inline-block; - vertical-align: middle; - font-size: 20px; } .spinner-container { @@ -26,8 +15,8 @@ } .travel-table { - min-height: 150px; - margin-top: 10px; + min-height: 100px; + margin: 10px; } .spinner-container mat-spinner { diff --git a/src/app/views/list/list.component.html b/src/app/views/list/list.component.html index 14f3609..fe97424 100644 --- a/src/app/views/list/list.component.html +++ b/src/app/views/list/list.component.html @@ -7,7 +7,7 @@ - Departure date @@ -32,7 +32,7 @@ Available places - {{ travel.places - travel.travelersInfo.length }} + {{ travel | availablePlaces }} diff --git a/src/styles.css b/src/styles.css index fc5bdef..c417663 100644 --- a/src/styles.css +++ b/src/styles.css @@ -4,5 +4,5 @@ html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .mat-card { - margin: 20px; + margin: 40px; } \ No newline at end of file