From 39cdcff53d80f30273e45afe69c0709e914323ce Mon Sep 17 00:00:00 2001 From: Nicolas Fuentes Maturana Date: Tue, 17 Aug 2021 09:35:10 +0200 Subject: [PATCH] Merge pull request #48 in TEA/dafa-web-monorepo from feature/TV-335 to develop Squashed commit of the following: commit 8135e484f44885875bd28dbb0cb7f89fdb7223fa Author: fueno Date: Sat Aug 14 19:32:56 2021 +0200 TV-335 refactoring validators commit f85a832f9f3e7161380b4b204639a71129eee2fb Author: fueno Date: Sat Aug 14 18:18:30 2021 +0200 TV-335 refactoring commit 81e03f9377ae3ea4d1e2a196d1074e2e620472b1 Author: fueno Date: Fri Aug 13 17:51:33 2021 +0200 TV-335 spelling commit 8542d0d20743e6a6d777c083e3559416b96529bc Author: fueno Date: Fri Aug 13 17:48:10 2021 +0200 TV-335 review fixes commit 07a5da0576d2d42f043b54cb655aac2070ab218c Author: fueno Date: Fri Aug 13 13:22:15 2021 +0200 TV-335 use digi layout gutters for margins commit 9c99d78b10a4c7414d83005ba1847ddd5420d7f5 Author: fueno Date: Fri Aug 13 11:47:32 2021 +0200 TV-335 added ChangeDetectorRef, refactoring submitForm commit 21b4e3ca5d72b3cedfd0795ef2d924f3380d167b Author: fueno Date: Fri Aug 13 11:09:40 2021 +0200 TV-335 refactoring due to review comments commit 9e389c7e67e56e9965453822c38c5fce44e99b35 Author: fueno Date: Fri Aug 13 00:43:57 2021 +0200 TV-335 refactoring commit e3c98416bea1ff705b17bd5bdd85112cbff13c91 Author: fueno Date: Thu Aug 12 18:56:21 2021 +0200 TV-335 invalidFormMessage commit f3f54fc8c331b7b00a4ebef72e80e7fb56640985 Merge: e68eb7c 466abd1 Author: fueno Date: Thu Aug 12 16:55:33 2021 +0200 Merge branch 'develop' into feature/TV-335 commit e68eb7cb09aecb2252f542bece1228db453d3cf6 Author: fueno Date: Thu Aug 12 16:49:40 2021 +0200 TV-335 email validator commit eee201919eca8186e0864a69e5f80fd696e40b91 Author: fueno Date: Thu Aug 12 16:23:30 2021 +0200 TV-335 post mock data api, notification alert commit 46b185ad82ac5946ccfdf173a960fde7261e51bd Author: fueno Date: Thu Aug 12 11:56:43 2021 +0200 TV-335 prepare method for post request commit 8b983fbf34ff39b6252cff9bfdc904d6aeeb3a46 Author: fueno Date: Thu Aug 12 11:54:25 2021 +0200 TV-335 routing commit 01bac9359100a22c3fafa482e828e88727c7ab29 Author: fueno Date: Thu Aug 12 11:53:30 2021 +0200 TV-335 employee-invite component --- .../administration-routing.module.ts | 4 ++ .../employee-invite.component.html | 60 +++++++++++++++++++ .../employee-invite.component.scss | 31 ++++++++++ .../employee-invite.component.spec.ts | 33 ++++++++++ .../employee-invite.component.ts | 56 +++++++++++++++++ .../employee-invite/employee-invite.module.ts | 22 +++++++ .../pages/employees/employees.component.html | 2 +- .../api/employee-invite.response.model.ts | 4 ++ .../models/employee-invite-mock-data.model.ts | 3 + .../shared/services/api/employee.service.ts | 19 +++++- 10 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.html create mode 100644 apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.scss create mode 100644 apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.spec.ts create mode 100644 apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts create mode 100644 apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.module.ts create mode 100644 apps/dafa-web/src/app/shared/models/api/employee-invite.response.model.ts create mode 100644 apps/dafa-web/src/app/shared/models/employee-invite-mock-data.model.ts diff --git a/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts b/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts index 69d5fd4..139d925 100644 --- a/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts +++ b/apps/dafa-web/src/app/pages/administration/administration-routing.module.ts @@ -19,6 +19,10 @@ const routes: Routes = [ path: 'skapa-konto', loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule), }, + { + path: 'bjuda-in', + loadChildren: () => import('./pages/employee-invite/employee-invite.module').then(m => m.EmployeeInviteModule), + }, { path: 'redigera-konto/:employeeId', loadChildren: () => import('./pages/employee-form/employee-form.module').then(m => m.EmployeeFormModule), diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.html b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.html new file mode 100644 index 0000000..efe2bde --- /dev/null +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.html @@ -0,0 +1,60 @@ + +
+ +

Skapa nytt personalkonto

+

Såhär skapar du ett nytt personalkonto:

+
    +
  1. Skicka en inbjudningslänk till personalens e-postadress.
  2. +
  3. Personalen öppnar inbjudningslänken via sin e-post och skapar ett personalkonto med sitt Bank-ID.
  4. +
  5. + När kontot är skapat ser du det i personallistan. Det nya personalkontot saknar fortfarande behörigheter. +
  6. +
  7. + Ge personalkontot behörigheter genom att klicka på namnet i personallistan och ange vilka behörigheter + personalen ska ha. Nu kan personalen logga in och arbeta. +
  8. +
+
+
+
+ +

Skicka en inbjudningslänk

+

+ Skicka en inbjudningslänk till personalen du vill lägga till som systemanvändare. Ange personalens + e-postadress nedan och tryck på skicka inbjudningslänk. +

+
+
+ + Skicka inbjudningslänk +
+
+ +

Inbjudan har skickats till {{latestSubmittedInvite.email}}

+
+ +
+ Tillbaka till personallistan +
+
+
+
diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.scss b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.scss new file mode 100644 index 0000000..8c539af --- /dev/null +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.scss @@ -0,0 +1,31 @@ +@import 'variables/gutters'; + +.employee-invite { + &__block { + max-width: var(--digi--typography--text--max-width); + margin-top: $digi--layout--gutter--xl; + margin-bottom: $digi--layout--gutter--xl; + } + + &__input-section { + display: flex; + margin-top: $digi--layout--gutter--xl; + } + + &__input { + display: block; + min-width: 240px; + margin-bottom: var(--digi--layout--gutter); + } + &__invitation-btn { + margin-top: 31px; + margin-left: 16px; + } + + &__footer { + margin-top: $digi--layout--gutter--xl; + display: flex; + gap: var(--digi--layout--gutter); + } + +} diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.spec.ts b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.spec.ts new file mode 100644 index 0000000..1d74194 --- /dev/null +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.spec.ts @@ -0,0 +1,33 @@ +import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { EmployeeInviteComponent } from './employee-invite.component'; + +describe('EmployeeInviteComponent', () => { + let component: EmployeeInviteComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [ EmployeeInviteComponent ], + imports: [ + ReactiveFormsModule, + DigiNgFormInputModule + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EmployeeInviteComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts new file mode 100644 index 0000000..cb8acad --- /dev/null +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.component.ts @@ -0,0 +1,56 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { EmployeeService } from '@dafa-services/api/employee.service'; +import { RequiredValidator } from '@dafa-utils/validators/required.validator'; +import { BehaviorSubject, Observable } from 'rxjs'; + + +@Component({ + selector: 'dafa-employee-invite', + templateUrl: './employee-invite.component.html', + styleUrls: ['./employee-invite.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EmployeeInviteComponent implements OnInit { + form: FormGroup; + private latestSubmittedInvite_ = new BehaviorSubject(''); + latestSubmittedInvite$: Observable = this.latestSubmittedInvite_.asObservable(); + + constructor(private employeeService: EmployeeService) { } + + ngOnInit(): void { + this.form = new FormGroup({ + email: new FormControl('', [ + RequiredValidator('E-postadress'), + Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$') + ]) + }); + } + + get email() { + return this.form.get('email'); + } + + submitForm(): void { + if (this.form.invalid) { + this.email.markAsDirty(); + this.email.markAsTouched(); + return; + } + + const post = this.employeeService.postEmployeeInvitation(this.form.value).subscribe({ + next: () => { + this.latestSubmittedInvite_.next(this.form.value); + this.form.reset(); + }, + complete: () => { + post.unsubscribe(); + } + }); + } + + onCloseAlert(): void { + this.latestSubmittedInvite_.next(''); + } +} + diff --git a/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.module.ts b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.module.ts new file mode 100644 index 0000000..18eb758 --- /dev/null +++ b/apps/dafa-web/src/app/pages/administration/pages/employee-invite/employee-invite.module.ts @@ -0,0 +1,22 @@ +import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input'; +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { BackLinkModule } from '@dafa-shared/components/back-link/back-link.module'; +import { LayoutModule } from '@dafa-shared/components/layout/layout.module'; +import { EmployeeInviteComponent } from './employee-invite.component'; + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [EmployeeInviteComponent], + imports: [ + CommonModule, + RouterModule.forChild([{ path: '', component: EmployeeInviteComponent }]), + LayoutModule, + BackLinkModule, + ReactiveFormsModule, + DigiNgFormInputModule + ] +}) +export class EmployeeInviteModule { } diff --git a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html index 754b764..1eb0266 100644 --- a/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html +++ b/apps/dafa-web/src/app/pages/administration/pages/employees/employees.component.html @@ -9,7 +9,7 @@

- +

Personallista

diff --git a/apps/dafa-web/src/app/shared/models/api/employee-invite.response.model.ts b/apps/dafa-web/src/app/shared/models/api/employee-invite.response.model.ts new file mode 100644 index 0000000..9ddfb47 --- /dev/null +++ b/apps/dafa-web/src/app/shared/models/api/employee-invite.response.model.ts @@ -0,0 +1,4 @@ +export interface EmployeeInviteMockApiResponse { + id: number, + createdAt: number +} diff --git a/apps/dafa-web/src/app/shared/models/employee-invite-mock-data.model.ts b/apps/dafa-web/src/app/shared/models/employee-invite-mock-data.model.ts new file mode 100644 index 0000000..9c7a505 --- /dev/null +++ b/apps/dafa-web/src/app/shared/models/employee-invite-mock-data.model.ts @@ -0,0 +1,3 @@ +export interface EmployeeInviteMockaData { + id: number +} diff --git a/apps/dafa-web/src/app/shared/services/api/employee.service.ts b/apps/dafa-web/src/app/shared/services/api/employee.service.ts index 817915f..0c9edc8 100644 --- a/apps/dafa-web/src/app/shared/services/api/employee.service.ts +++ b/apps/dafa-web/src/app/shared/services/api/employee.service.ts @@ -3,20 +3,23 @@ import { Injectable } from '@angular/core'; import { ErrorType } from '@dafa-enums/error-type.enum'; import { SortOrder } from '@dafa-enums/sort-order.enum'; import { environment } from '@dafa-environment'; +import { EmployeeInviteMockApiResponse } from '@dafa-models/api/employee-invite.response.model'; +import { EmployeeInviteMockaData } from '@dafa-models/employee-invite-mock-data.model'; import { Employee, EmployeeApiResponse, EmployeesApiResponse, EmployeesData, mapEmployeeReponseToEmployee, - mapEmployeeToEmployeeApiRequestData, + mapEmployeeToEmployeeApiRequestData } from '@dafa-models/employee.model'; import { Sort } from '@dafa-models/sort.model'; import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs'; -import { catchError, map, switchMap } from 'rxjs/operators'; +import { catchError, map, switchMap, take } from 'rxjs/operators'; const API_HEADERS = { headers: environment.api.headers }; + @Injectable({ providedIn: 'root', }) @@ -81,7 +84,7 @@ export class EmployeeService { .pipe(map(result => mapEmployeeReponseToEmployee(result.data))); } - constructor(private httpClient: HttpClient) {} + constructor(private httpClient: HttpClient) { } public setSearchFilter(value: string): void { this._searchFilter$.next(value); @@ -111,4 +114,14 @@ export class EmployeeService { catchError(error => throwError({ message: error as string, type: ErrorType.API })) ); } + + postEmployeeInvitation(email: string): Observable { + return this.httpClient + .post<{ data: EmployeeInviteMockApiResponse }>('http://localhost:8000/invites', { email }, API_HEADERS) + .pipe( + take(1), + map(res => res.data), + catchError(error => throwError({ message: error as string, type: ErrorType.API })) + ); + } }