diff --git a/apps/dafa-web/src/app/data/enums/sort-order.enum.ts b/apps/dafa-web/src/app/data/enums/sort-order.enum.ts new file mode 100644 index 0000000..cabb1f2 --- /dev/null +++ b/apps/dafa-web/src/app/data/enums/sort-order.enum.ts @@ -0,0 +1,4 @@ +export enum SortOrder { + ASC = 'asc', + DESC = 'desc', +} diff --git a/apps/dafa-web/src/app/data/models/authorization.model.ts b/apps/dafa-web/src/app/data/models/authorization.model.ts index 0ad4407..d858c62 100644 --- a/apps/dafa-web/src/app/data/models/authorization.model.ts +++ b/apps/dafa-web/src/app/data/models/authorization.model.ts @@ -6,11 +6,15 @@ export interface Authorization { } export interface AuthorizationApiResponse { + data: AuthorizationApiResponseData[]; +} + +export interface AuthorizationApiResponseData { id: string; name: AuthorizationEnum; } -export function mapAuthorizationApiResponseToAuthorization(data: AuthorizationApiResponse): Authorization { +export function mapAuthorizationApiResponseToAuthorization(data: AuthorizationApiResponseData): Authorization { const { id, name } = data; return { id, diff --git a/apps/dafa-web/src/app/data/models/employee.model.ts b/apps/dafa-web/src/app/data/models/employee.model.ts index f492e2f..a36cbc8 100644 --- a/apps/dafa-web/src/app/data/models/employee.model.ts +++ b/apps/dafa-web/src/app/data/models/employee.model.ts @@ -1,22 +1,43 @@ -import { Authorization } from './authorization.model'; +import { Authorization } from '@dafa-enums/authorization.enum'; import { Organization } from './organization.model'; -import { Participant } from './participant.model'; +import { PaginationMeta } from './pagination-meta.model'; import { Service } from './service.model'; -import { User, UserApiResponse } from './user.model'; -export interface Employee extends User { - languages: string[]; - participants: Participant[]; +export interface Employee { + id: string; + firstName: string; + lastName: string; + fullName: string; + ssn: string; + organizations: Organization[]; + authorizations: Authorization[]; services: Service[]; } -export interface EmployeeApiResponse extends UserApiResponse { - languages: string[]; - participants: Participant[]; +export interface EmployeesApiResponse { + data: Employee[]; + meta: PaginationMeta; +} + +export interface EmployeesData { + data: Employee[]; + meta: PaginationMeta; +} + +export interface EmployeeApiResponse { + data: Employee; +} + +export interface EmployeeApiResponseData { + id: string; + firstName: string; + lastName: string; + ssn: string; + organizations: Organization[]; + authorizations: Authorization[]; services: Service[]; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface export interface EmployeeApiRequestData { firstName: string; lastName: string; @@ -38,8 +59,8 @@ export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApi }; } -export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employee { - const { id, firstName, lastName, ssn, services, languages, organizations, authorizations, participants } = data; +export function mapEmployeeReponseToEmployee(data: EmployeeApiResponseData): Employee { + const { id, firstName, lastName, ssn, services, organizations, authorizations } = data; return { id, firstName, @@ -48,8 +69,6 @@ export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employe organizations, authorizations, services, - languages, ssn, - participants, }; } diff --git a/apps/dafa-web/src/app/data/models/pagination-meta.model.ts b/apps/dafa-web/src/app/data/models/pagination-meta.model.ts new file mode 100644 index 0000000..201f8e6 --- /dev/null +++ b/apps/dafa-web/src/app/data/models/pagination-meta.model.ts @@ -0,0 +1,6 @@ +export interface PaginationMeta { + count: number; + limit: number; + page: number; + totalPages: number; +} diff --git a/apps/dafa-web/src/app/data/models/participant.model.ts b/apps/dafa-web/src/app/data/models/participant.model.ts index a536361..faf70d7 100644 --- a/apps/dafa-web/src/app/data/models/participant.model.ts +++ b/apps/dafa-web/src/app/data/models/participant.model.ts @@ -1,7 +1,31 @@ import { ParticipantStatus } from '@dafa-enums/participant-status.enum'; import { Service } from '@dafa-enums/service.enum'; +import { PaginationMeta } from './pagination-meta.model'; export interface Participant { + id: string; + firstName: string; + lastName: string; + fullName: string; + status: ParticipantStatus; + nextStep: string; + service: Service; + errandNumber: number; + startDate: Date; + endDate: Date; + handleBefore: Date; +} + +export interface ParticipantsApiResponse { + data: ParticipantApiResponseData[]; + meta?: PaginationMeta; +} + +export interface ParticipantApiResponse { + data: ParticipantApiResponseData; +} + +export interface ParticipantApiResponseData { id: string; firstName: string; lastName: string; @@ -12,5 +36,21 @@ export interface Participant { startDate: Date; endDate: Date; handleBefore: Date; - fullName?: string; +} + +export function mapParticipantApiResponseToParticipant(data: ParticipantApiResponseData): Participant { + const { id, firstName, lastName, status, nextStep, service, errandNumber, startDate, endDate, handleBefore } = data; + return { + id, + firstName, + lastName, + fullName: `${firstName} ${lastName}`, + status, + nextStep, + service, + errandNumber, + startDate, + endDate, + handleBefore, + }; } diff --git a/apps/dafa-web/src/app/data/models/service.model.ts b/apps/dafa-web/src/app/data/models/service.model.ts index 7adea6d..9f5a581 100644 --- a/apps/dafa-web/src/app/data/models/service.model.ts +++ b/apps/dafa-web/src/app/data/models/service.model.ts @@ -6,11 +6,15 @@ export interface Service { } export interface ServiceApiResponse { + data: ServiceApiResponseData[]; +} + +export interface ServiceApiResponseData { id: string; name: ServiceEnum; } -export function mapServiceApiResponseToService(data: ServiceApiResponse): Service { +export function mapServiceApiResponseToService(data: ServiceApiResponseData): Service { const { id, name } = data; return { id, diff --git a/apps/dafa-web/src/app/data/models/sort-by.model.ts b/apps/dafa-web/src/app/data/models/sort-by.model.ts deleted file mode 100644 index ea280b3..0000000 --- a/apps/dafa-web/src/app/data/models/sort-by.model.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Employee } from './employee.model'; -import { Participant } from './participant.model'; - -export interface SortBy { - key: keyof Participant | keyof Employee; - reverse: boolean; -} diff --git a/apps/dafa-web/src/app/data/models/sort.model.ts b/apps/dafa-web/src/app/data/models/sort.model.ts new file mode 100644 index 0000000..ecea1bf --- /dev/null +++ b/apps/dafa-web/src/app/data/models/sort.model.ts @@ -0,0 +1,6 @@ +import { SortOrder } from '@dafa-enums/sort-order.enum'; + +export interface Sort { + key: Key; + order: SortOrder; +} diff --git a/apps/dafa-web/src/app/data/models/user.model.ts b/apps/dafa-web/src/app/data/models/user.model.ts index 9cc4d49..e01aaa8 100644 --- a/apps/dafa-web/src/app/data/models/user.model.ts +++ b/apps/dafa-web/src/app/data/models/user.model.ts @@ -12,6 +12,10 @@ export interface User { } export interface UserApiResponse { + data: UserApiResponseData; +} + +export interface UserApiResponseData { id: string; firstName: string; lastName: string; @@ -20,7 +24,7 @@ export interface UserApiResponse { authorizations: Authorization[]; } -export function mapUserApiResponseToUser(data: UserApiResponse): User { +export function mapUserApiResponseToUser(data: UserApiResponseData): User { const { id, firstName, lastName, ssn, organizations, authorizations } = data; return { id, diff --git a/apps/dafa-web/src/app/interceptors/custom-error-handler.module.ts b/apps/dafa-web/src/app/interceptors/custom-error-handler.module.ts index 37848a4..91d6073 100644 --- a/apps/dafa-web/src/app/interceptors/custom-error-handler.module.ts +++ b/apps/dafa-web/src/app/interceptors/custom-error-handler.module.ts @@ -8,6 +8,7 @@ export class CustomErrorHandler implements ErrorHandler { handleError(error: any): void { const customError: CustomError = errorToCustomError(error); + console.error(error); this.errorService.add(customError); } } diff --git a/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts b/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts index 4f05197..511cc3e 100644 --- a/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts +++ b/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts @@ -14,7 +14,7 @@ const routes: Routes = [ loadChildren: () => import('./pages/employees/employees.module').then(m => m.EmployeesModule), }, { - path: 'personal/:id', + path: 'personal/:employeeId', loadChildren: () => import('./pages/employee-card/employee-card.module').then(m => m.EmployeeCardModule), }, { @@ -22,7 +22,7 @@ const routes: Routes = [ loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule), }, { - path: 'redigera-konto/:id', + path: 'redigera-konto/:employeeId', loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule), }, ]; diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-card/employee-card.component.ts b/apps/dafa-web/src/app/pages/administration/pages/employee-card/employee-card.component.ts index d7d3a59..bf0e935 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employee-card/employee-card.component.ts +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-card/employee-card.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { UnsubscribeDirective } from '@dafa-directives/unsubscribe.directive'; import { Employee } from '@dafa-models/employee.model'; import { Participant } from '@dafa-models/participant.model'; import { EmployeeService } from '@dafa-services/api/employee.service'; import { BehaviorSubject, Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; @Component({ selector: 'dafa-employee-card', @@ -12,20 +12,15 @@ import { BehaviorSubject, Observable } from 'rxjs'; styleUrls: ['./employee-card.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class EmployeeCardComponent extends UnsubscribeDirective { - detailedEmployeeData$: Observable; - authorizationsAsString$: Observable; +export class EmployeeCardComponent { private _pendingSelectedParticipants$ = new BehaviorSubject([]); + private _employeeId$: Observable = this.activatedRoute.params.pipe(map(({ employeeId }) => employeeId)); + authorizationsAsString$: Observable; + detailedEmployeeData$: Observable = this._employeeId$.pipe( + switchMap(employeeId => this.employeeService.fetchDetailedEmployeeData$(employeeId)) + ); - constructor(private activatedRoute: ActivatedRoute, private employeeService: EmployeeService) { - super(); - - super.unsubscribeOnDestroy( - this.activatedRoute.params.subscribe(({ id }) => { - this.detailedEmployeeData$ = this.employeeService.getDetailedEmployeeData(id); - }) - ); - } + constructor(private activatedRoute: ActivatedRoute, private employeeService: EmployeeService) {} get pendingSelectedParticipants(): string[] { return this._pendingSelectedParticipants$.getValue(); diff --git a/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html b/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html index 820f993..6f1c769 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html +++ b/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html @@ -6,27 +6,45 @@ @@ -34,7 +52,7 @@ - + {{ employee.fullName }} @@ -64,8 +82,8 @@ [afTotalPages]="totalPages" [afCurrentResultStart]="currentResultStart" [afCurrentResultEnd]="currentResultEnd" - [afTotalResults]="employees.length" - (afOnPageChange)="handlePagination($event.detail)" + [afTotalResults]="count" + (afOnPageChange)="setNewPage($event.detail)" af-result-name="medarbetare" > diff --git a/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts b/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts index 634f8c1..8d0d1d8 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts +++ b/apps/dafa-web/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts @@ -1,7 +1,8 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { SortOrder } from '@dafa-enums/sort-order.enum'; import { Employee } from '@dafa-models/employee.model'; -import { SortBy } from '@dafa-models/sort-by.model'; -import { BehaviorSubject } from 'rxjs'; +import { PaginationMeta } from '@dafa-models/pagination-meta.model'; +import { Sort } from '@dafa-models/sort.model'; @Component({ selector: 'dafa-employees-list', @@ -11,37 +12,36 @@ import { BehaviorSubject } from 'rxjs'; }) export class EmployeesListComponent { @Input() employees: Employee[]; - @Input() sortBy: SortBy | null; + @Input() meta: PaginationMeta; + @Input() sort: Sort; + @Input() order: SortOrder; @Output() sorted = new EventEmitter(); + @Output() paginated = new EventEmitter(); - private _currentPage$ = new BehaviorSubject(1); - private _employeesPerPage = 10; + orderType = SortOrder; get currentPage(): number { - return this._currentPage$.getValue(); + return this.meta.page; } - get totalPages(): number { - return Math.ceil(this.employees.length / this._employeesPerPage); + return this.meta?.totalPages; } - - get pagedEmployees(): Employee[] { - return [...this.employees].slice(this.currentResultStart - 1, this.currentResultEnd); + get count(): number { + return this.meta.count; } - get currentResultStart(): number { - return (this.currentPage - 1) * this._employeesPerPage + 1; + return (this.currentPage - 1) * this.meta.limit + 1; } - get currentResultEnd(): number { - return this.currentResultStart + this._employeesPerPage - 1; + const end = this.currentResultStart + this.meta.limit - 1; + return end < this.count ? end : this.count; } handleSort(key: keyof Employee): void { this.sorted.emit(key); } - handlePagination(page: number): void { - this._currentPage$.next(page); + setNewPage(page: number): void { + this.paginated.emit(page); } } diff --git a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html index 63395be..cb560d9 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html +++ b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html @@ -22,10 +22,13 @@ diff --git a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.ts b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.ts index 4189f63..47a1c66 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.ts +++ b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { IconType } from '@dafa-enums/icon-type.enum'; -import { Employee } from '@dafa-models/employee.model'; -import { SortBy } from '@dafa-models/sort-by.model'; +import { Employee, EmployeesData } from '@dafa-models/employee.model'; +import { Sort } from '@dafa-models/sort.model'; import { EmployeeService } from '@dafa-services/api/employee.service'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -13,8 +13,8 @@ import { BehaviorSubject, Observable } from 'rxjs'; }) export class EmployeesComponent { private _searchValue$ = new BehaviorSubject(''); - filteredEmployees$: Observable = this.employeeService.filteredEmployees$; - employeesSortBy$: Observable = this.employeeService.employeesSortBy$; + employeesData$: Observable = this.employeeService.employeesData$; + sort$: Observable> = this.employeeService.sort$; iconType = IconType; constructor(private employeeService: EmployeeService) {} @@ -32,6 +32,10 @@ export class EmployeesComponent { } handleEmployeesSort(key: keyof Employee): void { - this.employeeService.setEmployeesSortKey(key); + this.employeeService.setSort(key); + } + + setNewPage(page: number): void { + this.employeeService.setPage(page); } } diff --git a/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.html b/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.html index f660dec..517ab7d 100644 --- a/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.html +++ b/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.html @@ -6,8 +6,14 @@ @@ -15,8 +21,14 @@ @@ -24,8 +36,14 @@ @@ -33,8 +51,14 @@ @@ -42,8 +66,14 @@ @@ -51,8 +81,14 @@ diff --git a/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.ts b/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.ts index 426e95a..7f36896 100644 --- a/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.ts +++ b/apps/dafa-web/src/app/pages/participants/components/participants-list/participants-list.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { Participant } from '@dafa-models/participant.model'; -import { SortBy } from '@dafa-models/sort-by.model'; +import { Sort } from '@dafa-models/sort.model'; import { BehaviorSubject } from 'rxjs'; @Component({ @@ -11,7 +11,7 @@ import { BehaviorSubject } from 'rxjs'; }) export class ParticipantsListComponent { @Input() participants: Participant[]; - @Input() sortBy: SortBy | null; + @Input() sortBy: Sort | null; @Output() sorted = new EventEmitter(); private _currentPage$ = new BehaviorSubject(1); diff --git a/apps/dafa-web/src/app/pages/participants/participants.component.ts b/apps/dafa-web/src/app/pages/participants/participants.component.ts index d070f09..6a2a31f 100644 --- a/apps/dafa-web/src/app/pages/participants/participants.component.ts +++ b/apps/dafa-web/src/app/pages/participants/participants.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Participant } from '@dafa-models/participant.model'; -import { SortBy } from '@dafa-models/sort-by.model'; +import { Sort } from '@dafa-models/sort.model'; import { ParticipantsService } from '@dafa-services/api/participants.service'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -14,8 +14,10 @@ export class ParticipantsComponent { private _searchValue$ = new BehaviorSubject(''); activeParticipants$: Observable = this.participantsService.activeParticipants$; followUpParticipants$: Observable = this.participantsService.followUpParticipants$; - activeParticipantsSortBy$: Observable = this.participantsService.activeParticipantsSortBy$; - followUpParticipantsSortBy$: Observable = this.participantsService.followUpParticipantsSortBy$; + activeParticipantsSortBy$: Observable | null> = this.participantsService + .activeParticipantsSortBy$; + followUpParticipantsSortBy$: Observable | null> = this.participantsService + .followUpParticipantsSortBy$; constructor(private participantsService: ParticipantsService) {} diff --git a/apps/dafa-web/src/app/services/api/authorizations.service.ts b/apps/dafa-web/src/app/services/api/authorizations.service.ts index 0db6ed8..eb46f3b 100644 --- a/apps/dafa-web/src/app/services/api/authorizations.service.ts +++ b/apps/dafa-web/src/app/services/api/authorizations.service.ts @@ -17,8 +17,10 @@ const API_HEADERS = { headers: environment.api.headers }; export class AuthorizationService { private _authorizationsApiUrl = `${environment.api.url}/authorizations`; public authorizations$: Observable = this.httpClient - .get(this._authorizationsApiUrl, API_HEADERS) - .pipe(map(response => response.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization)))); + .get(this._authorizationsApiUrl, API_HEADERS) + .pipe( + map(response => response.data.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization))) + ); constructor(private httpClient: HttpClient) {} } diff --git a/apps/dafa-web/src/app/services/api/employee.service.ts b/apps/dafa-web/src/app/services/api/employee.service.ts index 22db3b1..9b6670c 100644 --- a/apps/dafa-web/src/app/services/api/employee.service.ts +++ b/apps/dafa-web/src/app/services/api/employee.service.ts @@ -1,79 +1,83 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ErrorType } from '@dafa-enums/error-type.enum'; +import { SortOrder } from '@dafa-enums/sort-order.enum'; import { environment } from '@dafa-environment'; import { Employee, EmployeeApiResponse, + EmployeesApiResponse, + EmployeesData, mapEmployeeReponseToEmployee, mapEmployeeToEmployeeApiRequestData, } from '@dafa-models/employee.model'; -import { SortBy } from '@dafa-models/sort-by.model'; -import { sort } from '@dafa-utils/sort.util'; +import { Sort } from '@dafa-models/sort.model'; import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; - -function filterEmployees(employees: Employee[], searchFilter: string): Employee[] { - return employees.filter(person => person.fullName.toLowerCase().includes(searchFilter.toLowerCase())); -} +import { catchError, map, switchMap } from 'rxjs/operators'; const API_HEADERS = { headers: environment.api.headers }; + @Injectable({ providedIn: 'root', }) export class EmployeeService { - private _employeeApiUrl = `${environment.api.url}/employee`; - private _employeesRawData: Observable = this.httpClient.get( - this._employeeApiUrl, - API_HEADERS - ); - private _allEmployees$: Observable = this._employeesRawData.pipe( - map(results => results.map(result => mapEmployeeReponseToEmployee(result))) - ); - - private _employeesSortBy$ = new BehaviorSubject({ key: 'fullName', reverse: false }); - public employeesSortBy$: Observable = this._employeesSortBy$.asObservable(); + private _apiUrl = `${environment.api.url}/employee`; + private _limit$ = new BehaviorSubject(20); + private _page$ = new BehaviorSubject(1); + private _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC }); + public sort$: Observable> = this._sort$.asObservable(); private _searchFilter$ = new BehaviorSubject(''); public searchFilter$: Observable = this._searchFilter$.asObservable(); - private _filteredEmployees$: Observable = combineLatest([this._allEmployees$, this._searchFilter$]).pipe( - map(([employees, searchFilter]) => filterEmployees(employees, searchFilter)) + private _fetchEmployees$(limit: number, page: number, sort: Sort): Observable { + return this.httpClient + .get(this._apiUrl, { + ...API_HEADERS, + params: { + sort: sort.key, + order: sort.order, + limit: limit.toString(), + page: page.toString(), + }, + }) + .pipe( + map(({ data, meta }) => { + return { data: data.map(employee => mapEmployeeReponseToEmployee(employee)), meta }; + }) + ); + } + + public employeesData$: Observable = combineLatest([this._limit$, this._page$, this._sort$]).pipe( + switchMap(([limit, page, sort]) => this._fetchEmployees$(limit, page, sort)) ); - public resultCount$: Observable = this._employeesRawData.pipe(map(results => results.length)); // TODO: need META - public filteredEmployees$: Observable = combineLatest([ - this._filteredEmployees$, - this._employeesSortBy$, - ]).pipe( - map(([employees, sortBy]) => { - return sortBy ? sort(employees, sortBy) : employees; - }) - ); + public fetchDetailedEmployeeData$(id: string): Observable { + return this.httpClient + .get(`${this._apiUrl}/${id}`, { ...API_HEADERS }) + .pipe(map(result => mapEmployeeReponseToEmployee(result.data))); + } constructor(private httpClient: HttpClient) {} - public getDetailedEmployeeData(id: string): Observable { - return this.httpClient - .get(`${this._employeeApiUrl}/${id}`, { ...API_HEADERS }) - .pipe(map(result => mapEmployeeReponseToEmployee(result))); - } - public setSearchFilter(value: string) { this._searchFilter$.next(value); } - public setEmployeesSortKey(key: keyof Employee) { - const currentSortBy = this._employeesSortBy$.getValue(); - const reverse = currentSortBy?.key === key ? !currentSortBy.reverse : false; - this._employeesSortBy$.next({ key, reverse }); + public setSort(newSortKey: keyof Employee) { + const currentSort = this._sort$.getValue(); + let order = currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; + + this._sort$.next({ key: newSortKey, order }); + } + + public setPage(page: number) { + this._page$.next(page); } public postNewEmployee(employeeData: Employee): Observable { - return this.httpClient - .post(this._employeeApiUrl, mapEmployeeToEmployeeApiRequestData(employeeData), API_HEADERS) - .pipe( - map(({ id }) => id), - catchError(error => throwError({ message: error, type: ErrorType.API })) - ); + return this.httpClient.post(this._apiUrl, mapEmployeeToEmployeeApiRequestData(employeeData), API_HEADERS).pipe( + map(({ id }) => id), + catchError(error => throwError({ message: error, type: ErrorType.API })) + ); } } diff --git a/apps/dafa-web/src/app/services/api/participants.service.ts b/apps/dafa-web/src/app/services/api/participants.service.ts index 354956f..31dcae1 100644 --- a/apps/dafa-web/src/app/services/api/participants.service.ts +++ b/apps/dafa-web/src/app/services/api/participants.service.ts @@ -1,9 +1,14 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ParticipantStatus } from '@dafa-enums/participant-status.enum'; +import { SortOrder } from '@dafa-enums/sort-order.enum'; import { environment } from '@dafa-environment'; -import { Participant } from '@dafa-models/participant.model'; -import { SortBy } from '@dafa-models/sort-by.model'; +import { + mapParticipantApiResponseToParticipant, + Participant, + ParticipantsApiResponse, +} from '@dafa-models/participant.model'; +import { Sort } from '@dafa-models/sort.model'; import { sort } from '@dafa-utils/sort.util'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -21,13 +26,23 @@ function filterParticipants(participants: Participant[], searchFilter: string): providedIn: 'root', }) export class ParticipantsService { - private _allParticipants$: Observable = this.httpClient.get( - `${environment.api.url}/participants` - ); - private _activeParticipantsSortBy$ = new BehaviorSubject({ key: 'handleBefore', reverse: false }); - public activeParticipantsSortBy$: Observable = this._activeParticipantsSortBy$.asObservable(); - private _followUpParticipantsSortBy$ = new BehaviorSubject({ key: 'handleBefore', reverse: false }); - public followUpParticipantsSortBy$: Observable = this._followUpParticipantsSortBy$.asObservable(); + private _allParticipants$: Observable = this.httpClient + .get(`${environment.api.url}/participants`) + .pipe(map(response => response.data.map(participant => mapParticipantApiResponseToParticipant(participant)))); + private _activeParticipantsSortBy$ = new BehaviorSubject | null>({ + key: 'handleBefore', + order: SortOrder.ASC, + }); + public activeParticipantsSortBy$: Observable< + Sort + > = this._activeParticipantsSortBy$.asObservable(); + private _followUpParticipantsSortBy$ = new BehaviorSubject | null>({ + key: 'handleBefore', + order: SortOrder.ASC, + }); + public followUpParticipantsSortBy$: Observable< + Sort + > = this._followUpParticipantsSortBy$.asObservable(); private _searchFilter$ = new BehaviorSubject(''); public searchFilter$: Observable = this._searchFilter$.asObservable(); @@ -64,14 +79,20 @@ export class ParticipantsService { public setActiveParticipantsSortKey(key: keyof Participant) { const currentSortBy = this._activeParticipantsSortBy$.getValue(); - const reverse = currentSortBy?.key === key ? !currentSortBy.reverse : false; - this._activeParticipantsSortBy$.next({ key, reverse }); + 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) { const currentSortBy = this._followUpParticipantsSortBy$.getValue(); - const reverse = currentSortBy?.key === key ? !currentSortBy.reverse : false; - this._followUpParticipantsSortBy$.next({ key, reverse }); + 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) {} diff --git a/apps/dafa-web/src/app/services/api/service.service.ts b/apps/dafa-web/src/app/services/api/service.service.ts index f99b59d..580944f 100644 --- a/apps/dafa-web/src/app/services/api/service.service.ts +++ b/apps/dafa-web/src/app/services/api/service.service.ts @@ -13,8 +13,8 @@ const API_HEADERS = { headers: environment.api.headers }; export class ServiceService { private _servicesApiUrl = `${environment.api.url}/services`; public services$: Observable = this.httpClient - .get(this._servicesApiUrl, API_HEADERS) - .pipe(map(response => response.map(service => mapServiceApiResponseToService(service)))); + .get(this._servicesApiUrl, API_HEADERS) + .pipe(map(response => response.data.map(service => mapServiceApiResponseToService(service)))); constructor(private httpClient: HttpClient) {} } diff --git a/apps/dafa-web/src/app/services/api/user.service.ts b/apps/dafa-web/src/app/services/api/user.service.ts index 9f4c38d..5ae5705 100644 --- a/apps/dafa-web/src/app/services/api/user.service.ts +++ b/apps/dafa-web/src/app/services/api/user.service.ts @@ -14,7 +14,7 @@ export class UserService { private _userApiUrl = `${environment.api.url}/currentUser`; public currentUser$: Observable = this.httpClient .get(this._userApiUrl, API_HEADERS) - .pipe(map(response => mapUserApiResponseToUser(response))); + .pipe(map(response => mapUserApiResponseToUser(response.data))); constructor(private httpClient: HttpClient) {} } diff --git a/apps/dafa-web/src/app/services/error.service.ts b/apps/dafa-web/src/app/services/error.service.ts index c01f5c9..54654d9 100644 --- a/apps/dafa-web/src/app/services/error.service.ts +++ b/apps/dafa-web/src/app/services/error.service.ts @@ -21,7 +21,6 @@ export class ErrorService { } public add(error: CustomError) { - console.error(error); this.errorQueue$.next([...this.errorQueue$.value, error]); this.appRef.tick(); } diff --git a/apps/dafa-web/src/app/utils/sort.util.ts b/apps/dafa-web/src/app/utils/sort.util.ts index 6cb5bc4..fe5d223 100644 --- a/apps/dafa-web/src/app/utils/sort.util.ts +++ b/apps/dafa-web/src/app/utils/sort.util.ts @@ -1,11 +1,10 @@ -import { SortBy } from '@dafa-models/sort-by.model'; +import { Sort } from '@dafa-models/sort.model'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function sort(data: any[], sortBy: SortBy): any[] { - const reverse = sortBy.reverse ? -1 : 1; +export function sort(data: T[], sort: Sort): T[] { + const reverse = sort.order === 'desc' ? -1 : 1; return [...data].sort((a, b) => { - const first = a[sortBy.key]; - const second = b[sortBy.key]; + const first = a[sort.key]; + const second = b[sort.key]; return reverse * (+(first > second) - +(second > first)); }); diff --git a/mock-api/dafa-web/package.json b/mock-api/dafa-web/package.json index f24c632..5c205e5 100644 --- a/mock-api/dafa-web/package.json +++ b/mock-api/dafa-web/package.json @@ -4,7 +4,7 @@ "description": "A mock api implementing all needed endpoints for dafa-web", "scripts": { "generate-api": "node ./scripts/generate-api.js", - "start": "npm run generate-api && json-server --watch api.json --port 8000 --routes routes.json", + "start": "npm run generate-api && node server.js", "start:delay": "npm start -- --delay 500" }, "author": "Erik Tiekstra (erik.tiekstra@arbetsformedlingen.se)", diff --git a/mock-api/dafa-web/routes.json b/mock-api/dafa-web/routes.json deleted file mode 100644 index debab6d..0000000 --- a/mock-api/dafa-web/routes.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "/api/*": "/$1", - "/participants": "/participants?_embed=employees", - "/participant/:id": "/participants/:id?_embed=employees", - "/employee": "/employees", - "/employee/:id": "/employees/:id" -} diff --git a/mock-api/dafa-web/scripts/employees.js b/mock-api/dafa-web/scripts/employees.js index 9ad31c0..ee6be00 100644 --- a/mock-api/dafa-web/scripts/employees.js +++ b/mock-api/dafa-web/scripts/employees.js @@ -25,6 +25,7 @@ function generateEmployees(amount = 10) { organizations: [ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)]], services: [SERVICES[Math.floor(Math.random() * SERVICES.length)]], authorizations: authorizations.generate(), + createdAt: Date.now(), }; employees.push(person); diff --git a/mock-api/dafa-web/scripts/generate-api.js b/mock-api/dafa-web/scripts/generate-api.js index 0382d0a..99a5a8c 100644 --- a/mock-api/dafa-web/scripts/generate-api.js +++ b/mock-api/dafa-web/scripts/generate-api.js @@ -8,7 +8,7 @@ import organizations from './organizations.js'; import participants from './participants.js'; import services from './services.js'; -const generatedEmployees = employees.generate(10); +const generatedEmployees = employees.generate(50); const apiData = { services: services.generate(), diff --git a/mock-api/dafa-web/server.js b/mock-api/dafa-web/server.js new file mode 100644 index 0000000..e520d3e --- /dev/null +++ b/mock-api/dafa-web/server.js @@ -0,0 +1,61 @@ +import jsonServer from 'json-server'; + +const server = jsonServer.create(); +const router = jsonServer.router('api.json'); +const middlewares = jsonServer.defaults(); + +server.use(middlewares); + +server.use( + jsonServer.rewriter({ + '/api/*': '/$1', + '*sort=services*': '$1sort=services[0].name$2', + '*sort=organizations*': '$1sort=organizations[0].address.city$2', + '*sort=fullName*': '$1sort=firstName,lastName$2', + '/employee*': '/employees$1', + '/participants': '/participants?_embed=employees', + '/participant/:id': '/participants/:id?_embed=employees', + '*page=*': '$1_page=$2', + '*limit=*': '$1_limit=$2', + '*sort=*': '$1_sort=$2', + '*order=*': '$1_order=$2', + }) +); + +router.render = (req, res) => { + const params = new URLSearchParams(req._parsedUrl.query); + + // Add createdAt to the body + if (req.originalMethod === 'POST') { + req.body.createdAt = Date.now(); + } + + res.jsonp({ + data: res.locals.data, + ...appendMetaData(params, res), + }); +}; + +server.use(router); +server.listen(8000, () => { + console.info('JSON Server is running'); +}); + +function appendMetaData(params, res) { + if (params.has('_page')) { + const limit = +params.get('_limit'); + const page = +params.get('_page'); + const count = res.get('X-Total-Count'); + const totalPages = Math.ceil(count / limit); + return { + meta: { + count, + limit, + page, + totalPages, + }, + }; + } + + return null; +}