feat(api): Added connection to the dotnet api (TV-189)

Squashed commit of the following:

commit 6f06bf69b87c77473c21fbe5fcc5669964793b17
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Thu May 20 15:01:16 2021 +0200

    Fixed issue with including qp insde breadcrumbs

commit 579b6105acc7a60864f07b6082329872a2105bd3
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Thu May 20 14:46:54 2021 +0200

    Added some more data to the mock-api and fixed navigation-bar

commit f9b820136017584655fadafaf716155aec85316e
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Thu May 20 10:11:55 2021 +0200

    Updated mock-api

commit 5367d2a475b1c0b8807cf5a9c3f8987b3a586de4
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed May 19 16:00:07 2021 +0200

    Added different config for dotnet api

commit 47de5b8ad7c0924c82f07568aa2a4386613e3bd6
Merge: 22d5e9a dc40953
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed May 19 07:54:55 2021 +0200

    Merged develop

commit 22d5e9a126e3c1237531407ec3e6d47fbfea55e8
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed May 12 11:48:25 2021 +0200

    Added new configuration to talk with local API
This commit is contained in:
Erik Tiekstra
2021-05-20 15:20:29 +02:00
parent dc40953aba
commit 4334fd7364
32 changed files with 339 additions and 204 deletions

View File

@@ -90,6 +90,14 @@
"with": "apps/dafa-web/src/environments/environment.pega.ts" "with": "apps/dafa-web/src/environments/environment.pega.ts"
} }
] ]
},
"dotnet": {
"fileReplacements": [
{
"replace": "apps/dafa-web/src/environments/environment.ts",
"with": "apps/dafa-web/src/environments/environment.dotnet.ts"
}
]
} }
} }
}, },
@@ -105,6 +113,10 @@
}, },
"pega": { "pega": {
"browserTarget": "dafa-web:build:pega" "browserTarget": "dafa-web:build:pega"
},
"dotnet": {
"proxyConfig": "./config/proxy.conf.dotnet.json",
"browserTarget": "dafa-web:build:dotnet"
} }
} }
}, },

View File

@@ -30,7 +30,12 @@ export class AppComponent extends UnsubscribeDirective {
super(); super();
super.unsubscribeOnDestroy( super.unsubscribeOnDestroy(
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
const paths = this.router.url.split('/').filter(path => !!path); const urlTree = this.router.parseUrl(this.router.url);
urlTree.queryParams = {};
const paths = urlTree
.toString()
.split('/')
.filter(path => !!path);
this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this.startBreadcrumb)); this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this.startBreadcrumb));
}) })
); );

View File

@@ -5,6 +5,22 @@
</a> </a>
</div> </div>
<ul class="navigation__list dafa__hide-on-print"> <ul class="navigation__list dafa__hide-on-print">
<li class="navigation__item navigation__item--user" *ngIf="currentUser">
<span class="navigation__text">{{ currentUser.fullName }}</span>
<dafa-icon [icon]="iconType.USER" size="l"></dafa-icon>
</li>
<li class="navigation__item">
<a routerLink="/" class="navigation__link">
<dafa-icon [icon]="iconType.BELL" size="l"></dafa-icon>
<span class="dafa__a11y-sr-only">Notifieringar</span>
</a>
</li>
<li class="navigation__item">
<a routerLink="installningar" class="navigation__link">
<dafa-icon [icon]="iconType.SETTINGS" size="l"></dafa-icon>
<span class="dafa__a11y-sr-only">Inställningar</span>
</a>
</li>
<!-- <li class="navigation__item"> <!-- <li class="navigation__item">
<a <a
class="navigation__link" class="navigation__link"
@@ -16,11 +32,5 @@
<span class="navigation__text">Startsida</span> <span class="navigation__text">Startsida</span>
</a> </a>
</li> --> </li> -->
<li class="navigation__item" *ngIf="currentUser">
<div class="navigation__no-link">
<dafa-icon [icon]="iconType.USER" size="l"></dafa-icon>
<span class="navigation__text">{{ currentUser.fullName }}</span>
</div>
</li>
</ul> </ul>
</div> </div>

View File

@@ -1,6 +1,7 @@
@import 'mixins/list'; @import 'mixins/list';
@import 'variables/breakpoints'; @import 'variables/breakpoints';
@import 'variables/colors'; @import 'variables/colors';
@import 'variables/gutters';
@import 'variables/navigation'; @import 'variables/navigation';
.navigation { .navigation {
@@ -36,61 +37,35 @@
@include dafa__reset-list; @include dafa__reset-list;
display: flex; display: flex;
height: 100%; height: 100%;
gap: $digi--layout--gutter--l;
color: var(--digi--typography--color--text--light);
margin-right: var(--digi--layout--gutter);
} }
&__item { &__item {
display: flex; display: flex;
align-items: center; align-items: center;
border-left: 1px solid var(--digi--ui--color--background--off);
&--no-link { &--user {
padding: 0 var(--digi--layout--gutter); padding: var(--digi--layout--gutter--s);
flex-direction: column;
}
&:first-child {
margin-left: 0;
}
&:last-child {
border-right: 1px solid var(--digi--ui--color--background--off);
} }
} }
&__link, &__link {
&__no-link {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: --digi--typography--font-size--xs;
color: var(--digi--typography--color--text--light);
width: 7rem;
height: 100%;
font-weight: var(--digi--typography--font-weight);
text-decoration: none; text-decoration: none;
display: flex;
color: var(--digi--typography--color--text--light);
padding: var(--digi--layout--gutter--s);
} }
&__link { &__link {
&:hover { &:hover {
background-color: $digi--ui--color--primary-light;
}
&--active {
&::after {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 5px;
background-color: $digi--ui--color--secondary;
}
} }
} }
&__text { &__text {
margin-top: var(--digi--layout--gutter--xs); margin-right: var(--digi--layout--gutter);
font-size: var(--digi--typography--font-size--xs);
// margin-top: var(--digi--layout--gutter--xs);
} }
} }

View File

@@ -1,12 +1,12 @@
<div [ngClass]="className"> <div [ngClass]="className">
<div class="toast__icon-wrapper"> <div class="toast__icon-wrapper">
<digi-icon-exclamation-circle *ngIf="error.severity === errorSeverity.HIGH"></digi-icon-exclamation-circle> <dafa-icon [icon]="iconType.INFO" size="l" *ngIf="error.severity === errorSeverity.HIGH"></dafa-icon>
<digi-icon-exclamation-triangle *ngIf="error.severity === errorSeverity.MEDIUM"></digi-icon-exclamation-triangle> <dafa-icon [icon]="iconType.WARNING" size="l" *ngIf="error.severity === errorSeverity.MEDIUM"></dafa-icon>
<digi-icon-check-circle-reg *ngIf="error.severity === errorSeverity.LOW"></digi-icon-check-circle-reg> <dafa-icon [icon]="iconType.APPROVED" size="l" *ngIf="error.severity === errorSeverity.LOW"></dafa-icon>
</div> </div>
<div class="toast__content"> <div class="toast__content">
<button class="toast__close-button" aria-label="Stäng meddelandet" (click)="emitCloseEvent()"> <button class="toast__close-button" aria-label="Stäng meddelandet" (click)="emitCloseEvent()">
<digi-icon-x></digi-icon-x> <dafa-icon [icon]="iconType.X" size="l"></dafa-icon>
</button> </button>
<h3 class="toast__heading">{{ error.name }}</h3> <h3 class="toast__heading">{{ error.name }}</h3>
<p class="toast__message">{{ error.message }}</p> <p class="toast__message">{{ error.message }}</p>

View File

@@ -1,5 +1,6 @@
import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { ErrorSeverity } from '@dafa-enums/error-severity.enum'; import { ErrorSeverity } from '@dafa-enums/error-severity.enum';
import { IconType } from '@dafa-enums/icon-type.enum';
import { CustomError } from '@dafa-models/error/custom-error'; import { CustomError } from '@dafa-models/error/custom-error';
@Component({ @Component({
@@ -12,6 +13,7 @@ export class ToastComponent implements AfterViewInit {
@Input() error: CustomError; @Input() error: CustomError;
@Output() closeToast: EventEmitter<CustomError> = new EventEmitter(); @Output() closeToast: EventEmitter<CustomError> = new EventEmitter();
iconType = IconType;
errorSeverity = ErrorSeverity; errorSeverity = ErrorSeverity;
ngAfterViewInit(): void { ngAfterViewInit(): void {

View File

@@ -4,6 +4,7 @@ import { DigiNgIconInfoCircleRegModule } from '@af/digi-ng/_icon/icon-info-circl
import { DigiNgIconXModule } from '@af/digi-ng/_icon/icon-x'; import { DigiNgIconXModule } from '@af/digi-ng/_icon/icon-x';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { IconModule } from '@dafa-shared/components/icon/icon.module';
import { ToastComponent } from './toast.component'; import { ToastComponent } from './toast.component';
@NgModule({ @NgModule({
@@ -15,6 +16,7 @@ import { ToastComponent } from './toast.component';
DigiNgIconExclamationCircleModule, DigiNgIconExclamationCircleModule,
DigiNgIconExclamationTriangleModule, DigiNgIconExclamationTriangleModule,
DigiNgIconInfoCircleRegModule, DigiNgIconInfoCircleRegModule,
IconModule,
], ],
exports: [ToastComponent], exports: [ToastComponent],
}) })

View File

@@ -1,28 +0,0 @@
export enum AccessGroup {
UserManagement = 'UserManagement',
Economy = 'Economy',
Reports = 'Reports',
ParticipantManagement = 'ParticipantManagement',
User = 'User',
}
export enum PegaAccessGroup {
Users = 'MeetTest:Users',
Administrators = 'MeetTest:Administrators',
}
export function mapPegaAccessGroupToAccessGroups(pegaAccessGroup: PegaAccessGroup): AccessGroup[] {
const accessGroups: AccessGroup[] = [];
switch (pegaAccessGroup) {
case PegaAccessGroup.Users:
accessGroups.push(AccessGroup.User);
break;
case PegaAccessGroup.Administrators:
accessGroups.push(AccessGroup.UserManagement);
break;
default:
break;
}
return accessGroups;
}

View File

@@ -0,0 +1,28 @@
export enum Authorization {
UserManagement = 'UserManagement',
Economy = 'Economy',
Reports = 'Reports',
ParticipantManagement = 'ParticipantManagement',
User = 'User',
}
export enum PegaAuthorization {
Users = 'MeetTest:Users',
Administrators = 'MeetTest:Administrators',
}
export function mapPegaAuthorizationToAuthorization(pegaAuthorization: PegaAuthorization): Authorization[] {
const authorizations: Authorization[] = [];
switch (pegaAuthorization) {
case PegaAuthorization.Users:
authorizations.push(Authorization.User);
break;
case PegaAuthorization.Administrators:
authorizations.push(Authorization.UserManagement);
break;
default:
break;
}
return authorizations;
}

View File

@@ -9,4 +9,8 @@ export enum IconType {
ENVELOPE = 'envelope', ENVELOPE = 'envelope',
SOK_KANDIDAT = 'sok-kandidat', SOK_KANDIDAT = 'sok-kandidat',
EDIT = 'edit', EDIT = 'edit',
INFO = 'info',
WARNING = 'warning',
APPROVED = 'approved',
X = 'x',
} }

View File

@@ -1,7 +1,7 @@
import { mapPegaAccessGroupToAccessGroups, PegaAccessGroup } from '@dafa-enums/access-group.enum'; import { mapPegaAuthorizationToAuthorization, PegaAuthorization } from '@dafa-enums/authorization.enum';
import { Agency } from '@dafa-models/agency.model'; import { Service } from '@dafa-enums/service.enum';
import { Participant } from './participant.model'; import { Participant } from './participant.model';
import { User } from './user.model'; import { User, UserApiResponse } from './user.model';
// eslint-disable-next-line @typescript-eslint/no-empty-interface // eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Employee extends User { export interface Employee extends User {
@@ -10,12 +10,19 @@ export interface Employee extends User {
start: Date; start: Date;
end: Date; end: Date;
}[]; }[];
ssn: string; participants: Participant[];
agencies: Agency[]; services: Service[];
active: boolean;
}
export interface EmployeeApiResponse extends UserApiResponse {
active: boolean;
services: Service[];
languages: string[];
participants: Participant[]; participants: Participant[];
} }
export interface EmployeesApiResponse { export interface PegaEmployeesApiResponse {
pxMore: string; pxMore: string;
pxObjClass: string; pxObjClass: string;
pxPageCount: string; pxPageCount: string;
@@ -24,11 +31,11 @@ export interface EmployeesApiResponse {
pxTotalResultCount: string; pxTotalResultCount: string;
pyMaxRecords: string; pyMaxRecords: string;
pyObjClass: string; pyObjClass: string;
pxResults: EmployeeApiResponse[]; pxResults: PegaEmployeeApiResponse[];
pzPerformanceSettings: string[]; pzPerformanceSettings: string[];
} }
export interface EmployeeApiResponse { export interface PegaEmployeeApiResponse {
pxInsHandle: string; pxInsHandle: string;
pxObjClass: string; pxObjClass: string;
pyAccessGroup: string; pyAccessGroup: string;
@@ -42,13 +49,13 @@ export interface EmployeeApiResponse {
pyUserName: string; pyUserName: string;
} }
export interface EmployeeApiRequestData { export interface PegaEmployeeApiRequestData {
pyFirstName: string; pyFirstName: string;
pyLastName: string; pyLastName: string;
pyTelephone: string; pyTelephone: string;
} }
export interface EmployeeApiPostResponse { export interface PegaEmployeeApiPostResponse {
pxObjClass: string; pxObjClass: string;
pyErrorMessage: string; pyErrorMessage: string;
pyFirstName: string; pyFirstName: string;
@@ -58,7 +65,7 @@ export interface EmployeeApiPostResponse {
pyUserIdentifier: string; pyUserIdentifier: string;
} }
export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApiRequestData { export function mapEmployeeToEmployeeApiRequestData(data: Employee): PegaEmployeeApiRequestData {
return { return {
pyFirstName: data.firstName, pyFirstName: data.firstName,
pyLastName: data.lastName, pyLastName: data.lastName,
@@ -66,24 +73,65 @@ export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApi
}; };
} }
export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employee { export function mapPegaEmployeeReponseToEmployee(data: PegaEmployeeApiResponse): Employee {
return { return {
id: data.pyUserIdentifier, id: data.pyUserIdentifier,
lastName: data.pyLastName, lastName: data.pyLastName,
firstName: data.pyFirstName, firstName: data.pyFirstName,
fullName: `${data.pyFirstName} ${data.pyLastName}`, fullName: `${data.pyFirstName} ${data.pyLastName}`,
organization: data.pyOrganization, organization: {
organizationDivision: data.pyOrgDivision, id: '',
organizationUnit: data.pyOrgUnit, name: data.pyOrganization,
kaNumber: null,
address: {
street: null,
houseNumber: null,
postalCode: null,
city: null,
kommun: null,
},
},
phone: data.pyTelephone, phone: data.pyTelephone,
email: '', email: '',
accessGroups: mapPegaAccessGroupToAccessGroups(data.pyAccessGroup as PegaAccessGroup), authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
utforandeverksamhet: '', services: [],
service: '',
languages: [], languages: [],
outOfOffice: null, outOfOffice: null,
ssn: '', ssn: '',
agencies: [],
participants: [], participants: [],
active: true,
};
}
export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employee {
const {
id,
firstName,
lastName,
phone,
email,
ssn,
active,
services,
languages,
organization,
authorizations,
participants,
} = data;
return {
id,
firstName,
lastName,
fullName: `${firstName} ${lastName}`,
organization,
phone,
email,
authorizations,
services,
languages,
outOfOffice: null,
ssn,
participants,
active,
}; };
} }

View File

@@ -1,11 +1,11 @@
export interface Agency { export interface Organization {
id: string; id: string;
name: string; name: string;
kaNumber: number; kaNumber: string;
address: { address: {
street: string; street: string;
houseNumber: number; houseNumber: string;
postalCode: number; postalCode: string;
city: string; city: string;
kommun: string; kommun: string;
}; };

View File

@@ -1,21 +1,30 @@
import { AccessGroup, mapPegaAccessGroupToAccessGroups, PegaAccessGroup } from '@dafa-enums/access-group.enum'; import { Authorization, mapPegaAuthorizationToAuthorization, PegaAuthorization } from '@dafa-enums/authorization.enum';
import { Organization } from './organization.model';
export interface User { export interface User {
id: string; id: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
fullName: string; fullName: string;
organization: string; ssn: string;
organizationDivision: string;
organizationUnit: string;
phone: string; phone: string;
email: string; email: string;
accessGroups: AccessGroup[]; organization: Organization;
utforandeverksamhet: string; authorizations: Authorization[];
service: string;
} }
export interface UserApiResponse { export interface UserApiResponse {
id: string;
firstName: string;
lastName: string;
ssn: string;
phone: string;
email: string;
organization: Organization;
authorizations: Authorization[];
}
export interface PegaUserApiResponse {
pxInsName: string; pxInsName: string;
pxLimitedAccess: string; pxLimitedAccess: string;
pxObjClass: string; pxObjClass: string;
@@ -39,19 +48,42 @@ export interface UserApiResponse {
}; };
} }
export function mapUserApiReponseToUser(data: UserApiResponse): User { export function mapUserApiResponseToUser(data: UserApiResponse): User {
const { id, firstName, lastName, ssn, organization, phone, email, authorizations } = data;
return {
id,
firstName,
lastName,
fullName: `${firstName} ${lastName}`,
ssn,
organization,
phone,
email,
authorizations,
};
}
export function mapPegaUserApiReponseToUser(data: PegaUserApiResponse): User {
return { return {
id: data.pyUserIdentifier, id: data.pyUserIdentifier,
lastName: data.pyLastName, lastName: data.pyLastName,
firstName: data.pyFirstName, firstName: data.pyFirstName,
fullName: `${data.pyFirstName} ${data.pyLastName}`, fullName: `${data.pyFirstName} ${data.pyLastName}`,
organization: data.pyOrganization, ssn: null,
organizationDivision: data.pyOrgDivision, organization: {
organizationUnit: data.pyOrgUnit, id: '',
name: data.pyOrganization,
kaNumber: null,
address: {
street: null,
houseNumber: null,
postalCode: null,
city: null,
kommun: null,
},
},
phone: data.pyTelephone, phone: data.pyTelephone,
email: data.pyAddresses.Email.pyEmailAddress, email: data.pyAddresses.Email.pyEmailAddress,
accessGroups: mapPegaAccessGroupToAccessGroups(data.pyAccessGroup as PegaAccessGroup), authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
utforandeverksamhet: '',
service: '',
}; };
} }

View File

@@ -29,22 +29,14 @@
<h2>Uppgifter om arbete</h2> <h2>Uppgifter om arbete</h2>
<dl> <dl>
<dt>Organisation</dt> <dt>Utförandeverksamhet</dt>
<dd *ngIf="detailedEmployeeData.organization; else emptyDD">{{ detailedEmployeeData.organization }}</dd> <dd *ngIf="detailedEmployeeData.organization; else emptyDD">
<dt>Avdelning</dt> {{ detailedEmployeeData.organization.address.city }}
<dd *ngIf="detailedEmployeeData.organizationDivision; else emptyDD">
{{ detailedEmployeeData.organizationDivision }}
</dd> </dd>
<dt>Behörigheter</dt> <dt>Behörigheter</dt>
<dd *ngIf="detailedEmployeeData.accessGroups?.length; else emptyDD"> <dd *ngIf="detailedEmployeeData.authorizations?.length; else emptyDD">
{{ detailedEmployeeData.accessGroups.join(', ') }} {{ detailedEmployeeData.authorizations.join(', ') }}
</dd> </dd>
<dt>Behörighet</dt>
<ng-container *ngIf="detailedEmployeeData.authorisations?.length; else emptyDD">
<dd *ngFor="let item of detailedEmployeeData.authorisations">
{{ item }}
</dd>
</ng-container>
<dt>Frånvaroperiod</dt> <dt>Frånvaroperiod</dt>
<ng-container *ngIf="detailedEmployeeData.outOfOffice?.length; else emptyDD"> <ng-container *ngIf="detailedEmployeeData.outOfOffice?.length; else emptyDD">
<dd *ngFor="let date of detailedEmployeeData.outOfOffice"> <dd *ngFor="let date of detailedEmployeeData.outOfOffice">
@@ -52,7 +44,9 @@
</dd> </dd>
</ng-container> </ng-container>
<dt>Tjänst</dt> <dt>Tjänst</dt>
<dd *ngIf="detailedEmployeeData.service; else emptyDD">{{ detailedEmployeeData.service }}</dd> <dd *ngIf="detailedEmployeeData.services.length; else emptyDD">
{{ detailedEmployeeData.services.join(', ') }}
</dd>
<dt>Språk</dt> <dt>Språk</dt>
<dd>{{ detailedEmployeeData.languages?.join(', ') }}</dd> <dd>{{ detailedEmployeeData.languages?.join(', ') }}</dd>
</dl> </dl>

View File

@@ -13,18 +13,18 @@
</button> </button>
</th> </th>
<th scope="col" class="employees-list__column-head"> <th scope="col" class="employees-list__column-head">
<button class="employees-list__sort-button" (click)="handleSort('service')"> <button class="employees-list__sort-button" (click)="handleSort('services')">
Tjänst Tjänst
<ng-container *ngIf="sortBy?.key === 'service'"> <ng-container *ngIf="sortBy?.key === 'services'">
<digi-icon-caret-up class="employees-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up> <digi-icon-caret-up class="employees-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="employees-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down> <digi-icon-caret-down class="employees-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container> </ng-container>
</button> </button>
</th> </th>
<th scope="col" class="employees-list__column-head"> <th scope="col" class="employees-list__column-head">
<button class="employees-list__sort-button" (click)="handleSort('utforandeverksamhet')"> <button class="employees-list__sort-button" (click)="handleSort('organization')">
Utförandeverksamhet Utförandeverksamhet
<ng-container *ngIf="sortBy?.key === 'utforandeverksamhet'"> <ng-container *ngIf="sortBy?.key === 'organization'">
<digi-icon-caret-up class="employees-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up> <digi-icon-caret-up class="employees-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="employees-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down> <digi-icon-caret-down class="employees-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container> </ng-container>
@@ -37,8 +37,8 @@
<th scope="row"> <th scope="row">
<a [routerLink]="employees.id" class="employees-list__link">{{ employees.fullName }}</a> <a [routerLink]="employees.id" class="employees-list__link">{{ employees.fullName }}</a>
</th> </th>
<td>{{ employees.service || '-' }}</td> <td>{{ employees.services.length ? employees.services.join(', ') : '-' }}</td>
<td>{{ employees.kommun || '-' }}</td> <td>{{ employees.organization.address.city || '-' }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -4,10 +4,10 @@ import { ErrorType } from '@dafa-enums/error-type.enum';
import { environment } from '@dafa-environment'; import { environment } from '@dafa-environment';
import { import {
Employee, Employee,
EmployeeApiPostResponse, EmployeeApiResponse,
EmployeesApiResponse,
mapEmployeeReponseToEmployee, mapEmployeeReponseToEmployee,
mapEmployeeToEmployeeApiRequestData, mapEmployeeToEmployeeApiRequestData,
PegaEmployeeApiPostResponse,
} from '@dafa-models/employee.model'; } from '@dafa-models/employee.model';
import { SortBy } from '@dafa-models/sort-by.model'; import { SortBy } from '@dafa-models/sort-by.model';
import { sort } from '@dafa-utils/sort.util'; import { sort } from '@dafa-utils/sort.util';
@@ -23,14 +23,13 @@ const API_HEADERS = { headers: environment.api.headers };
providedIn: 'root', providedIn: 'root',
}) })
export class EmployeeService { export class EmployeeService {
private _employeesApiUrl = `${environment.api.meet}/employees`; private _employeeApiUrl = `${environment.api.url}/employee`;
private _employeeApiUrl = `${environment.api.meet}/employee`; private _employeesRawData: Observable<EmployeeApiResponse[]> = this.httpClient.get<EmployeeApiResponse[]>(
private _employeesRawData: Observable<EmployeesApiResponse> = this.httpClient.get<EmployeesApiResponse>( this._employeeApiUrl,
this._employeesApiUrl,
API_HEADERS API_HEADERS
); );
private _allEmployees$: Observable<Employee[]> = this._employeesRawData.pipe( private _allEmployees$: Observable<Employee[]> = this._employeesRawData.pipe(
map(({ pxResults }) => pxResults.map(result => mapEmployeeReponseToEmployee(result))) map(results => results.map(result => mapEmployeeReponseToEmployee(result)))
); );
private _employeesSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'fullName', reverse: false }); private _employeesSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'fullName', reverse: false });
@@ -42,7 +41,7 @@ export class EmployeeService {
map(([employees, searchFilter]) => filterEmployees(employees, searchFilter)) map(([employees, searchFilter]) => filterEmployees(employees, searchFilter))
); );
public resultCount$: Observable<number> = this._employeesRawData.pipe(map(({ pxResultCount }) => +pxResultCount)); public resultCount$: Observable<number> = this._employeesRawData.pipe(map(results => results.length)); // TODO: need META
public filteredEmployees$: Observable<Employee[]> = combineLatest([ public filteredEmployees$: Observable<Employee[]> = combineLatest([
this._filteredEmployees$, this._filteredEmployees$,
this._employeesSortBy$, this._employeesSortBy$,
@@ -56,8 +55,8 @@ export class EmployeeService {
public getDetailedEmployeeData(id: string): Observable<Employee> { public getDetailedEmployeeData(id: string): Observable<Employee> {
return this.httpClient return this.httpClient
.get<EmployeesApiResponse>(`${this._employeeApiUrl}`, { ...API_HEADERS, params: { OperatorId: id } }) .get<EmployeeApiResponse>(`${this._employeeApiUrl}/${id}`, { ...API_HEADERS })
.pipe(map(({ pxResults }) => mapEmployeeReponseToEmployee(pxResults[0]))); .pipe(map(result => mapEmployeeReponseToEmployee(result)));
} }
public setSearchFilter(value: string) { public setSearchFilter(value: string) {
@@ -72,7 +71,7 @@ export class EmployeeService {
public postNewEmployee(employeeData: Employee): Observable<string> { public postNewEmployee(employeeData: Employee): Observable<string> {
return this.httpClient return this.httpClient
.post<EmployeeApiPostResponse>( .post<PegaEmployeeApiPostResponse>(
this._employeeApiUrl, this._employeeApiUrl,
mapEmployeeToEmployeeApiRequestData(employeeData), mapEmployeeToEmployeeApiRequestData(employeeData),
API_HEADERS API_HEADERS

View File

@@ -22,7 +22,7 @@ function filterParticipants(participants: Participant[], searchFilter: string):
}) })
export class ParticipantsService { export class ParticipantsService {
private _allParticipants$: Observable<Participant[]> = this.httpClient.get<Participant[]>( private _allParticipants$: Observable<Participant[]> = this.httpClient.get<Participant[]>(
`${environment.api.meet}/participants` `${environment.api.url}/participants`
); );
private _activeParticipantsSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'handleBefore', reverse: false }); private _activeParticipantsSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'handleBefore', reverse: false });
public activeParticipantsSortBy$: Observable<SortBy> = this._activeParticipantsSortBy$.asObservable(); public activeParticipantsSortBy$: Observable<SortBy> = this._activeParticipantsSortBy$.asObservable();

View File

@@ -1,20 +1,20 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { environment } from '@dafa-environment'; import { environment } from '@dafa-environment';
import { mapUserApiReponseToUser, User, UserApiResponse } from '@dafa-models/user.model'; import { mapUserApiResponseToUser, User, UserApiResponse } from '@dafa-models/user.model';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
const API_HEADERS = { headers: environment.api.headers };
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class UserService { export class UserService {
private _userApiUrl = `${environment.api.default}/D_OperatorID`; private _userApiUrl = `${environment.api.url}/currentUser`;
public currentUser$: Observable<User> = this.httpClient public currentUser$: Observable<User> = this.httpClient
.get<UserApiResponse>(this._userApiUrl, { .get<UserApiResponse>(this._userApiUrl, API_HEADERS)
headers: environment.api.headers, .pipe(map(response => mapUserApiResponseToUser(response)));
})
.pipe(map(response => mapUserApiReponseToUser(response)));
constructor(private httpClient: HttpClient) {} constructor(private httpClient: HttpClient) {}
} }

View File

@@ -35,5 +35,12 @@
<digi-icon-envelope-filled *ngSwitchCase="iconType.ENVELOPE" [ngClass]="iconClass"></digi-icon-envelope-filled> <digi-icon-envelope-filled *ngSwitchCase="iconType.ENVELOPE" [ngClass]="iconClass"></digi-icon-envelope-filled>
<digi-icon-sokkandidat *ngSwitchCase="iconType.SOK_KANDIDAT" [ngClass]="iconClass"></digi-icon-sokkandidat> <digi-icon-sokkandidat *ngSwitchCase="iconType.SOK_KANDIDAT" [ngClass]="iconClass"></digi-icon-sokkandidat>
<digi-icon-edit *ngSwitchCase="iconType.EDIT" [ngClass]="iconClass"></digi-icon-edit> <digi-icon-edit *ngSwitchCase="iconType.EDIT" [ngClass]="iconClass"></digi-icon-edit>
<digi-icon-exclamation-circle *ngSwitchCase="iconType.INFO" [ngClass]="iconClass"></digi-icon-exclamation-circle>
<digi-icon-exclamation-triangle
*ngSwitchCase="iconType.WARNING"
[ngClass]="iconClass"
></digi-icon-exclamation-triangle>
<digi-icon-check-circle-reg *ngSwitchCase="iconType.APPROVED" [ngClass]="iconClass"></digi-icon-check-circle-reg>
<digi-icon-x *ngSwitchCase="iconType.X" [ngClass]="iconClass"></digi-icon-x>
</ng-container> </ng-container>
</ng-template> </ng-template>

View File

@@ -14,7 +14,7 @@ export function mapPathsToBreadcrumbs(
]; ];
if (isEmployeeCardRoute(paths)) { if (isEmployeeCardRoute(paths)) {
breadcrumbs[breadcrumbs.length - 1].text = 'Personalkort'; breadcrumbs[breadcrumbs.length - 1].text = 'Personal information';
} }
return breadcrumbs; return breadcrumbs;

View File

@@ -0,0 +1,7 @@
export const environment = {
production: false,
api: {
url: '/api',
headers: {},
},
};

View File

@@ -1,8 +1,7 @@
export const environment = { export const environment = {
production: true, production: true,
api: { api: {
meet: '/api', url: '/api',
default: '/api',
headers: {}, headers: {},
}, },
}; };

View File

@@ -1,8 +1,7 @@
export const environment = { export const environment = {
production: false, production: false,
api: { api: {
meet: '/api', url: '/api',
default: '/api',
headers: {}, headers: {},
}, },
}; };

View File

@@ -0,0 +1,6 @@
{
"/api": {
"target": "http://localhost:6001",
"secure": false
}
}

View File

@@ -1,6 +1,9 @@
{ {
"/api": { "/api": {
"target": "http://localhost:8000/", "target": "http://localhost:8000",
"secure": false "secure": false,
"pathRewrite": {
"^/api": "/"
}
} }
} }

View File

@@ -1,5 +1,7 @@
{ {
"/api/*": "/$1", "/api/*": "/$1",
"/participants": "/participants?_embed=employees", "/participants": "/participants?_embed=employees",
"/participant/:id": "/participants/:id?_embed=employees" "/participant/:id": "/participants/:id?_embed=employees",
"/employee": "/employees",
"/employee/:id": "/employees/:id"
} }

View File

@@ -0,0 +1,28 @@
import faker from 'faker';
import organizations from './organizations.js';
import chooseRandom from './utils/choose-random.util.js';
faker.locale = 'sv';
const ORGANIZATIONS = organizations.generate();
const AUTHORIZATIONS = ['Hantera användare', 'Hantera origisation', 'Hantera ekonomi'];
function generateCurrentUser() {
return {
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,
})}`,
phone: `07${faker.datatype.number(9)}-${faker.datatype.number({ min: 1000000, max: 9999999 })}`,
email: faker.internet.email(),
organization: ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)],
authorizations: chooseRandom(AUTHORIZATIONS, faker.datatype.number(3)),
};
}
export default {
generate: generateCurrentUser,
};

View File

@@ -1,18 +1,17 @@
import faker from 'faker'; import faker from 'faker';
import agencies from './agencies.js';
import kommuner from './kommuner.js'; import kommuner from './kommuner.js';
import languages from './languages.js'; import languages from './languages.js';
import organizations from './organizations.js';
import services from './services.js'; import services from './services.js';
import chooseRandom from './utils/choose-random.util.js';
faker.locale = 'sv'; faker.locale = 'sv';
const SERVICES = services.generate(); const SERVICES = services.generate();
const KOMMUN = kommuner.generate(); const KOMMUN = kommuner.generate();
const AGENCIES = agencies.generate(); const ORGANIZATIONS = organizations.generate();
const STATUSES = [true, false]; const STATUSES = [true, false];
const LANGUAGES = languages.generate(); const LANGUAGES = languages.generate();
const AUTHORISATIONS = ['Hantera användare', 'Hantera origisation', 'Hantera ekonomi']; const AUTHORIZATIONS = ['Hantera användare', 'Hantera origisation', 'Hantera ekonomi'];
function generateEmployees(amount = 10) { function generateEmployees(amount = 10) {
const employees = []; const employees = [];
@@ -20,16 +19,8 @@ function generateEmployees(amount = 10) {
for (let i = 1; i <= amount; ++i) { for (let i = 1; i <= amount; ++i) {
const person = { const person = {
id: faker.datatype.uuid(), id: faker.datatype.uuid(),
employeeId: faker.datatype.number(),
firstName: faker.name.firstName(), firstName: faker.name.firstName(),
lastName: faker.name.lastName(), lastName: faker.name.lastName(),
kommun: KOMMUN[Math.floor(Math.random() * KOMMUN.length)].kommun,
active: STATUSES[Math.floor(Math.random() * STATUSES.length)],
service: SERVICES[Math.floor(Math.random() * SERVICES.length)].name,
languages: [
LANGUAGES.find(language => language.name === 'Svenska'),
...chooseRandom(LANGUAGES, faker.datatype.number(3)),
],
ssn: `${faker.date.between('1950', '2000').toISOString().split('T')[0].replace(/-/g, '')}-${faker.datatype.number( ssn: `${faker.date.between('1950', '2000').toISOString().split('T')[0].replace(/-/g, '')}-${faker.datatype.number(
{ {
min: 1000, min: 1000,
@@ -38,19 +29,25 @@ function generateEmployees(amount = 10) {
)}`, )}`,
phone: `07${faker.datatype.number(9)}-${faker.datatype.number({ min: 1000000, max: 9999999 })}`, phone: `07${faker.datatype.number(9)}-${faker.datatype.number({ min: 1000000, max: 9999999 })}`,
email: faker.internet.email(), email: faker.internet.email(),
authorisations: chooseRandom(AUTHORISATIONS, faker.datatype.number(3)), organization: ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)],
outOfOffice: STATUSES[Math.floor(Math.random() * STATUSES.length)] services: [SERVICES[Math.floor(Math.random() * SERVICES.length)].name],
? [ authorizations: AUTHORIZATIONS,
{ // active: STATUSES[Math.floor(Math.random() * STATUSES.length)],
start: new Date('2021-07-12'), // languages: [
end: new Date('2021-07-24'), // LANGUAGES.find(language => language.name === 'Svenska'),
}, // ...chooseRandom(LANGUAGES, faker.datatype.number(3)),
] // ],
: [], // outOfOffice: STATUSES[Math.floor(Math.random() * STATUSES.length)]
agencies: chooseRandom(AGENCIES, faker.datatype.number(3)), // ? [
// {
// start: new Date('2021-07-12'),
// end: new Date('2021-07-24'),
// },
// ]
// : [],
}; };
employees.push({ ...person, fullName: `${person.firstName} ${person.lastName}` }); employees.push(person);
} }
console.info('Employees generated...'); console.info('Employees generated...');

View File

@@ -1,23 +1,25 @@
import fs from 'fs'; import fs from 'fs';
import agencies from './agencies.js'; import currentUser from './current-user.js';
import employees from './employees.js'; import employees from './employees.js';
import kommuner from './kommuner.js'; import kommuner from './kommuner.js';
import languages from './languages.js'; import languages from './languages.js';
import organizations from './organizations.js';
import participants from './participants.js'; import participants from './participants.js';
import services from './services.js'; import services from './services.js';
const generatedEmployees = employees.generate(5); const generatedEmployees = employees.generate(10);
const apiData = { const apiData = {
services: services.generate(), services: services.generate(),
languages: languages.generate(), languages: languages.generate(),
employees: generatedEmployees, employees: generatedEmployees,
kommuner: kommuner.generate(), kommuner: kommuner.generate(),
agencies: agencies.generate(), organizations: organizations.generate(),
participants: participants.generate(50).map(participant => ({ participants: participants.generate(50).map(participant => ({
...participant, ...participant,
employeeId: generatedEmployees[Math.floor(Math.random() * generatedEmployees.length)].id, employeeId: generatedEmployees[Math.floor(Math.random() * generatedEmployees.length)].id,
})), })),
currentUser: currentUser.generate(),
}; };
fs.writeFileSync('api.json', JSON.stringify(apiData, null, '\t')); fs.writeFileSync('api.json', JSON.stringify(apiData, null, '\t'));

View File

@@ -5,11 +5,11 @@ faker.locale = 'sv';
const KOMMUN = kommuner.generate(); const KOMMUN = kommuner.generate();
function generateAgencies(amount = 10) { function generateOrganizations(amount = 10) {
const agencies = []; const organizations = [];
for (let i = 1; i <= amount; ++i) { for (let i = 1; i <= amount; ++i) {
agencies.push({ organizations.push({
id: faker.datatype.uuid(), id: faker.datatype.uuid(),
name: faker.company.companyName(), name: faker.company.companyName(),
kaNumber: faker.datatype.number({ min: 100000, max: 999999 }), kaNumber: faker.datatype.number({ min: 100000, max: 999999 }),
@@ -23,11 +23,11 @@ function generateAgencies(amount = 10) {
}); });
} }
console.info('Agencies generated...'); console.info('Organizations generated...');
return agencies; return organizations;
} }
export default { export default {
generate: generateAgencies, generate: generateOrganizations,
}; };

View File

@@ -6,20 +6,20 @@ function generateServices() {
}, },
{ {
id: 2, id: 2,
name: 'STOM',
},
{
id: 3,
name: 'KVL', name: 'KVL',
}, },
{ // {
id: 4, // id: 3,
name: 'YSM', // name: 'STOM',
}, // },
{ // {
id: 5, // id: 4,
name: 'AUB', // name: 'YSM',
}, // },
// {
// id: 5,
// name: 'AUB',
// },
]; ];
console.info('Services generated...'); console.info('Services generated...');

View File

@@ -12,8 +12,10 @@
"nx": "nx", "nx": "nx",
"start": "ng serve dafa-web", "start": "ng serve dafa-web",
"start:pega": "ng serve dafa-web --configuration pega", "start:pega": "ng serve dafa-web --configuration pega",
"start:dotnet": "ng serve dafa-web --configuration dotnet",
"build": "ng build dafa-web", "build": "ng build dafa-web",
"build:pega": "ng build dafa-web --configuration pega", "build:pega": "ng build dafa-web --configuration pega",
"build:dotnet": "ng build dafa-web --configuration dotnet",
"test": "ng test dafa-web", "test": "ng test dafa-web",
"release": "bash ./tools/release.sh", "release": "bash ./tools/release.sh",
"release:dry": "bash ./tools/release.sh dry", "release:dry": "bash ./tools/release.sh dry",