Merge pull request #29 in TEA/dafa-web-monorepo from refactor/stricter-eslint to develop

Squashed commit of the following:

commit 4e7a87134edb95c9d522514140dcf8ff8b5c9bfa
Merge: c586d67 6f97f51
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Mon Jun 28 09:12:35 2021 +0200

    Merge branch 'develop' into refactor/stricter-eslint

    # Conflicts:
    #	apps/dafa-web/src/app/pages/administration/pages/employee-form/employee-form.component.ts

commit c586d675ced7a0dd4bd88599f62dd7a72fe39723
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Jun 23 16:24:37 2021 +0200

    Fix linting of avrop

commit 70aa93e06c7677ce6b2e5068c74709ef5f9feecc
Merge: ace9950 4afe9b5
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Jun 23 16:12:30 2021 +0200

    Merge branch 'develop' into refactor/stricter-eslint

    # Conflicts:
    #	apps/dafa-web/src/app/pages/avrop/avrop.component.spec.ts
    #	apps/dafa-web/src/app/pages/ciam-landing/ciam-landing.component.spec.ts
    #	apps/dafa-web/src/app/pages/logout/logout.component.spec.ts

commit ace9950d3dc9517d8fe4e4b53af6813b6b175720
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Jun 23 10:53:00 2021 +0200

    Fix remaining linting warnings/errors

commit c15b0a4b8a62604928871d45095d9ad257add0a8
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Jun 23 10:44:32 2021 +0200

    fix more linting errors and warnings

commit 924c925347dbf0a1029237859c748bf107cfd03c
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Jun 22 17:07:26 2021 +0200

    Fixed a lot of linting errors

commit f730f04ebb2d28e150ba20c2507493fac64f0a2d
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Jun 22 16:32:58 2021 +0200

    fix some linting problems

commit 64fe06c034586688fd7252198de0baf0caaccca2
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Jun 22 16:15:32 2021 +0200

    add recommended eslint rules with type-checking
This commit is contained in:
Daniel Appelgren
2021-06-30 08:53:30 +02:00
parent 6f97f51c3b
commit c06452dbbd
60 changed files with 1809 additions and 501 deletions

View File

@@ -7,9 +7,23 @@
"bracketSpacing": true,
"arrowParens": "avoid",
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
},
{
"files": "*.component.html",
"options": {
"parser": "angular"
}
},
{
"files": "*.html",
"options": {
"parser": "html"
}
}
]
}

View File

@@ -6,6 +6,9 @@
"files": ["*.ts"],
"extends": [
"plugin:@nrwl/nx/angular",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@angular-eslint/template/process-inline-templates"
],
"parserOptions": { "project": ["apps/dafa-web/tsconfig.*?.json"] },

View File

@@ -9,7 +9,7 @@ describe('FooterComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [FooterComponent],
imports: [RouterTestingModule, IconModule],
}).compileComponents();

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { NavigationBreadcrumbsItem } from '@af/digi-ng/_navigation/navigation-breadcrumbs';
import { BehaviorSubject, Observable } from 'rxjs';
import { User } from '@dafa-models/user.model';
@@ -14,7 +14,7 @@ import { environment } from '@dafa-environment';
selector: 'dafa-logged-in-shell',
templateUrl: './logged-in-shell.component.html',
styleUrls: ['./logged-in-shell.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoggedInShellComponent extends UnsubscribeDirective {
private startBreadcrumb: NavigationBreadcrumbsItem = {
@@ -28,18 +28,17 @@ export class LoggedInShellComponent extends UnsubscribeDirective {
return this._breadcrumbsItems$.getValue();
}
get isLoggedIn() {
get isLoggedIn(): boolean {
return this.authService.isLoggedIn();
}
constructor(private router: Router, private authService: AuthenticationService, private userService: UserService) {
super();
if(this.authService.isLoggedOut()) {
this.router.navigateByUrl(environment.loginUrl);
if (this.authService.isLoggedOut()) {
void this.router.navigateByUrl(environment.loginUrl);
}
super.unsubscribeOnDestroy(
this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
const urlTree = this.router.parseUrl(this.router.url);
@@ -52,5 +51,4 @@ export class LoggedInShellComponent extends UnsubscribeDirective {
})
);
}
}

View File

@@ -9,7 +9,7 @@ describe('NavigationComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [NavigationComponent],
imports: [RouterTestingModule, IconModule],
}).compileComponents();

View File

@@ -8,7 +8,7 @@ describe('SkipToContentComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [SkipToContentComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -16,7 +16,7 @@ export class SkipToContentComponent extends UnsubscribeDirective {
constructor(private router: Router, private changeDetectorRef: ChangeDetectorRef) {
super();
super.unsubscribeOnDestroy(
this.router.events.pipe(filter((event: any) => event instanceof NavigationEnd)).subscribe(({ url }) => {
this.router.events.pipe(filter((event: NavigationEnd) => event instanceof NavigationEnd)).subscribe(({ url }) => {
const mainContentId = `#${this.mainContentId}`;
// Check if the current URL already includes the mainContentId.
const existsInUrl: boolean = url.substring(url.length - mainContentId.length, url.length) === mainContentId;

View File

@@ -2,16 +2,17 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ToastListComponent } from './toast-list.component';
describe('ToastListComponent', () => {
let component: ToastListComponent;
let fixture: ComponentFixture<ToastListComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ToastListComponent],
}).compileComponents();
}));
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [ToastListComponent],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(ToastListComponent);

View File

@@ -10,7 +10,7 @@ describe('ToastComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ToastComponent],
}).compileComponents();

View File

@@ -11,7 +11,7 @@ import { CustomError } from '@dafa-models/error/custom-error';
})
export class ToastComponent implements AfterViewInit {
@Input() error: CustomError;
@Output() closeToast: EventEmitter<CustomError> = new EventEmitter();
@Output() closeToast = new EventEmitter<CustomError>();
iconType = IconType;
errorSeverity = ErrorSeverity;

View File

@@ -24,21 +24,27 @@ export class CustomError implements Error {
this.severity === ErrorSeverity.LOW ? 5000 : this.severity === ErrorSeverity.MEDIUM ? 10000 : 20000;
}
static getStack(error: Error): string {
static getStack(error: Error | { error: Error }): string {
if (!error) {
return '';
}
if (error.stack) {
if ('stack' in error) {
return error.stack;
} else if ((error as any).error) {
return this.getStack((error as any).error);
} else if ('error' in error) {
return this.getStack(error.error);
} else {
return error as any;
return error as never;
}
}
static getErrorType(error: Error): ErrorType {
let type = (error as any).type || ErrorType.UNKNOWN;
static getErrorType(error: Error | (Error & { type: ErrorType })): ErrorType {
let type: ErrorType;
if ('type' in error) {
type = error.type;
} else {
type = ErrorType.UNKNOWN;
}
if (error.name === 'HttpErrorResponse') {
type = ErrorType.API;
@@ -48,20 +54,20 @@ export class CustomError implements Error {
}
}
export function errorToCustomError(error: Error): CustomError {
export function errorToCustomError(error: Error & { ngDebugContext: unknown }): CustomError {
const type = CustomError.getErrorType(error);
const message = error.message || (error as any);
const message = error.message || error;
const severity = ErrorSeverity.HIGH;
// this is done to avoid circular references while running in debug mode
if ((error as any).ngDebugContext) {
(error as any).ngDebugContext = {};
if ('ngDebugContext' in error) {
error.ngDebugContext = {};
}
return new CustomError({
error,
type,
severity,
message,
message: message as string,
});
}

View File

@@ -6,7 +6,7 @@ import { ErrorService } from '@dafa-services/error.service';
export class CustomErrorHandler implements ErrorHandler {
constructor(private errorService: ErrorService) {}
handleError(error: any): void {
handleError(error: Error & { ngDebugContext: unknown }): void {
const customError: CustomError = errorToCustomError(error);
console.error(error);
this.errorService.add(customError);

View File

@@ -8,7 +8,7 @@ describe('AdministrationComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [AdministrationComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -10,7 +10,7 @@ describe('EmployeeCardComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [EmployeeCardComponent],
imports: [RouterTestingModule, HttpClientTestingModule],

View File

@@ -14,8 +14,10 @@ import { map, switchMap } from 'rxjs/operators';
})
export class EmployeeCardComponent {
private _pendingSelectedParticipants$ = new BehaviorSubject<string[]>([]);
private _employeeId$: Observable<string> = this.activatedRoute.params.pipe(map(({ employeeId }) => employeeId));
authorizationsAsString$: Observable<string>;
private _employeeId$: Observable<string> = this.activatedRoute.params.pipe(
map(({ employeeId }) => employeeId as string)
);
detailedEmployeeData$: Observable<Employee> = this._employeeId$.pipe(
switchMap(employeeId => this.employeeService.fetchDetailedEmployeeData$(employeeId))
);

View File

@@ -17,7 +17,7 @@ describe('EmployeeFormComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [EmployeeFormComponent],
imports: [

View File

@@ -25,7 +25,7 @@ export class EmployeeFormComponent {
map(services => services.map(({ name, id }) => ({ name, value: id })))
);
toggleDialog = false;
modalAuthInfo: any = {'name': 'Test Behörighetsnamn'};
modalAuthInfo: { name: string } = { name: 'Test Behörighetsnamn' };
formGroup: FormGroup = this.formBuilder.group({
firstName: this.formBuilder.control('', [RequiredValidator('Förnamn')]),
@@ -68,6 +68,7 @@ export class EmployeeFormComponent {
return controlsWithErrors.map(key => ({
id: key,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
message: this.formGroup.controls[key].errors.message,
}));
}
@@ -80,7 +81,7 @@ export class EmployeeFormComponent {
}
toggleAuthorization(authorization: Authorization, checked: boolean): void {
const currentAuthorizations = this.authorizationsControl.value;
const currentAuthorizations = this.authorizationsControl.value as { id: unknown }[];
if (checked) {
this.authorizationsControl.patchValue([...currentAuthorizations, authorization]);
@@ -92,7 +93,7 @@ export class EmployeeFormComponent {
}
toggleService(service: Service, checked: boolean): void {
const currentServices = this.servicesControl.value;
const currentServices = this.servicesControl.value as { id: unknown }[];
if (checked) {
this.servicesControl.patchValue([...currentServices, service]);
@@ -101,8 +102,9 @@ export class EmployeeFormComponent {
}
}
openDialog(val: boolean, authName?:any) {
if(authName) {
openDialog(val: boolean, authName?: string): void {
if (authName) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
this.modalAuthInfo.name = authName;
}
this.toggleDialog = val;
@@ -127,12 +129,13 @@ export class EmployeeFormComponent {
submitForm(): void {
this.submitted = true;
if (this.formGroup.valid) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const submittableValues = {
...this.formGroup.value,
};
const post = this.employeeService.postNewEmployee(submittableValues).subscribe({
next: id => {
this.router.navigate(['/administration', 'personal', id]);
void this.router.navigate(['/administration', 'personal', id]);
},
complete: () => {
post.unsubscribe();

View File

@@ -1,4 +1,4 @@
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { EmployeesListComponent } from './employees-list.component';
@@ -12,7 +12,6 @@ describe('EmployeesListComponent', () => {
let fixture: ComponentFixture<EmployeesListComponent>;
const getEmployeeRows = () => fixture.debugElement.queryAll(By.css('.employees-list__row'));
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -31,17 +30,17 @@ describe('EmployeesListComponent', () => {
describe('20 employees sorted by Full name Ascending', () => {
beforeEach(() => {
component.employees = employeesMock;
component.paginationMeta = {count: employeesMock.length, limit: 50, page: 1, totalPages: 3};
component.sort = {key: <keyof Employee>'fullName', order: SortOrder.ASC };
component.paginationMeta = { count: employeesMock.length, limit: 50, page: 1, totalPages: 3 };
component.sort = { key: <keyof Employee>'fullName', order: SortOrder.ASC };
fixture.detectChanges();
})
});
it('should display the rows from employees object 20 rows regardless of pagination', () => {
expect(getEmployeeRows().length).toBe(20);
});
it('should display the up caret next to Full name to indicate that it\'s sorted by full name Ascending', () => {
it('should display the up caret next to Full name to indicate that it´s sorted by full name Ascending', () => {
const fullNameUpCaret = fixture.debugElement.query(By.css('#sort-button-fullName > digi-icon-caret-up'));
expect(fullNameUpCaret).toBeTruthy();
});

View File

@@ -4,7 +4,6 @@ import { Employee } from '@dafa-models/employee.model';
import { PaginationMeta } from '@dafa-models/pagination-meta.model';
import { Sort } from '@dafa-models/sort.model';
@Component({
selector: 'dafa-employees-list',
templateUrl: './employees-list.component.html',
@@ -18,22 +17,36 @@ export class EmployeesListComponent {
@Output() sorted = new EventEmitter<keyof Employee>();
@Output() paginated = new EventEmitter<number>();
columnHeaders: {label: string, key: keyof Employee}[] = [{label: 'Namn', key: 'fullName'}, {label: 'Tjänst', key: 'services'}, {label: 'Utförandeverksamheter', key: 'organizations'}];
columnHeaders: { label: string; key: keyof Employee }[] = [
{ label: 'Namn', key: 'fullName' },
{
label: 'Tjänst',
key: 'services',
},
{
label: 'Utförandeverksamheter',
key: 'organizations',
},
];
orderType = SortOrder;
get currentPage(): number {
return this.paginationMeta.page;
}
get totalPages(): number {
return this.paginationMeta?.totalPages;
}
get count(): number {
return this.paginationMeta.count;
}
get currentResultStart(): number {
return (this.currentPage - 1) * this.paginationMeta.limit + 1;
}
get currentResultEnd(): number {
const end = this.currentResultStart + this.paginationMeta.limit - 1;
return end < this.count ? end : this.count;

View File

@@ -10,7 +10,7 @@ describe('EmployeesComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [EmployeesComponent],
imports: [RouterTestingModule, HttpClientTestingModule],
@@ -25,6 +25,6 @@ describe('EmployeesComponent', () => {
});
it('should create', () => {
expect(component).toBeTruthy();
void expect(component).toBeTruthy();
});
});

View File

@@ -27,7 +27,7 @@ export class EmployeesComponent {
this.employeeService.setSearchFilter(this.searchValue);
}
setSearchValue($event: CustomEvent): void {
setSearchValue($event: CustomEvent<{ target: { value: string } }>): void {
this._searchValue$.next($event.detail.target.value);
}

View File

@@ -1,7 +1,6 @@
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { AvropService } from '../avrop.service';
import { Observable } from 'rxjs';
import { Deltagare } from '../models/Deltagare';
import { MultiselectFilterOption } from '../models/AvropFilterOptions';
@Component({
@@ -10,7 +9,7 @@ import { MultiselectFilterOption } from '../models/AvropFilterOptions';
styleUrls: ['./avrop-filters.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropFiltersComponent implements OnInit {
export class AvropFiltersComponent {
selectableTjanster$: Observable<MultiselectFilterOption[]> = this.avropService.selectableTjanster$;
selectableUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.selectableUtforandeVerksamheter$;
@@ -18,17 +17,15 @@ export class AvropFiltersComponent implements OnInit {
constructor(private avropService: AvropService) {}
ngOnInit(): void {}
updateSelectedTjanster(filterOptions: MultiselectFilterOption[]) {
updateSelectedTjanster(filterOptions: MultiselectFilterOption[]): void {
this.avropService.setSelectedTjanster(filterOptions);
}
updateSelectedUtforandeVerksamheter(filterOptions: MultiselectFilterOption[]) {
updateSelectedUtforandeVerksamheter(filterOptions: MultiselectFilterOption[]): void {
this.avropService.setSelectedUtforandeVerksamheter(filterOptions);
}
updateSelectedKommuner(filterOptions: MultiselectFilterOption[]) {
updateSelectedKommuner(filterOptions: MultiselectFilterOption[]): void {
this.avropService.setSelectedKommuner(filterOptions);
}
}

View File

@@ -1,7 +1,6 @@
import { Component, OnInit, ChangeDetectionStrategy, Input, Output } from '@angular/core';
import { MultiselectFilterOption } from '../../models/AvropFilterOptions';
import { BehaviorSubject, Observable } from 'rxjs';
import { Deltagare } from '../../models/Deltagare';
import { EventEmitter } from '@angular/core';
@Component({
@@ -20,7 +19,6 @@ export class TemporaryFilterComponent implements OnInit {
@Output() selectedOptionsChange = new EventEmitter<MultiselectFilterOption[]>();
// THIS SHOULD BE REPLACED BY DIGI COMPONENT
constructor() {}
ngOnInit(): void {
this._selectedAvropFilterOption$ = new BehaviorSubject<MultiselectFilterOption[]>(this.selectedOptions);
@@ -31,7 +29,7 @@ export class TemporaryFilterComponent implements OnInit {
return this.selectedOptions?.includes(filterOption) ?? false;
}
setOptionState(filterOption: MultiselectFilterOption, isSelected: boolean) {
setOptionState(filterOption: MultiselectFilterOption, isSelected: boolean): void {
if (isSelected) {
return this._selectedAvropFilterOption$.next([...(this._selectedAvropFilterOption$.value ?? []), filterOption]);
}
@@ -40,7 +38,7 @@ export class TemporaryFilterComponent implements OnInit {
);
}
emitSelectedOptions() {
emitSelectedOptions(): void {
this.selectedOptionsChange.emit(this._selectedAvropFilterOption$.value);
}
}

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from '@angular/core';
import { Component, OnInit, ChangeDetectionStrategy, Input, Output } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, Output } from '@angular/core';
import { Deltagare } from '../../models/Deltagare';
import { Handledare } from '../../models/Handledare';
@@ -9,18 +9,15 @@ import { Handledare } from '../../models/Handledare';
styleUrls: ['./avrop-table-row.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropTableRowComponent implements OnInit {
export class AvropTableRowComponent {
@Input() deltagare: Deltagare;
@Input() isSelected: boolean;
@Input() isLocked: boolean;
@Output() isSelectedChange = new EventEmitter<boolean>();
@Input() handledare: Handledare;
@Input() handledareConfirmed: boolean;
constructor() {}
ngOnInit(): void {}
emitSelectionChange(isSelected: boolean) {
emitSelectionChange(isSelected: boolean): void {
this.isSelectedChange.emit(isSelected);
}
}

View File

@@ -25,8 +25,6 @@ export class AvropTableComponent implements OnInit {
return this.isLocked ? this.selectedDeltagareListInput : this.selectableDeltagareList;
}
constructor() {}
ngOnInit(): void {
this._selectedDeltagare$
.pipe(filter(x => !!x))

View File

@@ -8,7 +8,7 @@ describe('CallOffComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [AvropComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { AvropService } from './avrop.service';
import { Observable } from 'rxjs';
import { MultiselectFilterOption } from './models/AvropFilterOptions';
@@ -11,7 +11,7 @@ import { Handledare } from './models/Handledare';
styleUrls: ['./avrop.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropComponent implements OnInit {
export class AvropComponent {
steps = 3;
currentStep$ = this.avropService.currentStep$;
@@ -27,41 +27,37 @@ export class AvropComponent implements OnInit {
constructor(private avropService: AvropService) {}
updateSelectedDeltagareList(deltagareList: Deltagare[]) {
updateSelectedDeltagareList(deltagareList: Deltagare[]): void {
this.avropService.setSelectedDeltagare(deltagareList);
}
lockSelectedDeltagare() {
lockSelectedDeltagare(): void {
this.avropService.lockSelectedDeltagare();
}
unlockSelectedDeltagare() {
unlockSelectedDeltagare(): void {
this.avropService.unlockSelectedDeltagare();
}
ngOnInit(): void {
// this.avropService.loadFromAPI();
}
confirmHandledare() {
confirmHandledare(): void {
this.avropService.confirmHandledare();
}
unconfirmHandledare() {
unconfirmHandledare(): void {
this.avropService.unconfirmHandledare();
}
save() {
this.avropService.save();
async save(): Promise<void> {
return this.avropService.save();
}
changeHandledare(newHandledare: Event) {
const handledareId = newHandledare.target['value'];
changeHandledare(newHandledare: { target: HTMLInputElement }): void {
const handledareId = newHandledare.target.value;
this.avropService.setHandledareState(handledareId);
}
goToStep1() {
goToStep1(): void {
this.avropService.goToStep1();
}
}

View File

@@ -109,35 +109,35 @@ export class AvropService {
return 1;
}
setSelectedDeltagare(deltagare: Deltagare[]) {
setSelectedDeltagare(deltagare: Deltagare[]): void {
this._selectedDeltagareList$.next(deltagare);
}
constructor(private avropApiService: AvropApiService) {}
lockSelectedDeltagare() {
lockSelectedDeltagare(): void {
if ((this._selectedDeltagareList$?.value?.length ?? -1) <= 0) {
throw new Error('För att låsa deltagare behöver några ha markerats först.');
}
this._deltagareListIsLocked$.next(true);
}
unlockSelectedDeltagare() {
unlockSelectedDeltagare(): void {
this._deltagareListIsLocked$.next(false);
}
confirmHandledare() {
confirmHandledare(): void {
if (!this._selectedHandledare$?.value) {
throw new Error('För att kunna tilldela behövs en handledare väljas först.');
}
this._handledareIsConfirmed$.next(true);
}
unconfirmHandledare() {
unconfirmHandledare(): void {
this._handledareIsConfirmed$.next(false);
}
async save() {
async save(): Promise<void> {
if (!this._handledareIsConfirmed$) {
throw new Error('Handledaren måste bekräftas innan avropet kan sparas');
}
@@ -148,27 +148,28 @@ export class AvropService {
await this.avropApiService.tilldelaHandledare(this._selectedDeltagareList$.value, this._selectedHandledare$.value);
this._avropIsSaved$.next(true);
return;
}
setHandledareState(handledareId: string) {
setHandledareState(handledareId: string): void {
this.selectableHandledareList$.pipe(first()).subscribe(handledareList => {
this._selectedHandledare$.next(handledareList.find(handledare => handledare.id === handledareId));
});
}
setSelectedTjanster(selectedFilterOptions: MultiselectFilterOption[]) {
setSelectedTjanster(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedTjanster$.next(selectedFilterOptions);
}
setSelectedUtforandeVerksamheter(selectedFilterOptions: MultiselectFilterOption[]) {
setSelectedUtforandeVerksamheter(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedUtforandeVerksamheter$.next(selectedFilterOptions);
}
setSelectedKommuner(selectedFilterOptions: MultiselectFilterOption[]) {
setSelectedKommuner(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedKommuner$.next(selectedFilterOptions);
}
goToStep1() {
goToStep1(): void {
this._selectedHandledare$.next(null);
this._selectedDeltagareList$.next(null);
this._deltagareListIsLocked$.next(false);

View File

@@ -7,32 +7,31 @@ import { AuthenticationService } from '@dafa-services/api/authentication.service
selector: 'dafa-ciam-landing',
templateUrl: './ciam-landing.component.html',
styleUrls: ['./ciam-landing.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CiamLandingComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService
) {}
constructor(private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService
) {
}
ngOnInit() {
this.route.queryParams.pipe(
first(),
map(({code}) => {
if (!code) {
throw new Error('Expected CIAM to return \'code\' in queryparams.');
}
return code as string;
}),
switchMap(code => {
return this.authenticationService.login$(code)
}))
ngOnInit(): void {
this.route.queryParams
.pipe(
first(),
map(({ code }) => {
if (!code) {
throw new Error('Expected CIAM to return "code" in queryparams.');
}
return code as string;
}),
switchMap(code => {
return this.authenticationService.login$(code);
})
)
.subscribe(() => {
this.router.navigateByUrl('/')
})
;
void this.router.navigateByUrl('/');
});
}
}

View File

@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { environment } from '@dafa-environment';
import { AuthenticationService } from '@dafa-services/api/authentication.service';
@@ -12,11 +11,7 @@ import { AuthenticationService } from '@dafa-services/api/authentication.service
export class LogoutComponent implements OnInit {
loginUrl = environment.loginUrl;
constructor(
private authenticationService: AuthenticationService
) {
}
constructor(private authenticationService: AuthenticationService) {}
ngOnInit(): void {
this.authenticationService.logout();

View File

@@ -3,15 +3,10 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { LogoutComponent } from './logout.component';
import { DigiNgButtonModule } from '@af/digi-ng/_button/button';
import { LoggedInShellModule } from '../../components/logged-in-shell/logged-in-shell.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [LogoutComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: LogoutComponent }]),
DigiNgButtonModule
]
imports: [CommonModule, RouterModule.forChild([{ path: '', component: LogoutComponent }]), DigiNgButtonModule],
})
export class LogoutModule {}

View File

@@ -8,7 +8,7 @@ describe('MessagesComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [MessagesComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -8,7 +8,7 @@ describe('ReleasesComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
declarations: [MockLoginComponent],
}).compileComponents();

View File

@@ -3,15 +3,10 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MockLoginComponent } from './mock-login.component';
import { DigiNgButtonModule } from '@af/digi-ng/_button/button';
import { LoggedInShellModule } from '../../components/logged-in-shell/logged-in-shell.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [MockLoginComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: MockLoginComponent }]),
DigiNgButtonModule
]
imports: [CommonModule, RouterModule.forChild([{ path: '', component: MockLoginComponent }]), DigiNgButtonModule],
})
export class MockLoginModule {}

View File

@@ -9,7 +9,7 @@ describe('PageNotFoundComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [PageNotFoundComponent],
imports: [RouterTestingModule],

View File

@@ -14,7 +14,7 @@ import { map, switchMap } from 'rxjs/operators';
})
export class ParticipantCardComponent {
private _participantId$: Observable<string> = this.activatedRoute.params.pipe(
map(({ participantId }) => participantId)
map(({ participantId }) => participantId as string)
);
detailedParticipantData$: Observable<Participant> = this._participantId$.pipe(
switchMap(participantId => this.participantsService.fetchDetailedParticipantData$(participantId))

View File

@@ -4,8 +4,8 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BackLinkModule } from '@dafa-shared/components/back-link/back-link.module';
import { IconModule } from '@dafa-shared/components/icon/icon.module';
import { LoggedInShellModule } from 'apps/dafa-web/src/app/components/logged-in-shell/logged-in-shell.module';
import { ParticipantCardComponent } from './participant-card.component';
import { LoggedInShellModule } from '../../../../components/logged-in-shell/logged-in-shell.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],

View File

@@ -12,7 +12,7 @@ describe('ParticipantsComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ParticipantsComponent],
imports: [RouterTestingModule, HttpClientTestingModule, DigiNgSkeletonBaseModule, ParticipantsListModule],

View File

@@ -30,6 +30,7 @@ export class ParticipantsComponent {
}
handleSearchInput($event: CustomEvent): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
this._searchValue$.next($event.detail.target.value);
}

View File

@@ -8,7 +8,7 @@ describe('ReleasesComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
declarations: [ReleasesComponent],
}).compileComponents();

View File

@@ -8,7 +8,7 @@ describe('SettingsComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [SettingsComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -9,7 +9,7 @@ describe('StartComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [StartComponent],
imports: [RouterTestingModule],

View File

@@ -1,12 +1,4 @@
import { CardHeadingLevel, CardVariation } from '@af/digi-ng/_card/card';
import {
NotificationAlertHeadingLevel,
NotificationAlertSize,
NotificationAlertVariation,
} from '@af/digi-ng/_notification/notification-alert';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { InfoCardHeadingLevel } from '@digi/core/dist/types/components/_info-card/info-card/info-card-heading-level.enum';
import { InfoCardType } from '@digi/core/dist/types/components/_info-card/info-card/info-card-type.enum';
@Component({
selector: 'dafa-start',

View File

@@ -8,7 +8,7 @@ describe('StatisticsComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
declarations: [StatisticsComponent],
imports: [RouterTestingModule],
}).compileComponents();

View File

@@ -1,24 +1,22 @@
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { AuthenticationService } from './authentication.service';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthenticationService) {}
constructor(private auth: AuthenticationService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler) {
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const idToken = this.auth.getAuthorizationToken();
if (idToken) {
const cloned = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + idToken)
headers: req.headers.set('Authorization', 'Bearer ' + idToken),
});
return next.handle(cloned);
}
else {
} else {
return next.handle(req);
}
}

View File

@@ -6,19 +6,19 @@ import { map, tap } from 'rxjs/operators';
import {
AuthenticationApiResponse,
AuthenticationResult,
mapAuthApiResponseToAuthenticationResult
mapAuthApiResponseToAuthenticationResult,
} from '@dafa-models/authentication.model';
import { add, isBefore } from 'date-fns';
const API_HEADERS = { headers: environment.api.headers };
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class AuthenticationService {
private static _authTokenApiUrl(code: string): string {
return `${environment.api.url}/get-token?code=${code}`;
};
}
private static _setSession(authenticationResult: AuthenticationResult): void {
const expiresAt = add(new Date(), { seconds: authenticationResult.expiresIn });
@@ -52,16 +52,13 @@ export class AuthenticationService {
getExpiration(): number {
const expiration = localStorage.getItem('expires_at');
const expiresAt = JSON.parse(expiration);
return expiresAt;
return JSON.parse(expiration) as number;
}
constructor(private httpClient: HttpClient) {
}
constructor(private httpClient: HttpClient) {}
logout(): void {
localStorage.removeItem('id_token');
localStorage.removeItem('expires_at');
}
}

View File

@@ -30,8 +30,6 @@ const tempTjansterMock: MultiselectFilterOption[] = [
providedIn: 'root',
})
export class AvropApiService {
constructor() {}
getNyaDeltagare$(
tjanstIds: MultiselectFilterOption[],
kommunIds: MultiselectFilterOption[],
@@ -98,8 +96,9 @@ export class AvropApiService {
return of(tempKommunerMock).pipe(delay(300));
}
async tilldelaHandledare(deltagare: Deltagare[], handledare: Handledare) {
async tilldelaHandledare(deltagare: Deltagare[], handledare: Handledare): Promise<void> {
console.log('[API call] SAVE avrop. Inputs: deltagare, handledare', deltagare, handledare);
await of(null).pipe(delay(200)).toPromise();
// TODO anropa API
return;
}

View File

@@ -28,6 +28,13 @@ export class EmployeeService {
public sort$: Observable<Sort<keyof Employee>> = this._sort$.asObservable();
private _searchFilter$ = new BehaviorSubject<string>('');
public employeesData$: Observable<EmployeesData> = combineLatest([
this._limit$,
this._page$,
this._sort$,
this._searchFilter$,
]).pipe(switchMap(([limit, page, sort, searchFilter]) => this._fetchEmployees$(limit, page, sort, searchFilter)));
private _fetchEmployees$(
limit: number,
page: number,
@@ -57,13 +64,6 @@ export class EmployeeService {
);
}
public employeesData$: Observable<EmployeesData> = combineLatest([
this._limit$,
this._page$,
this._sort$,
this._searchFilter$,
]).pipe(switchMap(([limit, page, sort, searchFilter]) => this._fetchEmployees$(limit, page, sort, searchFilter)));
public fetchDetailedEmployeeData$(id: string): Observable<Employee> {
return this.httpClient
.get<EmployeeApiResponse>(`${this._apiUrl}/${id}`, { ...API_HEADERS })
@@ -72,25 +72,28 @@ export class EmployeeService {
constructor(private httpClient: HttpClient) {}
public setSearchFilter(value: string) {
public setSearchFilter(value: string): void {
this._searchFilter$.next(value);
}
public setSort(newSortKey: keyof Employee) {
public setSort(newSortKey: keyof Employee): void {
const currentSort = this._sort$.getValue();
let order = currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
const order =
currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
this._sort$.next({ key: newSortKey, order });
}
public setPage(page: number) {
public setPage(page: number): void {
this._page$.next(page);
}
public postNewEmployee(employeeData: Employee): Observable<string> {
return this.httpClient.post<any>(this._apiUrl, mapEmployeeToEmployeeApiRequestData(employeeData), API_HEADERS).pipe(
map(({ id }) => id),
catchError(error => throwError({ message: error, type: ErrorType.API }))
);
return this.httpClient
.post<{ id: string }>(this._apiUrl, mapEmployeeToEmployeeApiRequestData(employeeData), API_HEADERS)
.pipe(
map(({ id }) => id),
catchError(error => throwError({ message: error as string, type: ErrorType.API }))
);
}
}

View File

@@ -83,11 +83,11 @@ export class ParticipantsService {
.pipe(map(result => mapParticipantApiResponseToParticipant(result.data)));
}
public setSearchFilter(value: string) {
public setSearchFilter(value: string): void {
this._searchFilter$.next(value);
}
public setActiveParticipantsSortKey(key: keyof Participant) {
public setActiveParticipantsSortKey(key: keyof Participant): void {
const currentSortBy = this._activeParticipantsSortBy$.getValue();
let order = currentSortBy.order;
if (currentSortBy?.key === key) {
@@ -96,7 +96,7 @@ export class ParticipantsService {
this._activeParticipantsSortBy$.next({ key, order });
}
public setFollowUpParticipantsSortKey(key: keyof Participant) {
public setFollowUpParticipantsSortKey(key: keyof Participant): void {
const currentSortBy = this._followUpParticipantsSortBy$.getValue();
let order = currentSortBy.order;
if (currentSortBy?.key === key) {

View File

@@ -7,8 +7,8 @@ import { map } from 'rxjs/operators';
providedIn: 'root',
})
export class ErrorService {
private appRef: any;
private errorQueue$: BehaviorSubject<CustomError[]> = new BehaviorSubject([]);
private appRef: ApplicationRef;
private errorQueue$ = new BehaviorSubject<CustomError[]>([]);
public errors$: Observable<CustomError[]> = this.errorQueue$.pipe(
map(errors => errors.sort((a, b) => (a.timestamp > b.timestamp ? -1 : 1)))
@@ -20,12 +20,12 @@ export class ErrorService {
setTimeout(() => (this.appRef = this.injector.get(ApplicationRef)));
}
public add(error: CustomError) {
public add(error: CustomError): void {
this.errorQueue$.next([...this.errorQueue$.value, error]);
this.appRef.tick();
}
public remove(error: CustomError) {
public remove(error: CustomError): void {
const newErrorQueue = this.errorQueue$.value.filter(currentError => currentError.id !== error.id);
this.errorQueue$.next(newErrorQueue);
this.appRef.tick();

View File

@@ -8,7 +8,7 @@ describe('BackLinkComponent', () => {
let fixture: ComponentFixture<BackLinkComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [BackLinkComponent],
}).compileComponents();

View File

@@ -1,18 +1,18 @@
/* tslint:disable:no-unused-variable */
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IconComponent } from './icon.component';
describe('IconComponent', () => {
let component: IconComponent;
let fixture: ComponentFixture<IconComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
beforeEach(() => {
void TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [IconComponent],
}).compileComponents();
}));
});
beforeEach(() => {
fixture = TestBed.createComponent(IconComponent);

View File

@@ -1,5 +1,5 @@
import { Navigation } from '@dafa-constants/navigation';
export function mapPathToPageName(path: string): string {
return Navigation[path] || `${path.charAt(0).toUpperCase()}${path.slice(1)}`;
return (Navigation[path] || `${path.charAt(0).toUpperCase()}${path.slice(1)}`) as string;
}

View File

@@ -7,13 +7,13 @@ export function SocialSecurityNumberValidator(): ValidatorFn {
return null;
}
const ssn = control.value;
const ssn = control.value as string;
if (/[^0-9-]/g.test(ssn)) {
return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
}
let strippedSsn = control.value.replace(/[^0-9]/g, '');
let strippedSsn = ssn.replace(/[^0-9]/g, '');
// Year format 1991 -> 91
if (strippedSsn.length === 12) {
@@ -26,14 +26,14 @@ export function SocialSecurityNumberValidator(): ValidatorFn {
}
// Check month
if (strippedSsn.substr(2, 2) > 12 || strippedSsn.substr(2, 2) === '00') {
if (+strippedSsn.substr(2, 2) > 12 || strippedSsn.substr(2, 2) === '00') {
return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
}
// Check date (valid date + 60 is also apporved because of co-ordination number)
if (
(strippedSsn.substr(4, 2) > 31 || strippedSsn.substr(4, 2) === '00') &&
(strippedSsn.substr(4, 2) > 91 || strippedSsn.substr(4, 2) <= 60)
(+strippedSsn.substr(4, 2) > 31 || strippedSsn.substr(4, 2) === '00') &&
(+strippedSsn.substr(4, 2) > 91 || +strippedSsn.substr(4, 2) <= 60)
) {
return { type: 'ssnInvalid', message: 'Inkorrekt personnummer' };
}

View File

@@ -1,5 +1,7 @@
export const environment = {
loginUrl: 'https://ciam-test.arbetsformedlingen.se:8443/uas/oauth2/authorization?response_type=code&scope=openid&redirect_uri=https://localhost:4200/ciam-landing&client_id=5d08c2e4-763e-42f6-b858-24e4773bb83d',
loginUrl:
// eslint-disable-next-line max-len
'https://ciam-test.arbetsformedlingen.se:8443/uas/oauth2/authorization?response_type=code&scope=openid&redirect_uri=https://localhost:4200/ciam-landing&client_id=5d08c2e4-763e-42f6-b858-24e4773bb83d',
production: false,
api: {
url: '/api',

View File

@@ -12,4 +12,4 @@ platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
defineCustomElements();
void defineCustomElements();

1871
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -42,6 +42,7 @@
"dependencies": {
"@af/auth": "^11.1.0",
"@af/digi-ng": "^14.0.0",
"@angular-eslint/schematics": "^1.2.0",
"@angular/animations": "^11.2.0",
"@angular/cdk": "^11.2.12",
"@angular/common": "^11.2.0",