feat(employee): Added functionality to delete employees. (TV-352)
Squashed commit of the following: commit 2602b9205ef86307fa7c85ecb6ce87cb51b71150 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Sep 2 15:40:49 2021 +0200 Added error-handling commit 4a3f79948b992e5ad7278328957380215ad21ed7 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Sep 2 14:48:54 2021 +0200 Removed some unused variables commit f8e69dce84fee2b1fe2a06d6f0960511c286e2b5 Merge: b155865b80bf22Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Sep 2 14:39:44 2021 +0200 Merged develop and fixed conflicts commit b15586559dab669d652d3a44625bacae4620ed40 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Sep 2 14:34:58 2021 +0200 Added separate component for employee deletion commit 1720b4954ebbc2868db6f0304fc57b0f3ff07216 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Tue Aug 31 07:50:56 2021 +0200 added the showing of errror- and succsess messages when employee is deleted (TV-352) commit 2556d53802249fff545a2d256a178479103108e7 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Mon Aug 30 13:33:22 2021 +0200 minor change (TV-352) commit 035dbbe67486392a7a9c656ac73103e938953b24 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Mon Aug 30 12:38:07 2021 +0200 added close and open methods for better handling of modal (TV-352) commit abc9ce0f8580ae1b9d784bc7591f095faeddcc33 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Mon Aug 30 12:27:07 2021 +0200 feat(edit-employee): added functionality to delete employee, and catch error if such exists commit 23603dd2d84b0e694a19c2131c9c842cf730d97b Merge: 56a4a6a02cf0f6Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Mon Aug 30 11:19:18 2021 +0200 Merge branch 'develop' into feature/TV-352-add-delete-employee-button commit 56a4a6a9c9195542f51578bd4ee937086ace8df8 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Fri Aug 27 12:58:23 2021 +0200 added the basic http-delete request method commit 2f61ff7d6ac2c42a7972fc88548d3d59172cbcc1 Author: Aden Hassan <aden.hassan@arbetsformedlingen.se> Date: Fri Aug 27 09:18:13 2021 +0200 added the basic html/css and functionality for deleting an employee from edit-page
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
<ng-container *ngIf="deleteEmployeeData$ | async as deleteEmployeeData">
|
||||||
|
<digi-ng-dialog
|
||||||
|
*ngIf="deleteEmployeeData.toDelete"
|
||||||
|
[afActive]="deleteEmployeeData.toDelete"
|
||||||
|
(afOnPrimaryClick)="deleteEmployeeModelPrimaryClick(deleteEmployeeData)"
|
||||||
|
(afOnSecondaryClick)="closeDeleteEmployeeModal()"
|
||||||
|
(afOnInactive)="closeDeleteEmployeeModal()"
|
||||||
|
afHeading="Ta bort personalkonto"
|
||||||
|
afHeadingLevel="h2"
|
||||||
|
[afPrimaryButtonText]="getPrimaryButtonText(deleteEmployeeData.lastDeleted)"
|
||||||
|
[afSecondaryButtonText]="getSecondaryButtonText(deleteEmployeeData.lastDeleted)"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="deleteEmployeeData.lastDeleted; else deletionWarning">
|
||||||
|
<digi-notification-alert af-variation="success" af-heading="Allt gick bra" af-heading-level="h3">
|
||||||
|
<p>Personalkonto för {{deleteEmployeeData.lastDeleted.fullName}} är borttaget.</p>
|
||||||
|
</digi-notification-alert>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #deletionWarning>
|
||||||
|
<p>Är du säker på att du vill ta bort personalkontot för {{deleteEmployeeData.toDelete.fullName}}?</p>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-container *ngIf="errorDuringDeletion$ | async as error">
|
||||||
|
<digi-notification-alert af-variation="danger" af-heading="Någonting gick fel" af-heading-level="h3">
|
||||||
|
<p>
|
||||||
|
Vi kunde inte radera personalkontot för {{deleteEmployeeData.toDelete.fullName}}. Ladda om sidan och försök
|
||||||
|
igen.
|
||||||
|
</p>
|
||||||
|
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
|
||||||
|
</digi-notification-alert>
|
||||||
|
</ng-container>
|
||||||
|
</digi-ng-dialog>
|
||||||
|
</ng-container>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { EmployeeDeleteComponent } from './employee-delete.component';
|
||||||
|
|
||||||
|
describe('EmployeeDeleteComponent', () => {
|
||||||
|
let component: EmployeeDeleteComponent;
|
||||||
|
let fixture: ComponentFixture<EmployeeDeleteComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [EmployeeDeleteComponent],
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(EmployeeDeleteComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Employee } from '@msfa-models/employee.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
import { EmployeeService } from '@msfa-services/api/employee.service';
|
||||||
|
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
interface DeleteEmployeeData {
|
||||||
|
toDelete: Employee;
|
||||||
|
lastDeleted: Employee;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'msfa-employee-delete',
|
||||||
|
templateUrl: './employee-delete.component.html',
|
||||||
|
styleUrls: ['./employee-delete.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class EmployeeDeleteComponent {
|
||||||
|
@Input() returnToEmployeeList = false;
|
||||||
|
|
||||||
|
deleteEmployeeData$: Observable<DeleteEmployeeData> = combineLatest([
|
||||||
|
this.employeeService.employeeToDelete$,
|
||||||
|
this.employeeService.lastDeletedEmployee$,
|
||||||
|
]).pipe(
|
||||||
|
map(([employeeToDelete, lastDeletedEmployee]) => {
|
||||||
|
return {
|
||||||
|
toDelete: employeeToDelete,
|
||||||
|
lastDeleted: lastDeletedEmployee?.id === employeeToDelete?.id ? lastDeletedEmployee : null,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
private _errorDuringDeletion$ = new BehaviorSubject<CustomError>(null);
|
||||||
|
errorDuringDeletion$: Observable<CustomError> = this._errorDuringDeletion$.asObservable();
|
||||||
|
|
||||||
|
getPrimaryButtonText(lastDeleted: Employee): string {
|
||||||
|
return lastDeleted ? 'Stäng' : 'Ta bort';
|
||||||
|
}
|
||||||
|
|
||||||
|
getSecondaryButtonText(lastDeleted: Employee): string {
|
||||||
|
return lastDeleted ? null : 'Avbryt';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private employeeService: EmployeeService, private router: Router) {}
|
||||||
|
|
||||||
|
deleteEmployeeModelPrimaryClick(deleteEmployeeData: DeleteEmployeeData): void {
|
||||||
|
const { lastDeleted, toDelete } = deleteEmployeeData;
|
||||||
|
if (lastDeleted) {
|
||||||
|
this.closeDeleteEmployeeModal();
|
||||||
|
if (this.returnToEmployeeList) {
|
||||||
|
void this.router.navigateByUrl('/administration/personal');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteEmployeeSubscription = this.employeeService.deleteEmployee(toDelete).subscribe({
|
||||||
|
error: (error: CustomError) => {
|
||||||
|
this._errorDuringDeletion$.next(error);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
deleteEmployeeSubscription.unsubscribe();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDeleteEmployeeModal(): void {
|
||||||
|
this.employeeService.setEmployeeToDelete(null);
|
||||||
|
this._errorDuringDeletion$.next(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { EmployeeDeleteComponent } from './employee-delete.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [EmployeeDeleteComponent],
|
||||||
|
imports: [CommonModule, RouterModule, DigiNgDialogModule],
|
||||||
|
exports: [EmployeeDeleteComponent],
|
||||||
|
})
|
||||||
|
export class EmployeeDeleteModule {}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
<msfa-layout>
|
<msfa-layout>
|
||||||
<section class="employee-form" *ngIf="employee$ | async as employee">
|
<section class="employee-form" *ngIf="employee$ | async as employee">
|
||||||
<!--### Errormeddelanden ###-->
|
|
||||||
<!-- <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> -->
|
|
||||||
|
|
||||||
<digi-typography>
|
<digi-typography>
|
||||||
<h1>{{ employee.fullName }}</h1>
|
<header class="employee-form__header">
|
||||||
|
<h1>{{ employee.fullName }}</h1>
|
||||||
|
<msfa-employee-delete [returnToEmployeeList]="true"></msfa-employee-delete>
|
||||||
|
<digi-button
|
||||||
|
class="employee-card__delete-account-button"
|
||||||
|
af-variation="primary"
|
||||||
|
[afAriaLabel]="'Ta bort konto för ' + employee.fullName"
|
||||||
|
(afOnClick)="setEmployeeToDelete(employee)"
|
||||||
|
>
|
||||||
|
Ta bort konto
|
||||||
|
</digi-button>
|
||||||
|
</header>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
|
|
||||||
<!--### Personuppgifter ###-->
|
<!--### Personuppgifter ###-->
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
@import 'variables/gutters';
|
@import 'variables/gutters';
|
||||||
|
|
||||||
.employee-form {
|
.employee-form {
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
&__block {
|
&__block {
|
||||||
max-width: var(--digi--typography--text--max-width);
|
max-width: var(--digi--typography--text--max-width);
|
||||||
margin-bottom: $digi--layout--gutter--xl;
|
margin-bottom: $digi--layout--gutter--xl;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
import { FormSelectItem } from '@af/digi-ng/_form/form-select';
|
import { FormSelectItem } from '@af/digi-ng/_form/form-select';
|
||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
@@ -58,4 +57,8 @@ export class EmployeeFormComponent extends UnsubscribeDirective implements OnIni
|
|||||||
updateEmployee(editEmployeeFormData: EditEmployeeFormData): void {
|
updateEmployee(editEmployeeFormData: EditEmployeeFormData): void {
|
||||||
console.log(editEmployeeFormData);
|
console.log(editEmployeeFormData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEmployeeToDelete(employee: Employee): void {
|
||||||
|
this.employeeService.setEmployeeToDelete(employee);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
import { DigiNgButtonModule } from '@af/digi-ng/_button/button';
|
||||||
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
|
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
|
||||||
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||||
import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input';
|
import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input';
|
||||||
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||||
import { DigiNgPopoverModule } from '@af/digi-ng/_popover/popover';
|
import { DigiNgPopoverModule } from '@af/digi-ng/_popover/popover';
|
||||||
import { DigiNgButtonModule } from '@af/digi-ng/_button/button';
|
|
||||||
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 { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
||||||
import { EmployeeFormComponent } from './employee-form.component';
|
import { EmployeeDeleteModule } from '../../components/employee-delete/employee-delete.module';
|
||||||
import { EditEmployeeFormComponent } from './edit-employee-form/edit-employee-form.component';
|
import { EditEmployeeFormComponent } from './edit-employee-form/edit-employee-form.component';
|
||||||
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
import { EmployeeFormComponent } from './employee-form.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
@@ -30,9 +30,9 @@ import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.modu
|
|||||||
DigiNgFormSelectModule,
|
DigiNgFormSelectModule,
|
||||||
DigiNgPopoverModule,
|
DigiNgPopoverModule,
|
||||||
DigiNgFormCheckboxModule,
|
DigiNgFormCheckboxModule,
|
||||||
DigiNgDialogModule,
|
|
||||||
DigiNgButtonModule,
|
DigiNgButtonModule,
|
||||||
LayoutModule,
|
LayoutModule,
|
||||||
|
EmployeeDeleteModule,
|
||||||
HideTextModule,
|
HideTextModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<digi-button
|
<digi-button
|
||||||
af-variation="tertiary"
|
af-variation="tertiary"
|
||||||
[afAriaLabel]="'Ta bort konto för ' + employee.fullName"
|
[afAriaLabel]="'Ta bort konto för ' + employee.fullName"
|
||||||
(afOnClick)="openDialog(true, employee)"
|
(afOnClick)="emitDeleteEmployee(employee)"
|
||||||
>
|
>
|
||||||
<digi-icon-x style="--digi--ui--width--icon: 1.25rem" slot="icon"></digi-icon-x>
|
<digi-icon-x style="--digi--ui--width--icon: 1.25rem" slot="icon"></digi-icon-x>
|
||||||
</digi-button>
|
</digi-button>
|
||||||
@@ -57,22 +57,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</digi-table>
|
</digi-table>
|
||||||
|
|
||||||
<!-- Modal/ Dialog window -->
|
|
||||||
<digi-ng-dialog
|
|
||||||
*ngIf="employeeSelected$ | async as employee"
|
|
||||||
[afActive]="showDialog"
|
|
||||||
(afOnInactive)="openDialog(false)"
|
|
||||||
(afOnPrimaryClick)="openDialog(false)"
|
|
||||||
(afOnSecondaryClick)="onDeleteEmployee(employee)"
|
|
||||||
[afHeading]="'Ta bort personalkonto'"
|
|
||||||
afHeadingLevel="h2"
|
|
||||||
afPrimaryButtonText="Avbryt"
|
|
||||||
afSecondaryButtonText="Radera personalkonto"
|
|
||||||
>
|
|
||||||
<p>Är du säker på att du vill ta bort personalkontot för {{employee?.fullName}}?</p>
|
|
||||||
</digi-ng-dialog>
|
|
||||||
|
|
||||||
<digi-navigation-pagination
|
<digi-navigation-pagination
|
||||||
*ngIf="totalPage > 1"
|
*ngIf="totalPage > 1"
|
||||||
class="employees-list__pagination"
|
class="employees-list__pagination"
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
||||||
import { DeleteEmployeeMockApiResponse } from '@msfa-models/api/delete-employee.response.model';
|
|
||||||
import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model';
|
import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model';
|
||||||
import { EmployeeCompact } from '@msfa-models/employee.model';
|
import { EmployeeCompact } from '@msfa-models/employee.model';
|
||||||
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
|
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
|
||||||
import { Sort } from '@msfa-models/sort.model';
|
import { Sort } from '@msfa-models/sort.model';
|
||||||
import { EmployeeService } from '@msfa-services/api/employee.service';
|
|
||||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
||||||
import { takeUntil } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-employees-list',
|
selector: 'msfa-employees-list',
|
||||||
@@ -15,19 +11,13 @@ import { takeUntil } from 'rxjs/operators';
|
|||||||
styleUrls: ['./employees-list.component.scss'],
|
styleUrls: ['./employees-list.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class EmployeesListComponent implements OnDestroy {
|
export class EmployeesListComponent {
|
||||||
@Input() employees: EmployeeCompact[];
|
@Input() employees: EmployeeCompact[];
|
||||||
@Input() paginationMeta: PaginationMeta;
|
@Input() paginationMeta: PaginationMeta;
|
||||||
@Input() sort: Sort<keyof EmployeeCompactResponse>;
|
@Input() sort: Sort<keyof EmployeeCompactResponse>;
|
||||||
@Output() sorted = new EventEmitter<keyof EmployeeCompactResponse>();
|
@Output() sorted = new EventEmitter<keyof EmployeeCompactResponse>();
|
||||||
@Output() paginated = new EventEmitter<number>();
|
@Output() paginated = new EventEmitter<number>();
|
||||||
@Output() deletedEmployee = new EventEmitter<EmployeeCompact>(null);
|
@Output() deleteEmployee = new EventEmitter<EmployeeCompact>();
|
||||||
private componentDestroyed$ = new Subject<void>();
|
|
||||||
private _employeeSelected$ = new BehaviorSubject<EmployeeCompact>(null);
|
|
||||||
employeeSelected$: Observable<EmployeeCompact> = this._employeeSelected$.asObservable();
|
|
||||||
showDialog: boolean;
|
|
||||||
|
|
||||||
constructor(private employeeService: EmployeeService) {}
|
|
||||||
|
|
||||||
columnHeaders: { label: string; key: keyof EmployeeCompactResponse }[] = [
|
columnHeaders: { label: string; key: keyof EmployeeCompactResponse }[] = [
|
||||||
{ label: 'Namn', key: 'name' },
|
{ label: 'Namn', key: 'name' },
|
||||||
@@ -72,37 +62,7 @@ export class EmployeesListComponent implements OnDestroy {
|
|||||||
this.paginated.emit(page);
|
this.paginated.emit(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteEmployee(employee: EmployeeCompact): void {
|
emitDeleteEmployee(employee: EmployeeCompact): void {
|
||||||
this.employeeService
|
this.deleteEmployee.emit(employee);
|
||||||
.deleteEmployee(employee.id)
|
|
||||||
.pipe(takeUntil(this.componentDestroyed$))
|
|
||||||
.subscribe({
|
|
||||||
next: (res: DeleteEmployeeMockApiResponse) => {
|
|
||||||
if (res.status === 200) {
|
|
||||||
this.employees = this.employees ? this.employees.filter(e => e.id !== employee.id) : [];
|
|
||||||
this.deletedEmployee.emit(employee);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
complete: () => {
|
|
||||||
this.showDialog = false;
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
console.log(err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
openDialog(val: boolean, employee: EmployeeCompact): void {
|
|
||||||
if (!val) {
|
|
||||||
this.showDialog = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.showDialog = val;
|
|
||||||
this._employeeSelected$.next(employee);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.componentDestroyed$.next();
|
|
||||||
this.componentDestroyed$.complete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
|
||||||
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 { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
@@ -7,7 +6,7 @@ import { EmployeesListComponent } from './employees-list.component';
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [EmployeesListComponent],
|
declarations: [EmployeesListComponent],
|
||||||
imports: [CommonModule, RouterModule, DigiNgDialogModule],
|
imports: [CommonModule, RouterModule],
|
||||||
exports: [EmployeesListComponent],
|
exports: [EmployeesListComponent],
|
||||||
})
|
})
|
||||||
export class EmployeesListModule {}
|
export class EmployeesListModule {}
|
||||||
|
|||||||
@@ -12,22 +12,6 @@
|
|||||||
<digi-ng-link-button afText="Skapa nytt konto" afRoute="/administration/bjuda-in"></digi-ng-link-button>
|
<digi-ng-link-button afText="Skapa nytt konto" afRoute="/administration/bjuda-in"></digi-ng-link-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="employees__notification-alert">
|
|
||||||
<digi-notification-alert
|
|
||||||
*ngIf="(deletedEmployee$ | async) as deletedEmployee"
|
|
||||||
af-variation="success"
|
|
||||||
af-heading="Allt gick bra"
|
|
||||||
af-heading-level="h3"
|
|
||||||
af-closeable="true"
|
|
||||||
(click)="onCloseAlert()"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
{{deletedEmployee?.fullName + (deletedEmployee?.fullName[deletedEmployee?.fullName.length -1] === 's' ? '' :
|
|
||||||
's') }} personalkonto är borttaget.
|
|
||||||
</p>
|
|
||||||
</digi-notification-alert>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Personallista</h2>
|
<h2>Personallista</h2>
|
||||||
|
|
||||||
<form class="employees__search-wrapper" (ngSubmit)="setSearchFilter()">
|
<form class="employees__search-wrapper" (ngSubmit)="setSearchFilter()">
|
||||||
@@ -39,10 +23,13 @@
|
|||||||
<digi-form-checkbox
|
<digi-form-checkbox
|
||||||
class="employees__only-employees-without-authorization"
|
class="employees__only-employees-without-authorization"
|
||||||
af-label="Visa endast personer utan behörigheter"
|
af-label="Visa endast personer utan behörigheter"
|
||||||
(afOnChange)="setOnlyEmployeesWithoutAuthorization($event)"
|
[afChecked]="onlyEmployeesWithoutAuthorization$ | async"
|
||||||
|
(afOnChange)="setOnlyEmployeesWithoutAuthorization($event.detail.target.checked)"
|
||||||
></digi-form-checkbox>
|
></digi-form-checkbox>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<msfa-employee-delete></msfa-employee-delete>
|
||||||
|
|
||||||
<msfa-employees-list
|
<msfa-employees-list
|
||||||
*ngIf="employeesData$ | async as employeesData; else loadingRef"
|
*ngIf="employeesData$ | async as employeesData; else loadingRef"
|
||||||
[employees]="employeesData.data"
|
[employees]="employeesData.data"
|
||||||
@@ -51,7 +38,7 @@
|
|||||||
[order]="order$ | async"
|
[order]="order$ | async"
|
||||||
(sorted)="handleEmployeesSort($event)"
|
(sorted)="handleEmployeesSort($event)"
|
||||||
(paginated)="setNewPage($event)"
|
(paginated)="setNewPage($event)"
|
||||||
(deletedEmployee)="handleDeletedEmployee($event)"
|
(deleteEmployee)="setEmployeeToDelete($event)"
|
||||||
></msfa-employees-list>
|
></msfa-employees-list>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,12 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
|||||||
})
|
})
|
||||||
export class EmployeesComponent {
|
export class EmployeesComponent {
|
||||||
private _searchValue$ = new BehaviorSubject<string>('');
|
private _searchValue$ = new BehaviorSubject<string>('');
|
||||||
private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject<boolean>(false);
|
onlyEmployeesWithoutAuthorization$: Observable<boolean> = this.employeeService.onlyEmployeesWithoutAuthorization$;
|
||||||
employeesData$: Observable<EmployeesData> = this.employeeService.employeesData$;
|
employeesData$: Observable<EmployeesData> = this.employeeService.employeesData$;
|
||||||
sort$: Observable<Sort<keyof EmployeeCompactResponse>> = this.employeeService.sort$;
|
sort$: Observable<Sort<keyof EmployeeCompactResponse>> = this.employeeService.sort$;
|
||||||
private _deletedEmployee$ = new BehaviorSubject<Employee>(null);
|
|
||||||
deletedEmployee$: Observable<Employee> = this._deletedEmployee$.asObservable();
|
|
||||||
iconType = IconType;
|
iconType = IconType;
|
||||||
|
|
||||||
constructor(private employeeService: EmployeeService) { }
|
constructor(private employeeService: EmployeeService) {}
|
||||||
|
|
||||||
get searchValue(): string {
|
get searchValue(): string {
|
||||||
return this._searchValue$.getValue();
|
return this._searchValue$.getValue();
|
||||||
@@ -43,20 +41,11 @@ export class EmployeesComponent {
|
|||||||
this.employeeService.setPage(page);
|
this.employeeService.setPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
get onlyEmployeesWithoutAuthorization(): boolean {
|
setOnlyEmployeesWithoutAuthorization(checked: boolean): void {
|
||||||
return this._onlyEmployeesWithoutAuthorization$.getValue();
|
this.employeeService.setOnlyEmployeesWithoutAuthorization(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnlyEmployeesWithoutAuthorization(event: CustomEvent<{ target: { checked: boolean } }>): void {
|
setEmployeeToDelete(employee: Employee): void {
|
||||||
this._onlyEmployeesWithoutAuthorization$.next(event.detail.target.checked);
|
this.employeeService.setEmployeeToDelete(employee);
|
||||||
this.employeeService.setOnlyEmployeesWithoutAuthorization(this.onlyEmployeesWithoutAuthorization);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDeletedEmployee(employee: Employee): void {
|
|
||||||
this._deletedEmployee$.next(employee);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloseAlert(): void {
|
|
||||||
this._deletedEmployee$.next(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { EmployeeDeleteModule } from '../../components/employee-delete/employee-delete.module';
|
||||||
import { EmployeesListModule } from './components/employees-list/employees-list.module';
|
import { EmployeesListModule } from './components/employees-list/employees-list.module';
|
||||||
import { EmployeesComponent } from './employees.component';
|
import { EmployeesComponent } from './employees.component';
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ import { EmployeesComponent } from './employees.component';
|
|||||||
DigiNgLinkInternalModule,
|
DigiNgLinkInternalModule,
|
||||||
DigiNgSkeletonBaseModule,
|
DigiNgSkeletonBaseModule,
|
||||||
EmployeesListModule,
|
EmployeesListModule,
|
||||||
|
EmployeeDeleteModule,
|
||||||
DigiNgLinkButtonModule,
|
DigiNgLinkButtonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { ErrorType } from '@msfa-enums/error-type.enum';
|
|||||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { DeleteEmployeeMockApiResponse } from '@msfa-models/api/delete-employee.response.model';
|
|
||||||
import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model';
|
import { EmployeeInviteResponse } from '@msfa-models/api/employee-invite.response.model';
|
||||||
import {
|
import {
|
||||||
EmployeeCompactResponse,
|
EmployeeCompactResponse,
|
||||||
@@ -25,7 +24,7 @@ import { mapRoleResponseToRoleObject, Role } from '@msfa-models/role.model';
|
|||||||
import { Sort } from '@msfa-models/sort.model';
|
import { Sort } from '@msfa-models/sort.model';
|
||||||
import { ErrorService } from '@msfa-services/error.service';
|
import { ErrorService } from '@msfa-services/error.service';
|
||||||
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
|
||||||
import { catchError, filter, map, switchMap, take } from 'rxjs/operators';
|
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { TjanstService } from './tjanst.service';
|
import { TjanstService } from './tjanst.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -40,9 +39,14 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
public sort$: Observable<Sort<keyof EmployeeCompactResponse>> = this._sort$.asObservable();
|
public sort$: Observable<Sort<keyof EmployeeCompactResponse>> = this._sort$.asObservable();
|
||||||
private _searchFilter$ = new BehaviorSubject<string>('');
|
private _searchFilter$ = new BehaviorSubject<string>('');
|
||||||
private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject<boolean>(false);
|
private _onlyEmployeesWithoutAuthorization$ = new BehaviorSubject<boolean>(false);
|
||||||
|
public onlyEmployeesWithoutAuthorization$: Observable<boolean> = this._onlyEmployeesWithoutAuthorization$.asObservable();
|
||||||
private _employee$ = new BehaviorSubject<Employee>(null);
|
private _employee$ = new BehaviorSubject<Employee>(null);
|
||||||
|
|
||||||
public employee$: Observable<Employee> = this._employee$.asObservable();
|
public employee$: Observable<Employee> = this._employee$.asObservable();
|
||||||
|
private _lastDeletedEmployee$ = new BehaviorSubject<Employee>(null);
|
||||||
|
public lastDeletedEmployee$: Observable<Employee> = this._lastDeletedEmployee$.asObservable();
|
||||||
|
private _employeeToDelete$ = new BehaviorSubject<Employee>(null);
|
||||||
|
public employeeToDelete$: Observable<Employee> = this._employeeToDelete$.asObservable();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private httpClient: HttpClient,
|
private httpClient: HttpClient,
|
||||||
private errorService: ErrorService,
|
private errorService: ErrorService,
|
||||||
@@ -58,7 +62,7 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
filter(([employee, allTjanster]) => !!(employee && allTjanster?.length)),
|
filter(([employee, allTjanster]) => !!(employee && allTjanster?.length)),
|
||||||
map(([employee, allTjanster]) => {
|
map(([employee, allTjanster]) => {
|
||||||
const tjanster = [];
|
const tjanster = [];
|
||||||
employee.tjanstCodes.forEach(code => {
|
employee.tjanstCodes?.forEach(code => {
|
||||||
const currentTjanst = allTjanster.find(tjanst => tjanst.code === code);
|
const currentTjanst = allTjanster.find(tjanst => tjanst.code === code);
|
||||||
|
|
||||||
if (currentTjanst) {
|
if (currentTjanst) {
|
||||||
@@ -82,6 +86,7 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
this._sort$,
|
this._sort$,
|
||||||
this._searchFilter$,
|
this._searchFilter$,
|
||||||
this._onlyEmployeesWithoutAuthorization$,
|
this._onlyEmployeesWithoutAuthorization$,
|
||||||
|
this._lastDeletedEmployee$,
|
||||||
]).pipe(
|
]).pipe(
|
||||||
switchMap(([limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization]) =>
|
switchMap(([limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization]) =>
|
||||||
this._fetchEmployees$(limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization)
|
this._fetchEmployees$(limit, page, sort, searchFilter, onlyEmployeesWithoutAuthorization)
|
||||||
@@ -144,17 +149,17 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
this._onlyEmployeesWithoutAuthorization$.next(value);
|
this._onlyEmployeesWithoutAuthorization$.next(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not done, waiting for delete api http response
|
public setEmployeeToDelete(employee: Employee): void {
|
||||||
public deleteEmployee(id: string): Observable<any> {
|
this._employeeToDelete$.next(employee);
|
||||||
return this.httpClient.delete<DeleteEmployeeMockApiResponse>(`${this._apiBaseUrl}/${id}`).pipe(
|
}
|
||||||
take(1),
|
|
||||||
map(response => {
|
public deleteEmployee(employee: Employee): Observable<Employee | Partial<Employee>> {
|
||||||
return {
|
return this.httpClient.delete<void>(`${this._apiBaseUrl}/hubba`).pipe(
|
||||||
status: response.status || 200, // mockresponse
|
tap(() => {
|
||||||
message: response.message || 'deleted succeeded', // mockresponse
|
this._lastDeletedEmployee$.next(employee);
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
|
map(() => employee),
|
||||||
|
catchError(error => throwError(errorToCustomError(error)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,8 +189,11 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
map(res => res.data),
|
map(({ data }) => data),
|
||||||
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
|
catchError(error => {
|
||||||
|
this.errorService.add(errorToCustomError(error));
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ dl {
|
|||||||
@include msfa__a11y-sr-only;
|
@include msfa__a11y-sr-only;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__small-text {
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
&__spinner {
|
&__spinner {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
animation: spinning infinite 0.6s linear;
|
animation: spinning infinite 0.6s linear;
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ function generateEmployees(amount = 10) {
|
|||||||
const currentTjanster = chooseRandom(TJANSTER, faker.datatype.number({ min: 1, max: TJANSTER.length }));
|
const currentTjanster = chooseRandom(TJANSTER, faker.datatype.number({ min: 1, max: TJANSTER.length }));
|
||||||
const currentUtforandeVerksamheter = chooseRandom(UTFORANDEVERKSAMHETER, faker.datatype.number({ min: 1, max: 5 }));
|
const currentUtforandeVerksamheter = chooseRandom(UTFORANDEVERKSAMHETER, faker.datatype.number({ min: 1, max: 5 }));
|
||||||
const hasBehorigheter = Math.random() > 0.1;
|
const hasBehorigheter = Math.random() > 0.1;
|
||||||
|
const id = faker.datatype.uuid();
|
||||||
|
|
||||||
const employee = {
|
const employee = {
|
||||||
ciamUserId: faker.datatype.uuid(),
|
id,
|
||||||
|
ciamUserId: id,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
name: `${firstName} ${lastName}`,
|
name: `${firstName} ${lastName}`,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ server.use(
|
|||||||
'*sort=utforandeVerksamhet*': '$1sort=utforandeVerksamhet[0]$2',
|
'*sort=utforandeVerksamhet*': '$1sort=utforandeVerksamhet[0]$2',
|
||||||
'*sort=tjanst*': '$1sort=tjanst[0]$2',
|
'*sort=tjanst*': '$1sort=tjanst[0]$2',
|
||||||
'/users/invite*': '/invites$1',
|
'/users/invite*': '/invites$1',
|
||||||
'/users/:id': '/employees?ciamUserId=:id',
|
|
||||||
'/users*': '/employees$1',
|
'/users*': '/employees$1',
|
||||||
'/employees*search=*': '/employees$1fullName_like=$2',
|
'/employees*search=*': '/employees$1fullName_like=$2',
|
||||||
'/employees*onlyEmployeesWithoutAuthorization=*': '/employees$1roles.length_lte=1',
|
'/employees*onlyEmployeesWithoutAuthorization=*': '/employees$1roles.length_lte=1',
|
||||||
@@ -88,10 +87,6 @@ router.render = (req, res) => {
|
|||||||
const authRegex = /(?:\/auth\/)(userinfo|organizations)/g;
|
const authRegex = /(?:\/auth\/)(userinfo|organizations)/g;
|
||||||
const isAuthPath = authRegex.exec(pathname);
|
const isAuthPath = authRegex.exec(pathname);
|
||||||
|
|
||||||
if (isEmployeePath) {
|
|
||||||
data = data[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAuthPath) {
|
if (isAuthPath) {
|
||||||
const authSubPath = isAuthPath[1];
|
const authSubPath = isAuthPath[1];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user