From b4b71efe2cd0eb85bbc770b8140cc924c5af3bf8 Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Thu, 12 Aug 2021 15:28:33 +0200 Subject: [PATCH] feat(deltagare): Added avrop-information to deltagare card. (TV-324) Squashed commit of the following: commit 7ed848709ff02565de4d7377e75dee804b2752ff Merge: 1dd9593 35213d6 Author: Erik Tiekstra Date: Thu Aug 12 13:24:28 2021 +0200 Merged develop and resolved conflict commit 1dd959342bd93110339db56035552c9560451b09 Author: Erik Tiekstra Date: Thu Aug 12 08:39:52 2021 +0200 Refactored custom error mapping commit a456ddc6a3df6851b9bb894c6bf1af9884cc4870 Author: Erik Tiekstra Date: Wed Aug 11 15:52:04 2021 +0200 Updates regarding fetching deltagare commit c320848ada6082ae06e8789d7b2569b269032a10 Author: Erik Tiekstra Date: Wed Aug 11 07:00:30 2021 +0200 Added some error-handling for deltagare service commit b5591427114f080083026babd9c60ae7e8eec4a3 Author: Erik Tiekstra Date: Tue Aug 10 11:30:40 2021 +0200 Added mock-data and request inside service. Also changed view with updated properties --- .../pages/deltagare/deltagare.component.html | 7 +- .../deltagare-card.component.html | 36 +++-- .../deltagare-card.component.ts | 13 +- .../models/api/deltagare.response.model.ts | 2 + .../src/app/shared/models/deltagare.model.ts | 23 ++- .../app/shared/models/error/custom-error.ts | 18 +-- .../shared/services/api/deltagare.service.ts | 140 +++++++++++++----- mock-api/dafa-web/scripts/avrop.js | 2 +- mock-api/dafa-web/server.js | 11 +- 9 files changed, 170 insertions(+), 82 deletions(-) diff --git a/apps/dafa-web/src/app/pages/deltagare/deltagare.component.html b/apps/dafa-web/src/app/pages/deltagare/deltagare.component.html index 65d7ebb..e1c73c4 100644 --- a/apps/dafa-web/src/app/pages/deltagare/deltagare.component.html +++ b/apps/dafa-web/src/app/pages/deltagare/deltagare.component.html @@ -7,7 +7,12 @@ information om deltagarna.

- Klicka för att gå till en test-deltagare från API:et +
  • {{deltagare.fullName}}
  • diff --git a/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html b/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html index 24f31b4..8f310bd 100644 --- a/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html +++ b/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html @@ -32,7 +32,7 @@
    Telefon:
    - +
    {{ phoneNumber.type }}: {{phoneNumber.number}}
    @@ -66,27 +66,33 @@

    Om tjänsten

    Tillhörande tjänst:
    -
    {{ deltagare.service }}
    +
    + {{ deltagare.avropInformation.tjanst }} +
    Datum för tjänstens början:
    -
    {{ deltagare.startDate | localDate }}
    +
    + +
    Datum för tjänstens slut:
    -
    {{ deltagare.endDate | localDate }}
    +
    + +
    Deltagandefrekvens:
    -
    {{ deltagare.service.frequency }}
    +
    + {{ deltagare.avropInformation.participationFrequency }} +
    Utförande verksamhet:
    -
    {{ deltagare.service.organisation }}
    +
    + {{ deltagare.avropInformation.utforandeVerksamhet }} +
    Utförande adress:
    -
    - {{ deltagare.service.organisation.adress }} +
    + {{ deltagare.avropInformation.utforandeAdress }}
    Genomförandereferens:
    -
    {{ deltagare.service.reference }}
    +
    + {{ deltagare.avropInformation.genomforandeReferens }} +
    diff --git a/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts b/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts index 733fb64..d318bfe 100644 --- a/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts +++ b/apps/dafa-web/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts @@ -5,7 +5,7 @@ import { Deltagare } from '@dafa-models/deltagare.model'; import { WorkExperience } from '@dafa-models/work-experience.model'; import { DeltagareService } from '@dafa-services/api/deltagare.service'; import { Observable } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; @Component({ selector: 'dafa-deltagare-card', @@ -14,12 +14,7 @@ import { map, switchMap } from 'rxjs/operators'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class DeltagareCardComponent { - private _deltagareId$: Observable = this.activatedRoute.params.pipe( - map(({ deltagareId }) => deltagareId as string) - ); - deltagare$: Observable = this._deltagareId$.pipe( - switchMap(deltagareId => this.deltagaresService.deltagare$(deltagareId)) - ); + deltagare$: Observable = this.deltagareService.deltagare$; firstVisibleWorkExperiences$: Observable = this.deltagare$.pipe( map(deltagare => deltagare.workExperiences.slice(0, 2)) ); @@ -30,7 +25,9 @@ export class DeltagareCardComponent { iconType = IconType; accordionExpanded = false; - constructor(private activatedRoute: ActivatedRoute, private deltagaresService: DeltagareService) {} + constructor(private activatedRoute: ActivatedRoute, private deltagareService: DeltagareService) { + this.deltagareService.setCurrentDeltagareId(this.activatedRoute.snapshot.params.deltagareId); + } toggleAccordionExpanded(): void { this.accordionExpanded = !this.accordionExpanded; diff --git a/apps/dafa-web/src/app/shared/models/api/deltagare.response.model.ts b/apps/dafa-web/src/app/shared/models/api/deltagare.response.model.ts index e0fbac3..9562663 100644 --- a/apps/dafa-web/src/app/shared/models/api/deltagare.response.model.ts +++ b/apps/dafa-web/src/app/shared/models/api/deltagare.response.model.ts @@ -1,3 +1,4 @@ +import { AvropResponse } from './avrop.response.model'; import { ContactInformationResponse } from './contact-information.response.model'; import { DisabilitiesResponse } from './disabilities.response.model'; import { DriversLicenseResponse } from './drivers-license.response.model'; @@ -23,4 +24,5 @@ export interface DeltagareResponse { workLanguages: WorkLanguagesResponse; disabilities: DisabilitiesResponse; workExperiences: WorkExperiencesResponse; + avropInformation: AvropResponse; } diff --git a/apps/dafa-web/src/app/shared/models/deltagare.model.ts b/apps/dafa-web/src/app/shared/models/deltagare.model.ts index cc73748..0a98e97 100644 --- a/apps/dafa-web/src/app/shared/models/deltagare.model.ts +++ b/apps/dafa-web/src/app/shared/models/deltagare.model.ts @@ -1,5 +1,7 @@ import { Address } from './address.model'; -import { DeltagareCompactResponse, DeltagareResponse } from './api/deltagare.response.model'; +import { AvropResponse } from './api/avrop.response.model'; +import { DeltagareResponse } from './api/deltagare.response.model'; +import { Avrop, mapAvropResponseToAvrop } from './avrop.model'; import { mapResponseToContactInformation } from './contact-information.model'; import { Disability, mapResponseToDisability } from './disability.model'; import { DriversLicense, mapResponseToDriversLicense } from './drivers-license.model'; @@ -11,10 +13,13 @@ import { mapResponseToWorkExperience, WorkExperience } from './work-experience.m export interface DeltagareCompact { id: string; fullName: string; - kommun?: string; + utforandeVerksamhet: string; + utforandeAdress: string; } -export interface Deltagare extends DeltagareCompact { +export interface Deltagare { + id: string; + fullName: string; firstName: string; lastName: string; ssn: string; @@ -28,14 +33,16 @@ export interface Deltagare extends DeltagareCompact { disabilities: Disability[]; workLanguages: string[]; workExperiences: WorkExperience[]; + avropInformation: Avrop; } -export function mapResponseToDeltagareCompact(data: DeltagareCompactResponse): DeltagareCompact { - const { sokandeId, deltagare, kommun } = data; +export function mapResponseToDeltagareCompact(data: AvropResponse): DeltagareCompact { + const { sokandeId, deltagare, adress, utforandeverksamhet } = data; return { - id: sokandeId, + id: sokandeId.toString(), fullName: deltagare, - kommun, + utforandeVerksamhet: utforandeverksamhet, + utforandeAdress: adress, }; } @@ -50,6 +57,7 @@ export function mapResponseToDeltagare(data: DeltagareResponse): Deltagare { workLanguages, disabilities, workExperiences, + avropInformation, } = data; return { @@ -65,5 +73,6 @@ export function mapResponseToDeltagare(data: DeltagareResponse): Deltagare { workExperiences: workExperiences && workExperiences.arbetslivserfarenheter.map(workExperience => mapResponseToWorkExperience(workExperience)), + avropInformation: avropInformation && mapAvropResponseToAvrop(avropInformation), }; } diff --git a/apps/dafa-web/src/app/shared/models/error/custom-error.ts b/apps/dafa-web/src/app/shared/models/error/custom-error.ts index 66a2f56..9abfb09 100644 --- a/apps/dafa-web/src/app/shared/models/error/custom-error.ts +++ b/apps/dafa-web/src/app/shared/models/error/custom-error.ts @@ -28,6 +28,9 @@ export class CustomError implements Error { if (!error) { return ''; } + if (typeof error === 'string') { + return error; + } if ('stack' in error) { return error.stack; @@ -39,18 +42,13 @@ export class CustomError implements Error { } static getErrorType(error: Error | (Error & { type: ErrorType })): ErrorType { - let type: ErrorType; - if ('type' in error) { - type = error.type; - } else { - type = ErrorType.UNKNOWN; + if (typeof error === 'object' && 'type' in error) { + return error.type; + } else if (error.name === 'HttpErrorResponse') { + return ErrorType.API; } - if (error.name === 'HttpErrorResponse') { - type = ErrorType.API; - } - - return type; + return ErrorType.UNKNOWN; } } diff --git a/apps/dafa-web/src/app/shared/services/api/deltagare.service.ts b/apps/dafa-web/src/app/shared/services/api/deltagare.service.ts index bb935f9..43d21d3 100644 --- a/apps/dafa-web/src/app/shared/services/api/deltagare.service.ts +++ b/apps/dafa-web/src/app/shared/services/api/deltagare.service.ts @@ -1,8 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { UnsubscribeDirective } from '@dafa-directives/unsubscribe.directive'; import { environment } from '@dafa-environment'; +import { AvropResponse } from '@dafa-models/api/avrop.response.model'; import { ContactInformationResponse } from '@dafa-models/api/contact-information.response.model'; -import { DeltagareCompactResponse } from '@dafa-models/api/deltagare.response.model'; import { DisabilityResponse } from '@dafa-models/api/disability.response.model'; import { DriversLicenseResponse } from '@dafa-models/api/drivers-license.response.model'; import { EducationsResponse } from '@dafa-models/api/educations.response.model'; @@ -10,55 +11,90 @@ import { HighestEducationResponse } from '@dafa-models/api/highest-education.res import { TranslatorResponse } from '@dafa-models/api/translator.response.model'; import { WorkExperiencesResponse } from '@dafa-models/api/work-experiences.response.model'; import { WorkLanguagesResponse } from '@dafa-models/api/work-languages.response.model'; +import { Avrop, mapAvropResponseToAvrop } from '@dafa-models/avrop.model'; import { ContactInformation, mapResponseToContactInformation } from '@dafa-models/contact-information.model'; import { Deltagare, DeltagareCompact, mapResponseToDeltagareCompact } from '@dafa-models/deltagare.model'; import { Disability, mapResponseToDisability } from '@dafa-models/disability.model'; import { DriversLicense, mapResponseToDriversLicense } from '@dafa-models/drivers-license.model'; import { Education, mapResponseToEducation } from '@dafa-models/education.model'; +import { errorToCustomError } from '@dafa-models/error/custom-error'; import { HighestEducation, mapResponseToHighestEducation } from '@dafa-models/highest-education.model'; import { mapResponseToWorkExperience, WorkExperience } from '@dafa-models/work-experience.model'; +import { ErrorService } from '@dafa-services/error.service'; import { sortFromToDates } from '@dafa-utils/sort.util'; -import { combineLatest, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; +import { catchError, filter, map, switchMap } from 'rxjs/operators'; const API_HEADERS = { headers: environment.api.headers }; @Injectable({ providedIn: 'root', }) -export class DeltagareService { +export class DeltagareService extends UnsubscribeDirective { private _apiBaseUrl = `${environment.api.url}/customerinfo`; private _apiAvropUrl = `${environment.api.url}/avrop`; + private _currentDeltagareId$ = new BehaviorSubject(null); - // private _fetchAllDeltagare: Observable = this.httpClient - // .get<{ data: DeltagareCompactResponse[] }>(`${this._apiAvropUrl}`, { ...API_HEADERS }) - // .pipe(map(response => response.data)); + constructor(private httpClient: HttpClient, private errorService: ErrorService) { + super(); + this.unsubscribeOnDestroy( + this._currentDeltagareId$ + .pipe( + filter(currentDeltagareId => !!currentDeltagareId), + switchMap(currentDeltagareId => this.fetchDeltagare$(currentDeltagareId)) + ) + .subscribe(deltagare => { + this._deltagare$.next(deltagare); + }) + ); + } - private _fetchAllDeltagare: Observable = this.httpClient.get( - `${this._apiAvropUrl}`, - { ...API_HEADERS } - ); + private _deltagare$ = new BehaviorSubject(null); + public deltagare$: Observable = this._deltagare$.asObservable(); - public allDeltagare$: Observable = this._fetchAllDeltagare.pipe( - map(data => data.map(deltagare => mapResponseToDeltagareCompact(deltagare))) - ); + public allDeltagare$: Observable = this.httpClient + .get<{ data: AvropResponse[] }>(`${this._apiAvropUrl}`, { ...API_HEADERS }) + .pipe(map(response => response.data.map(deltagare => mapResponseToDeltagareCompact(deltagare)))); - private _fetchContactInformation$(id: string): Observable { + public setCurrentDeltagareId(currentDeltagareId: string): void { + this._deltagare$.next(null); + this._currentDeltagareId$.next(currentDeltagareId); + } + + private _fetchContactInformation$(id: string): Observable> { return this.httpClient .get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/contact/${id}`, { ...API_HEADERS }) - .pipe(map(response => mapResponseToContactInformation(response.data))); + .pipe( + map(response => mapResponseToContactInformation(response.data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); } - private _fetchDriversLicense$(id: string): Observable { + private _fetchDriversLicense$(id: string): Observable> { return this.httpClient .get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/driverlicense/${id}`, { ...API_HEADERS }) - .pipe(map(response => mapResponseToDriversLicense(response.data))); + .pipe( + map(response => mapResponseToDriversLicense(response.data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); } - private _fetchHighestEducation$(id: string): Observable { + private _fetchHighestEducation$(id: string): Observable> { return this.httpClient .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/education/highest/${id}`, { ...API_HEADERS }) - .pipe(map(response => mapResponseToHighestEducation(response.data))); + .pipe( + map(response => mapResponseToHighestEducation(response.data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); } private _fetchEducations$(id: string): Observable { @@ -73,20 +109,38 @@ export class DeltagareService { } return []; }), - map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))) + map( + educations => educations.map(utbildning => mapResponseToEducation(utbildning)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ) ); } private _fetchTranslator$(id: string): Observable { return this.httpClient .get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/translator/${id}`, { ...API_HEADERS }) - .pipe(map(response => (response.data.sprak ? response.data.sprak.beskrivning : null))); + .pipe( + map(response => (response.data.sprak ? response.data.sprak.beskrivning : null)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of(''); + }) + ); } private _fetchWorkLanguages$(id: string): Observable { return this.httpClient .get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/work/languages/${id}`, { ...API_HEADERS }) - .pipe(map(response => (response.data.sprak ? response.data.sprak.map(sprak => sprak.beskrivning) : []))); + .pipe( + map(response => (response.data.sprak ? response.data.sprak.map(sprak => sprak.beskrivning) : [])), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ); } private _fetchDisabilities$(id: string): Observable { @@ -97,7 +151,11 @@ export class DeltagareService { response.data.length ? response.data.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) : [] - ) + ), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) ); } @@ -113,23 +171,31 @@ export class DeltagareService { } return []; }), - map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))) + map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) ); } - public deltagareCompact$(id: string): Observable { - return this._fetchContactInformation$(id).pipe( - map(contactInformation => ({ - id, - fullName: contactInformation.fullName, - })) - ); + private _fetchAvropInformation$(id: string): Observable> { + return this.httpClient + .get<{ data: AvropResponse }>(`${this._apiAvropUrl}/${id}`, { ...API_HEADERS }) + .pipe( + filter(response => !!response.data), + map(response => mapAvropResponseToAvrop(response.data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); } // As TypeScript has some limitations regarding combining Observables this way, // we need to type it manually when exceeding 6 Observables inside a combineLatest. // Read: https://github.com/ReactiveX/rxjs/issues/3601#issuecomment-384711601 - public deltagare$(id: string): Observable { + public fetchDeltagare$(id: string): Observable { return combineLatest([ this._fetchContactInformation$(id), this._fetchDriversLicense$(id), @@ -139,6 +205,7 @@ export class DeltagareService { this._fetchWorkLanguages$(id), this._fetchDisabilities$(id), this._fetchWorkExperiences$(id), + this._fetchAvropInformation$(id), ]).pipe( map( ([ @@ -150,6 +217,7 @@ export class DeltagareService { workLanguages, disabilities, workExperiences, + avropInformation, ]: [ ContactInformation, DriversLicense, @@ -158,7 +226,8 @@ export class DeltagareService { string, string[], Disability[], - WorkExperience[] + WorkExperience[], + Avrop ]) => ({ id, ...contactInformation, @@ -169,10 +238,9 @@ export class DeltagareService { workLanguages, disabilities, workExperiences, + avropInformation, }) ) ); } - - constructor(private httpClient: HttpClient) {} } diff --git a/mock-api/dafa-web/scripts/avrop.js b/mock-api/dafa-web/scripts/avrop.js index 76d3e56..948c1e9 100644 --- a/mock-api/dafa-web/scripts/avrop.js +++ b/mock-api/dafa-web/scripts/avrop.js @@ -25,7 +25,7 @@ function generateAvrop(amount = 10, deltagare) { avrop.push({ id: faker.datatype.uuid(), - deltagare: currentDeltagare.fullName, + deltagare: `${currentDeltagare.contact.fornamn} ${currentDeltagare.contact.efternamn}`, genomforandeReferens: faker.datatype.number({ min: 100000000, max: 999999999 }), orgId: faker.datatype.uuid(), leverantorId: faker.datatype.number({ min: 1000, max: 99999 }), diff --git a/mock-api/dafa-web/server.js b/mock-api/dafa-web/server.js index c8ce794..f8dd81b 100644 --- a/mock-api/dafa-web/server.js +++ b/mock-api/dafa-web/server.js @@ -21,6 +21,7 @@ server.use( '/avrop/tjanster*': '/tjanster$1', '/avrop/utforandeverksamheter*': '/organizations$1', '/avrop/kommuner*': '/kommuner$1', + '/avrop/:sokandeId': '/avrop?sokandeId=:sokandeId', '*page=*': '$1_page=$2', '*limit=*': '$1_limit=$2', '*sort=*': '$1_sort=$2', @@ -57,7 +58,7 @@ router.render = (req, res) => { let data = res.locals.data; const deltagareRegex = /(?:\/customerinfo\/)(contact|driverlicense|education\/highest|education|translator|work\/disability|work\/languages|work\/experience)/g; const isDeltagarePath = deltagareRegex.exec(pathname); - const avropRegex = /(?:\/avrop\/)(tjanster|utforandeverksamheter|kommuner)/g; + const avropRegex = /(?:\/avrop\/)(tjanster|utforandeverksamheter|kommuner|\d)/g; const isAvropPath = avropRegex.exec(pathname); if (isDeltagarePath) { @@ -75,6 +76,8 @@ router.render = (req, res) => { }); data = newData.filter((value, index, arr) => arr.findIndex(item => item.code === value.code) === index); + } else if (isAvropPath[1]) { + data = data[0]; } } @@ -91,9 +94,9 @@ server.listen(8000, () => { }); function appendMetaData(params, res) { - if (params && params.has('_page')) { - const limit = +params.get('_limit'); - const page = +params.get('_page'); + if (params && params.has('page')) { + const limit = +params.get('limit'); + const page = +params.get('page'); const count = res.get('X-Total-Count'); const totalPages = Math.ceil(count / limit); return {