diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare-routing.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare-routing.module.ts index d5f3f36..032bc5f 100644 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare-routing.module.ts +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare-routing.module.ts @@ -15,7 +15,8 @@ const routes: Routes = [ { path: 'rapportera/:deltagareId', data: { title: 'Skapa rapport' }, - loadChildren: () => import('./pages/deltagare-report/deltagare-report.module').then(m => m.DeltagareReportModule), + // eslint-disable-next-line max-len + loadChildren: () => import('./pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.module').then(m => m.DeltagareAvvikelseModule), }, { path: 'planering/:deltagareId', @@ -31,4 +32,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) -export class DeltagareRoutingModule {} +export class DeltagareRoutingModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.html deleted file mode 100644 index 9c2f60c..0000000 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.html +++ /dev/null @@ -1,13 +0,0 @@ - -
- -

Avvikelserapport

-

Här ska det komma en förklarande text om avvikelserapporter.

-

Skapa rapport

-
-

Deltagare: Göran Ekman

-

Personnummer: 999999

-
-
-
-
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.spec.ts deleted file mode 100644 index 7ad0608..0000000 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { LayoutComponent } from '@msfa-shared/components/layout/layout.component'; - -import { DeltagareReportComponent } from './deltagare-report.component'; - -describe('DeltagareReportComponent', () => { - let component: DeltagareReportComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA], - declarations: [DeltagareReportComponent, LayoutComponent], - imports: [RouterTestingModule, HttpClientTestingModule] - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DeltagareReportComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.ts deleted file mode 100644 index db026e6..0000000 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -@Component({ - selector: 'msfa-deltagare-report', - templateUrl: './deltagare-report.component.html', - styleUrls: ['./deltagare-report.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DeltagareReportComponent { - - - -} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.module.ts deleted file mode 100644 index 4d58ea6..0000000 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/deltagare-report.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { LayoutModule } from '@msfa-shared/components/layout/layout.module'; -import { DeltagareReportComponent } from './deltagare-report.component'; - - - -@NgModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA], - declarations: [DeltagareReportComponent], - imports: [ - CommonModule, - RouterModule.forChild([{ path: '', component: DeltagareReportComponent }]), - LayoutModule - ], - exports: [DeltagareReportComponent] -}) -export class DeltagareReportModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.html new file mode 100644 index 0000000..1ce221a --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.html @@ -0,0 +1,38 @@ +
+ +
+ + + Beskrivning är obligatoriskt + +
+
+ +
+ + + Beskrivning är obligatoriskt + +
+
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.scss new file mode 100644 index 0000000..1aef2ca --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.scss @@ -0,0 +1,7 @@ +@import 'variables/gutters'; + +.fragor-form { + &__content { + margin-bottom: $digi--layout--gutter--xl; + } +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.spec.ts new file mode 100644 index 0000000..8fa0f24 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.spec.ts @@ -0,0 +1,30 @@ +import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group'; +import { DigiNgFormTextareaModule } from '@af/digi-ng/_form/form-textarea'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { DeltagareFragorFormComponent } from './deltagare-fragor-form.component'; + +describe('DeltagareAvvikelseFormComponent', () => { + let component: DeltagareFragorFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + imports: [DigiNgFormRadiobuttonGroupModule, ReactiveFormsModule, DigiNgFormTextareaModule], + declarations: [DeltagareFragorFormComponent] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeltagareFragorFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.ts new file mode 100644 index 0000000..74377d5 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.component.ts @@ -0,0 +1,20 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model'; + +@Component({ + selector: 'msfa-deltagare-fragor-form', + templateUrl: './deltagare-fragor-form.component.html', + styleUrls: ['./deltagare-fragor-form.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush + +}) + +export class DeltagareFragorFormComponent { + @Input() fragor1: FragorForAvvikelser[] | null = null; + @Input() fragor2: FragorForAvvikelser[] | null = null; + @Input() fragorFormGroup: FormGroup | null = null; + @Input() avvikelseFormGroup: FormGroup | null = null; + @Input() selectedOrsaksKod: string | null = null; + +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.module.ts new file mode 100644 index 0000000..bffba9d --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/deltagare-fragor-form.module.ts @@ -0,0 +1,20 @@ +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DeltagareFragorFormComponent } from './deltagare-fragor-form.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { DigiNgFormTextareaModule } from '@af/digi-ng/_form/form-textarea'; +import { FilterFragorPipe } from './filter-fragor.pipe'; + + + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareFragorFormComponent, FilterFragorPipe], + imports: [ + CommonModule, + ReactiveFormsModule, + DigiNgFormTextareaModule + ], + exports: [DeltagareFragorFormComponent] +}) +export class DeltagareFragorFormModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/filter-fragor.pipe.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/filter-fragor.pipe.ts new file mode 100644 index 0000000..e1c8bb9 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-fragor-form/filter-fragor.pipe.ts @@ -0,0 +1,19 @@ + +import { Pipe, PipeTransform } from '@angular/core'; +import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model'; + +@Pipe({ + name: 'filterFragor', + +}) +export class FilterFragorPipe implements PipeTransform { + transform(fragor: FragorForAvvikelser[], id: string): FragorForAvvikelser[] { + if (fragor?.length === 0) { + return fragor; + } + + return fragor?.filter(fraga => fraga?.id.substring(0, 2) === id); + } +} + + diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.html new file mode 100644 index 0000000..81bb409 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.html @@ -0,0 +1,39 @@ +
+ +
+ + + Orsak är obligatoriskt + +
+
+ + +
+ + + Orsak är obligatoriskt + +
+
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.scss new file mode 100644 index 0000000..095b4ae --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.scss @@ -0,0 +1,7 @@ +@import 'variables/gutters'; + +.orsaks-form { + &__content { + margin-bottom: $digi--layout--gutter--xl; + } +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.spec.ts new file mode 100644 index 0000000..b76083e --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.spec.ts @@ -0,0 +1,30 @@ +import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group'; +import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { DeltagareOrsaksFormComponent } from './deltagare-orsaks-form.component'; + + +describe('DeltagareAvvikelseFormComponent', () => { + let component: DeltagareOrsaksFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + imports: [DigiNgFormRadiobuttonGroupModule, ReactiveFormsModule, DigiNgFormSelectModule], + declarations: [DeltagareOrsaksFormComponent] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeltagareOrsaksFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.ts new file mode 100644 index 0000000..92a7365 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.component.ts @@ -0,0 +1,33 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { Alternative } from '@msfa-enums/alternative.enum'; +import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum'; +import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model'; +import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model'; + +@Component({ + selector: 'msfa-deltagare-orsaks-form', + templateUrl: './deltagare-orsaks-form.component.html', + styleUrls: ['./deltagare-orsaks-form.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DeltagareOrsaksFormComponent implements OnChanges { + @Input() franvaroOrsaker: OrsaksKoderFranvaro[] | null = null; + @Input() avvikelseOrsaker: OrsaksKoderAvvikelse[] | null = null; + @Input() andraKandaOrsaker: OrsaksKoderFranvaro[] | null = null; + @Input() orsakerFormGroup: FormGroup | null = null; + @Input() avvikelseFormGroup: FormGroup | null = null; + @Input() selectedAlternative: string | null = null; + + ngOnChanges(changes: SimpleChanges): void { + if (changes) { + this.orsakerFormGroup.reset(); + } + } + + get showAndraKandaOrsaker(): boolean { + return this.selectedAlternative === Alternative.FRANVARO && + +this.orsakerFormGroup.get('orsaker')?.value === FranvaroOrsaksKodEnum.AnnanKandOrsak; + } + +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.module.ts new file mode 100644 index 0000000..752cd17 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-orsaks-form/deltagare-orsaks-form.module.ts @@ -0,0 +1,19 @@ +import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select'; +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { DeltagareOrsaksFormComponent } from './deltagare-orsaks-form.component'; + + + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareOrsaksFormComponent], + imports: [ + CommonModule, + ReactiveFormsModule, + DigiNgFormSelectModule + ], + exports: [DeltagareOrsaksFormComponent] +}) +export class DeltagareOrsaksFormModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.html new file mode 100644 index 0000000..3aee8b7 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.html @@ -0,0 +1,38 @@ +
+
+ + + + +
+ + + Starttid är obligatoriskt + +
+
+ + + Sluttid är obligatoriskt + +
+
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.scss new file mode 100644 index 0000000..36cc7f9 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.scss @@ -0,0 +1,16 @@ +@import 'variables/gutters'; + +.deltagare-timepicker { + margin-bottom: $digi--layout--gutter--xl; + &__heading { + font-weight: var(--digi--typography--font-weight--semibold); + } + + &__start-time { + margin-bottom: $digi--layout--gutter--m; + } + + &__input { + width: 127px; + } +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.spec.ts new file mode 100644 index 0000000..862dcc3 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.spec.ts @@ -0,0 +1,30 @@ +import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input'; +import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { DeltagareTimePickerComponent } from './deltagare-time-picker.component'; + + +describe('DeltagareTimePickerComponent', () => { + let component: DeltagareTimePickerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareTimePickerComponent], + imports: [ReactiveFormsModule, DigiNgFormSelectModule, DigiNgFormInputModule] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeltagareTimePickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.ts new file mode 100644 index 0000000..328fe97 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.component.ts @@ -0,0 +1,13 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +@Component({ + selector: 'msfa-deltagare-time-picker', + templateUrl: './deltagare-time-picker.component.html', + styleUrls: ['./deltagare-time-picker.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DeltagareTimePickerComponent { + @Input() timepickerFormGroup: FormGroup | null = null; + @Input() avvikelseFormGroup: FormGroup | null = null; +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.module.ts new file mode 100644 index 0000000..2bb832e --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/components/deltagare-time-picker/deltagare-time-picker.module.ts @@ -0,0 +1,21 @@ +import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select'; +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { DeltagareTimePickerComponent } from './deltagare-time-picker.component'; +import {DigiNgFormInputModule} from '@af/digi-ng/_form/form-input' + + + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareTimePickerComponent], + imports: [ + CommonModule, + ReactiveFormsModule, + DigiNgFormSelectModule, + DigiNgFormInputModule + ], + exports: [DeltagareTimePickerComponent] +}) +export class DeltagareTimePickerModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.html new file mode 100644 index 0000000..dbb4de1 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.html @@ -0,0 +1,118 @@ + +
+ +

Rapport

+

+ Här rapporterar du deltagarens frånvaro och eventuella misskötsel i tjänsten. Rapportering via avvikelserapport + ska också ske om tjänsten inte fungerar för deltagaren. +

+

Skapa rapport

+
+

Deltagare: {{contactInformation.fornamn + ' ' + contactInformation.efternamn}}

+

Personnummer: {{contactInformation.personnummer}}

+
+
+

Vad är det du vill rapportera?

+
+
+ + + Alternativ är obligatoriskt + +
+ + + +
+ + + Beskrivning är obligatoriskt + +
+
+ + + + + +
+ + + {{avvikelseFormGroup.errors?.dateIsRequired}} + +
+ +
+ + + Hel- eller halvdag är obligatoriskt + +
+ + + + + + Tillbaka + + Nästa +
+
+
+
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.scss new file mode 100644 index 0000000..2c59c52 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.scss @@ -0,0 +1,27 @@ +@import 'variables/gutters'; + +.deltagare-avvikelse { + &__deltagare, + &__alternative, + &__description, + &__datepicker, + &__dayOrPartOfDay { + margin-bottom: $digi--layout--gutter--xl; + + span { + font-weight: var(--digi--typography--font-weight--semibold); + } + + p { + margin: var(--digi--layout--gutter--s) 0 0; + } + } + + &__alternative-heading { + font-size: var(--digi--typography--font-size--h3); + } + + &__go-back { + margin-right: $digi--layout--gutter; + } +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.spec.ts new file mode 100644 index 0000000..b4e8da9 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.spec.ts @@ -0,0 +1,37 @@ +import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; +import { LayoutComponent } from '@msfa-shared/components/layout/layout.component'; +import { DeltagareAvvikelseComponent } from './deltagare-avvikelse.component'; + + +describe('DeltagareAvvikelseComponent', () => { + let component: DeltagareAvvikelseComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareAvvikelseComponent, LayoutComponent], + imports: [ + RouterTestingModule, + HttpClientTestingModule, + ReactiveFormsModule, + DigiNgFormRadiobuttonGroupModule + ] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeltagareAvvikelseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.ts new file mode 100644 index 0000000..9da4781 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.component.ts @@ -0,0 +1,303 @@ +import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group'; +import { FormTextareaSize } from '@af/digi-ng/_form/form-textarea'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Alternative } from '@msfa-enums/alternative.enum'; +import { DayOrPartOfDay } from '@msfa-enums/day-or-part-of-day.enum'; +import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum'; +import { KandaOrsakerEnum } from '@msfa-enums/kanda-orsaker-kod.enum'; +import { ContactInformationCompact } from '@msfa-models/api/contact-information.response.model'; +import { AvvikelseAlternativ } from '@msfa-models/avvikelse-alternativ.model'; +import { Avvikelse } from '@msfa-models/avvikelse.model'; +import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model'; +import { FranvaroAlternativ } from '@msfa-models/franvaro-alternativ.model'; +import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model'; +import { KandaAvvikelseKoder, OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model'; +import { + AnnanKandOrsakeIsRequiredCheck, + DateIsRequiredCheck, + DayOrPartOfDayIsRequiredCheck, + DescriptionIsRequiredCheck, + EndTimeIsRequiredCheck, + MotiveringIsRequiredCheck, + OrsakerIsRequiredCheck, StartTimeIsRequiredCheck +} from '@msfa-utils/validators/avvikelse-form-validator'; +import { RequiredValidator } from '@msfa-utils/validators/required.validator'; +import { Observable } from 'rxjs'; +import { first, map, switchMap } from 'rxjs/operators'; +import { DeltagareAvvikelseService } from '../../services/deltagare-avvikelse.service'; +import { avvikelseAlternatives, dayOrPartOfDay } from './report-alternatives'; + +@Component({ + selector: 'msfa-deltagare-avvikelse', + templateUrl: './deltagare-avvikelse.component.html', + styleUrls: ['./deltagare-avvikelse.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DeltagareAvvikelseComponent implements OnInit { + readonly alternativeFormControlName = 'alternative'; + readonly descriptionFormControlName = 'description' + readonly dateFormControlName = 'date'; + readonly dayOrPartOfDayFormControlName = 'dayOrPartOfDay'; + readonly orsakerFormControlName = 'orsaker' + readonly andraKandaOrsakerFormControlName = 'andraKandaOrsaker'; + readonly fraga1FormControlName = 'fraga1'; + readonly fraga2FormControlName = 'fraga2'; + readonly startTimeFormControlName = 'startTime'; + readonly endTimeFormControlName = 'endTime'; + + contactInformation$: Observable = this.activatedRoute.params + .pipe( + switchMap(({ deltagareId }) => this.deltagareAvvikelseService.getContactInformationCompact$(deltagareId)) + ) + franvaroOrsaker$: Observable; + avvikelseOrsaker$: Observable; + andraKandaOrsaker$: Observable; + fragor1$: Observable; + fragor2$: Observable; + contactInformation: ContactInformationCompact; + sizeTextArea: FormTextareaSize.S; + todayDate = new Date().toISOString().slice(0, 10); + avvikelseAlternatives: RadiobuttonModel[] = avvikelseAlternatives; + dayOrPartOfDay: RadiobuttonModel[] = dayOrPartOfDay; + selectedOrsaksKod: string; + avvikelseFormGroup: FormGroup | null = null; + + constructor( + private deltagareAvvikelseService: DeltagareAvvikelseService, + private activatedRoute: ActivatedRoute, + private router: Router) { + } + + ngOnInit(): void { + this.contactInformation$ + .pipe( + first(), + ) + .subscribe(contactInformation => { + this.contactInformation = contactInformation; + }); + + this.avvikelseFormGroup = new FormGroup({ + alternative: new FormControl(null, [RequiredValidator()]), + description: new FormControl(''), + date: new FormControl(this.todayDate), + dayOrPartOfDay: new FormControl(null), + orsakerFormGroup: new FormGroup({ + orsaker: new FormControl([]), + andraKandaOrsaker: new FormControl([]) + }), + fragorFormGroup: new FormGroup({ + fraga1: new FormControl(''), + fraga2: new FormControl('') + }), + timepickerFormGroup: new FormGroup({ + startTime: new FormControl(''), + endTime: new FormControl('') + }) + }, { + validators: + [ + DescriptionIsRequiredCheck.CheckIfRequired('description', 'orsakerFormGroup', 'andraKandaOrsaker', KandaOrsakerEnum.AnnanOrsak), + DayOrPartOfDayIsRequiredCheck.CheckIfRequired('dayOrPartOfDay'), + OrsakerIsRequiredCheck.CheckIfRequired('orsakerFormGroup', 'orsaker'), + AnnanKandOrsakeIsRequiredCheck.CheckIfRequired('orsakerFormGroup', 'andraKandaOrsaker'), + DateIsRequiredCheck.CheckIfRequired('date'), + StartTimeIsRequiredCheck.CheckIfRequired('timepickerFormGroup', 'startTime'), + EndTimeIsRequiredCheck.CheckIfRequired('timepickerFormGroup', 'endTime'), + MotiveringIsRequiredCheck.CheckIfRequired('fraga1') + ] + }) + } + + get alternativeFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get(this.alternativeFormControlName); + } + + get descriptionFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get(this.descriptionFormControlName); + } + + get dateFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get(this.dateFormControlName); + } + + get dayOrPartOfDayFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get(this.dayOrPartOfDayFormControlName); + } + + get orsakerFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get('orsakerFormGroup').get(this.orsakerFormControlName); + } + + get andraKandaOrsakerFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup?.get('orsakerFormGroup').get(this.andraKandaOrsakerFormControlName); + } + + get fraga1FormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup.get('fragorFormGroup').get(this.fraga1FormControlName); + } + + get fraga2FormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup.get('fragorFormGroup').get(this.fraga2FormControlName); + } + + get startTimeFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup.get('timepickerFormGroup').get(this.startTimeFormControlName); + } + + get endTimeFormControl(): AbstractControl | undefined { + return this.avvikelseFormGroup.get('timepickerFormGroup').get(this.endTimeFormControlName); + } + + onFormSubmitted(): void { + if (this.avvikelseFormGroup.invalid) { + this.alternativeFormControl.markAsTouched(); + return; + } + + this.avvikelseFormGroup.markAllAsTouched(); + + const postAvvikelse: Avvikelse = { + datum_for_rapportering: this.todayDate, + arbetssokande: { + personnummer: this.contactInformation.personnummer.toString(), + fornamn: this.contactInformation.fornamn.toString(), + efternamn: this.contactInformation.efternamn.toString() + }, + sokandeId: +this.activatedRoute.snapshot.params['deltagareId'] + } + + if (this.alternativeFormControl.value as string === Alternative.AVVIKELSE) { + postAvvikelse['avvikelsealternativ'] = this.avvikelse; + } else if (this.alternativeFormControl.value as string == Alternative.FRANVARO) { + postAvvikelse['franvaro'] = this.franvaro; + } + + this.deltagareAvvikelseService.createAvvikelse$(postAvvikelse, this.alternativeFormControl.value as string) + .subscribe({ + next: () => this.avvikelseFormGroup.reset() + }); + } + + get franvaro(): FranvaroAlternativ { + return { + avvikelseorsakskod: this.orsakerFormControl.value as string, + datum: this.dateFormControl.value as string, + heldag: this.dayOrPartOfDayFormControl.value === DayOrPartOfDay.HELDAG ? true : false, + starttid: this.startTimeFormControl.value as string || '9:00', + sluttid: this.endTimeFormControl.value as string || '16:00', + forvantad_narvaro: { + starttid: '', + sluttid: '' + }, + alternativ_for_kanda_orsaker: { + typ: this.andraKandaOrsakerFormControl.value as string || '', + motivering: this.descriptionFormControl.value as string + } + } + } + + get avvikelse(): AvvikelseAlternativ { + return { + avvikelseorsakskod: this.orsakerFormControl.value as string, + frageformular: [ + { + fraga: this.avvikelseFormGroup.get('orsakerFormGroup').get('orsaker').value as string + '_1', + svar: this.fraga1FormControl.value as string + }, + { + fraga: this.fraga2FormControl.value as string !== '' ? + this.avvikelseFormGroup.get('orsakerFormGroup').get('orsaker').value as string + '_2' : '', + svar: this.fraga2FormControl.value as string + } + ], + rapporteringsdatum: this.dateFormControl.value as string + } + } + + + setAlternative(): void { + if (this.alternativeFormControl.value as string == Alternative.FRANVARO) { + this.franvaroOrsaker$ = this.deltagareAvvikelseService.getOrsaksKoderFranvaro$(); + this.andraKandaOrsaker$ = this.deltagareAvvikelseService.getAndraKandaOrsaker$(); + } + + if (this.alternativeFormControl.value as string == Alternative.AVVIKELSE) { + this.avvikelseOrsaker$ = this.deltagareAvvikelseService.getOrsaksKoderAvvikelse$(); + this.fragor1$ = this.deltagareAvvikelseService.getFragorForAvvikelser$() + .pipe( + map(fragor => fragor.filter(fraga => fraga.id.includes('_1')) + ) + ); + + this.fragor2$ = this.deltagareAvvikelseService.getFragorForAvvikelser$() + .pipe( + map(fragor => fragor.filter(fraga => { + this.setIfRequiredDescription(fraga); + return fraga.id.includes('_2') + })) + ) + } + + this.clearControlOnAlternativeChange(); + } + + private setIfRequiredDescription(fraga: FragorForAvvikelser) { + fraga.id === '19_2' || fraga.id === '20_2' ? + fraga.descriptionIsRequired = false : + fraga.descriptionIsRequired = true; + } + + setOrsakerChanged(): void { + this.avvikelseFormGroup.markAsUntouched(); + if (this.alternativeFormControl.value as string === Alternative.AVVIKELSE) { + this.selectedOrsaksKod = this.avvikelseFormGroup.get('orsakerFormGroup').get('orsaker').value as string; + } + + this.avvikelseFormGroup.get('orsakerFormGroup').get('orsaker').valueChanges + .subscribe(value => { + if (value !== null) { + this.avvikelseFormGroup.get('orsakerFormGroup').get('andraKandaOrsaker').reset(); + } + }); + } + + get showDescription(): boolean { + return this.alternativeFormControl.value as string == Alternative.FRANVARO && + +this.orsakerFormControl.value === FranvaroOrsaksKodEnum.AnnanKandOrsak && + +this.andraKandaOrsakerFormControl.value === KandaOrsakerEnum.AnnanOrsak + } + + get showFragor(): boolean { + return this.alternativeFormControl.value as string === Alternative.AVVIKELSE && + this.orsakerFormControl.value > 0; + } + + get showDatePicker(): boolean { + return this.orsakerFormControl.value > 0; + } + + get showDayOrPartOfDayPicker(): boolean { + return this.alternativeFormControl.value as string === Alternative.FRANVARO && this.orsakerFormControl.value > 0; + } + + get showTimePicker(): boolean { + return this.alternativeFormControl.value as string === Alternative.FRANVARO && + this.dayOrPartOfDayFormControl.value as string === DayOrPartOfDay.DEL_AV_DAG + } + + private clearControlOnAlternativeChange(): void { + this.descriptionFormControl.setValue(''); + this.fraga1FormControl.setValue(''); + this.fraga2FormControl.setValue(''); + this.dayOrPartOfDayFormControl.reset(); + this.avvikelseFormGroup?.get('timepickerFormGroup').reset(); + } + + goBack(): void { + void this.router.navigate(['./deltagare']); + } + +} diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.module.ts new file mode 100644 index 0000000..2a8ca3c --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/deltagare-avvikelse.module.ts @@ -0,0 +1,32 @@ +import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker'; +import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group'; +import { DigiNgFormTextareaModule } from '@af/digi-ng/_form/form-textarea'; +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { LayoutModule } from '@msfa-shared/components/layout/layout.module'; +import { DeltagareFragorFormModule } from './components/deltagare-fragor-form/deltagare-fragor-form.module'; +import { DeltagareOrsaksFormModule } from './components/deltagare-orsaks-form/deltagare-orsaks-form.module'; +import { DeltagareTimePickerModule } from './components/deltagare-time-picker/deltagare-time-picker.module'; +import { DeltagareAvvikelseComponent } from './deltagare-avvikelse.component'; + + +@NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [DeltagareAvvikelseComponent], + imports: [ + CommonModule, + RouterModule.forChild([{ path: '', component: DeltagareAvvikelseComponent }]), + LayoutModule, + ReactiveFormsModule, + DigiNgFormRadiobuttonGroupModule, + DigiNgFormDatepickerModule, + DigiNgFormTextareaModule, + DeltagareOrsaksFormModule, + DeltagareFragorFormModule, + DeltagareTimePickerModule + ], + exports: [DeltagareAvvikelseComponent] +}) +export class DeltagareAvvikelseModule { } diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/report-alternatives.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/report-alternatives.ts new file mode 100644 index 0000000..3da09b6 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/pages/deltagare-avvikelse/report-alternatives.ts @@ -0,0 +1,24 @@ +import { Alternative } from '@msfa-enums/alternative.enum' +import { DayOrPartOfDay } from '@msfa-enums/day-or-part-of-day.enum' +import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group' + +export const avvikelseAlternatives: RadiobuttonModel[] = [ + { + label: 'Frånvaro', + value: Alternative.FRANVARO + }, + { + label: 'Avvikelse', + value: Alternative.AVVIKELSE + } +] + +export const dayOrPartOfDay: RadiobuttonModel[] = [{ + label: 'Heldag', + value: DayOrPartOfDay.HELDAG +}, +{ + label: 'Del av dag', + value: DayOrPartOfDay.DEL_AV_DAG +} +] diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/services/deltagare-avvikelse.service.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/services/deltagare-avvikelse.service.ts new file mode 100644 index 0000000..6640f2a --- /dev/null +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-report/services/deltagare-avvikelse.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@angular/core'; +import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum'; +import { ContactInformationCompact } from '@msfa-models/api/contact-information.response.model'; +import { Avvikelse } from '@msfa-models/avvikelse.model'; +import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model'; +import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model'; +import { KandaAvvikelseKoder, OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model'; +import { AvvikelseApiService } from '@msfa-services/api/avvikelse-api.service'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class DeltagareAvvikelseService { + + constructor(private avvikelseApiService: AvvikelseApiService) { } + + public getOrsaksKoderFranvaro$(): Observable { + return this.avvikelseApiService.getOrsaksKoderFranvaro$() + .pipe( + map((orsaksKoder: OrsaksKoderFranvaro[]) => this.sortOrsaksKoder(orsaksKoder)) + ) + } + + public getOrsaksKoderAvvikelse$(): Observable { + return this.avvikelseApiService.getOrsaksKoderAvvikelse$(); + } + + public getAndraKandaOrsaker$(): Observable { + return this.avvikelseApiService.getAndraKandaOrsaker$(); + } + + public getFragorForAvvikelser$(): Observable { + return this.avvikelseApiService.getFragorForAvvikelser$(); + } + + public createAvvikelse$(avvikelse: Avvikelse, alternative: string): Observable { + return this.avvikelseApiService.createAvvikelse$(avvikelse, alternative); + } + + public getContactInformationCompact$(id: number): Observable { + return this.avvikelseApiService.getContactInformation$(id); + } + + private sortOrsaksKoder(orsaksKoder: OrsaksKoderFranvaro[]): OrsaksKoderFranvaro[] { + orsaksKoder.map(orsak => { + if (+orsak.value == FranvaroOrsaksKodEnum.Sjuk) { + orsak.index = 1; + } + if (+orsak.value == FranvaroOrsaksKodEnum.Arbete) { + orsak.index = 3; + } + if (+orsak.value == FranvaroOrsaksKodEnum.OkandOrsak) { + orsak.index = 6; + } + if (+orsak.value == FranvaroOrsaksKodEnum.AnnanKandOrsak) { + orsak.index = 5; + } + if (+orsak.value == FranvaroOrsaksKodEnum.VAB) { + orsak.index = 2; + } + if (+orsak.value == FranvaroOrsaksKodEnum.Utbildning) { + orsak.index = 4; + } + }) + + const sortedOrsaksKoder = orsaksKoder.sort((kodA, kodB) => kodA.index < kodB.index ? -1 : 1); + return sortedOrsaksKoder; + } + +} + diff --git a/apps/mina-sidor-fa/src/app/shared/enums/alternative.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/alternative.enum.ts new file mode 100644 index 0000000..4fcfa8a --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/enums/alternative.enum.ts @@ -0,0 +1,4 @@ +export enum Alternative { + FRANVARO = 'franvaro', + AVVIKELSE = 'avvikelse' +} diff --git a/apps/mina-sidor-fa/src/app/shared/enums/avvikelse-orsak-kod.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/avvikelse-orsak-kod.enum.ts new file mode 100644 index 0000000..5ead09a --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/enums/avvikelse-orsak-kod.enum.ts @@ -0,0 +1,7 @@ +export enum AvvikelseOrsaksKodEnum { + TackatNejTillInsatsEllerAktivitet = 19, + TackatNejTillErbjudetArbete = 20, + KanInteTillGodoGoraSigProgrammet = 21, + MisskottSigEllerStortVerksamheten = 22, + SerTillAttErbjudetArbeteInteKommerTillStand = 28 +} diff --git a/apps/mina-sidor-fa/src/app/shared/enums/day-or-part-of-day.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/day-or-part-of-day.enum.ts new file mode 100644 index 0000000..75b6126 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/enums/day-or-part-of-day.enum.ts @@ -0,0 +1,4 @@ +export enum DayOrPartOfDay { + HELDAG = 'HELDAG', + DEL_AV_DAG = 'DEL_AV_DAG' +} diff --git a/apps/mina-sidor-fa/src/app/shared/enums/franvaro-orsak-kod.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/franvaro-orsak-kod.enum.ts new file mode 100644 index 0000000..052b43a --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/enums/franvaro-orsak-kod.enum.ts @@ -0,0 +1,8 @@ +export enum FranvaroOrsaksKodEnum { + Sjuk = 15, + Arbete = 16, + OkandOrsak = 17, + AnnanKandOrsak = 18, + VAB = 26, + Utbildning = 27, +} diff --git a/apps/mina-sidor-fa/src/app/shared/enums/kanda-orsaker-kod.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/kanda-orsaker-kod.enum.ts new file mode 100644 index 0000000..95849dd --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/enums/kanda-orsaker-kod.enum.ts @@ -0,0 +1,7 @@ +export enum KandaOrsakerEnum { + LakarbesokTandlakarbesok = 1, + Familjeangelagenhet = 2, + MoteMedMyndighet = 3, + Anstallningsintervju = 4, + AnnanOrsak = 5 +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/contact-information.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/contact-information.response.model.ts index 6ca776f..203f1df 100644 --- a/apps/mina-sidor-fa/src/app/shared/models/api/contact-information.response.model.ts +++ b/apps/mina-sidor-fa/src/app/shared/models/api/contact-information.response.model.ts @@ -9,3 +9,19 @@ export interface ContactInformationResponse { telekomadresser: PhoneNumberResponse[]; adresser: AddressResponse[]; } + +export interface ContactInformationCompact { + fornamn: string; + efternamn: string; + personnummer: string; +} + +export function mapResponseToContactInformationCompact(data: ContactInformationResponse): ContactInformationCompact { + const { fornamn, efternamn, personnummer } = data; + + return { + fornamn, + efternamn, + personnummer + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/fragor-for-avvikelser.response.ts b/apps/mina-sidor-fa/src/app/shared/models/api/fragor-for-avvikelser.response.ts new file mode 100644 index 0000000..319b36b --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/fragor-for-avvikelser.response.ts @@ -0,0 +1,4 @@ +export interface FragorForAvvikelserResponse { + id: string; + name: string; +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/kanda-avvikelse-koder.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/kanda-avvikelse-koder.response.model.ts new file mode 100644 index 0000000..7be3b17 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/kanda-avvikelse-koder.response.model.ts @@ -0,0 +1,4 @@ +export interface KandaAvvikelseKoderResponse { + id: number; + name: string; +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/orsak.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/orsak.response.model.ts new file mode 100644 index 0000000..0634cc7 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/orsak.response.model.ts @@ -0,0 +1,5 @@ + +export interface OrsakResponse { + name: string; + id: number; +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-avvikelse.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-avvikelse.response.model.ts new file mode 100644 index 0000000..ad152e9 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-avvikelse.response.model.ts @@ -0,0 +1,5 @@ +export interface OrsaksKoderAvvikelseResponse { + id: number; + name: string; + state: number +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-franvaro.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-franvaro.response.model.ts new file mode 100644 index 0000000..d0d6f2c --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/api/orsaks-koder-franvaro.response.model.ts @@ -0,0 +1,5 @@ +export interface OrsaksKoderFranvaroResponse { + id: number; + name: string; + state: number; +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/arbetssokande.model.ts b/apps/mina-sidor-fa/src/app/shared/models/arbetssokande.model.ts new file mode 100644 index 0000000..fe5fe23 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/arbetssokande.model.ts @@ -0,0 +1,5 @@ +export interface Arbetssokande { + personnummer: string, + fornamn: string, + efternamn: string +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/avvikelse-alternativ.model.ts b/apps/mina-sidor-fa/src/app/shared/models/avvikelse-alternativ.model.ts new file mode 100644 index 0000000..87c422f --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/avvikelse-alternativ.model.ts @@ -0,0 +1,7 @@ +import { Fraga } from './fraga.model'; + +export interface AvvikelseAlternativ { + avvikelseorsakskod: string, + frageformular: Array, + rapporteringsdatum: string +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/avvikelse.model.ts b/apps/mina-sidor-fa/src/app/shared/models/avvikelse.model.ts new file mode 100644 index 0000000..7035335 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/avvikelse.model.ts @@ -0,0 +1,36 @@ +import { Arbetssokande } from './arbetssokande.model'; +import { AvvikelseAlternativ } from './avvikelse-alternativ.model'; +import { FranvaroAlternativ } from './franvaro-alternativ.model'; + +export interface Avvikelse { + datum_for_rapportering: string, + arbetssokande: Arbetssokande, + sokandeId: number; + avvikelsealternativ?: AvvikelseAlternativ, + franvaro?: FranvaroAlternativ +} + +export interface AvvikelseRequestData { + datum_for_rapportering: string, + arbetssokande: Arbetssokande, + sokandeId: number; + avvikelsealternativ: AvvikelseAlternativ, +} + +export interface FranvaroRequestData { + datum_for_rapportering: string, + arbetssokande: Arbetssokande, + sokandeId: number; + franvaro: FranvaroAlternativ +} + +export function mapAvvikelseRequestDataToAvvikelse(data: AvvikelseRequestData): Avvikelse { + const { datum_for_rapportering, arbetssokande, sokandeId, avvikelsealternativ } = data; + + return { + datum_for_rapportering, + arbetssokande, + sokandeId, + avvikelsealternativ + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/fraga.model.ts b/apps/mina-sidor-fa/src/app/shared/models/fraga.model.ts new file mode 100644 index 0000000..2f19a81 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/fraga.model.ts @@ -0,0 +1,4 @@ +export interface Fraga { + fraga: string, + svar: string +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/fragor-for-avvikelser.model.ts b/apps/mina-sidor-fa/src/app/shared/models/fragor-for-avvikelser.model.ts new file mode 100644 index 0000000..f3ddf33 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/fragor-for-avvikelser.model.ts @@ -0,0 +1,16 @@ +import { FragorForAvvikelserResponse } from './api/fragor-for-avvikelser.response'; + +export interface FragorForAvvikelser { + name: string; + id: string; + descriptionIsRequired?: boolean +} + +export function mapResponseToFragorForAvvikelser(data: FragorForAvvikelserResponse): FragorForAvvikelser { + const { name, id } = data; + + return { + name: name, + id: id + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/franvaro-alternativ.model.ts b/apps/mina-sidor-fa/src/app/shared/models/franvaro-alternativ.model.ts new file mode 100644 index 0000000..7410e0b --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/franvaro-alternativ.model.ts @@ -0,0 +1,15 @@ +export interface FranvaroAlternativ { + avvikelseorsakskod: string; + datum: string; + heldag: boolean; + starttid: string; + sluttid: string; + forvantad_narvaro: { + starttid: string; + sluttid: string; + }, + alternativ_for_kanda_orsaker: { + typ: string, + motivering: string; + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-avvikelse.model.ts b/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-avvikelse.model.ts new file mode 100644 index 0000000..4758209 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-avvikelse.model.ts @@ -0,0 +1,18 @@ +import { AvvikelseOrsaksKodEnum } from '@msfa-enums/avvikelse-orsak-kod.enum'; +import { OrsaksKoderAvvikelseResponse } from './api/orsaks-koder-avvikelse.response.model'; + +export interface OrsaksKoderAvvikelse { + name: string; + value: AvvikelseOrsaksKodEnum; + state: number; +} + +export function mapResponseToOrsaksKoderAvvikelse(data: OrsaksKoderAvvikelseResponse): OrsaksKoderAvvikelse { + const { name, id, state } = data; + + return { + name, + value: id, + state + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-franvaro.model.ts b/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-franvaro.model.ts new file mode 100644 index 0000000..45700c9 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/models/orsaks-koder-franvaro.model.ts @@ -0,0 +1,35 @@ +import { AvvikelseOrsaksKodEnum } from '@msfa-enums/avvikelse-orsak-kod.enum'; +import { KandaOrsakerEnum } from '@msfa-enums/kanda-orsaker-kod.enum'; +import { KandaAvvikelseKoderResponse } from './api/kanda-avvikelse-koder.response.model'; +import { OrsaksKoderFranvaroResponse } from './api/orsaks-koder-franvaro.response.model'; + +export interface OrsaksKoderFranvaro { + name: string; + value: AvvikelseOrsaksKodEnum | KandaOrsakerEnum; + state: number; + index?: number; +} + +export interface KandaAvvikelseKoder { + name: string; + value: number; +} + +export function mapResponseToOrsaksKoderFranvaro(data: OrsaksKoderFranvaroResponse): OrsaksKoderFranvaro { + const { name, id, state } = data; + + return { + name, + value: id, + state + } +} + +export function mapResponseToAndraKandaOrsaker(data: KandaAvvikelseKoderResponse): KandaAvvikelseKoder { + const { name, id } = data; + + return { + name, + value: id + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts new file mode 100644 index 0000000..bdef38f --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts @@ -0,0 +1,109 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Alternative } from '@msfa-enums/alternative.enum'; +import { ErrorType } from '@msfa-enums/error-type.enum'; +import { environment } from '@msfa-environment'; + +import { + ContactInformationCompact, + ContactInformationResponse, + mapResponseToContactInformationCompact +} from '@msfa-models/api/contact-information.response.model'; +import { FragorForAvvikelserResponse } from '@msfa-models/api/fragor-for-avvikelser.response'; +import { KandaAvvikelseKoderResponse } from '@msfa-models/api/kanda-avvikelse-koder.response.model'; +import { OrsaksKoderAvvikelseResponse } from '@msfa-models/api/orsaks-koder-avvikelse.response.model'; +import { Avvikelse, AvvikelseRequestData, mapAvvikelseRequestDataToAvvikelse } from '@msfa-models/avvikelse.model'; +import { errorToCustomError } from '@msfa-models/error/custom-error'; +import { FragorForAvvikelser, mapResponseToFragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model'; +import { mapResponseToOrsaksKoderAvvikelse, OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model'; +import { + KandaAvvikelseKoder, + mapResponseToAndraKandaOrsaker, + mapResponseToOrsaksKoderFranvaro, + OrsaksKoderFranvaro +} from '@msfa-models/orsaks-koder-franvaro.model'; +import { ErrorService } from '@msfa-services/error.service'; +import { Observable, of, throwError } from 'rxjs'; +import { catchError, filter, map, take } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) + +export class AvvikelseApiService { + + private _apiBaseUrl = `${environment.api.url}`; + + public getOrsaksKoderFranvaro$(): Observable { + return this.httpClient.get<{ data: OrsaksKoderAvvikelseResponse[] }>(`${this._apiBaseUrl}/orsakskoderfranvaro`).pipe( + filter(response => !!response?.data), + map(({ data }) => data.map(orsak => mapResponseToOrsaksKoderFranvaro(orsak))) + ) + } + + public getOrsaksKoderAvvikelse$(): Observable { + return this.httpClient.get<{ data: OrsaksKoderAvvikelseResponse[] }>(`${this._apiBaseUrl}/orsakskoderavvikelse`).pipe( + filter(response => !!response?.data), + map(({ data }) => data.map(avvikelse => mapResponseToOrsaksKoderAvvikelse(avvikelse))) + ) + } + + public getAndraKandaOrsaker$(): Observable { + return this.httpClient.get<{ data: KandaAvvikelseKoderResponse[] }>(`${this._apiBaseUrl}/kandaavvikelsekoder`).pipe( + filter(response => !!response?.data), + map(({ data }) => data.map(annanKandOrsak => mapResponseToAndraKandaOrsaker(annanKandOrsak))) + ) + } + + public getFragorForAvvikelser$(): Observable { + return this.httpClient.get<{ data: FragorForAvvikelserResponse[] }>(`${this._apiBaseUrl}/fragorforavvikelser`).pipe( + filter(response => !!response?.data), + map(({ data }) => data.map(fraga => mapResponseToFragorForAvvikelser(fraga))) + ) + } + + public getContactInformation$(id: number): Observable { + return this.httpClient.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/deltagare/${id}/contact`).pipe( + filter(response => !!response?.data), + map(({ data }) => mapResponseToContactInformationCompact(data), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ) + ) + } + + public createAvvikelse$(avvikelse: Avvikelse, alternative: string): Observable { + + return this.httpClient + .post<{ data: AvvikelseRequestData }>(`${this._apiBaseUrl}/${this.setEndPoint(alternative)}`, avvikelse).pipe( + filter(response => !!response?.data), + take(1), + map(({ data }) => mapAvvikelseRequestDataToAvvikelse(data)), + catchError(error => throwError({ message: error as string, type: ErrorType.API })) + ) + } + + private setEndPoint(alternative: string): string { + let endpoint = ''; + + switch (alternative) { + case Alternative.AVVIKELSE: + endpoint = 'avvikelse'; + break; + case Alternative.FRANVARO: + endpoint = 'franvaro'; + break + default: + break; + } + return endpoint; + } + + constructor(private httpClient: HttpClient, private errorService: ErrorService) { } +} + + + + diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts new file mode 100644 index 0000000..4c15092 --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts @@ -0,0 +1,267 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive'; +import { SortOrder } from '@msfa-enums/sort-order.enum'; +import { environment } from '@msfa-environment'; +import { AvropResponse } from '@msfa-models/api/avrop.response.model'; +import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model'; +import { DeltagareCompactApiResponse } from '@msfa-models/api/deltagare.response.model'; +import { DisabilityResponse } from '@msfa-models/api/disability.response.model'; +import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model'; +import { EducationsResponse } from '@msfa-models/api/educations.response.model'; +import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model'; +import { Params } from '@msfa-models/api/params.model'; +import { TranslatorResponse } from '@msfa-models/api/translator.response.model'; +import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model'; +import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model'; +import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model'; +import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model'; +import { + Deltagare, + DeltagareCompact, + DeltagareCompactData, + mapResponseToDeltagareCompact +} from '@msfa-models/deltagare.model'; +import { Disability, mapResponseToDisability } from '@msfa-models/disability.model'; +import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model'; +import { Education, mapResponseToEducation } from '@msfa-models/education.model'; +import { errorToCustomError } from '@msfa-models/error/custom-error'; +import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model'; +import { Sort } from '@msfa-models/sort.model'; +import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model'; +import { ErrorService } from '@msfa-services/error.service'; +import { sortFromToDates } from '@msfa-utils/sort.util'; +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; +import { catchError, filter, map, switchMap } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root', +}) +export class DeltagareApiService extends UnsubscribeDirective { + private _apiBaseUrl = `${environment.api.url}/deltagare`; + private _currentDeltagareId$ = new BehaviorSubject(null); + private _limit$ = new BehaviorSubject(20); + private _page$ = new BehaviorSubject(1); + private _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC }); + public sort$: Observable> = this._sort$.asObservable(); + + constructor(private httpClient: HttpClient, private errorService: ErrorService) { + super(); + super.unsubscribeOnDestroy( + this._currentDeltagareId$ + .pipe( + filter(currentDeltagareId => !!currentDeltagareId), + switchMap(currentDeltagareId => this._fetchDeltagare$(currentDeltagareId)) + ) + .subscribe(deltagare => { + this._deltagare$.next(deltagare); + }) + ); + } + + private _deltagare$ = new BehaviorSubject(null); + public deltagare$: Observable = this._deltagare$.asObservable(); + + public allDeltagareData$: Observable = combineLatest([ + this._limit$, + this._page$, + this._sort$, + ]).pipe(switchMap(([limit, page, sort]) => this._fetchAllDeltagare$(limit, page, sort))); + + public setSort(newSortKey: keyof DeltagareCompact): void { + const currentSort = this._sort$.getValue(); + const order = + currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; + + this._sort$.next({ key: newSortKey, order }); + } + + public setPage(page: number): void { + this._page$.next(page); + } + + private _fetchAllDeltagare$( + limit: number, + page: number, + sort: Sort + ): Observable { + const params: Params = { + sort: sort.key as string, + order: sort.order as string, + limit: limit.toString(), + page: page.toString(), + }; + + return this.httpClient + .get(this._apiBaseUrl, { + params, + }) + .pipe( + map(({ data, meta }) => { + return { data: data.map(deltagare => mapResponseToDeltagareCompact(deltagare)), meta }; + }) + ); + } + + public setCurrentDeltagareId(currentDeltagareId: string): void { + this._deltagare$.next(null); + this._currentDeltagareId$.next(currentDeltagareId); + } + + private _fetchContactInformation$(id: string): Observable> { + return this.httpClient.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${id}/contact`).pipe( + map(({ data }) => mapResponseToContactInformation(data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); + } + + private _fetchDriversLicense$(id: string): Observable> { + return this.httpClient.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${id}/driverlicense`).pipe( + map(({ data }) => mapResponseToDriversLicense(data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); + } + + private _fetchHighestEducation$(id: string): Observable> { + return this.httpClient + .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${id}/educationlevels/highest`) + .pipe( + map(({ data }) => mapResponseToHighestEducation(data)), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); + } + + private _fetchEducations$(id: string): Observable { + return this.httpClient.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${id}/educations`).pipe( + map(({ data }) => + data.utbildningar + ? data.utbildningar.sort((a, b) => + sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom }) + ) + : [] + ), + map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ); + } + + private _fetchTranslator$(id: string): Observable { + return this.httpClient.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${id}/translator`).pipe( + map(({ data }) => data.sprak?.beskrivning || null), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of(''); + }) + ); + } + + private _fetchWorkLanguages$(id: string): Observable { + return this.httpClient.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${id}/work/languages`).pipe( + map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ); + } + + private _fetchDisabilities$(id: string): Observable { + return this.httpClient.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${id}/work/disabilities`).pipe( + map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ); + } + + private _fetchWorkExperiences$(id: string): Observable { + return this.httpClient.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${id}/work/experiences`).pipe( + map( + ({ data }) => + data?.arbetslivserfarenheter?.sort((a, b) => + sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom }) + ) || [] + ), + map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of([]); + }) + ); + } + + private _fetchAvropInformation$(id: string): Observable> { + return this.httpClient.get<{ data: AvropResponse }>(`${this._apiBaseUrl}/${id}/avrop`).pipe( + map(({ data }) => (data ? mapAvropResponseToAvrop(data) : {})), + catchError(error => { + this.errorService.add(errorToCustomError(error)); + return of({}); + }) + ); + } + + // As TypeScript has some limitations regarding combining Observables this way, + // we need to type it manually when exceeding 6 Observables inside a combineLatest. + // Read: https://github.com/ReactiveX/rxjs/issues/3601#issuecomment-384711601 + private _fetchDeltagare$(id: string): Observable { + return combineLatest([ + this._fetchContactInformation$(id), + this._fetchDriversLicense$(id), + this._fetchHighestEducation$(id), + this._fetchEducations$(id), + this._fetchTranslator$(id), + this._fetchWorkLanguages$(id), + this._fetchDisabilities$(id), + this._fetchWorkExperiences$(id), + this._fetchAvropInformation$(id), + ]).pipe( + map( + ([ + contactInformation, + driversLicense, + highestEducation, + educations, + translator, + workLanguages, + disabilities, + workExperiences, + avropInformation, + ]: [ + ContactInformation, + DriversLicense, + HighestEducation, + Education[], + string, + string[], + Disability[], + WorkExperience[], + Avrop + ]) => ({ + id, + ...contactInformation, + driversLicense, + highestEducation, + educations, + translator, + workLanguages, + disabilities, + workExperiences, + avropInformation, + }) + ) + ); + } +} diff --git a/apps/mina-sidor-fa/src/app/shared/utils/validators/avvikelse-form-validator.ts b/apps/mina-sidor-fa/src/app/shared/utils/validators/avvikelse-form-validator.ts new file mode 100644 index 0000000..4d2baca --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/utils/validators/avvikelse-form-validator.ts @@ -0,0 +1,115 @@ +import { AbstractControl, ValidatorFn } from '@angular/forms'; +import { Alternative } from '@msfa-enums/alternative.enum'; +import { DayOrPartOfDay } from '@msfa-enums/day-or-part-of-day.enum'; +import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum'; +import { KandaOrsakerEnum } from '@msfa-enums/kanda-orsaker-kod.enum'; + +export class DescriptionIsRequiredCheck { + static CheckIfRequired( + controlToValidateName: string, + nestedFormGroupName: string, + nestedFormGroupControlName: string, + valueForWhichTheControlShouldBeRequired: KandaOrsakerEnum + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfControlToValidate = fg?.get(controlToValidateName)?.value as string; + const valueOfNestedFormControl = fg?.get(nestedFormGroupName)?.get(nestedFormGroupControlName)?.value as string; + const isRequired = +valueOfNestedFormControl === valueForWhichTheControlShouldBeRequired && valueOfControlToValidate === ''; + + return isRequired ? { descriptionIsRequired: true } : null; + }; + } +} + +export class DayOrPartOfDayIsRequiredCheck { + static CheckIfRequired( + controlToValidateName: string, + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfControlToValidate = fg?.get(controlToValidateName)?.value as string; + const isRequired = valueOfControlToValidate === null && fg?.get('alternative').value === Alternative.FRANVARO; + + return isRequired ? { dayOrPartOfDayIsRequired: true } : null; + }; + } +} + +export class OrsakerIsRequiredCheck { + static CheckIfRequired( + nestedFormGroupName: string, + controlToValidateName: string, + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfNestedFormControl = fg?.get(nestedFormGroupName)?.get(controlToValidateName)?.value as string; + const isRequired = valueOfNestedFormControl === null && fg?.get(nestedFormGroupName)?.get('andraKandaOrsaker')?.value === null; + + return isRequired ? { orsakerIsRequired: true } : null; + }; + } +} + +export class AnnanKandOrsakeIsRequiredCheck { + static CheckIfRequired( + nestedFormGroupName: string, + controlToValidateName: string, + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfNestedFormControl = fg?.get(nestedFormGroupName)?.get(controlToValidateName)?.value as string; + const isRequired = +fg?.get(nestedFormGroupName).get('orsaker').value === FranvaroOrsaksKodEnum.AnnanKandOrsak && + valueOfNestedFormControl === null; + + return isRequired ? { annanKandorsakIsRequired: true } : null; + }; + } +} + +export class StartTimeIsRequiredCheck { + static CheckIfRequired( + nestedFormGroupName: string, + startTimeControlToValidateName: string, + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfStartTimeFormControl = fg?.get(nestedFormGroupName)?.get(startTimeControlToValidateName)?.value as string; + const isRequired = fg?.get('alternative')?.value as string === Alternative.FRANVARO && + fg?.get('dayOrPartOfDay').value === DayOrPartOfDay.DEL_AV_DAG && !valueOfStartTimeFormControl; + + return isRequired ? { startTimeIsRequired: true } : null; + } + } +} + +export class EndTimeIsRequiredCheck { + static CheckIfRequired( + nestedFormGroupName: string, + endTimeControlToValidateName: string + ): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfEndTimeFormControl = fg?.get(nestedFormGroupName)?.get(endTimeControlToValidateName)?.value as string; + const isRequired = fg?.get('alternative').value as string === Alternative.FRANVARO && + fg?.get('dayOrPartOfDay').value === DayOrPartOfDay.DEL_AV_DAG && !valueOfEndTimeFormControl; + + return isRequired ? { endTimeIsRequired: true } : null; + } + } +} + +export class MotiveringIsRequiredCheck { + static CheckIfRequired(controlToValidateName: string): ValidatorFn { + return (fg: AbstractControl): { [key: string]: boolean } => { + const valueOfControlToValidate = fg?.get('fragorFormGroup').get(controlToValidateName)?.value as string; + const isRequired = fg?.get('alternative').value as string === Alternative.AVVIKELSE && valueOfControlToValidate === ''; + + return isRequired ? { motiveringIsRequired: true } : null; + }; + } +} +export class DateIsRequiredCheck { + static CheckIfRequired(controlToValidateName: string): ValidatorFn { + return (fg: AbstractControl): { [key: string]: string } => { + const valueOfControlToValidate = fg?.get(controlToValidateName).value as string; + const isRequired = valueOfControlToValidate === ''; + + return isRequired ? { dateIsRequired: 'Datum är obligatoriskt' } : null; + }; + } +} diff --git a/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/andra-kanda-orsaker.js b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/andra-kanda-orsaker.js new file mode 100644 index 0000000..0fced1f --- /dev/null +++ b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/andra-kanda-orsaker.js @@ -0,0 +1,17 @@ +function generateAndraKandaOrsaker() { + const andraKandaOrsaker = [ + { name: 'Läkarbesök/Tandläkarbesök', id: 1 }, + { name: 'Familjeangelägenhet', id: 2 }, + { name: 'Möte med myndighet', id: 3 }, + { name: 'Anställningsintervju', id: 4 }, + { name: 'Annan orsak', id: 5 }, + ]; + + console.info('AndraKandaOrsaker generated...'); + + return andraKandaOrsaker; +} + +export default { + generate: generateAndraKandaOrsaker, +}; diff --git a/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/avvikelse-orsaker.js b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/avvikelse-orsaker.js new file mode 100644 index 0000000..2715124 --- /dev/null +++ b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/avvikelse-orsaker.js @@ -0,0 +1,17 @@ +function generateAvvikelseOrsaker() { + const avvikelseOrsaker = [ + { name: 'Tackat nej till insats eller aktivitet', id: 19 }, + { name: 'Tackat nej till erbjudet arbete', id: 20 }, + { name: 'Misskött sig eller stört verksamheten', id: 22 }, + { name: 'Kan inte tillgodogöra sig programmet', id: 21 }, + { name: 'Ser till att erbjudet arbete inte kommer till stånd', id: 28 }, + ]; + + console.info('AvvikelseOrsaker generated...'); + + return avvikelseOrsaker; +} + +export default { + generate: generateAvvikelseOrsaker, +}; diff --git a/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/fraga-for-avvikelse-orsak.js b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/fraga-for-avvikelse-orsak.js new file mode 100644 index 0000000..57f3d44 --- /dev/null +++ b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/fraga-for-avvikelse-orsak.js @@ -0,0 +1,57 @@ +function generateFragorForAvvikelser() { + const fragaForAvvikelseOrsak = [ + { + // AvvikelseOrsaksKodEnum.TackatNejTillInsatsEllerAktivitet + name: 'Vilken insats eller aktivitet har deltagaren tackat nej till? Obligatoriskt', + id: '19.1', + }, + { + // AvvikelseOrsaksKodEnum.TackatNejTillInsatsEllerAktivitet + name: 'Motivering som deltagaren angett', + id: '19.2', + }, + { + // AvvikelseOrsaksKodEnum.TackatNejTillErbjudetArbete + name: 'Vilket arbete har deltagaren tackat nej till? Obligatoriskt', + id: '20.1', + }, + { + // AvvikelseOrsaksKodEnum.TackatNejTillErbjudetArbete + name: 'Motivering som deltagaren angett', + id: '20.2', + }, + { + // AvvikelseOrsaksKodEnum.KanInteTillGodoGoraSigProgrammet + name: 'På vilket sätt kan deltagaren inte tillgodogöra sig programmet? Obigatoriskt', + id: '21.1', + }, + { + // AvvikelseOrsaksKodEnum.KanInteTillGodoGoraSigProgrammet + name: 'Vad har ni gjort för att stötta deltagaren/anpassa programmet?', + id: '21.2', + }, + { + // AvvikelseOrsaksKodEnum.MisskottSigEllerStortVerksamheten + name: 'På vilket sätt har deltagaren misskött sig eller stört verksamheten? Obligatoriskt', + id: '22.1', + }, + { + // AvvikelseOrsaksKodEnum.MisskottSigEllerStortVerksamheten + name: 'Hur har ni försökt lösa problemet? Obligatoriskt', + id: '22.2', + }, + { + // AvvikelseOrsaksKodEnumSerTillAttErbjudetArbeteInteKommerTillStand + name: 'På vilket sätt har deltagaren agerat för att inte få ett arbete?', + id: '28.1', + } + ]; + + console.info('FragaForAvvikelseOrsak generated...'); + + return fragaForAvvikelseOrsak; +} + +export default { + generate: generateFragorForAvvikelser, +}; diff --git a/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/franvaro-orsaker.js b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/franvaro-orsaker.js new file mode 100644 index 0000000..49acc6b --- /dev/null +++ b/mock-api/mina-sidor-fa/scripts/avvikelse-alternative.js/franvaro-orsaker.js @@ -0,0 +1,18 @@ +function generateFranvaroOrsaker() { + const franvaroOrsaker = [ + { name: 'Sjuk', id: 15 }, + { name: 'VAB', id: 23 }, + { name: 'Arbete', id: 16 }, + { name: 'Utbildning', id: 24 }, + { name: 'Annan känd orsak', id: 18 }, + { name: 'Okänd orsak', id: 17 }, + ]; + + console.info('FranvaroOrsaker generated...'); + + return franvaroOrsaker; +} + +export default { + generate: generateFranvaroOrsaker, +}; diff --git a/mock-api/mina-sidor-fa/scripts/generate-api.js b/mock-api/mina-sidor-fa/scripts/generate-api.js index 77d2d24..5f60950 100644 --- a/mock-api/mina-sidor-fa/scripts/generate-api.js +++ b/mock-api/mina-sidor-fa/scripts/generate-api.js @@ -1,6 +1,10 @@ import fs from 'fs'; import { authTokens } from './auth-tokens.js'; import avrop from './avrop.js'; +import franvaroOrsaker from './avvikelse-alternative.js/franvaro-orsaker.js'; +import avvikelseOrsaker from './avvikelse-alternative.js/avvikelse-orsaker.js'; +import andraKandaOrsaker from './avvikelse-alternative.js/andra-kanda-orsaker.js'; +import fragaForAvvikelseOrsak from './avvikelse-alternative.js/fraga-for-avvikelse-orsak.js'; import currentUser from './current-user.js'; import userinfo from './userinfo.js'; import deltagare from './deltagare.js'; @@ -30,6 +34,10 @@ const generatedUserinfo = { const avropTjanster = []; const utforandeVerksamheter = []; const avropKommuner = []; +const generatedFranvaroOrsaker = franvaroOrsaker.generate(); +const generateAvvikelseOrsaker = avvikelseOrsaker.generate(); +const generateAndraKandaOrsaker = andraKandaOrsaker.generate(); +const generatefragaForAvvikelseOrsak = fragaForAvvikelseOrsak.generate(); generatedAvrop.forEach(({ tjanstekod, tjansteNamn, utforandeVerksamhetId, utforandeverksamhet, kommunKod, kommun }) => { const tjanstExists = avropTjanster.find(tjanst => tjanst.id === tjanstekod); @@ -113,6 +121,10 @@ const apiData = { userinfo: generatedUserinfo, getTokenFullAccess: authTokens.auth_code_from_CIAM_with_all_permissions, invites: [], + franvaroOrsaker: generatedFranvaroOrsaker, + avvikelseOrsaker: generateAvvikelseOrsaker, + andraKandaOrsaker: generateAndraKandaOrsaker, + fragaForAvvikelseOrsaker: generatefragaForAvvikelseOrsak, minaDeltagare: chooseRandom(generatedAvrop, 3), }; diff --git a/mock-api/mina-sidor-fa/server.js b/mock-api/mina-sidor-fa/server.js index 012bf0d..39873bd 100644 --- a/mock-api/mina-sidor-fa/server.js +++ b/mock-api/mina-sidor-fa/server.js @@ -47,6 +47,10 @@ server.use( '/deltagare?*': '/avrop?$1', '/deltagare/:sokandeId/avrop': '/avrop?sokandeId=:sokandeId', '/deltagare/:sokandeId/*': '/deltagare/:sokandeId', + '/franvaro/franvaroOrsaker': '/franvaroOrsaker', + '/avvikelse/avvikelseOrsaker': '/avvikelseOrsaker', + '/andraKandaOrsaker': '/andraKandaOrsaker', + '/fragaForAvvikelseOrsaker': '/fragaForAvvikelseOrsaker', '*page=*': '$1_page=$2', '*limit=*': '$1_limit=$2', '*sort=*': '$1_sort=$2',