feat(felhantering): Ändrat felhantering och loggning. (TV-945)
Merge in TEA/mina-sidor-fa-web from feature/TV-945-felhantering to develop Squashed commit of the following: commit b621bd7d9dd0a03a22476f196521f2535731fa12 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 16:01:06 2021 +0100 Added better error-handling to employee commit 876ed3caf6ff1ffb98bb16491526e4417086cba9 Merge: 02607a5fec63435fAuthor: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 15:24:31 2021 +0100 Merge branch 'develop' into feature/TV-945-felhantering commit 02607a5f007dc7e46d61460fc71a1b27bdda9392 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 08:25:49 2021 +0100 Added better error-handling to deltagare händelser commit 30c2726ccebc73a2ca9a0c72cdc564cad2ac82aa Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 08:17:22 2021 +0100 Updated deltagare error handling with data commit 893de8478e5a2919c684667eb31afd35986cb396 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 08:05:50 2021 +0100 Added better error-handling to avvikelse commit 5c64b8c10a7f3fb2cec5cab2c8d86073169a6033 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 07:47:59 2021 +0100 Added better error-handling to authentication commit 8fa187d4da0b75d2bb62bc16cdcf540064bd4433 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Dec 3 07:47:43 2021 +0100 Added better error-handling to avrop commit 3bd23e6ad642e95caa5bd88215442281495f970c Merge: f941d144938014abAuthor: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Dec 2 13:02:08 2021 +0100 Merge branch 'develop' into feature/TV-945-felhantering commit f941d14435e1ed3e371cee84ef85d508ed70b2ce Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Dec 1 16:08:00 2021 +0100 Added improved error-handling to deltagare-api.service commit 3889b398d9ce0e5e1b6498e10794a946b65c2a47 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Dec 1 15:45:36 2021 +0100 Added better error-handling connected to APM
This commit is contained in:
@@ -19,6 +19,7 @@ const providers: Provider[] = [
|
|||||||
ApmErrorHandler,
|
ApmErrorHandler,
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||||
{ provide: LOCALE_ID, useValue: 'sv-SE' },
|
{ provide: LOCALE_ID, useValue: 'sv-SE' },
|
||||||
|
{ provide: ErrorHandler, useClass: CustomErrorHandler },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Skip error handler in Dev until "Uncaught Error: ApplicationRef.tick is called recursively" is fixed
|
// Skip error handler in Dev until "Uncaught Error: ApplicationRef.tick is called recursively" is fixed
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<ng-container *ngIf="errors.length">
|
<ng-container *ngIf="errors.length">
|
||||||
<h3>{{ errors.length }} fel har uppstått!</h3>
|
<h3>{{ errors.length }} fel har uppstått!</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let error of errors">{{ error.name }}: {{ error.message }}</li>
|
<li *ngFor="let error of errors">{{ error.type }}: {{ error.message }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
<button class="toast__close-button" aria-label="Stäng meddelandet" (click)="emitCloseEvent()">
|
<button class="toast__close-button" aria-label="Stäng meddelandet" (click)="emitCloseEvent()">
|
||||||
<ui-icon [uiType]="UiIconType.X" [uiSize]="UiIconSize.L"></ui-icon>
|
<ui-icon [uiType]="UiIconType.X" [uiSize]="UiIconSize.L"></ui-icon>
|
||||||
</button>
|
</button>
|
||||||
<h3 class="toast__heading">{{ error.name }}</h3>
|
<h3 class="toast__heading">{{ error.type }}</h3>
|
||||||
<p class="toast__message">{{ error.message }}</p>
|
<p class="toast__message">{{ error.message }}</p>
|
||||||
|
<span class="toast__error-id">Error id: {{error.id}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,4 +62,10 @@
|
|||||||
padding: var(--digi--layout--gutter--s);
|
padding: var(--digi--layout--gutter--s);
|
||||||
color: var(--digi--typography--color--text);
|
color: var(--digi--typography--color--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__error-id {
|
||||||
|
align-self: flex-end;
|
||||||
|
font-size: var(--digi--typography--font-size--s);
|
||||||
|
margin-top: var(--digi--layout--gutter--s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { ErrorSeverity } from '@msfa-enums/error-severity.enum';
|
import { ErrorSeverity } from '@msfa-enums/error-severity.enum';
|
||||||
import { UiIconType } from '@ui/icon/icon-type.enum';
|
|
||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { UiIconSize } from '@ui/icon/icon-size.enum';
|
import { UiIconSize } from '@ui/icon/icon-size.enum';
|
||||||
|
import { UiIconType } from '@ui/icon/icon-type.enum';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-toast',
|
selector: 'msfa-toast',
|
||||||
@@ -19,11 +19,11 @@ export class ToastComponent implements AfterViewInit {
|
|||||||
ErrorSeverity = ErrorSeverity;
|
ErrorSeverity = ErrorSeverity;
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
if (this.error.removeAfter) {
|
// if (this.error.removeAfter) {
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
this.closeToast.emit(this.error);
|
// this.closeToast.emit(this.error);
|
||||||
}, this.error.removeAfter);
|
// }, this.error.removeAfter);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
get className(): string {
|
get className(): string {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { environment } from '@msfa-environment';
|
|||||||
export class LoggingModule {
|
export class LoggingModule {
|
||||||
private _elasticConfig = environment.elastic;
|
private _elasticConfig = environment.elastic;
|
||||||
private _activeFeatures = environment.activeFeatures;
|
private _activeFeatures = environment.activeFeatures;
|
||||||
|
private _version = environment.version;
|
||||||
|
|
||||||
constructor(private apmService: ApmService) {
|
constructor(private apmService: ApmService) {
|
||||||
if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) {
|
if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) {
|
||||||
@@ -18,6 +19,7 @@ export class LoggingModule {
|
|||||||
serviceName,
|
serviceName,
|
||||||
serverUrl,
|
serverUrl,
|
||||||
environment: this.currentEnvironment,
|
environment: this.currentEnvironment,
|
||||||
|
serviceVersion: this._version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,9 @@ export class EmployeeFormComponent implements OnInit {
|
|||||||
next: () => {
|
next: () => {
|
||||||
void this.router.navigateByUrl(`/administration/personal/${this.employeeId}`);
|
void this.router.navigateByUrl(`/administration/personal/${this.employeeId}`);
|
||||||
},
|
},
|
||||||
error: error => {
|
error: (error: CustomError) => {
|
||||||
this._errorWhileUpdating$.next(error);
|
this._errorWhileUpdating$.next(error);
|
||||||
|
throw error;
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
updateEmployeeSubscription.unsubscribe();
|
updateEmployeeSubscription.unsubscribe();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';
|
|||||||
import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model';
|
import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model';
|
||||||
import { Handledare } from '@msfa-models/handledare.model';
|
import { Handledare } from '@msfa-models/handledare.model';
|
||||||
import { AvropService } from '@msfa-services/avrop.service';
|
import { AvropService } from '@msfa-services/avrop.service';
|
||||||
import { Observable, BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-avrop',
|
selector: 'msfa-avrop',
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ export class AvvikelseReportFormService {
|
|||||||
.fetchAvvikelseQuestions$()
|
.fetchAvvikelseQuestions$()
|
||||||
.pipe(shareReplay(1));
|
.pipe(shareReplay(1));
|
||||||
|
|
||||||
fetchAvvikelseReasons$: Observable<AvvikelseReason[]> = this.avvikelseApiService
|
fetchAvvikelseReasons$: Observable<AvvikelseReason[]> = this.avvikelseApiService.fetchAvvikelseReasons$().pipe(
|
||||||
.fetchAvvikelseReasons$()
|
map(reasons => sortAvvikelseReasons(reasons)),
|
||||||
.pipe(map(reasons => sortAvvikelseReasons(reasons)));
|
shareReplay(1)
|
||||||
|
);
|
||||||
|
|
||||||
constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {}
|
constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import { ErrorHandler, Injectable } from '@angular/core';
|
import { ErrorHandler, Injectable } from '@angular/core';
|
||||||
import { ApmErrorHandler } from '@elastic/apm-rum-angular';
|
import { ApmErrorHandler, ApmService } from '@elastic/apm-rum-angular';
|
||||||
import { Feature } from '@msfa-enums/feature.enum';
|
import { Feature } from '@msfa-enums/feature.enum';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { ErrorService } from '@msfa-services/error.service';
|
import { ErrorService } from '@msfa-services/error.service';
|
||||||
|
|
||||||
|
interface ApmError {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
type: string;
|
||||||
|
method: string;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CustomErrorHandler implements ErrorHandler {
|
export class CustomErrorHandler implements ErrorHandler {
|
||||||
private _elasticConfig = environment.elastic;
|
private _elasticConfig = environment.elastic;
|
||||||
private _activeFeatures = environment.activeFeatures;
|
private _activeFeatures = environment.activeFeatures;
|
||||||
constructor(private errorService: ErrorService, public apmErrorHandler: ApmErrorHandler) {}
|
constructor(
|
||||||
|
private errorService: ErrorService,
|
||||||
|
public apmErrorHandler: ApmErrorHandler,
|
||||||
|
private apmService: ApmService
|
||||||
|
) {}
|
||||||
|
|
||||||
handleError(customError: CustomError & { ngDebugContext: unknown }): void {
|
handleError(customError: CustomError & { ngDebugContext: unknown }): void {
|
||||||
if (!customError.avoidToast) {
|
if (!customError.avoidToast) {
|
||||||
@@ -17,7 +29,11 @@ export class CustomErrorHandler implements ErrorHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) {
|
if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) {
|
||||||
this.apmErrorHandler.handleError(customError);
|
const { id, method, name, type, message, timestamp, data } = customError;
|
||||||
|
const apmError: ApmError = { id, method, name, type, message, timestamp };
|
||||||
|
|
||||||
|
this.apmService.apm.addLabels({ id, method, data });
|
||||||
|
this.apmService.apm.captureError(apmError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
export interface Authorization {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthorizationApiResponse {
|
|
||||||
data: AuthorizationApiResponseData[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthorizationApiResponseData {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mapAuthorizationApiResponseToAuthorization(data: AuthorizationApiResponseData): Authorization {
|
|
||||||
const { id, name } = data;
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { ErrorSeverity } from '@msfa-enums/error-severity.enum';
|
import { ErrorSeverity } from '@msfa-enums/error-severity.enum';
|
||||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||||
|
|
||||||
@@ -7,15 +8,20 @@ export class CustomError implements Error {
|
|||||||
message: string;
|
message: string;
|
||||||
stack: string;
|
stack: string;
|
||||||
type: ErrorType;
|
type: ErrorType;
|
||||||
|
data: string;
|
||||||
|
method: string;
|
||||||
severity: ErrorSeverity;
|
severity: ErrorSeverity;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
error: Error;
|
error: Error | HttpErrorResponse;
|
||||||
removeAfter: number;
|
removeAfter: number;
|
||||||
avoidToast?: boolean;
|
avoidToast?: boolean;
|
||||||
|
|
||||||
constructor(args: {
|
constructor(args: {
|
||||||
error: Error;
|
error: Error | HttpErrorResponse;
|
||||||
type?: ErrorType;
|
type?: ErrorType;
|
||||||
|
name?: string;
|
||||||
|
data?: unknown;
|
||||||
|
method?: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
severity?: ErrorSeverity;
|
severity?: ErrorSeverity;
|
||||||
stack?: string;
|
stack?: string;
|
||||||
@@ -23,7 +29,10 @@ export class CustomError implements Error {
|
|||||||
}) {
|
}) {
|
||||||
this.timestamp = new Date();
|
this.timestamp = new Date();
|
||||||
this.id = this.timestamp.getTime().toString();
|
this.id = this.timestamp.getTime().toString();
|
||||||
this.type = this.name = args.type || CustomError.getErrorType(args.error);
|
this.type = args.type || CustomError.getErrorType(args.error);
|
||||||
|
this.name = args.name || args.error?.name || this.type;
|
||||||
|
this.method = args.method || '';
|
||||||
|
this.data = JSON.stringify(args.data);
|
||||||
this.message = args.message || args.error.message;
|
this.message = args.message || args.error.message;
|
||||||
this.severity = args.severity || ErrorSeverity.HIGH;
|
this.severity = args.severity || ErrorSeverity.HIGH;
|
||||||
this.stack = args.stack || CustomError.getStack(args.error);
|
this.stack = args.stack || CustomError.getStack(args.error);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model';
|
import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model';
|
||||||
import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.model';
|
import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { add, isAfter, isBefore, sub } from 'date-fns';
|
import { add, isAfter, isBefore, sub } from 'date-fns';
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
|
||||||
@@ -103,12 +104,22 @@ export class AuthenticationService {
|
|||||||
login$(authorizationCodeFromCiam: string): Observable<Authentication> {
|
login$(authorizationCodeFromCiam: string): Observable<Authentication> {
|
||||||
this.removeLocalStorageData();
|
this.removeLocalStorageData();
|
||||||
|
|
||||||
|
const apiUrl = AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam);
|
||||||
|
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<{ data: AuthenticationResponse }>(AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam))
|
.get<{ data: AuthenticationResponse }>(AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam))
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ data }) => mapAuthApiResponseToAuthenticationResult(data)),
|
map(({ data }) => mapAuthApiResponseToAuthenticationResult(data)),
|
||||||
tap(authenticationResult => {
|
tap(authenticationResult => {
|
||||||
this._setSession(authenticationResult);
|
this._setSession(authenticationResult);
|
||||||
|
}),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte logga in.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AuthenticationService.login$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { environment } from '@msfa-environment';
|
|
||||||
import {
|
|
||||||
Authorization,
|
|
||||||
AuthorizationApiResponse,
|
|
||||||
mapAuthorizationApiResponseToAuthorization,
|
|
||||||
} from '@msfa-models/authorization.model';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class AuthorizationService {
|
|
||||||
private _apiBaseUrl = `${environment.api.url}/authorizations`;
|
|
||||||
public authorizations$: Observable<Authorization[]> = this.httpClient
|
|
||||||
.get<AuthorizationApiResponse>(this._apiBaseUrl)
|
|
||||||
.pipe(map(({ data }) => data.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization))));
|
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) {}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import { AvropAndMetaResponse } from '@msfa-models/api/avrop.response.model';
|
|||||||
import { Params } from '@msfa-models/api/params.model';
|
import { Params } from '@msfa-models/api/params.model';
|
||||||
import { AvropFilter, mapResponseToAvropFilter } from '@msfa-models/avrop-filter.model';
|
import { AvropFilter, mapResponseToAvropFilter } from '@msfa-models/avrop-filter.model';
|
||||||
import { Avrop, AvropAndMeta, mapResponseToAvrop } from '@msfa-models/avrop.model';
|
import { Avrop, AvropAndMeta, mapResponseToAvrop } from '@msfa-models/avrop.model';
|
||||||
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
import { catchError, filter, map } from 'rxjs/operators';
|
import { catchError, filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export class AvropApiService {
|
|||||||
|
|
||||||
fetchAvrop$(params: Params): Observable<AvropAndMeta | null> {
|
fetchAvrop$(params: Params): Observable<AvropAndMeta | null> {
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<AvropAndMetaResponse>(`${this._apiBaseUrl}`, { params })
|
.get<AvropAndMetaResponse>(this._apiBaseUrl, { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ data, meta }) => ({ data: data.map(avrop => mapResponseToAvrop(avrop)), meta })),
|
map(({ data, meta }) => ({ data: data.map(avrop => mapResponseToAvrop(avrop)), meta })),
|
||||||
catchError((error: Error & { status: number }) => {
|
catchError((error: Error & { status: number }) => {
|
||||||
@@ -35,48 +35,68 @@ export class AvropApiService {
|
|||||||
this._showUnauthorizedError$.next(true);
|
this._showUnauthorizedError$.next(true);
|
||||||
return of(null as null);
|
return of(null as null);
|
||||||
} else {
|
} else {
|
||||||
throw new CustomError(
|
throw new CustomError({
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta nya deltagare.\n\n${error.message}` })
|
error,
|
||||||
);
|
message: `Kunde inte hämta nya deltagare.\n\n${error.message}`,
|
||||||
|
name: `GET ${this._apiBaseUrl}`,
|
||||||
|
method: 'AvropApiService.fetchAvrop$',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAvailableTjanster$(params: Params): Observable<AvropFilter[]> {
|
fetchAvailableTjanster$(params: Params): Observable<AvropFilter[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/tjanster`;
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/tjanster`, { params })
|
.get<{ data: AvropFilterResponse[] }>(apiUrl, { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(response => !!response?.data),
|
filter(response => !!response?.data),
|
||||||
map(({ data }) => data.map(tjanster => mapResponseToAvropFilter(tjanster)))
|
map(({ data }) => data.map(tjanster => mapResponseToAvropFilter(tjanster))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta tjänster.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AvropApiService.fetchAvailableTjanster$',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAvailableUtforandeVerksamheter$(params: Params): Observable<AvropFilter[]> {
|
fetchAvailableUtforandeVerksamheter$(params: Params): Observable<AvropFilter[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/utforandeverksamheter`;
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/utforandeverksamheter`, { params })
|
.get<{ data: AvropFilterResponse[] }>(apiUrl, { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(response => !!response?.data),
|
filter(response => !!response?.data),
|
||||||
map(({ data }) => data.map(utforandeverksamheter => mapResponseToAvropFilter(utforandeverksamheter)))
|
map(({ data }) => data.map(utforandeverksamheter => mapResponseToAvropFilter(utforandeverksamheter))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta utförande verksamheter.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AvropApiService.fetchAvailableUtforandeVerksamheter$',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAvailableKommuner$(params: Params): Observable<AvropFilter[]> {
|
fetchAvailableKommuner$(params: Params): Observable<AvropFilter[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/kommuner`;
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/kommuner`, { params })
|
.get<{ data: AvropFilterResponse[] }>(apiUrl, { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(response => !!response?.data),
|
filter(response => !!response?.data),
|
||||||
map(({ data }) => data.map(kommun => mapResponseToAvropFilter(kommun)))
|
map(({ data }) => data.map(kommun => mapResponseToAvropFilter(kommun))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta kommuner.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AvropApiService.fetchAvailableKommuner$',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async assignHandledare(avrop: Avrop[], handledareId: string): Promise<void> {
|
|
||||||
const params: Params = {
|
|
||||||
avropIds: avrop.map(deltagare => deltagare.id),
|
|
||||||
ciamUserId: handledareId,
|
|
||||||
};
|
|
||||||
return this.httpClient
|
|
||||||
.patch<void>(`${this._apiBaseUrl}/handledare/assign`, null, { params })
|
|
||||||
.toPromise();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { AvvikelseQuestionsResponse } from '@msfa-models/api/avvikelse-question.response.model';
|
import { AvvikelseQuestionsResponse } from '@msfa-models/api/avvikelse-question.response.model';
|
||||||
import { AvvikelseReasonResponse } from '@msfa-models/api/avvikelse-reason.response.model';
|
import { AvvikelseReasonResponse } from '@msfa-models/api/avvikelse-reason.response.model';
|
||||||
@@ -20,26 +19,47 @@ export class AvvikelseApiService {
|
|||||||
constructor(private httpClient: HttpClient) {}
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
public fetchAvvikelseReasons$(): Observable<AvvikelseReason[]> {
|
public fetchAvvikelseReasons$(): Observable<AvvikelseReason[]> {
|
||||||
return this.httpClient.get<{ data: AvvikelseReasonResponse[] }>(`${this._apiBaseUrl}/orsakskoderavvikelse`).pipe(
|
const apiUrl = `${this._apiBaseUrl}/orsakskoderavvikelse`;
|
||||||
|
return this.httpClient.get<{ data: AvvikelseReasonResponse[] }>(apiUrl).pipe(
|
||||||
filter(response => !!response?.data),
|
filter(response => !!response?.data),
|
||||||
map(({ data }) => data.map(avvikelse => mapResponseToAvvikelseReason(avvikelse)))
|
map(({ data }) => data.map(avvikelse => mapResponseToAvvikelseReason(avvikelse))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta orsaker till avvikelse.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AvvikelseApiService.fetchAvvikelseReasons$',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchAvvikelseQuestions$(): Observable<AvvikelseQuestion[]> {
|
public fetchAvvikelseQuestions$(): Observable<AvvikelseQuestion[]> {
|
||||||
return this.httpClient.get<{ data: AvvikelseQuestionsResponse[] }>(`${this._apiBaseUrl}/fragorforavvikelser`).pipe(
|
const apiUrl = `${this._apiBaseUrl}/fragorforavvikelser`;
|
||||||
|
return this.httpClient.get<{ data: AvvikelseQuestionsResponse[] }>(apiUrl).pipe(
|
||||||
filter(response => !!response?.data),
|
filter(response => !!response?.data),
|
||||||
map(({ data }) => data.map(fraga => mapResponseToAvvikelseQuestion(fraga)))
|
map(({ data }) => data.map(fraga => mapResponseToAvvikelseQuestion(fraga))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta avvikelse frågor.\n\n${error.message}`,
|
||||||
|
name: `GET ${apiUrl}`,
|
||||||
|
method: 'AvvikelseApiService.fetchAvvikelseQuestions$',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createAvvikelse$(avvikelse: AvvikelseReportRequest): Observable<unknown> {
|
public createAvvikelse$(avvikelse: AvvikelseReportRequest): Observable<unknown> {
|
||||||
return this.httpClient.post<void>(`${this._apiBaseUrl}/avvikelse`, avvikelse).pipe(
|
const apiUrl = `${this._apiBaseUrl}/avvikelse`;
|
||||||
|
return this.httpClient.post<void>(apiUrl, avvikelse).pipe(
|
||||||
catchError((error: Error) => {
|
catchError((error: Error) => {
|
||||||
throw new CustomError({
|
throw new CustomError({
|
||||||
error,
|
error,
|
||||||
message: `Kunde inte spara Avvikelserapport (avvikelse).\n\n${error.message}`,
|
message: `Kunde inte spara Avvikelserapport (avvikelse).\n\n${error.message}`,
|
||||||
type: ErrorType.API,
|
name: `POST ${apiUrl}`,
|
||||||
|
data: avvikelse,
|
||||||
|
method: 'AvvikelseApiService.createAvvikelse$',
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import {
|
|||||||
DeltagareHandelserApiResponse,
|
DeltagareHandelserApiResponse,
|
||||||
mapDeltagareHandelseApiResponse,
|
mapDeltagareHandelseApiResponse,
|
||||||
} from '@msfa-models/deltagare-handelse.model';
|
} from '@msfa-models/deltagare-handelse.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
import { replaceGenomforandereferensFromUrl } from '@msfa-shared/utils/replace-genomforandereferens-from-url.util';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { catchError, filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -19,26 +21,31 @@ export class DeltagareHandelserApiService {
|
|||||||
constructor(private httpClient: HttpClient) {}
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
fetchDeltagareHandelser$(
|
fetchDeltagareHandelser$(
|
||||||
genomforandeReferens: number,
|
genomforandereferens: number,
|
||||||
handelserParams: PaginationParams
|
handelserParams: PaginationParams
|
||||||
): Observable<DeltagareHandelseData> {
|
): Observable<DeltagareHandelseData> {
|
||||||
if (!genomforandeReferens) {
|
if (!genomforandereferens) {
|
||||||
throw new Error('Genomförandereferens kunde inte hittas.');
|
throw new Error('Genomförandereferens kunde inte hittas.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const params: Params = { page: handelserParams.page.toString() };
|
const params: Params = { page: handelserParams.page.toString() };
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/deltagare/${genomforandereferens}/handelser`;
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.get<DeltagareHandelserApiResponse>(`${this._apiBaseUrl}/deltagare/${genomforandeReferens}/handelser`, { params })
|
.get<DeltagareHandelserApiResponse>(apiUrl, { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ data, meta }) => {
|
filter(({ data }) => !!data),
|
||||||
if (data) {
|
map(({ data, meta }) => ({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
data: data.map(genomforandeHandelse => mapDeltagareHandelseApiResponse(genomforandeHandelse)),
|
||||||
return {
|
meta,
|
||||||
data: data.map(genomforandeHandelse => mapDeltagareHandelseApiResponse(genomforandeHandelse)),
|
})),
|
||||||
meta,
|
catchError((error: Error) => {
|
||||||
};
|
throw new CustomError({
|
||||||
}
|
error,
|
||||||
|
message: `Kunde inte hämta händelser.\n\n${error.message}`,
|
||||||
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareHandelserApiService.fetchDeltagareHandelser$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ import { DeltagareCompactData, mapResponseToDeltagareCompact } from '@msfa-model
|
|||||||
import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
|
import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
|
||||||
import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
|
import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
|
||||||
import { Education, mapResponseToEducation } from '@msfa-models/education.model';
|
import { Education, mapResponseToEducation } from '@msfa-models/education.model';
|
||||||
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
|
import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
|
||||||
import { mapResponseToReport, ReportsData } from '@msfa-models/report.model';
|
import { mapResponseToReport, ReportsData } from '@msfa-models/report.model';
|
||||||
import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
|
import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
|
||||||
|
import { replaceGenomforandereferensFromUrl } from '@msfa-shared/utils/replace-genomforandereferens-from-url.util';
|
||||||
import { sortFromToDates } from '@msfa-utils/sort.util';
|
import { sortFromToDates } from '@msfa-utils/sort.util';
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
@@ -69,18 +70,21 @@ export class DeltagareApiService {
|
|||||||
this._showUnauthorizedError$.next(true);
|
this._showUnauthorizedError$.next(true);
|
||||||
return of(null as null);
|
return of(null as null);
|
||||||
} else {
|
} else {
|
||||||
throw new CustomError(
|
throw new CustomError({
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta deltagare.\n\n${error.message}` })
|
error,
|
||||||
);
|
message: `Kunde inte hämta deltagare.\n\n${error.message}`,
|
||||||
|
name: `GET ${this._apiBaseUrl}`,
|
||||||
|
method: 'DeltagareApiService.fetchAllDeltagare$',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchReports$(genomforandeReferens: number, paginationParams: PaginationParams): Observable<ReportsData> {
|
public fetchReports$(genomforandereferens: number, paginationParams: PaginationParams): Observable<ReportsData> {
|
||||||
const { page, limit } = paginationParams;
|
const { page, limit } = paginationParams;
|
||||||
const params: { [param: string]: string | string[] } = {
|
const params: { [param: string]: string | string[] } = {
|
||||||
genomforandeReferens: genomforandeReferens.toString(),
|
genomforandeReferens: genomforandereferens.toString(),
|
||||||
page: page.toString(),
|
page: page.toString(),
|
||||||
limit: limit.toString(),
|
limit: limit.toString(),
|
||||||
};
|
};
|
||||||
@@ -90,143 +94,170 @@ export class DeltagareApiService {
|
|||||||
.pipe(
|
.pipe(
|
||||||
map(({ data, meta }) => ({ data: data.map(report => mapResponseToReport(report)), meta })),
|
map(({ data, meta }) => ({ data: data.map(report => mapResponseToReport(report)), meta })),
|
||||||
catchError((error: Error) => {
|
catchError((error: Error) => {
|
||||||
throw new CustomError(
|
throw new CustomError({
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta rapporter.\n\n${error.message}` })
|
error,
|
||||||
);
|
message: `Kunde inte hämta rapporter.\n\n${error.message}`,
|
||||||
|
name: `GET ${this._apiReportUrl}`,
|
||||||
|
method: 'DeltagareApiService.fetchReports$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchContactInformation$(genomforandeReferens: number): Observable<ContactInformation> {
|
public fetchContactInformation$(genomforandereferens: number): Observable<ContactInformation> {
|
||||||
return this.httpClient
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/contact`;
|
||||||
.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/contact`)
|
return this.httpClient.get<{ data: ContactInformationResponse }>(apiUrl).pipe(
|
||||||
.pipe(
|
map(({ data }) => mapResponseToContactInformation(data)),
|
||||||
map(({ data }) => mapResponseToContactInformation(data)),
|
catchError((error: Error) => {
|
||||||
catchError((error: Error) => {
|
throw new CustomError({
|
||||||
throw new CustomError(
|
error,
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta kontaktinformation.\n\n${error.message}` })
|
message: `Kunde inte hämta kontaktinformation.\n\n${error.message}`,
|
||||||
);
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
})
|
data: { genomforandereferens },
|
||||||
);
|
method: 'DeltagareApiService.fetchContactInformation$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchDriversLicense$(genomforandeReferens: number): Observable<DriversLicense> {
|
public fetchDriversLicense$(genomforandereferens: number): Observable<DriversLicense> {
|
||||||
return this.httpClient
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/driverlicense`;
|
||||||
.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/driverlicense`)
|
return this.httpClient.get<{ data: DriversLicenseResponse }>(apiUrl).pipe(
|
||||||
.pipe(
|
map(({ data }) => mapResponseToDriversLicense(data)),
|
||||||
map(({ data }) => mapResponseToDriversLicense(data)),
|
catchError((error: Error) => {
|
||||||
catchError((error: Error) => {
|
throw new CustomError({
|
||||||
throw new CustomError(
|
error,
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta körkortsinformation.\n\n${error.message}` })
|
message: `Kunde inte hämta körkortsinformation.\n\n${error.message}`,
|
||||||
);
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
})
|
data: { genomforandereferens },
|
||||||
);
|
method: 'DeltagareApiService.fetchDriversLicense$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchHighestEducation$(genomforandeReferens: number): Observable<HighestEducation> {
|
public fetchHighestEducation$(genomforandereferens: number): Observable<HighestEducation> {
|
||||||
return this.httpClient
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/educationlevels/highest`;
|
||||||
.get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educationlevels/highest`)
|
return this.httpClient.get<{ data: HighestEducationResponse }>(apiUrl).pipe(
|
||||||
.pipe(
|
map(({ data }) => mapResponseToHighestEducation(data)),
|
||||||
map(({ data }) => mapResponseToHighestEducation(data)),
|
catchError((error: Error) => {
|
||||||
catchError((error: Error) => {
|
throw new CustomError({
|
||||||
throw new CustomError(
|
error,
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta högsta utbildning.\n\n${error.message}` })
|
message: `Kunde inte hämta högsta utbildning.\n\n${error.message}`,
|
||||||
);
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
})
|
data: { genomforandereferens },
|
||||||
);
|
method: 'DeltagareApiService.fetchHighestEducation$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchEducations$(genomforandeReferens: number): Observable<Education[]> {
|
public fetchEducations$(genomforandereferens: number): Observable<Education[]> {
|
||||||
return this.httpClient
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/educations`;
|
||||||
.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educations`)
|
return this.httpClient.get<{ data: EducationsResponse }>(apiUrl).pipe(
|
||||||
.pipe(
|
map(({ data }) =>
|
||||||
map(({ data }) =>
|
data.utbildningar
|
||||||
data.utbildningar
|
? data.utbildningar.sort((a, b) =>
|
||||||
? data.utbildningar.sort((a, b) =>
|
|
||||||
sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
|
|
||||||
)
|
|
||||||
: []
|
|
||||||
),
|
|
||||||
map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
|
|
||||||
catchError((error: Error) => {
|
|
||||||
throw new CustomError(
|
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta utbildningar.\n\n${error.message}` })
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchTranslator$(genomforandeReferens: number): Observable<string> {
|
|
||||||
return this.httpClient
|
|
||||||
.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/translator`)
|
|
||||||
.pipe(
|
|
||||||
map(({ data }) => data.sprak?.beskrivning || null),
|
|
||||||
catchError((error: Error) => {
|
|
||||||
throw new CustomError(
|
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta tolkinformation.\n\n${error.message}` })
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchWorkLanguages$(genomforandeReferens: number): Observable<string[]> {
|
|
||||||
return this.httpClient
|
|
||||||
.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/languages`)
|
|
||||||
.pipe(
|
|
||||||
map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
|
|
||||||
catchError((error: Error) => {
|
|
||||||
throw new CustomError(
|
|
||||||
errorToCustomError({
|
|
||||||
...error,
|
|
||||||
message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchDisabilities$(genomforandeReferens: number): Observable<Disability[]> {
|
|
||||||
return this.httpClient
|
|
||||||
.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/disabilities`)
|
|
||||||
.pipe(
|
|
||||||
map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
|
|
||||||
catchError((error: Error) => {
|
|
||||||
throw new CustomError(
|
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}` })
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchWorkExperiences$(genomforandeReferens: number): Observable<WorkExperience[]> {
|
|
||||||
return this.httpClient
|
|
||||||
.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/experiences`)
|
|
||||||
.pipe(
|
|
||||||
map(
|
|
||||||
({ data }) =>
|
|
||||||
data?.arbetslivserfarenheter?.sort((a, b) =>
|
|
||||||
sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
|
sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
|
||||||
) || []
|
)
|
||||||
),
|
: []
|
||||||
map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
|
),
|
||||||
catchError((error: Error) => {
|
map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
|
||||||
throw new CustomError(
|
catchError((error: Error) => {
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}` })
|
throw new CustomError({
|
||||||
);
|
error,
|
||||||
})
|
message: `Kunde inte hämta utbildningar.\n\n${error.message}`,
|
||||||
);
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareApiService.fetchEducations$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchAvropInformation$(genomforandeReferens: number): Observable<DeltagareAvrop> {
|
public fetchTranslator$(genomforandereferens: number): Observable<string> {
|
||||||
return this.httpClient
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/translator`;
|
||||||
.get<{ data: DeltagareAvropResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/avrop`)
|
return this.httpClient.get<{ data: TranslatorResponse }>(apiUrl).pipe(
|
||||||
.pipe(
|
map(({ data }) => data.sprak?.beskrivning || null),
|
||||||
map(({ data }) => mapResponseToDeltagareAvrop(data)),
|
catchError((error: Error) => {
|
||||||
catchError((error: Error) => {
|
throw new CustomError({
|
||||||
throw new CustomError(
|
error,
|
||||||
errorToCustomError({ ...error, message: `Kunde inte hämta avropsinformation.\n\n${error.message}` })
|
message: `Kunde inte hämta tolkinformation.\n\n${error.message}`,
|
||||||
);
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
})
|
data: { genomforandereferens },
|
||||||
);
|
method: 'DeltagareApiService.fetchTranslator$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchWorkLanguages$(genomforandereferens: number): Observable<string[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/languages`;
|
||||||
|
return this.httpClient.get<{ data: WorkLanguagesResponse }>(apiUrl).pipe(
|
||||||
|
map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`,
|
||||||
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareApiService.fetchWorkLanguages$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchDisabilities$(genomforandereferens: number): Observable<Disability[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/disabilities`;
|
||||||
|
return this.httpClient.get<{ data: DisabilityResponse[] }>(apiUrl).pipe(
|
||||||
|
map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}`,
|
||||||
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareApiService.fetchDisabilities$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchWorkExperiences$(genomforandereferens: number): Observable<WorkExperience[]> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/experiences`;
|
||||||
|
return this.httpClient.get<{ data: WorkExperiencesResponse }>(apiUrl).pipe(
|
||||||
|
map(
|
||||||
|
({ data }) =>
|
||||||
|
data?.arbetslivserfarenheter?.sort((a, b) =>
|
||||||
|
sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
|
||||||
|
) || []
|
||||||
|
),
|
||||||
|
map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}`,
|
||||||
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareApiService.fetchWorkExperiences$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchAvropInformation$(genomforandereferens: number): Observable<DeltagareAvrop> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/avrop`;
|
||||||
|
return this.httpClient.get<{ data: DeltagareAvropResponse }>(apiUrl).pipe(
|
||||||
|
map(({ data }) => mapResponseToDeltagareAvrop(data)),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta avropsinformation.\n\n${error.message}`,
|
||||||
|
name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`,
|
||||||
|
data: { genomforandereferens },
|
||||||
|
method: 'DeltagareApiService.fetchAvropInformation$',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import {
|
|||||||
mapResponseToEmployee,
|
mapResponseToEmployee,
|
||||||
mapResponseToEmployeeCompact,
|
mapResponseToEmployeeCompact,
|
||||||
} from '@msfa-models/employee.model';
|
} from '@msfa-models/employee.model';
|
||||||
import { errorToCustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { Sort } from '@msfa-models/sort.model';
|
import { Sort } from '@msfa-models/sort.model';
|
||||||
import { ErrorService } from '@msfa-services/error.service';
|
import { ErrorService } from '@msfa-services/error.service';
|
||||||
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||||
import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
const DEFAULT_PARAMS: EmployeeParams = {
|
const DEFAULT_PARAMS: EmployeeParams = {
|
||||||
@@ -119,16 +119,30 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
map(({ data, meta }) => {
|
map(({ data, meta }) => {
|
||||||
this._employeesLoading$.next(false);
|
this._employeesLoading$.next(false);
|
||||||
return { data: data.map(employee => mapResponseToEmployeeCompact(employee)), meta };
|
return { data: data.map(employee => mapResponseToEmployeeCompact(employee)), meta };
|
||||||
|
}),
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta personal.\n\n${error.message}`,
|
||||||
|
name: `GET ${this._apiBaseUrl}`,
|
||||||
|
method: 'EmployeeService._fetchEmployees$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fetchEmployee$(id: string): Observable<Employee | Partial<Employee>> {
|
private _fetchEmployee$(ciamUserId: string): Observable<Employee> {
|
||||||
return this.httpClient.get<{ data: EmployeeResponse }>(`${this._apiBaseUrl}/${id}`).pipe(
|
const apiUrl = `${this._apiBaseUrl}/${ciamUserId}`;
|
||||||
|
return this.httpClient.get<{ data: EmployeeResponse }>(apiUrl).pipe(
|
||||||
map(({ data }) => mapResponseToEmployee(data)),
|
map(({ data }) => mapResponseToEmployee(data)),
|
||||||
catchError(error => {
|
catchError((error: Error) => {
|
||||||
this.errorService.add(errorToCustomError(error));
|
throw new CustomError({
|
||||||
return of({});
|
error,
|
||||||
|
message: `Kunde inte hämta personal.\n\n${error.message}`,
|
||||||
|
name: `GET ${this._apiBaseUrl}/{ciamUserId}`,
|
||||||
|
data: { ciamUserId },
|
||||||
|
method: 'EmployeeService._fetchEmployee$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -153,13 +167,20 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
this._employeeToDelete$.next(employee);
|
this._employeeToDelete$.next(employee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteEmployee(employee: Employee): Observable<Employee | Partial<Employee>> {
|
public deleteEmployee(employee: Employee): Observable<Employee> {
|
||||||
return this.httpClient.delete<void>(`${this._apiBaseUrl}/${employee.id}`).pipe(
|
return this.httpClient.delete<void>(`${this._apiBaseUrl}/${employee.id}`).pipe(
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this._lastDeletedEmployee$.next(employee);
|
this._lastDeletedEmployee$.next(employee);
|
||||||
}),
|
}),
|
||||||
map(() => employee),
|
map(() => employee),
|
||||||
catchError(error => throwError(errorToCustomError(error)))
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte ta bort personal.\n\n${error.message}`,
|
||||||
|
name: `DELETE ${this._apiBaseUrl}/{ciamUserId}`,
|
||||||
|
method: 'EmployeeService.deleteEmployee',
|
||||||
|
});
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,28 +196,40 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public postEmployeeInvitation(emails: string[]): Observable<EmployeeInviteResponse | null> {
|
public postEmployeeInvitation(emails: string[]): Observable<EmployeeInviteResponse | null> {
|
||||||
|
const apiUrl = `${this._apiBaseUrl}/invite`;
|
||||||
return this.httpClient
|
return this.httpClient
|
||||||
.patch<{ data: EmployeeInviteResponse }>(`${this._apiBaseUrl}/invite`, { emails })
|
.patch<{ data: EmployeeInviteResponse }>(apiUrl, { emails })
|
||||||
.pipe(
|
.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
map(({ data }) => data),
|
map(({ data }) => data),
|
||||||
catchError(error => {
|
catchError((error: Error) => {
|
||||||
this.errorService.add(errorToCustomError(error));
|
throw new CustomError({
|
||||||
return throwError(errorToCustomError(error));
|
error,
|
||||||
|
message: `Kunde inte bjuda in personal.\n\n${error.message}`,
|
||||||
|
name: `PATCH ${apiUrl}`,
|
||||||
|
data: { emails },
|
||||||
|
method: 'EmployeeService.postEmployeeInvitation',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateEmployee$(id: string, data: EmployeeEditRequest): Observable<boolean> {
|
public updateEmployee$(ciamUserId: string, data: EmployeeEditRequest): Observable<boolean> {
|
||||||
return this.httpClient.put<boolean>(`${this._apiBaseUrl}/${id}`, data).pipe(
|
return this.httpClient.put<boolean>(`${this._apiBaseUrl}/${ciamUserId}`, data).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
tap(() => {
|
tap(() => {
|
||||||
this._employee$.next(null);
|
this._employee$.next(null);
|
||||||
this._lastUpdatedEmployeeId$.next(id);
|
this._lastUpdatedEmployeeId$.next(ciamUserId);
|
||||||
}),
|
}),
|
||||||
map(() => true),
|
map(() => true),
|
||||||
catchError(error => {
|
catchError((error: Error) => {
|
||||||
return throwError(errorToCustomError(error));
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte redigera personal.\n\n${error.message}`,
|
||||||
|
name: `PATCH ${this._apiBaseUrl}/{ciamUserId}`,
|
||||||
|
data: { ciamUserId },
|
||||||
|
method: 'EmployeeService.updateEmployee$',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ApmService } from '@elastic/apm-rum-angular';
|
||||||
import { SELECTED_ORGANIZATION_NUMBER_KEY } from '@msfa-constants/local-storage-keys';
|
import { SELECTED_ORGANIZATION_NUMBER_KEY } from '@msfa-constants/local-storage-keys';
|
||||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
@@ -38,7 +39,11 @@ export class UserService extends UnsubscribeDirective {
|
|||||||
return this._userRoles$.getValue();
|
return this._userRoles$.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) {
|
constructor(
|
||||||
|
private httpClient: HttpClient,
|
||||||
|
private authenticationService: AuthenticationService,
|
||||||
|
private apmService: ApmService
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
||||||
super.unsubscribeOnDestroy(
|
super.unsubscribeOnDestroy(
|
||||||
@@ -64,6 +69,8 @@ export class UserService extends UnsubscribeDirective {
|
|||||||
this._user$.next(currentUser);
|
this._user$.next(currentUser);
|
||||||
this._userLoading$.next(false);
|
this._userLoading$.next(false);
|
||||||
this._userRolesLoading$.next(false);
|
this._userRolesLoading$.next(false);
|
||||||
|
|
||||||
|
this.apmService.apm.setUserContext({ id: currentUser.id });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { AvropParams, Params } from '@msfa-models/api/params.model';
|
|||||||
import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model';
|
import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model';
|
||||||
import { Handledare } from '@msfa-models/handledare.model';
|
import { Handledare } from '@msfa-models/handledare.model';
|
||||||
import { AvropApiService } from '@msfa-services/api/avrop-api.service';
|
import { AvropApiService } from '@msfa-services/api/avrop-api.service';
|
||||||
|
import { MultiselectFilterOption } from '@ui/multiselect/multiselect-filter-option';
|
||||||
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||||
import { HandledareApiService } from './api/handledare.api.service';
|
import { HandledareApiService } from './api/handledare.api.service';
|
||||||
import { MultiselectFilterOption } from '@ui/multiselect/multiselect-filter-option';
|
|
||||||
|
|
||||||
type Step = 1 | 2 | 3 | 4;
|
type Step = 1 | 2 | 3 | 4;
|
||||||
|
|
||||||
@@ -249,7 +249,12 @@ export class AvropService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.avropApiService.assignHandledare(this._selectedAvrop$.value, this._selectedHandledareId$.value);
|
const avrop: Avrop[] = this._selectedAvrop$.getValue();
|
||||||
|
|
||||||
|
await this.handledareApiService.assignHandledare(
|
||||||
|
avrop.map(deltagare => deltagare.id),
|
||||||
|
{ ciamUserId: this._selectedHandledareId$.value } as Handledare
|
||||||
|
);
|
||||||
this._avropIsSubmitted$.next(true);
|
this._avropIsSubmitted$.next(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export function replaceGenomforandereferensFromUrl(url: string): string {
|
||||||
|
const regex = /\/\d*\//;
|
||||||
|
return url.replace(regex, '/{genomforandereferens}/');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user