refactor(project): Renamed all instances of dafa to msfa or mina-sidor-fa. (TV-379)
Squashed commit of the following: commit d3f52ff6876f6e246c7d3c188e56cc2370289341 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Aug 17 14:10:38 2021 +0200 Renamed all dafa instances to msfa
This commit is contained in:
0
apps/mina-sidor-fa/src/app/shared/services/.gitkeep
Normal file
0
apps/mina-sidor-fa/src/app/shared/services/.gitkeep
Normal file
@@ -0,0 +1,23 @@
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthenticationService } from './authentication.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
constructor(private auth: AuthenticationService) {}
|
||||
|
||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
const idToken = this.auth.currentAuthorizationToken;
|
||||
|
||||
if (idToken) {
|
||||
const cloned = req.clone({
|
||||
headers: req.headers.set('Authorization', 'Bearer ' + idToken),
|
||||
});
|
||||
|
||||
return next.handle(cloned);
|
||||
} else {
|
||||
return next.handle(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model';
|
||||
import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.model';
|
||||
import { add, isBefore } from 'date-fns';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthenticationService {
|
||||
private _token$ = new BehaviorSubject<string>(null);
|
||||
private _expiration$ = new BehaviorSubject<number>(null);
|
||||
|
||||
isLoggedIn$: Observable<boolean> = combineLatest([this._token$, this._expiration$]).pipe(
|
||||
map(([token, expiration]) => {
|
||||
if (token && expiration) {
|
||||
return isBefore(new Date(), expiration);
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
private static _authTokenApiUrl(code: string): string {
|
||||
return `${environment.api.url}/auth/token?accessCode=${code}`;
|
||||
}
|
||||
|
||||
private _setSession(authenticationResult: Authentication): void {
|
||||
const expiresAt = add(new Date(), { seconds: authenticationResult.expiresIn });
|
||||
|
||||
this._token$.next(authenticationResult.idToken);
|
||||
localStorage.setItem('id_token', authenticationResult.idToken);
|
||||
this._expiration$.next(expiresAt.valueOf());
|
||||
localStorage.setItem('expires_at', JSON.stringify(expiresAt.valueOf()));
|
||||
}
|
||||
|
||||
login$(authorizationCodeFromCiam: string): Observable<Authentication> {
|
||||
return this.httpClient
|
||||
.get<{ data: AuthenticationResponse }>(
|
||||
AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam),
|
||||
API_HEADERS
|
||||
)
|
||||
.pipe(
|
||||
map(({ data }) => mapAuthApiResponseToAuthenticationResult(data)),
|
||||
tap(authenticationResult => {
|
||||
this._setSession(authenticationResult);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _getLocalStorageData(): { id_token: string; expires_at: number } {
|
||||
const id_token = localStorage.getItem('id_token');
|
||||
const expiration = localStorage.getItem('expires_at');
|
||||
|
||||
return id_token && expiration
|
||||
? {
|
||||
id_token,
|
||||
expires_at: +JSON.parse(expiration),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
get currentAuthorizationToken(): string {
|
||||
return this._token$.getValue();
|
||||
}
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
const localStorageData = this._getLocalStorageData();
|
||||
|
||||
if (localStorageData) {
|
||||
this._token$.next(localStorageData.id_token);
|
||||
this._expiration$.next(localStorageData.expires_at);
|
||||
}
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
localStorage.removeItem('id_token');
|
||||
localStorage.removeItem('expires_at');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthorizationService {
|
||||
private _authorizationsApiUrl = `${environment.api.url}/authorizations`;
|
||||
public authorizations$: Observable<Authorization[]> = this.httpClient
|
||||
.get<AuthorizationApiResponse>(this._authorizationsApiUrl, API_HEADERS)
|
||||
.pipe(map(({ data }) => data.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization))));
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { AvropResponse } from '@msfa-models/api/avrop.response.model';
|
||||
import { KommunResponse } from '@msfa-models/api/kommun.response.model';
|
||||
import { TjanstResponse } from '@msfa-models/api/tjanst.response.model';
|
||||
import { UtforandeVerksamhetResponse } from '@msfa-models/api/utforande-verksamhet.response.model';
|
||||
import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
|
||||
import { mapKommunResponseToKommun } from '@msfa-models/kommun.model';
|
||||
import { MultiselectFilterOption } from '@msfa-models/multiselect-filter-option';
|
||||
import { mapTjanstResponseToTjanst } from '@msfa-models/tjanst.model';
|
||||
import { mapUtforandeVerksamhetResponseToUtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay, filter, map } from 'rxjs/operators';
|
||||
import { HandledareAvrop } from '../../../pages/avrop/models/handledare-avrop';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
const tempHandledareMock: HandledareAvrop[] = [
|
||||
{ id: '1', fullName: 'Göran Persson' },
|
||||
{ id: '2', fullName: 'Stefan Löfven' },
|
||||
];
|
||||
|
||||
const tempMockDelay = 300;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AvropApiService {
|
||||
private _apiBaseUrl = `${environment.api.url}/avrop`;
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
getNyaAvrop$(
|
||||
tjanstIds: MultiselectFilterOption[],
|
||||
kommunIds: MultiselectFilterOption[],
|
||||
utforandeVerksamhetIds: MultiselectFilterOption[],
|
||||
offset = 0,
|
||||
limit = 20
|
||||
): Observable<Avrop[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: AvropResponse[] }>(`${this._apiBaseUrl}`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(avrop => mapAvropResponseToAvrop(avrop)))
|
||||
);
|
||||
}
|
||||
|
||||
getSelectableHandledare$(deltagare: Avrop[]): Observable<HandledareAvrop[]> {
|
||||
// TODO replace with API-call
|
||||
console.log('[API call] getSelectableHandledare$. Inputs: deltagare', deltagare);
|
||||
return of(tempHandledareMock).pipe(delay(tempMockDelay));
|
||||
}
|
||||
|
||||
getSelectableTjanster$(
|
||||
selectedKommuner: MultiselectFilterOption[],
|
||||
selectedUtforandeVerksamheter: MultiselectFilterOption[]
|
||||
): Observable<MultiselectFilterOption[]> {
|
||||
return this.httpClient.get<{ data: TjanstResponse[] }>(`${this._apiBaseUrl}/tjanster`).pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(tjanster => ({ label: mapTjanstResponseToTjanst(tjanster).name })))
|
||||
);
|
||||
}
|
||||
|
||||
getSelectableUtforandeVerksamheter$(
|
||||
selectedTjanster: MultiselectFilterOption[],
|
||||
selectedKommuner: MultiselectFilterOption[]
|
||||
): Observable<MultiselectFilterOption[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: UtforandeVerksamhetResponse[] }>(`${this._apiBaseUrl}/utforandeverksamheter`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) =>
|
||||
data.map(utforandeverksamheter => ({
|
||||
label: mapUtforandeVerksamhetResponseToUtforandeVerksamhet(utforandeverksamheter).name,
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getSelectableKommuner$(
|
||||
selectedTjanster: MultiselectFilterOption[],
|
||||
selectedUtforandeVerksamheter: MultiselectFilterOption[]
|
||||
): Observable<MultiselectFilterOption[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: KommunResponse[] }>(`${this._apiBaseUrl}/kommuner`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(kommun => ({ label: mapKommunResponseToKommun(kommun).name })))
|
||||
);
|
||||
}
|
||||
|
||||
async tilldelaHandledare(deltagare: Avrop[], handledare: HandledareAvrop): Promise<void> {
|
||||
console.log('[API call] SAVE avrop. Inputs: deltagare, handledare', deltagare, handledare);
|
||||
await of(null).pipe(delay(200)).toPromise();
|
||||
// TODO anropa API
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { AvropResponse } from '@msfa-models/api/avrop.response.model';
|
||||
import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model';
|
||||
import { DisabilityResponse } from '@msfa-models/api/disability.response.model';
|
||||
import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model';
|
||||
import { EducationsResponse } from '@msfa-models/api/educations.response.model';
|
||||
import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model';
|
||||
import { TranslatorResponse } from '@msfa-models/api/translator.response.model';
|
||||
import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model';
|
||||
import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model';
|
||||
import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
|
||||
import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model';
|
||||
import { Deltagare, DeltagareCompact, mapResponseToDeltagareCompact } from '@msfa-models/deltagare.model';
|
||||
import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
|
||||
import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
|
||||
import { Education, mapResponseToEducation } from '@msfa-models/education.model';
|
||||
import { errorToCustomError } from '@msfa-models/error/custom-error';
|
||||
import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
|
||||
import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
|
||||
import { ErrorService } from '@msfa-services/error.service';
|
||||
import { sortFromToDates } from '@msfa-utils/sort.util';
|
||||
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 extends UnsubscribeDirective {
|
||||
private _apiBaseUrl = `${environment.api.url}/deltagare`;
|
||||
private _currentDeltagareId$ = new BehaviorSubject<string>(null);
|
||||
|
||||
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 _deltagare$ = new BehaviorSubject<Deltagare>(null);
|
||||
public deltagare$: Observable<Deltagare> = this._deltagare$.asObservable();
|
||||
|
||||
public allDeltagare$: Observable<DeltagareCompact[]> = this.httpClient
|
||||
.get<{ data: AvropResponse[] }>(`${this._apiBaseUrl}`, { ...API_HEADERS })
|
||||
.pipe(map(({ data }) => data.map(deltagare => mapResponseToDeltagareCompact(deltagare))));
|
||||
|
||||
public setCurrentDeltagareId(currentDeltagareId: string): void {
|
||||
this._deltagare$.next(null);
|
||||
this._currentDeltagareId$.next(currentDeltagareId);
|
||||
}
|
||||
|
||||
private _fetchContactInformation$(id: string): Observable<ContactInformation | Partial<ContactInformation>> {
|
||||
return this.httpClient
|
||||
.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${id}/contact`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => mapResponseToContactInformation(data)),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of({});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchDriversLicense$(id: string): Observable<DriversLicense | Partial<DriversLicense>> {
|
||||
return this.httpClient
|
||||
.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${id}/driverlicense`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => mapResponseToDriversLicense(data)),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of({});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchHighestEducation$(id: string): Observable<HighestEducation | Partial<HighestEducation>> {
|
||||
return this.httpClient
|
||||
.get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${id}/educationlevels/highest`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => mapResponseToHighestEducation(data)),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of({});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchEducations$(id: string): Observable<Education[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${id}/educations`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) =>
|
||||
data.utbildningar
|
||||
? 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 => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchTranslator$(id: string): Observable<string> {
|
||||
return this.httpClient
|
||||
.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${id}/translator`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => data.sprak?.beskrivning || null),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of('');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchWorkLanguages$(id: string): Observable<string[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${id}/work/languages`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchDisabilities$(id: string): Observable<Disability[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${id}/work/disabilities`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
|
||||
catchError(error => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchWorkExperiences$(id: string): Observable<WorkExperience[]> {
|
||||
return this.httpClient
|
||||
.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${id}/work/experiences`, { ...API_HEADERS })
|
||||
.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 => {
|
||||
this.errorService.add(errorToCustomError(error));
|
||||
return of([]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchAvropInformation$(id: string): Observable<Avrop | Partial<Avrop>> {
|
||||
return this.httpClient
|
||||
.get<{ data: AvropResponse }>(`${this._apiBaseUrl}/${id}/avrop`, { ...API_HEADERS })
|
||||
.pipe(
|
||||
map(({ data }) => (data ? mapAvropResponseToAvrop(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 fetchDeltagare$(id: string): Observable<Deltagare> {
|
||||
return combineLatest([
|
||||
this._fetchContactInformation$(id),
|
||||
this._fetchDriversLicense$(id),
|
||||
this._fetchHighestEducation$(id),
|
||||
this._fetchEducations$(id),
|
||||
this._fetchTranslator$(id),
|
||||
this._fetchWorkLanguages$(id),
|
||||
this._fetchDisabilities$(id),
|
||||
this._fetchWorkExperiences$(id),
|
||||
this._fetchAvropInformation$(id),
|
||||
]).pipe(
|
||||
map(
|
||||
([
|
||||
contactInformation,
|
||||
driversLicense,
|
||||
highestEducation,
|
||||
educations,
|
||||
translator,
|
||||
workLanguages,
|
||||
disabilities,
|
||||
workExperiences,
|
||||
avropInformation,
|
||||
]: [
|
||||
ContactInformation,
|
||||
DriversLicense,
|
||||
HighestEducation,
|
||||
Education[],
|
||||
string,
|
||||
string[],
|
||||
Disability[],
|
||||
WorkExperience[],
|
||||
Avrop
|
||||
]) => ({
|
||||
id,
|
||||
...contactInformation,
|
||||
driversLicense,
|
||||
highestEducation,
|
||||
educations,
|
||||
translator,
|
||||
workLanguages,
|
||||
disabilities,
|
||||
workExperiences,
|
||||
avropInformation,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { EmployeeInviteMockApiResponse } from '@msfa-models/api/employee-invite.response.model';
|
||||
import { EmployeeInviteMockaData } from '@msfa-models/employee-invite-mock-data.model';
|
||||
import {
|
||||
Employee,
|
||||
EmployeeApiResponse,
|
||||
EmployeesApiResponse,
|
||||
EmployeesData,
|
||||
mapEmployeeReponseToEmployee,
|
||||
mapEmployeeToEmployeeApiRequestData,
|
||||
} from '@msfa-models/employee.model';
|
||||
import { Sort } from '@msfa-models/sort.model';
|
||||
import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs';
|
||||
import { catchError, map, switchMap, take } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EmployeeService {
|
||||
private _apiUrl = `${environment.api.url}/employee`;
|
||||
private _limit$ = new BehaviorSubject<number>(20);
|
||||
private _page$ = new BehaviorSubject<number>(1);
|
||||
private _sort$ = new BehaviorSubject<Sort<keyof Employee>>({ key: 'fullName', order: SortOrder.ASC });
|
||||
public sort$: Observable<Sort<keyof Employee>> = this._sort$.asObservable();
|
||||
private _searchFilter$ = new BehaviorSubject<string>('');
|
||||
private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
public employeesData$: Observable<EmployeesData> = combineLatest([
|
||||
this._limit$,
|
||||
this._page$,
|
||||
this._sort$,
|
||||
this._searchFilter$,
|
||||
this._onlyEmployeesWithoutAuthorization$,
|
||||
]).pipe(
|
||||
switchMap(([limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization]) =>
|
||||
this._fetchEmployees$(limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization)
|
||||
)
|
||||
);
|
||||
|
||||
private _fetchEmployees$(
|
||||
limit: number,
|
||||
page: number,
|
||||
sort: Sort<keyof Employee>,
|
||||
searchFilter: string,
|
||||
onlyEmployeesWithoutAuthorization?: boolean
|
||||
): Observable<EmployeesData> {
|
||||
const params: { [param: string]: string | string[] } = {
|
||||
sort: sort.key as string,
|
||||
order: sort.order as string,
|
||||
limit: limit.toString(),
|
||||
page: page.toString(),
|
||||
};
|
||||
|
||||
if (searchFilter) {
|
||||
params.search = searchFilter;
|
||||
}
|
||||
|
||||
if (onlyEmployeesWithoutAuthorization) {
|
||||
params.onlyEmployeesWithoutAuthorization = onlyEmployeesWithoutAuthorization?.toString();
|
||||
}
|
||||
|
||||
return this.httpClient
|
||||
.get<EmployeesApiResponse>(this._apiUrl, {
|
||||
...API_HEADERS,
|
||||
params,
|
||||
})
|
||||
.pipe(
|
||||
map(({ data, meta }) => {
|
||||
return { data: data.map(employee => mapEmployeeReponseToEmployee(employee)), meta };
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public fetchDetailedEmployeeData$(id: string): Observable<Employee> {
|
||||
return this.httpClient
|
||||
.get<EmployeeApiResponse>(`${this._apiUrl}/${id}`, { ...API_HEADERS })
|
||||
.pipe(map(result => mapEmployeeReponseToEmployee(result.data)));
|
||||
}
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
public setSearchFilter(value: string): void {
|
||||
this._searchFilter$.next(value);
|
||||
}
|
||||
|
||||
public setOnlyEmployeesWithoutAuthorization(value: boolean): void {
|
||||
this._onlyEmployeesWithoutAuthorization$.next(value);
|
||||
}
|
||||
|
||||
public setSort(newSortKey: keyof Employee): void {
|
||||
const currentSort = this._sort$.getValue();
|
||||
const order =
|
||||
currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
|
||||
this._sort$.next({ key: newSortKey, order });
|
||||
}
|
||||
|
||||
public setPage(page: number): void {
|
||||
this._page$.next(page);
|
||||
}
|
||||
|
||||
public postNewEmployee(employeeData: Employee): Observable<string> {
|
||||
return this.httpClient
|
||||
.post<{ id: string }>(this._apiUrl, mapEmployeeToEmployeeApiRequestData(employeeData), API_HEADERS)
|
||||
.pipe(
|
||||
map(({ id }) => id),
|
||||
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
|
||||
);
|
||||
}
|
||||
|
||||
postEmployeeInvitation(email: string): Observable<EmployeeInviteMockaData> {
|
||||
return this.httpClient
|
||||
.post<{ data: EmployeeInviteMockApiResponse }>('http://localhost:8000/invites', { email }, API_HEADERS)
|
||||
.pipe(
|
||||
take(1),
|
||||
map(res => res.data),
|
||||
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ParticipantStatus } from '@msfa-enums/participant-status.enum';
|
||||
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
||||
import { environment } from '@msfa-environment';
|
||||
import {
|
||||
mapParticipantApiResponseToParticipant,
|
||||
Participant,
|
||||
ParticipantApiResponse,
|
||||
ParticipantsApiResponse,
|
||||
} from '@msfa-models/participant.model';
|
||||
import { Sort } from '@msfa-models/sort.model';
|
||||
import { sort } from '@msfa-utils/sort.util';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
function filterParticipants(participants: Participant[], searchFilter: string): Participant[] {
|
||||
return participants.filter(participant => {
|
||||
const searchValueExistsInName = participant.fullName.toLowerCase().includes(searchFilter.toLowerCase());
|
||||
const searchValueExistsInErrandNumber = participant.errandNumber.toString().includes(searchFilter);
|
||||
|
||||
return searchValueExistsInName || searchValueExistsInErrandNumber;
|
||||
});
|
||||
}
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ParticipantsService {
|
||||
private _apiUrl = `${environment.api.url}/participants`;
|
||||
private _allParticipants$: Observable<Participant[]> = this.httpClient
|
||||
.get<ParticipantsApiResponse>(this._apiUrl)
|
||||
.pipe(map(response => response.data.map(participant => mapParticipantApiResponseToParticipant(participant))));
|
||||
private _activeParticipantsSortBy$ = new BehaviorSubject<Sort<keyof Participant> | null>({
|
||||
key: 'handleBefore',
|
||||
order: SortOrder.ASC,
|
||||
});
|
||||
public activeParticipantsSortBy$: Observable<
|
||||
Sort<keyof Participant>
|
||||
> = this._activeParticipantsSortBy$.asObservable();
|
||||
private _followUpParticipantsSortBy$ = new BehaviorSubject<Sort<keyof Participant> | null>({
|
||||
key: 'handleBefore',
|
||||
order: SortOrder.ASC,
|
||||
});
|
||||
public followUpParticipantsSortBy$: Observable<
|
||||
Sort<keyof Participant>
|
||||
> = this._followUpParticipantsSortBy$.asObservable();
|
||||
private _searchFilter$ = new BehaviorSubject<string>('');
|
||||
public searchFilter$: Observable<string> = this._searchFilter$.asObservable();
|
||||
|
||||
public filteredParticipants$: Observable<Participant[]> = combineLatest([
|
||||
this._allParticipants$,
|
||||
this._searchFilter$,
|
||||
]).pipe(map(([participants, searchFilter]) => filterParticipants(participants, searchFilter)));
|
||||
|
||||
public activeParticipants$: Observable<Participant[]> = combineLatest([
|
||||
this.filteredParticipants$,
|
||||
this._activeParticipantsSortBy$,
|
||||
]).pipe(
|
||||
map(([participants, sortBy]) => {
|
||||
const activeParticipants = participants.filter(participant => participant.status === ParticipantStatus.ACTIVE);
|
||||
return sortBy ? sort(activeParticipants, sortBy) : activeParticipants;
|
||||
})
|
||||
);
|
||||
|
||||
public followUpParticipants$: Observable<Participant[]> = combineLatest([
|
||||
this.filteredParticipants$,
|
||||
this._followUpParticipantsSortBy$,
|
||||
]).pipe(
|
||||
map(([participants, sortBy]) => {
|
||||
const followUpParticipants = participants.filter(
|
||||
participant => participant.status === ParticipantStatus.FOLLOW_UP
|
||||
);
|
||||
return sortBy ? sort(followUpParticipants, sortBy) : followUpParticipants;
|
||||
})
|
||||
);
|
||||
|
||||
public fetchDetailedParticipantData$(id: string): Observable<Participant> {
|
||||
return this.httpClient
|
||||
.get<ParticipantApiResponse>(`${this._apiUrl}/${id}`, { ...API_HEADERS })
|
||||
.pipe(map(result => mapParticipantApiResponseToParticipant(result.data)));
|
||||
}
|
||||
|
||||
public setSearchFilter(value: string): void {
|
||||
this._searchFilter$.next(value);
|
||||
}
|
||||
|
||||
public setActiveParticipantsSortKey(key: keyof Participant): void {
|
||||
const currentSortBy = this._activeParticipantsSortBy$.getValue();
|
||||
let order = currentSortBy.order;
|
||||
if (currentSortBy?.key === key) {
|
||||
order = order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
}
|
||||
this._activeParticipantsSortBy$.next({ key, order });
|
||||
}
|
||||
|
||||
public setFollowUpParticipantsSortKey(key: keyof Participant): void {
|
||||
const currentSortBy = this._followUpParticipantsSortBy$.getValue();
|
||||
let order = currentSortBy.order;
|
||||
if (currentSortBy?.key === key) {
|
||||
order = order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
}
|
||||
this._followUpParticipantsSortBy$.next({ key, order });
|
||||
}
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { mapServiceApiResponseToService, Service, ServiceApiResponse } from '@msfa-models/service.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ServiceService {
|
||||
private _servicesApiUrl = `${environment.api.url}/services`;
|
||||
public services$: Observable<Service[]> = this.httpClient
|
||||
.get<ServiceApiResponse>(this._servicesApiUrl, API_HEADERS)
|
||||
.pipe(map(response => response.data.map(service => mapServiceApiResponseToService(service))));
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { OrganizationResponse } from '@msfa-models/api/organization.response.model';
|
||||
import { UserInfoResponse } from '@msfa-models/api/user-info.response.model';
|
||||
import { mapResponseToOrganization, Organization } from '@msfa-models/organization.model';
|
||||
import { mapResponseToUserInfo, UserInfo } from '@msfa-models/user-info.model';
|
||||
import { User } from '@msfa-models/user.model';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService extends UnsubscribeDirective {
|
||||
private _authApiUrl = `${environment.api.url}/auth`;
|
||||
private _user$ = new BehaviorSubject<User>(null);
|
||||
|
||||
public user$: Observable<User> = this._user$.asObservable();
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
super();
|
||||
this.unsubscribeOnDestroy(
|
||||
combineLatest([this._fetchUserInfo$(), this._fetchOrganizations$()]).subscribe(([userInfo, organizations]) => {
|
||||
this._user$.next({ ...userInfo, organizations });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchOrganizations$(): Observable<Organization[]> {
|
||||
return this.httpClient.get<{ data: OrganizationResponse[] }>(`${this._authApiUrl}/organizations`, API_HEADERS).pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(organization => mapResponseToOrganization(organization)))
|
||||
);
|
||||
}
|
||||
|
||||
private _fetchUserInfo$(): Observable<UserInfo> {
|
||||
return this.httpClient.get<{ data: UserInfoResponse }>(`${this._authApiUrl}/userinfo`, API_HEADERS).pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => mapResponseToUserInfo(data))
|
||||
);
|
||||
}
|
||||
}
|
||||
33
apps/mina-sidor-fa/src/app/shared/services/error.service.ts
Normal file
33
apps/mina-sidor-fa/src/app/shared/services/error.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ApplicationRef, Injectable, Injector } from '@angular/core';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ErrorService {
|
||||
private appRef: ApplicationRef;
|
||||
private errorQueue$ = new BehaviorSubject<CustomError[]>([]);
|
||||
|
||||
public errors$: Observable<CustomError[]> = this.errorQueue$.pipe(
|
||||
map(errors => errors.sort((a, b) => (a.timestamp > b.timestamp ? -1 : 1)))
|
||||
);
|
||||
|
||||
constructor(private injector: Injector) {
|
||||
// Workaround to fix change-detection when using Error interceptor
|
||||
// See https://stackoverflow.com/a/37793791
|
||||
setTimeout(() => (this.appRef = this.injector.get(ApplicationRef)));
|
||||
}
|
||||
|
||||
public add(error: CustomError): void {
|
||||
this.errorQueue$.next([...this.errorQueue$.value, error]);
|
||||
this.appRef.tick();
|
||||
}
|
||||
|
||||
public remove(error: CustomError): void {
|
||||
const newErrorQueue = this.errorQueue$.value.filter(currentError => currentError.id !== error.id);
|
||||
this.errorQueue$.next(newErrorQueue);
|
||||
this.appRef.tick();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user