Merge pull request #14 in TEA/dafa-web-monorepo from feature/more-mock-api-fixes to develop
Squashed commit of the following: commit 9473ca4e23eb6c5967d9df3403cc071d48e0ab73 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Jun 1 08:18:39 2021 +0200 Adding footer commit 8f13809ad3928fd09fd67d713a61c2ca4ef27bc4 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Jun 1 06:57:34 2021 +0200 Added footer commit ee8a5be048786843e3c5672368a0663ead424852 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Mon May 31 16:07:14 2021 +0200 Added size to edit icon commit b8e99713bc0190075cfe201f8cd515dca78e184e Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Mon May 24 15:54:15 2021 +0200 Renamed create account to employee form and fixed some validation commit 935509bc5dd6bc7f6533b580197da2f8c15affc3 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Mon May 24 08:28:40 2021 +0200 More changes to mock-api and views commit d0bb8e1c0130c996ee89413f5ddda015fcf49ec5 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri May 21 11:22:40 2021 +0200 Modified mock-api and implemented a part inside employee-list
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"angular.enable-strict-mode-prompt": false
|
||||
}
|
||||
@@ -13,5 +13,7 @@
|
||||
></digi-ng-navigation-breadcrumbs>
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
<dafa-footer class="dafa__footer"></dafa-footer>
|
||||
</div>
|
||||
<dafa-toast-list></dafa-toast-list>
|
||||
|
||||
@@ -3,22 +3,24 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.dafa {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
height: 100vh;
|
||||
grid-template-columns: 15rem 1fr;
|
||||
grid-template-rows: $dafa__navigation-height 1fr;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-template-areas:
|
||||
'header header'
|
||||
'sidebar content';
|
||||
'sidebar content'
|
||||
'footer footer';
|
||||
|
||||
@media (min-width: $digi--layout--breakpoint--m) {
|
||||
grid-template-rows: $dafa__navigation-height-large 1fr;
|
||||
}
|
||||
// @media (min-width: $digi--layout--breakpoint--m) {
|
||||
// grid-template-rows: $dafa__navigation-height-large 1fr auto;
|
||||
// }
|
||||
|
||||
&__header {
|
||||
grid-area: header;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
@@ -30,11 +32,16 @@
|
||||
&__content {
|
||||
grid-area: content;
|
||||
padding: var(--digi--layout--gutter) $digi--layout--gutter--l $digi--layout--gutter--xxl;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__breadcrumbs {
|
||||
display: block;
|
||||
margin-bottom: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__footer {
|
||||
grid-area: footer;
|
||||
background-color: var(--digi--ui--color--primary);
|
||||
min-height: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CustomErrorHandler } from '@dafa-interceptors/custom-error-handler.modu
|
||||
import { MarkdownModule } from 'ngx-markdown';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { FooterModule } from './components/footer/footer.module';
|
||||
import { NavigationModule } from './components/navigation/navigation.module';
|
||||
import { SidebarModule } from './components/sidebar/sidebar.module';
|
||||
import { SkipToContentModule } from './components/skip-to-content/skip-to-content.module';
|
||||
@@ -23,6 +24,7 @@ import { ToastListModule } from './components/toast-list/toast-list.module';
|
||||
NavigationModule,
|
||||
SidebarModule,
|
||||
ToastListModule,
|
||||
FooterModule,
|
||||
MarkdownModule.forRoot({ loader: HttpClient }),
|
||||
DigiNgNavigationBreadcrumbsModule,
|
||||
],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<footer class="footer">
|
||||
<div class="footer__logo-wrapper">
|
||||
<a class="footer__logo-link" href="/">
|
||||
<img class="footer__logo" src="/assets/logo/arbetsformedlingen-light.svg" alt="Arbetsförmedlingen" />
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,15 @@
|
||||
.footer {
|
||||
background-color: var(--digi--ui--color--background--profile);
|
||||
padding: var(--digi--layout--gutter);
|
||||
|
||||
&__logo-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
height: 2rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { IconModule } from '@dafa-shared/components/icon/icon.module';
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FooterComponent],
|
||||
imports: [RouterTestingModule, IconModule],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dafa-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FooterComponent {}
|
||||
11
apps/dafa-web/src/app/components/footer/footer.module.ts
Normal file
11
apps/dafa-web/src/app/components/footer/footer.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooterComponent],
|
||||
imports: [CommonModule, RouterModule],
|
||||
exports: [FooterComponent],
|
||||
})
|
||||
export class FooterModule {}
|
||||
@@ -0,0 +1,7 @@
|
||||
@media print {
|
||||
.footer {
|
||||
border-bottom-width: 0;
|
||||
padding: var(--digi--layout--gutter) 0;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
@import 'variables/colors';
|
||||
@import 'variables/navigation';
|
||||
@import 'mixins/list';
|
||||
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
top: $dafa__navigation-height-large;
|
||||
|
||||
&__list {
|
||||
@include dafa__reset-list;
|
||||
|
||||
19
apps/dafa-web/src/app/data/models/authorization.model.ts
Normal file
19
apps/dafa-web/src/app/data/models/authorization.model.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Authorization as AuthorizationEnum } from '@dafa-enums/authorization.enum';
|
||||
|
||||
export interface Authorization {
|
||||
id: string;
|
||||
name: AuthorizationEnum;
|
||||
}
|
||||
|
||||
export interface AuthorizationApiResponse {
|
||||
id: string;
|
||||
name: AuthorizationEnum;
|
||||
}
|
||||
|
||||
export function mapAuthorizationApiResponseToAuthorization(data: AuthorizationApiResponse): Authorization {
|
||||
const { id, name } = data;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
@@ -1,27 +1,22 @@
|
||||
import { mapPegaAuthorizationToAuthorization, PegaAuthorization } from '@dafa-enums/authorization.enum';
|
||||
import { Service } from '@dafa-enums/service.enum';
|
||||
import { Participant } from './participant.model';
|
||||
import { Service } from './service.model';
|
||||
import { User, UserApiResponse } from './user.model';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface Employee extends User {
|
||||
languages: string[];
|
||||
outOfOffice: {
|
||||
start: Date;
|
||||
end: Date;
|
||||
}[];
|
||||
participants: Participant[];
|
||||
services: Service[];
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface EmployeeApiResponse extends UserApiResponse {
|
||||
active: boolean;
|
||||
services: Service[];
|
||||
languages: string[];
|
||||
participants: Participant[];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface EmployeeApiRequestData extends Employee {}
|
||||
|
||||
export interface PegaEmployeesApiResponse {
|
||||
pxMore: string;
|
||||
pxObjClass: string;
|
||||
@@ -44,7 +39,6 @@ export interface PegaEmployeeApiResponse {
|
||||
pyOrganization: string;
|
||||
pyOrgDivision: string;
|
||||
pyOrgUnit: string;
|
||||
pyTelephone: string;
|
||||
pyUserIdentifier: string;
|
||||
pyUserName: string;
|
||||
}
|
||||
@@ -52,7 +46,6 @@ export interface PegaEmployeeApiResponse {
|
||||
export interface PegaEmployeeApiRequestData {
|
||||
pyFirstName: string;
|
||||
pyLastName: string;
|
||||
pyTelephone: string;
|
||||
}
|
||||
|
||||
export interface PegaEmployeeApiPostResponse {
|
||||
@@ -61,15 +54,17 @@ export interface PegaEmployeeApiPostResponse {
|
||||
pyFirstName: string;
|
||||
pyHasError: 'true' | 'false';
|
||||
pyLastName: string;
|
||||
pyTelephone: string;
|
||||
pyUserIdentifier: string;
|
||||
}
|
||||
|
||||
export function mapEmployeeToEmployeeApiRequestData(data: Employee): PegaEmployeeApiRequestData {
|
||||
export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApiRequestData {
|
||||
return data;
|
||||
}
|
||||
|
||||
export function mapEmployeeToPegaEmployeeApiRequestData(data: Employee): PegaEmployeeApiRequestData {
|
||||
return {
|
||||
pyFirstName: data.firstName,
|
||||
pyLastName: data.lastName,
|
||||
pyTelephone: data.phone,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,59 +74,41 @@ export function mapPegaEmployeeReponseToEmployee(data: PegaEmployeeApiResponse):
|
||||
lastName: data.pyLastName,
|
||||
firstName: data.pyFirstName,
|
||||
fullName: `${data.pyFirstName} ${data.pyLastName}`,
|
||||
organization: {
|
||||
id: '',
|
||||
name: data.pyOrganization,
|
||||
kaNumber: null,
|
||||
address: {
|
||||
street: null,
|
||||
houseNumber: null,
|
||||
postalCode: null,
|
||||
city: null,
|
||||
kommun: null,
|
||||
organizations: [
|
||||
{
|
||||
id: '',
|
||||
name: data.pyOrganization,
|
||||
kaNumber: null,
|
||||
address: {
|
||||
street: null,
|
||||
houseNumber: null,
|
||||
postalCode: null,
|
||||
city: null,
|
||||
kommun: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
phone: data.pyTelephone,
|
||||
email: '',
|
||||
authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
|
||||
],
|
||||
authorizations: null,
|
||||
// authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
|
||||
services: [],
|
||||
languages: [],
|
||||
outOfOffice: null,
|
||||
ssn: '',
|
||||
participants: [],
|
||||
active: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employee {
|
||||
const {
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
phone,
|
||||
email,
|
||||
ssn,
|
||||
active,
|
||||
services,
|
||||
languages,
|
||||
organization,
|
||||
authorizations,
|
||||
participants,
|
||||
} = data;
|
||||
const { id, firstName, lastName, ssn, services, languages, organizations, authorizations, participants } = data;
|
||||
return {
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
organization,
|
||||
phone,
|
||||
email,
|
||||
organizations,
|
||||
authorizations,
|
||||
services,
|
||||
languages,
|
||||
outOfOffice: null,
|
||||
ssn,
|
||||
participants,
|
||||
active,
|
||||
};
|
||||
}
|
||||
|
||||
19
apps/dafa-web/src/app/data/models/service.model.ts
Normal file
19
apps/dafa-web/src/app/data/models/service.model.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Service as ServiceEnum } from '@dafa-enums/service.enum';
|
||||
|
||||
export interface Service {
|
||||
id: string;
|
||||
name: ServiceEnum;
|
||||
}
|
||||
|
||||
export interface ServiceApiResponse {
|
||||
id: string;
|
||||
name: ServiceEnum;
|
||||
}
|
||||
|
||||
export function mapServiceApiResponseToService(data: ServiceApiResponse): Service {
|
||||
const { id, name } = data;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Authorization, mapPegaAuthorizationToAuthorization, PegaAuthorization } from '@dafa-enums/authorization.enum';
|
||||
import { Authorization } from './authorization.model';
|
||||
import { Organization } from './organization.model';
|
||||
|
||||
export interface User {
|
||||
@@ -7,9 +7,7 @@ export interface User {
|
||||
lastName: string;
|
||||
fullName: string;
|
||||
ssn: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
organization: Organization;
|
||||
organizations: Organization[];
|
||||
authorizations: Authorization[];
|
||||
}
|
||||
|
||||
@@ -18,9 +16,7 @@ export interface UserApiResponse {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
ssn: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
organization: Organization;
|
||||
organizations: Organization[];
|
||||
authorizations: Authorization[];
|
||||
}
|
||||
|
||||
@@ -35,30 +31,20 @@ export interface PegaUserApiResponse {
|
||||
pyOrganization: string;
|
||||
pyOrgDivision: string;
|
||||
pyOrgUnit: string;
|
||||
pyTelephone: string;
|
||||
pyUserIdentifier: string;
|
||||
pyUserName: string;
|
||||
pyAccessGroupsAdditional: string[];
|
||||
pyAddresses: {
|
||||
Email: {
|
||||
pxObjClass: string;
|
||||
pxSubscript: string;
|
||||
pyEmailAddress: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function mapUserApiResponseToUser(data: UserApiResponse): User {
|
||||
const { id, firstName, lastName, ssn, organization, phone, email, authorizations } = data;
|
||||
const { id, firstName, lastName, ssn, organizations, authorizations } = data;
|
||||
return {
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
ssn,
|
||||
organization,
|
||||
phone,
|
||||
email,
|
||||
organizations,
|
||||
authorizations,
|
||||
};
|
||||
}
|
||||
@@ -70,20 +56,21 @@ export function mapPegaUserApiReponseToUser(data: PegaUserApiResponse): User {
|
||||
firstName: data.pyFirstName,
|
||||
fullName: `${data.pyFirstName} ${data.pyLastName}`,
|
||||
ssn: null,
|
||||
organization: {
|
||||
id: '',
|
||||
name: data.pyOrganization,
|
||||
kaNumber: null,
|
||||
address: {
|
||||
street: null,
|
||||
houseNumber: null,
|
||||
postalCode: null,
|
||||
city: null,
|
||||
kommun: null,
|
||||
organizations: [
|
||||
{
|
||||
id: '',
|
||||
name: data.pyOrganization,
|
||||
kaNumber: null,
|
||||
address: {
|
||||
street: null,
|
||||
houseNumber: null,
|
||||
postalCode: null,
|
||||
city: null,
|
||||
kommun: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
phone: data.pyTelephone,
|
||||
email: data.pyAddresses.Email.pyEmailAddress,
|
||||
authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
|
||||
],
|
||||
authorizations: null,
|
||||
// authorizations: mapPegaAuthorizationToAuthorization(data.pyAccessGroup as PegaAuthorization),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'skapa-konto',
|
||||
loadChildren: () => import('./pages/create-account/create-account.module').then(m => m.CreateAccountModule),
|
||||
loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule),
|
||||
},
|
||||
{
|
||||
path: 'redigera-konto/:id',
|
||||
loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule),
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<section class="create-account">
|
||||
<digi-typography>
|
||||
<h1>Skapa nytt konto</h1>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam magna neque, interdum vel massa eget, condimentum
|
||||
rutrum velit. Sed vitae ullamcorper sem. Aliquam malesuada nunc sed purus mollis scelerisque. Curabitur bibendum
|
||||
leo quis ante porttitor tincidunt. Nam tincidunt imperdiet tortor eu suscipit. Maecenas ut dui est.
|
||||
</p>
|
||||
</digi-typography>
|
||||
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
|
||||
<div class="create-account__block">
|
||||
<h2>Personuppgifter</h2>
|
||||
<digi-ng-form-input
|
||||
class="create-account__input"
|
||||
formControlName="firstName"
|
||||
afLabel="Förnamn"
|
||||
afInvalidMessage="Förnamn är obligatoriskt"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="firstNameControl.invalid && firstNameControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
<digi-ng-form-input
|
||||
class="create-account__input"
|
||||
formControlName="lastName"
|
||||
afLabel="Efternamn"
|
||||
afInvalidMessage="Efternamn är obligatoriskt"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="lastNameControl.invalid && lastNameControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
<digi-ng-form-input
|
||||
class="create-account__input"
|
||||
formControlName="ssn"
|
||||
afLabel="Personnummer"
|
||||
[afInvalidMessage]="ssnControl.errors?.message || ''"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="ssnControl.invalid && ssnControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
<digi-ng-form-input
|
||||
class="create-account__input"
|
||||
formControlName="phone"
|
||||
afLabel="Telefonnummer"
|
||||
[afInvalidMessage]="phoneControl.errors?.message || ''"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="phoneControl.invalid && phoneControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
</div>
|
||||
<div class="create-account__block">
|
||||
<h2>Tjänst</h2>
|
||||
|
||||
<digi-ng-form-select afPlaceholder=" " afLabel="Välj tjänst" formControlName="service" [afSelectItems]="list">
|
||||
</digi-ng-form-select>
|
||||
</div>
|
||||
<fieldset class="create-account__fieldset">
|
||||
<legend>Tilldela behörigheter</legend>
|
||||
|
||||
<div class="create-account__permission-checkbox">
|
||||
<digi-ng-form-checkbox formControlName="permissions" afLabel="Välj administrera behörigheter">
|
||||
</digi-ng-form-checkbox>
|
||||
<digi-ng-popover class="create-account__popover">
|
||||
<p>Jag har tillräckligt med utrymme för att öppnas till höger (inline-start).</p>
|
||||
<a href="#">Tab to me</a>
|
||||
</digi-ng-popover>
|
||||
</div>
|
||||
|
||||
<div class="create-account__permission-checkbox">
|
||||
<digi-ng-form-checkbox formControlName="participant" afLabel="Välj ta emot deltagare"></digi-ng-form-checkbox>
|
||||
<digi-ng-popover class="create-account__popover">
|
||||
<p>Jag har tillräckligt med utrymme för att öppnas till höger (inline-start).</p>
|
||||
<a href="#">Tab to me</a>
|
||||
</digi-ng-popover>
|
||||
</div>
|
||||
|
||||
<div class="create-account__permission-checkbox">
|
||||
<digi-ng-form-checkbox
|
||||
formControlName="infoParticipant"
|
||||
afLabel="Välj rapportering, planering och infomration om deltagare"
|
||||
>
|
||||
</digi-ng-form-checkbox>
|
||||
<digi-ng-popover class="create-account__popover">
|
||||
<p>Jag har tillräckligt med utrymme för att öppnas till höger (inline-start).</p>
|
||||
<a href="#">Tab to me</a>
|
||||
</digi-ng-popover>
|
||||
</div>
|
||||
|
||||
<div class="create-account__permission-checkbox">
|
||||
<digi-ng-form-checkbox formControlName="orderBills" afLabel="Välj administrera ordrar och fakturor">
|
||||
</digi-ng-form-checkbox>
|
||||
<digi-ng-popover class="create-account__popover">
|
||||
<p>Jag har tillräckligt med utrymme för att öppnas till höger (inline-start).</p>
|
||||
<a href="#">Tab to me</a>
|
||||
</digi-ng-popover>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="create-account__footer">
|
||||
<digi-button af-type="reset" af-variation="secondary">Avbryt</digi-button>
|
||||
<digi-button af-type="submit">Registrera konto</digi-button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -1,70 +0,0 @@
|
||||
@import 'mixins/list';
|
||||
|
||||
.create-account {
|
||||
&__block {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
}
|
||||
&__combined-inputs {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__permission-checkbox {
|
||||
display: flex;
|
||||
height: 5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__fieldset {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin-top: var(--digi--layout--gutter);
|
||||
|
||||
legend {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-weight: var(--digi--typography--font-weight--bold);
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: var(--digi--layout--gutter--s);
|
||||
}
|
||||
}
|
||||
|
||||
&__pending-out-of-office-list {
|
||||
@include dafa__reset-list;
|
||||
margin-top: var(--digi--layout--gutter);
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--digi--layout--gutter--s);
|
||||
padding: var(--digi--layout--gutter--s);
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: var(--digi--ui--color--background--tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: var(--digi--layout--gutter);
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__popover {
|
||||
margin-left: var(--digi--layout--gutter);
|
||||
|
||||
::ng-deep .digi-ng-popover__container {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
import { FormSelectBaseItem } from '@af/digi-ng/_form/form-select-base';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Service } from '@dafa-enums/service.enum';
|
||||
import { EmployeeService } from '@dafa-services/api/employee.service';
|
||||
import { RequiredValidator } from '@dafa-validators/required.validator';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'dafa-create-account',
|
||||
templateUrl: './create-account.component.html',
|
||||
styleUrls: ['./create-account.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateAccountComponent {
|
||||
private _searchValue$ = new BehaviorSubject<string>('');
|
||||
|
||||
formGroup: FormGroup;
|
||||
todaysDate = new Date();
|
||||
|
||||
list: FormSelectBaseItem[] = [
|
||||
{
|
||||
name: Service.STOM,
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: Service.KVL,
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
name: Service.KROM,
|
||||
value: '3',
|
||||
},
|
||||
];
|
||||
|
||||
constructor(private formBuilder: FormBuilder, private employeeService: EmployeeService, private router: Router) {
|
||||
this.formGroup = this.formBuilder.group({
|
||||
firstName: this.formBuilder.control('', [RequiredValidator('Förnamn')]),
|
||||
lastName: this.formBuilder.control('', [RequiredValidator('Efternamn')]),
|
||||
phone: this.formBuilder.control('', [RequiredValidator('Telefonnummer')]),
|
||||
// ssn: this.formBuilder.control('', [RequiredValidator('Personnummer'), SocialSecurityNumberValidator()]),
|
||||
// employeeId: this.formBuilder.control('', [RequiredValidator('Personal-ID')]),
|
||||
ssn: this.formBuilder.control(''),
|
||||
employeeId: this.formBuilder.control(''),
|
||||
service: this.formBuilder.control(''),
|
||||
permissions: this.formBuilder.control(false),
|
||||
participant: this.formBuilder.control(false),
|
||||
infoParticipant: this.formBuilder.control(false),
|
||||
orderBills: this.formBuilder.control(false),
|
||||
});
|
||||
}
|
||||
|
||||
handleSearchSubmit(): void {
|
||||
// skicka searchvalue till en service och filtrera
|
||||
}
|
||||
|
||||
handleSearchInput($event: CustomEvent): void {
|
||||
this._searchValue$.next($event.detail.target.value);
|
||||
}
|
||||
|
||||
get formselectItem(): FormSelectBaseItem[] {
|
||||
return [
|
||||
{
|
||||
name: Service.STOM,
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: Service.KVL,
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
name: Service.KROM,
|
||||
value: '3',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get firstNameControl(): AbstractControl {
|
||||
return this.formGroup.get('firstName');
|
||||
}
|
||||
get lastNameControl(): AbstractControl {
|
||||
return this.formGroup.get('lastName');
|
||||
}
|
||||
get ssnControl(): AbstractControl {
|
||||
return this.formGroup.get('ssn');
|
||||
}
|
||||
get phoneControl(): AbstractControl {
|
||||
return this.formGroup.get('phone');
|
||||
}
|
||||
|
||||
private _markFormAsDirty(): void {
|
||||
Object.keys(this.formGroup.controls).forEach(control => {
|
||||
this.formGroup.get(control).markAsDirty();
|
||||
this.formGroup.get(control).markAsTouched();
|
||||
});
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
if (this.formGroup.valid) {
|
||||
const submittableValues = {
|
||||
...this.formGroup.value,
|
||||
fullName: `${this.firstNameControl.value} ${this.lastNameControl.value}`,
|
||||
};
|
||||
|
||||
delete submittableValues.outOfOfficeStart;
|
||||
delete submittableValues.outOfOfficeEnd;
|
||||
|
||||
const post = this.employeeService.postNewEmployee(submittableValues).subscribe({
|
||||
next: id => {
|
||||
this.router.navigate(['/administration', 'personal', id]);
|
||||
},
|
||||
complete: () => {
|
||||
post.unsubscribe();
|
||||
},
|
||||
});
|
||||
|
||||
// this.formGroup.reset();
|
||||
} else {
|
||||
console.error('Form is invalid, do something...');
|
||||
this._markFormAsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,76 +7,58 @@
|
||||
perferendis commodi.
|
||||
</p>
|
||||
|
||||
<div class="staff-card__contents">
|
||||
<div class="staff-card__column">
|
||||
<div class="employee-card__contents">
|
||||
<div class="employee-card__column">
|
||||
<h2>Kontaktuppgifter</h2>
|
||||
|
||||
<dl>
|
||||
<dt>Namn</dt>
|
||||
<dd *ngIf="detailedEmployeeData.fullName; else emptyDD">{{ detailedEmployeeData.fullName }}</dd>
|
||||
<dt>Personnummer</dt>
|
||||
<dd *ngIf="detailedEmployeeData.ssn; else emptyDD">{{ detailedEmployeeData.ssn }}</dd>
|
||||
<dt>Telefonnummer</dt>
|
||||
<dd *ngIf="detailedEmployeeData.phone; else emptyDD">
|
||||
<a [attr.href]="'tel:' + detailedEmployeeData.phone">{{ detailedEmployeeData.phone }}</a>
|
||||
</dd>
|
||||
<dt>Epost adress</dt>
|
||||
<dd *ngIf="detailedEmployeeData.email; else emptyDD">
|
||||
<a [attr.href]="'mailto:' + detailedEmployeeData.email">{{ detailedEmployeeData.email }}</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="staff-card__column">
|
||||
<div class="employee-card__column">
|
||||
<h2>Uppgifter om arbete</h2>
|
||||
|
||||
<dl>
|
||||
<dt>Utförandeverksamhet</dt>
|
||||
<dd *ngIf="detailedEmployeeData.organization; else emptyDD">
|
||||
{{ detailedEmployeeData.organization.address.city }}
|
||||
</dd>
|
||||
<dt>Behörigheter</dt>
|
||||
<dd *ngIf="detailedEmployeeData.authorizations?.length; else emptyDD">
|
||||
{{ detailedEmployeeData.authorizations.join(', ') }}
|
||||
</dd>
|
||||
<dt>Frånvaroperiod</dt>
|
||||
<ng-container *ngIf="detailedEmployeeData.outOfOffice?.length; else emptyDD">
|
||||
<dd *ngFor="let date of detailedEmployeeData.outOfOffice">
|
||||
{{ date.start | localDate }} - {{ date.end | localDate }}
|
||||
</dd>
|
||||
<ng-container *ngIf="detailedEmployeeData.authorizations.length; else emptyDD">
|
||||
<dd *ngFor="let authorization of detailedEmployeeData.authorizations">{{ authorization.name }}</dd>
|
||||
</ng-container>
|
||||
<dt>Tjänster</dt>
|
||||
<ng-container *ngIf="detailedEmployeeData.services.length; else emptyDD">
|
||||
<dd *ngFor="let service of detailedEmployeeData.services">{{ service.name }}</dd>
|
||||
</ng-container>
|
||||
<dt>Tjänst</dt>
|
||||
<dd *ngIf="detailedEmployeeData.services.length; else emptyDD">
|
||||
{{ detailedEmployeeData.services.join(', ') }}
|
||||
</dd>
|
||||
<dt>Språk</dt>
|
||||
<dd>{{ detailedEmployeeData.languages?.join(', ') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="staff-card__column">
|
||||
<h2>Utförande verksamhet</h2>
|
||||
<div class="employee-card__column">
|
||||
<h2>Utförande verksamheter</h2>
|
||||
|
||||
<ul *ngIf="detailedEmployeeData.agencies?.length" class="staff-card__agencies">
|
||||
<li *ngFor="let agency of detailedEmployeeData.agencies" class="staff-card__agency">
|
||||
<h3>{{ agency.name }}</h3>
|
||||
<ul *ngIf="detailedEmployeeData.organizations?.length" class="employee-card__organizations">
|
||||
<li *ngFor="let organization of detailedEmployeeData.organizations" class="employee-card__organization">
|
||||
<h3>{{ organization.name }}</h3>
|
||||
<dl>
|
||||
<dt>KA-nummer</dt>
|
||||
<dd>{{ agency.kaNumber }}</dd>
|
||||
<dd>{{ organization.kaNumber }}</dd>
|
||||
<dt>Adress</dt>
|
||||
<dd>{{ agency.address.street }} {{ agency.address.houseNumber }}</dd>
|
||||
<dd>{{ agency.address.postalCode }} {{ agency.address.city }}</dd>
|
||||
<dd>{{ organization.address.street }} {{ organization.address.houseNumber }}</dd>
|
||||
<dd>{{ organization.address.postalCode }} {{ organization.address.city }}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="staff-card__column">
|
||||
<div class="employee-card__column">
|
||||
<digi-ng-layout-expansion-panel>
|
||||
<h3 style="margin-bottom: 0" data-slot-trigger>
|
||||
Tilldelade deltagare ({{ detailedEmployeeData.participants?.length || 0 }})
|
||||
</h3>
|
||||
<ng-container *ngIf="detailedEmployeeData.participants?.length; else noParticipantsInfo">
|
||||
<ul class="staff-card__participants">
|
||||
<li *ngIf="detailedEmployeeData.participants.length > 1" class="staff-card__participant">
|
||||
<ul class="employee-card__participants">
|
||||
<li *ngIf="detailedEmployeeData.participants.length > 1" class="employee-card__participant">
|
||||
<digi-form-checkbox
|
||||
af-variation="primary"
|
||||
af-label="Välj alla"
|
||||
@@ -88,7 +70,7 @@
|
||||
"
|
||||
></digi-form-checkbox>
|
||||
</li>
|
||||
<li *ngFor="let participant of detailedEmployeeData.participants" class="staff-card__participant">
|
||||
<li *ngFor="let participant of detailedEmployeeData.participants" class="employee-card__participant">
|
||||
<digi-form-checkbox
|
||||
af-variation="primary"
|
||||
[afLabel]="participant.fullName"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
&__contents {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
gap: $digi--layout--gutter--xl $digi--layout--gutter--l;
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
@@ -37,14 +37,14 @@
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
&__agencies {
|
||||
&__organizations {
|
||||
@include dafa__reset-list;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
&__agency {
|
||||
&__organization {
|
||||
border: 1px solid #333;
|
||||
padding: var(--digi--layout--gutter);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
||||
})
|
||||
export class EmployeeCardComponent extends UnsubscribeDirective {
|
||||
detailedEmployeeData$: Observable<Employee>;
|
||||
authorizationsAsString$: Observable<string>;
|
||||
private _pendingSelectedParticipants$ = new BehaviorSubject<string[]>([]);
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute, private employeeService: EmployeeService) {
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<section class="employee-form">
|
||||
<digi-typography>
|
||||
<h1>Skapa nytt konto</h1>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam magna neque, interdum vel massa eget, condimentum
|
||||
rutrum velit. Sed vitae ullamcorper sem. Aliquam malesuada nunc sed purus mollis scelerisque. Curabitur bibendum
|
||||
leo quis ante porttitor tincidunt. Nam tincidunt imperdiet tortor eu suscipit. Maecenas ut dui est.
|
||||
</p>
|
||||
</digi-typography>
|
||||
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
|
||||
<digi-form-error-list
|
||||
class="employee-form__error-list"
|
||||
*ngIf="formGroup.invalid && submitted && formErrors.length"
|
||||
af-heading="Felmeddelanden"
|
||||
>
|
||||
<a *ngFor="let error of formErrors" [routerLink]="" [fragment]="'employee-form-' + error.id">{{
|
||||
error.message
|
||||
}}</a>
|
||||
</digi-form-error-list>
|
||||
|
||||
<div class="employee-form__block">
|
||||
<digi-typography>
|
||||
<h2>Personuppgifter</h2>
|
||||
</digi-typography>
|
||||
<digi-ng-form-input
|
||||
afId="employee-form-firstName"
|
||||
class="employee-form__input"
|
||||
formControlName="firstName"
|
||||
afLabel="Förnamn"
|
||||
afInvalidMessage="Förnamn är obligatoriskt"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="firstNameControl.invalid && firstNameControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
<digi-ng-form-input
|
||||
afId="employee-form-lastName"
|
||||
class="employee-form__input"
|
||||
formControlName="lastName"
|
||||
afLabel="Efternamn"
|
||||
afInvalidMessage="Efternamn är obligatoriskt"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="lastNameControl.invalid && lastNameControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
<digi-ng-form-input
|
||||
afId="employee-form-ssn"
|
||||
class="employee-form__input"
|
||||
formControlName="ssn"
|
||||
afLabel="Personnummer"
|
||||
[afInvalidMessage]="ssnControl.errors?.message || ''"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="ssnControl.invalid && ssnControl.dirty"
|
||||
></digi-ng-form-input>
|
||||
</div>
|
||||
<div class="employee-form__block" *ngIf="services$ | async as services">
|
||||
<digi-typography>
|
||||
<h2>Tjänst</h2>
|
||||
</digi-typography>
|
||||
|
||||
<!-- TODO: Right now there are issues within digi-form-filter where Angular
|
||||
attributes are not working. Also event-handling is not working.
|
||||
The digi-team is notified -->
|
||||
<digi-form-filter
|
||||
af-id="employee-form-services"
|
||||
af-name="Tjänst"
|
||||
af-filter-button-text="Välj tjänst"
|
||||
af-submit-button-text="Spara tjänster"
|
||||
(afOnSubmitFilters)="addPendingServicesToFormControl()"
|
||||
(afOnFocusOut)="addPendingServicesToFormControl()"
|
||||
(afOnResetFilters)="resetPendingServices()"
|
||||
>
|
||||
<digi-form-checkbox
|
||||
af-variation="primary"
|
||||
af-label="Välj alla"
|
||||
[afChecked]="pendingServices?.length"
|
||||
[afIndeterminate]="pendingServices?.length !== services?.length"
|
||||
(afOnChange)="togglePendingService(services, $event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
<digi-form-checkbox
|
||||
*ngFor="let service of services"
|
||||
af-variation="primary"
|
||||
[afLabel]="service.name"
|
||||
[afValue]="service.id"
|
||||
[afChecked]="pendingServices.includes(service)"
|
||||
(afOnChange)="togglePendingService(service, $event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
</digi-form-filter>
|
||||
<digi-form-validation-message
|
||||
class="employee-form__validation-message"
|
||||
*ngIf="servicesControl.invalid && servicesControl.dirty"
|
||||
af-variation="error"
|
||||
>
|
||||
{{ servicesControl.errors.message }}
|
||||
</digi-form-validation-message>
|
||||
|
||||
<div class="employee-form__added-services">
|
||||
<digi-typography>
|
||||
<h3>Valda tjänster</h3>
|
||||
</digi-typography>
|
||||
<ul *ngIf="servicesControl.value?.length; else noServicesAdded" class="employee-form__services">
|
||||
<li class="employee-form__service-item" *ngFor="let service of servicesControl.value">
|
||||
<span>{{ service.name }}</span>
|
||||
<digi-button af-variation="tertiary" (afOnClick)="removeServiceFromFormControl(service.id)">
|
||||
<digi-icon-x-button slot="icon"></digi-icon-x-button>
|
||||
</digi-button>
|
||||
</li>
|
||||
</ul>
|
||||
<ng-template #noServicesAdded>Inga tjänster vald.</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="employee-form__block" *ngIf="authorizations$ | async as authorizations">
|
||||
<fieldset class="employee-form__fieldset">
|
||||
<digi-typography>
|
||||
<legend>Tilldela behörigheter</legend>
|
||||
</digi-typography>
|
||||
|
||||
<ul class="employee-form__authorizations">
|
||||
<li *ngFor="let authorization of authorizations; let first = first" class="employee-form__authorization-item">
|
||||
<digi-form-checkbox
|
||||
[afId]="(first && 'employee-form-authorizations') || undefined"
|
||||
af-variation="primary"
|
||||
[afValidation]="authorizationsControl.invalid && authorizationsControl.dirty && 'error'"
|
||||
[afLabel]="authorization.name"
|
||||
[afValue]="authorization.id"
|
||||
[afChecked]="authorizationsControl.value.includes(authorization)"
|
||||
(afOnChange)="toggleAuthorization(authorization, $event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
<digi-ng-popover class="employee-form__popover">
|
||||
<p>Info om behörighet</p>
|
||||
</digi-ng-popover>
|
||||
</li>
|
||||
</ul>
|
||||
<digi-form-validation-message
|
||||
class="employee-form__validation-message"
|
||||
*ngIf="authorizationsControl.invalid && authorizationsControl.dirty"
|
||||
af-variation="error"
|
||||
>
|
||||
{{ authorizationsControl.errors.message }}
|
||||
</digi-form-validation-message>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="employee-form__footer">
|
||||
<digi-button af-type="reset" af-variation="secondary" (afOnClick)="resetForm($event.detail)">Avbryt</digi-button>
|
||||
<digi-button af-type="submit">Registrera konto</digi-button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -0,0 +1,84 @@
|
||||
@import 'mixins/list';
|
||||
@import 'variables/gutters';
|
||||
|
||||
.employee-form {
|
||||
&__block {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
}
|
||||
|
||||
&__input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__fieldset {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
|
||||
legend {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: var(--digi--typography--font-weight--semibold);
|
||||
font-size: var(--digi--typography--font-size--h2--desktop);
|
||||
margin-bottom: var(--digi-typography--margin--h2);
|
||||
}
|
||||
}
|
||||
|
||||
&__validation-message {
|
||||
display: block;
|
||||
margin-top: var(--digi--layout--gutter--s);
|
||||
}
|
||||
|
||||
&__added-services {
|
||||
margin-top: var(--digi--layout--gutter);
|
||||
max-width: 50rem;
|
||||
}
|
||||
|
||||
&__services,
|
||||
&__authorizations {
|
||||
@include dafa__reset-list;
|
||||
margin-bottom: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__service-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--digi--layout--gutter--xs) var(--digi--layout--gutter--s);
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: var(--digi--ui--color--background--secondary);
|
||||
}
|
||||
}
|
||||
|
||||
&__authorization-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
|
||||
&__error-list {
|
||||
display: block;
|
||||
margin-top: $digi--layout--gutter--l;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: $digi--layout--gutter--xl;
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__popover {
|
||||
margin-left: var(--digi--layout--gutter);
|
||||
|
||||
::ng-deep .digi-ng-popover__container {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,17 +9,17 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { CreateAccountComponent } from './create-account.component';
|
||||
import { EmployeeFormComponent } from './employee-form.component';
|
||||
|
||||
describe('CreateAccountComponent', () => {
|
||||
let component: CreateAccountComponent;
|
||||
let fixture: ComponentFixture<CreateAccountComponent>;
|
||||
describe('EmployeeFormComponent', () => {
|
||||
let component: EmployeeFormComponent;
|
||||
let fixture: ComponentFixture<EmployeeFormComponent>;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [CreateAccountComponent],
|
||||
declarations: [EmployeeFormComponent],
|
||||
imports: [
|
||||
RouterTestingModule,
|
||||
HttpClientTestingModule,
|
||||
@@ -36,7 +36,7 @@ describe('CreateAccountComponent', () => {
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CreateAccountComponent);
|
||||
fixture = TestBed.createComponent(EmployeeFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -0,0 +1,165 @@
|
||||
import { FormSelectBaseItem } from '@af/digi-ng/_form/form-select-base';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Authorization } from '@dafa-models/authorization.model';
|
||||
import { Service } from '@dafa-models/service.model';
|
||||
import { AuthorizationService } from '@dafa-services/api/authorizations.service';
|
||||
import { EmployeeService } from '@dafa-services/api/employee.service';
|
||||
import { ServiceService } from '@dafa-services/api/service.service';
|
||||
import { SocialSecurityNumberValidator } from '@dafa-utils/validators/social-security-number.validator';
|
||||
import { RequiredValidator } from '@dafa-validators/required.validator';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'dafa-employee-form',
|
||||
templateUrl: './employee-form.component.html',
|
||||
styleUrls: ['./employee-form.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class EmployeeFormComponent {
|
||||
services$: Observable<Service[]> = this.serviceService.services$;
|
||||
authorizations$: Observable<Authorization[]> = this.authorizationService.authorizations$;
|
||||
servicesSelectItems$: Observable<FormSelectBaseItem[]> = this.services$.pipe(
|
||||
map(services => services.map(({ name, id }) => ({ name, value: id })))
|
||||
);
|
||||
private _pendingServices$ = new BehaviorSubject<Service[]>([]);
|
||||
|
||||
formGroup: FormGroup = this.formBuilder.group({
|
||||
firstName: this.formBuilder.control('', [RequiredValidator('Förnamn')]),
|
||||
lastName: this.formBuilder.control('', [RequiredValidator('Efternamn')]),
|
||||
ssn: this.formBuilder.control('', [RequiredValidator('Personnummer'), SocialSecurityNumberValidator()]),
|
||||
// services: this.formBuilder.control([], [RequiredValidator('en tjänst')]),
|
||||
services: this.formBuilder.control([]),
|
||||
authorizations: this.formBuilder.control([], [RequiredValidator('en behörighet')]),
|
||||
});
|
||||
todaysDate = new Date();
|
||||
submitted = false;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private employeeService: EmployeeService,
|
||||
private serviceService: ServiceService,
|
||||
private authorizationService: AuthorizationService,
|
||||
private router: Router
|
||||
) {
|
||||
this.formGroup.valueChanges.subscribe(values => console.log(this.formGroup));
|
||||
}
|
||||
|
||||
get pendingServices(): Service[] {
|
||||
return this._pendingServices$.getValue();
|
||||
}
|
||||
|
||||
get firstNameControl(): AbstractControl {
|
||||
return this.formGroup.get('firstName');
|
||||
}
|
||||
get lastNameControl(): AbstractControl {
|
||||
return this.formGroup.get('lastName');
|
||||
}
|
||||
get ssnControl(): AbstractControl {
|
||||
return this.formGroup.get('ssn');
|
||||
}
|
||||
get servicesControl(): AbstractControl {
|
||||
return this.formGroup.get('services');
|
||||
}
|
||||
get authorizationsControl(): AbstractControl {
|
||||
return this.formGroup.get('authorizations');
|
||||
}
|
||||
|
||||
get formErrors(): { id: string; message: string }[] {
|
||||
const controlsWithErrors = Object.keys(this.formGroup.controls).filter(
|
||||
key => !!this.formGroup.controls[key].errors
|
||||
);
|
||||
|
||||
return controlsWithErrors.map(key => ({
|
||||
id: key,
|
||||
message: this.formGroup.controls[key].errors.message,
|
||||
}));
|
||||
}
|
||||
|
||||
private _markFormAsDirty(): void {
|
||||
Object.keys(this.formGroup.controls).forEach(control => {
|
||||
this.formGroup.get(control).markAsDirty();
|
||||
this.formGroup.get(control).markAsTouched();
|
||||
});
|
||||
}
|
||||
|
||||
toggleAuthorization(authorization: Authorization, checked: boolean): void {
|
||||
const currentAuthorizations = this.authorizationsControl.value;
|
||||
|
||||
if (checked) {
|
||||
this.authorizationsControl.patchValue([...currentAuthorizations, authorization]);
|
||||
} else {
|
||||
this.authorizationsControl.patchValue(
|
||||
currentAuthorizations.filter(currentAuthorization => currentAuthorization.id !== authorization.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
togglePendingService(service: Service, checked: boolean): void {
|
||||
const currentPendingServices = this.pendingServices;
|
||||
|
||||
if (checked) {
|
||||
this._pendingServices$.next([...currentPendingServices, service]);
|
||||
} else {
|
||||
this._pendingServices$.next(currentPendingServices.filter(currentService => currentService.id !== service.id));
|
||||
}
|
||||
}
|
||||
|
||||
toggleAllPendingServices(services: Service[], checked: boolean): void {
|
||||
this._pendingServices$.next(checked ? services : []);
|
||||
}
|
||||
|
||||
resetPendingServices(): void {
|
||||
this._pendingServices$.next([]);
|
||||
}
|
||||
|
||||
addPendingServicesToFormControl(): void {
|
||||
this.servicesControl.patchValue(this.pendingServices);
|
||||
}
|
||||
|
||||
removeServiceFromFormControl(id: string): void {
|
||||
const currentAddedServices = this.servicesControl.value;
|
||||
|
||||
this.servicesControl.patchValue(currentAddedServices.filter(currentService => currentService.id !== id));
|
||||
}
|
||||
|
||||
setFocusOnInvalidInput(event: CustomEvent): void {
|
||||
console.log(event.target);
|
||||
}
|
||||
|
||||
resetForm(event: Event): void {
|
||||
event.preventDefault();
|
||||
this.formGroup.reset({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
ssn: '',
|
||||
services: [],
|
||||
authorizations: [],
|
||||
});
|
||||
// Object.keys(this.formGroup.controls).forEach(controlKey => this.formGroup.controls[controlKey].markAsPristine());
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
this.submitted = true;
|
||||
if (this.formGroup.valid) {
|
||||
const submittableValues = {
|
||||
...this.formGroup.value,
|
||||
};
|
||||
const post = this.employeeService.postNewEmployee(submittableValues).subscribe({
|
||||
next: id => {
|
||||
this.router.navigate(['/administration', 'personal', id]);
|
||||
},
|
||||
complete: () => {
|
||||
post.unsubscribe();
|
||||
},
|
||||
});
|
||||
|
||||
// this.formGroup.reset();
|
||||
} else {
|
||||
console.error('Form is invalid, do something...');
|
||||
this._markFormAsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
|
||||
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||
import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input';
|
||||
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
import { DigiNgPopoverModule } from '@af/digi-ng/_popover/popover';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { LocalDatePipeModule } from '@dafa-shared/pipes/local-date/local-date.module';
|
||||
import { CreateAccountComponent } from './create-account.component';
|
||||
import { DigiNgPopoverModule } from '@af/digi-ng/_popover/popover';
|
||||
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
|
||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
import { EmployeeFormComponent } from './employee-form.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [CreateAccountComponent],
|
||||
declarations: [EmployeeFormComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{ path: '', component: CreateAccountComponent }]),
|
||||
RouterModule.forChild([{ path: '', component: EmployeeFormComponent }]),
|
||||
ReactiveFormsModule,
|
||||
LocalDatePipeModule,
|
||||
DigiNgFormInputModule,
|
||||
@@ -27,4 +27,4 @@ import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
DigiNgFormCheckboxModule,
|
||||
],
|
||||
})
|
||||
export class CreateAccountModule {}
|
||||
export class EmployeeFormModule {}
|
||||
@@ -22,23 +22,37 @@
|
||||
</button>
|
||||
</th>
|
||||
<th scope="col" class="employees-list__column-head">
|
||||
<button class="employees-list__sort-button" (click)="handleSort('organization')">
|
||||
Utförandeverksamhet
|
||||
<button class="employees-list__sort-button" (click)="handleSort('organizations')">
|
||||
Utförandeverksamheter
|
||||
<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-down class="employees-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
|
||||
</ng-container>
|
||||
</button>
|
||||
</th>
|
||||
<th scope="col" class="employees-list__column-head">Redigera</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let employees of pagedEmployees">
|
||||
<tr *ngFor="let employee of pagedEmployees">
|
||||
<th scope="row">
|
||||
<a [routerLink]="employees.id" class="employees-list__link">{{ employees.fullName }}</a>
|
||||
<a [routerLink]="employee.id" class="employees-list__link">{{ employee.fullName }}</a>
|
||||
</th>
|
||||
<td>{{ employees.services.length ? employees.services.join(', ') : '-' }}</td>
|
||||
<td>{{ employees.organization.address.city || '-' }}</td>
|
||||
<td>
|
||||
<ng-container *ngFor="let service of employee.services; let last = last">
|
||||
{{ service.name }}<ng-container *ngIf="!last">, </ng-container>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td>
|
||||
<ng-container *ngFor="let organization of employee.organizations; let last = last">
|
||||
{{ organization.address.city }}<ng-container *ngIf="!last">, </ng-container>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td>
|
||||
<digi-button af-variation="tertiary">
|
||||
<digi-icon-edit style="--digi--ui--width--icon: 1.25rem;" slot="icon"></digi-icon-edit>
|
||||
</digi-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -17,8 +17,6 @@ export class EmployeesListComponent {
|
||||
private _currentPage$ = new BehaviorSubject<number>(1);
|
||||
private _employeesPerPage = 10;
|
||||
|
||||
private _searchValue$ = new BehaviorSubject<string>('');
|
||||
|
||||
get currentPage(): number {
|
||||
return this._currentPage$.getValue();
|
||||
}
|
||||
|
||||
24
apps/dafa-web/src/app/services/api/authorizations.service.ts
Normal file
24
apps/dafa-web/src/app/services/api/authorizations.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@dafa-environment';
|
||||
import {
|
||||
Authorization,
|
||||
AuthorizationApiResponse,
|
||||
mapAuthorizationApiResponseToAuthorization,
|
||||
} from '@dafa-models/authorization.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthorizationService {
|
||||
private _authorizationsApiUrl = `${environment.api.url}/authorizations`;
|
||||
public authorizations$: Observable<Authorization[]> = this.httpClient
|
||||
.get<AuthorizationApiResponse[]>(this._authorizationsApiUrl, API_HEADERS)
|
||||
.pipe(map(response => response.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization))));
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
}
|
||||
@@ -70,6 +70,8 @@ export class EmployeeService {
|
||||
}
|
||||
|
||||
public postNewEmployee(employeeData: Employee): Observable<string> {
|
||||
console.log(employeeData);
|
||||
return;
|
||||
return this.httpClient
|
||||
.post<PegaEmployeeApiPostResponse>(
|
||||
this._employeeApiUrl,
|
||||
|
||||
20
apps/dafa-web/src/app/services/api/service.service.ts
Normal file
20
apps/dafa-web/src/app/services/api/service.service.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@dafa-environment';
|
||||
import { mapServiceApiResponseToService, Service, ServiceApiResponse } from '@dafa-models/service.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
const API_HEADERS = { headers: environment.api.headers };
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ServiceService {
|
||||
private _servicesApiUrl = `${environment.api.url}/services`;
|
||||
public services$: Observable<Service[]> = this.httpClient
|
||||
.get<ServiceApiResponse[]>(this._servicesApiUrl, API_HEADERS)
|
||||
.pipe(map(response => response.map(service => mapServiceApiResponseToService(service))));
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
}
|
||||
@@ -3,8 +3,14 @@ import { ValidationError } from '@dafa-models/validation-error.model';
|
||||
|
||||
export function RequiredValidator(label = 'Fältet'): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
if (control && !control.value) {
|
||||
return { type: 'required', message: `${label} är obligatoriskt` };
|
||||
if (control) {
|
||||
if (!control.value) {
|
||||
return { type: 'required', message: `${label} är obligatoriskt` };
|
||||
}
|
||||
|
||||
if (Array.isArray(control.value) && !control.value.length) {
|
||||
return { type: 'required', message: `Minst ${label} behöver väljas` };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.6 KiB |
@@ -3,6 +3,7 @@ export const environment = {
|
||||
api: {
|
||||
meet: 'https://dafa-utv.tocp.arbetsformedlingen.se/prweb/api/meettest/v1',
|
||||
default: 'https://dafa-utv.tocp.arbetsformedlingen.se/prweb/api/v1/data',
|
||||
url: '/api',
|
||||
headers: {
|
||||
Authorization: 'Basic dGVzdHVzZXIxOmRhZmFAMTIz', // user: testuser1, password: dafa@123
|
||||
},
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-weight: var(--digi--typography--font-weight);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
28
mock-api/dafa-web/scripts/authorizations.js
Normal file
28
mock-api/dafa-web/scripts/authorizations.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import faker from 'faker';
|
||||
|
||||
faker.locale = 'sv';
|
||||
|
||||
function generateAuthorizations() {
|
||||
return [
|
||||
{
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'Hantera behörigheter',
|
||||
},
|
||||
{
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'Hantera användare',
|
||||
},
|
||||
{
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'Hantera organisation',
|
||||
},
|
||||
{
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'Hantera ekonomi',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default {
|
||||
generate: generateAuthorizations,
|
||||
};
|
||||
@@ -16,9 +16,7 @@ function generateCurrentUser() {
|
||||
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)],
|
||||
organizations: [ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)]],
|
||||
authorizations: chooseRandom(AUTHORIZATIONS, faker.datatype.number(3)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import faker from 'faker';
|
||||
import kommuner from './kommuner.js';
|
||||
import languages from './languages.js';
|
||||
import authorizations from './authorizations.js';
|
||||
import organizations from './organizations.js';
|
||||
import services from './services.js';
|
||||
|
||||
faker.locale = 'sv';
|
||||
|
||||
const SERVICES = services.generate();
|
||||
const KOMMUN = kommuner.generate();
|
||||
const ORGANIZATIONS = organizations.generate();
|
||||
const STATUSES = [true, false];
|
||||
const LANGUAGES = languages.generate();
|
||||
const AUTHORIZATIONS = ['Hantera användare', 'Hantera origisation', 'Hantera ekonomi'];
|
||||
|
||||
function generateEmployees(amount = 10) {
|
||||
const employees = [];
|
||||
@@ -27,24 +22,9 @@ function generateEmployees(amount = 10) {
|
||||
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)],
|
||||
services: [SERVICES[Math.floor(Math.random() * SERVICES.length)].name],
|
||||
authorizations: AUTHORIZATIONS,
|
||||
// active: STATUSES[Math.floor(Math.random() * STATUSES.length)],
|
||||
// languages: [
|
||||
// LANGUAGES.find(language => language.name === 'Svenska'),
|
||||
// ...chooseRandom(LANGUAGES, faker.datatype.number(3)),
|
||||
// ],
|
||||
// outOfOffice: STATUSES[Math.floor(Math.random() * STATUSES.length)]
|
||||
// ? [
|
||||
// {
|
||||
// start: new Date('2021-07-12'),
|
||||
// end: new Date('2021-07-24'),
|
||||
// },
|
||||
// ]
|
||||
// : [],
|
||||
organizations: [ORGANIZATIONS[Math.floor(Math.random() * ORGANIZATIONS.length)]],
|
||||
services: [SERVICES[Math.floor(Math.random() * SERVICES.length)]],
|
||||
authorizations: authorizations.generate(),
|
||||
};
|
||||
|
||||
employees.push(person);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import authorizations from './authorizations.js';
|
||||
import currentUser from './current-user.js';
|
||||
import employees from './employees.js';
|
||||
import kommuner from './kommuner.js';
|
||||
@@ -20,6 +21,7 @@ const apiData = {
|
||||
employeeId: generatedEmployees[Math.floor(Math.random() * generatedEmployees.length)].id,
|
||||
})),
|
||||
currentUser: currentUser.generate(),
|
||||
authorizations: authorizations.generate(),
|
||||
};
|
||||
|
||||
fs.writeFileSync('api.json', JSON.stringify(apiData, null, '\t'));
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import faker from 'faker';
|
||||
|
||||
faker.locale = 'sv';
|
||||
|
||||
function generateServices() {
|
||||
const services = [
|
||||
{
|
||||
id: 1,
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'KROM',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
id: faker.datatype.uuid(),
|
||||
name: 'KVL',
|
||||
},
|
||||
// {
|
||||
// id: 3,
|
||||
// id: faker.datatype.uuid(),
|
||||
// name: 'STOM',
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// id: faker.datatype.uuid(),
|
||||
// name: 'YSM',
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// id: faker.datatype.uuid(),
|
||||
// name: 'AUB',
|
||||
// },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user