feat(report): Added reporting for frånvaro and avvikelser. (TV-731)
Squashed commit of the following:
commit 3df2d57cc2afdd3a64bea03b7d6e1e6520b18a0c
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 14:41:39 2021 +0200
wip
commit ce4acffd4d4919a0dd38d83226ba6917ee9ecf32
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 14:33:58 2021 +0200
recievedTimestamp
commit a8aa0494c39d9e0218bdd3edefa6f6c063d60189
Merge: 0bbe98e2 6c6e37ed
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 14:31:34 2021 +0200
Merge pull request #188 in TEA/mina-sidor-fa-web from feature/TV-731-new-avvikelserapport to feature/TV-731-refaktorisera-avvikelserapport-split-rapporter
* commit '6c6e37edeed74b6a8ea6dec24f68765027c3b50b':
Avvikelserapport (avvikelse)
Delete avvikelse-orsak-kod.enum.ts
Update deltagare-avvikelserapport.component.html
cleanup and rearrange
unsubscribe, add types and error handling
Delete avvikelse-form-validator.ts
remove uneccessary component
fix validation
form and dialog done
wip
wip
make fragor dynamic with formarray
form arrays working
first
commit 6c6e37edeed74b6a8ea6dec24f68765027c3b50b
Merge: a621fc7f 0bbe98e2
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 14:00:47 2021 +0200
Merge branch 'feature/TV-731-refaktorisera-avvikelserapport-split-rapporter' into feature/TV-731-new-avvikelserapport
# Conflicts:
# apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/deltagare-details.module.ts
# apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.html
# apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/deltagare-reports/components/report-layout/report-layout.component.html
# apps/mina-sidor-fa/src/app/shared/constants/navigation.ts
# apps/mina-sidor-fa/src/app/shared/enums/report-type.enum.ts
commit a621fc7f2425699b6f8720d9cc53776174b65d42
Merge: 3a34d434 a1b81ba3
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 13:55:45 2021 +0200
Merge branch 'feature/TV-731-refaktorisera-avvikelserapport-split-rapporter' into feature/TV-731-new-avvikelserapport
commit 0bbe98e2165ec412d61f1314a1021191c3b77042
Merge: a1b81ba3 d2041c10
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 13:55:16 2021 +0200
Merge pull request #189 in TEA/mina-sidor-fa-web from feature/TV-731-franvaro-erik to feature/TV-731-refaktorisera-avvikelserapport-split-rapporter
* commit 'd2041c10fe02a6c197571149093f734f3bf026f9':
Updated constant
Minor change after PR
Minor changes after PR
Minor changes after demo
Removed console.log
Implemented confirm dialog
Fixed breadcrumbs
Removed steps and fixed error message
Added more validation and styling
Added validation
Started validation
possible to post
WIP
implemented some form elements
Added frånvaro-report component
commit d2041c10fe02a6c197571149093f734f3bf026f9
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 13:53:09 2021 +0200
Updated constant
commit 59820cef664c467d8a85fcfde79de7f1930cd9ed
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 13:52:00 2021 +0200
Minor change after PR
commit b45d975135d1de351661f42d6cb90fd9cd5e9aa3
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 13:43:13 2021 +0200
Minor changes after PR
commit a1b81ba3a7318c5ad7183194e1959058fbc151df
Merge: dad332c3 132aba21
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 13:39:03 2021 +0200
Merge branch 'develop' into feature/TV-731-refaktorisera-avvikelserapport-split-rapporter
commit 3a34d434c0504cb9e4317e9fa5bfa786750d7ae4
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 13:38:49 2021 +0200
Avvikelserapport (avvikelse)
commit b122196d3edf32b8535f01a2bf8159a032409b8f
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 13:16:05 2021 +0200
Minor changes after demo
commit 582e5f4b73fab2bcb86096cdda74c58b9320b3b2
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 13:04:49 2021 +0200
Delete avvikelse-orsak-kod.enum.ts
commit 70a20b7232289669915ae452a4c06db2a20f4f5a
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 12:17:11 2021 +0200
Removed console.log
commit d63e20f087efdf664f05f90b27ff52ad1d912561
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date: Fri Oct 8 12:15:51 2021 +0200
Implemented confirm dialog
commit ccbb7709451e6af43bb5463bc5847bda9d5fe097
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 12:04:47 2021 +0200
Update deltagare-avvikelserapport.component.html
commit 9bb05d9e8d66a412e1435ff8fb6b9f14ae442f2c
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 12:04:23 2021 +0200
cleanup and rearrange
commit c8b7496bdc3258efa7169bafd17284899baba862
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 12:00:55 2021 +0200
unsubscribe, add types and error handling
commit bfedf8c5c1d282cd049aef1ae0775aaa2a1bf74f
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 11:52:36 2021 +0200
Delete avvikelse-form-validator.ts
commit 9f060e2f1271822a3f18be58d6f6ad4faa6c5ecc
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date: Fri Oct 8 11:51:11 2021 +0200
remove uneccessary component
... and 29 more commits
This commit is contained in:
@@ -9,11 +9,17 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'avvikelserapport',
|
||||
data: { title: 'Skapa avvikelserapport' },
|
||||
loadChildren: () =>
|
||||
import('./pages/deltagare-reports/deltagare-avvikelserapport/deltagare-avvikelserapport.module').then(
|
||||
m => m.DeltagareAvvikelserapportModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'franvarorapport',
|
||||
data: { title: 'Skapa rapport' },
|
||||
loadChildren: () =>
|
||||
import('./pages/deltagare-reports/deltagare-avvikelse/deltagare-avvikelse.module').then(
|
||||
m => m.DeltagareAvvikelseModule
|
||||
),
|
||||
import('./pages/deltagare-reports/franvaro-report/franvaro-report.module').then(m => m.FranvaroReportModule),
|
||||
},
|
||||
{
|
||||
path: 'gemensam-planering',
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
<div class="deltagare-tab-reports">
|
||||
<form [formGroup]="reportPickerFormGroup" class="deltagare-tab-reports__form">
|
||||
<h3>Skapa ny rapport</h3>
|
||||
<p>Här kan du skicka rapporter om deltagaren till arbetsförmedlingen.</p>
|
||||
<digi-ng-form-select
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="reportsFormControl.invalid && reportsFormControl.touched"
|
||||
[afRequired]="true"
|
||||
[afSelectItems]="selectableReportTypes"
|
||||
[formControlName]="reportsFormControlName"
|
||||
afLabel="Välj rapporttyp"
|
||||
afPlaceholder="Välj rapporttyp"
|
||||
>
|
||||
</digi-ng-form-select>
|
||||
<digi-form-validation-message *ngIf="reportsFormControl.invalid && reportsFormControl.touched" af-variation="error">
|
||||
Du måste välja en rapporttyp
|
||||
</digi-form-validation-message>
|
||||
</form>
|
||||
<div class="deltagare-tab-reports__cta-wrapper">
|
||||
<!-- <form [formGroup]="reportPickerFormGroup" class="deltagare-tab-reports__form">-->
|
||||
<h3>Skapa ny rapport</h3>
|
||||
<p>Här kan du skicka rapporter om deltagaren till arbetsförmedlingen.</p>
|
||||
|
||||
<!-- TODO put these in a styled list-->
|
||||
<p>
|
||||
<digi-ng-link-button [afRoute]="'./gemensam-planering'" afText="Skapa ny Gemensam planering"></digi-ng-link-button>
|
||||
</p>
|
||||
<p>
|
||||
<digi-ng-link-button
|
||||
(click)="onFormSubmitted($event, reportsFormControl.value)"
|
||||
afText="Skapa ny rapport"
|
||||
[afRoute]="'./franvarorapport'"
|
||||
afText="Skapa ny Avvikelserapport (frånvaro)"
|
||||
></digi-ng-link-button>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<digi-ng-link-button
|
||||
[afRoute]="'./avvikelserapport'"
|
||||
afText="Skapa ny Avvikelserapport (avvikelse)"
|
||||
></digi-ng-link-button>
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="reportsData; else loadingRef">
|
||||
<h3>Inskickade rapporter</h3>
|
||||
<msfa-reports-list
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="report-layout__progress-bar">
|
||||
<div class="report-layout__progress-bar" *ngIf="!!totalAmountOfSteps && !!currentStep">
|
||||
<digi-progressbar
|
||||
[afTotalSteps]="totalAmountOfSteps"
|
||||
[afCompletedSteps]="currentStep - 1"
|
||||
|
||||
@@ -16,9 +16,8 @@ export class ReportLayoutComponent {
|
||||
@Input() service: string;
|
||||
@Input() isPeriodDate = false;
|
||||
@Input() avrop: Avrop;
|
||||
@Input() totalAmountOfSteps = 3;
|
||||
@Input() currentStep = 1;
|
||||
@Input() totalAmountOfSteps: number;
|
||||
@Input() currentStep: number;
|
||||
@Input() showSuccessNotification = false;
|
||||
@Input() showDangerNotification = false;
|
||||
@Input() submitted = false;
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<section class="deltagare-confirm">
|
||||
<h3 class="deltagare-confirm__header">Vad är det du vill rapportera?</h3>
|
||||
<p>{{ formGroup?.get('alternative').value === 'franvaro' ? 'Frånvaro' : 'Avvikelse'}}</p>
|
||||
|
||||
<ng-container *ngIf="formGroup?.get('alternative').value === 'franvaro'">
|
||||
<h3 class="deltagare-confirm__header">Orsak till frånvaro</h3>
|
||||
<ng-container *ngFor="let orsak of orsakskoderfranvaro">
|
||||
<p *ngIf="orsak.value == formGroup?.get('orsakerFormGroup').get('orsaker').value">{{orsak.name}}</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="formGroup?.get('orsakerFormGroup').get('andraKandaOrsaker').value">
|
||||
<h3 class="deltagare-confirm__header">Annan känd orsak</h3>
|
||||
<ng-container *ngFor="let annanKandOrsak of andraKandaOrsaker">
|
||||
<p *ngIf="annanKandOrsak.value == formGroup?.get('orsakerFormGroup').get('andraKandaOrsaker').value">
|
||||
{{annanKandOrsak.name}}
|
||||
</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="formGroup?.get('orsakerFormGroup').get('andraKandaOrsaker').value == 5">
|
||||
<h3 class="deltagare-confirm__header">Beskrivning</h3>
|
||||
<p>{{formGroup?.get('description').value}}</p>
|
||||
</ng-container>
|
||||
|
||||
<h3 class="deltagare-confirm__header">Datum</h3>
|
||||
<p>{{formGroup?.get('date').value}}</p>
|
||||
<h3 class="deltagare-confirm__header">Hel eller del av dag</h3>
|
||||
<p>{{formGroup.get('dayOrPartOfDay').value === 'HELDAG' ? 'Heldag' : 'Del av dag'}}</p>
|
||||
|
||||
<ng-container *ngIf="formGroup.get('dayOrPartOfDay').value === 'DEL_AV_DAG'">
|
||||
<h3 class="deltagare-confirm__header">Starttid</h3>
|
||||
<p>{{formGroup?.get('timepickerFormGroup').get('startTime').value}}</p>
|
||||
|
||||
<h3 class="deltagare-confirm__header">Sluttid</h3>
|
||||
<p>{{formGroup?.get('timepickerFormGroup').get('endTime').value}}</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="formGroup?.get('alternative').value === 'avvikelse'">
|
||||
<h3 class="deltagare-confirm__header">Orsak till avvikelse</h3>
|
||||
<ng-container *ngFor="let orsak of avvikelseOrsaker">
|
||||
<p *ngIf="orsak.value == formGroup?.get('orsakerFormGroup').get('orsaker').value">{{orsak.name}}</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let fraga of fragor1">
|
||||
<h3
|
||||
*ngIf="fraga.id.includes(formGroup?.get('orsakerFormGroup').get('orsaker').value)"
|
||||
class="deltagare-confirm__header"
|
||||
>
|
||||
{{fraga.name}}
|
||||
</h3>
|
||||
</ng-container>
|
||||
<p>{{formGroup?.get('fragorFormGroup').get('fraga1').value}}</p>
|
||||
|
||||
<ng-container *ngFor="let fraga of fragor2">
|
||||
<h3
|
||||
*ngIf="formGroup?.get('fragorFormGroup').get('fraga2').value && fraga.id.includes(formGroup?.get('orsakerFormGroup').get('orsaker').value)"
|
||||
class="deltagare-confirm__header"
|
||||
>
|
||||
{{fraga.name}}
|
||||
</h3>
|
||||
</ng-container>
|
||||
<p *ngIf="selectedOrsak !== '28'">{{formGroup?.get('fragorFormGroup').get('fraga2').value}}</p>
|
||||
|
||||
<h3 class="deltagare-confirm__header">Datum</h3>
|
||||
<p>{{formGroup?.get('date').value}}</p>
|
||||
</ng-container>
|
||||
</section>
|
||||
@@ -1,10 +0,0 @@
|
||||
@import 'apps/mina-sidor-fa/src/styles/variables/gutters';
|
||||
|
||||
.deltagare-confirm {
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
|
||||
&__header {
|
||||
font-size: var(--digi--typography--font-size--l);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DeltagareConfirmFormComponent } from './deltagare-confirm-form.component';
|
||||
|
||||
describe('DeltagareConfirmFormComponent', () => {
|
||||
let component: DeltagareConfirmFormComponent;
|
||||
let fixture: ComponentFixture<DeltagareConfirmFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DeltagareConfirmFormComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DeltagareConfirmFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { AvvikelseOrsaksKodEnum } from '@msfa-enums/avvikelse-orsak-kod.enum';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-deltagare-confirm-form',
|
||||
templateUrl: './deltagare-confirm-form.component.html',
|
||||
styleUrls: ['./deltagare-confirm-form.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class DeltagareConfirmFormComponent implements OnChanges {
|
||||
@Input() formGroup: FormGroup | null = null;
|
||||
@Input() orsakskoderfranvaro: OrsaksKoderFranvaro[];
|
||||
@Input() andraKandaOrsaker: KandaAvvikelseKoder[];
|
||||
@Input() avvikelseOrsaker: OrsaksKoderAvvikelse[];
|
||||
@Input() fragor1: FragorForAvvikelser[];
|
||||
@Input() fragor2: FragorForAvvikelser[];
|
||||
@Input() selectedOrsak: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (Number(changes.selectedOrsak?.currentValue) === AvvikelseOrsaksKodEnum.SerTillAttErbjudetArbeteInteKommerTillStand) {
|
||||
this.formGroup?.get('fragorFormGroup').get('fraga2').reset('');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DeltagareConfirmFormComponent } from './deltagare-confirm-form.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [DeltagareConfirmFormComponent],
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
exports: [DeltagareConfirmFormComponent]
|
||||
})
|
||||
export class DeltagareConfirmFormModule { }
|
||||
@@ -1,28 +0,0 @@
|
||||
<form class="fragor-form" [formGroup]="fragorFormGroup" *ngIf="fragorFormGroup">
|
||||
<ng-container *ngFor="let fraga of fragor1 | filterFragor: selectedOrsaksKod">
|
||||
<div class="fragor-form__content">
|
||||
<digi-ng-form-textarea
|
||||
[formControlName]="'fraga1'"
|
||||
[afLabel]="fraga.name"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalidMessage]="fragorFormGroup.get('fraga1').errors?.message"
|
||||
[afInvalid]="fragorFormGroup.get('fraga1').invalid && fragorFormGroup.get('fraga1').touched"
|
||||
[afMaxLength]="2000"
|
||||
></digi-ng-form-textarea>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngFor="let fraga of fragor2 | filterFragor: selectedOrsaksKod">
|
||||
<div class="fragor-form__content">
|
||||
<digi-ng-form-textarea
|
||||
[formControlName]="'fraga2'"
|
||||
[afLabel]="fraga.name"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="selectedOrsaksKod !== '19' && selectedOrsaksKod !== '20' && selectedOrsaksKod !== '28'"
|
||||
[afInvalidMessage]="fragorFormGroup.get('fraga2').errors?.message"
|
||||
[afInvalid]="fragorFormGroup.get('fraga2').invalid && fragorFormGroup.get('fraga2').touched"
|
||||
[afMaxLength]="2000"
|
||||
></digi-ng-form-textarea>
|
||||
</div>
|
||||
</ng-container>
|
||||
</form>
|
||||
@@ -1,8 +0,0 @@
|
||||
@import 'apps/mina-sidor-fa/src/styles/variables/gutters';
|
||||
|
||||
.fragor-form {
|
||||
&__content {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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<DeltagareFragorFormComponent>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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 { }
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<form class="orsaks-form" [formGroup]="orsakerFormGroup" *ngIf="orsakerFormGroup">
|
||||
<div *ngIf="selectedAlternative" class="orsaks-form__content">
|
||||
<digi-ng-form-select
|
||||
*ngIf="franvaroOrsaker || avvikelseOrsaker; else loadingRef"
|
||||
[formControlName]="'orsaker'"
|
||||
[afLabel]="selectedAlternative === 'franvaro' ? 'Orsak till frånvaro' : 'Orsak till avvikelse'"
|
||||
[afPlaceholder]="'Välj orsak till ' + (selectedAlternative === 'franvaro' ? 'frånvaro' : 'avvikelse')"
|
||||
[afSelectItems]="selectedAlternative === 'franvaro' ? franvaroOrsaker : avvikelseOrsaker"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="avvikelseFormGroup.errors?.orsakerIsRequired && orsakerFormGroup.touched"
|
||||
></digi-ng-form-select>
|
||||
<digi-form-validation-message
|
||||
af-variation="error"
|
||||
*ngIf="avvikelseFormGroup.errors?.orsakerIsRequired && orsakerFormGroup.touched"
|
||||
>
|
||||
Orsak är obligatoriskt
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
<ng-template #loadingRef>
|
||||
<msfa-loader type="padded"></msfa-loader>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="showAndraKandaOrsaker" class="orsaks-form__content">
|
||||
<digi-ng-form-select
|
||||
*ngIf="andraKandaOrsaker"
|
||||
[formControlName]="'andraKandaOrsaker'"
|
||||
[afLabel]="'Välj känd orsak'"
|
||||
[afPlaceholder]="'Känd orsak'"
|
||||
[afSelectItems]="andraKandaOrsaker"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="avvikelseFormGroup.errors?.annanKandorsakIsRequired && orsakerFormGroup.touched"
|
||||
></digi-ng-form-select>
|
||||
<digi-form-validation-message
|
||||
af-variation="error"
|
||||
*ngIf="avvikelseFormGroup.errors?.annanKandorsakIsRequired && orsakerFormGroup.touched"
|
||||
>
|
||||
Orsak är obligatoriskt
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
<ng-template #loadingRef>
|
||||
<msfa-loader type="padded"></msfa-loader>
|
||||
</ng-template>
|
||||
</form>
|
||||
@@ -1,8 +0,0 @@
|
||||
@import 'apps/mina-sidor-fa/src/styles/variables/gutters';
|
||||
|
||||
.orsaks-form {
|
||||
&__content {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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<DeltagareOrsaksFormComponent>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum';
|
||||
import { ReportType } from '@msfa-enums/report-type.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 === ReportType.FRANVARO &&
|
||||
+this.orsakerFormGroup.get('orsaker')?.value === FranvaroOrsaksKodEnum.AnnanKandOrsak
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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 { LoaderModule } from '@msfa-shared/components/loader/loader.module';
|
||||
import { DeltagareOrsaksFormComponent } from './deltagare-orsaks-form.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [DeltagareOrsaksFormComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
DigiNgFormSelectModule,
|
||||
LoaderModule
|
||||
],
|
||||
exports: [DeltagareOrsaksFormComponent]
|
||||
})
|
||||
export class DeltagareOrsaksFormModule { }
|
||||
@@ -1,28 +0,0 @@
|
||||
<div class="deltagare-timepicker">
|
||||
<form [formGroup]="timepickerFormGroup" *ngIf="timepickerFormGroup">
|
||||
<digi-typography>
|
||||
<label class="deltagare-timepicker__heading">Välj tid för frånvaro</label>
|
||||
</digi-typography>
|
||||
|
||||
<div class="deltagare-timepicker__input deltagare-timepicker__start-time">
|
||||
<digi-ng-form-input
|
||||
afLabel
|
||||
[afDescription]="'Ange starttid'"
|
||||
[formControl]="timepickerFormGroup.get('startTime')"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalidMessage]="timepickerFormGroup.get('startTime').errors?.message"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
</div>
|
||||
<div class="deltagare-timepicker__input">
|
||||
<digi-ng-form-input
|
||||
afLabel
|
||||
[afDescription]="'Ange sluttid'"
|
||||
[formControl]="timepickerFormGroup.get('endTime')"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalidMessage]="timepickerFormGroup.get('endTime').errors?.message"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,17 +0,0 @@
|
||||
@import 'apps/mina-sidor-fa/src/styles/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--l;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 127px;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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<DeltagareTimePickerComponent>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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 { }
|
||||
@@ -1,166 +0,0 @@
|
||||
<msfa-layout *ngIf="avrop$ | async as avrop; else skeletonRef">
|
||||
<msfa-report-layout
|
||||
[currentStep]="currentStep"
|
||||
[totalAmountOfSteps]="totalAmountOfSteps"
|
||||
description="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."
|
||||
reportSubTitle="Skapa rapport"
|
||||
reportTitle="Avvikelserapport"
|
||||
>
|
||||
<div *ngIf="submittedDate$ | async as submittedDate; " class="deltagare-avvikelse__confirmation">
|
||||
<digi-notification-alert
|
||||
af-heading="Allt gick bra"
|
||||
af-heading-level="h3"
|
||||
af-variation="success"
|
||||
class="deltagare-avvikelse__alert"
|
||||
>
|
||||
<p>
|
||||
Avvikelserapport för deltagare {{avrop.fullName}} är nu inskickad till
|
||||
Arbetsförmedlingen och inväntar
|
||||
godkännande.
|
||||
</p>
|
||||
<dl>
|
||||
<dt>Datum</dt>
|
||||
<dd>{{submittedDate | date:'longDate'}} kl {{submittedDate | date:'shortTime'}}</dd>
|
||||
</dl>
|
||||
</digi-notification-alert>
|
||||
<msfa-back-link [route]="['../']">Tillbaka till deltagaren</msfa-back-link>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="currentStep === 1">
|
||||
<h3 class="deltagare-avvikelse__alternative-heading">Vad är det du vill rapportera?</h3>
|
||||
<form [formGroup]="avvikelseFormGroup">
|
||||
<div class="deltagare-avvikelse__alternative">
|
||||
<digi-ng-form-radiobutton-group
|
||||
(change)="setAlternative()"
|
||||
[afRadiobuttons]="avvikelseAlternatives"
|
||||
[formControlName]="alternativeFormControlName"
|
||||
></digi-ng-form-radiobutton-group>
|
||||
<digi-form-validation-message
|
||||
*ngIf="alternativeFormControl.invalid && alternativeFormControl.touched"
|
||||
af-variation="error"
|
||||
>
|
||||
Alternativ är obligatoriskt
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
|
||||
<msfa-deltagare-orsaks-form
|
||||
(change)="setOrsakerChanged()"
|
||||
[andraKandaOrsaker]="andraKandaOrsaker$ | async"
|
||||
[avvikelseFormGroup]="avvikelseFormGroup"
|
||||
[avvikelseOrsaker]="avvikelseOrsaker$ | async "
|
||||
[franvaroOrsaker]="franvaroOrsaker$ | async"
|
||||
[orsakerFormGroup]="avvikelseFormGroup.get('orsakerFormGroup')"
|
||||
[selectedAlternative]="alternativeFormControl.value"
|
||||
></msfa-deltagare-orsaks-form>
|
||||
|
||||
<div *ngIf="showDescription" class="deltagare-avvikelse__description">
|
||||
<digi-ng-form-textarea
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalidMessage]="descriptionFormControl.errors?.message"
|
||||
[afInvalid]="descriptionFormControl?.invalid && descriptionFormControl.touched"
|
||||
[afMaxLength]="2000"
|
||||
[afRequired]="true"
|
||||
[afSize]="sizeTextArea"
|
||||
[formControlName]="descriptionFormControlName"
|
||||
afLabel="Beskriv frånvaro"
|
||||
></digi-ng-form-textarea>
|
||||
</div>
|
||||
|
||||
<msfa-deltagare-fragor-form
|
||||
*ngIf="showFragor"
|
||||
[avvikelseFormGroup]="avvikelseFormGroup"
|
||||
[fragor1]="fragor1$ | async"
|
||||
[fragor2]="fragor2$ | async"
|
||||
[fragorFormGroup]="avvikelseFormGroup.get('fragorFormGroup')"
|
||||
[selectedOrsaksKod]="selectedOrsaksKod"
|
||||
></msfa-deltagare-fragor-form>
|
||||
|
||||
<div *ngIf="showDatePicker" class="deltagare-avvikelse__datepicker">
|
||||
<digi-ng-form-datepicker
|
||||
[afDisableValidStyle]="true"
|
||||
[afMinDate]="setMinDate(avrop.startDate)"
|
||||
[afMaxDate]="setMaxDate"
|
||||
[afInvalid]="avvikelseFormGroup.errors?.dateIsRequired"
|
||||
[afLabel]="'Välj dag för ' + (alternativeFormControl.value === 'franvaro' ? 'frånvaro' : 'avvikelse')"
|
||||
[formControlName]="dateFormControlName"
|
||||
></digi-ng-form-datepicker>
|
||||
<digi-form-validation-message *ngIf="avvikelseFormGroup.errors?.dateIsRequired" af-variation="error">
|
||||
{{avvikelseFormGroup.errors?.dateIsRequired}}
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
|
||||
<div *ngIf="showDayOrPartOfDayPicker" class="deltagare-avvikelse__dayOrPartOfDay">
|
||||
<digi-ng-form-radiobutton-group
|
||||
(change)="setDayOrPartOfDayChanged()"
|
||||
[afRadiobuttons]="dayOrPartOfDay"
|
||||
[afRequired]="dayOrPartOfDayFormControl.invalid && dayOrPartOfDayFormControl.touched"
|
||||
[formControlName]="dayOrPartOfDayFormControlName"
|
||||
></digi-ng-form-radiobutton-group>
|
||||
<digi-form-validation-message
|
||||
*ngIf="dayOrPartOfDayFormControl.invalid && dayOrPartOfDayFormControl.touched"
|
||||
af-variation="error"
|
||||
>
|
||||
{{dayOrPartOfDayFormControl.errors?.message}}
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
|
||||
<msfa-deltagare-time-picker
|
||||
*ngIf="showTimePicker"
|
||||
[avvikelseFormGroup]="avvikelseFormGroup"
|
||||
[timepickerFormGroup]="avvikelseFormGroup.get('timepickerFormGroup')"
|
||||
></msfa-deltagare-time-picker>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<msfa-deltagare-confirm-form
|
||||
*ngIf="currentStep === totalAmountOfSteps"
|
||||
[andraKandaOrsaker]="andraKandaOrsaker$ | async"
|
||||
[avvikelseOrsaker]="avvikelseOrsaker$ | async"
|
||||
[formGroup]="avvikelseFormGroup"
|
||||
[fragor1]="fragor1$ | async"
|
||||
[fragor2]="fragor2$ | async"
|
||||
[orsakskoderfranvaro]="franvaroOrsaker$ | async"
|
||||
[selectedOrsak]="selectedOrsaksKod"
|
||||
></msfa-deltagare-confirm-form>
|
||||
|
||||
<digi-notification-alert
|
||||
*ngIf="error$ | async as error"
|
||||
af-heading="Någonting gick fel"
|
||||
af-variation="danger"
|
||||
class="deltagare-avvikelse__alert"
|
||||
>
|
||||
<p>Kunde inte spara avvikelserapporten. Ladda om sidan och försök igen.</p>
|
||||
<p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>
|
||||
</digi-notification-alert>
|
||||
|
||||
<div class="deltagare-avvikelse__step-buttons-wrapper">
|
||||
<ng-container *ngIf="(submittedDate$ | async) === null">
|
||||
<digi-button
|
||||
(afOnClick)="previousStep()"
|
||||
*ngIf="currentStep > 1"
|
||||
af-size="m"
|
||||
af-variation="secondary"
|
||||
class="deltagare-avvikelse__step-buttons-wrapper--space-right"
|
||||
>
|
||||
Tillbaka
|
||||
</digi-button>
|
||||
<digi-button (afOnClick)="openConfirmDialog = true" *ngIf="currentStep === totalAmountOfSteps" af-size="m">
|
||||
Skicka in
|
||||
</digi-button>
|
||||
</ng-container>
|
||||
<digi-button (afOnClick)="nextStep" *ngIf="currentStep === (totalAmountOfSteps -1)" af-size="m">
|
||||
Förhandsgranska
|
||||
</digi-button>
|
||||
</div>
|
||||
</msfa-report-layout>
|
||||
|
||||
<msfa-confirm-dialog
|
||||
(confirmDialogChanged)="setConfirmDialogChanged($event)"
|
||||
[openConfirmDialog]="openConfirmDialog"
|
||||
reportToConfirm="avvikelserapport"
|
||||
>
|
||||
</msfa-confirm-dialog>
|
||||
</msfa-layout>
|
||||
<ng-template #skeletonRef>
|
||||
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för avvikelserapport"></digi-ng-skeleton-base>
|
||||
</ng-template>
|
||||
@@ -1,360 +0,0 @@
|
||||
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { FormTextareaSize } from '@af/digi-ng/_form/form-textarea';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ConfirmDialog } from '@msfa-enums/confirm-dialog.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 { ReportType } from '@msfa-enums/report-type.enum';
|
||||
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 { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
import {
|
||||
requiredAnnanKandOrsakValidator,
|
||||
RequiredDateValidator,
|
||||
requiredDayOrPartOfDayValidator,
|
||||
requiredDescriptionValidator,
|
||||
requiredEndTimeValidator,
|
||||
requiredFraga1Validator,
|
||||
requiredfraga2Validator,
|
||||
requiredOrsakerValidator,
|
||||
requiredStartTimeValidator,
|
||||
} from '@msfa-validators/avvikelse-form-validator';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { DeltagareAvvikelseService } from './deltagare-avvikelse.service';
|
||||
import { avvikelseAlternatives, dayOrPartOfDay } from './report-alternatives';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
|
||||
interface Params {
|
||||
genomforandeReferens: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-deltagare-avvikelse',
|
||||
templateUrl: './deltagare-avvikelse.component.html',
|
||||
styleUrls: ['./deltagare-avvikelse.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DeltagareAvvikelseComponent {
|
||||
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';
|
||||
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||
map((params: Params) => +params.genomforandeReferens)
|
||||
);
|
||||
avrop$: Observable<Avrop> = this.genomforandeReferens$.pipe(
|
||||
switchMap(genomforandeReferens => this.deltagareAvvikelseService.fetchAvropInformation$(genomforandeReferens)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
franvaroOrsaker$: Observable<OrsaksKoderFranvaro[]>;
|
||||
avvikelseOrsaker$: Observable<OrsaksKoderAvvikelse[]>;
|
||||
andraKandaOrsaker$: Observable<KandaAvvikelseKoder[]>;
|
||||
fragor1$: Observable<FragorForAvvikelser[]>;
|
||||
fragor2$: Observable<FragorForAvvikelser[]>;
|
||||
sizeTextArea: FormTextareaSize.S;
|
||||
todayDate = new Date().toISOString().slice(0, 10);
|
||||
avvikelseAlternatives: RadiobuttonModel[] = avvikelseAlternatives;
|
||||
dayOrPartOfDay: RadiobuttonModel[] = dayOrPartOfDay;
|
||||
selectedOrsaksKod: string;
|
||||
avvikelseFormGroup = new FormGroup(
|
||||
{
|
||||
alternative: new FormControl(null, [RequiredValidator()]),
|
||||
description: new FormControl('', [requiredDescriptionValidator()]),
|
||||
date: new FormControl(this.todayDate),
|
||||
dayOrPartOfDay: new FormControl(null, [requiredDayOrPartOfDayValidator()]),
|
||||
orsakerFormGroup: new FormGroup({
|
||||
orsaker: new FormControl([], [requiredOrsakerValidator()]),
|
||||
andraKandaOrsaker: new FormControl([], [requiredAnnanKandOrsakValidator()]),
|
||||
}),
|
||||
fragorFormGroup: new FormGroup({
|
||||
fraga1: new FormControl('', [requiredFraga1Validator()]),
|
||||
fraga2: new FormControl('', [requiredfraga2Validator()]),
|
||||
}),
|
||||
timepickerFormGroup: new FormGroup({
|
||||
startTime: new FormControl('', [requiredStartTimeValidator()]),
|
||||
endTime: new FormControl('', [requiredEndTimeValidator()]),
|
||||
}),
|
||||
},
|
||||
{
|
||||
validators: [RequiredDateValidator.CheckIfRequired()],
|
||||
}
|
||||
);
|
||||
totalAmountOfSteps = 2;
|
||||
currentStep = 1;
|
||||
openConfirmDialog = false;
|
||||
private _submittedDate$ = new BehaviorSubject<Date | null>(null);
|
||||
submittedDate$: Observable<Date | null> = this._submittedDate$.asObservable();
|
||||
private _error$ = new BehaviorSubject<CustomError>(null);
|
||||
error$: Observable<CustomError> = this._error$.asObservable();
|
||||
private _showDangerNotification$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(
|
||||
private deltagareAvvikelseService: DeltagareAvvikelseService,
|
||||
private deltagareApiService: DeltagareApiService,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) {}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
get franvaro(): FranvaroAlternativ {
|
||||
return {
|
||||
avvikelseOrsaksKod: this.orsakerFormControl.value as string,
|
||||
datum: this.dateFormControl.value as string,
|
||||
heldag: this.dayOrPartOfDayFormControl.value === DayOrPartOfDay.HELDAG,
|
||||
startTid: (this.startTimeFormControl.value as string) || '9:00',
|
||||
slutTid: (this.endTimeFormControl.value as string) || '16:00',
|
||||
forvantadNarvaro: {
|
||||
startTid: '',
|
||||
slutTid: '',
|
||||
},
|
||||
alternativForKandaOrsaker: {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
get showDescription(): boolean {
|
||||
return (
|
||||
(this.alternativeFormControl.value as string) === ReportType.FRANVARO &&
|
||||
+this.orsakerFormControl.value === FranvaroOrsaksKodEnum.AnnanKandOrsak &&
|
||||
+this.andraKandaOrsakerFormControl.value === KandaOrsakerEnum.AnnanOrsak
|
||||
);
|
||||
}
|
||||
|
||||
get showFragor(): boolean {
|
||||
return (
|
||||
(this.alternativeFormControl.value as string) === ReportType.AVVIKELSE &&
|
||||
(this.orsakerFormControl.value as boolean)
|
||||
);
|
||||
}
|
||||
|
||||
get showDatePicker(): boolean {
|
||||
return this.orsakerFormControl.value as boolean;
|
||||
}
|
||||
|
||||
get showDayOrPartOfDayPicker(): boolean {
|
||||
return (
|
||||
(this.alternativeFormControl.value as string) === ReportType.FRANVARO &&
|
||||
(this.orsakerFormControl.value as boolean)
|
||||
);
|
||||
}
|
||||
|
||||
get showTimePicker(): boolean {
|
||||
return (
|
||||
(this.alternativeFormControl.value as string) === ReportType.FRANVARO &&
|
||||
(this.dayOrPartOfDayFormControl.value as string) === DayOrPartOfDay.DEL_AV_DAG
|
||||
);
|
||||
}
|
||||
|
||||
get nextStep(): number {
|
||||
this.avvikelseFormGroup.markAllAsTouched();
|
||||
if (this.avvikelseFormGroup.valid && this.currentStep < this.totalAmountOfSteps) {
|
||||
return this.currentStep++;
|
||||
}
|
||||
}
|
||||
|
||||
setConfirmDialogChanged(confirm: ConfirmDialog): void {
|
||||
this.openConfirmDialog = false;
|
||||
if (confirm === ConfirmDialog.ACCEPTED) {
|
||||
const postAvvikelse: Avvikelse = {
|
||||
genomforandeReferens: +this.activatedRoute.snapshot.params['genomforandeReferens'],
|
||||
};
|
||||
|
||||
if ((this.alternativeFormControl.value as string) === ReportType.AVVIKELSE) {
|
||||
this.postAvvikelse();
|
||||
} else if ((this.alternativeFormControl.value as string) === ReportType.FRANVARO) {
|
||||
this.postFranvaro();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get genomforandeReferensSnapshot() {
|
||||
return +this.activatedRoute.snapshot.params['genomforandeReferens'];
|
||||
}
|
||||
|
||||
postAvvikelse() {
|
||||
const avvikelseData: Avvikelse = {
|
||||
genomforandeReferens: this.genomforandeReferensSnapshot,
|
||||
avvikelseAlternativ: this.avvikelse,
|
||||
};
|
||||
|
||||
this.deltagareAvvikelseService
|
||||
.createAvvikelse$(avvikelseData)
|
||||
.then(() => {
|
||||
this._submittedDate$.next(new Date());
|
||||
this.avvikelseFormGroup.reset();
|
||||
this.currentStep = 3;
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this._error$.next(new CustomError({ error, message: error.message, type: ErrorType.API }));
|
||||
});
|
||||
}
|
||||
|
||||
postFranvaro() {
|
||||
const avvikelseData: Avvikelse = {
|
||||
genomforandeReferens: this.genomforandeReferensSnapshot,
|
||||
franvaro: this.franvaro,
|
||||
};
|
||||
|
||||
this.deltagareAvvikelseService
|
||||
.createFranvaro$(avvikelseData)
|
||||
.then(() => {
|
||||
this._submittedDate$.next(new Date());
|
||||
this.avvikelseFormGroup.reset();
|
||||
this.currentStep = 3;
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this._error$.next(new CustomError({ error, message: error.message, type: ErrorType.API }));
|
||||
});
|
||||
}
|
||||
|
||||
setAlternative(): void {
|
||||
if ((this.alternativeFormControl.value as string) == ReportType.FRANVARO) {
|
||||
this.franvaroOrsaker$ = this.deltagareAvvikelseService.getOrsaksKoderFranvaro$.pipe(shareReplay(1));
|
||||
this.andraKandaOrsaker$ = this.deltagareAvvikelseService.getAndraKandaOrsaker$.pipe(shareReplay(1));
|
||||
}
|
||||
|
||||
if ((this.alternativeFormControl.value as string) == ReportType.AVVIKELSE) {
|
||||
this.avvikelseOrsaker$ = this.deltagareAvvikelseService.getOrsaksKoderAvvikelse$.pipe(shareReplay(1));
|
||||
this.fragor1$ = this.deltagareAvvikelseService.fragorForAvvikelser$.pipe(
|
||||
map((fragor: FragorForAvvikelser[]) => {
|
||||
return fragor.filter((fraga: FragorForAvvikelser) => fraga.id.includes('_1'));
|
||||
})
|
||||
);
|
||||
|
||||
this.fragor2$ = this.deltagareAvvikelseService.fragorForAvvikelser$.pipe(
|
||||
map((fragor: FragorForAvvikelser[]) => {
|
||||
return fragor.filter((fraga: FragorForAvvikelser) => {
|
||||
return fraga.id.includes('_2');
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.clearControlOnAlternativeChange();
|
||||
}
|
||||
|
||||
setOrsakerChanged(): void {
|
||||
this.avvikelseFormGroup.markAsUntouched();
|
||||
if ((this.alternativeFormControl.value as string) === ReportType.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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setDayOrPartOfDayChanged(): void {
|
||||
if (this.dayOrPartOfDayFormControl.value === DayOrPartOfDay.HELDAG) {
|
||||
this.startTimeFormControl.reset();
|
||||
this.endTimeFormControl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
setMinDate(startdatumAvrop: Date): Date {
|
||||
return new Date(startdatumAvrop);
|
||||
}
|
||||
|
||||
get setMaxDate(): Date {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
previousStep(): void {
|
||||
if (this.currentStep > 1) {
|
||||
this.currentStep--;
|
||||
this._submittedDate$.next(null);
|
||||
this._showDangerNotification$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
private clearControlOnAlternativeChange(): void {
|
||||
this.descriptionFormControl.setValue('');
|
||||
this.fraga1FormControl.setValue('');
|
||||
this.fraga2FormControl.setValue('');
|
||||
this.dayOrPartOfDayFormControl.reset();
|
||||
this.avvikelseFormGroup?.get('timepickerFormGroup').reset();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FranvaroOrsaksKodEnum } from '@msfa-enums/franvaro-orsak-kod.enum';
|
||||
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, shareReplay } from 'rxjs/operators';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeltagareAvvikelseService {
|
||||
public fragorForAvvikelser$: Observable<
|
||||
FragorForAvvikelser[]
|
||||
> = this.avvikelseApiService.getFragorForAvvikelser$().pipe(shareReplay(1));
|
||||
public getOrsaksKoderFranvaro$: Observable<
|
||||
OrsaksKoderFranvaro[]
|
||||
> = this.avvikelseApiService.getOrsaksKoderFranvaro$().pipe(
|
||||
map((orsaksKoder: OrsaksKoderFranvaro[]) => {
|
||||
orsaksKoder.find(kod => {
|
||||
if (kod.value === FranvaroOrsaksKodEnum.VAB) {
|
||||
kod.name = 'Vård av barn';
|
||||
}
|
||||
});
|
||||
return this.sortOrsaksKoder(orsaksKoder);
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {}
|
||||
|
||||
public getOrsaksKoderAvvikelse$: Observable<
|
||||
OrsaksKoderAvvikelse[]
|
||||
> = this.avvikelseApiService.getOrsaksKoderAvvikelse$();
|
||||
|
||||
public getAndraKandaOrsaker$: Observable<KandaAvvikelseKoder[]> = this.avvikelseApiService.getAndraKandaOrsaker$();
|
||||
|
||||
public createAvvikelse$(avvikelse: Avvikelse): Promise<void> {
|
||||
return this.avvikelseApiService.createAvvikelse$(avvikelse);
|
||||
}
|
||||
|
||||
public createFranvaro$(avvikelse: Avvikelse): Promise<void> {
|
||||
return this.avvikelseApiService.createFranvaro$(avvikelse);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fetchAvropInformation$(genomforandeReferens: number) {
|
||||
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<msfa-layout>
|
||||
<msfa-report-layout
|
||||
[avrop]="avrop$ | async"
|
||||
description="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."
|
||||
reportSubTitle="Skapa rapport"
|
||||
reportTitle="Avvikelserapport (avvikelse)"
|
||||
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||
>
|
||||
<div *ngIf="submittedDate$ | async as submittedDate; else formRef" class="deltagare-avvikelse__confirmation">
|
||||
<digi-notification-alert
|
||||
af-heading="Allt gick bra"
|
||||
af-heading-level="h3"
|
||||
af-variation="success"
|
||||
class="deltagare-avvikelse__alert"
|
||||
>
|
||||
<p>
|
||||
Avvikelserapport för deltagare {{avrop.fullName}} är nu inskickad till Arbetsförmedlingen och inväntar
|
||||
godkännande.
|
||||
</p>
|
||||
<dl>
|
||||
<dt>Datum</dt>
|
||||
<dd>{{submittedDate | date:'longDate'}} kl {{submittedDate | date:'shortTime'}}</dd>
|
||||
</dl>
|
||||
</digi-notification-alert>
|
||||
<msfa-back-link [route]="['../']">Tillbaka till deltagaren</msfa-back-link>
|
||||
</div>
|
||||
|
||||
<ng-template #formRef>
|
||||
<form [formGroup]="avvikelseFormGroup" (ngSubmit)="openConfirmDialog()">
|
||||
<div class="orsaks-form__content">
|
||||
<digi-ng-form-select
|
||||
*ngIf="reasonsAsNgDigiFormSelectItems$ | async; let reason; else loadingRef"
|
||||
[formControlName]="reasonFormName"
|
||||
[afLabel]=" 'Orsak till avvikelse'"
|
||||
[afPlaceholder]="'Välj orsak till avvikelse'"
|
||||
[afSelectItems]="reason"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="formControlIsInvalid(reasonFormControl)"
|
||||
></digi-ng-form-select>
|
||||
<digi-form-validation-message *ngIf="formControlIsInvalid(reasonFormControl)" af-variation="error"
|
||||
>{{reasonFormControl.errors?.message}}
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
|
||||
<div [formArrayName]="questionsFormName">
|
||||
<ng-container *ngIf="questionsForChosenReason$ | async; let questions">
|
||||
<div *ngFor="let question of questionsFormArray.controls; let i=index">
|
||||
<div class="fragor-form__content">
|
||||
<digi-ng-form-textarea
|
||||
[formControlName]="i"
|
||||
[afLabel]="questions[i]?.name"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="questionIsRequired(questions[i])"
|
||||
[afMaxLength]="2000"
|
||||
[afInvalid]="formControlIsInvalid(question)"
|
||||
></digi-ng-form-textarea>
|
||||
|
||||
<digi-form-validation-message *ngIf="formControlIsInvalid(question)" af-variation="error"
|
||||
>{{question.errors?.message}}
|
||||
</digi-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="chosenReasonId$ | async">
|
||||
<digi-ng-form-datepicker
|
||||
[afDisableValidStyle]="true"
|
||||
[afMinDate]="minDate(avrop)"
|
||||
[afMaxDate]="maxDate"
|
||||
[afInvalid]="formControlIsInvalid(avvikelseDateFormControl)"
|
||||
[afLabel]="'Välj dag för avvikelse'"
|
||||
[formControlName]="reportingDateFormName"
|
||||
></digi-ng-form-datepicker>
|
||||
|
||||
<!-- NOTE: Other errors (such as formatting) are captured and displayed within digi-ng-form-datepicker -->
|
||||
<digi-form-validation-message
|
||||
*ngIf="avvikelseDateFormControl?.errors?.type === 'required'"
|
||||
af-variation="error"
|
||||
>{{avvikelseDateFormControl.errors?.message}}
|
||||
</digi-form-validation-message>
|
||||
</ng-container>
|
||||
|
||||
<div class="deltagare-avvikelse__cta-wrapper">
|
||||
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
|
||||
<msfa-back-link [showIcon]="false" [asButton]="true" [route]="['../']">Avbryt</msfa-back-link>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</msfa-report-layout>
|
||||
|
||||
<digi-ng-dialog
|
||||
[afActive]="confirmDialogIsOpen$ | async"
|
||||
(afOnPrimaryClick)="submitAndCloseConfirmDialog()"
|
||||
(afOnInactive)="cancelConfirmDialog()"
|
||||
afHeadingLevel="h2"
|
||||
[afPrimaryButtonText]="'Skicka in Avvikelserapport (avvikelse)'"
|
||||
[afSecondaryButtonText]="'Avbryt'"
|
||||
(afOnSecondaryClick)="cancelConfirmDialog()"
|
||||
[afHeading]="'Bekräfta och skicka in'"
|
||||
id="confirmAvvikelserapport"
|
||||
>
|
||||
<dl>
|
||||
<dt>Orsak till avvikelse:</dt>
|
||||
<dd>{{(chosenReason$ | async)?.name }}</dd>
|
||||
<ng-container *ngIf="avvikelseSubmitData$ | async; let avvikelseSubmitData; else loadingRef">
|
||||
<ng-container *ngFor="let question of avvikelseSubmitData.avvikelseAlternativ.frageformular">
|
||||
<dt>{{getCurrentQuestionFromId(question.fraga).name}}</dt>
|
||||
<dd>{{question.svar.length === 0 ? 'Inget svar' : question.svar }}</dd>
|
||||
</ng-container>
|
||||
<dt>Dag för avvikelse:</dt>
|
||||
<dd>{{avvikelseSubmitData.avvikelseAlternativ.rapporteringsdatum }}</dd>
|
||||
</ng-container>
|
||||
</dl>
|
||||
|
||||
<msfa-loader *ngIf="submitIsLoading$ | async"></msfa-loader>
|
||||
</digi-ng-dialog>
|
||||
</msfa-layout>
|
||||
<ng-template #skeletonRef>
|
||||
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för avvikelserapport"></digi-ng-skeleton-base>
|
||||
</ng-template>
|
||||
<ng-template #loadingRef>
|
||||
<msfa-loader type="padded"></msfa-loader>
|
||||
</ng-template>
|
||||
@@ -36,4 +36,26 @@
|
||||
&__alert {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
}
|
||||
|
||||
|
||||
&__cta-wrapper {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.fragor-form {
|
||||
&__content {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.orsaks-form {
|
||||
&__content {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
margin-bottom: $digi--layout--gutter--xl;
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,17 @@ 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';
|
||||
import { DeltagareAvvikelseService } from './deltagare-avvikelse.service';
|
||||
import { DeltagareAvvikelserapportComponent } from './deltagare-avvikelserapport.component';
|
||||
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
|
||||
|
||||
describe('DeltagareAvvikelseComponent', () => {
|
||||
let component: DeltagareAvvikelseComponent;
|
||||
let fixture: ComponentFixture<DeltagareAvvikelseComponent>;
|
||||
let component: DeltagareAvvikelserapportComponent;
|
||||
let fixture: ComponentFixture<DeltagareAvvikelserapportComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [DeltagareAvvikelseComponent, LayoutComponent],
|
||||
declarations: [DeltagareAvvikelserapportComponent, LayoutComponent],
|
||||
imports: [
|
||||
RouterTestingModule,
|
||||
HttpClientTestingModule,
|
||||
@@ -24,12 +24,12 @@ describe('DeltagareAvvikelseComponent', () => {
|
||||
DigiNgFormRadiobuttonGroupModule,
|
||||
DigiNgFormDatepickerModule,
|
||||
],
|
||||
providers: [DeltagareAvvikelseService],
|
||||
providers: [DeltagareAvvikelserapportService],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DeltagareAvvikelseComponent);
|
||||
fixture = TestBed.createComponent(DeltagareAvvikelserapportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -0,0 +1,205 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AvvikelseAlternativ, AvvikelseRequestData } from '@msfa-models/avvikelse.model';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
import { FormSelectItem } from '@af/digi-ng/_form/form-select';
|
||||
import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
|
||||
interface Params {
|
||||
genomforandeReferens: string;
|
||||
}
|
||||
|
||||
interface AvvikelseFormData {
|
||||
reason: string;
|
||||
questions: string[];
|
||||
reportingDate: string;
|
||||
}
|
||||
|
||||
type AvvikelseFormKeys = keyof AvvikelseFormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-deltagare-avvikelse',
|
||||
templateUrl: './deltagare-avvikelserapport.component.html',
|
||||
styleUrls: ['./deltagare-avvikelserapport.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
reasonFormName: AvvikelseFormKeys = 'reason';
|
||||
questionsFormName: AvvikelseFormKeys = 'questions';
|
||||
reportingDateFormName: AvvikelseFormKeys = 'reportingDate';
|
||||
|
||||
submitIsLoading$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||
map((params: Params) => +params.genomforandeReferens)
|
||||
);
|
||||
avrop$: Observable<Avrop> = this.genomforandeReferens$.pipe(
|
||||
switchMap(genomforandeReferens => this.deltagareAvvikelseService.fetchAvropInformation$(genomforandeReferens)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
reasons$ = this.deltagareAvvikelseService.getAvvikelseOrsaker$;
|
||||
reasonsAsNgDigiFormSelectItems$: Observable<FormSelectItem[]> = this.reasons$.pipe(
|
||||
map(reasons => reasons.map(reason => ({ name: reason.name, value: reason.id })))
|
||||
);
|
||||
|
||||
allAvvikelseQuestions$ = this.deltagareAvvikelseService.fragorForAvvikelser$;
|
||||
chosenReasonId$: Observable<string>;
|
||||
chosenReason$: Observable<OrsaksKoderAvvikelse>;
|
||||
questionsForChosenReason$: Observable<FragorForAvvikelser[]>;
|
||||
avvikelseSubmitData$: Observable<AvvikelseRequestData>;
|
||||
confirmDialogIsOpen$ = new BehaviorSubject<boolean>(false);
|
||||
submittedDate$ = new BehaviorSubject<Date | null>(null);
|
||||
private subscriptions: Subscription[] = [];
|
||||
private todayDateISO = new Date().toISOString().slice(0, 10);
|
||||
avvikelseFormGroup = new FormGroup({
|
||||
[this.reasonFormName]: new FormControl(null, RequiredValidator('En orsak')), //[this.orsakFormControlName]:
|
||||
[this.reportingDateFormName]: new FormControl(this.todayDateISO, RequiredValidator('Datum')),
|
||||
[this.questionsFormName]: new FormArray([]),
|
||||
});
|
||||
private formData$: Observable<AvvikelseFormData> = this.avvikelseFormGroup
|
||||
.valueChanges as Observable<AvvikelseFormData>;
|
||||
private currentQuestions: FragorForAvvikelser[];
|
||||
|
||||
constructor(
|
||||
private deltagareAvvikelseService: DeltagareAvvikelserapportService,
|
||||
private deltagareApiService: DeltagareApiService,
|
||||
private activatedRoute: ActivatedRoute
|
||||
) {}
|
||||
|
||||
get reasonFormControl(): AbstractControl | undefined {
|
||||
return this.avvikelseFormGroup.get(this.reasonFormName);
|
||||
}
|
||||
|
||||
get avvikelseDateFormControl(): AbstractControl | undefined {
|
||||
return this.avvikelseFormGroup.get(this.reportingDateFormName);
|
||||
}
|
||||
|
||||
get questionsFormArray(): FormArray {
|
||||
return this.avvikelseFormGroup.get('questions') as FormArray;
|
||||
}
|
||||
|
||||
get maxDate(): Date {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
getCurrentQuestionFromId(id: string): FragorForAvvikelser {
|
||||
return this.currentQuestions.find(currentQuestions => currentQuestions.id === id);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.chosenReasonId$ = this.reasonFormControl.valueChanges as Observable<string>;
|
||||
this.chosenReason$ = combineLatest([this.chosenReasonId$, this.reasons$]).pipe(
|
||||
map(([chosenReasonId, reasons]) => reasons.find(reason => reason.id === chosenReasonId))
|
||||
);
|
||||
|
||||
this.questionsForChosenReason$ = combineLatest([this.chosenReasonId$, this.allAvvikelseQuestions$]).pipe(
|
||||
map(([chosenOrsak, allAvvikelseQuestions]) => {
|
||||
return allAvvikelseQuestions.filter(question => question.id.startsWith(chosenOrsak.toString() + '_'));
|
||||
})
|
||||
);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.chosenReason$.subscribe(() => {
|
||||
this.shouldValidate$.next(false);
|
||||
}),
|
||||
this.questionsForChosenReason$.subscribe(questions => {
|
||||
this.clearQuestions();
|
||||
questions.forEach(question => this.addQuestionToForm(question));
|
||||
})
|
||||
);
|
||||
|
||||
this.avvikelseSubmitData$ = combineLatest([this.genomforandeReferens$, this.chosenReasonId$, this.formData$]).pipe(
|
||||
map(([genomforandeReferens, chosenReason, formData]) =>
|
||||
this.makeAvvikelseSubmitData(genomforandeReferens, chosenReason, formData)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
questionIsRequired(question: FragorForAvvikelser): boolean {
|
||||
return !(question.id === '19_2' || question.id === '20_2');
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||
}
|
||||
|
||||
minDate(avrop: Avrop): Date {
|
||||
return new Date(avrop.recievedTimestamp);
|
||||
}
|
||||
|
||||
openConfirmDialog(): void {
|
||||
this.shouldValidate$.next(true);
|
||||
|
||||
if (this.avvikelseFormGroup.valid) {
|
||||
this.confirmDialogIsOpen$.next(true);
|
||||
}
|
||||
}
|
||||
|
||||
submitAndCloseConfirmDialog(): void {
|
||||
this.submitIsLoading$.next(true);
|
||||
this.avvikelseSubmitData$.pipe(take(1)).subscribe(avvikelseSubmitData =>
|
||||
this.deltagareAvvikelseService.createAvvikelse$(avvikelseSubmitData).subscribe({
|
||||
next: () => {
|
||||
this.submitIsLoading$.next(false);
|
||||
this.submittedDate$.next(new Date());
|
||||
this.confirmDialogIsOpen$.next(false);
|
||||
},
|
||||
error: error => {
|
||||
this.submitIsLoading$.next(false);
|
||||
throw new CustomError(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
cancelConfirmDialog(): void {
|
||||
this.confirmDialogIsOpen$.next(false);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
private makeAvvikelseSubmitData(
|
||||
genomforandeReferens: number,
|
||||
chosenReason: string,
|
||||
formData: AvvikelseFormData
|
||||
): AvvikelseRequestData {
|
||||
const avvikelseAlternativ: AvvikelseAlternativ = {
|
||||
avvikelseorsakskod: chosenReason,
|
||||
frageformular: formData.questions.map((question, index) => ({
|
||||
fraga: this.currentQuestions[index].id,
|
||||
svar: question,
|
||||
})),
|
||||
rapporteringsdatum: formData.reportingDate,
|
||||
};
|
||||
|
||||
return { genomforandeReferens, avvikelseAlternativ };
|
||||
}
|
||||
|
||||
private clearQuestions(): void {
|
||||
this.questionsFormArray.clear();
|
||||
this.currentQuestions = [];
|
||||
}
|
||||
|
||||
private addQuestionToForm(question: FragorForAvvikelser): void {
|
||||
// FormArray doesnt hold any IDs so we need to store these seperately and rebuild structure at submit
|
||||
this.currentQuestions.push(question);
|
||||
|
||||
this.questionsFormArray.push(
|
||||
new FormControl('', this.questionIsRequired(question) ? RequiredValidator('Frågan') : null)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,37 +9,37 @@ import { RouterModule } from '@angular/router';
|
||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||
import { ConfirmDialogModule } from '@msfa-shared/components/confirm-dialog/confirm-dialog.module';
|
||||
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
|
||||
import { DeltagareConfirmFormModule } from './components/deltagare-confirm-form/deltagare-confirm-form.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';
|
||||
import { DeltagareAvvikelseService } from './deltagare-avvikelse.service';
|
||||
import { DeltagareAvvikelserapportComponent } from './deltagare-avvikelserapport.component';
|
||||
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
|
||||
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
|
||||
import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input';
|
||||
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [DeltagareAvvikelseComponent],
|
||||
declarations: [DeltagareAvvikelserapportComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{ path: '', component: DeltagareAvvikelseComponent }]),
|
||||
RouterModule.forChild([{ path: '', component: DeltagareAvvikelserapportComponent }]),
|
||||
LayoutModule,
|
||||
ReactiveFormsModule,
|
||||
DigiNgFormRadiobuttonGroupModule,
|
||||
DigiNgFormDatepickerModule,
|
||||
DigiNgFormTextareaModule,
|
||||
DigiNgProgressProgressbarModule,
|
||||
DeltagareOrsaksFormModule,
|
||||
DeltagareFragorFormModule,
|
||||
DeltagareTimePickerModule,
|
||||
DeltagareConfirmFormModule,
|
||||
ReportLayoutModule,
|
||||
ConfirmDialogModule,
|
||||
BackLinkModule,
|
||||
DigiNgSkeletonBaseModule,
|
||||
DigiNgFormSelectModule,
|
||||
LoaderModule,
|
||||
DigiNgFormInputModule,
|
||||
DigiNgDialogModule,
|
||||
],
|
||||
providers: [DeltagareAvvikelseService],
|
||||
exports: [DeltagareAvvikelseComponent],
|
||||
providers: [DeltagareAvvikelserapportService],
|
||||
exports: [DeltagareAvvikelserapportComponent],
|
||||
})
|
||||
export class DeltagareAvvikelseModule {}
|
||||
export class DeltagareAvvikelserapportModule {}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
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 { AvvikelseApiService } from '@msfa-services/api/avvikelse-api.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeltagareAvvikelserapportService {
|
||||
fragorForAvvikelser$: Observable<FragorForAvvikelser[]> = this.avvikelseApiService
|
||||
.getFragorForAvvikelser$()
|
||||
.pipe(shareReplay(1));
|
||||
|
||||
getAvvikelseOrsaker$: Observable<OrsaksKoderAvvikelse[]> = this.avvikelseApiService.getOrsaksKoderAvvikelse$();
|
||||
|
||||
constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {}
|
||||
|
||||
createAvvikelse$(avvikelse: Avvikelse): Observable<unknown> {
|
||||
return this.avvikelseApiService.createAvvikelse$(avvikelse);
|
||||
}
|
||||
|
||||
fetchAvropInformation$(genomforandeReferens: number) {
|
||||
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,6 @@
|
||||
import { ReportType } from '@msfa-enums/report-type.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: ReportType.FRANVARO,
|
||||
},
|
||||
{
|
||||
label: 'Avvikelse',
|
||||
value: ReportType.AVVIKELSE,
|
||||
},
|
||||
];
|
||||
|
||||
export const dayOrPartOfDay: RadiobuttonModel[] = [
|
||||
{
|
||||
label: 'Heldag',
|
||||
@@ -2,10 +2,7 @@
|
||||
<msfa-report-layout
|
||||
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||
reportTitle="Gemensam planering"
|
||||
[totalAmountOfSteps]="totalAmountOfSteps"
|
||||
[currentStep]="currentStep"
|
||||
[avrop]="avrop"
|
||||
[submitted]="lastSubmittedGP$ | async"
|
||||
[isPeriodDate]="true"
|
||||
>
|
||||
<div class="gemensam-planering" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
|
||||
@@ -35,66 +32,44 @@
|
||||
id="gemensam-planering-form"
|
||||
>
|
||||
<msfa-loader *ngIf="submitLoading$ | async" type="absolute"></msfa-loader>
|
||||
<ng-container *ngIf="currentStep === 1">
|
||||
<digi-form-fieldset
|
||||
af-legend="Deltar arbetssökande på distans?"
|
||||
af-name="distance"
|
||||
af-form="gemensam-planering-form"
|
||||
>
|
||||
<digi-ng-form-radiobutton-group
|
||||
[afRadiobuttons]="distanceRadiobuttons"
|
||||
formControlName="distance"
|
||||
[afRequired]="true"
|
||||
[afRadiobuttonGroupDirection]="RadiobuttonGroupDirection.HORIZONTAL"
|
||||
></digi-ng-form-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
<digi-form-fieldset af-legend="Aktiviteter" af-name="aktivitetsIds" af-form="gemensam-planering-form">
|
||||
<p>
|
||||
Varje gemensam planering måste innehålla de två obligatoriska aktiviteterna samt en frivillig aktivitet
|
||||
som en del av det individuella stödet för varje deltagare.
|
||||
</p>
|
||||
<digi-form-fieldset
|
||||
af-legend="Deltar arbetssökande på distans?"
|
||||
af-name="distance"
|
||||
af-form="gemensam-planering-form"
|
||||
>
|
||||
<digi-ng-form-radiobutton-group
|
||||
[afRadiobuttons]="distanceRadiobuttons"
|
||||
formControlName="distance"
|
||||
[afRequired]="true"
|
||||
[afRadiobuttonGroupDirection]="RadiobuttonGroupDirection.HORIZONTAL"
|
||||
></digi-ng-form-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
<digi-form-fieldset af-legend="Aktiviteter" af-name="aktivitetsIds" af-form="gemensam-planering-form">
|
||||
<p>
|
||||
Varje gemensam planering måste innehålla de två obligatoriska aktiviteterna samt en frivillig aktivitet
|
||||
som en del av det individuella stödet för varje deltagare.
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="activities$ | async as activities; else loadingRef">
|
||||
<ul class="gemensam-planering__activity-list">
|
||||
<li class="gemensam-planering__activity-item" *ngFor="let activity of activities;">
|
||||
<digi-form-checkbox
|
||||
[afLabel]="activity.name + (isActivityObligatory(activity.id) ? ' (obligatorisk)' : '')"
|
||||
[afValue]="activity.id"
|
||||
[afValidation]="showActivityAsInvalid(activity.id) ? 'error' : 'neutral'"
|
||||
[afChecked]="isActivityChecked(activity.id) || isActivityObligatory(activity.id)"
|
||||
(afOnChange)="updateActivityIds(activity.id, $event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<digi-form-validation-message
|
||||
*ngIf="shouldValidate && gpFormGroup.errors?.activityIds"
|
||||
af-variation="error"
|
||||
>{{gpFormGroup.errors.activityIds}}</digi-form-validation-message
|
||||
>
|
||||
</ng-container>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<ng-container *ngIf="activities$ | async as activities; else loadingRef">
|
||||
<ul class="gemensam-planering__activity-list">
|
||||
<li class="gemensam-planering__activity-item" *ngFor="let activity of activities;">
|
||||
<digi-form-checkbox
|
||||
[afLabel]="activity.name + (isActivityObligatory(activity.id) ? ' (obligatorisk)' : '')"
|
||||
[afValue]="activity.id"
|
||||
[afValidation]="showActivityAsInvalid(activity.id) ? 'error' : 'neutral'"
|
||||
[afChecked]="isActivityChecked(activity.id) || isActivityObligatory(activity.id)"
|
||||
(afOnChange)="updateActivityIds(activity.id, $event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<digi-form-validation-message
|
||||
*ngIf="shouldValidate && gpFormGroup.errors?.activityIds"
|
||||
af-variation="error"
|
||||
>{{gpFormGroup.errors.activityIds}}</digi-form-validation-message
|
||||
>
|
||||
</ng-container>
|
||||
</digi-form-fieldset>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentStep === 2">
|
||||
<dl>
|
||||
<dt>Deltar arbetssökande på distans?</dt>
|
||||
<dd>{{gpFormGroup.value.distance ? 'Ja' : 'Nej'}}</dd>
|
||||
<dt>Aktiviteter</dt>
|
||||
<dd>
|
||||
<ul class="gemensam-planering__activity-list" *ngFor="let activity of activities$ | async">
|
||||
<li
|
||||
class="gemensam-planering__activity-item"
|
||||
*ngIf="activityIdsFormArray.value.includes(activity.id)"
|
||||
>
|
||||
<digi-icon-check-circle
|
||||
class="msfa__digi-icon gemensam-planering__activity-check"
|
||||
aria-hidden="true"
|
||||
></digi-icon-check-circle>
|
||||
{{activity.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</ng-container>
|
||||
<footer class="gemensam-planering__footer">
|
||||
<digi-notification-alert
|
||||
*ngIf="error$ | async as error"
|
||||
@@ -106,29 +81,37 @@
|
||||
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
|
||||
</digi-notification-alert>
|
||||
<div class="gemensam-planering__cta-wrapper">
|
||||
<ng-container *ngIf="currentStep > 1">
|
||||
<digi-button
|
||||
class="gemensam-planering__step-buttons-wrapper--space-right"
|
||||
af-variation="secondary"
|
||||
af-size="m"
|
||||
(afOnClick)="goToStep1()"
|
||||
>Tillbaka</digi-button
|
||||
>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentStep === 1">
|
||||
<digi-button af-size="m" (afOnClick)="goToPreview()">Förhandsgranska</digi-button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="currentStep === 2">
|
||||
<digi-button af-type="submit" af-size="m">Bekräfta och skicka in</digi-button>
|
||||
</ng-container>
|
||||
<digi-button af-type="submit" af-size="m">Bekräfta och skicka in</digi-button>
|
||||
<msfa-back-link [showIcon]="false" [asButton]="true" [route]="['/deltagare/'+ genomforandeReferens]"
|
||||
>Avbryt</msfa-back-link
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
<msfa-confirm-dialog
|
||||
[openConfirmDialog]="confirmDialogOpen"
|
||||
reportToConfirm="gemensam planering"
|
||||
[dialogOpen]="confirmDialogOpen"
|
||||
dialogTitle="Förhandsgranska och bekräfta"
|
||||
ariaLabel="Förhandsgranska och bekräfta Avvikelserapport (frånvaro)"
|
||||
primaryButtonText="Bekräfta och skicka in"
|
||||
(confirmDialogChanged)="closeConfirmDialogAndProceed($event, genomforandeReferens)"
|
||||
></msfa-confirm-dialog>
|
||||
>
|
||||
<dl>
|
||||
<dt>Deltar arbetssökande på distans?</dt>
|
||||
<dd>{{gpFormGroup.value.distance ? 'Ja' : 'Nej'}}</dd>
|
||||
<dt>Aktiviteter</dt>
|
||||
<dd>
|
||||
<ul class="gemensam-planering__activity-list" *ngFor="let activity of activities$ | async">
|
||||
<li class="gemensam-planering__activity-item" *ngIf="activityIdsFormArray.value.includes(activity.id)">
|
||||
<digi-icon-check-circle
|
||||
class="msfa__digi-icon gemensam-planering__activity-check"
|
||||
aria-hidden="true"
|
||||
></digi-icon-check-circle>
|
||||
{{activity.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</msfa-confirm-dialog>
|
||||
</ng-template>
|
||||
</div>
|
||||
</msfa-report-layout>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.gemensam-planering {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
|
||||
&__confirmation,
|
||||
&__form {
|
||||
position: relative;
|
||||
@@ -26,10 +28,6 @@
|
||||
color: var(--digi--ui--color--border--success);
|
||||
}
|
||||
|
||||
&__alert {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -40,15 +38,4 @@
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
.digi-form-fieldset {
|
||||
margin: 0;
|
||||
|
||||
&__legend {
|
||||
margin-bottom: var(--digi--layout--gutter--s);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import { GemensamPlaneringValidator } from './gemensam-planering.validator';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DeltagareGemensamPlaneringComponent {
|
||||
totalAmountOfSteps = 2;
|
||||
currentStep = 1;
|
||||
obligatoryActivityIds = [165, 188];
|
||||
shouldValidate = false;
|
||||
RadiobuttonGroupDirection = RadiobuttonGroupDirection;
|
||||
@@ -109,22 +107,12 @@ export class DeltagareGemensamPlaneringComponent {
|
||||
}
|
||||
}
|
||||
|
||||
goToPreview(): void {
|
||||
openConfirmDialog(): void {
|
||||
this.shouldValidate = true;
|
||||
|
||||
if (this.gpFormGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentStep = 2;
|
||||
}
|
||||
|
||||
goToStep1(): void {
|
||||
this.shouldValidate = false;
|
||||
this.currentStep = 1;
|
||||
}
|
||||
|
||||
openConfirmDialog(): void {
|
||||
this.confirmDialogOpen = true;
|
||||
}
|
||||
|
||||
@@ -144,7 +132,6 @@ export class DeltagareGemensamPlaneringComponent {
|
||||
.postGemensamPlanering(mapGemensamPlaneringToGemensamPlaneringPostRequest(postRequest))
|
||||
.then(() => {
|
||||
this._lastSubmittedGP$.next(new Date());
|
||||
this.currentStep = 3;
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this._error$.next(new CustomError({ error, message: error.message, type: ErrorType.API }));
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
<msfa-layout>
|
||||
<msfa-report-layout
|
||||
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||
[avrop]="avrop"
|
||||
description="Här rapporterar du deltagarens frånvaro i tjänsten."
|
||||
reportTitle="Avvikelserapport (frånvaro)"
|
||||
>
|
||||
<div class="franvaro-report" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
|
||||
<digi-notification-alert
|
||||
*ngIf="maxDate < avrop.startDate; else reportRef"
|
||||
af-variation="warning"
|
||||
af-heading="Kan inte skapa Avvikelserapport (frånvaro)"
|
||||
>
|
||||
<p>Det går inte att rapportera frånvaro eftersom tjänsten inte har startad ännu.</p>
|
||||
</digi-notification-alert>
|
||||
|
||||
<ng-template #reportRef>
|
||||
<div
|
||||
class="franvaro-report__confirmation"
|
||||
*ngIf="lastSubmittedFranvaroReport$ | async as lastSubmittedFranvaroReport; else formRef"
|
||||
>
|
||||
<digi-notification-alert
|
||||
class="franvaro-report__alert"
|
||||
af-variation="success"
|
||||
af-heading="Allt gick bra"
|
||||
af-heading-level="h3"
|
||||
>
|
||||
<p>Avvikelserapport (frånvaro) för deltagare {{avrop.fullName}} är nu inskickad till Arbetsförmedlingen.</p>
|
||||
<dl>
|
||||
<dt>Datum</dt>
|
||||
<dd>
|
||||
{{lastSubmittedFranvaroReport | date:'longDate'}} kl {{lastSubmittedFranvaroReport | date:'shortTime'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</digi-notification-alert>
|
||||
<msfa-back-link [route]="['/deltagare/'+ genomforandeReferens]">Tillbaka till deltagaren</msfa-back-link>
|
||||
</div>
|
||||
<ng-template #formRef>
|
||||
<form
|
||||
class="franvaro-report__form"
|
||||
[formGroup]="franvaroFormGroup"
|
||||
(ngSubmit)="openConfirmDialog()"
|
||||
id="franvaro-report-form"
|
||||
>
|
||||
<msfa-loader *ngIf="submitLoading$ | async" type="absolute"></msfa-loader>
|
||||
<div class="franvaro-report__form-item">
|
||||
<digi-ng-form-select
|
||||
*ngIf="reasons$ | async as reasons; else loadingRef"
|
||||
[formControl]="reasonFormControl"
|
||||
afLabel="Orsak till frånvaro"
|
||||
afPlaceholder="Välj orsak till frånvaro"
|
||||
[afSelectItems]="reasons"
|
||||
[afRequired]="true"
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="formControlIsInvalid(['reason'])"
|
||||
></digi-ng-form-select>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['reason'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.reason"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="reasonFormControl.value">
|
||||
<ng-container *ngIf="showOtherKnownReasonsSelect">
|
||||
<div
|
||||
class="franvaro-report__form-item"
|
||||
*ngIf="otherKnownReasons$ | async as otherKnownReasons; else loadingRef"
|
||||
>
|
||||
<digi-ng-form-select
|
||||
[formControl]="otherKnownReasonFormControl"
|
||||
afLabel="Känd orsak"
|
||||
afPlaceholder="Välj känd orsak"
|
||||
[afSelectItems]="otherKnownReasons"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalid]="formControlIsInvalid(['otherKnownReason'])"
|
||||
></digi-ng-form-select>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['otherKnownReason'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.otherKnownReason"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="franvaro-report__form-item" *ngIf="showKnownReasonTextArea">
|
||||
<digi-ng-form-textarea
|
||||
[afDisableValidStyle]="true"
|
||||
[afInvalid]="formControlIsInvalid(['knownReasonComment'])"
|
||||
[afMaxLength]="2000"
|
||||
[afRequired]="true"
|
||||
afLabel="Beskriv frånvaro"
|
||||
afSize="s"
|
||||
[formControl]="knownReasonCommentFormControl"
|
||||
></digi-ng-form-textarea>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['knownReasonComment'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.knownReasonComment"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="franvaro-report__form-item">
|
||||
<digi-ng-form-datepicker
|
||||
[afDisableValidStyle]="true"
|
||||
[afMinDate]="avrop.startDate"
|
||||
[afMaxDate]="maxDate"
|
||||
[afInvalid]="formControlIsInvalid(['date'])"
|
||||
[afRequired]="true"
|
||||
afLabel="Välj dag för frånvaro"
|
||||
[formControl]="dateFormControl"
|
||||
></digi-ng-form-datepicker>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['date'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.date"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<digi-form-fieldset af-legend="Hel eller del av dag" af-name="wholeDay" af-form="franvaro-report-form">
|
||||
<digi-ng-form-radiobutton-group
|
||||
[afRadiobuttons]="wholeDayOrPartOfDayRadiobuttons"
|
||||
[afRequired]="true"
|
||||
[formControl]="wholeDayFormControl"
|
||||
></digi-ng-form-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<digi-form-fieldset
|
||||
*ngIf="showTimePickers"
|
||||
af-legend="Välj tid för frånvaro"
|
||||
af-name="time"
|
||||
af-form="franvaro-report-form"
|
||||
>
|
||||
<div class="franvaro-report__time-pickers">
|
||||
<div class="franvaro-report__time-picker">
|
||||
<digi-ng-form-input
|
||||
afLabel="Ange starttid"
|
||||
[formControl]="startTimeFormControl"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalid]="formControlIsInvalid(['startTime', 'expectedEndTimeIsBeforeStartTime'])"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['startTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.startTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
<div class="franvaro-report__time-picker">
|
||||
<digi-ng-form-input
|
||||
afLabel="Ange sluttid"
|
||||
[formControl]="endTimeFormControl"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalid]="formControlIsInvalid(['endTime', 'expectedEndTimeIsBeforeStartTime'])"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['endTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.endTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['expectedEndTimeIsBeforeStartTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.expectedEndTimeIsBeforeStartTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<digi-form-fieldset
|
||||
af-legend="Välj tid förväntad närvaro"
|
||||
af-name="expectedPresence"
|
||||
af-form="franvaro-report-form"
|
||||
>
|
||||
<div class="franvaro-report__time-pickers">
|
||||
<div class="franvaro-report__time-picker">
|
||||
<digi-ng-form-input
|
||||
afLabel="Ange starttid"
|
||||
[formControl]="expectedPresenceStartTimeFormControl"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalid]="formControlIsInvalid(['expectedPresenceStartTime', 'expectedPresenceEndTimeIsBeforeStartTime'])"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['expectedPresenceStartTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.expectedPresenceStartTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
<div class="franvaro-report__time-picker">
|
||||
<digi-ng-form-input
|
||||
afLabel="Ange sluttid"
|
||||
[formControl]="expectedPresenceEndTimeFormControl"
|
||||
[afDisableValidStyle]="true"
|
||||
[afRequired]="true"
|
||||
[afInvalid]="formControlIsInvalid(['expectedPresenceEndTime', 'expectedPresenceEndTimeIsBeforeStartTime'])"
|
||||
afType="time"
|
||||
></digi-ng-form-input>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['expectedPresenceEndTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.expectedPresenceEndTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-ng-form-validation-message
|
||||
*ngIf="formControlIsInvalid(['expectedPresenceEndTimeIsBeforeStartTime'])"
|
||||
class="franvaro-report__validation-message"
|
||||
[afPositive]="false"
|
||||
[afValidationText]="formErrors.expectedPresenceEndTimeIsBeforeStartTime"
|
||||
></digi-ng-form-validation-message>
|
||||
</div>
|
||||
</digi-form-fieldset>
|
||||
</ng-container>
|
||||
|
||||
<footer class="franvaro-report__footer">
|
||||
<digi-notification-alert
|
||||
*ngIf="error$ | async as error"
|
||||
class="franvaro-report__alert"
|
||||
af-variation="danger"
|
||||
af-heading="Någonting gick fel"
|
||||
>
|
||||
<p>Kunde inte spara Avvikelserapport (frånvaro). Ladda om sidan och försök igen.</p>
|
||||
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
|
||||
</digi-notification-alert>
|
||||
<div class="franvaro-report__cta-wrapper">
|
||||
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
|
||||
<msfa-back-link [showIcon]="false" [asButton]="true" [route]="['/deltagare/'+ genomforandeReferens]"
|
||||
>Avbryt</msfa-back-link
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
<msfa-confirm-dialog
|
||||
[dialogOpen]="confirmDialogOpen$ | async"
|
||||
dialogTitle="Förhandsgranska och bekräfta"
|
||||
ariaLabel="Förhandsgranska och bekräfta Avvikelserapport (frånvaro)"
|
||||
primaryButtonText="Bekräfta och skicka in"
|
||||
(confirmDialogChanged)="closeConfirmDialogAndProceed($event, genomforandeReferens)"
|
||||
>
|
||||
<dl *ngIf="reasons$ | async as reasons">
|
||||
<ng-container *ngIf="reasons$ | async as reasons">
|
||||
<dt>Orsak till frånvaro</dt>
|
||||
<dd>{{getReasonNameFromValue(reasons, reasonFormControl.value)}}</dd>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showOtherKnownReasonsSelect">
|
||||
<ng-container *ngIf="otherKnownReasons$ | async as otherKnownReasons">
|
||||
<dt>Annan känd orsak</dt>
|
||||
<dd>{{getReasonNameFromValue(otherKnownReasons, otherKnownReasonFormControl.value)}}</dd>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showKnownReasonTextArea">
|
||||
<dt>Beskrivning för frånvaro</dt>
|
||||
<dd>{{knownReasonCommentFormControl.value}}</dd>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<dt>Datum</dt>
|
||||
<dd><digi-typography-time [afDateTime]="dateFormControl.value"></digi-typography-time></dd>
|
||||
<dt>Hel eller del av dag</dt>
|
||||
<dd>{{dayOrPartOfDayFromValue}}</dd>
|
||||
<ng-container *ngIf="showTimePickers">
|
||||
<dt>Tid för frånvaro</dt>
|
||||
<dd>{{startTimeFormControl.value}} - {{endTimeFormControl.value}}</dd>
|
||||
</ng-container>
|
||||
<dt>Tid för förväntad närvaro</dt>
|
||||
<dd>{{expectedPresenceStartTimeFormControl.value}} - {{expectedPresenceEndTimeFormControl.value}}</dd>
|
||||
</dl>
|
||||
</msfa-confirm-dialog>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</msfa-report-layout>
|
||||
</msfa-layout>
|
||||
|
||||
<ng-template #skeletonRef>
|
||||
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för Avvikelserapport (frånvaro)"></digi-ng-skeleton-base>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #loadingRef>
|
||||
<msfa-loader type="padded"></msfa-loader>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,39 @@
|
||||
@import 'variables/gutters';
|
||||
@import 'variables/z-index';
|
||||
|
||||
.franvaro-report {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
|
||||
&__confirmation,
|
||||
&__form {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
z-index: $msfa__z-index-default;
|
||||
}
|
||||
|
||||
&__time-pickers {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__time-picker {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__cta-wrapper {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__validation-message {
|
||||
margin-top: var(--digi--layout--gutter--s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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 { FranvaroReportComponent } from './franvaro-report.component';
|
||||
|
||||
describe('FranvaroReportComponent', () => {
|
||||
let component: FranvaroReportComponent;
|
||||
let fixture: ComponentFixture<FranvaroReportComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [FranvaroReportComponent, LayoutComponent],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FranvaroReportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,187 @@
|
||||
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ANNAN_KAND_ORSAK_ID, ANNAN_ORSAK_ID } from '@msfa-constants/franvaro-reasons';
|
||||
import { ConfirmDialog } from '@msfa-enums/confirm-dialog.enum';
|
||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { Franvaro } from '@msfa-models/franvaro.model';
|
||||
import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
|
||||
import { dateToIsoString } from '@msfa-utils/format-to-date.util';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { FranvaroReportService } from './franvaro-report.service';
|
||||
import { FranvaroReportValidator } from './franvaro-report.validator';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-franvaro-report',
|
||||
templateUrl: './franvaro-report.component.html',
|
||||
styleUrls: ['./franvaro-report.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FranvaroReportComponent {
|
||||
maxDate = new Date();
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
|
||||
franvaroFormGroup = new FormGroup(
|
||||
{
|
||||
reason: new FormControl(null),
|
||||
otherKnownReason: new FormControl(null),
|
||||
knownReasonComment: new FormControl(''),
|
||||
date: new FormControl(new Date()),
|
||||
wholeDay: new FormControl(true),
|
||||
startTime: new FormControl(null),
|
||||
endTime: new FormControl(null),
|
||||
expectedPresenceStartTime: new FormControl(null),
|
||||
expectedPresenceEndTime: new FormControl(null),
|
||||
},
|
||||
[FranvaroReportValidator.isFranvaroReportValid()]
|
||||
);
|
||||
|
||||
error$ = new BehaviorSubject<CustomError>(null);
|
||||
submitLoading$ = new BehaviorSubject<boolean>(false);
|
||||
lastSubmittedFranvaroReport$ = new BehaviorSubject<Date>(null);
|
||||
currentGenomforandeReferens$: Observable<string> = this.activatedRoute.params.pipe(
|
||||
map(params => params.genomforandeReferens as string)
|
||||
);
|
||||
avrop$: Observable<Avrop> = this.currentGenomforandeReferens$.pipe(
|
||||
switchMap(genomforandeReferens => this.franvaroReportService.fetchAvropInformation$(+genomforandeReferens)),
|
||||
shareReplay(1)
|
||||
);
|
||||
reasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportService.reasons$;
|
||||
otherKnownReasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportService.otherKnownReasons$;
|
||||
|
||||
wholeDayOrPartOfDayRadiobuttons: RadiobuttonModel[] = [
|
||||
{ label: 'Heldag', value: true },
|
||||
{ label: 'Del av dag', value: false },
|
||||
];
|
||||
|
||||
constructor(private franvaroReportService: FranvaroReportService, private activatedRoute: ActivatedRoute) {}
|
||||
|
||||
get showOtherKnownReasonsSelect(): boolean {
|
||||
return this.reasonFormControl.value === ANNAN_KAND_ORSAK_ID;
|
||||
}
|
||||
|
||||
get showKnownReasonTextArea(): boolean {
|
||||
return this.otherKnownReasonFormControl.value === ANNAN_ORSAK_ID;
|
||||
}
|
||||
|
||||
get showTimePickers(): boolean {
|
||||
return !this.wholeDayFormControl.value;
|
||||
}
|
||||
|
||||
get formErrors(): { [key: string]: string } {
|
||||
return this.franvaroFormGroup.errors || {};
|
||||
}
|
||||
|
||||
get reasonFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('reason') as FormControl;
|
||||
}
|
||||
get dateFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('date') as FormControl;
|
||||
}
|
||||
get wholeDayFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('wholeDay') as FormControl;
|
||||
}
|
||||
get startTimeFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('startTime') as FormControl;
|
||||
}
|
||||
get endTimeFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('endTime') as FormControl;
|
||||
}
|
||||
get expectedPresenceStartTimeFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('expectedPresenceStartTime') as FormControl;
|
||||
}
|
||||
get expectedPresenceEndTimeFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('expectedPresenceEndTime') as FormControl;
|
||||
}
|
||||
get otherKnownReasonFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('otherKnownReason') as FormControl;
|
||||
}
|
||||
get knownReasonCommentFormControl(): FormControl {
|
||||
return this.franvaroFormGroup.get('knownReasonComment') as FormControl;
|
||||
}
|
||||
|
||||
getReasonNameFromValue(reasons: OrsaksKoderFranvaro[], value: string): string {
|
||||
return reasons.find(reason => reason.value.toString() === value)?.name;
|
||||
}
|
||||
|
||||
get dayOrPartOfDayFromValue(): string {
|
||||
return this.wholeDayFormControl.value ? 'Heldag' : 'Del av dag';
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControlNames: string[]): boolean {
|
||||
return (
|
||||
formControlNames.some(formControlName => this.formErrors[formControlName]) && this.shouldValidate$.getValue()
|
||||
);
|
||||
}
|
||||
|
||||
openConfirmDialog(): void {
|
||||
this.shouldValidate$.next(true);
|
||||
if (this.franvaroFormGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.confirmDialogOpen$.next(true);
|
||||
}
|
||||
|
||||
closeConfirmDialogAndProceed(confirmDialogAnswer: ConfirmDialog, genomforandeReferens: number): void {
|
||||
this.confirmDialogOpen$.next(true);
|
||||
|
||||
if (confirmDialogAnswer === ConfirmDialog.ACCEPTED) {
|
||||
void this.postFranvaroReport(genomforandeReferens);
|
||||
}
|
||||
}
|
||||
|
||||
async postFranvaroReport(genomforandeReferens: number): Promise<void> {
|
||||
this.submitLoading$.next(true);
|
||||
|
||||
const {
|
||||
reason,
|
||||
date,
|
||||
wholeDay,
|
||||
startTime,
|
||||
endTime,
|
||||
otherKnownReason,
|
||||
knownReasonComment,
|
||||
expectedPresenceStartTime,
|
||||
expectedPresenceEndTime,
|
||||
} = this.franvaroFormGroup.value as Franvaro;
|
||||
|
||||
const postRequest: FranvaroRequestData = {
|
||||
genomforandeReferens,
|
||||
franvaro: {
|
||||
avvikelseOrsaksKod: reason,
|
||||
datum: dateToIsoString(date),
|
||||
heldag: wholeDay,
|
||||
startTid: this.showTimePickers ? startTime : '0:00', // BÄR doesn't accept empty string or null
|
||||
slutTid: this.showTimePickers ? endTime : '23:59', // BÄR doesn't accept empty string or null
|
||||
alternativForKandaOrsaker: this.showOtherKnownReasonsSelect
|
||||
? {
|
||||
typ: otherKnownReason,
|
||||
motivering: this.showKnownReasonTextArea ? knownReasonComment : '',
|
||||
}
|
||||
: null,
|
||||
forvantadNarvaro: {
|
||||
startTid: expectedPresenceStartTime,
|
||||
slutTid: expectedPresenceEndTime,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return this.franvaroReportService
|
||||
.postFranvaroReport(postRequest)
|
||||
.then(() => {
|
||||
this.lastSubmittedFranvaroReport$.next(new Date());
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this.error$.next(new CustomError({ error, message: error.message, type: ErrorType.API }));
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading$.next(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||
import { DigiNgFormInputModule } from '@af/digi-ng/_form/form-input';
|
||||
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
import { DigiNgFormTextareaModule } from '@af/digi-ng/_form/form-textarea';
|
||||
import { DigiNgFormValidationMessageModule } from '@af/digi-ng/_form/form-validation-message';
|
||||
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||
import { ConfirmDialogModule } from '@msfa-shared/components/confirm-dialog/confirm-dialog.module';
|
||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
|
||||
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
|
||||
import { FranvaroReportComponent } from './franvaro-report.component';
|
||||
import { FranvaroReportService } from './franvaro-report.service';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [FranvaroReportComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{ path: '', component: FranvaroReportComponent }]),
|
||||
ReactiveFormsModule,
|
||||
LayoutModule,
|
||||
ReportLayoutModule,
|
||||
LoaderModule,
|
||||
BackLinkModule,
|
||||
ConfirmDialogModule,
|
||||
DigiNgFormSelectModule,
|
||||
DigiNgFormDatepickerModule,
|
||||
DigiNgFormRadiobuttonGroupModule,
|
||||
DigiNgSkeletonBaseModule,
|
||||
DigiNgFormTextareaModule,
|
||||
DigiNgFormInputModule,
|
||||
DigiNgFormValidationMessageModule,
|
||||
],
|
||||
providers: [FranvaroReportService],
|
||||
exports: [FranvaroReportComponent],
|
||||
})
|
||||
export class FranvaroReportModule {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
|
||||
import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
|
||||
import { FranvaroReportApiService } from '@msfa-services/api/franvaro-report.api.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class FranvaroReportService {
|
||||
public reasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportApiService.fetchReasons$();
|
||||
public otherKnownReasons$: Observable<
|
||||
OrsaksKoderFranvaro[]
|
||||
> = this.franvaroReportApiService.fetchOtherKnownReasons$();
|
||||
|
||||
constructor(private franvaroReportApiService: FranvaroReportApiService) {}
|
||||
|
||||
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
|
||||
return this.franvaroReportApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
|
||||
public async postFranvaroReport(requestData: FranvaroRequestData): Promise<void> {
|
||||
return this.franvaroReportApiService.postFranvaroReport$(requestData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { ANNAN_KAND_ORSAK_ID, ANNAN_ORSAK_ID } from '@msfa-constants/franvaro-reasons';
|
||||
import { Franvaro } from '@msfa-models/franvaro.model';
|
||||
|
||||
const TIME_REGEX = /^([0-1]?[0-9]|2[0-4]):([0-5][0-9])(:[0-5][0-9])?$/;
|
||||
|
||||
function isTimeValid(value: string): boolean {
|
||||
return TIME_REGEX.test(value);
|
||||
}
|
||||
|
||||
export class FranvaroReportValidator {
|
||||
static isFranvaroReportValid(): ValidatorFn {
|
||||
return (c: AbstractControl): { [key: string]: string } => {
|
||||
let errors: { [key: string]: string } = null;
|
||||
const {
|
||||
reason,
|
||||
date,
|
||||
wholeDay,
|
||||
startTime,
|
||||
endTime,
|
||||
otherKnownReason,
|
||||
knownReasonComment,
|
||||
expectedPresenceStartTime,
|
||||
expectedPresenceEndTime,
|
||||
} = c.value as Franvaro;
|
||||
|
||||
if (!reason) {
|
||||
errors = {
|
||||
...errors,
|
||||
reason: 'Orsak till frånvaro måste väljas',
|
||||
};
|
||||
}
|
||||
if (reason === ANNAN_KAND_ORSAK_ID) {
|
||||
if (!otherKnownReason) {
|
||||
errors = {
|
||||
...errors,
|
||||
otherKnownReason: 'Känd orsak måste väljas',
|
||||
};
|
||||
} else if (otherKnownReason === ANNAN_ORSAK_ID && !knownReasonComment) {
|
||||
errors = {
|
||||
...errors,
|
||||
knownReasonComment: 'Beskrivning av frånvaro är obligatorisk',
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!date) {
|
||||
errors = {
|
||||
...errors,
|
||||
date: 'Dag för frånvaro måste väljas',
|
||||
};
|
||||
}
|
||||
if (!wholeDay) {
|
||||
if (!startTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
startTime: 'Starttid för frånvaro måste väljas',
|
||||
};
|
||||
} else if (!isTimeValid(startTime)) {
|
||||
errors = {
|
||||
...errors,
|
||||
startTime: 'Felaktig tid för starttid (HH:MM)',
|
||||
};
|
||||
}
|
||||
if (!endTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
endTime: 'Sluttid för frånvaro måste väljas',
|
||||
};
|
||||
} else if (!isTimeValid(endTime)) {
|
||||
errors = {
|
||||
...errors,
|
||||
endTime: 'Felaktig tid för sluttid (HH:MM)',
|
||||
};
|
||||
}
|
||||
if (endTime && startTime && endTime < startTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedEndTimeIsBeforeStartTime: 'Sluttid för frånvaro får inte vara före starttid',
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!expectedPresenceStartTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedPresenceStartTime: 'Starttid för förväntad närvaro måste väljas',
|
||||
};
|
||||
} else if (!isTimeValid(expectedPresenceStartTime)) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedPresenceStartTime: 'Felaktig tid för starttid för förväntad närvaro (HH:MM)',
|
||||
};
|
||||
}
|
||||
if (!expectedPresenceEndTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedPresenceEndTime: 'Sluttid för förväntad närvaro måste väljas',
|
||||
};
|
||||
} else if (!isTimeValid(expectedPresenceEndTime)) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedPresenceEndTime: 'Felaktig tid för sluttid för förväntad närvaro (HH:MM)',
|
||||
};
|
||||
}
|
||||
|
||||
if (expectedPresenceEndTime && expectedPresenceStartTime && expectedPresenceEndTime < expectedPresenceStartTime) {
|
||||
errors = {
|
||||
...errors,
|
||||
expectedPresenceEndTimeIsBeforeStartTime: 'Sluttid för förväntad närvaro får inte vara före starttid',
|
||||
};
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<a class="back-link" [routerLink]="route">
|
||||
<msfa-icon [icon]="iconType.ARROW_LEFT"></msfa-icon>
|
||||
<a [ngClass]="backLinkClass" [routerLink]="route">
|
||||
<msfa-icon *ngIf="showIcon" [icon]="iconType.ARROW_LEFT"></msfa-icon>
|
||||
<ng-content></ng-content>
|
||||
</a>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
@import 'mixins/buttons';
|
||||
@import 'mixins/link';
|
||||
|
||||
.back-link {
|
||||
@include msfa__link(true);
|
||||
&--link {
|
||||
@include msfa__link(true);
|
||||
}
|
||||
|
||||
&--button {
|
||||
@include msfa__button('secondary');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,17 @@ import { IconType } from '@msfa-enums/icon-type.enum';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BackLinkComponent {
|
||||
private readonly _defaultClass = 'back-link';
|
||||
@Input() route: string[];
|
||||
@Input() showIcon = true;
|
||||
@Input() asButton = false;
|
||||
iconType = IconType;
|
||||
|
||||
get backLinkClass(): string {
|
||||
if (this.asButton) {
|
||||
return `${this._defaultClass} ${this._defaultClass}--button`;
|
||||
}
|
||||
|
||||
return `${this._defaultClass} ${this._defaultClass}--link`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<digi-ng-dialog
|
||||
*ngIf="openConfirmDialog"
|
||||
[afActive]="openConfirmDialog"
|
||||
(afOnPrimaryClick)="sendRequest()"
|
||||
(afOnSecondaryClick)="closeConfirmDialog()"
|
||||
(afOnInactive)="closeConfirmDialog()"
|
||||
afHeading="Bekräfta"
|
||||
*ngIf="dialogOpen"
|
||||
[afActive]="dialogOpen"
|
||||
(afOnPrimaryClick)="closeDialog(true)"
|
||||
(afOnSecondaryClick)="closeDialog(false)"
|
||||
(afOnInactive)="closeDialog(false)"
|
||||
[afHeading]="dialogTitle"
|
||||
afHeadingLevel="h2"
|
||||
[afAriaLabel]="'Bekräfta att skicka in en ' + reportToConfirm"
|
||||
afPrimaryButtonText="Skicka"
|
||||
afSecondaryButtonText="Avbryt"
|
||||
[afAriaLabel]="ariaLabel"
|
||||
[afPrimaryButtonText]="primaryButtonText"
|
||||
[afSecondaryButtonText]="secondaryButtonText"
|
||||
>
|
||||
<p>Är du säker på att du vill skicka in en {{reportToConfirm}}?</p>
|
||||
<ng-content></ng-content>
|
||||
</digi-ng-dialog>
|
||||
|
||||
@@ -8,17 +8,15 @@ import { ConfirmDialog } from '@msfa-enums/confirm-dialog.enum';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ConfirmDialogComponent {
|
||||
@Input() openConfirmDialog: boolean;
|
||||
@Input() reportToConfirm: string;
|
||||
@Input() dialogOpen: boolean;
|
||||
@Input() dialogTitle = 'Bekräfta';
|
||||
@Input() ariaLabel = 'Bekräfta för att gå vidare';
|
||||
@Input() primaryButtonText = 'Skicka';
|
||||
@Input() secondaryButtonText = 'Avbryt';
|
||||
@Output() confirmDialogChanged = new EventEmitter<ConfirmDialog>();
|
||||
|
||||
sendRequest(): void {
|
||||
this.openConfirmDialog = false;
|
||||
this.confirmDialogChanged.emit(ConfirmDialog.ACCEPTED);
|
||||
}
|
||||
|
||||
closeConfirmDialog(): void {
|
||||
this.openConfirmDialog = false;
|
||||
this.confirmDialogChanged.emit(ConfirmDialog.DISMISSED);
|
||||
closeDialog(confirmed: boolean): void {
|
||||
this.dialogOpen = false;
|
||||
this.confirmDialogChanged.emit(confirmed ? ConfirmDialog.ACCEPTED : ConfirmDialog.DISMISSED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const ANNAN_KAND_ORSAK_ID = '18';
|
||||
export const ANNAN_ORSAK_ID = '5';
|
||||
@@ -1,7 +1,8 @@
|
||||
export const DELTAGARE_REPORTING_ROUTES = {
|
||||
'gemensam-planering': 'Gemensam planering',
|
||||
'periodisk-redovisning': 'Periodisk redovisning',
|
||||
avvikelserapport: 'Avvikelserapport',
|
||||
franvarorapport: 'Avvikelserapport (frånvaro)',
|
||||
avvikelserapport: 'Avvikelserapport (avvikelse)',
|
||||
};
|
||||
|
||||
export const NAVIGATION = {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export const EMAIL_REGEX = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/;
|
||||
export const ISO_DATE_NO_TIME = /^\d{4}[\-\/\s]?((((0[13578])|(1[02]))[\-\/\s]?(([0-2][0-9])|(3[01])))|(((0[469])|(11))[\-\/\s]?(([0-2][0-9])|(30)))|(02[\-\/\s]?[0-2][0-9]))$/;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum AvvikelseOrsaksKodEnum {
|
||||
TackatNejTillInsatsEllerAktivitet = 19,
|
||||
TackatNejTillErbjudetArbete = 20,
|
||||
KanInteTillGodoGoraSigProgrammet = 21,
|
||||
MisskottSigEllerStortVerksamheten = 22,
|
||||
SerTillAttErbjudetArbeteInteKommerTillStand = 28
|
||||
}
|
||||
@@ -2,6 +2,6 @@ export enum ReportType {
|
||||
FRANVARO = 'franvaro',
|
||||
AVVIKELSE = 'avvikelse',
|
||||
GemensamPlanering = 'Gemensam planering',
|
||||
Franvaro = 'Frånvaro',
|
||||
Avvikelse = 'Avvikelse',
|
||||
Avvikelse = 'Avvikelserapport (avvikelse)',
|
||||
Franvaro = 'Avvikelserapport (frånvaro)',
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface AvropResponse {
|
||||
sparNamn: string;
|
||||
handledareCiamUserId: string;
|
||||
handledare: string;
|
||||
recievedTimestamp: string;
|
||||
recievedTimestamp: Date;
|
||||
}
|
||||
|
||||
export interface AvropApiResponse {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export interface OrsaksKoderFranvaroResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
state: number;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface Avrop extends AvropCompact {
|
||||
utforandeVerksamhet: string; // utforandeverksamhet
|
||||
handledareCiamUserId: string;
|
||||
handledare: string;
|
||||
recievedTimestamp: Date;
|
||||
}
|
||||
|
||||
export interface AvropCompactData {
|
||||
@@ -49,6 +50,7 @@ export function mapAvropResponseToAvrop(data: AvropResponse): Avrop {
|
||||
utforandeverksamhet,
|
||||
handledareCiamUserId,
|
||||
handledare,
|
||||
recievedTimestamp,
|
||||
} = data;
|
||||
|
||||
return {
|
||||
@@ -69,5 +71,6 @@ export function mapAvropResponseToAvrop(data: AvropResponse): Avrop {
|
||||
utforandeVerksamhet: utforandeverksamhet,
|
||||
handledareCiamUserId: handledareCiamUserId,
|
||||
handledare,
|
||||
recievedTimestamp,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Fraga } from './fraga.model';
|
||||
|
||||
export interface AvvikelseAlternativ {
|
||||
avvikelseorsakskod: string,
|
||||
frageformular: Array<Fraga>,
|
||||
rapporteringsdatum: string
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
import { AvvikelseAlternativ } from './avvikelse-alternativ.model';
|
||||
import { FranvaroAlternativ } from './franvaro-alternativ.model';
|
||||
import { Fraga } from '@msfa-models/fraga.model';
|
||||
|
||||
export interface AvvikelseAlternativ {
|
||||
avvikelseorsakskod: string;
|
||||
frageformular: Fraga[];
|
||||
rapporteringsdatum: string;
|
||||
}
|
||||
|
||||
export interface Avvikelse {
|
||||
genomforandeReferens: number;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface DateFormatOptions {
|
||||
year?: 'short' | 'long' | 'numeric';
|
||||
year?: 'numeric' | '2-digit';
|
||||
month?: 'short' | 'long' | 'numeric';
|
||||
day?: 'short' | 'long' | 'numeric';
|
||||
weekday?: 'short' | 'long' | 'numeric';
|
||||
day?: 'numeric' | '2-digit';
|
||||
weekday?: 'short' | 'long' | 'narrow';
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ export interface FranvaroAlternativ {
|
||||
avvikelseOrsaksKod: string;
|
||||
datum: string;
|
||||
heldag: boolean;
|
||||
startTid: string;
|
||||
slutTid: string;
|
||||
startTid: string | null;
|
||||
slutTid: string | null;
|
||||
forvantadNarvaro: {
|
||||
startTid: string;
|
||||
slutTid: string;
|
||||
|
||||
11
apps/mina-sidor-fa/src/app/shared/models/franvaro.model.ts
Normal file
11
apps/mina-sidor-fa/src/app/shared/models/franvaro.model.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface Franvaro {
|
||||
reason: string;
|
||||
date: Date;
|
||||
wholeDay: boolean;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
expectedPresenceStartTime: string;
|
||||
expectedPresenceEndTime: string;
|
||||
otherKnownReason: string;
|
||||
knownReasonComment: string;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AvvikelseOrsaksKodEnum } from '@msfa-enums/avvikelse-orsak-kod.enum';
|
||||
import { OrsaksKoderAvvikelseResponse } from './api/orsaks-koder-avvikelse.response.model';
|
||||
|
||||
// TODO rename to AvvikelseOrsaker
|
||||
export interface OrsaksKoderAvvikelse {
|
||||
name: string;
|
||||
value: AvvikelseOrsaksKodEnum;
|
||||
id: string; //AvvikelseOrsaksKodEnum
|
||||
state: number;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function mapResponseToOrsaksKoderAvvikelse(data: OrsaksKoderAvvikelseResp
|
||||
|
||||
return {
|
||||
name,
|
||||
value: id,
|
||||
state
|
||||
}
|
||||
id: id.toString(),
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { OrsaksKoderFranvaroResponse } from './api/orsaks-koder-franvaro.respons
|
||||
export interface OrsaksKoderFranvaro {
|
||||
name: string;
|
||||
value: FranvaroOrsaksKodEnum;
|
||||
state: number;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
@@ -15,13 +14,12 @@ export interface KandaAvvikelseKoder {
|
||||
}
|
||||
|
||||
export function mapResponseToOrsaksKoderFranvaro(data: OrsaksKoderFranvaroResponse): OrsaksKoderFranvaro {
|
||||
const { name, id, state } = data;
|
||||
const { name, id } = data;
|
||||
|
||||
return {
|
||||
name,
|
||||
value: id,
|
||||
state
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function mapResponseToAndraKandaOrsaker(data: KandaAvvikelseKoderResponse): KandaAvvikelseKoder {
|
||||
@@ -29,6 +27,6 @@ export function mapResponseToAndraKandaOrsaker(data: KandaAvvikelseKoderResponse
|
||||
|
||||
return {
|
||||
name,
|
||||
value: id
|
||||
}
|
||||
value: id,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
OrsaksKoderFranvaro,
|
||||
} from '@msfa-models/orsaks-koder-franvaro.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { catchError, filter, map } from 'rxjs/operators';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -56,8 +58,16 @@ export class AvvikelseApiService {
|
||||
);
|
||||
}
|
||||
|
||||
public createAvvikelse$(avvikelse: Avvikelse): Promise<void> {
|
||||
return this.httpClient.post<void>(`${this._apiBaseUrl}/avvikelse`, avvikelse).toPromise();
|
||||
public createAvvikelse$(avvikelse: Avvikelse): Observable<unknown> {
|
||||
return this.httpClient.post<void>(`${this._apiBaseUrl}/avvikelse`, avvikelse).pipe(
|
||||
catchError((error: Error) => {
|
||||
throw new CustomError({
|
||||
error,
|
||||
message: 'Det gick inte att skicka avvikelse \n\n' + error.message,
|
||||
type: ErrorType.API,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public createFranvaro$(avvikelse: Avvikelse): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { OrsaksKoderFranvaroResponse } from '@msfa-models/api/orsaks-koder-franvaro.response.model';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
|
||||
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
|
||||
import { mapResponseToOrsaksKoderFranvaro, OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { catchError, filter, map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FranvaroReportApiService {
|
||||
private _apiBaseUrl = `${environment.api.url}/rapporter`;
|
||||
|
||||
constructor(private httpClient: HttpClient, private deltagareApiService: DeltagareApiService) {}
|
||||
|
||||
public fetchReasons$(): Observable<OrsaksKoderFranvaro[]> {
|
||||
return this.httpClient.get<{ data: OrsaksKoderFranvaroResponse[] }>(`${this._apiBaseUrl}/orsakskoderfranvaro`).pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(reason => mapResponseToOrsaksKoderFranvaro(reason))),
|
||||
catchError((error: Error & { status: number }) => {
|
||||
throw new CustomError(
|
||||
errorToCustomError({ ...error, message: `Kunde inte hämta orsaker till frånvaro.\n\n${error.message}` })
|
||||
);
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
public fetchOtherKnownReasons$(): Observable<OrsaksKoderFranvaro[]> {
|
||||
return this.httpClient.get<{ data: OrsaksKoderFranvaroResponse[] }>(`${this._apiBaseUrl}/kandaavvikelsekoder`).pipe(
|
||||
filter(response => !!response?.data),
|
||||
map(({ data }) => data.map(reason => mapResponseToOrsaksKoderFranvaro(reason))),
|
||||
catchError((error: Error & { status: number }) => {
|
||||
throw new CustomError(
|
||||
errorToCustomError({ ...error, message: `Kunde inte hämta kända orsaker till frånvaro.\n\n${error.message}` })
|
||||
);
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
|
||||
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
|
||||
public async postFranvaroReport$(requestData: FranvaroRequestData): Promise<void> {
|
||||
return this.httpClient.post<void>(`${this._apiBaseUrl}/franvaro`, requestData).toPromise();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { DateFormatOptions } from '@msfa-models/date-format-options.model';
|
||||
|
||||
// Takes either 6 or 8 characters string (YYYYMMDD) and formats it to ISO standard (YYYY-MM-DD).
|
||||
export function formatToIsoString(date: string): string {
|
||||
if (date.length === 6) {
|
||||
@@ -16,3 +18,13 @@ export function formatToDate(date: string): Date {
|
||||
|
||||
return new Date(`${year}-${month}-${day}`);
|
||||
}
|
||||
|
||||
export function dateToIsoString(date: Date, locale: string = 'sv-SE'): string {
|
||||
const formatOptions: DateFormatOptions = {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
return new Date(date).toLocaleDateString(locale, formatOptions);
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { ReportType } from '@msfa-enums/report-type.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 { ValidationError } from '@msfa-models/validation-error.model';
|
||||
|
||||
export interface Controls {
|
||||
[key: string]: AbstractControl;
|
||||
}
|
||||
|
||||
export function requiredDescriptionValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const valueOfNestedFormControl = ctrls['orsakerFormGroup'].get('andraKandaOrsaker').value as string;
|
||||
const valueOfFormControl = control.value as string;
|
||||
const isRequired = !valueOfFormControl && +valueOfNestedFormControl === KandaOrsakerEnum.AnnanOrsak;
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: 'Beskrivning är obligatoriskt' };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredOrsakerValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const valueOfNestedFormControl = ctrls['orsaker'].value as string;
|
||||
const isRequired = !valueOfNestedFormControl && !ctrls['andraKandaOrsaker'].value;
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Orsak är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredAnnanKandOrsakValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isAnnanKandOrsak = +ctrls['orsaker'].value === FranvaroOrsaksKodEnum.AnnanKandOrsak;
|
||||
const valueOfNestedFormControl = ctrls['andraKandaOrsaker'].value as string;
|
||||
const isRequired = isAnnanKandOrsak && !valueOfNestedFormControl;
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Annan orsak är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class RequiredDateValidator {
|
||||
static CheckIfRequired(): ValidatorFn {
|
||||
return (fg: AbstractControl): { [key: string]: string } => {
|
||||
const valueOfFormControl = fg?.get('date')?.value as string;
|
||||
const isRequired = !valueOfFormControl;
|
||||
|
||||
return isRequired ? { dateIsRequired: 'Datum är obligatoriskt' } : null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function requiredDayOrPartOfDayValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isFranvaro = ctrls['alternative'].value === ReportType.FRANVARO;
|
||||
const valueOfFormControl = control.value as string;
|
||||
const isRequired = isFranvaro && !valueOfFormControl;
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Hel- eller del av dag är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredStartTimeValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isFranvaro = (ctrls['alternative']?.value as string) === ReportType.FRANVARO;
|
||||
const isPartOfDay = ctrls['dayOrPartOfDay']?.value === DayOrPartOfDay.DEL_AV_DAG;
|
||||
const valueOfFormControl = control?.value as string;
|
||||
const isRequired = isFranvaro && isPartOfDay && (valueOfFormControl === '' || valueOfFormControl === null);
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Starttid är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredEndTimeValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isFranvaro = (ctrls['alternative']?.value as string) === ReportType.FRANVARO;
|
||||
const isPartOfDay = ctrls['dayOrPartOfDay']?.value === DayOrPartOfDay.DEL_AV_DAG;
|
||||
const valueOfFormControl = control?.value as string;
|
||||
const isRequired = isFranvaro && isPartOfDay && (valueOfFormControl === '' || valueOfFormControl === null);
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Sluttid är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredFraga1Validator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isAvvikelse = ctrls['alternative']?.value === 'avvikelse';
|
||||
const valueOfFormControl = control.value as string;
|
||||
const isRequired = isAvvikelse && !valueOfFormControl;
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Beskrivning är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function requiredfraga2Validator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
const ctrls = control?.parent?.parent?.controls as Controls;
|
||||
|
||||
if (ctrls) {
|
||||
const isAvvikelse = ctrls['alternative']?.value === 'avvikelse';
|
||||
const valueOfFormControl = control.value as string;
|
||||
const orsaksKodToValidate = ctrls['orsakerFormGroup']?.get('orsaker')?.value as string;
|
||||
const isRequired =
|
||||
isAvvikelse &&
|
||||
!valueOfFormControl &&
|
||||
orsaksKodToValidate !== '19' &&
|
||||
orsaksKodToValidate !== '20' &&
|
||||
orsaksKodToValidate !== '28';
|
||||
|
||||
if (isRequired) {
|
||||
return { type: 'required', message: `Beskrivning är obligatoriskt` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { ISO_DATE_NO_TIME } from '@msfa-constants/regex';
|
||||
import { ValidationError } from '@msfa-models/validation-error.model';
|
||||
|
||||
export function isoDateIsValid(date: string): boolean {
|
||||
return ISO_DATE_NO_TIME.test(date);
|
||||
}
|
||||
|
||||
export function isoDateWithoutTimeValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationError => {
|
||||
if (control && control.value) {
|
||||
const value: string = control.value as string;
|
||||
|
||||
if (!isoDateIsValid(value)) {
|
||||
return { type: 'invalid', message: `Ogiltigt datum, vänligen ange YYYY-MM-DD` };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
@if $type == 'secondary' {
|
||||
background-color: var(--digi-button--background--secondary--hover);
|
||||
color: var(--digi-button--color--secondary--hover);
|
||||
color: var(--digi-button--color--secondary);
|
||||
} @else if $type == 'tertiary' {
|
||||
color: var(--digi-button--color--tertiary--hover);
|
||||
} @else {
|
||||
|
||||
@@ -66,6 +66,16 @@ dl {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
// Removing margins from digi fieldset component.
|
||||
.digi-form-fieldset {
|
||||
margin: 0;
|
||||
|
||||
&__legend {
|
||||
margin-bottom: var(--digi--layout--gutter--s);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.msfa {
|
||||
&__a11y-sr-only {
|
||||
@include msfa__a11y-sr-only;
|
||||
|
||||
Reference in New Issue
Block a user