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}}

+
+ + +
+
+
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 })) + ); + } }