feat(employee): Now possible to submit edit employee form. (TV-513)

Squashed commit of the following:

commit fc79d20601988735b407a54009426e10e233df39
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 13:24:30 2021 +0200

    Small fixes

commit bc8741bab9b3161d356d1375b7ab869b202a8195
Merge: 1ad9db8 cac0515
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 13:16:21 2021 +0200

    Merged develop

commit 1ad9db87e7f1f19fdb2cd3e2cfcc17e08c9ccfef
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 12:49:37 2021 +0200

    Stylechanges

commit 5a3b863f87f956ed90564fecbef9abe9460558bd
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 11:26:35 2021 +0200

    Now possible to edit employee-accounts

commit 0677f2ebeee12adeba6cb65a673781dea7a6c06d
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 10:05:35 2021 +0200

    Updated models to include new structure

commit 76bde9e0afbbf44a09348ee8e4849b4816b0c39d
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Sep 7 08:43:25 2021 +0200

    edit employee

commit c8cf93b6bbeef0a3b4b6f141f36aea7ea86f399e
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 16:22:46 2021 +0200

    WIP

commit fe5cff4aa209c4da0025ebfb70b79d6a95c8d1f1
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 15:46:26 2021 +0200

    Fixed issue not fetching new utforande verksamheter on page load

commit c997dbe80c8f9f5ec84aad2ac18805918b7ab6f5
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 15:35:34 2021 +0200

    Added scrollPositionRestoration

commit 58ee0147ff553f2a97fdf143ddcbdf1b0241d26f
Merge: 27b595c 9a73ffe
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 15:30:08 2021 +0200

    Merge branch 'develop' into feature/TV-513

commit 27b595cbf94dc1d95ad32a0da1943e2e6fce61c4
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 14:28:10 2021 +0200

    WIP

commit 81cb9df1869784a80f6747ac68bfdc81ae5d777a
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Sep 6 13:21:41 2021 +0200

    Fixed roles
This commit is contained in:
Erik Tiekstra
2021-09-07 13:26:46 +02:00
parent cac0515c9c
commit bb4d88a8e0
37 changed files with 386 additions and 359 deletions

View File

@@ -45,7 +45,7 @@ export class LayoutComponent extends UnsubscribeDirective {
const pageTitle = this.activatedRoute?.snapshot?.data?.title as string;
const urlTree = this.router.parseUrl(this.router.url);
this.titleService.setTitle(`Mina sidor FA${pageTitle ? ` - ${pageTitle}` : ''}`);
this.titleService.setTitle(`Mina sidor för fristående aktörer${pageTitle ? ` - ${pageTitle}` : ''}`);
if (urlTree.queryParams.code) {
void this.router.navigate([], {

View File

@@ -1,5 +1,6 @@
@import 'variables/colors';
@import 'functions/rem';
@import 'mixins/list';
@import 'variables/colors';
@import 'variables/gutters';
.expanded-tree-node {
@@ -50,8 +51,8 @@
&__node {
&--leaf {
margin-top: 0.625rem;
padding-left: 0.625rem;
margin-top: $digi--layout--gutter--s;
padding-left: $digi--layout--gutter;
&:first-child {
margin-top: 0;
@@ -60,7 +61,7 @@
}
&__node-checkbox-presentation {
font-size: 1rem;
font-size: var(--digi--typography--font-size--s);
font-weight: 400;
display: block;
position: relative;
@@ -80,7 +81,7 @@
}
&--toggle-all {
margin: 1.25rem 0.625rem;
margin: 1.25rem $digi--layout--gutter;
}
&--checked &__box {
@@ -116,7 +117,7 @@
background-color: transparent;
align-items: center;
white-space: nowrap;
font-size: 0.875rem;
font-size: var(--digi--typography--font-size--s);
text-align: center;
position: relative;
@@ -125,11 +126,6 @@
background-color: var(--digi--ui--color--background--secondary);
}
&--focus {
border-top: 1px solid var(--digi--typography--color--text--disabled);
border-bottom: 1px solid var(--digi--typography--color--text--disabled);
}
&--active {
background-color: var(--digi--ui--color--primary);
color: var(--digi--ui--color--background);
@@ -143,7 +139,8 @@
&__text {
text-align: left;
flex-grow: 1;
max-width: 250px;
max-width: rem(450);
margin-right: $digi--layout--gutter--s;
display: block;
overflow: hidden;
white-space: nowrap;
@@ -155,7 +152,7 @@
height: 1rem;
border-radius: 50%;
background-color: var(--digi--ui--color--success);
margin-right: 10px;
margin-right: $digi--layout--gutter--s;
}
&__icon {

View File

@@ -1,13 +1,15 @@
@import 'variables/colors';
@import 'functions/rem';
@import 'mixins/list';
@import 'variables/colors';
@import 'variables/gutters';
@import 'variables/shadows';
.tree-nodes-selector-panel {
position: relative;
background-color: var(--digi--ui--color--background);
min-width: 710px;
min-height: 144px;
box-shadow: 0 0.2rem 0.6rem 0 var(--digi--ui--color--shadow);
border-radius: 4px;
min-width: rem(710);
box-shadow: $msfa__shadow;
border-radius: var(--digi--ui--border--radius);
header {
margin: 0;
@@ -18,13 +20,11 @@
&__heading {
width: 100%;
height: 45px;
display: flex;
align-items: center;
margin: 0;
font-weight: 700;
font-size: 1rem;
padding: 0.625rem 0.9375rem;
font-size: var(--digi--typography--font-size--h4);
padding: $digi--layout--gutter--m $digi--layout--gutter;
border-bottom: 1px solid var(--digi--typography--color--text--disabled);
}
@@ -34,12 +34,11 @@
right: 0;
top: 0;
display: block;
background-image: none;
background-color: transparent;
border: 0 none transparent;
border-width: 0;
color: var(--digi--ui--color--danger);
padding: 0.625rem 0.9375rem;
font-size: 0.875rem;
padding: $digi--layout--gutter--m $digi--layout--gutter;
font-size: var(--digi--typography--font-size--xs);
}
&__expanded-nodes {
@@ -57,11 +56,12 @@
flex: 0 0 50%;
max-width: 50%;
overflow: auto;
padding-top: 15px;
padding-top: $digi--layout--gutter--m;
padding-bottom: $digi--layout--gutter--l;
border-left: 1px solid var(--digi--typography--color--text--disabled);
&:first-child {
border-left: 0 none transparent;
border-left-width: 0;
}
}
@@ -70,6 +70,6 @@
justify-content: center;
align-items: center;
background-color: var(--digi--ui--color--background--tertiary);
height: 60px;
padding: $digi--layout--gutter--m $digi--layout--gutter--l;
}
}

View File

@@ -1,13 +1,13 @@
import { ButtonSize } from '@af/digi-ng/_button/button';
import {
Component,
OnInit,
ChangeDetectionStrategy,
Input,
Component,
ElementRef,
ViewChild,
Output,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import {
FilterTreeNodeData,
@@ -24,7 +24,7 @@ import {
export class TreeNodesSelectorPanelComponent implements OnInit {
@Input() rootNode: TreeNodeModel | null = null;
@Input() headingText: string;
@Input() confirmationButtonText = 'Stäng';
@Input() confirmationButtonText = 'Spara';
@Output() selectedChangesConfirmed = new EventEmitter<TreeNodeModel>();
@Output() closePanelRequested = new EventEmitter<void>();

View File

@@ -8,17 +8,19 @@
}
&__toggle-panel-btn {
position: relative;
display: flex;
border-radius: 0.375rem;
border: 1px solid var(--digi--ui--input--border--color);
background-color: var(--digi--ui--color--background);
color: var(--digi--ui--color--primary);
padding: 6px 12px;
width: 100%;
justify-content: space-between;
align-items: center;
font-size: var(--digi--typography--font-size--xs);
appearance: none;
width: 100%;
height: var(--digi--ui--input--height);
padding: var(--digi--ui--input--padding);
border: 0.0625rem solid var(--digi--ui--input--border--color);
cursor: pointer;
font-size: var(--digi--typography--font-size--m);
color: var(--digi--ui--color--background--overlay--opaque);
background-color: var(--digi--ui--color--background);
border-color: var(--digi-form-select--border-color);
&:focus {
border-color: var(--digi--ui--color--focus--light);
@@ -60,5 +62,6 @@
z-index: 10;
top: 100%;
left: 0;
margin-top: $digi--layout--gutter--xs;
}
}

View File

@@ -1,14 +1,14 @@
import {
Component,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
forwardRef,
Input,
Output,
Renderer2,
ViewChild,
ElementRef,
Output,
EventEmitter,
ChangeDetectorRef,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UUID } from 'angular2-uuid';
@@ -37,7 +37,7 @@ interface PropagateTouchedFn {
})
export class TreeNodesSelectorComponent implements ControlValueAccessor {
@Input() headingText: string;
@Input() confirmationButtonText = 'Stäng';
@Input() confirmationButtonText = 'Spara';
@Input() isInvalid = false;
@Input() showValidation = false;
@Input() validationMessages: Array<string>;

View File

@@ -2,5 +2,5 @@ export enum RoleEnum {
MSFA_AuthAdmin = 'MSFA_AuthAdmin',
MSFA_ReceiveDeltagare = 'MSFA_ReceiveDeltagare',
MSFA_ReportAndPlanning = 'MSFA_ReportAndPlanning',
// MSFA_Standard = 'MSFA_Standard', // Default role
MSFA_Standard = 'MSFA_Standard', // Default role
}

View File

@@ -1,4 +1,4 @@
export interface AvropTjanstResponse {
export interface AvropFilterResponse {
id: string;
label: string;
count: number;

View File

@@ -0,0 +1,10 @@
import { RoleEnum } from '@msfa-enums/role.enum';
export interface EmployeeEditRequest {
email: string;
roles: RoleEnum[];
tjanstIds: number[];
allaUtforandeVerksamheter: boolean;
utforandeVerksamhetIds?: number[];
adressIds: number[];
}

View File

@@ -1,5 +1,18 @@
import { RoleEnum } from '@msfa-enums/role.enum';
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
import { TjanstResponse } from './tjanst.response.model';
interface UtforandeVerksamhetResponse {
id: number;
name: string;
allaAdresser: boolean;
adresser?: AdressResponse[];
}
interface AdressResponse {
id: number;
name: string;
}
export interface EmployeeCompactResponse {
ciamUserId: string;
@@ -14,16 +27,19 @@ export interface EmployeeResponse {
firstName: string;
lastName: string;
email: string;
personnummer: string;
ssn: string;
roles: RoleEnum[];
tjansteKoder: string[];
tjanster: TjanstResponse[];
allaUtforandeVerksamheter: boolean;
utforandeVerksamhet: string[];
utforandeVerksamheter: UtforandeVerksamhetResponse[];
// Will be removed
tjansteKoder: string[];
utforandeVerksamhetIds: number[];
adressIds: number[];
}
export interface EmployeesApiResponse {
export interface EmployeesDataResponse {
data: EmployeeCompactResponse[];
meta: PaginationMeta;
}

View File

@@ -1,5 +0,0 @@
export interface KommunResponse {
id: string;
label: string;
count: number;
}

View File

@@ -1,6 +1,6 @@
export interface TjanstResponse {
id: string;
name: string;
tjanstekod: string;
tjanstId: number;
id?: string;
tjanstId?: number;
}

View File

@@ -1,5 +0,0 @@
export interface UtforandeVerksamhetResponse {
id: string;
label: string;
count: number;
}

View File

@@ -0,0 +1,17 @@
import { AvropFilterResponse } from './api/avrop-filter.response.model';
export interface AvropFilter {
id: string;
label: string;
count: number;
}
export function mapResponseToAvropFilter(data: AvropFilterResponse): AvropFilter {
const { id, label, count } = data;
return {
id,
label,
count,
};
}

View File

@@ -1,17 +0,0 @@
import { AvropTjanstResponse } from './api/avrop-tjanst.response.model';
export interface AvropTjanst {
id: string;
label: string;
count: number;
}
export function mapResponseToAvropTjanst(data: AvropTjanstResponse): AvropTjanst {
const { id, label, count } = data;
return {
id,
label,
count,
};
}

View File

@@ -1,7 +1,21 @@
import { RoleEnum } from '@msfa-enums/role.enum';
import { EmployeeCompactResponse, EmployeeResponse } from './api/employee.response.model';
import { PaginationMeta } from './pagination-meta.model';
import { Tjanst } from './tjanst.model';
import { mapResponseToTjanst, Tjanst } from './tjanst.model';
const CURRENT_YEAR = new Date().getFullYear().toString().slice(2, 4);
interface UtforandeVerksamhet {
id: number;
name: string;
allaAdresser: boolean;
adresser?: Adress[];
}
interface Adress {
id: number;
name: string;
}
export interface EmployeeCompact {
id: string;
@@ -19,9 +33,11 @@ export interface Employee {
email: string;
ssn: string;
roles: RoleEnum[];
tjanstCodes: string[];
tjanster?: Tjanst[];
tjanster: Tjanst[];
allaUtforandeVerksamheter: boolean;
utforandeVerksamheter: UtforandeVerksamhet[];
tjanstCodes: string[];
utforandeVerksamhetIds: number[];
utforandeAdressIds: number[];
}
@@ -31,26 +47,15 @@ export interface EmployeesData {
meta: PaginationMeta;
}
export interface EmployeeRequestData {
email: string;
roles: string[];
tjansteKoder: string[];
allaUtforandeVerksamheter: boolean;
utforandeVerksamhetIds: number[];
adressIds: number[];
}
export function mapEmployeeToRequestData(data: Employee): EmployeeRequestData {
const { email, roles, tjanstCodes, allaUtforandeVerksamheter, utforandeVerksamhetIds, utforandeAdressIds } = data;
return {
email,
roles,
tjansteKoder: tjanstCodes,
utforandeVerksamhetIds,
adressIds: utforandeAdressIds,
allaUtforandeVerksamheter: allaUtforandeVerksamheter,
};
function mapResponseToSsn(ssn: string): string {
if (ssn.length === 10) {
const century = +CURRENT_YEAR - +ssn.slice(0, 2) > 0 ? '20' : '19';
ssn = ssn.padStart(12, century);
}
if (ssn.length === 12) {
ssn = `${ssn.slice(0, 8)}-${ssn.slice(8, 12)}`;
}
return ssn;
}
export function mapResponseToEmployeeCompact(data: EmployeeCompactResponse): EmployeeCompact {
@@ -70,10 +75,12 @@ export function mapResponseToEmployee(data: EmployeeResponse): Employee {
firstName,
lastName,
email,
personnummer,
ssn,
roles,
tjansteKoder,
tjanster,
allaUtforandeVerksamheter,
utforandeVerksamheter,
tjansteKoder,
utforandeVerksamhetIds,
adressIds,
} = data;
@@ -83,10 +90,12 @@ export function mapResponseToEmployee(data: EmployeeResponse): Employee {
lastName,
fullName: `${firstName} ${lastName}`,
email,
ssn: personnummer,
ssn: ssn ? mapResponseToSsn(ssn) : null,
roles: roles || [],
tjanstCodes: tjansteKoder || [],
tjanster: tjanster?.map(tjanst => mapResponseToTjanst(tjanst)),
allaUtforandeVerksamheter,
utforandeVerksamheter,
tjanstCodes: tjansteKoder || [],
utforandeVerksamhetIds: utforandeVerksamhetIds || [],
utforandeAdressIds: adressIds || [],
};

View File

@@ -1,17 +0,0 @@
import { KommunResponse } from './api/kommun.response.model';
export interface Kommun {
id: string;
label: string;
count: number;
}
export function mapKommunResponseToKommun(data: KommunResponse): Kommun {
const { id, label, count } = data;
return {
id,
label,
count,
};
}

View File

@@ -5,29 +5,31 @@ export interface Role {
type: RoleEnum;
}
export function mapRoleResponseToRoleObject(type: string): Role {
switch (type) {
// case 'MSFA_Standard':
// return {
// name: 'Basanvändare',
// type: RoleEnum[type],
// };
case 'MSFA_ReportAndPlanning':
return {
name: 'Rapportering, planering och information om deltagare',
type: RoleEnum[type],
};
case 'MSFA_ReceiveDeltagare':
return {
name: 'Ta emot deltagare',
type: RoleEnum[RoleEnum[type]],
};
case 'MSFA_AuthAdmin':
return {
name: 'Administrera behörigheter',
type: RoleEnum[RoleEnum[type]],
};
default:
return null;
}
export function mapResponseToRoles(types: RoleEnum[] = []): Role[] {
return types.map(type => {
switch (type) {
case 'MSFA_Standard':
return {
name: 'Basanvändare',
type: RoleEnum[type],
};
case 'MSFA_ReportAndPlanning':
return {
name: 'Rapportering, planering och information om deltagare',
type: RoleEnum[type],
};
case 'MSFA_ReceiveDeltagare':
return {
name: 'Ta emot deltagare',
type: RoleEnum[RoleEnum[type]],
};
case 'MSFA_AuthAdmin':
return {
name: 'Administrera behörigheter',
type: RoleEnum[RoleEnum[type]],
};
default:
return;
}
});
}

View File

@@ -1,10 +1,10 @@
import { TjanstResponse } from './api/tjanst.response.model';
export interface Tjanst {
id: string;
id?: string;
name: string;
code: string;
tjanstId: number;
tjanstId?: number;
}
export function mapResponseToTjanst(data: TjanstResponse): Tjanst {

View File

@@ -1,19 +0,0 @@
import { UtforandeVerksamhetResponse } from './api/utforande-verksamhet.response.model';
export interface UtforandeVerksamhet {
id: string;
label: string;
count: number;
}
export function mapUtforandeVerksamhetResponseToUtforandeVerksamhet(
data: UtforandeVerksamhetResponse
): UtforandeVerksamhet {
const { id, label, count } = data;
return {
id,
label,
count,
};
}

View File

@@ -1,20 +1,13 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@msfa-environment';
import { AvropTjanstResponse } from '@msfa-models/api/avrop-tjanst.response.model';
import { AvropFilterResponse } from '@msfa-models/api/avrop-filter.response.model';
import { AvropApiResponse } from '@msfa-models/api/avrop.response.model';
import { HandledareResponse } from '@msfa-models/api/handledare.response.model';
import { KommunResponse } from '@msfa-models/api/kommun.response.model';
import { Params } from '@msfa-models/api/params.model';
import { UtforandeVerksamhetResponse } from '@msfa-models/api/utforande-verksamhet.response.model';
import { AvropTjanst, mapResponseToAvropTjanst } from '@msfa-models/avrop-tjanst.model';
import { AvropFilter, mapResponseToAvropFilter } from '@msfa-models/avrop-filter.model';
import { AvropCompact, AvropCompactData, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
import { Handledare, mapHandledareResponseToHandledare } from '@msfa-models/handledare.model';
import { Kommun, mapKommunResponseToKommun } from '@msfa-models/kommun.model';
import {
mapUtforandeVerksamhetResponseToUtforandeVerksamhet,
UtforandeVerksamhet,
} from '@msfa-models/utforande-verksamhet.model';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators';
@@ -62,32 +55,30 @@ export class AvropApiService {
);
}
fetchAvailableTjanster$(params: Params): Observable<AvropTjanst[]> {
fetchAvailableTjanster$(params: Params): Observable<AvropFilter[]> {
return this.httpClient
.get<{ data: AvropTjanstResponse[] }>(`${this._apiBaseUrl}/tjanster`, { params })
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/tjanster`, { params })
.pipe(
filter(response => !!response?.data),
map(({ data }) => data.map(tjanster => mapResponseToAvropTjanst(tjanster)))
map(({ data }) => data.map(tjanster => mapResponseToAvropFilter(tjanster)))
);
}
fetchAvailableUtforandeVerksamheter$(params: Params): Observable<UtforandeVerksamhet[]> {
fetchAvailableUtforandeVerksamheter$(params: Params): Observable<AvropFilter[]> {
return this.httpClient
.get<{ data: UtforandeVerksamhetResponse[] }>(`${this._apiBaseUrl}/utforandeverksamheter`, { params })
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/utforandeverksamheter`, { params })
.pipe(
filter(response => !!response?.data),
map(({ data }) =>
data.map(utforandeverksamheter => mapUtforandeVerksamhetResponseToUtforandeVerksamhet(utforandeverksamheter))
)
map(({ data }) => data.map(utforandeverksamheter => mapResponseToAvropFilter(utforandeverksamheter)))
);
}
fetchAvailableKommuner$(params: Params): Observable<Kommun[]> {
fetchAvailableKommuner$(params: Params): Observable<AvropFilter[]> {
return this.httpClient
.get<{ data: KommunResponse[] }>(`${this._apiBaseUrl}/kommuner`, { params })
.get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/kommuner`, { params })
.pipe(
filter(response => !!response?.data),
map(({ data }) => data.map(kommun => mapKommunResponseToKommun(kommun)))
map(({ data }) => data.map(kommun => mapResponseToAvropFilter(kommun)))
);
}

View File

@@ -1,30 +1,29 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
import { ErrorType } from '@msfa-enums/error-type.enum';
import { RoleEnum } from '@msfa-enums/role.enum';
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,
EmployeesApiResponse,
EmployeesDataResponse,
} from '@msfa-models/api/employee.response.model';
import { Params } from '@msfa-models/api/params.model';
import {
Employee,
EmployeesData,
mapEmployeeToRequestData,
mapResponseToEmployee,
mapResponseToEmployeeCompact,
} from '@msfa-models/employee.model';
import { errorToCustomError } from '@msfa-models/error/custom-error';
import { mapRoleResponseToRoleObject, Role } from '@msfa-models/role.model';
import { mapResponseToRoles, Role } from '@msfa-models/role.model';
import { Sort } from '@msfa-models/sort.model';
import { ErrorService } from '@msfa-services/error.service';
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { TjanstService } from './tjanst.service';
@Injectable({
@@ -42,6 +41,8 @@ export class EmployeeService extends UnsubscribeDirective {
public onlyEmployeesWithoutAuthorization$: Observable<boolean> = this._onlyEmployeesWithoutAuthorization$.asObservable();
private _employee$ = new BehaviorSubject<Employee>(null);
public employee$: Observable<Employee> = this._employee$.asObservable();
private _lastUpdatedEmployeeId$ = new BehaviorSubject<string>(null);
public lastUpdatedEmployeeId$: Observable<string> = this._lastUpdatedEmployeeId$.asObservable();
private _lastDeletedEmployee$ = new BehaviorSubject<Employee>(null);
public lastDeletedEmployee$: Observable<Employee> = this._lastDeletedEmployee$.asObservable();
private _employeeToDelete$ = new BehaviorSubject<Employee>(null);
@@ -54,10 +55,14 @@ export class EmployeeService extends UnsubscribeDirective {
) {
super();
super.unsubscribeOnDestroy(
this._currentEmployeeId$
combineLatest([this._currentEmployeeId$, this._lastUpdatedEmployeeId$])
.pipe(
filter(currentEmployeeId => !!currentEmployeeId),
switchMap(currentEmployeeId =>
filter(([currentEmployeeId]) => !!currentEmployeeId),
distinctUntilChanged(
([prevEmployeeId], [currEmployeeId, currLastUpdatedEmployeeId]) =>
!currLastUpdatedEmployeeId && prevEmployeeId === currEmployeeId
),
switchMap(([currentEmployeeId]) =>
combineLatest([this._fetchEmployee$(currentEmployeeId), this.tjanstService.tjanster$]).pipe(
filter(([employee, allTjanster]) => !!(employee && allTjanster?.length)),
map(([employee, allTjanster]) => {
@@ -77,6 +82,29 @@ export class EmployeeService extends UnsubscribeDirective {
.subscribe(employee => {
this._employee$.next(employee as Employee);
})
// this._currentEmployeeId$
// .pipe(
// filter(currentEmployeeId => !!currentEmployeeId),
// switchMap(currentEmployeeId =>
// combineLatest([this._fetchEmployee$(currentEmployeeId), this.tjanstService.tjanster$]).pipe(
// filter(([employee, allTjanster]) => !!(employee && allTjanster?.length)),
// map(([employee, allTjanster]) => {
// const tjanster = [];
// employee.tjanstCodes?.forEach(code => {
// const currentTjanst = allTjanster.find(tjanst => tjanst.code === code);
// if (currentTjanst) {
// tjanster.push(currentTjanst);
// }
// });
// return { ...employee, tjanster };
// })
// )
// )
// )
// .subscribe(employee => {
// this._employee$.next(employee as Employee);
// })
);
}
@@ -100,6 +128,10 @@ export class EmployeeService extends UnsubscribeDirective {
}
}
public resetLastUpdatedEmployeeId(): void {
this._lastUpdatedEmployeeId$.next(null);
}
private _fetchEmployees$(
limit: number,
page: number,
@@ -123,7 +155,7 @@ export class EmployeeService extends UnsubscribeDirective {
}
return this.httpClient
.get<EmployeesApiResponse>(this._apiBaseUrl, { params })
.get<EmployeesDataResponse>(this._apiBaseUrl, { params })
.pipe(
map(({ data, meta }) => {
return { data: data.map(employee => mapResponseToEmployeeCompact(employee)), meta };
@@ -175,14 +207,7 @@ export class EmployeeService extends UnsubscribeDirective {
this._page$.next(page);
}
public postNewEmployee(employeeData: Employee): Observable<string> {
return this.httpClient.post<{ id: string }>(this._apiBaseUrl, mapEmployeeToRequestData(employeeData)).pipe(
map(({ id }) => id),
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
);
}
public postEmployeeInvitation(emails: string[]): Observable<EmployeeInviteResponse> {
public postEmployeeInvitation(emails: string[]): Observable<EmployeeInviteResponse | null> {
return this.httpClient
.patch<{ data: EmployeeInviteResponse }>(`${this._apiBaseUrl}/invite`, { emails })
.pipe(
@@ -195,18 +220,22 @@ export class EmployeeService extends UnsubscribeDirective {
);
}
public updateEmployeeData(employeeData: Employee): Observable<string> {
return; /* this.httpClient.put<{ id: string }>(`${this._apiBaseUrl}/${employeeData.id}`,
mapEmployeeToRequestData(employeeData), API_HEADERS).pipe(
map(({ id }) => id),
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
); */
public updateEmployee$(id: string, data: EmployeeEditRequest): Observable<boolean> {
return this.httpClient.put<boolean>(`${this._apiBaseUrl}/${id}`, data).pipe(
take(1),
tap(() => {
this._employee$.next(null);
this._lastUpdatedEmployeeId$.next(id);
}),
map(() => true),
catchError(error => {
this.errorService.add(errorToCustomError(error));
return of(false);
})
);
}
public get allRoles(): Role[] {
const allAuths: string[] = Object.keys(RoleEnum).filter(item => {
return isNaN(Number(item));
});
return allAuths.map(key => mapRoleResponseToRoleObject(key));
return mapResponseToRoles(Object.keys(RoleEnum) as RoleEnum[]);
}
}

View File

@@ -1,6 +1,7 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@msfa-environment';
import { Params } from '@msfa-models/api/params.model';
import {
TreeNode,
TreeNodesSelectorService,
@@ -27,20 +28,21 @@ export class UtforandeVerksamheterService {
constructor(private treeNodesSelectorService: TreeNodesSelectorService, private httpClient: HttpClient) {}
getUtforandeVerksamheter(tjanstIds: Array<number>): Observable<Array<UtforandeVerksamhet>> {
let params = new HttpParams();
let i: number;
if (!tjanstIds) {
if (!tjanstIds.length) {
return of<Array<UtforandeVerksamhet>>([]);
}
for (i = 0; i < tjanstIds.length; i++) {
params = params.append('tjansteIds', tjanstIds[i].toString());
}
const params: Params = { tjansteIds: tjanstIds.map(tjanstId => tjanstId.toString()) };
return this.httpClient.get<Array<UtforandeVerksamhet>>(`${this.apiBaseUrl}`, { params });
}
getSelectedAdressIdsFromTreeNode(treeNode: TreeNode): number[] {
const selectedUtforandeVerksamheter = this.getSelectedUtforandeVerksamheterFromTreeNode(treeNode);
return selectedUtforandeVerksamheter.map(uv => uv.adresser.map(adress => adress.id)).flat();
}
getTreeNodeDataFromUtforandeVerksamheter(utforandeVerksamhetList: Array<UtforandeVerksamhet>): TreeNode | null {
let treeNode: TreeNode | null = null;

View File

@@ -1,18 +1,18 @@
import { AbstractControl, ValidatorFn } from '@angular/forms';
import { ValidationError } from '@msfa-models/validation-error.model';
export function RequiredValidator(label = 'Fältet'): ValidatorFn {
export function RequiredValidator(label = 'Fältet', arrayValue = false): ValidatorFn {
return (control: AbstractControl): ValidationError => {
if (control) {
if (!control.value) {
return { type: 'required', message: `${label} är obligatoriskt` };
if ((arrayValue && !control.value) || (Array.isArray(control.value) && !control.value.length)) {
return { type: 'required', message: `Minst ${label} behöver väljas` };
}
if (Array.isArray(control.value) && !control.value.length) {
return { type: 'required', message: `Minst ${label} behöver väljas` };
if (!control.value) {
return { type: 'required', message: `${label} är obligatoriskt` };
}
}
return null;
};
}
}