diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.html b/apps/mina-sidor-fa/src/app/pages/administration/administration.component.html deleted file mode 100644 index e69de29..0000000 diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.scss b/apps/mina-sidor-fa/src/app/pages/administration/administration.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/administration/administration.component.spec.ts deleted file mode 100644 index 8cf40fa..0000000 --- a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AdministrationComponent } from './administration.component'; - -describe('AdministrationComponent', () => { - let component: AdministrationComponent; - let fixture: ComponentFixture; - - beforeEach( - waitForAsync(() => { - void TestBed.configureTestingModule({ - declarations: [AdministrationComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }) - ); - - beforeEach(() => { - fixture = TestBed.createComponent(AdministrationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/administration.component.ts deleted file mode 100644 index 92eddb4..0000000 --- a/apps/mina-sidor-fa/src/app/pages/administration/administration.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -@Component({ - selector: 'msfa-administration', - templateUrl: './administration.component.html', - styleUrls: ['./administration.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AdministrationComponent {} diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.module.ts b/apps/mina-sidor-fa/src/app/pages/administration/administration.module.ts index 9335331..e42db12 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/administration.module.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/administration.module.ts @@ -1,10 +1,9 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { AdministrationRoutingModule } from './administration-routing.module'; -import { AdministrationComponent } from './administration.component'; @NgModule({ - declarations: [AdministrationComponent], + declarations: [], imports: [CommonModule, AdministrationRoutingModule], }) export class AdministrationModule {} diff --git a/apps/mina-sidor-fa/src/app/pages/administration/administration.service.ts b/apps/mina-sidor-fa/src/app/pages/administration/administration.service.ts new file mode 100644 index 0000000..9cb806c --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/administration/administration.service.ts @@ -0,0 +1,141 @@ +import { Injectable } from '@angular/core'; +import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive'; +import { SortOrder } from '@msfa-enums/sort-order.enum'; +import { EmployeeEditRequest } from '@msfa-models/api/employee-edit.request.model'; +import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model'; +import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model'; +import { EmployeeParams } from '@msfa-models/api/params.model'; +import { Employee, EmployeesData } from '@msfa-models/employee.model'; +import { Sort } from '@msfa-models/sort.model'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators'; +import { EmployeeApiService } from '@msfa-services/api/employee.api.service'; + +const DEFAULT_PARAMS: EmployeeParams = { + page: 1, + limit: 10, + sort: 'name', + order: SortOrder.ASC, + search: '', + onlyEmployeesWithoutAuthorization: false, +}; + +@Injectable({ + providedIn: 'root', +}) +export class AdministrationService extends UnsubscribeDirective { + private _currentEmployeeId$ = new BehaviorSubject(null); + private _params$ = new BehaviorSubject(DEFAULT_PARAMS); + sort$: Observable> = this._params$.pipe( + map(({ sort, order }) => ({ key: sort, order })) + ); + onlyEmployeesWithoutAuthorization$: Observable = this._params$.pipe( + map(({ onlyEmployeesWithoutAuthorization }) => onlyEmployeesWithoutAuthorization) + ); + private _employee$ = new BehaviorSubject(null); + employee$: Observable = this._employee$.asObservable(); + private _lastUpdatedEmployeeId$ = new BehaviorSubject(null); + lastUpdatedEmployeeId$: Observable = this._lastUpdatedEmployeeId$.asObservable(); + private _lastDeletedEmployee$ = new BehaviorSubject(null); + lastDeletedEmployee$: Observable = this._lastDeletedEmployee$.asObservable(); + private _employeeToDelete$ = new BehaviorSubject(null); + employeeToDelete$: Observable = this._employeeToDelete$.asObservable(); + private _employeesLoading$ = new BehaviorSubject(false); + employeesLoading$: Observable = this._employeesLoading$.asObservable(); + + constructor(private employeeApiService: EmployeeApiService) { + super(); + super.unsubscribeOnDestroy( + combineLatest([this._currentEmployeeId$, this._lastUpdatedEmployeeId$]) + .pipe( + distinctUntilChanged( + ([prevEmployeeId], [currEmployeeId, currLastUpdatedEmployeeId]) => + !currLastUpdatedEmployeeId && prevEmployeeId === currEmployeeId + ), + filter(([currentEmployeeId]) => !!currentEmployeeId), + switchMap(([currentEmployeeId]) => + this._fetchEmployee$(currentEmployeeId).pipe(filter(employee => !!employee)) + ) + ) + .subscribe(employee => { + this._employee$.next(employee); + }) + ); + } + + employeesData$: Observable = combineLatest([this._params$, this._lastDeletedEmployee$]).pipe( + switchMap(([params]) => this._fetchEmployees$(params)) + ); + + resetParams(): void { + this._params$.next(DEFAULT_PARAMS); + } + + setCurrentEmployeeId(currentEmployeeId: string): void { + if (this._currentEmployeeId$.getValue() !== currentEmployeeId) { + this._employee$.next(null); + this._currentEmployeeId$.next(currentEmployeeId); + } + } + + resetLastUpdatedEmployeeId(): void { + this._lastUpdatedEmployeeId$.next(null); + } + + private _fetchEmployees$(employeeParams: EmployeeParams): Observable { + this._employeesLoading$.next(true); + + return this.employeeApiService.fetchEmployees$(employeeParams).pipe(tap(() => this._employeesLoading$.next(false))); + } + + private _fetchEmployee$(ciamUserId: string): Observable { + return this.employeeApiService.fetchEmployee$(ciamUserId); + } + + setSearchString(value: string): void { + this._params$.next({ + ...this._params$.getValue(), + search: value, + page: 1, + }); + } + + setOnlyEmployeesWithoutAuthorization(value: boolean): void { + this._params$.next({ + ...this._params$.getValue(), + onlyEmployeesWithoutAuthorization: value, + page: 1, + }); + } + + setEmployeeToDelete(employee: Employee): void { + this._employeeToDelete$.next(employee); + } + + deleteEmployee(employee: Employee): Observable { + return this.employeeApiService.deleteEmployee(employee).pipe( + tap(() => { + this._lastDeletedEmployee$.next(employee); + }) + ); + } + + setSort(sort: keyof EmployeeCompactResponse): void { + const params = this._params$.getValue(); + const order = params.sort === sort && params.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; + + this._params$.next({ ...params, sort, order }); + } + + setPage(page: number): void { + this._params$.next({ ...this._params$.getValue(), page }); + } + + postEmployeeInvitation$(emails: string[]): Observable { + return this.employeeApiService.postEmployeeInvitation(emails); + } + + updateEmployee$(ciamUserId: string, data: EmployeeEditRequest): Observable { + return this.employeeApiService.updateEmployee$(ciamUserId, data); + } +} diff --git a/apps/mina-sidor-fa/src/app/pages/administration/components/employee-delete/employee-delete.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/components/employee-delete/employee-delete.component.ts index bad7e22..3f24259 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/components/employee-delete/employee-delete.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/components/employee-delete/employee-delete.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Router } from '@angular/router'; import { Employee } from '@msfa-models/employee.model'; import { CustomError } from '@msfa-models/error/custom-error'; -import { EmployeeService } from '@msfa-services/api/employee.service'; +import { AdministrationService } from '../../administration.service'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -21,8 +21,8 @@ export class EmployeeDeleteComponent { @Input() returnToEmployeeList = false; deleteEmployeeData$: Observable = combineLatest([ - this.employeeService.employeeToDelete$, - this.employeeService.lastDeletedEmployee$, + this.administrationService.employeeToDelete$, + this.administrationService.lastDeletedEmployee$, ]).pipe( map(([employeeToDelete, lastDeletedEmployee]) => { return { @@ -42,7 +42,7 @@ export class EmployeeDeleteComponent { return lastDeleted ? null : 'Avbryt'; } - constructor(private employeeService: EmployeeService, private router: Router) {} + constructor(private administrationService: AdministrationService, private router: Router) {} deleteEmployeeModelPrimaryClick(deleteEmployeeData: DeleteEmployeeData): void { const { lastDeleted, toDelete } = deleteEmployeeData; @@ -54,7 +54,7 @@ export class EmployeeDeleteComponent { return; } - const deleteEmployeeSubscription = this.employeeService.deleteEmployee(toDelete).subscribe({ + const deleteEmployeeSubscription = this.administrationService.deleteEmployee(toDelete).subscribe({ error: (error: CustomError) => { this._errorDuringDeletion$.next(error); }, @@ -65,7 +65,7 @@ export class EmployeeDeleteComponent { } closeDeleteEmployeeModal(): void { - this.employeeService.setEmployeeToDelete(null); + this.administrationService.setEmployeeToDelete(null); this._errorDuringDeletion$.next(null); } } diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-card/employee-card.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-card/employee-card.component.ts index f08da94..73cbe3a 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-card/employee-card.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-card/employee-card.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Employee } from '@msfa-models/employee.model'; import { Role } from '@msfa-models/role.model'; -import { EmployeeService } from '@msfa-services/api/employee.service'; +import { AdministrationService } from '../../administration.service'; import { RoleService } from '@msfa-services/role.service'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -14,22 +14,22 @@ import { BehaviorSubject, Observable } from 'rxjs'; }) export class EmployeeCardComponent implements OnDestroy { private _employeeId$ = new BehaviorSubject(this.activatedRoute.snapshot.params['employeeId']); - employee$: Observable = this.employeeService.employee$; - lastUpdatedEmployeeId$: Observable = this.employeeService.lastUpdatedEmployeeId$; + employee$: Observable = this.administrationService.employee$; + lastUpdatedEmployeeId$: Observable = this.administrationService.lastUpdatedEmployeeId$; allRoles: Role[] = this.roleService.allRoles; accordionsExpanded = []; constructor( private activatedRoute: ActivatedRoute, - private employeeService: EmployeeService, + private administrationService: AdministrationService, private roleService: RoleService ) { - this.employeeService.setCurrentEmployeeId(this.employeeId); + this.administrationService.setCurrentEmployeeId(this.employeeId); } ngOnDestroy(): void { - this.employeeService.resetLastUpdatedEmployeeId(); - this.employeeService.setCurrentEmployeeId(null); + this.administrationService.resetLastUpdatedEmployeeId(); + this.administrationService.setCurrentEmployeeId(null); } get employeeId(): string { @@ -41,7 +41,7 @@ export class EmployeeCardComponent implements OnDestroy { } closeUpdatedNotificationAlert(): void { - this.employeeService.resetLastUpdatedEmployeeId(); + this.administrationService.resetLastUpdatedEmployeeId(); } toggleAccordionExpanded(currentId: number): void { @@ -52,7 +52,7 @@ export class EmployeeCardComponent implements OnDestroy { } } - hasAccess(employee: Employee, role: Role): boolean{ + hasAccess(employee: Employee, role: Role): boolean { return employee.roles.includes(role.type); } } diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts index 964abd9..2783577 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts @@ -6,7 +6,7 @@ import { CustomError } from '@msfa-models/error/custom-error'; import { Role } from '@msfa-models/role.model'; import { Tjanst } from '@msfa-models/tjanst.model'; import { UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model'; -import { EmployeeService } from '@msfa-services/api/employee.service'; +import { AdministrationService } from '../../administration.service'; import { RoleService } from '@msfa-services/role.service'; import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -29,14 +29,14 @@ export class EmployeeFormComponent implements OnInit { ); errorWhileUpdating$: Observable = this._errorWhileUpdating$.asObservable(); - employee$ = this.employeeService.employee$; + employee$ = this.administrationService.employee$; tjanster$: Observable = this.employeeFormService.fetchTjanster$(); availableRoles: Role[] = this.roleService.allRoles; isLoadingUtforandeVerksamheter$: Observable; constructor( - private employeeService: EmployeeService, + private administrationService: AdministrationService, private roleService: RoleService, private employeeFormService: EmployeeFormService, private utforandeVerksamheterService: UtforandeVerksamheterService, @@ -49,11 +49,11 @@ export class EmployeeFormComponent implements OnInit { } ngOnInit(): void { - this.employeeService.setCurrentEmployeeId(this.employeeId); + this.administrationService.setCurrentEmployeeId(this.employeeId); } updateEmployee(employeeFormData: EmployeeEditRequest): void { - const updateEmployeeSubscription = this.employeeService + const updateEmployeeSubscription = this.administrationService .updateEmployee$(this.employeeId, employeeFormData) .subscribe({ next: () => { @@ -78,7 +78,7 @@ export class EmployeeFormComponent implements OnInit { ); } setEmployeeToDelete(employee: Employee): void { - this.employeeService.setEmployeeToDelete(employee); + this.administrationService.setEmployeeToDelete(employee); } closeError(): void { diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts index 174652a..ef72e8e 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model'; import { CustomError } from '@msfa-models/error/custom-error'; -import { EmployeeService } from '@msfa-services/api/employee.service'; +import { AdministrationService } from '../../administration.service'; import { CommaSeparatedEmailValidator } from '@msfa-utils/validators/email.validator'; import { RequiredValidator } from '@msfa-utils/validators/required.validator'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -21,7 +21,7 @@ export class EmployeeInviteComponent { lastInvites$: Observable = this._lastInvites$.asObservable(); submitIsLoading$ = new BehaviorSubject(false); - constructor(private employeeService: EmployeeService) {} + constructor(private administrationService: AdministrationService) {} get emailsControl(): AbstractControl { return this.formGroup.get('emails'); @@ -93,7 +93,7 @@ export class EmployeeInviteComponent { return; } - const post = this.employeeService.postEmployeeInvitation(this.emailsControlValueAsArray).subscribe({ + const post = this.administrationService.postEmployeeInvitation$(this.emailsControlValueAsArray).subscribe({ next: data => { this.submitIsLoading$.next(false); this._lastInvites$.next(data); diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/employees.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/employees.component.ts index 21e1fa8..b6e816d 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/employees.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/employees.component.ts @@ -3,7 +3,7 @@ import { UiIconType } from '@ui/icon/icon-type.enum'; import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model'; import { Employee, EmployeesData } from '@msfa-models/employee.model'; import { Sort } from '@msfa-models/sort.model'; -import { EmployeeService } from '@msfa-services/api/employee.service'; +import { AdministrationService } from '../../administration.service'; import { BehaviorSubject, Observable } from 'rxjs'; import { UiLinkButtonType } from '@ui/link-button/link-button-type.enum'; @@ -16,16 +16,17 @@ import { UiLinkButtonType } from '@ui/link-button/link-button-type.enum'; export class EmployeesComponent implements OnDestroy { UiLinkButtonType = UiLinkButtonType; private _searchValue$ = new BehaviorSubject(''); - onlyEmployeesWithoutAuthorization$: Observable = this.employeeService.onlyEmployeesWithoutAuthorization$; - employeesData$: Observable = this.employeeService.employeesData$; - employeesLoading$: Observable = this.employeeService.employeesLoading$; - sort$: Observable> = this.employeeService.sort$; + onlyEmployeesWithoutAuthorization$: Observable = this.administrationService + .onlyEmployeesWithoutAuthorization$; + employeesData$: Observable = this.administrationService.employeesData$; + employeesLoading$: Observable = this.administrationService.employeesLoading$; + sort$: Observable> = this.administrationService.sort$; iconType = UiIconType; - constructor(private employeeService: EmployeeService) {} + constructor(private administrationService: AdministrationService) {} ngOnDestroy(): void { - this.employeeService.resetParams(); + this.administrationService.resetParams(); } get searchValue(): string { @@ -33,7 +34,7 @@ export class EmployeesComponent implements OnDestroy { } setSearchString(): void { - this.employeeService.setSearchString(this.searchValue); + this.administrationService.setSearchString(this.searchValue); } setSearchValue($event: CustomEvent<{ target: { value: string } }>): void { @@ -41,18 +42,18 @@ export class EmployeesComponent implements OnDestroy { } handleEmployeesSort(key: keyof EmployeeCompactResponse): void { - this.employeeService.setSort(key); + this.administrationService.setSort(key); } setNewPage(page: number): void { - this.employeeService.setPage(page); + this.administrationService.setPage(page); } setOnlyEmployeesWithoutAuthorization(checked: boolean): void { - this.employeeService.setOnlyEmployeesWithoutAuthorization(checked); + this.administrationService.setOnlyEmployeesWithoutAuthorization(checked); } setEmployeeToDelete(employee: Employee): void { - this.employeeService.setEmployeeToDelete(employee); + this.administrationService.setEmployeeToDelete(employee); } } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/employee.api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/employee.api.service.ts new file mode 100644 index 0000000..759db94 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/services/api/employee.api.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '@msfa-environment'; +import { EmployeeParams, Params } from '@msfa-models/api/params.model'; +import { EmployeeResponse, EmployeesDataResponse } from '@msfa-models/api/employee.response.model'; +import { catchError, map, mapTo, take } from 'rxjs/operators'; +import { + Employee, + EmployeeCompact, + mapResponseToEmployee, + mapResponseToEmployeeCompact, +} from '@msfa-models/employee.model'; +import { CustomError } from '@msfa-models/error/custom-error'; +import { Observable } from 'rxjs'; +import { PaginationMeta } from '@msfa-models/pagination-meta.model'; +import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model'; +import { EmployeeEditRequest } from '@msfa-models/api/employee-edit.request.model'; + +@Injectable({ + providedIn: 'root', +}) +export class EmployeeApiService { + private _apiBaseUrl = `${environment.api.url}/users`; + constructor(private httpClient: HttpClient) {} + + fetchEmployees$(employeeParams: EmployeeParams): Observable<{ data: EmployeeCompact[]; meta: PaginationMeta }> { + const { sort, order, limit, page, search, onlyEmployeesWithoutAuthorization } = employeeParams; + const params: Params = { + sort, + order, + limit: limit.toString(), + page: page.toString(), + }; + if (search) { + params.search = search; + } + + if (onlyEmployeesWithoutAuthorization) { + params.onlyEmployeesWithoutAuthorization = onlyEmployeesWithoutAuthorization.toString(); + } + + return this.httpClient + .get(this._apiBaseUrl, { params }) + .pipe( + map(({ data, 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: 'EmployeeApiService._fetchEmployees$', + }); + }) + ); + } + + fetchEmployee$(ciamUserId: string): Observable { + const apiUrl = `${this._apiBaseUrl}/${ciamUserId}`; + return this.httpClient.get<{ data: EmployeeResponse }>(apiUrl).pipe( + map(({ data }) => mapResponseToEmployee(data)), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta personal.\n\n${error.message}`, + name: `GET ${this._apiBaseUrl}/{ciamUserId}`, + data: { ciamUserId }, + method: 'EmployeeApiService._fetchEmployee$', + }); + }) + ); + } + + deleteEmployee(employee: Employee): Observable { + return this.httpClient.delete(`${this._apiBaseUrl}/${employee.id}`).pipe( + map(() => employee), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte ta bort personal.\n\n${error.message}`, + name: `DELETE ${this._apiBaseUrl}/{ciamUserId}`, + method: 'EmployeeApiService.deleteEmployee', + }); + }) + ); + } + + postEmployeeInvitation(emails: string[]): Observable { + const apiUrl = `${this._apiBaseUrl}/invite`; + return this.httpClient + .patch<{ data: EmployeeInviteResponse }>(apiUrl, { emails }) + .pipe( + take(1), + map(({ data }) => data), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte bjuda in personal.\n\n${error.message}`, + name: `PATCH ${apiUrl}`, + data: { emails }, + method: 'EmployeeApiService.postEmployeeInvitation', + }); + }) + ); + } + + updateEmployee$(ciamUserId: string, data: EmployeeEditRequest): Observable { + return this.httpClient.put(`${this._apiBaseUrl}/${ciamUserId}`, data).pipe( + mapTo(true), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte redigera personal.\n\n${error.message}`, + name: `PATCH ${this._apiBaseUrl}/{ciamUserId}`, + data: { ciamUserId }, + method: 'EmployeeApiService.updateEmployee$', + }); + }) + ); + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts deleted file mode 100644 index ee70722..0000000 --- a/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive'; -import { SortOrder } from '@msfa-enums/sort-order.enum'; -import { environment } from '@msfa-environment'; -import { EmployeeEditRequest } from '@msfa-models/api/employee-edit.request.model'; -import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model'; -import { - EmployeeCompactResponse, - EmployeeResponse, - EmployeesDataResponse, -} from '@msfa-models/api/employee.response.model'; -import { EmployeeParams, Params } from '@msfa-models/api/params.model'; -import { - Employee, - EmployeesData, - mapResponseToEmployee, - mapResponseToEmployeeCompact, -} from '@msfa-models/employee.model'; -import { CustomError } from '@msfa-models/error/custom-error'; -import { Sort } from '@msfa-models/sort.model'; -import { ErrorService } from '@msfa-services/error.service'; -import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators'; - -const DEFAULT_PARAMS: EmployeeParams = { - page: 1, - limit: 10, - sort: 'name', - order: SortOrder.ASC, - search: '', - onlyEmployeesWithoutAuthorization: false, -}; - -@Injectable({ - providedIn: 'root', -}) -export class EmployeeService extends UnsubscribeDirective { - private _apiBaseUrl = `${environment.api.url}/users`; - private _currentEmployeeId$ = new BehaviorSubject(null); - private _params$ = new BehaviorSubject(DEFAULT_PARAMS); - public sort$: Observable> = this._params$.pipe( - map(({ sort, order }) => ({ key: sort, order })) - ); - public onlyEmployeesWithoutAuthorization$: Observable = this._params$.pipe( - map(({ onlyEmployeesWithoutAuthorization }) => onlyEmployeesWithoutAuthorization) - ); - private _employee$ = new BehaviorSubject(null); - public employee$: Observable = this._employee$.asObservable(); - private _lastUpdatedEmployeeId$ = new BehaviorSubject(null); - public lastUpdatedEmployeeId$: Observable = this._lastUpdatedEmployeeId$.asObservable(); - private _lastDeletedEmployee$ = new BehaviorSubject(null); - public lastDeletedEmployee$: Observable = this._lastDeletedEmployee$.asObservable(); - private _employeeToDelete$ = new BehaviorSubject(null); - public employeeToDelete$: Observable = this._employeeToDelete$.asObservable(); - private _employeesLoading$ = new BehaviorSubject(false); - public employeesLoading$: Observable = this._employeesLoading$.asObservable(); - - constructor(private httpClient: HttpClient, private errorService: ErrorService) { - super(); - super.unsubscribeOnDestroy( - combineLatest([this._currentEmployeeId$, this._lastUpdatedEmployeeId$]) - .pipe( - distinctUntilChanged( - ([prevEmployeeId], [currEmployeeId, currLastUpdatedEmployeeId]) => - !currLastUpdatedEmployeeId && prevEmployeeId === currEmployeeId - ), - filter(([currentEmployeeId]) => !!currentEmployeeId), - switchMap(([currentEmployeeId]) => - this._fetchEmployee$(currentEmployeeId).pipe(filter(employee => !!employee)) - ) - ) - .subscribe(employee => { - this._employee$.next(employee); - }) - ); - } - - public employeesData$: Observable = combineLatest([this._params$, this._lastDeletedEmployee$]).pipe( - switchMap(([params]) => this._fetchEmployees$(params)) - ); - - public resetParams(): void { - this._params$.next(DEFAULT_PARAMS); - } - - public setCurrentEmployeeId(currentEmployeeId: string): void { - if (this._currentEmployeeId$.getValue() !== currentEmployeeId) { - this._employee$.next(null); - this._currentEmployeeId$.next(currentEmployeeId); - } - } - - public resetLastUpdatedEmployeeId(): void { - this._lastUpdatedEmployeeId$.next(null); - } - - private _fetchEmployees$(employeeParams: EmployeeParams): Observable { - const { sort, order, limit, page, search, onlyEmployeesWithoutAuthorization } = employeeParams; - const params: Params = { - sort, - order, - limit: limit.toString(), - page: page.toString(), - }; - - if (search) { - params.search = search; - } - - if (onlyEmployeesWithoutAuthorization) { - params.onlyEmployeesWithoutAuthorization = onlyEmployeesWithoutAuthorization.toString(); - } - this._employeesLoading$.next(true); - - return this.httpClient - .get(this._apiBaseUrl, { params }) - .pipe( - map(({ data, meta }) => { - this._employeesLoading$.next(false); - 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$(ciamUserId: string): Observable { - const apiUrl = `${this._apiBaseUrl}/${ciamUserId}`; - return this.httpClient.get<{ data: EmployeeResponse }>(apiUrl).pipe( - map(({ data }) => mapResponseToEmployee(data)), - catchError((error: Error) => { - throw new CustomError({ - error, - message: `Kunde inte hämta personal.\n\n${error.message}`, - name: `GET ${this._apiBaseUrl}/{ciamUserId}`, - data: { ciamUserId }, - method: 'EmployeeService._fetchEmployee$', - }); - }) - ); - } - - public setSearchString(value: string): void { - this._params$.next({ - ...this._params$.getValue(), - search: value, - page: 1, - }); - } - - public setOnlyEmployeesWithoutAuthorization(value: boolean): void { - this._params$.next({ - ...this._params$.getValue(), - onlyEmployeesWithoutAuthorization: value, - page: 1, - }); - } - - public setEmployeeToDelete(employee: Employee): void { - this._employeeToDelete$.next(employee); - } - - public deleteEmployee(employee: Employee): Observable { - return this.httpClient.delete(`${this._apiBaseUrl}/${employee.id}`).pipe( - tap(() => { - this._lastDeletedEmployee$.next(employee); - }), - map(() => employee), - 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', - }); - }) - ); - } - - public setSort(sort: keyof EmployeeCompactResponse): void { - const params = this._params$.getValue(); - const order = params.sort === sort && params.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; - - this._params$.next({ ...params, sort, order }); - } - - public setPage(page: number): void { - this._params$.next({ ...this._params$.getValue(), page }); - } - - public postEmployeeInvitation(emails: string[]): Observable { - const apiUrl = `${this._apiBaseUrl}/invite`; - return this.httpClient - .patch<{ data: EmployeeInviteResponse }>(apiUrl, { emails }) - .pipe( - take(1), - map(({ data }) => data), - catchError((error: Error) => { - throw new CustomError({ - error, - message: `Kunde inte bjuda in personal.\n\n${error.message}`, - name: `PATCH ${apiUrl}`, - data: { emails }, - method: 'EmployeeService.postEmployeeInvitation', - }); - }) - ); - } - - public updateEmployee$(ciamUserId: string, data: EmployeeEditRequest): Observable { - return this.httpClient.put(`${this._apiBaseUrl}/${ciamUserId}`, data).pipe( - take(1), - tap(() => { - this._employee$.next(null); - this._lastUpdatedEmployeeId$.next(ciamUserId); - }), - map(() => true), - catchError((error: Error) => { - throw new CustomError({ - error, - message: `Kunde inte redigera personal.\n\n${error.message}`, - name: `PATCH ${this._apiBaseUrl}/{ciamUserId}`, - data: { ciamUserId }, - method: 'EmployeeService.updateEmployee$', - }); - }) - ); - } -}