diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html index caaefb6..73c2ba7 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.html @@ -31,13 +31,17 @@ {{ employee.fullName }} - - {{ service.name }}, + + {{ employee.tjanster[0] }} + (+{{employee.tjanster.length - 1}}) - - {{ organization.address.city }}, + + {{ employee.utforandeVerksamheter[0] }} + (+{{employee.utforandeVerksamheter.length - 1}}) diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts index a9b28d0..1bfccda 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employees/components/employees-list/employees-list.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { SortOrder } from '@msfa-enums/sort-order.enum'; -import { Employee } from '@msfa-models/employee.model'; +import { EmployeeCompact } from '@msfa-models/employee.model'; import { PaginationMeta } from '@msfa-models/pagination-meta.model'; import { Sort } from '@msfa-models/sort.model'; @@ -11,21 +11,21 @@ import { Sort } from '@msfa-models/sort.model'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class EmployeesListComponent { - @Input() employees: Employee[]; + @Input() employees: EmployeeCompact[]; @Input() paginationMeta: PaginationMeta; - @Input() sort: Sort; - @Output() sorted = new EventEmitter(); + @Input() sort: Sort; + @Output() sorted = new EventEmitter(); @Output() paginated = new EventEmitter(); - columnHeaders: { label: string; key: keyof Employee }[] = [ + columnHeaders: { label: string; key: keyof EmployeeCompact }[] = [ { label: 'Namn', key: 'fullName' }, { label: 'Tjänst', - key: 'services', + key: 'tjanster', }, { label: 'Utförandeverksamheter', - key: 'organizations', + key: 'utforandeVerksamheter', }, ]; @@ -52,7 +52,7 @@ export class EmployeesListComponent { return end < this.count ? end : this.count; } - handleSort(key: keyof Employee): void { + handleSort(key: keyof EmployeeCompact): void { this.sorted.emit(key); } 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 8b3dc39..8d2f5aa 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 @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { IconType } from '@msfa-enums/icon-type.enum'; -import { Employee, EmployeesData } from '@msfa-models/employee.model'; +import { EmployeeCompact, EmployeesData } from '@msfa-models/employee.model'; import { Sort } from '@msfa-models/sort.model'; import { EmployeeService } from '@msfa-services/api/employee.service'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -15,7 +15,7 @@ export class EmployeesComponent { private _searchValue$ = new BehaviorSubject(''); private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject(false); employeesData$: Observable = this.employeeService.employeesData$; - sort$: Observable> = this.employeeService.sort$; + sort$: Observable> = this.employeeService.sort$; iconType = IconType; constructor(private employeeService: EmployeeService) {} @@ -32,7 +32,7 @@ export class EmployeesComponent { this._searchValue$.next($event.detail.target.value); } - handleEmployeesSort(key: keyof Employee): void { + handleEmployeesSort(key: keyof EmployeeCompact): void { this.employeeService.setSort(key); } diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/employee.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/employee.response.model.ts new file mode 100644 index 0000000..6dfa418 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/employee.response.model.ts @@ -0,0 +1,25 @@ +import { PaginationMeta } from '@msfa-models/pagination-meta.model'; + +export interface EmployeeCompactResponse { + ciamUserId: string; + name: string; + tjanst: string[]; + utforandeVerksamhet: string[]; +} + +export interface EmployeeResponse { + ciamUserId: string; + firstName: string; + lastName: string; + email: string; + personnummer: string; + roles: string[]; + tjansteKoder: string[]; + utforandeVerksamhetIds: string[]; + adressIds: string[]; +} + +export interface EmployeesApiResponse { + data: EmployeeCompactResponse[]; + meta: PaginationMeta; +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/employee.model.ts b/apps/mina-sidor-fa/src/app/shared/models/employee.model.ts index 31d45a3..00f2ba0 100644 --- a/apps/mina-sidor-fa/src/app/shared/models/employee.model.ts +++ b/apps/mina-sidor-fa/src/app/shared/models/employee.model.ts @@ -1,75 +1,81 @@ -import { Authorization } from '@msfa-enums/authorization.enum'; -import { Organization } from './organization.model'; +import { EmployeeCompactResponse, EmployeeResponse } from './api/employee.response.model'; import { PaginationMeta } from './pagination-meta.model'; -import { Service } from './service.model'; + +export interface EmployeeCompact { + id: string; + fullName: string; + tjanster: string[]; + utforandeVerksamheter: string[]; +} export interface Employee { id: string; firstName: string; lastName: string; - fullName?: string; + email: string; ssn: string; - organizations: Organization[]; - services: Service[]; - authorizations: Authorization[]; - createdAt?: number; -} - -export interface EmployeesApiResponse { - data: Employee[]; - meta: PaginationMeta; + roles: string[]; + tjanstCodes: string[]; + utforandeVerksamhetIds: string[]; + utforandeAdressIds: string[]; } export interface EmployeesData { - data: Employee[]; + data: EmployeeCompact[]; meta: PaginationMeta; } -export interface EmployeeApiResponse { - data: Employee; +export interface EmployeeRequestData { + email: string; + roles: string[]; + tjansteKoder: string[]; + utforandeVerksamhetIds: string[]; + adressIds: string[]; } -export interface EmployeeApiResponseData { - id: string; - firstName: string; - lastName: string; - ssn: string; - organizations: Organization[]; - authorizations: Authorization[]; - services: Service[]; -} +export function mapEmployeeToRequestData(data: Employee): EmployeeRequestData { + const { email, roles, tjanstCodes, utforandeVerksamhetIds, utforandeAdressIds } = data; -export interface EmployeeApiRequestData { - firstName: string; - lastName: string; - ssn: string; - organizations: Organization[]; - services: Service[]; - authorizations: Authorization[]; -} - -export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApiRequestData { - const { firstName, lastName, ssn, services, organizations, authorizations } = data; return { - firstName, - lastName, - ssn, - services, - organizations, - authorizations, + email, + roles, + tjansteKoder: tjanstCodes, + utforandeVerksamhetIds, + adressIds: utforandeAdressIds, }; } -export function mapEmployeeReponseToEmployee(data: EmployeeApiResponseData): Employee { - const { id, firstName, lastName, ssn, services, organizations, authorizations } = data; +export function mapResponseToEmployeeCompact(data: EmployeeCompactResponse): EmployeeCompact { + const { ciamUserId, name, tjanst, utforandeVerksamhet } = data; return { - id, - firstName, - lastName, - fullName: `${firstName} ${lastName}`, - organizations, - authorizations, - services, - ssn, + id: ciamUserId, + fullName: name, + tjanster: tjanst || [], + utforandeVerksamheter: utforandeVerksamhet || [], + }; +} + +export function mapResponseToEmployee(data: EmployeeResponse): Employee { + const { + ciamUserId, + firstName, + lastName, + email, + personnummer, + roles, + tjansteKoder, + utforandeVerksamhetIds, + adressIds, + } = data; + return { + id: ciamUserId, + firstName, + lastName, + email, + ssn: personnummer, + roles, + tjanstCodes: tjansteKoder, + utforandeVerksamhetIds, + utforandeAdressIds: adressIds, }; } 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 index 2ed876a..f35237f 100644 --- 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 @@ -4,14 +4,15 @@ 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 { EmployeeResponse, EmployeesApiResponse } from '@msfa-models/api/employee.response.model'; import { EmployeeInviteMockaData } from '@msfa-models/employee-invite-mock-data.model'; import { Employee, - EmployeeApiResponse, - EmployeesApiResponse, + EmployeeCompact, EmployeesData, - mapEmployeeReponseToEmployee, - mapEmployeeToEmployeeApiRequestData, + mapEmployeeToRequestData, + mapResponseToEmployee, + mapResponseToEmployeeCompact, } from '@msfa-models/employee.model'; import { Sort } from '@msfa-models/sort.model'; import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs'; @@ -23,11 +24,11 @@ const API_HEADERS = { headers: environment.api.headers }; providedIn: 'root', }) export class EmployeeService { - private _apiUrl = `${environment.api.url}/employee`; + private _apiUrl = `${environment.api.url}/users`; 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 _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC }); + public sort$: Observable> = this._sort$.asObservable(); private _searchFilter$ = new BehaviorSubject(''); private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject(false); @@ -46,7 +47,7 @@ export class EmployeeService { private _fetchEmployees$( limit: number, page: number, - sort: Sort, + sort: Sort, searchFilter: string, onlyEmployeesWithoutAuthorization?: boolean ): Observable { @@ -72,15 +73,15 @@ export class EmployeeService { }) .pipe( map(({ data, meta }) => { - return { data: data.map(employee => mapEmployeeReponseToEmployee(employee)), meta }; + return { data: data.map(employee => mapResponseToEmployeeCompact(employee)), meta }; }) ); } public fetchDetailedEmployeeData$(id: string): Observable { return this.httpClient - .get(`${this._apiUrl}/${id}`, { ...API_HEADERS }) - .pipe(map(result => mapEmployeeReponseToEmployee(result.data))); + .get<{ data: EmployeeResponse }>(`${this._apiUrl}/${id}`, { ...API_HEADERS }) + .pipe(map(result => mapResponseToEmployee(result.data))); } constructor(private httpClient: HttpClient) {} @@ -93,7 +94,7 @@ export class EmployeeService { this._onlyEmployeesWithoutAuthorization$.next(value); } - public setSort(newSortKey: keyof Employee): void { + public setSort(newSortKey: keyof EmployeeCompact): void { const currentSort = this._sort$.getValue(); const order = currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; @@ -106,17 +107,15 @@ export class EmployeeService { } public postNewEmployee(employeeData: Employee): Observable { - 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 })) - ); + return this.httpClient.post<{ id: string }>(this._apiUrl, mapEmployeeToRequestData(employeeData), API_HEADERS).pipe( + map(({ id }) => id), + catchError(error => throwError({ message: error as string, type: ErrorType.API })) + ); } - postEmployeeInvitation(email: string): Observable { + public postEmployeeInvitation(email: string): Observable { return this.httpClient - .post<{ data: EmployeeInviteMockApiResponse }>('http://localhost:8000/invites', { email }, API_HEADERS) + .post<{ data: EmployeeInviteMockApiResponse }>(`${this._apiUrl}/invite`, { email }, API_HEADERS) .pipe( take(1), map(res => res.data), diff --git a/mock-api/mina-sidor-fa/scripts/employees.js b/mock-api/mina-sidor-fa/scripts/employees.js index a90a580..958eeae 100644 --- a/mock-api/mina-sidor-fa/scripts/employees.js +++ b/mock-api/mina-sidor-fa/scripts/employees.js @@ -1,7 +1,7 @@ import faker from 'faker'; -import authorizations from './authorizations.js'; import organizations from './organizations.js'; import tjanster from './tjanster.js'; +import chooseRandom from './utils/choose-random.util.js'; faker.locale = 'sv'; @@ -12,24 +12,31 @@ function generateEmployees(amount = 10) { const employees = []; for (let i = 1; i <= amount; ++i) { - const person = { - id: faker.datatype.uuid(), - firstName: faker.name.firstName(), - lastName: faker.name.lastName(), - ssn: `${faker.date.between('1950', '2000').toISOString().split('T')[0].replace(/-/g, '')}-${faker.datatype.number( - { - min: 1000, - max: 9999, - } - )}`, - organizations: [ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)]], - services: [TJANSTER[Math.floor(Math.random() * TJANSTER.length)]], - authorizations: i % 2 === 0 ? authorizations.generate() : [], - createdAt: Date.now(), + const firstName = faker.name.firstName(); + const lastName = faker.name.lastName(); + const currentTjanster = chooseRandom(TJANSTER, faker.datatype.number({ min: 1, max: TJANSTER.length })); + const currentOrganizations = chooseRandom(ORGANIZATIONS, faker.datatype.number({ min: 1, max: 5 })); + + const employee = { + ciamUserId: faker.datatype.uuid(), + firstName, + lastName, + name: `${firstName} ${lastName}`, + personnummer: `${faker.date + .between('1950', '2000') + .toISOString() + .split('T')[0] + .replace(/-/g, '')}-${faker.datatype.number({ + min: 1000, + max: 9999, + })}`, + tjanst: currentTjanster.map(tjanst => tjanst.name), + tjansteKoder: currentTjanster.map(tjanst => tjanst.code), + utforandeVerksamhet: currentOrganizations.map(organization => organization.name), + utforandeVerksamhetIds: currentOrganizations.map(organization => organization.id), }; - person.fullName = `${person.firstName} ${person.lastName}`; - employees.push(person); + employees.push(employee); } console.info('Employees generated...'); diff --git a/mock-api/mina-sidor-fa/scripts/tjanster.js b/mock-api/mina-sidor-fa/scripts/tjanster.js index ab45273..6712748 100644 --- a/mock-api/mina-sidor-fa/scripts/tjanster.js +++ b/mock-api/mina-sidor-fa/scripts/tjanster.js @@ -8,10 +8,10 @@ function generateTjanster() { code: faker.datatype.uuid(), name: 'Kundval Rusta och matcha', }, - { - code: faker.datatype.uuid(), - name: 'Karriärvägledning', - }, + // { + // code: faker.datatype.uuid(), + // name: 'Karriärvägledning', + // }, // { // code: faker.datatype.uuid(), // name: 'STOM', diff --git a/mock-api/mina-sidor-fa/server.js b/mock-api/mina-sidor-fa/server.js index eab47fc..79905de 100644 --- a/mock-api/mina-sidor-fa/server.js +++ b/mock-api/mina-sidor-fa/server.js @@ -9,13 +9,15 @@ 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=utforandeVerksamhet*': '$1sort=utforandeverksamhet$2', - '*sort=tjanst*': '$1sort=tjansteNamn$2', - '/employee*search=*': '/employee$1fullName_like=$2', - '/employee*onlyEmployeesWithoutAuthorization=*': '/employee$1authorizations.length_gte=1', - '/employee*': '/employees$1', + '*sort=fullName*': '$1sort=name$2', + '*sort=utforandeVerksamheter*': '$1sort=utforandeVerksamhet[0]$2', + '*sort=tjanster*': '$1sort=tjanst[0]$2', + '/users/:id': '/employees?ciamUserId=:id', + '/users*': '/employees$1', + '/employees*search=*': '/employees$1fullName_like=$2', + '/employees*onlyEmployeesWithoutAuthorization=*': '/employees$1authorizations.length_gte=1', + '/employees/invite': '/invites', + '/employees*': '/employees$1', '/participants': '/participants?_embed=employees', '/participant/:id': '/participants/:id?_embed=employees', '/auth/userinfo': '/currentUser',